From 37bfb3c9a813eb42c838398e405c04af03de96c0 Mon Sep 17 00:00:00 2001 From: James Deng Date: Wed, 29 Nov 2017 11:28:32 +0800 Subject: [PATCH] Initial source --- README.md | 2 +- adb/Android.mk | 180 ++ adb/MODULE_LICENSE_APACHE2 | 0 adb/Makefile | 46 + adb/NOTICE | 191 ++ adb/OVERVIEW.TXT | 139 ++ adb/SERVICES.TXT | 247 ++ adb/adb.c | 1646 ++++++++++++ adb/adb.h | 489 ++++ adb/adb_auth.h | 54 + adb/adb_auth_client.c | 271 ++ adb/adb_auth_host.c | 426 ++++ adb/adb_client.c | 340 +++ adb/adb_client.h | 57 + adb/backup_service.c | 155 ++ adb/commandline.c | 1746 +++++++++++++ adb/console.c | 45 + adb/fdevent.c | 695 ++++++ adb/fdevent.h | 83 + adb/file_sync_client.c | 1024 ++++++++ adb/file_sync_service.c | 414 +++ adb/file_sync_service.h | 87 + adb/framebuffer_service.c | 180 ++ adb/get_my_path_darwin.c | 30 + adb/get_my_path_freebsd.c | 36 + adb/get_my_path_linux.c | 33 + adb/get_my_path_windows.c | 34 + adb/jdwp_service.c | 735 ++++++ adb/log_service.c | 92 + adb/mutex_list.h | 25 + adb/protocol.txt | 271 ++ adb/remount_service.c | 120 + adb/services.c | 583 +++++ adb/sockets.c | 867 +++++++ adb/sockets.dia | Bin 0 -> 2333 bytes adb/sysdeps.h | 521 ++++ adb/sysdeps_win32.c | 2220 +++++++++++++++++ adb/test_track_devices.c | 97 + adb/test_track_jdwp.c | 97 + adb/transport.c | 1232 +++++++++ adb/transport.h | 26 + adb/transport_local.c | 444 ++++ adb/transport_usb.c | 148 ++ adb/usb_libusb.c | 657 +++++ adb/usb_linux.c | 715 ++++++ adb/usb_linux_client.c | 492 ++++ adb/usb_osx.c | 545 ++++ adb/usb_vendors.c | 310 +++ adb/usb_vendors.h | 25 + adb/usb_windows.c | 515 ++++ include/android/api-level.h | 33 + include/android/log.h | 128 + include/cutils/android_reboot.h | 34 + include/cutils/aref.h | 62 + include/cutils/ashmem.h | 45 + include/cutils/atomic-arm.h | 176 ++ include/cutils/atomic-inline.h | 72 + include/cutils/atomic-mips.h | 199 ++ include/cutils/atomic-x86.h | 153 ++ include/cutils/atomic.h | 121 + include/cutils/bitops.h | 120 + include/cutils/compiler.h | 44 + include/cutils/config_utils.h | 64 + include/cutils/cpu_info.h | 34 + include/cutils/debugger.h | 58 + include/cutils/dir_hash.h | 26 + include/cutils/fs.h | 70 + include/cutils/hashmap.h | 150 ++ include/cutils/iosched_policy.h | 38 + include/cutils/jstring.h | 43 + include/cutils/klog.h | 40 + include/cutils/list.h | 59 + include/cutils/log.h | 1 + include/cutils/memory.h | 42 + include/cutils/misc.h | 48 + include/cutils/multiuser.h | 41 + include/cutils/native_handle.h | 69 + include/cutils/open_memstream.h | 36 + include/cutils/partition_utils.h | 27 + include/cutils/process_name.h | 42 + include/cutils/properties.h | 90 + include/cutils/qtaguid.h | 66 + include/cutils/record_stream.h | 43 + include/cutils/sched_policy.h | 61 + include/cutils/sockets.h | 112 + include/cutils/str_parms.h | 49 + include/cutils/threads.h | 146 ++ include/cutils/trace.h | 294 +++ include/cutils/tztime.h | 24 + include/cutils/uevent.h | 35 + include/hardware/qemu_pipe.h | 91 + include/log/event_tag_map.h | 50 + include/log/log.h | 563 +++++ include/log/logd.h | 49 + include/log/logger.h | 81 + include/log/logprint.h | 156 ++ include/log/uio.h | 48 + include/mincrypt/hash-internal.h | 40 + include/mincrypt/rsa.h | 58 + include/mincrypt/sha.h | 30 + include/mincrypt/sha256.h | 29 + .../private/android_filesystem_capability.h | 117 + include/private/android_filesystem_config.h | 301 +++ include/sys/system_properties.h | 102 + libcutils/Android.mk | 155 ++ libcutils/MODULE_LICENSE_APACHE2 | 0 libcutils/Makefile | 79 + libcutils/NOTICE | 190 ++ libcutils/android_reboot.c | 134 + libcutils/arch-arm/memset32.S | 93 + libcutils/arch-mips/android_memset.c | 31 + libcutils/arch-x86/android_memset16.S | 32 + libcutils/arch-x86/android_memset32.S | 33 + libcutils/arch-x86/cache_wrapper.S | 24 + libcutils/arch-x86/sse2-memset16-atom.S | 722 ++++++ libcutils/arch-x86/sse2-memset32-atom.S | 513 ++++ libcutils/ashmem-dev.c | 90 + libcutils/ashmem-host.c | 114 + libcutils/atomic.c | 19 + libcutils/config_utils.c | 329 +++ libcutils/cpu_info.c | 83 + libcutils/debugger.c | 87 + libcutils/dir_hash.c | 334 +++ libcutils/dlmalloc_stubs.c | 35 + libcutils/fs.c | 237 ++ libcutils/hashmap.c | 351 +++ libcutils/iosched_policy.c | 67 + libcutils/klog.c | 66 + libcutils/list.c | 37 + libcutils/load_file.c | 51 + libcutils/loghack.h | 38 + libcutils/memory.c | 91 + libcutils/multiuser.c | 29 + libcutils/native_handle.c | 60 + libcutils/open_memstream.c | 381 +++ libcutils/partition_utils.c | 67 + libcutils/process_name.c | 95 + libcutils/properties.c | 339 +++ libcutils/qtaguid.c | 177 ++ libcutils/record_stream.c | 186 ++ libcutils/sched_policy.c | 362 +++ libcutils/socket_inaddr_any_server.c | 70 + libcutils/socket_local.h | 39 + libcutils/socket_local_client.c | 167 ++ libcutils/socket_local_server.c | 124 + libcutils/socket_loopback_client.c | 59 + libcutils/socket_loopback_server.c | 71 + libcutils/socket_network_client.c | 65 + libcutils/sockets.c | 44 + libcutils/str_parms.c | 372 +++ libcutils/strdup16to8.c | 168 ++ libcutils/strdup8to16.c | 214 ++ libcutils/tests/Android.mk | 1 + libcutils/tests/memset_mips/Android.mk | 23 + .../tests/memset_mips/android_memset_dumb.S | 36 + .../tests/memset_mips/android_memset_test.S | 152 ++ libcutils/tests/memset_mips/memset_cmips.S | 227 ++ libcutils/tests/memset_mips/memset_omips.S | 90 + libcutils/tests/memset_mips/test_memset.c | 235 ++ libcutils/threads.c | 84 + libcutils/trace.c | 186 ++ libcutils/uevent.c | 120 + liblog/Android.mk | 81 + liblog/Makefile | 27 + liblog/NOTICE | 190 ++ liblog/event_tag_map.c | 438 ++++ liblog/fake_log_device.c | 685 +++++ liblog/logd_write.c | 294 +++ liblog/logprint.c | 1016 ++++++++ liblog/uio.c | 76 + libmincrypt/Android.mk | 18 + libmincrypt/Makefile | 28 + libmincrypt/NOTICE | 23 + libmincrypt/rsa.c | 308 +++ libmincrypt/sha.c | 155 ++ libmincrypt/sha256.c | 184 ++ libmincrypt/test/Android.mk | 10 + libmincrypt/test/rsa_test.c | 838 +++++++ libmincrypt/tools/Android.mk | 21 + libmincrypt/tools/DumpPublicKey.java | 169 ++ libmincrypt/tools/DumpPublicKey.mf | 1 + 181 files changed, 36936 insertions(+), 1 deletion(-) create mode 100644 adb/Android.mk create mode 100644 adb/MODULE_LICENSE_APACHE2 create mode 100644 adb/Makefile create mode 100644 adb/NOTICE create mode 100644 adb/OVERVIEW.TXT create mode 100644 adb/SERVICES.TXT create mode 100644 adb/adb.c create mode 100644 adb/adb.h create mode 100644 adb/adb_auth.h create mode 100644 adb/adb_auth_client.c create mode 100644 adb/adb_auth_host.c create mode 100644 adb/adb_client.c create mode 100644 adb/adb_client.h create mode 100644 adb/backup_service.c create mode 100644 adb/commandline.c create mode 100644 adb/console.c create mode 100644 adb/fdevent.c create mode 100644 adb/fdevent.h create mode 100644 adb/file_sync_client.c create mode 100644 adb/file_sync_service.c create mode 100644 adb/file_sync_service.h create mode 100644 adb/framebuffer_service.c create mode 100644 adb/get_my_path_darwin.c create mode 100644 adb/get_my_path_freebsd.c create mode 100644 adb/get_my_path_linux.c create mode 100644 adb/get_my_path_windows.c create mode 100644 adb/jdwp_service.c create mode 100644 adb/log_service.c create mode 100644 adb/mutex_list.h create mode 100644 adb/protocol.txt create mode 100644 adb/remount_service.c create mode 100644 adb/services.c create mode 100644 adb/sockets.c create mode 100644 adb/sockets.dia create mode 100644 adb/sysdeps.h create mode 100644 adb/sysdeps_win32.c create mode 100644 adb/test_track_devices.c create mode 100644 adb/test_track_jdwp.c create mode 100644 adb/transport.c create mode 100644 adb/transport.h create mode 100644 adb/transport_local.c create mode 100644 adb/transport_usb.c create mode 100644 adb/usb_libusb.c create mode 100644 adb/usb_linux.c create mode 100644 adb/usb_linux_client.c create mode 100644 adb/usb_osx.c create mode 100644 adb/usb_vendors.c create mode 100644 adb/usb_vendors.h create mode 100644 adb/usb_windows.c create mode 100644 include/android/api-level.h create mode 100644 include/android/log.h create mode 100644 include/cutils/android_reboot.h create mode 100644 include/cutils/aref.h create mode 100644 include/cutils/ashmem.h create mode 100644 include/cutils/atomic-arm.h create mode 100644 include/cutils/atomic-inline.h create mode 100644 include/cutils/atomic-mips.h create mode 100644 include/cutils/atomic-x86.h create mode 100644 include/cutils/atomic.h create mode 100644 include/cutils/bitops.h create mode 100644 include/cutils/compiler.h create mode 100644 include/cutils/config_utils.h create mode 100644 include/cutils/cpu_info.h create mode 100644 include/cutils/debugger.h create mode 100644 include/cutils/dir_hash.h create mode 100644 include/cutils/fs.h create mode 100644 include/cutils/hashmap.h create mode 100644 include/cutils/iosched_policy.h create mode 100644 include/cutils/jstring.h create mode 100644 include/cutils/klog.h create mode 100644 include/cutils/list.h create mode 100644 include/cutils/log.h create mode 100644 include/cutils/memory.h create mode 100644 include/cutils/misc.h create mode 100644 include/cutils/multiuser.h create mode 100644 include/cutils/native_handle.h create mode 100644 include/cutils/open_memstream.h create mode 100644 include/cutils/partition_utils.h create mode 100644 include/cutils/process_name.h create mode 100644 include/cutils/properties.h create mode 100644 include/cutils/qtaguid.h create mode 100644 include/cutils/record_stream.h create mode 100644 include/cutils/sched_policy.h create mode 100644 include/cutils/sockets.h create mode 100644 include/cutils/str_parms.h create mode 100644 include/cutils/threads.h create mode 100644 include/cutils/trace.h create mode 100644 include/cutils/tztime.h create mode 100644 include/cutils/uevent.h create mode 100644 include/hardware/qemu_pipe.h create mode 100644 include/log/event_tag_map.h create mode 100644 include/log/log.h create mode 100644 include/log/logd.h create mode 100644 include/log/logger.h create mode 100644 include/log/logprint.h create mode 100644 include/log/uio.h create mode 100644 include/mincrypt/hash-internal.h create mode 100644 include/mincrypt/rsa.h create mode 100644 include/mincrypt/sha.h create mode 100644 include/mincrypt/sha256.h create mode 100644 include/private/android_filesystem_capability.h create mode 100644 include/private/android_filesystem_config.h create mode 100644 include/sys/system_properties.h create mode 100644 libcutils/Android.mk create mode 100644 libcutils/MODULE_LICENSE_APACHE2 create mode 100644 libcutils/Makefile create mode 100644 libcutils/NOTICE create mode 100644 libcutils/android_reboot.c create mode 100644 libcutils/arch-arm/memset32.S create mode 100644 libcutils/arch-mips/android_memset.c create mode 100644 libcutils/arch-x86/android_memset16.S create mode 100644 libcutils/arch-x86/android_memset32.S create mode 100644 libcutils/arch-x86/cache_wrapper.S create mode 100644 libcutils/arch-x86/sse2-memset16-atom.S create mode 100644 libcutils/arch-x86/sse2-memset32-atom.S create mode 100644 libcutils/ashmem-dev.c create mode 100644 libcutils/ashmem-host.c create mode 100644 libcutils/atomic.c create mode 100644 libcutils/config_utils.c create mode 100644 libcutils/cpu_info.c create mode 100644 libcutils/debugger.c create mode 100644 libcutils/dir_hash.c create mode 100644 libcutils/dlmalloc_stubs.c create mode 100644 libcutils/fs.c create mode 100644 libcutils/hashmap.c create mode 100644 libcutils/iosched_policy.c create mode 100644 libcutils/klog.c create mode 100644 libcutils/list.c create mode 100644 libcutils/load_file.c create mode 100644 libcutils/loghack.h create mode 100644 libcutils/memory.c create mode 100644 libcutils/multiuser.c create mode 100644 libcutils/native_handle.c create mode 100644 libcutils/open_memstream.c create mode 100644 libcutils/partition_utils.c create mode 100644 libcutils/process_name.c create mode 100644 libcutils/properties.c create mode 100644 libcutils/qtaguid.c create mode 100644 libcutils/record_stream.c create mode 100644 libcutils/sched_policy.c create mode 100644 libcutils/socket_inaddr_any_server.c create mode 100644 libcutils/socket_local.h create mode 100644 libcutils/socket_local_client.c create mode 100644 libcutils/socket_local_server.c create mode 100644 libcutils/socket_loopback_client.c create mode 100644 libcutils/socket_loopback_server.c create mode 100644 libcutils/socket_network_client.c create mode 100644 libcutils/sockets.c create mode 100644 libcutils/str_parms.c create mode 100644 libcutils/strdup16to8.c create mode 100644 libcutils/strdup8to16.c create mode 100644 libcutils/tests/Android.mk create mode 100644 libcutils/tests/memset_mips/Android.mk create mode 100644 libcutils/tests/memset_mips/android_memset_dumb.S create mode 100644 libcutils/tests/memset_mips/android_memset_test.S create mode 100644 libcutils/tests/memset_mips/memset_cmips.S create mode 100644 libcutils/tests/memset_mips/memset_omips.S create mode 100644 libcutils/tests/memset_mips/test_memset.c create mode 100644 libcutils/threads.c create mode 100644 libcutils/trace.c create mode 100644 libcutils/uevent.c create mode 100644 liblog/Android.mk create mode 100644 liblog/Makefile create mode 100644 liblog/NOTICE create mode 100644 liblog/event_tag_map.c create mode 100644 liblog/fake_log_device.c create mode 100644 liblog/logd_write.c create mode 100644 liblog/logprint.c create mode 100644 liblog/uio.c create mode 100644 libmincrypt/Android.mk create mode 100644 libmincrypt/Makefile create mode 100644 libmincrypt/NOTICE create mode 100644 libmincrypt/rsa.c create mode 100644 libmincrypt/sha.c create mode 100644 libmincrypt/sha256.c create mode 100644 libmincrypt/test/Android.mk create mode 100644 libmincrypt/test/rsa_test.c create mode 100644 libmincrypt/tools/Android.mk create mode 100644 libmincrypt/tools/DumpPublicKey.java create mode 100644 libmincrypt/tools/DumpPublicKey.mf diff --git a/README.md b/README.md index d7c48de..1b1c386 100644 --- a/README.md +++ b/README.md @@ -1 +1 @@ -# tinyasc \ No newline at end of file +# tinyasc A tiny android system core providing adbd function, seperated from android-4.4.4_r2.0.1. \ No newline at end of file diff --git a/adb/Android.mk b/adb/Android.mk new file mode 100644 index 0000000..721b48d --- /dev/null +++ b/adb/Android.mk @@ -0,0 +1,180 @@ +# Copyright 2005 The Android Open Source Project +# +# Android.mk for adb +# + +LOCAL_PATH:= $(call my-dir) + +# adb host tool +# ========================================================= +include $(CLEAR_VARS) + +# Default to a virtual (sockets) usb interface +USB_SRCS := +EXTRA_SRCS := + +ifeq ($(HOST_OS),linux) + USB_SRCS := usb_linux.c + EXTRA_SRCS := get_my_path_linux.c + LOCAL_LDLIBS += -lrt -ldl -lpthread + LOCAL_CFLAGS += -DWORKAROUND_BUG6558362 +endif + +ifeq ($(HOST_OS),darwin) + USB_SRCS := usb_osx.c + EXTRA_SRCS := get_my_path_darwin.c + LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon +endif + +ifeq ($(HOST_OS),freebsd) + USB_SRCS := usb_libusb.c + EXTRA_SRCS := get_my_path_freebsd.c + LOCAL_LDLIBS += -lpthread -lusb +endif + +ifeq ($(HOST_OS),windows) + USB_SRCS := usb_windows.c + EXTRA_SRCS := get_my_path_windows.c ../libcutils/list.c + EXTRA_STATIC_LIBS := AdbWinApi + ifneq ($(strip $(USE_CYGWIN)),) + # Pure cygwin case + LOCAL_LDLIBS += -lpthread -lgdi32 + LOCAL_C_INCLUDES += /usr/include/w32api/ddk + endif + ifneq ($(strip $(USE_MINGW)),) + # MinGW under Linux case + LOCAL_LDLIBS += -lws2_32 -lgdi32 + USE_SYSDEPS_WIN32 := 1 + LOCAL_C_INCLUDES += /usr/i586-mingw32msvc/include/ddk + endif + LOCAL_C_INCLUDES += development/host/windows/usb/api/ +endif + +LOCAL_SRC_FILES := \ + adb.c \ + console.c \ + transport.c \ + transport_local.c \ + transport_usb.c \ + commandline.c \ + adb_client.c \ + adb_auth_host.c \ + sockets.c \ + services.c \ + file_sync_client.c \ + $(EXTRA_SRCS) \ + $(USB_SRCS) \ + usb_vendors.c + +LOCAL_C_INCLUDES += external/openssl/include + +ifneq ($(USE_SYSDEPS_WIN32),) + LOCAL_SRC_FILES += sysdeps_win32.c +else + LOCAL_SRC_FILES += fdevent.c +endif + +LOCAL_CFLAGS += -O2 -g -DADB_HOST=1 -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE +LOCAL_MODULE := adb +LOCAL_MODULE_TAGS := debug + +LOCAL_STATIC_LIBRARIES := libzipfile libunz libcrypto_static $(EXTRA_STATIC_LIBS) +ifeq ($(USE_SYSDEPS_WIN32),) + LOCAL_STATIC_LIBRARIES += libcutils +endif + +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 +endif + + +# adbd device daemon +# ========================================================= + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + adb.c \ + backup_service.c \ + fdevent.c \ + transport.c \ + transport_local.c \ + transport_usb.c \ + adb_auth_client.c \ + sockets.c \ + services.c \ + file_sync_service.c \ + jdwp_service.c \ + framebuffer_service.c \ + remount_service.c \ + usb_linux_client.c \ + log_service.c + +LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter +LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE + +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1 +endif + +LOCAL_MODULE := adbd + +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) +LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) + +LOCAL_STATIC_LIBRARIES := liblog libcutils libc libmincrypt +include $(BUILD_EXECUTABLE) + + +# adb host tool for device-as-host +# ========================================================= +ifneq ($(SDK_ONLY),true) +include $(CLEAR_VARS) + +LOCAL_LDLIBS := -lrt -ldl -lpthread + +LOCAL_SRC_FILES := \ + adb.c \ + console.c \ + transport.c \ + transport_local.c \ + transport_usb.c \ + commandline.c \ + adb_client.c \ + adb_auth_host.c \ + sockets.c \ + services.c \ + file_sync_client.c \ + get_my_path_linux.c \ + usb_linux.c \ + usb_vendors.c \ + fdevent.c + +LOCAL_CFLAGS := \ + -O2 \ + -g \ + -DADB_HOST=1 \ + -DADB_HOST_ON_TARGET=1 \ + -Wall \ + -Wno-unused-parameter \ + -D_XOPEN_SOURCE \ + -D_GNU_SOURCE + +LOCAL_C_INCLUDES += external/openssl/include + +LOCAL_MODULE := adb + +LOCAL_STATIC_LIBRARIES := libzipfile libunz libcutils + +LOCAL_SHARED_LIBRARIES := libcrypto + +include $(BUILD_EXECUTABLE) +endif diff --git a/adb/MODULE_LICENSE_APACHE2 b/adb/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/adb/Makefile b/adb/Makefile new file mode 100644 index 0000000..cea3d9e --- /dev/null +++ b/adb/Makefile @@ -0,0 +1,46 @@ +TARGET = adbd +INSTALL_DIR = ../bin + +SRCS := \ + adb.c \ + backup_service.c \ + fdevent.c \ + transport.c \ + transport_local.c \ + transport_usb.c \ + adb_auth_client.c \ + sockets.c \ + services.c \ + file_sync_service.c \ + jdwp_service.c \ + framebuffer_service.c \ + remount_service.c \ + usb_linux_client.c \ + log_service.c + +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld + +CFLAGS += -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter +CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE +CFLAGS += -DALLOW_ADBD_ROOT=1 +CFLAGS += -DHAVE_SYS_UIO_H +CFLAGS += -I../include + +LDFLAGS += -lpthread -L../lib -lcutils -llog -lmincrypt + +OBJS = $(SRCS:.c=.o) + +all: $(TARGET) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(TARGET): $(OBJS) + $(CC) $(OBJS) $(LDFLAGS) -o $@ + [ -f $(INSTALL_DIR) ] || mkdir -p $(INSTALL_DIR) + cp $(TARGET) $(INSTALL_DIR) + +.PHONY: clean +clean: + rm -rf $(OBJS) $(TARGET) diff --git a/adb/NOTICE b/adb/NOTICE new file mode 100644 index 0000000..9ffcc08 --- /dev/null +++ b/adb/NOTICE @@ -0,0 +1,191 @@ + + Copyright (c) 2006-2009, The Android Open Source Project + Copyright 2006, Brian Swetland + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/adb/OVERVIEW.TXT b/adb/OVERVIEW.TXT new file mode 100644 index 0000000..c40695a --- /dev/null +++ b/adb/OVERVIEW.TXT @@ -0,0 +1,139 @@ +Implementation notes regarding ADB. + +I. General Overview: + +The Android Debug Bridge (ADB) is used to: + +- keep track of all Android devices and emulators instances + connected to or running on a given host developer machine + +- implement various control commands (e.g. "adb shell", "adb pull", etc..) + for the benefit of clients (command-line users, or helper programs like + DDMS). These commands are what is called a 'service' in ADB. + +As a whole, everything works through the following components: + + 1. The ADB server + + This is a background process that runs on the host machine. Its purpose + if to sense the USB ports to know when devices are attached/removed, + as well as when emulator instances start/stop. + + It thus maintains a list of "connected devices" and assigns a 'state' + to each one of them: OFFLINE, BOOTLOADER, RECOVERY or ONLINE (more on + this below). + + The ADB server is really one giant multiplexing loop whose purpose is + to orchestrate the exchange of data (packets, really) between clients, + services and devices. + + + 2. The ADB daemon (adbd) + + The 'adbd' program runs as a background process within an Android device + or emulated system. Its purpose is to connect to the ADB server + (through USB for devices, through TCP for emulators) and provide a + few services for clients that run on the host. + + The ADB server considers that a device is ONLINE when it has successfully + connected to the adbd program within it. Otherwise, the device is OFFLINE, + meaning that the ADB server detected a new device/emulator, but could not + connect to the adbd daemon. + + the BOOTLOADER and RECOVERY states correspond to alternate states of + devices when they are in the bootloader or recovery mode. + + 3. The ADB command-line client + + The 'adb' command-line program is used to run adb commands from a shell + or a script. It first tries to locate the ADB server on the host machine, + and will start one automatically if none is found. + + then, the client sends its service requests to the ADB server. It doesn't + need to know. + + Currently, a single 'adb' binary is used for both the server and client. + this makes distribution and starting the server easier. + + + 4. Services + + There are essentially two kinds of services that a client can talk to. + + Host Services: + these services run within the ADB Server and thus do not need to + communicate with a device at all. A typical example is "adb devices" + which is used to return the list of currently known devices and their + state. They are a few couple other services though. + + Local Services: + these services either run within the adbd daemon, or are started by + it on the device. The ADB server is used to multiplex streams + between the client and the service running in adbd. In this case + its role is to initiate the connection, then of being a pass-through + for the data. + + +II. Protocol details: + + 1. Client <-> Server protocol: + + This details the protocol used between ADB clients and the ADB + server itself. The ADB server listens on TCP:localhost:5037. + + A client sends a request using the following format: + + 1. A 4-byte hexadecimal string giving the length of the payload + 2. Followed by the payload itself. + + For example, to query the ADB server for its internal version number, + the client will do the following: + + 1. Connect to tcp:localhost:5037 + 2. Send the string "000Chost:version" to the corresponding socket + + The 'host:' prefix is used to indicate that the request is addressed + to the server itself (we will talk about other kinds of requests later). + The content length is encoded in ASCII for easier debugging. + + The server should answer a request with one of the following: + + 1. For success, the 4-byte "OKAY" string + + 2. For failure, the 4-byte "FAIL" string, followed by a + 4-byte hex length, followed by a string giving the reason + for failure. + + 3. As a special exception, for 'host:version', a 4-byte + hex string corresponding to the server's internal version number + + Note that the connection is still alive after an OKAY, which allows the + client to make other requests. But in certain cases, an OKAY will even + change the state of the connection. + + For example, the case of the 'host:transport:' request, + where '' is used to identify a given device/emulator; after + the "OKAY" answer, all further requests made by the client will go + directly to the corresponding adbd daemon. + + The file SERVICES.TXT lists all services currently implemented by ADB. + + + 2. Transports: + + An ADB transport models a connection between the ADB server and one device + or emulator. There are currently two kinds of transports: + + - USB transports, for physical devices through USB + + - Local transports, for emulators running on the host, connected to + the server through TCP + + In theory, it should be possible to write a local transport that proxies + a connection between an ADB server and a device/emulator connected to/ + running on another machine. This hasn't been done yet though. + + Each transport can carry one or more multiplexed streams between clients + and the device/emulator they point to. The ADB server must handle + unexpected transport disconnections (e.g. when a device is physically + unplugged) properly. diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT new file mode 100644 index 0000000..5966686 --- /dev/null +++ b/adb/SERVICES.TXT @@ -0,0 +1,247 @@ +This file tries to document all requests a client can make +to the ADB server of an adbd daemon. See the OVERVIEW.TXT document +to understand what's going on here. + +HOST SERVICES: + +host:version + Ask the ADB server for its internal version number. + + As a special exception, the server will respond with a 4-byte + hex string corresponding to its internal version number, without + any OKAY or FAIL. + +host:kill + Ask the ADB server to quit immediately. This is used when the + ADB client detects that an obsolete server is running after an + upgrade. + +host:devices +host:devices-l + Ask to return the list of available Android devices and their + state. devices-l includes the device paths in the state. + After the OKAY, this is followed by a 4-byte hex len, + and a string that will be dumped as-is by the client, then + the connection is closed + +host:track-devices + This is a variant of host:devices which doesn't close the + connection. Instead, a new device list description is sent + each time a device is added/removed or the state of a given + device changes (hex4 + content). This allows tools like DDMS + to track the state of connected devices in real-time without + polling the server repeatedly. + +host:emulator: + This is a special query that is sent to the ADB server when a + new emulator starts up. is a decimal number corresponding + to the emulator's ADB control port, i.e. the TCP port that the + emulator will forward automatically to the adbd daemon running + in the emulator system. + + This mechanism allows the ADB server to know when new emulator + instances start. + +host:transport: + Ask to switch the connection to the device/emulator identified by + . After the OKAY response, every client request will + be sent directly to the adbd daemon running on the device. + (Used to implement the -s option) + +host:transport-usb + Ask to switch the connection to one device connected through USB + to the host machine. This will fail if there are more than one such + devices. (Used to implement the -d convenience option) + +host:transport-local + Ask to switch the connection to one emulator connected through TCP. + This will fail if there is more than one such emulator instance + running. (Used to implement the -e convenience option) + +host:transport-any + Another host:transport variant. Ask to switch the connection to + either the device or emulator connect to/running on the host. + Will fail if there is more than one such device/emulator available. + (Used when neither -s, -d or -e are provided) + +host-serial:: + This is a special form of query, where the 'host-serial::' + prefix can be used to indicate that the client is asking the ADB server + for information related to a specific device. can be in one + of the format described below. + +host-usb: + A variant of host-serial used to target the single USB device connected + to the host. This will fail if there is none or more than one. + +host-local: + A variant of host-serial used to target the single emulator instance + running on the host. This will fail if there is none or more than one. + +host: + When asking for information related to a device, 'host:' can also be + interpreted as 'any single device or emulator connected to/running on + the host'. + +:get-product + XXX + +:get-serialno + Returns the serial number of the corresponding device/emulator. + Note that emulator serial numbers are of the form "emulator-5554" + +:get-devpath + Returns the device path of the corresponding device/emulator. + +:get-state + Returns the state of a given device as a string. + +:forward:; + Asks the ADB server to forward local connections from + to the address on a given device. + + There, can be one of the + host-serial/host-usb/host-local/host prefixes as described previously + and indicates which device/emulator to target. + + the format of is one of: + + tcp: -> TCP connection on localhost: + local: -> Unix local domain socket on + + the format of is one of: + + tcp: -> TCP localhost: on device + local: -> Unix local domain socket on device + jdwp: -> JDWP thread on VM process + + or even any one of the local services described below. + +:forward:norebind:; + Same as :forward:; except that it will + fail it there is already a forward connection from . + + Used to implement 'adb forward --no-rebind ' + +:killforward: + Remove any existing forward local connection from . + This is used to implement 'adb forward --remove ' + +:killforward-all + Remove all forward network connections. + This is used to implement 'adb forward --remove-all'. + +:list-forward + List all existing forward connections from this server. + This returns something that looks like the following: + + : The length of the payload, as 4 hexadecimal chars. + : A series of lines of the following format: + + " " " " "\n" + + Where is a device serial number. + is the host-specific endpoint (e.g. tcp:9000). + is the device-specific endpoint. + + Used to implement 'adb forward --list'. + +LOCAL SERVICES: + +All the queries below assumed that you already switched the transport +to a real device, or that you have used a query prefix as described +above. + +shell:command arg1 arg2 ... + Run 'command arg1 arg2 ...' in a shell on the device, and return + its output and error streams. Note that arguments must be separated + by spaces. If an argument contains a space, it must be quoted with + double-quotes. Arguments cannot contain double quotes or things + will go very wrong. + + Note that this is the non-interactive version of "adb shell" + +shell: + Start an interactive shell session on the device. Redirect + stdin/stdout/stderr as appropriate. Note that the ADB server uses + this to implement "adb shell", but will also cook the input before + sending it to the device (see interactive_shell() in commandline.c) + +remount: + Ask adbd to remount the device's filesystem in read-write mode, + instead of read-only. This is usually necessary before performing + an "adb sync" or "adb push" request. + + This request may not succeed on certain builds which do not allow + that. + +dev: + Opens a device file and connects the client directly to it for + read/write purposes. Useful for debugging, but may require special + privileges and thus may not run on all devices. is a full + path from the root of the filesystem. + +tcp: + Tries to connect to tcp port on localhost. + +tcp:: + Tries to connect to tcp port on machine from + the device. This can be useful to debug some networking/proxy + issues that can only be revealed on the device itself. + +local: + Tries to connect to a Unix domain socket on the device + +localreserved: +localabstract: +localfilesystem: + Variants of local: that are used to access other Android + socket namespaces. + +log: + Opens one of the system logs (/dev/log/) and allows the client + to read them directly. Used to implement 'adb logcat'. The stream + will be read-only for the client. + +framebuffer: + This service is used to send snapshots of the framebuffer to a client. + It requires sufficient privileges but works as follow: + + After the OKAY, the service sends 16-byte binary structure + containing the following fields (little-endian format): + + depth: uint32_t: framebuffer depth + size: uint32_t: framebuffer size in bytes + width: uint32_t: framebuffer width in pixels + height: uint32_t: framebuffer height in pixels + + With the current implementation, depth is always 16, and + size is always width*height*2 + + Then, each time the client wants a snapshot, it should send + one byte through the channel, which will trigger the service + to send it 'size' bytes of framebuffer data. + + If the adbd daemon doesn't have sufficient privileges to open + the framebuffer device, the connection is simply closed immediately. + +jdwp: + Connects to the JDWP thread running in the VM of process . + +track-jdwp + This is used to send the list of JDWP pids periodically to the client. + The format of the returned data is the following: + + : the length of all content as a 4-char hexadecimal string + : a series of ASCII lines of the following format: + "\n" + + This service is used by DDMS to know which debuggable processes are running + on the device/emulator. + + 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" + and "adb pull". Since this service is pretty complex, it will be detailed + in a companion document named SYNC.TXT diff --git a/adb/adb.c b/adb/adb.c new file mode 100644 index 0000000..f75c121 --- /dev/null +++ b/adb/adb.c @@ -0,0 +1,1646 @@ +/* + * 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_ADB + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sysdeps.h" +#include "adb.h" +#include "adb_auth.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +#if !ADB_HOST +#include +#include +// #include +#include +#include +#else +#include "usb_vendors.h" +#endif + +#if ADB_TRACE +ADB_MUTEX_DEFINE( D_lock ); +#endif + +int HOST = 0; +int gListenAll = 0; + +static int auth_enabled = 0; + +#if !ADB_HOST +static const char *adb_device_banner = "device"; +#endif + +void fatal(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(-1); +} + +void fatal_errno(const char *fmt, ...) +{ + 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); +} + +int adb_trace_mask; + +/* read a comma/space/colum/semi-column separated list of tags + * from the ADB_TRACE environment variable and build the trace + * mask from it. note that '1' and 'all' are special cases to + * enable all tracing + */ +void adb_trace_init(void) +{ + const char* p = getenv("ADB_TRACE"); + const char* q; + + static const struct { + const char* tag; + int flag; + } tags[] = { + { "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 }, + { NULL, 0 } + }; + + if (p == NULL) + return; + + /* use a comma/column/semi-colum/space separated list */ + while (*p) { + int len, tagn; + + q = strpbrk(p, " ,:;"); + if (q == NULL) { + q = p + strlen(p); + } + len = q - p; + + for (tagn = 0; tags[tagn].tag != NULL; tagn++) + { + int taglen = strlen(tags[tagn].tag); + + if (len == taglen && !memcmp(tags[tagn].tag, p, len) ) + { + int flag = tags[tagn].flag; + if (flag == 0) { + adb_trace_mask = ~0; + return; + } + adb_trace_mask |= (1 << flag); + break; + } + } + p = q; + if (*p) + p++; + } +} + +#if !ADB_HOST +/* + * Implements ADB tracing inside the emulator. + */ + +#include + +/* + * 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 +#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. */ +static 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)); + } +} +#endif /* !ADB_HOST */ + +apacket *get_apacket(void) +{ + apacket *p = malloc(sizeof(apacket)); + if(p == 0) fatal("failed to allocate an apacket"); + memset(p, 0, sizeof(apacket) - MAX_PAYLOAD); + return p; +} + +void put_apacket(apacket *p) +{ + free(p); +} + +void handle_online(atransport *t) +{ + D("adb: online\n"); + t->online = 1; +} + +void handle_offline(atransport *t) +{ + D("adb: offline\n"); + //Close the associated usb + t->online = 0; + run_transport_disconnects(t); +} + +#if DEBUG_PACKETS +#define DUMPMAX 32 +void print_packet(const char *label, apacket *p) +{ + char *tag; + char *x; + unsigned count; + + switch(p->msg.command){ + case A_SYNC: tag = "SYNC"; break; + case A_CNXN: tag = "CNXN" ; break; + case A_OPEN: tag = "OPEN"; break; + case A_OKAY: tag = "OKAY"; break; + case A_CLSE: tag = "CLSE"; break; + case A_WRTE: tag = "WRTE"; break; + case A_AUTH: tag = "AUTH"; break; + default: tag = "????"; break; + } + + fprintf(stderr, "%s: %s %08x %08x %04x \"", + label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length); + count = p->msg.data_length; + x = (char*) p->data; + if(count > DUMPMAX) { + count = DUMPMAX; + tag = "\n"; + } else { + tag = "\"\n"; + } + while(count-- > 0){ + if((*x >= ' ') && (*x < 127)) { + fputc(*x, stderr); + } else { + fputc('.', stderr); + } + x++; + } + fputs(tag, stderr); +} +#endif + +static void send_ready(unsigned local, unsigned remote, atransport *t) +{ + D("Calling send_ready \n"); + apacket *p = get_apacket(); + p->msg.command = A_OKAY; + p->msg.arg0 = local; + p->msg.arg1 = remote; + send_packet(p, t); +} + +static void send_close(unsigned local, unsigned remote, atransport *t) +{ + D("Calling send_close \n"); + apacket *p = get_apacket(); + p->msg.command = A_CLSE; + p->msg.arg0 = local; + p->msg.arg1 = remote; + send_packet(p, t); +} + +static size_t fill_connect_data(char *buf, size_t bufsize) +{ +#if ADB_HOST + return snprintf(buf, bufsize, "host::") + 1; +#else + static const char *cnxn_props[] = { + "ro.product.name", + "ro.product.model", + "ro.product.device", + }; + static const int num_cnxn_props = ARRAY_SIZE(cnxn_props); + int i; + size_t remaining = bufsize; + size_t len; + + len = snprintf(buf, remaining, "%s::", adb_device_banner); + remaining -= len; + buf += len; + for (i = 0; i < num_cnxn_props; i++) { + char value[PROPERTY_VALUE_MAX]; + property_get(cnxn_props[i], value, ""); + len = snprintf(buf, remaining, "%s=%s;", cnxn_props[i], value); + remaining -= len; + buf += len; + } + + return bufsize - remaining + 1; +#endif +} + +static void send_connect(atransport *t) +{ + D("Calling send_connect \n"); + apacket *cp = get_apacket(); + cp->msg.command = A_CNXN; + cp->msg.arg0 = A_VERSION; + cp->msg.arg1 = MAX_PAYLOAD; + cp->msg.data_length = fill_connect_data((char *)cp->data, + sizeof(cp->data)); + send_packet(cp, t); +} + +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); +} + +static 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); +} + +static 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, sizeof(p->data)); + 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); +} + +static char *connection_state_name(atransport *t) +{ + if (t == NULL) { + return "unknown"; + } + + switch(t->connection_state) { + case CS_BOOTLOADER: + return "bootloader"; + case CS_DEVICE: + return "device"; + case CS_RECOVERY: + return "recovery"; + case CS_SIDELOAD: + return "sideload"; + case CS_OFFLINE: + return "offline"; + case CS_UNAUTHORIZED: + return "unauthorized"; + default: + return "unknown"; + } +} + +/* qual_overwrite is used to overwrite a qualifier string. dst is a + * pointer to a char pointer. It is assumed that if *dst is non-NULL, it + * was malloc'ed and needs to freed. *dst will be set to a dup of src. + */ +static void qual_overwrite(char **dst, const char *src) +{ + if (!dst) + return; + + free(*dst); + *dst = NULL; + + if (!src || !*src) + return; + + *dst = strdup(src); +} + +void parse_banner(char *banner, atransport *t) +{ + static const char *prop_seps = ";"; + static const char key_val_sep = '='; + char *cp; + char *type; + + D("parse_banner: %s\n", banner); + type = banner; + cp = strchr(type, ':'); + if (cp) { + *cp++ = 0; + /* Nothing is done with second field. */ + cp = strchr(cp, ':'); + if (cp) { + char *save; + char *key; + key = adb_strtok_r(cp + 1, prop_seps, &save); + while (key) { + cp = strchr(key, key_val_sep); + if (cp) { + *cp++ = '\0'; + if (!strcmp(key, "ro.product.name")) + qual_overwrite(&t->product, cp); + else if (!strcmp(key, "ro.product.model")) + qual_overwrite(&t->model, cp); + else if (!strcmp(key, "ro.product.device")) + qual_overwrite(&t->device, cp); + } + key = adb_strtok_r(NULL, prop_seps, &save); + } + } + } + + if(!strcmp(type, "bootloader")){ + D("setting connection_state to CS_BOOTLOADER\n"); + t->connection_state = CS_BOOTLOADER; + update_transports(); + return; + } + + if(!strcmp(type, "device")) { + D("setting connection_state to CS_DEVICE\n"); + t->connection_state = CS_DEVICE; + update_transports(); + return; + } + + if(!strcmp(type, "recovery")) { + D("setting connection_state to CS_RECOVERY\n"); + t->connection_state = CS_RECOVERY; + update_transports(); + return; + } + + if(!strcmp(type, "sideload")) { + D("setting connection_state to CS_SIDELOAD\n"); + t->connection_state = CS_SIDELOAD; + update_transports(); + return; + } + + t->connection_state = CS_HOST; +} + +void handle_packet(apacket *p, atransport *t) +{ + asocket *s; + + D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0], + ((char*) (&(p->msg.command)))[1], + ((char*) (&(p->msg.command)))[2], + ((char*) (&(p->msg.command)))[3]); + print_packet("recv", p); + + switch(p->msg.command){ + case A_SYNC: + if(p->msg.arg0){ + send_packet(p, t); + if(HOST) send_connect(t); + } else { + t->connection_state = CS_OFFLINE; + handle_offline(t); + send_packet(p, t); + } + return; + + case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */ + /* XXX verify version, etc */ + if(t->connection_state != CS_OFFLINE) { + t->connection_state = CS_OFFLINE; + handle_offline(t); + } + + parse_banner((char*) p->data, t); + + if (HOST || !auth_enabled) { + handle_online(t); + if(!HOST) send_connect(t); + } else { + send_auth_request(t); + } + break; + + case A_AUTH: + if (p->msg.arg0 == ADB_AUTH_TOKEN) { + t->connection_state = CS_UNAUTHORIZED; + t->key = adb_auth_nextkey(t->key); + if (t->key) { + 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; + + case A_OPEN: /* OPEN(local-id, 0, "destination") */ + if (t->online) { + 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) { + send_close(0, p->msg.arg0, t); + } else { + s->peer = create_remote_socket(p->msg.arg0, t); + s->peer->peer = s; + send_ready(s->id, s->peer->id, t); + s->ready(s); + } + } + break; + + case A_OKAY: /* READY(local-id, remote-id, "") */ + if (t->online) { + if((s = find_local_socket(p->msg.arg1))) { + if(s->peer == 0) { + s->peer = create_remote_socket(p->msg.arg0, t); + s->peer->peer = s; + } + s->ready(s); + } + } + break; + + case A_CLSE: /* CLOSE(local-id, remote-id, "") */ + if (t->online) { + if((s = find_local_socket(p->msg.arg1))) { + s->close(s); + } + } + break; + + case A_WRTE: + if (t->online) { + if((s = find_local_socket(p->msg.arg1))) { + unsigned rid = p->msg.arg0; + p->len = p->msg.data_length; + + if(s->enqueue(s, p) == 0) { + D("Enqueue the socket\n"); + send_ready(s->id, rid, t); + } + return; + } + } + break; + + default: + printf("handle_packet: what is %08x?!\n", p->msg.command); + } + + put_apacket(p); +} + +alistener listener_list = { + .next = &listener_list, + .prev = &listener_list, +}; + +static void ss_listener_event_func(int _fd, unsigned ev, void *_l) +{ + asocket *s; + + if(ev & FDE_READ) { + struct sockaddr addr; + socklen_t alen; + int fd; + + 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) { + connect_to_smartsocket(s); + return; + } + + adb_close(fd); + } +} + +static void listener_event_func(int _fd, unsigned ev, void *_l) +{ + alistener *l = _l; + asocket *s; + + if(ev & FDE_READ) { + struct sockaddr addr; + socklen_t alen; + int fd; + + alen = sizeof(addr); + fd = adb_socket_accept(_fd, &addr, &alen); + if(fd < 0) return; + + s = create_local_socket(fd); + if(s) { + s->transport = l->transport; + connect_to_remote(s, l->connect_to); + return; + } + + adb_close(fd); + } +} + +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* _l, atransport* t) +{ + alistener* l = _l; + + free_listener(l); +} + +int local_name_to_fd(const char *name) +{ + int port; + + if(!strncmp("tcp:", name, 4)){ + int ret; + port = atoi(name + 4); + + if (gListenAll > 0) { + ret = socket_inaddr_any_server(port, SOCK_STREAM); + } else { + ret = socket_loopback_server(port, SOCK_STREAM); + } + + return ret; + } +#ifndef HAVE_WIN32_IPC /* no Unix-domain sockets on Win32 */ + // It's non-sensical to support the "reserved" space on the adb host side + if(!strncmp(name, "local:", 6)) { + return socket_local_server(name + 6, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + } else if(!strncmp(name, "localabstract:", 14)) { + return socket_local_server(name + 14, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + } else if(!strncmp(name, "localfilesystem:", 16)) { + return socket_local_server(name + 16, + ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM); + } + +#endif + printf("unknown local portname '%s'\n", name); + return -1; +} + +// Write a single line describing a listener to a user-provided buffer. +// Appends a trailing zero, even in case of truncation, but the function +// returns the full line length. +// If |buffer| is NULL, does not write but returns required size. +static int format_listener(alistener* l, char* buffer, size_t buffer_len) { + // Format is simply: + // + // " " " " "\n" + // + int local_len = strlen(l->local_name); + int connect_len = strlen(l->connect_to); + int serial_len = strlen(l->transport->serial); + + if (buffer != NULL) { + snprintf(buffer, buffer_len, "%s %s %s\n", + l->transport->serial, l->local_name, l->connect_to); + } + // NOTE: snprintf() on Windows returns -1 in case of truncation, so + // return the computed line length instead. + return local_len + connect_len + serial_len + 3; +} + +// Write the list of current listeners (network redirections) into a +// user-provided buffer. Appends a trailing zero, even in case of +// trunctaion, but return the full size in bytes. +// If |buffer| is NULL, does not write but returns required size. +static int format_listeners(char* buf, size_t buflen) +{ + alistener* l; + int result = 0; + for (l = listener_list.next; l != &listener_list; l = l->next) { + // Ignore special listeners like those for *smartsocket* + if (l->connect_to[0] == '*') + continue; + int len = format_listener(l, buf, buflen); + // Ensure there is space for the trailing zero. + result += len; + if (buf != NULL) { + buf += len; + buflen -= len; + if (buflen <= 0) + break; + } + } + return result; +} + +static int 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); + return 0; + } + } + return -1; +} + +static void remove_all_listeners(void) +{ + alistener *l, *l_next; + for (l = listener_list.next; l != &listener_list; l = l_next) { + l_next = l->next; + // Never remove smart sockets. + if (l->connect_to[0] == '*') + continue; + listener_disconnect(l, l->transport); + } +} + +// error/status codes for install_listener. +typedef enum { + INSTALL_STATUS_OK = 0, + INSTALL_STATUS_INTERNAL_ERROR = -1, + INSTALL_STATUS_CANNOT_BIND = -2, + INSTALL_STATUS_CANNOT_REBIND = -3, +} install_status_t; + +static install_status_t install_listener(const char *local_name, + const char *connect_to, + atransport* transport, + int no_rebind) +{ + alistener *l; + + //printf("install_listener('%s','%s')\n", local_name, connect_to); + + for(l = listener_list.next; l != &listener_list; l = l->next){ + if(strcmp(local_name, l->local_name) == 0) { + char *cto; + + /* can't repurpose a smartsocket */ + if(l->connect_to[0] == '*') { + return INSTALL_STATUS_INTERNAL_ERROR; + } + + /* can't repurpose a listener if 'no_rebind' is true */ + if (no_rebind) { + return INSTALL_STATUS_CANNOT_REBIND; + } + + cto = strdup(connect_to); + if(cto == 0) { + return INSTALL_STATUS_INTERNAL_ERROR; + } + + //printf("rebinding '%s' to '%s'\n", local_name, connect_to); + free((void*) l->connect_to); + l->connect_to = cto; + if (l->transport != transport) { + remove_transport_disconnect(l->transport, &l->disconnect); + l->transport = transport; + add_transport_disconnect(l->transport, &l->disconnect); + } + return INSTALL_STATUS_OK; + } + } + + if((l = calloc(1, sizeof(alistener))) == 0) goto nomem; + if((l->local_name = strdup(local_name)) == 0) goto nomem; + if((l->connect_to = strdup(connect_to)) == 0) goto nomem; + + + l->fd = local_name_to_fd(local_name); + if(l->fd < 0) { + free((void*) l->local_name); + free((void*) l->connect_to); + free(l); + printf("cannot bind '%s'\n", local_name); + return -2; + } + + close_on_exec(l->fd); + if(!strcmp(l->connect_to, "*smartsocket*")) { + fdevent_install(&l->fde, l->fd, ss_listener_event_func, l); + } else { + fdevent_install(&l->fde, l->fd, listener_event_func, l); + } + fdevent_set(&l->fde, FDE_READ); + + l->next = &listener_list; + l->prev = listener_list.prev; + l->next->prev = l; + l->prev->next = l; + l->transport = transport; + + if (transport) { + l->disconnect.opaque = l; + l->disconnect.func = listener_disconnect; + add_transport_disconnect(transport, &l->disconnect); + } + return INSTALL_STATUS_OK; + +nomem: + fatal("cannot allocate listener"); + return INSTALL_STATUS_INTERNAL_ERROR; +} + +#ifdef HAVE_WIN32_PROC +static BOOL WINAPI ctrlc_handler(DWORD type) +{ + exit(STATUS_CONTROL_C_EXIT); + return TRUE; +} +#endif + +static void adb_cleanup(void) +{ + usb_cleanup(); +} + +void start_logging(void) +{ +#ifdef HAVE_WIN32_PROC + char temp[ MAX_PATH ]; + FILE* fnul; + FILE* flog; + + GetTempPath( sizeof(temp) - 8, temp ); + strcat( temp, "adb.log" ); + + /* Win32 specific redirections */ + fnul = fopen( "NUL", "rt" ); + if (fnul != NULL) + stdin[0] = fnul[0]; + + flog = fopen( temp, "at" ); + if (flog == NULL) + flog = fnul; + + setvbuf( flog, NULL, _IONBF, 0 ); + + stdout[0] = flog[0]; + stderr[0] = flog[0]; + fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); +#else + int fd; + + fd = unix_open("/dev/null", O_RDONLY); + dup2(fd, 0); + adb_close(fd); + + fd = unix_open("/tmp/adb.log", O_WRONLY | O_CREAT | O_APPEND, 0640); + if(fd < 0) { + fd = unix_open("/dev/null", O_WRONLY); + } + dup2(fd, 1); + dup2(fd, 2); + adb_close(fd); + fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); +#endif +} + +#if !ADB_HOST +void start_device_log(void) +{ + int fd; + char path[PATH_MAX]; + struct tm now; + time_t t; + char value[PROPERTY_VALUE_MAX]; + + // read the trace mask from persistent property persist.adb.trace_mask + // give up if the property is not set or cannot be parsed + property_get("persist.adb.trace_mask", value, ""); + if (sscanf(value, "%x", &adb_trace_mask) != 1) + return; + + adb_mkdir("/data/adb", 0775); + tzset(); + time(&t); + localtime_r(&t, &now); + strftime(path, sizeof(path), + "/data/adb/adb-%Y-%m-%d-%H-%M-%S.txt", + &now); + fd = unix_open(path, O_WRONLY | O_CREAT | O_TRUNC, 0640); + if (fd < 0) + return; + + // redirect stdout and stderr to the log file + dup2(fd, 1); + dup2(fd, 2); + fprintf(stderr,"--- adb starting (pid %d) ---\n", getpid()); + adb_close(fd); + + fd = unix_open("/dev/null", O_RDONLY); + dup2(fd, 0); + adb_close(fd); +} +#endif + +#if ADB_HOST + +#ifdef WORKAROUND_BUG6558362 +#include +#define AFFINITY_ENVVAR "ADB_CPU_AFFINITY_BUG6558362" +void adb_set_affinity(void) +{ + cpu_set_t cpu_set; + const char* cpunum_str = getenv(AFFINITY_ENVVAR); + char* strtol_res; + int cpu_num; + + if (!cpunum_str || !*cpunum_str) + return; + 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_ENVVAR); + + 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]); +} +#endif + +int launch_server(int server_port) +{ +#ifdef HAVE_WIN32_PROC + /* 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 pipe_read, pipe_write; + HANDLE stdout_handle, stderr_handle; + SECURITY_ATTRIBUTES sa; + STARTUPINFO startup; + PROCESS_INFORMATION pinfo; + char program_path[ MAX_PATH ]; + int ret; + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + /* create pipe, and ensure its read handle isn't inheritable */ + ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 ); + if (!ret) { + fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() ); + return -1; + } + + SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 ); + + /* Some programs want to launch an adb command and collect its output by + * calling CreateProcess with inheritable stdout/stderr handles, then + * using read() to get its output. When this happens, the stdout/stderr + * handles passed to the adb client process will also be inheritable. + * When starting the adb server here, care must be taken to reset them + * to non-inheritable. + * Otherwise, something bad happens: even if the adb command completes, + * 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). + */ + 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 ); + } + + ZeroMemory( &startup, sizeof(startup) ); + startup.cb = sizeof(startup); + startup.hStdInput = GetStdHandle( STD_INPUT_HANDLE ); + startup.hStdOutput = pipe_write; + startup.hStdError = GetStdHandle( STD_ERROR_HANDLE ); + startup.dwFlags = STARTF_USESTDHANDLES; + + ZeroMemory( &pinfo, sizeof(pinfo) ); + + /* get path of current program */ + GetModuleFileName( NULL, program_path, sizeof(program_path) ); + + ret = CreateProcess( + program_path, /* program path */ + "adb fork-server server", + /* the fork-server argument will set the + debug = 2 in the child */ + NULL, /* process handle is not inheritable */ + NULL, /* thread handle is not inheritable */ + TRUE, /* yes, inherit some handles */ + DETACHED_PROCESS, /* the new process doesn't have a console */ + NULL, /* use parent's environment block */ + NULL, /* use parent's starting directory */ + &startup, /* startup info, i.e. std handles */ + &pinfo ); + + CloseHandle( pipe_write ); + + if (!ret) { + fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() ); + CloseHandle( pipe_read ); + return -1; + } + + CloseHandle( pinfo.hProcess ); + CloseHandle( pinfo.hThread ); + + /* wait for the "OK\n" message */ + { + char temp[3]; + DWORD count; + + ret = ReadFile( pipe_read, temp, 3, &count, NULL ); + CloseHandle( pipe_read ); + if ( !ret ) { + fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() ); + return -1; + } + if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { + fprintf(stderr, "ADB server didn't ACK\n" ); + return -1; + } + } +#elif defined(HAVE_FORKEXEC) + char path[PATH_MAX]; + int fd[2]; + + // set up a pipe so the child can tell us when it is ready. + // fd[0] will be parent's end, and fd[1] will get mapped to stderr in the child. + if (pipe(fd)) { + fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno); + return -1; + } + get_my_path(path, PATH_MAX); + pid_t pid = fork(); + if(pid < 0) return -1; + + if (pid == 0) { + // child side of the fork + + // redirect stderr to the pipe + // we use stderr instead of stdout due to stdout's buffering behavior. + adb_close(fd[0]); + dup2(fd[1], STDERR_FILENO); + adb_close(fd[1]); + + char str_port[30]; + snprintf(str_port, sizeof(str_port), "%d", server_port); + // child process + int result = execl(path, "adb", "-P", str_port, "fork-server", "server", NULL); + // this should not return + fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno); + } else { + // parent side of the fork + + char temp[3]; + + temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C'; + // wait for the "OK\n" message + adb_close(fd[1]); + int ret = adb_read(fd[0], temp, 3); + int saved_errno = errno; + adb_close(fd[0]); + if (ret < 0) { + fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", saved_errno); + return -1; + } + if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') { + fprintf(stderr, "ADB server didn't ACK\n" ); + return -1; + } + + setsid(); + } +#else +#error "cannot implement background server start on this platform" +#endif + return 0; +} +#endif + +/* Constructs a local name of form tcp:port. + * target_str points to the target string, it's content will be overwritten. + * target_size is the capacity of the target string. + * server_port is the port number to use for the local name. + */ +void build_local_name(char* target_str, size_t target_size, int server_port) +{ + snprintf(target_str, target_size, "tcp:%d", server_port); +} + +#if !ADB_HOST + +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) { + return; + } +#endif + int i; + for (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)) { + exit(1); + } + } +} + +static int should_drop_privileges() { +#ifndef ALLOW_ADBD_ROOT + return 1; +#else /* ALLOW_ADBD_ROOT */ + int secure = 0; + char value[PROPERTY_VALUE_MAX]; + + /* run adbd in secure mode if ro.secure is set and + ** we are not in the emulator + */ + property_get("ro.kernel.qemu", value, ""); + if (strcmp(value, "1") != 0) { + property_get("ro.secure", value, "1"); + if (strcmp(value, "1") == 0) { + // don't run as root if ro.secure is set... + secure = 1; + + // ... except we allow running as root in userdebug builds if the + // service.adb.root property has been set by the "adb root" command + property_get("ro.debuggable", value, ""); + if (strcmp(value, "1") == 0) { + property_get("service.adb.root", value, ""); + if (strcmp(value, "1") == 0) { + secure = 0; + } + } + } + } + return secure; +#endif /* ALLOW_ADBD_ROOT */ +} +#endif /* !ADB_HOST */ + +int adb_main(int is_daemon, int server_port) +{ +#if !ADB_HOST + int port; + char value[PROPERTY_VALUE_MAX]; + + umask(000); +#endif + + atexit(adb_cleanup); +#ifdef HAVE_WIN32_PROC + SetConsoleCtrlHandler( ctrlc_handler, TRUE ); +#elif defined(HAVE_FORKEXEC) + // No SIGCHLD. Let the service subproc handle its children. + signal(SIGPIPE, SIG_IGN); +#endif + + init_transport_registration(); + +#if ADB_HOST + HOST = 1; + +#ifdef WORKAROUND_BUG6558362 + if(is_daemon) adb_set_affinity(); +#endif + usb_vendors_init(); + usb_init(); + local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); + adb_auth_init(); + + char local_name[30]; + build_local_name(local_name, sizeof(local_name), server_port); + if(install_listener(local_name, "*smartsocket*", NULL, 0)) { + exit(1); + } +#else + property_get("ro.adb.secure", value, "0"); + auth_enabled = !strcmp(value, "1"); + if (auth_enabled) + adb_auth_init(); + + // Our external storage path may be different than apps, since + // we aren't able to bind mount after dropping root. + const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE"); + if (NULL != adb_external_storage) { + setenv("EXTERNAL_STORAGE", adb_external_storage, 1); + } else { + D("Warning: ADB_EXTERNAL_STORAGE is not set. Leaving EXTERNAL_STORAGE" + " unchanged.\n"); + } + + /* 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(); + + /* 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 (netcfg, ping) + ** AID_GRAPHICS to access the frame buffer + ** 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_GRAPHICS, + 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) { + exit(1); + } + + /* then switch user and group to "shell" */ + if (setgid(AID_SHELL) != 0) { + exit(1); + } + if (setuid(AID_SHELL) != 0) { + exit(1); + } + + D("Local port disabled\n"); + } else { + char local_name[30]; + build_local_name(local_name, sizeof(local_name), server_port); + if(install_listener(local_name, "*smartsocket*", NULL, 0)) { + exit(1); + } + } + + int usb = 0; + if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) { + // listen on USB + usb_init(); + usb = 1; + } + + // 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. + property_get("service.adb.tcp.port", value, ""); + if (!value[0]) { + property_get("persist.adb.tcp.port", value, ""); + } + if (sscanf(value, "%d", &port) == 1 && port > 0) { + printf("using port=%d\n", port); + // listen on TCP port specified by service.adb.tcp.port property + local_init(port); + } else if (!usb) { + // listen on default port + local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT); + } + + D("adb_main(): pre init_jdwp()\n"); + init_jdwp(); + D("adb_main(): post init_jdwp()\n"); +#endif + + if (is_daemon) + { + // inform our parent that we are up and running. +#ifdef HAVE_WIN32_PROC + DWORD count; + WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), "OK\n", 3, &count, NULL ); +#elif defined(HAVE_FORKEXEC) + fprintf(stderr, "OK\n"); +#endif + start_logging(); + } + D("Event loop starting\n"); + + fdevent_loop(); + + usb_cleanup(); + + return 0; +} + +int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s) +{ + atransport *transport = NULL; + char buf[4096]; + + if(!strcmp(service, "kill")) { + fprintf(stderr,"adb server killed by remote request\n"); + fflush(stdout); + adb_write(reply_fd, "OKAY", 4); + usb_cleanup(); + exit(0); + } + +#if ADB_HOST + // "transport:" is used for switching transport with a specified serial number + // "transport-usb:" is used for switching transport to the only USB transport + // "transport-local:" is used for switching transport to the only local transport + // "transport-any:" is used for switching transport to the only transport + if (!strncmp(service, "transport", strlen("transport"))) { + char* error_string = "unknown failure"; + transport_type type = kTransportAny; + + if (!strncmp(service, "transport-usb", strlen("transport-usb"))) { + type = kTransportUsb; + } else if (!strncmp(service, "transport-local", strlen("transport-local"))) { + type = kTransportLocal; + } else if (!strncmp(service, "transport-any", strlen("transport-any"))) { + type = kTransportAny; + } else if (!strncmp(service, "transport:", strlen("transport:"))) { + service += strlen("transport:"); + serial = service; + } + + transport = acquire_one_transport(CS_ANY, type, serial, &error_string); + + if (transport) { + s->transport = transport; + adb_write(reply_fd, "OKAY", 4); + } else { + sendfailmsg(reply_fd, error_string); + } + return 1; + } + + // return a list of all connected devices + if (!strncmp(service, "devices", 7)) { + char buffer[4096]; + int use_long = !strcmp(service+7, "-l"); + if (use_long || service[7] == 0) { + memset(buf, 0, sizeof(buf)); + memset(buffer, 0, sizeof(buffer)); + D("Getting device list \n"); + list_transports(buffer, sizeof(buffer), use_long); + snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer); + D("Wrote device list \n"); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + } + + // remove TCP transport + if (!strncmp(service, "disconnect:", 11)) { + char buffer[4096]; + memset(buffer, 0, sizeof(buffer)); + char* serial = service + 11; + if (serial[0] == 0) { + // disconnect from all TCP devices + unregister_all_tcp_transports(); + } else { + char hostbuf[100]; + // assume port 5555 if no port is specified + if (!strchr(serial, ':')) { + snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial); + serial = hostbuf; + } + atransport *t = find_transport(serial); + + if (t) { + unregister_transport(t); + } else { + snprintf(buffer, sizeof(buffer), "No such device %s", serial); + } + } + + snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + + // returns our value for ADB_SERVER_VERSION + if (!strcmp(service, "version")) { + char version[12]; + snprintf(version, sizeof version, "%04x", ADB_SERVER_VERSION); + snprintf(buf, sizeof buf, "OKAY%04x%s", (unsigned)strlen(version), version); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + + if(!strncmp(service,"get-serialno",strlen("get-serialno"))) { + char *out = "unknown"; + transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); + if (transport && transport->serial) { + out = transport->serial; + } + snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + if(!strncmp(service,"get-devpath",strlen("get-devpath"))) { + char *out = "unknown"; + transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); + if (transport && transport->devpath) { + out = transport->devpath; + } + snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + // 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; + } +#endif // ADB_HOST + + if(!strcmp(service,"list-forward")) { + // Create the list of forward redirections. + char header[9]; + int buffer_size = format_listeners(NULL, 0); + // Add one byte for the trailing zero. + char* buffer = malloc(buffer_size+1); + (void) format_listeners(buffer, buffer_size+1); + snprintf(header, sizeof header, "OKAY%04x", buffer_size); + writex(reply_fd, header, 8); + writex(reply_fd, buffer, buffer_size); + free(buffer); + return 0; + } + + if (!strcmp(service,"killforward-all")) { + remove_all_listeners(); + adb_write(reply_fd, "OKAYOKAY", 8); + return 0; + } + + if(!strncmp(service,"forward:",8) || + !strncmp(service,"killforward:",12)) { + char *local, *remote, *err; + int r; + atransport *transport; + + int createForward = strncmp(service,"kill",4); + int no_rebind = 0; + + local = strchr(service, ':') + 1; + + // Handle forward:norebind:... here + if (createForward && !strncmp(local, "norebind:", 9)) { + no_rebind = 1; + local = strchr(local, ':') + 1; + } + + remote = strchr(local,';'); + + if (createForward) { + // Check forward: parameter format: ';' + if(remote == 0) { + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } + + *remote++ = 0; + if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){ + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } + } else { + // Check killforward: parameter format: '' + if (local[0] == 0) { + sendfailmsg(reply_fd, "malformed forward spec"); + return 0; + } + } + + transport = acquire_one_transport(CS_ANY, ttype, serial, &err); + if (!transport) { + sendfailmsg(reply_fd, err); + return 0; + } + + if (createForward) { + r = install_listener(local, remote, transport, no_rebind); + } else { + r = remove_listener(local, transport); + } + if(r == 0) { + /* 1st OKAY is connect, 2nd OKAY is status */ + writex(reply_fd, "OKAYOKAY", 8); + return 0; + } + + if (createForward) { + const char* message; + switch (r) { + case INSTALL_STATUS_CANNOT_BIND: + message = "cannot bind to socket"; + break; + case INSTALL_STATUS_CANNOT_REBIND: + message = "cannot rebind existing socket"; + break; + default: + message = "internal error"; + } + sendfailmsg(reply_fd, message); + } else { + sendfailmsg(reply_fd, "cannot remove listener"); + } + return 0; + } + + if(!strncmp(service,"get-state",strlen("get-state"))) { + transport = acquire_one_transport(CS_ANY, ttype, serial, NULL); + char *state = connection_state_name(transport); + snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(state),state); + writex(reply_fd, buf, strlen(buf)); + return 0; + } + return -1; +} + +#if !ADB_HOST +int recovery_mode = 0; +#endif + +int main(int argc, char **argv) +{ +#if ADB_HOST + adb_sysdeps_init(); + adb_trace_init(); + D("Handling commandline()\n"); + return adb_commandline(argc - 1, argv + 1); +#else + /* If adbd runs inside the emulator this will enable adb tracing via + * adb-debug qemud service in the emulator. */ + adb_qemu_trace_init(); + if((argc > 1) && (!strcmp(argv[1],"recovery"))) { + adb_device_banner = "recovery"; + recovery_mode = 1; + } + + start_device_log(); + D("Handling main()\n"); + return adb_main(0, DEFAULT_ADB_PORT); +#endif +} diff --git a/adb/adb.h b/adb/adb.h new file mode 100644 index 0000000..622ca70 --- /dev/null +++ b/adb/adb.h @@ -0,0 +1,489 @@ +/* + * 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 __ADB_H +#define __ADB_H + +#include + +#include "transport.h" /* readx(), writex() */ + +#define MAX_PAYLOAD 4096 + +#define A_SYNC 0x434e5953 +#define A_CNXN 0x4e584e43 +#define A_OPEN 0x4e45504f +#define A_OKAY 0x59414b4f +#define A_CLSE 0x45534c43 +#define A_WRTE 0x45545257 +#define A_AUTH 0x48545541 + +#define A_VERSION 0x01000000 // ADB protocol version + +#define ADB_VERSION_MAJOR 1 // Used for help/version information +#define ADB_VERSION_MINOR 0 // Used for help/version information + +#define ADB_SERVER_VERSION 31 // Increment this when we want to force users to start a new adb server + +typedef struct amessage amessage; +typedef struct apacket apacket; +typedef struct asocket asocket; +typedef struct alistener alistener; +typedef struct aservice aservice; +typedef struct atransport atransport; +typedef struct adisconnect adisconnect; +typedef struct usb_handle 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 */ +}; + +struct apacket +{ + apacket *next; + + unsigned len; + unsigned char *ptr; + + amessage msg; + unsigned 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); + + /* 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; +}; + + +/* the adisconnect structure is used to record a callback that +** will be called whenever a transport is disconnected (e.g. by the user) +** this should be used to cleanup objects that depend on the +** transport (e.g. remote sockets, listeners, etc...) +*/ +struct adisconnect +{ + void (*func)(void* opaque, atransport* t); + void* opaque; + adisconnect* next; + adisconnect* prev; +}; + + +/* a transport object models the connection to a remote device or emulator +** there is one transport per connected device/emulator. a "local transport" +** connects through TCP (for the emulator), while a "usb transport" through +** USB (for real devices) +** +** note that kTransportHost doesn't really correspond to a real transport +** object, it's a special value used to indicate that a client wants to +** connect to a service implemented within the ADB server itself. +*/ +typedef enum transport_type { + kTransportUsb, + kTransportLocal, + kTransportAny, + kTransportHost, +} transport_type; + +#define TOKEN_SIZE 20 + +struct atransport +{ + atransport *next; + atransport *prev; + + int (*read_from_remote)(apacket *p, atransport *t); + int (*write_to_remote)(apacket *p, atransport *t); + void (*close)(atransport *t); + void (*kick)(atransport *t); + + int fd; + int transport_socket; + fdevent transport_fde; + int ref_count; + unsigned sync_token; + int connection_state; + int online; + transport_type type; + + /* usb handle or socket fd as needed */ + usb_handle *usb; + int sfd; + + /* used to identify transports for clients */ + char *serial; + char *product; + char *model; + char *device; + char *devpath; + int adb_port; // Use for emulators (local transport) + + /* a list of adisconnect callbacks called when the transport is kicked */ + int kicked; + adisconnect disconnects; + + void *key; + unsigned char token[TOKEN_SIZE]; + fdevent auth_fde; + unsigned failed_auth_attempts; +}; + + +/* 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; + + const char *local_name; + const char *connect_to; + atransport *transport; + adisconnect disconnect; +}; + + +void print_packet(const char *label, apacket *p); + +asocket *find_local_socket(unsigned id); +void install_local_socket(asocket *s); +void remove_socket(asocket *s); +void close_all_sockets(atransport *t); + +#define LOCAL_CLIENT_PREFIX "emulator-" + +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, ...); + +void handle_packet(apacket *p, atransport *t); +void send_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); + + +/* transports are ref-counted +** get_device_transport does an acquire on your behalf before returning +*/ +void init_transport_registration(void); +int list_transports(char *buf, size_t bufsize, int long_listing); +void update_transports(void); + +asocket* create_device_tracker(void); + +/* Obtain a transport from the available transports. +** If state is != CS_ANY, 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. +*/ +atransport *acquire_one_transport(int state, transport_type ttype, const char* serial, char **error_out); +void add_transport_disconnect( atransport* t, adisconnect* dis ); +void remove_transport_disconnect( atransport* t, adisconnect* dis ); +void run_transport_disconnects( atransport* t ); +void kick_transport( atransport* t ); + +/* initialize a transport object's func pointers and state */ +#if ADB_HOST +int get_available_local_transport_index(); +#endif +int init_socket_transport(atransport *t, int s, int port, int local); +void init_usb_transport(atransport *t, usb_handle *usb, int state); + +/* for MacOS X cleanup */ +void close_usb_devices(); + +/* cause new transports to be init'd and added to the list */ +int register_socket_transport(int s, const char *serial, int port, int local); + +/* these should only be used for the "adb disconnect" command */ +void unregister_transport(atransport *t); +void unregister_all_tcp_transports(); + +void register_usb_transport(usb_handle *h, const char *serial, const char *devpath, unsigned writeable); + +/* this should only be used for transports with connection_state == CS_NOPERM */ +void unregister_usb_transport(usb_handle *usb); + +atransport *find_transport(const char *serial); +#if ADB_HOST +atransport* find_emulator_transport_by_adb_port(int adb_port); +#endif + +int service_to_fd(const char *name); +#if ADB_HOST +asocket *host_service_to_socket(const char* name, const char *serial); +#endif + +#if !ADB_HOST +int init_jdwp(void); +asocket* create_jdwp_service_socket(); +asocket* create_jdwp_tracker_service_socket(); +int create_jdwp_connection_fd(int jdwp_pid); +#endif + +#if !ADB_HOST +typedef enum { + BACKUP, + RESTORE +} BackupOperation; +int backup_service(BackupOperation operation, char* args); +void framebuffer_service(int fd, void *cookie); +void log_service(int fd, void *cookie); +void remount_service(int fd, void *cookie); +char * get_log_file_path(const char * log_name); +#endif + +/* packet allocator */ +apacket *get_apacket(void); +void put_apacket(apacket *p); + +int check_header(apacket *p); +int check_data(apacket *p); + +/* define ADB_TRACE to 1 to enable tracing support, or 0 to disable it */ + +#define ADB_TRACE 1 + +/* 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 + */ +typedef enum { + 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, +} AdbTrace; + +#if ADB_TRACE + +#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). + */ + +/* 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 */ + + extern int adb_trace_mask; + extern unsigned char adb_trace_output_count; + void adb_trace_init(void); + +# define ADB_TRACING ((adb_trace_mask & (1 << TRACE_TAG)) != 0) + + /* you must define TRACE_TAG before using this macro */ +# define D(...) \ + do { \ + if (ADB_TRACING) { \ + int save_errno = errno; \ + adb_mutex_lock(&D_lock); \ + fprintf(stderr, "%s::%s():", \ + __FILE__, __FUNCTION__); \ + 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(...) ((void)0) +# define DR(...) ((void)0) +# define ADB_TRACING 0 +#endif + + +#if !DEBUG_PACKETS +#define print_packet(tag,p) do {} while (0) +#endif + +#if ADB_HOST_ON_TARGET +/* adb and adbd are coexisting on the target, so use 5038 for adb + * to avoid conflicting with adbd's usage of 5037 + */ +# define DEFAULT_ADB_PORT 5038 +#else +# define DEFAULT_ADB_PORT 5037 +#endif + +#define DEFAULT_ADB_LOCAL_TRANSPORT_PORT 5555 + +#define ADB_CLASS 0xff +#define ADB_SUBCLASS 0x42 +#define ADB_PROTOCOL 0x1 + + +void local_init(int port); +int local_connect(int port); +int local_connect_arbitrary_ports(int console_port, int adb_port); + +/* usb host/client interface */ +void usb_init(); +void usb_cleanup(); +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 + +unsigned host_to_le32(unsigned n); +int adb_commandline(int argc, char **argv); + +int connection_state(atransport *t); + +#define CS_ANY -1 +#define CS_OFFLINE 0 +#define CS_BOOTLOADER 1 +#define CS_DEVICE 2 +#define CS_HOST 3 +#define CS_RECOVERY 4 +#define CS_NOPERM 5 /* Insufficient permissions to communicate with the device */ +#define CS_SIDELOAD 6 +#define CS_UNAUTHORIZED 7 + +extern int HOST; +extern int SHELL_EXIT_NOTIFY_FD; + +#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 + +#define USB_FFS_ADB_EP0 USB_FFS_ADB_EP(ep0) +#define USB_FFS_ADB_OUT USB_FFS_ADB_EP(ep1) +#define USB_FFS_ADB_IN USB_FFS_ADB_EP(ep2) +#endif + +int sendfailmsg(int fd, const char *reason); +int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s); + +#endif diff --git a/adb/adb_auth.h b/adb/adb_auth.h new file mode 100644 index 0000000..b24c674 --- /dev/null +++ b/adb/adb_auth.h @@ -0,0 +1,54 @@ +/* + * 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 __ADB_AUTH_H +#define __ADB_AUTH_H + +void adb_auth_init(void); +void adb_auth_verified(atransport *t); + +void send_auth_request(atransport *t); + +/* AUTH packets first argument */ +/* Request */ +#define ADB_AUTH_TOKEN 1 +/* Response */ +#define ADB_AUTH_SIGNATURE 2 +#define ADB_AUTH_RSAPUBLICKEY 3 + +#if ADB_HOST + +int adb_auth_sign(void *key, void *token, size_t token_size, void *sig); +void *adb_auth_nextkey(void *current); +int adb_auth_get_userkey(unsigned char *data, size_t len); + +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) { } + +#else // !ADB_HOST + +static inline int adb_auth_sign(void* key, void *token, size_t token_size, void *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; } + +int adb_auth_generate_token(void *token, size_t token_size); +int adb_auth_verify(void *token, void *sig, int siglen); +void adb_auth_confirm_key(unsigned char *data, size_t len, atransport *t); + +#endif // ADB_HOST + +#endif // __ADB_AUTH_H diff --git a/adb/adb_auth_client.c b/adb/adb_auth_client.c new file mode 100644 index 0000000..c0f4198 --- /dev/null +++ b/adb/adb_auth_client.c @@ -0,0 +1,271 @@ +/* + * 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 +#include +#include +#include +#include + +#include "sysdeps.h" +#include "adb.h" +#include "adb_auth.h" +#include "fdevent.h" +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +#define TRACE_TAG TRACE_AUTH + + +struct adb_public_key { + struct listnode node; + RSAPublicKey key; +}; + +static 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) +{ + struct adb_public_key *key; + FILE *f; + char buf[MAX_PAYLOAD]; + char *sep; + int ret; + + f = fopen(file, "r"); + 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 */ + key = calloc(1, sizeof(*key) + 4); + if (!key) { + D("Can't malloc key\n"); + break; + } + + sep = strpbrk(buf, " \t"); + if (sep) + *sep = '\0'; +#if 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; + } +#else + D("FIXME\n"); +#endif + 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) +{ + char *path; + 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", "r"); + if (!f) + return 0; + + ret = fread(token, token_size, 1, f); + + fclose(f); + return ret * token_size; +} + +int adb_auth_verify(void *token, void *sig, int siglen) +{ + struct listnode *item; + struct adb_public_key *key; + struct listnode key_list; + int ret = 0; + + if (siglen != RSANUMBYTES) + return 0; + + load_keys(&key_list); + + list_for_each(item, &key_list) { + 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]; + 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", 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 adb_auth_init(void) +{ + int fd, ret; + + fd = android_get_control_socket("adbd"); + if (fd < 0) { + D("Failed to get adbd socket\n"); + return; + } + + ret = listen(fd, 4); + if (ret < 0) { + 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.c b/adb/adb_auth_host.c new file mode 100644 index 0000000..9039d42 --- /dev/null +++ b/adb/adb_auth_host.c @@ -0,0 +1,426 @@ +/* + * 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 + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include "windows.h" +# include "shlobj.h" +#else +# include +# include +# include +#endif +#include + +#include "sysdeps.h" +#include "adb.h" +#include "adb_auth.h" + +/* 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 + +#include +#include +#include +#include +#include + +#define TRACE_TAG TRACE_AUTH + +#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; + +#ifndef _WIN32 + ret = gethostname(hostname, sizeof(hostname)); + if (ret < 0) +#endif + strcpy(hostname, "unknown"); + +#if !defined _WIN32 && !defined ADB_HOST_ON_TARGET + ret = getlogin_r(username, sizeof(username)); + if (ret < 0) +#endif + strcpy(username, "unknown"); + + ret = snprintf(buf, len, " %s@%s", username, hostname); + if (ret >= (signed)len) + buf[len - 1] = '\0'; +} + +static int write_public_keyfile(RSA *private_key, const char *private_key_path) +{ + RSAPublicKey pkey; + BIO *bio, *b64, *bfile; + char path[PATH_MAX], info[MAX_PAYLOAD]; + int ret; + + ret = snprintf(path, sizeof(path), "%s.pub", private_key_path); + if (ret >= (signed)sizeof(path)) + return 0; + + ret = RSA_to_RSAPublicKey(private_key, &pkey); + if (!ret) { + D("Failed to convert to publickey\n"); + return 0; + } + + bfile = BIO_new_file(path, "w"); + if (!bfile) { + D("Failed to open '%s'\n", path); + return 0; + } + + D("Writing public key to '%s'\n", path); + + b64 = BIO_new(BIO_f_base64()); + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + + bio = BIO_push(b64, bfile); + BIO_write(bio, &pkey, sizeof(pkey)); + BIO_flush(bio); + BIO_pop(b64); + BIO_free(b64); + + get_user_info(info, sizeof(info)); + BIO_write(bfile, info, strlen(info)); + BIO_flush(bfile); + BIO_free_all(bfile); + + return 1; +} + +static int generate_key(const char *file) +{ + EVP_PKEY* pkey = EVP_PKEY_new(); + BIGNUM* exponent = BN_new(); + RSA* rsa = RSA_new(); + mode_t old_mask; + FILE *f = NULL; + int ret = 0; + + D("generate_key '%s'\n", file); + + if (!pkey || !exponent || !rsa) { + D("Failed to allocate key\n"); + goto out; + } + + BN_set_word(exponent, RSA_F4); + RSA_generate_key_ex(rsa, 2048, exponent, NULL); + EVP_PKEY_set1_RSA(pkey, rsa); + + old_mask = umask(077); + + f = fopen(file, "w"); + if (!f) { + D("Failed to open '%s'\n", file); + umask(old_mask); + goto out; + } + + umask(old_mask); + + if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) { + D("Failed to write key\n"); + goto out; + } + + if (!write_public_keyfile(rsa, file)) { + D("Failed to write public key\n"); + goto out; + } + + ret = 1; + +out: + 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) +{ + struct adb_private_key *key; + FILE *f; + + D("read_key '%s'\n", file); + + f = fopen(file, "r"); + if (!f) { + D("Failed to open '%s'\n", file); + return 0; + } + + key = malloc(sizeof(*key)); + if (!key) { + D("Failed to alloc key\n"); + fclose(f); + return 0; + } + key->rsa = RSA_new(); + + if (!PEM_read_RSAPrivateKey(f, &key->rsa, NULL, NULL)) { + D("Failed to read key\n"); + fclose(f); + RSA_free(key->rsa); + free(key); + return 0; + } + + fclose(f); + list_add_tail(list, &key->node); + return 1; +} + +static int get_user_keyfilepath(char *filename, size_t len) +{ + const char *format, *home; + char android_dir[PATH_MAX]; + struct stat buf; +#ifdef _WIN32 + char path[PATH_MAX]; + home = getenv("ANDROID_SDK_HOME"); + if (!home) { + SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path); + home = path; + } + format = "%s\\%s"; +#else + home = getenv("HOME"); + if (!home) + return -1; + format = "%s/%s"; +#endif + + 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'", android_dir); + return -1; + } + } + + return snprintf(filename, len, format, android_dir, ADB_KEY_FILE); +} + +static int get_user_key(struct listnode *list) +{ + struct stat buf; + char path[PATH_MAX]; + int ret; + + ret = get_user_keyfilepath(path, sizeof(path)); + if (ret < 0 || ret >= (signed)sizeof(path)) { + D("Error getting user key filename"); + return 0; + } + + D("user key '%s'\n", path); + + if (stat(path, &buf) == -1) { + if (!generate_key(path)) { + D("Failed to generate new key\n"); + return 0; + } + } + + return read_key(path, list); +} + +static void get_vendor_keys(struct listnode *list) +{ + const char *adb_keys_path; + char keys_path[MAX_PAYLOAD]; + char *path; + char *save; + struct stat buf; + + adb_keys_path = getenv("ADB_VENDOR_KEYS"); + if (!adb_keys_path) + return; + strncpy(keys_path, adb_keys_path, sizeof(keys_path)); + + path = adb_strtok_r(keys_path, ENV_PATH_SEPARATOR_STR, &save); + while (path) { + D("Reading: '%s'\n", path); + + if (stat(path, &buf)) + D("Can't read '%s'\n", path); + else if (!read_key(path, list)) + D("Failed to read '%s'\n", path); + + path = adb_strtok_r(NULL, ENV_PATH_SEPARATOR_STR, &save); + } +} + +int adb_auth_sign(void *node, void *token, size_t token_size, void *sig) +{ + unsigned int len; + struct adb_private_key *key = node_to_item(node, struct adb_private_key, node); + + if (!RSA_sign(NID_sha1, token, token_size, sig, &len, key->rsa)) { + return 0; + } + + D("adb_auth_sign len=%d\n", 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; + } + } + + return NULL; +} + +int adb_auth_get_userkey(unsigned char *data, size_t len) +{ + char path[PATH_MAX]; + char *file; + int ret; + + ret = get_user_keyfilepath(path, sizeof(path) - 4); + if (ret < 0 || ret >= (signed)(sizeof(path) - 4)) { + D("Error getting user key filename"); + return 0; + } + strcat(path, ".pub"); + + file = load_file(path, (unsigned*)&ret); + if (!file) { + D("Can't load '%s'\n", path); + return 0; + } + + if (len < (size_t)(ret + 1)) { + D("%s: Content too large ret=%d\n", path, ret); + return 0; + } + + memcpy(data, file, ret); + data[ret] = '\0'; + + return ret + 1; +} + +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"); + return; + } + + get_vendor_keys(&key_list); +} diff --git a/adb/adb_client.c b/adb/adb_client.c new file mode 100644 index 0000000..af87d2a --- /dev/null +++ b/adb/adb_client.c @@ -0,0 +1,340 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_ADB +#include "adb_client.h" + +static transport_type __adb_transport = kTransportAny; +static const char* __adb_serial = NULL; + +static int __adb_server_port = DEFAULT_ADB_PORT; +static const char* __adb_server_name = NULL; + +void adb_set_transport(transport_type 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; +} + +int adb_get_emulator_console_port(void) +{ + const char* serial = __adb_serial; + int port; + + if (serial == NULL) { + /* if no specific device was specified, we need to look at */ + /* the list of connected devices, and extract an emulator */ + /* name from it. two emulators is an error */ + char* tmp = adb_query("host:devices"); + char* p = tmp; + if(!tmp) { + printf("no emulator connected\n"); + return -1; + } + while (*p) { + char* q = strchr(p, '\n'); + if (q != NULL) + *q++ = 0; + else + q = p + strlen(p); + + if (!memcmp(p, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1)) { + if (serial != NULL) { /* more than one emulator listed */ + free(tmp); + return -2; + } + serial = p; + } + + p = q; + } + free(tmp); + + if (serial == NULL) + return -1; /* no emulator found */ + } + else { + if (memcmp(serial, LOCAL_CLIENT_PREFIX, sizeof(LOCAL_CLIENT_PREFIX)-1) != 0) + return -1; /* not an emulator */ + } + + serial += sizeof(LOCAL_CLIENT_PREFIX)-1; + port = strtol(serial, NULL, 10); + return port; +} + +static char __adb_error[256] = { 0 }; + +const char *adb_error(void) +{ + return __adb_error; +} + +static int switch_socket_transport(int fd) +{ + char service[64]; + char tmp[5]; + int len; + + if (__adb_serial) + snprintf(service, sizeof service, "host:transport:%s", __adb_serial); + else { + char* transport_type = "???"; + + switch (__adb_transport) { + case kTransportUsb: + transport_type = "transport-usb"; + break; + case kTransportLocal: + transport_type = "transport-local"; + break; + case kTransportAny: + transport_type = "transport-any"; + break; + case kTransportHost: + // no switch necessary + return 0; + break; + } + + snprintf(service, sizeof service, "host:%s", transport_type); + } + len = strlen(service); + snprintf(tmp, sizeof tmp, "%04x", len); + + if(writex(fd, tmp, 4) || writex(fd, service, len)) { + strcpy(__adb_error, "write failure during connection"); + adb_close(fd); + return -1; + } + D("Switch transport in progress\n"); + + if(adb_status(fd)) { + adb_close(fd); + D("Switch transport failed\n"); + return -1; + } + D("Switch transport success\n"); + return 0; +} + +int adb_status(int fd) +{ + unsigned char buf[5]; + unsigned len; + + if(readx(fd, buf, 4)) { + strcpy(__adb_error, "protocol fault (no status)"); + return -1; + } + + if(!memcmp(buf, "OKAY", 4)) { + return 0; + } + + if(memcmp(buf, "FAIL", 4)) { + sprintf(__adb_error, + "protocol fault (status %02x %02x %02x %02x?!)", + buf[0], buf[1], buf[2], buf[3]); + return -1; + } + + if(readx(fd, buf, 4)) { + strcpy(__adb_error, "protocol fault (status len)"); + return -1; + } + buf[4] = 0; + len = strtoul((char*)buf, 0, 16); + if(len > 255) len = 255; + if(readx(fd, __adb_error, len)) { + strcpy(__adb_error, "protocol fault (status read)"); + return -1; + } + __adb_error[len] = 0; + return -1; +} + +int _adb_connect(const char *service) +{ + char tmp[5]; + int len; + int fd; + + D("_adb_connect: %s\n", service); + len = strlen(service); + if((len < 1) || (len > 1024)) { + strcpy(__adb_error, "service name too long"); + return -1; + } + snprintf(tmp, sizeof tmp, "%04x", len); + + if (__adb_server_name) + fd = socket_network_client(__adb_server_name, __adb_server_port, SOCK_STREAM); + else + fd = socket_loopback_client(__adb_server_port, SOCK_STREAM); + + if(fd < 0) { + strcpy(__adb_error, "cannot connect to daemon"); + return -2; + } + + if (memcmp(service,"host",4) != 0 && switch_socket_transport(fd)) { + return -1; + } + + if(writex(fd, tmp, 4) || writex(fd, service, len)) { + strcpy(__adb_error, "write failure during connection"); + adb_close(fd); + return -1; + } + + if(adb_status(fd)) { + adb_close(fd); + return -1; + } + + D("_adb_connect: return fd %d\n", fd); + return fd; +} + +int adb_connect(const char *service) +{ + // first query the adb server's version + int fd = _adb_connect("host:version"); + + D("adb_connect: service %s\n", service); + if(fd == -2 && __adb_server_name) { + fprintf(stderr,"** Cannot start server on remote host\n"); + return fd; + } else if(fd == -2) { + fprintf(stdout,"* daemon not running. starting it now on port %d *\n", + __adb_server_port); + start_server: + if(launch_server(__adb_server_port)) { + fprintf(stderr,"* failed to start daemon *\n"); + return -1; + } else { + fprintf(stdout,"* daemon started successfully *\n"); + } + /* give the server some time to start properly and detect devices */ + adb_sleep_ms(3000); + // fall through to _adb_connect + } else { + // if server was running, check its version to make sure it is not out of date + char buf[100]; + size_t n; + int version = ADB_SERVER_VERSION - 1; + + // if we have a file descriptor, then parse version result + if(fd >= 0) { + if(readx(fd, buf, 4)) goto error; + + buf[4] = 0; + n = strtoul(buf, 0, 16); + if(n > sizeof(buf)) goto error; + if(readx(fd, buf, n)) goto error; + adb_close(fd); + + if (sscanf(buf, "%04x", &version) != 1) goto error; + } else { + // if fd is -1, then check for "unknown host service", + // which would indicate a version of adb that does not support the version command + if (strcmp(__adb_error, "unknown host service") != 0) + return fd; + } + + if(version != ADB_SERVER_VERSION) { + printf("adb server is out of date. killing...\n"); + fd = _adb_connect("host:kill"); + adb_close(fd); + + /* XXX can we better detect its death? */ + adb_sleep_ms(2000); + goto start_server; + } + } + + // if the command is start-server, we are done. + if (!strcmp(service, "host:start-server")) + return 0; + + fd = _adb_connect(service); + if(fd == -2) { + fprintf(stderr,"** daemon still not running\n"); + } + D("adb_connect: return fd %d\n", fd); + + return fd; +error: + adb_close(fd); + return -1; +} + + +int adb_command(const char *service) +{ + int fd = adb_connect(service); + if(fd < 0) { + return -1; + } + + if(adb_status(fd)) { + adb_close(fd); + return -1; + } + + return 0; +} + +char *adb_query(const char *service) +{ + char buf[5]; + unsigned n; + char *tmp; + + D("adb_query: %s\n", service); + int fd = adb_connect(service); + if(fd < 0) { + fprintf(stderr,"error: %s\n", __adb_error); + return 0; + } + + if(readx(fd, buf, 4)) goto oops; + + buf[4] = 0; + n = strtoul(buf, 0, 16); + if(n > 1024) goto oops; + + tmp = malloc(n + 1); + if(tmp == 0) goto oops; + + if(readx(fd, tmp, n) == 0) { + tmp[n] = 0; + adb_close(fd); + return tmp; + } + free(tmp); + +oops: + adb_close(fd); + return 0; +} diff --git a/adb/adb_client.h b/adb/adb_client.h new file mode 100644 index 0000000..0ec47ca --- /dev/null +++ b/adb/adb_client.h @@ -0,0 +1,57 @@ +#ifndef _ADB_CLIENT_H_ +#define _ADB_CLIENT_H_ + +#include "adb.h" + +/* 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 char *service); +int _adb_connect(const char *service); + +/* connect to adb, connect to the named service, return 0 if +** the connection succeeded AND the service returned OKAY +*/ +int adb_command(const char *service); + +/* connect to adb, connect to the named service, return +** a malloc'd string of its response upon success or NULL +** on failure. +*/ +char *adb_query(const char *service); + +/* Set the preferred transport to connect to. +*/ +void adb_set_transport(transport_type type, const char* 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); + +/* Return the console port of the currently connected emulator (if any) + * of -1 if there is no emulator, and -2 if there is more than one. + * assumes adb_set_transport() was alled previously... + */ +int adb_get_emulator_console_port(void); + +/* send commands to the current emulator instance. will fail if there + * is zero, or more than one emulator connected (or if you use -s + * with a that does not designate an emulator) + */ +int adb_send_emulator_command(int argc, char** argv); + +/* return verbose error string from last operation */ +const char *adb_error(void); + +/* read a standard adb status response (OKAY|FAIL) and +** return 0 in the event of OKAY, -1 in the event of FAIL +** or protocol error +*/ +int adb_status(int fd); + +#endif diff --git a/adb/backup_service.c b/adb/backup_service.c new file mode 100644 index 0000000..669ff86 --- /dev/null +++ b/adb/backup_service.c @@ -0,0 +1,155 @@ +/* + * 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 +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_ADB +#include "adb.h" + +typedef struct { + pid_t pid; + int fd; +} backup_harvest_params; + +// socketpair but do *not* mark as close_on_exec +static int backup_socketpair(int sv[2]) { + int rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv ); + if (rc < 0) + return -1; + + return 0; +} + +// harvest the child process then close the read end of the socketpair +static void* backup_child_waiter(void* args) { + int status; + backup_harvest_params* params = (backup_harvest_params*) args; + + waitpid(params->pid, &status, 0); + adb_close(params->fd); + free(params); + return NULL; +} + +/* returns the data socket passing the backup data here for forwarding */ +int backup_service(BackupOperation op, char* args) { + pid_t pid; + int s[2]; + char* operation; + int socketnum; + + // Command string and choice of stdin/stdout for the pipe depend on our invocation + if (op == BACKUP) { + operation = "backup"; + socketnum = STDOUT_FILENO; + } else { + operation = "restore"; + socketnum = STDIN_FILENO; + } + + D("backup_service(%s, %s)\n", operation, args); + + // set up the pipe from the subprocess to here + // parent will read s[0]; child will write s[1] + if (backup_socketpair(s)) { + D("can't create backup/restore socketpair\n"); + fprintf(stderr, "unable to create backup/restore socketpair\n"); + return -1; + } + + D("Backup/restore socket pair: (send=%d, receive=%d)\n", s[1], s[0]); + close_on_exec(s[0]); // only the side we hold on to + + // spin off the child process to run the backup command + pid = fork(); + if (pid < 0) { + // failure + D("can't fork for %s\n", operation); + fprintf(stderr, "unable to fork for %s\n", operation); + adb_close(s[0]); + adb_close(s[1]); + return -1; + } + + // Great, we're off and running. + if (pid == 0) { + // child -- actually run the backup here + char* p; + int argc; + char portnum[16]; + char** bu_args; + + // fixed args: [0] is 'bu', [1] is the port number, [2] is the 'operation' string + argc = 3; + for (p = (char*)args; p && *p; ) { + argc++; + while (*p && *p != ':') p++; + if (*p == ':') p++; + } + + bu_args = (char**) alloca(argc*sizeof(char*) + 1); + + // run through again to build the argv array + argc = 0; + bu_args[argc++] = "bu"; + snprintf(portnum, sizeof(portnum), "%d", s[1]); + bu_args[argc++] = portnum; + bu_args[argc++] = operation; + for (p = (char*)args; p && *p; ) { + bu_args[argc++] = p; + while (*p && *p != ':') p++; + if (*p == ':') { + *p = 0; + p++; + } + } + bu_args[argc] = NULL; + + // Close the half of the socket that we don't care about, route 'bu's console + // to the output socket, and off we go + adb_close(s[0]); + + // off we go + execvp("/system/bin/bu", (char * const *)bu_args); + // oops error - close up shop and go home + fprintf(stderr, "Unable to exec 'bu', bailing\n"); + exit(-1); + } else { + adb_thread_t t; + backup_harvest_params* params; + + // parent, i.e. adbd -- close the sending half of the socket + D("fork() returned pid %d\n", pid); + adb_close(s[1]); + + // spin a thread to harvest the child process + params = (backup_harvest_params*) malloc(sizeof(backup_harvest_params)); + params->pid = pid; + params->fd = s[0]; + if (adb_thread_create(&t, backup_child_waiter, params)) { + adb_close(s[0]); + free(params); + D("Unable to create child harvester\n"); + return -1; + } + } + + // we'll be reading from s[0] as the data is sent by the child process + return s[0]; +} diff --git a/adb/commandline.c b/adb/commandline.c new file mode 100644 index 0000000..27a1754 --- /dev/null +++ b/adb/commandline.c @@ -0,0 +1,1746 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sysdeps.h" + +#ifdef HAVE_TERMIO_H +#include +#endif + +#define TRACE_TAG TRACE_ADB +#include "adb.h" +#include "adb_client.h" +#include "file_sync_service.h" + +static int do_cmd(transport_type ttype, char* serial, char *cmd, ...); + +void get_my_path(char *s, size_t maxLen); +int find_sync_dirs(const char *srcarg, + char **android_srcdir_out, char **data_srcdir_out); +int install_app(transport_type transport, char* serial, int argc, char** argv); +int uninstall_app(transport_type transport, char* serial, int argc, char** argv); + +static const char *gProductOutPath = NULL; +extern int gListenAll; + +static char *product_file(const char *extra) +{ + int n; + char *x; + + if (gProductOutPath == NULL) { + fprintf(stderr, "adb: Product directory not specified; " + "use -p or define ANDROID_PRODUCT_OUT\n"); + exit(1); + } + + n = strlen(gProductOutPath) + strlen(extra) + 2; + x = malloc(n); + if (x == 0) { + fprintf(stderr, "adb: Out of memory (product_file())\n"); + exit(1); + } + + snprintf(x, (size_t)n, "%s" OS_PATH_SEPARATOR_STR "%s", gProductOutPath, extra); + return x; +} + +void version(FILE * out) { + fprintf(out, "Android Debug Bridge version %d.%d.%d\n", + ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION); +} + +void help() +{ + version(stderr); + + fprintf(stderr, + "\n" + " -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 - directs command to the device or emulator with the given\n" + " serial number or qualifier. Overrides ANDROID_SERIAL\n" + " environment variable.\n" + " -p - 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 [:] - connect to a device via TCP/IP\n" + " Port 5555 is used by default if no port number is specified.\n" + " disconnect [[:]] - 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" + "\n" + "device commands:\n" + " adb push - copy file/dir to device\n" + " adb pull [] - copy file/dir from device\n" + " adb sync [ ] - copy host->device only if changed\n" + " (-l means list but don't copy)\n" + " (see 'adb help all')\n" + " adb shell - run remote shell interactively\n" + " adb shell - run remote shell command\n" + " adb emu - run emulator console command\n" + " adb logcat [ ] - 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" + " \" \" \" \" \"\\n\"\n" + " adb forward - forward socket connections\n" + " forward specs are one of: \n" + " tcp:\n" + " localabstract:\n" + " localreserved:\n" + " localfilesystem:\n" + " dev:\n" + " jdwp: (remote only)\n" + " adb forward --no-rebind \n" + " - same as 'adb forward ' but fails\n" + " if is already forwarded\n" + " adb forward --remove - remove a specific forward socket connection\n" + " adb forward --remove-all - remove all forward socket connections\n" + " adb jdwp - list PIDs of processes hosting a JDWP transport\n" + " adb install [-l] [-r] [-s] [--algo --key --iv ] \n" + " - push this package file to the device and install it\n" + " ('-l' means forward-lock the app)\n" + " ('-r' means reinstall the app, keeping its data)\n" + " ('-s' means install on SD card instead of internal storage)\n" + " ('--algo', '--key', and '--iv' mean the file is encrypted already)\n" + " adb uninstall [-k] - 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 ] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] []\n" + " - write an archive of the device's data to .\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" + " ( 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 - restore device contents from the backup archive\n" + "\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: \n" + " adb get-devpath - prints: \n" + " adb status-window - continuously print device status for a specified device\n" + " adb remount - remounts the /system partition on the device read-write\n" + " adb reboot [bootloader|recovery] - reboots the device, optionally into the bootloader or recovery program\n" + " adb reboot-bootloader - reboots the device into the bootloader\n" + " adb root - restarts the adbd daemon with root permissions\n" + " adb usb - restarts the adbd daemon listening on USB\n" + " adb tcpip - restarts the adbd daemon listening on TCP on the specified port" + "\n" + "networking:\n" + " adb ppp [parameters] - Run PPP over USB.\n" + " Note: you should not automatically start a PPP connection.\n" + " refers to the tty for PPP stream. Eg. dev:/dev/omap_csmi_tty1\n" + " [parameters] - Eg. defaultroute debug dump local notty usepeerdns\n" + "\n" + "adb sync notes: adb sync [ ]\n" + " can be interpreted in several ways:\n" + "\n" + " - If is not specified, both /system and /data partitions will be updated.\n" + "\n" + " - If it is \"system\" or \"data\", only the corresponding partition\n" + " is updated.\n" + "\n" + "environmental 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" + ); +} + +int usage() +{ + help(); + return 1; +} + +#ifdef HAVE_TERMIO_H +static struct termios tio_save; + +static void stdin_raw_init(int fd) +{ + struct termios tio; + + if(tcgetattr(fd, &tio)) return; + if(tcgetattr(fd, &tio_save)) return; + + tio.c_lflag = 0; /* disable CANON, ECHO*, etc */ + + /* no timeout but request at least one character per read */ + tio.c_cc[VTIME] = 0; + tio.c_cc[VMIN] = 1; + + tcsetattr(fd, TCSANOW, &tio); + tcflush(fd, TCIFLUSH); +} + +static void stdin_raw_restore(int fd) +{ + tcsetattr(fd, TCSANOW, &tio_save); + tcflush(fd, TCIFLUSH); +} +#endif + +static void read_and_dump(int fd) +{ + char buf[4096]; + int len; + + while(fd >= 0) { + D("read_and_dump(): pre adb_read(fd=%d)\n", fd); + len = adb_read(fd, buf, 4096); + D("read_and_dump(): post adb_read(fd=%d): len=%d\n", fd, len); + if(len == 0) { + break; + } + + if(len < 0) { + if(errno == EINTR) continue; + break; + } + fwrite(buf, 1, len, stdout); + fflush(stdout); + } +} + +static void copy_to_file(int inFd, int outFd) { + const size_t BUFSIZE = 32 * 1024; + char* buf = (char*) malloc(BUFSIZE); + int len; + long total = 0; + + D("copy_to_file(%d -> %d)\n", inFd, outFd); + for (;;) { + 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; + } + adb_write(outFd, buf, len); + total += len; + } + D("copy_to_file() finished after %lu bytes\n", total); + free(buf); +} + +static void *stdin_read_thread(void *x) +{ + int fd, fdi; + unsigned char buf[1024]; + int r, n; + int state = 0; + + int *fds = (int*) x; + fd = fds[0]; + fdi = fds[1]; + free(fds); + + 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"); +#ifdef HAVE_TERMIO_H + stdin_raw_restore(fdi); +#endif + exit(0); + } + default: + state = 0; + } + } + r = adb_write(fd, buf, r); + if(r <= 0) { + break; + } + } + return 0; +} + +int interactive_shell(void) +{ + adb_thread_t thr; + int fdi, fd; + int *fds; + + fd = adb_connect("shell:"); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + fdi = 0; //dup(0); + + fds = malloc(sizeof(int) * 2); + fds[0] = fd; + fds[1] = fdi; + +#ifdef HAVE_TERMIO_H + stdin_raw_init(fdi); +#endif + adb_thread_create(&thr, stdin_read_thread, fds); + read_and_dump(fd); +#ifdef HAVE_TERMIO_H + stdin_raw_restore(fdi); +#endif + return 0; +} + + +static void format_host_command(char* buffer, size_t buflen, const char* command, transport_type ttype, const char* serial) +{ + if (serial) { + snprintf(buffer, buflen, "host-serial:%s:%s", serial, command); + } else { + const char* prefix = "host"; + if (ttype == kTransportUsb) + prefix = "host-usb"; + else if (ttype == kTransportLocal) + prefix = "host-local"; + + snprintf(buffer, buflen, "%s:%s", prefix, command); + } +} + +int adb_download_buffer(const char *service, const char *fn, const void* data, int sz, + unsigned progress) +{ + char buf[4096]; + unsigned total; + int fd; + const unsigned char *ptr; + + sprintf(buf,"%s:%d", service, sz); + fd = adb_connect(buf); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return -1; + } + + int opt = CHUNK_SIZE; + opt = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)); + + total = sz; + ptr = data; + + if(progress) { + char *x = strrchr(service, ':'); + if(x) service = x + 1; + } + + while(sz > 0) { + unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz; + if(writex(fd, ptr, xfer)) { + adb_status(fd); + fprintf(stderr,"* failed to write data '%s' *\n", adb_error()); + return -1; + } + sz -= xfer; + ptr += xfer; + if(progress) { + printf("sending: '%s' %4d%% \r", fn, (int)(100LL - ((100LL * sz) / (total)))); + fflush(stdout); + } + } + if(progress) { + printf("\n"); + } + + if(readx(fd, buf, 4)){ + fprintf(stderr,"* error reading response *\n"); + adb_close(fd); + return -1; + } + if(memcmp(buf, "OKAY", 4)) { + buf[4] = 0; + fprintf(stderr,"* error response '%s' *\n", buf); + adb_close(fd); + return -1; + } + + adb_close(fd); + return 0; +} + + +int adb_download(const char *service, const char *fn, unsigned progress) +{ + void *data; + unsigned sz; + + data = load_file(fn, &sz); + if(data == 0) { + fprintf(stderr,"* cannot read '%s' *\n", fn); + return -1; + } + + int status = adb_download_buffer(service, fn, data, sz, progress); + free(data); + return status; +} + +static void status_window(transport_type ttype, const char* serial) +{ + char command[4096]; + char *state = 0; + char *laststate = 0; + + /* silence stderr */ +#ifdef _WIN32 + /* XXX: TODO */ +#else + int fd; + fd = unix_open("/dev/null", O_WRONLY); + dup2(fd, 2); + adb_close(fd); +#endif + + format_host_command(command, sizeof command, "get-state", ttype, serial); + + for(;;) { + adb_sleep_ms(250); + + if(state) { + free(state); + state = 0; + } + + state = adb_query(command); + + if(state) { + if(laststate && !strcmp(state,laststate)){ + continue; + } else { + if(laststate) free(laststate); + laststate = strdup(state); + } + } + + printf("%c[2J%c[2H", 27, 27); + printf("Android Debug Bridge\n"); + printf("State: %s\n", state ? state : "offline"); + fflush(stdout); + } +} + +/** duplicate string and quote all \ " ( ) chars + space character. */ +static char * +dupAndQuote(const char *s) +{ + const char *ts; + size_t alloc_len; + char *ret; + char *dest; + + ts = s; + + alloc_len = 0; + + for( ;*ts != '\0'; ts++) { + alloc_len++; + if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { + alloc_len++; + } + } + + ret = (char *)malloc(alloc_len + 1); + + ts = s; + dest = ret; + + for ( ;*ts != '\0'; ts++) { + if (*ts == ' ' || *ts == '"' || *ts == '\\' || *ts == '(' || *ts == ')') { + *dest++ = '\\'; + } + + *dest++ = *ts; + } + + *dest++ = '\0'; + + return ret; +} + +/** + * Run ppp in "notty" mode against a resource listed as the first parameter + * eg: + * + * ppp dev:/dev/omap_csmi_tty0 + * + */ +int ppp(int argc, char **argv) +{ +#ifdef HAVE_WIN32_PROC + fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]); + return -1; +#else + char *adb_service_name; + pid_t pid; + int fd; + + if (argc < 2) { + fprintf(stderr, "usage: adb %s [ppp opts]\n", + argv[0]); + + return 1; + } + + adb_service_name = argv[1]; + + fd = adb_connect(adb_service_name); + + if(fd < 0) { + fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n", + adb_service_name, adb_error()); + return 1; + } + + pid = fork(); + + if (pid < 0) { + perror("from fork()"); + return 1; + } else if (pid == 0) { + int err; + int i; + const char **ppp_args; + + // copy args + ppp_args = (const char **) alloca(sizeof(char *) * argc + 1); + ppp_args[0] = "pppd"; + for (i = 2 ; i < argc ; i++) { + //argv[2] and beyond become ppp_args[1] and beyond + ppp_args[i - 1] = argv[i]; + } + ppp_args[i-1] = NULL; + + // child side + + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + adb_close(STDERR_FILENO); + adb_close(fd); + + err = execvp("pppd", (char * const *)ppp_args); + + if (err < 0) { + perror("execing pppd"); + } + exit(-1); + } else { + // parent side + + adb_close(fd); + return 0; + } +#endif /* !HAVE_WIN32_PROC */ +} + +static int send_shellcommand(transport_type transport, char* serial, char* buf) +{ + int fd, ret; + + for(;;) { + fd = adb_connect(buf); + if(fd >= 0) + break; + fprintf(stderr,"- waiting for device -\n"); + adb_sleep_ms(1000); + do_cmd(transport, serial, "wait-for-device", 0); + } + + read_and_dump(fd); + ret = adb_close(fd); + if (ret) + perror("close"); + + return ret; +} + +static int logcat(transport_type transport, char* serial, int argc, char **argv) +{ + char buf[4096]; + + char *log_tags; + char *quoted_log_tags; + + log_tags = getenv("ANDROID_LOG_TAGS"); + quoted_log_tags = dupAndQuote(log_tags == NULL ? "" : log_tags); + + snprintf(buf, sizeof(buf), + "shell:export ANDROID_LOG_TAGS=\"\%s\" ; exec logcat", + quoted_log_tags); + + free(quoted_log_tags); + + if (!strcmp(argv[0],"longcat")) { + strncat(buf, " -v long", sizeof(buf)-1); + } + + argc -= 1; + argv += 1; + while(argc-- > 0) { + char *quoted; + + quoted = dupAndQuote (*argv++); + + strncat(buf, " ", sizeof(buf)-1); + strncat(buf, quoted, sizeof(buf)-1); + free(quoted); + } + + send_shellcommand(transport, serial, buf); + return 0; +} + +static int mkdirs(char *path) +{ + int ret; + char *x = path + 1; + + for(;;) { + x = adb_dirstart(x); + if(x == 0) return 0; + *x = 0; + ret = adb_mkdir(path, 0775); + *x = OS_PATH_SEPARATOR; + if((ret < 0) && (errno != EEXIST)) { + return ret; + } + x++; + } + return 0; +} + +static int backup(int argc, char** argv) { + char buf[4096]; + char default_name[32]; + const char* filename = strcpy(default_name, "./backup.ab"); + int fd, outFd; + int i, j; + + /* find, extract, and use any -f argument */ + for (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(); + } + filename = argv[i+1]; + for (j = i+2; j <= argc; ) { + argv[i++] = argv[j++]; + } + argc -= 2; + argv[argc] = NULL; + } + } + + /* bare "adb backup" or "adb backup -f filename" are not valid invocations */ + if (argc < 2) return usage(); + + adb_unlink(filename); + mkdirs((char *)filename); + outFd = adb_creat(filename, 0640); + if (outFd < 0) { + fprintf(stderr, "adb: unable to open file %s\n", filename); + return -1; + } + + snprintf(buf, sizeof(buf), "backup"); + for (argc--, argv++; argc; argc--, argv++) { + strncat(buf, ":", sizeof(buf) - strlen(buf) - 1); + strncat(buf, argv[0], sizeof(buf) - strlen(buf) - 1); + } + + D("backup. filename=%s buf=%s\n", filename, buf); + fd = adb_connect(buf); + if (fd < 0) { + fprintf(stderr, "adb: unable to connect for backup\n"); + adb_close(outFd); + return -1; + } + + printf("Now unlock your device and confirm the backup operation.\n"); + copy_to_file(fd, outFd); + + adb_close(fd); + adb_close(outFd); + return 0; +} + +static int restore(int argc, char** argv) { + const char* filename; + int fd, tarFd; + + if (argc != 2) return usage(); + + filename = argv[1]; + tarFd = adb_open(filename, O_RDONLY); + if (tarFd < 0) { + fprintf(stderr, "adb: unable to open file %s\n", filename); + return -1; + } + + fd = adb_connect("restore:"); + if (fd < 0) { + fprintf(stderr, "adb: unable to connect for backup\n"); + adb_close(tarFd); + return -1; + } + + printf("Now unlock your device and confirm the restore operation.\n"); + copy_to_file(tarFd, fd); + + adb_close(fd); + adb_close(tarFd); + return 0; +} + +#define SENTINEL_FILE "config" OS_PATH_SEPARATOR_STR "envsetup.make" +static int top_works(const char *top) +{ + if (top != NULL && adb_is_absolute_host_path(top)) { + char path_buf[PATH_MAX]; + snprintf(path_buf, sizeof(path_buf), + "%s" OS_PATH_SEPARATOR_STR SENTINEL_FILE, top); + return access(path_buf, F_OK) == 0; + } + return 0; +} + +static char *find_top_from(const char *indir, char path_buf[PATH_MAX]) +{ + strcpy(path_buf, indir); + while (1) { + if (top_works(path_buf)) { + return path_buf; + } + char *s = adb_dirstop(path_buf); + if (s != NULL) { + *s = '\0'; + } else { + path_buf[0] = '\0'; + return NULL; + } + } +} + +static char *find_top(char path_buf[PATH_MAX]) +{ + char *top = getenv("ANDROID_BUILD_TOP"); + if (top != NULL && top[0] != '\0') { + if (!top_works(top)) { + fprintf(stderr, "adb: bad ANDROID_BUILD_TOP value \"%s\"\n", top); + return NULL; + } + } else { + top = getenv("TOP"); + if (top != NULL && top[0] != '\0') { + if (!top_works(top)) { + fprintf(stderr, "adb: bad TOP value \"%s\"\n", top); + return NULL; + } + } else { + top = NULL; + } + } + + if (top != NULL) { + /* The environment pointed to a top directory that works. + */ + strcpy(path_buf, top); + return path_buf; + } + + /* The environment didn't help. Walk up the tree from the CWD + * to see if we can find the top. + */ + char dir[PATH_MAX]; + top = find_top_from(getcwd(dir, sizeof(dir)), path_buf); + if (top == NULL) { + /* If the CWD isn't under a good-looking top, see if the + * executable is. + */ + get_my_path(dir, PATH_MAX); + top = find_top_from(dir, path_buf); + } + return top; +} + +/* may be: + * - A simple product name + * e.g., "sooner" +TODO: debug? sooner-debug, sooner:debug? + * - A relative path from the CWD to the ANDROID_PRODUCT_OUT dir + * e.g., "out/target/product/sooner" + * - An absolute path to the PRODUCT_OUT dir + * e.g., "/src/device/out/target/product/sooner" + * + * Given , try to construct an absolute path to the + * ANDROID_PRODUCT_OUT dir. + */ +static const char *find_product_out_path(const char *hint) +{ + static char path_buf[PATH_MAX]; + + if (hint == NULL || hint[0] == '\0') { + return NULL; + } + + /* If it's already absolute, don't bother doing any work. + */ + if (adb_is_absolute_host_path(hint)) { + strcpy(path_buf, hint); + return path_buf; + } + + /* If there are any slashes in it, assume it's a relative path; + * make it absolute. + */ + if (adb_dirstart(hint) != NULL) { + if (getcwd(path_buf, sizeof(path_buf)) == NULL) { + fprintf(stderr, "adb: Couldn't get CWD: %s\n", strerror(errno)); + return NULL; + } + if (strlen(path_buf) + 1 + strlen(hint) >= sizeof(path_buf)) { + fprintf(stderr, "adb: Couldn't assemble path\n"); + return NULL; + } + strcat(path_buf, OS_PATH_SEPARATOR_STR); + strcat(path_buf, hint); + return path_buf; + } + + /* It's a string without any slashes. Try to do something with it. + * + * Try to find the root of the build tree, and build a PRODUCT_OUT + * path from there. + */ + char top_buf[PATH_MAX]; + const char *top = find_top(top_buf); + if (top == NULL) { + fprintf(stderr, "adb: Couldn't find top of build tree\n"); + return NULL; + } +//TODO: if we have a way to indicate debug, look in out/debug/target/... + snprintf(path_buf, sizeof(path_buf), + "%s" OS_PATH_SEPARATOR_STR + "out" OS_PATH_SEPARATOR_STR + "target" OS_PATH_SEPARATOR_STR + "product" OS_PATH_SEPARATOR_STR + "%s", top_buf, hint); + if (access(path_buf, F_OK) < 0) { + fprintf(stderr, "adb: Couldn't find a product dir " + "based on \"-p %s\"; \"%s\" doesn't exist\n", hint, path_buf); + return NULL; + } + return path_buf; +} + +int adb_commandline(int argc, char **argv) +{ + char buf[4096]; + int no_daemon = 0; + int is_daemon = 0; + int is_server = 0; + int persist = 0; + int r; + int quote; + transport_type ttype = kTransportAny; + char* serial = NULL; + char* server_port_str = NULL; + + /* 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 + * command requires this information, then the user must + * specify the path using "-p". + */ + gProductOutPath = getenv("ANDROID_PRODUCT_OUT"); + if (gProductOutPath == NULL || gProductOutPath[0] == '\0') { + gProductOutPath = NULL; + } + // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint + + serial = getenv("ANDROID_SERIAL"); + + /* Validate and assign the server port */ + 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 = (int) strtol(server_port_str, NULL, 0); + if (server_port <= 0 || server_port > 65535) { + fprintf(stderr, + "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number less than 65535. Got \"%s\"\n", + server_port_str); + return usage(); + } + } + + /* modifiers and flags */ + while(argc > 0) { + if(!strcmp(argv[0],"server")) { + is_server = 1; + } else if(!strcmp(argv[0],"nodaemon")) { + no_daemon = 1; + } else if (!strcmp(argv[0], "fork-server")) { + /* this is a special flag used only when the ADB client launches the ADB Server */ + is_daemon = 1; + } else if(!strcmp(argv[0],"persist")) { + persist = 1; + } else if(!strncmp(argv[0], "-p", 2)) { + const char *product = NULL; + if (argv[0][2] == '\0') { + if (argc < 2) return usage(); + product = argv[1]; + argc--; + argv++; + } else { + product = argv[0] + 2; + } + gProductOutPath = find_product_out_path(product); + if (gProductOutPath == NULL) { + fprintf(stderr, "adb: could not resolve \"-p %s\"\n", + product); + return usage(); + } + } else if (argv[0][0]=='-' && argv[0][1]=='s') { + if (isdigit(argv[0][2])) { + serial = argv[0] + 2; + } else { + if(argc < 2 || argv[0][2] != '\0') return usage(); + serial = argv[1]; + argc--; + argv++; + } + } else if (!strcmp(argv[0],"-d")) { + ttype = kTransportUsb; + } else if (!strcmp(argv[0],"-e")) { + ttype = kTransportLocal; + } 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]; + argc--; + argv++; + } else { + hostname = 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(); + server_port_str = argv[1]; + argc--; + argv++; + } 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 { + /* out of recognized modifiers and flags */ + break; + } + argc--; + argv++; + } + + adb_set_transport(ttype, serial); + adb_set_tcp_specifics(server_port); + + if (is_server) { + if (no_daemon || is_daemon) { + r = adb_main(is_daemon, server_port); + } else { + r = launch_server(server_port); + } + if(r) { + fprintf(stderr,"* could not start server *\n"); + } + return r; + } + +top: + if(argc == 0) { + return usage(); + } + + /* adb_connect() commands */ + + if(!strcmp(argv[0], "devices")) { + char *tmp; + char *listopt; + if (argc < 2) + listopt = ""; + else if (argc == 2 && !strcmp(argv[1], "-l")) + listopt = argv[1]; + else { + fprintf(stderr, "Usage: adb devices [-l]\n"); + return 1; + } + snprintf(buf, sizeof buf, "host:%s%s", argv[0], listopt); + tmp = adb_query(buf); + if(tmp) { + printf("List of devices attached \n"); + printf("%s\n", tmp); + return 0; + } else { + return 1; + } + } + + if(!strcmp(argv[0], "connect")) { + char *tmp; + if (argc != 2) { + fprintf(stderr, "Usage: adb connect [:]\n"); + return 1; + } + snprintf(buf, sizeof buf, "host:connect:%s", argv[1]); + tmp = adb_query(buf); + if(tmp) { + printf("%s\n", tmp); + return 0; + } else { + return 1; + } + } + + if(!strcmp(argv[0], "disconnect")) { + char *tmp; + if (argc > 2) { + fprintf(stderr, "Usage: adb disconnect [[:]]\n"); + return 1; + } + if (argc == 2) { + snprintf(buf, sizeof buf, "host:disconnect:%s", argv[1]); + } else { + snprintf(buf, sizeof buf, "host:disconnect:"); + } + tmp = adb_query(buf); + if(tmp) { + printf("%s\n", tmp); + return 0; + } else { + return 1; + } + } + + if (!strcmp(argv[0], "emu")) { + return adb_send_emulator_command(argc, argv); + } + + if(!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) { + int r; + int fd; + + 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; + } + + snprintf(buf, sizeof buf, "shell:%s", argv[1]); + argc -= 2; + argv += 2; + while(argc-- > 0) { + strcat(buf, " "); + + /* quote empty strings and strings with spaces */ + quote = (**argv == 0 || strchr(*argv, ' ')); + if (quote) + strcat(buf, "\""); + strcat(buf, *argv++); + if (quote) + strcat(buf, "\""); + } + + for(;;) { + D("interactive shell loop. buff=%s\n", buf); + fd = adb_connect(buf); + 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", adb_error()); + r = -1; + } + + if(persist) { + fprintf(stderr,"\n- waiting for device -\n"); + adb_sleep_ms(1000); + do_cmd(ttype, serial, "wait-for-device", 0); + } else { + if (h) { + printf("\x1b[0m"); + fflush(stdout); + } + D("interactive shell loop. return r=%d\n", r); + return r; + } + } + } + + if(!strcmp(argv[0], "kill-server")) { + int fd; + fd = _adb_connect("host:kill"); + if(fd == -1) { + fprintf(stderr,"* server not running *\n"); + return 1; + } + return 0; + } + + if(!strcmp(argv[0], "sideload")) { + if(argc != 2) return usage(); + if(adb_download("sideload", argv[1], 1)) { + return 1; + } else { + return 0; + } + } + + if(!strcmp(argv[0], "remount") || !strcmp(argv[0], "reboot") + || !strcmp(argv[0], "reboot-bootloader") + || !strcmp(argv[0], "tcpip") || !strcmp(argv[0], "usb") + || !strcmp(argv[0], "root")) { + char command[100]; + if (!strcmp(argv[0], "reboot-bootloader")) + snprintf(command, sizeof(command), "reboot:bootloader"); + else if (argc > 1) + snprintf(command, sizeof(command), "%s:%s", argv[0], argv[1]); + else + snprintf(command, sizeof(command), "%s:", argv[0]); + int fd = adb_connect(command); + if(fd >= 0) { + read_and_dump(fd); + adb_close(fd); + return 0; + } + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + if(!strcmp(argv[0], "bugreport")) { + if (argc != 1) return usage(); + do_cmd(ttype, serial, "shell", "bugreport", 0); + return 0; + } + + /* adb_command() wrapper commands */ + + if(!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) { + char* service = argv[0]; + if (!strncmp(service, "wait-for-device", strlen("wait-for-device"))) { + if (ttype == kTransportUsb) { + service = "wait-for-usb"; + } else if (ttype == kTransportLocal) { + service = "wait-for-local"; + } else { + service = "wait-for-any"; + } + } + + format_host_command(buf, sizeof buf, service, ttype, serial); + + if (adb_command(buf)) { + D("failure: %s *\n",adb_error()); + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + /* Allow a command to be run after wait-for-device, + * e.g. 'adb wait-for-device shell'. + */ + if(argc > 1) { + argc--; + argv++; + goto top; + } + return 0; + } + + if(!strcmp(argv[0], "forward")) { + char host_prefix[64]; + char remove = 0; + char remove_all = 0; + char list = 0; + char no_rebind = 0; + + // Parse options here. + while (argc > 1 && argv[1][0] == '-') { + if (!strcmp(argv[1], "--list")) + list = 1; + else if (!strcmp(argv[1], "--remove")) + remove = 1; + else if (!strcmp(argv[1], "--remove-all")) + remove_all = 1; + else if (!strcmp(argv[1], "--no-rebind")) + no_rebind = 1; + else { + return usage(); + } + argc--; + argv++; + } + + // Ensure we can only use one option at a time. + if (list + remove + remove_all + no_rebind > 1) { + return usage(); + } + + // Determine the for this command. + if (serial) { + snprintf(host_prefix, sizeof host_prefix, "host-serial:%s", + serial); + } else if (ttype == kTransportUsb) { + snprintf(host_prefix, sizeof host_prefix, "host-usb"); + } else if (ttype == kTransportLocal) { + snprintf(host_prefix, sizeof host_prefix, "host-local"); + } else { + snprintf(host_prefix, sizeof host_prefix, "host"); + } + + // Implement forward --list + if (list) { + if (argc != 1) + return usage(); + snprintf(buf, sizeof buf, "%s:list-forward", host_prefix); + char* forwards = adb_query(buf); + if (forwards == NULL) { + fprintf(stderr, "error: %s\n", adb_error()); + return 1; + } + printf("%s", forwards); + free(forwards); + return 0; + } + + // Implement forward --remove-all + else if (remove_all) { + if (argc != 1) + return usage(); + snprintf(buf, sizeof buf, "%s:killforward-all", host_prefix); + } + + // Implement forward --remove + else if (remove) { + if (argc != 2) + return usage(); + snprintf(buf, sizeof buf, "%s:killforward:%s", host_prefix, argv[1]); + } + // Or implement one of: + // forward + // forward --no-rebind + else + { + if (argc != 3) + return usage(); + const char* command = no_rebind ? "forward:norebind:" : "forward"; + snprintf(buf, sizeof buf, "%s:%s:%s;%s", host_prefix, command, argv[1], argv[2]); + } + + if(adb_command(buf)) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + return 0; + } + + /* do_sync_*() commands */ + + if(!strcmp(argv[0], "ls")) { + if(argc != 2) return usage(); + return do_sync_ls(argv[1]); + } + + if(!strcmp(argv[0], "push")) { + if(argc != 3) return usage(); + return do_sync_push(argv[1], argv[2], 0 /* no verify APK */); + } + + if(!strcmp(argv[0], "pull")) { + if (argc == 2) { + return do_sync_pull(argv[1], "."); + } else if (argc == 3) { + return do_sync_pull(argv[1], argv[2]); + } else { + return usage(); + } + } + + if(!strcmp(argv[0], "install")) { + if (argc < 2) return usage(); + return install_app(ttype, serial, argc, argv); + } + + if(!strcmp(argv[0], "uninstall")) { + if (argc < 2) return usage(); + return uninstall_app(ttype, serial, argc, argv); + } + + if(!strcmp(argv[0], "sync")) { + char *srcarg, *android_srcpath, *data_srcpath; + int listonly = 0; + + int ret; + if(argc < 2) { + /* No local path was specified. */ + srcarg = NULL; + } else if (argc >= 2 && strcmp(argv[1], "-l") == 0) { + listonly = 1; + if (argc == 3) { + srcarg = argv[2]; + } else { + srcarg = NULL; + } + } else if(argc == 2) { + /* A local path or "android"/"data" arg was specified. */ + srcarg = argv[1]; + } else { + return usage(); + } + ret = find_sync_dirs(srcarg, &android_srcpath, &data_srcpath); + if(ret != 0) return usage(); + + if(android_srcpath != NULL) + ret = do_sync_sync(android_srcpath, "/system", listonly); + if(ret == 0 && data_srcpath != NULL) + ret = do_sync_sync(data_srcpath, "/data", listonly); + + free(android_srcpath); + free(data_srcpath); + return ret; + } + + /* passthrough commands */ + + if(!strcmp(argv[0],"get-state") || + !strcmp(argv[0],"get-serialno") || + !strcmp(argv[0],"get-devpath")) + { + char *tmp; + + format_host_command(buf, sizeof buf, argv[0], ttype, serial); + tmp = adb_query(buf); + if(tmp) { + printf("%s\n", tmp); + return 0; + } else { + return 1; + } + } + + /* other commands */ + + if(!strcmp(argv[0],"status-window")) { + status_window(ttype, serial); + return 0; + } + + if(!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) { + return logcat(ttype, serial, argc, argv); + } + + if(!strcmp(argv[0],"ppp")) { + return ppp(argc, argv); + } + + if (!strcmp(argv[0], "start-server")) { + return adb_connect("host:start-server"); + } + + if (!strcmp(argv[0], "backup")) { + return backup(argc, argv); + } + + if (!strcmp(argv[0], "restore")) { + return restore(argc, argv); + } + + if (!strcmp(argv[0], "jdwp")) { + int fd = adb_connect("jdwp"); + if (fd >= 0) { + read_and_dump(fd); + adb_close(fd); + return 0; + } else { + fprintf(stderr, "error: %s\n", adb_error()); + return -1; + } + } + + /* "adb /?" is a common idiom under Windows */ + if(!strcmp(argv[0], "help") || !strcmp(argv[0], "/?")) { + help(); + return 0; + } + + if(!strcmp(argv[0], "version")) { + version(stdout); + return 0; + } + + usage(); + return 1; +} + +static int do_cmd(transport_type ttype, char* serial, char *cmd, ...) +{ + char *argv[16]; + int argc; + va_list ap; + + va_start(ap, cmd); + argc = 0; + + if (serial) { + argv[argc++] = "-s"; + argv[argc++] = serial; + } else if (ttype == kTransportUsb) { + argv[argc++] = "-d"; + } else if (ttype == kTransportLocal) { + argv[argc++] = "-e"; + } + + argv[argc++] = cmd; + while((argv[argc] = va_arg(ap, char*)) != 0) argc++; + va_end(ap); + +#if 0 + int n; + fprintf(stderr,"argc = %d\n",argc); + for(n = 0; n < argc; n++) { + fprintf(stderr,"argv[%d] = \"%s\"\n", n, argv[n]); + } +#endif + + return adb_commandline(argc, argv); +} + +int find_sync_dirs(const char *srcarg, + char **android_srcdir_out, char **data_srcdir_out) +{ + char *android_srcdir, *data_srcdir; + + if(srcarg == NULL) { + android_srcdir = product_file("system"); + data_srcdir = product_file("data"); + } else { + /* srcarg may be "data", "system" or NULL. + * if srcarg is NULL, then both data and system are synced + */ + if(strcmp(srcarg, "system") == 0) { + android_srcdir = product_file("system"); + data_srcdir = NULL; + } else if(strcmp(srcarg, "data") == 0) { + android_srcdir = NULL; + data_srcdir = product_file("data"); + } else { + /* It's not "system" or "data". + */ + return 1; + } + } + + if(android_srcdir_out != NULL) + *android_srcdir_out = android_srcdir; + else + free(android_srcdir); + + if(data_srcdir_out != NULL) + *data_srcdir_out = data_srcdir; + else + free(data_srcdir); + + return 0; +} + +static int pm_command(transport_type transport, char* serial, + int argc, char** argv) +{ + char buf[4096]; + + snprintf(buf, sizeof(buf), "shell:pm"); + + while(argc-- > 0) { + char *quoted; + + quoted = dupAndQuote(*argv++); + + strncat(buf, " ", sizeof(buf)-1); + strncat(buf, quoted, sizeof(buf)-1); + free(quoted); + } + + send_shellcommand(transport, serial, buf); + return 0; +} + +int uninstall_app(transport_type transport, char* serial, int argc, 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(transport_type transport, char* serial, char* filename) +{ + char buf[4096]; + char* quoted; + + snprintf(buf, sizeof(buf), "shell:rm "); + quoted = dupAndQuote(filename); + strncat(buf, quoted, sizeof(buf)-1); + free(quoted); + + send_shellcommand(transport, serial, buf); + return 0; +} + +static const char* get_basename(const char* filename) +{ + const char* basename = adb_dirstop(filename); + if (basename) { + basename++; + return basename; + } else { + return filename; + } +} + +static int check_file(const char* filename) +{ + struct stat st; + + if (filename == NULL) { + return 0; + } + + if (stat(filename, &st) != 0) { + fprintf(stderr, "can't find '%s' to install\n", filename); + return 1; + } + + if (!S_ISREG(st.st_mode)) { + fprintf(stderr, "can't install '%s' because it's not a file\n", filename); + return 1; + } + + return 0; +} + +int install_app(transport_type transport, char* serial, int argc, 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; + char apk_dest[PATH_MAX]; + char verification_dest[PATH_MAX]; + char* apk_file; + char* verification_file = NULL; + int file_arg = -1; + int err; + int i; + int verify_apk = 1; + + for (i = 1; i < argc; i++) { + if (*argv[i] != '-') { + file_arg = i; + break; + } else if (!strcmp(argv[i], "-i")) { + // Skip the installer package name. + i++; + } else if (!strcmp(argv[i], "-s")) { + where = SD_DEST; + } else if (!strcmp(argv[i], "--algo")) { + verify_apk = 0; + i++; + } else if (!strcmp(argv[i], "--iv")) { + verify_apk = 0; + i++; + } else if (!strcmp(argv[i], "--key")) { + verify_apk = 0; + i++; + } + } + + if (file_arg < 0) { + fprintf(stderr, "can't find filename in arguments\n"); + return 1; + } else if (file_arg + 2 < argc) { + fprintf(stderr, "too many files specified; only takes APK file and verifier file\n"); + return 1; + } + + apk_file = argv[file_arg]; + + if (file_arg != argc - 1) { + verification_file = argv[file_arg + 1]; + } + + if (check_file(apk_file) || check_file(verification_file)) { + return 1; + } + + snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file)); + if (verification_file != NULL) { + snprintf(verification_dest, sizeof(verification_dest), where, get_basename(verification_file)); + + if (!strcmp(apk_dest, verification_dest)) { + fprintf(stderr, "APK and verification file can't have the same name\n"); + return 1; + } + } + + err = do_sync_push(apk_file, apk_dest, verify_apk); + if (err) { + goto cleanup_apk; + } else { + argv[file_arg] = apk_dest; /* destination name, not source location */ + } + + if (verification_file != NULL) { + err = do_sync_push(verification_file, verification_dest, 0 /* no verify APK */); + if (err) { + goto cleanup_apk; + } else { + argv[file_arg + 1] = verification_dest; /* destination name, not source location */ + } + } + + pm_command(transport, serial, argc, argv); + +cleanup_apk: + if (verification_file != NULL) { + delete_file(transport, serial, verification_dest); + } + + delete_file(transport, serial, apk_dest); + + return err; +} diff --git a/adb/console.c b/adb/console.c new file mode 100644 index 0000000..b813d33 --- /dev/null +++ b/adb/console.c @@ -0,0 +1,45 @@ +#include "sysdeps.h" +#include "adb.h" +#include "adb_client.h" +#include + +static int connect_to_console(void) +{ + int fd, port; + + port = adb_get_emulator_console_port(); + if (port < 0) { + if (port == -2) + fprintf(stderr, "error: more than one emulator detected. use -s option\n"); + else + fprintf(stderr, "error: no emulator detected\n"); + return -1; + } + fd = socket_loopback_client( port, SOCK_STREAM ); + if (fd < 0) { + fprintf(stderr, "error: could not connect to TCP port %d\n", port); + return -1; + } + return fd; +} + + +int adb_send_emulator_command(int argc, char** argv) +{ + int fd, nn; + + fd = connect_to_console(); + if (fd < 0) + return 1; + +#define QUIT "quit\n" + + for (nn = 1; nn < argc; nn++) { + adb_write( fd, argv[nn], strlen(argv[nn]) ); + adb_write( fd, (nn == argc-1) ? "\n" : " ", 1 ); + } + adb_write( fd, QUIT, sizeof(QUIT)-1 ); + adb_close(fd); + + return 0; +} diff --git a/adb/fdevent.c b/adb/fdevent.c new file mode 100644 index 0000000..5c374a7 --- /dev/null +++ b/adb/fdevent.c @@ -0,0 +1,695 @@ +/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c +** +** Copyright 2006, Brian Swetland +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "fdevent.h" +#include "transport.h" +#include "sysdeps.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 */ + +// 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. +int SHELL_EXIT_NOTIFY_FD = -1; + +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 +#define D(...) \ + do { \ + adb_mutex_lock(&D_lock); \ + int save_errno = errno; \ + fprintf(stderr, "%s::%s():", __FILE__, __FUNCTION__); \ + errno = save_errno; \ + fprintf(stderr, __VA_ARGS__); \ + adb_mutex_unlock(&D_lock); \ + errno = save_errno; \ + } while(0) +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 D(...) ((void)0) +#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 void fdevent_subproc_event_func(int fd, unsigned events, void *userdata); + +static fdevent list_pending = { + .next = &list_pending, + .prev = &list_pending, +}; + +static fdevent **fd_table = 0; +static int fd_table_max = 0; + +#ifdef CRAPTASTIC +//HAVE_EPOLL + +#include + +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 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); + } + } +} + +#else /* USE_SELECT */ + +#ifdef HAVE_WINSOCK +#include +#else +#include +#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 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 = 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); +} + +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(readx(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); + } +} + +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->force_eof = 0; + fde->func = func; + fde->arg = arg; + +#ifndef HAVE_WINSOCK + fcntl(fd, F_SETFL, O_NONBLOCK); +#endif + 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) == 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_subproc_setup() +{ + int s[2]; + + if(adb_socketpair(s)) { + FATAL("cannot create shell-exit socket-pair\n"); + } + 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); +} + +void fdevent_loop() +{ + fdevent *fde; + fdevent_subproc_setup(); + + for(;;) { + D("--- ---- waiting for events\n"); + + fdevent_process(); + + while((fde = fdevent_plist_dequeue())) { + fdevent_call_fdfunc(fde); + } + } +} diff --git a/adb/fdevent.h b/adb/fdevent.h new file mode 100644 index 0000000..a0ebe2a --- /dev/null +++ b/adb/fdevent.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __FDEVENT_H +#define __FDEVENT_H + +#include /* 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 + +typedef struct fdevent fdevent; + +typedef void (*fd_func)(int fd, unsigned events, void *userdata); + +/* Allocate and initialize a new fdevent object + * Note: use FD_TIMER as 'fd' to create a fd-less object + * (used to implement timers). +*/ +fdevent *fdevent_create(int fd, fd_func func, void *arg); + +/* Uninitialize and deallocate an fdevent object that was +** created by fdevent_create() +*/ +void fdevent_destroy(fdevent *fde); + +/* Initialize an fdevent object that was externally allocated +*/ +void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg); + +/* Uninitialize an fdevent object that was initialized by +** fdevent_install() +*/ +void fdevent_remove(fdevent *item); + +/* Change which events should cause notifications +*/ +void fdevent_set(fdevent *fde, unsigned events); +void fdevent_add(fdevent *fde, unsigned events); +void fdevent_del(fdevent *fde, unsigned events); + +void fdevent_set_timeout(fdevent *fde, int64_t timeout_ms); + +/* loop forever, handling events. +*/ +void fdevent_loop(); + +struct fdevent +{ + fdevent *next; + fdevent *prev; + + int fd; + int force_eof; + + unsigned short state; + unsigned short events; + + fd_func func; + void *arg; +}; + + +#endif diff --git a/adb/file_sync_client.c b/adb/file_sync_client.c new file mode 100644 index 0000000..354d0fb --- /dev/null +++ b/adb/file_sync_client.c @@ -0,0 +1,1024 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sysdeps.h" +#include "adb.h" +#include "adb_client.h" +#include "file_sync_service.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); +} + +void sync_quit(int fd) +{ + syncmsg msg; + + msg.req.id = ID_QUIT; + msg.req.namelen = 0; + + writex(fd, &msg.req, sizeof(msg.req)); +} + +typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie); + +int sync_ls(int fd, const char *path, sync_ls_cb func, void *cookie) +{ + syncmsg msg; + char buf[257]; + int len; + + len = strlen(path); + if(len > 1024) goto fail; + + msg.req.id = ID_LIST; + msg.req.namelen = htoll(len); + + if(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, path, len)) { + goto fail; + } + + for(;;) { + if(readx(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; + + if(readx(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; +} + +typedef struct syncsendbuf syncsendbuf; + +struct syncsendbuf { + unsigned id; + unsigned size; + char data[SYNC_DATA_MAX]; +}; + +static syncsendbuf send_buffer; + +int sync_readtime(int fd, const char *path, unsigned *timestamp) +{ + syncmsg msg; + int len = strlen(path); + + msg.req.id = ID_STAT; + msg.req.namelen = htoll(len); + + if(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, path, len)) { + return -1; + } + + if(readx(fd, &msg.stat, sizeof(msg.stat))) { + return -1; + } + + if(msg.stat.id != ID_STAT) { + return -1; + } + + *timestamp = ltohl(msg.stat.time); + 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(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, path, len)) { + return -1; + } + + return 0; +} + +static int sync_finish_readtime(int fd, unsigned int *timestamp, + unsigned int *mode, unsigned int *size) +{ + syncmsg msg; + + if(readx(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; +} + +int sync_readmode(int fd, const char *path, unsigned *mode) +{ + syncmsg msg; + int len = strlen(path); + + msg.req.id = ID_STAT; + msg.req.namelen = htoll(len); + + if(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, path, len)) { + return -1; + } + + if(readx(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) +{ + int lfd, err = 0; + + lfd = adb_open(path, O_RDONLY); + if(lfd < 0) { + fprintf(stderr,"cannot open '%s': %s\n", path, strerror(errno)); + return -1; + } + + 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(writex(fd, sbuf, sizeof(unsigned) * 2 + ret)){ + err = -1; + break; + } + total_bytes += ret; + } + + adb_close(lfd); + return err; +} + +static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf) +{ + 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(writex(fd, sbuf, sizeof(unsigned) * 2 + count)){ + err = -1; + break; + } + total += count; + total_bytes += count; + } + + return err; +} + +#ifdef HAVE_SYMLINKS +static int write_data_link(int fd, const char *path, syncsendbuf *sbuf) +{ + int len, ret; + + 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; + + ret = writex(fd, sbuf, sizeof(unsigned) * 2 + len + 1); + if(ret) + 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, int verifyApk) +{ + 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); + + if (verifyApk) { + int lfd; + zipfile_t zip; + zipentry_t entry; + int amt; + + // if we are transferring an APK file, then sanity check to make sure + // we have a real zip file that contains an AndroidManifest.xml + // this requires that we read the entire file into memory. + lfd = adb_open(lpath, O_RDONLY); + if(lfd < 0) { + fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno)); + return -1; + } + + size = adb_lseek(lfd, 0, SEEK_END); + if (size == -1 || -1 == adb_lseek(lfd, 0, SEEK_SET)) { + fprintf(stderr, "error seeking in file '%s'\n", lpath); + adb_close(lfd); + return 1; + } + + file_buffer = (char *)malloc(size); + if (file_buffer == NULL) { + fprintf(stderr, "could not allocate buffer for '%s'\n", + lpath); + adb_close(lfd); + return 1; + } + amt = adb_read(lfd, file_buffer, size); + if (amt != size) { + fprintf(stderr, "error reading from file: '%s'\n", lpath); + adb_close(lfd); + free(file_buffer); + return 1; + } + + adb_close(lfd); + + zip = init_zipfile(file_buffer, size); + if (zip == NULL) { + fprintf(stderr, "file '%s' is not a valid zip file\n", + lpath); + free(file_buffer); + return 1; + } + + entry = lookup_zipentry(zip, "AndroidManifest.xml"); + release_zipfile(zip); + if (entry == NULL) { + fprintf(stderr, "file '%s' does not contain AndroidManifest.xml\n", + lpath); + free(file_buffer); + return 1; + } + } + + msg.req.id = ID_SEND; + msg.req.namelen = htoll(len + r); + + if(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, rpath, len) || writex(fd, tmp, r)) { + free(file_buffer); + goto fail; + } + + if (file_buffer) { + write_data_buffer(fd, file_buffer, size, sbuf); + free(file_buffer); + } else if (S_ISREG(mode)) + write_data_file(fd, lpath, sbuf); +#ifdef HAVE_SYMLINKS + else if (S_ISLNK(mode)) + write_data_link(fd, lpath, sbuf); +#endif + else + goto fail; + + msg.data.id = ID_DONE; + msg.data.size = htoll(mtime); + if(writex(fd, &msg.data, sizeof(msg.data))) + goto fail; + + if(readx(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(readx(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 mkdirs(char *name) +{ + int ret; + char *x = name + 1; + + for(;;) { + x = adb_dirstart(x); + if(x == 0) return 0; + *x = 0; + ret = adb_mkdir(name, 0775); + *x = OS_PATH_SEPARATOR; + if((ret < 0) && (errno != EEXIST)) { + return ret; + } + x++; + } + return 0; +} + +int sync_recv(int fd, const char *rpath, const char *lpath) +{ + syncmsg msg; + int len; + int lfd = -1; + char *buffer = send_buffer.data; + unsigned id; + + len = strlen(rpath); + if(len > 1024) return -1; + + msg.req.id = ID_RECV; + msg.req.namelen = htoll(len); + if(writex(fd, &msg.req, sizeof(msg.req)) || + writex(fd, rpath, len)) { + return -1; + } + + if(readx(fd, &msg.data, sizeof(msg.data))) { + return -1; + } + id = msg.data.id; + + if((id == ID_DATA) || (id == ID_DONE)) { + adb_unlink(lpath); + mkdirs((char *)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(readx(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(readx(fd, buffer, len)) { + adb_close(lfd); + return -1; + } + + if(writex(lfd, buffer, len)) { + fprintf(stderr,"cannot write '%s': %s\n", rpath, strerror(errno)); + adb_close(lfd); + return -1; + } + + total_bytes += len; + } + + 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(readx(fd, buffer, len)) { + return -1; + } + buffer[len] = 0; + } else { + memcpy(buffer, &id, 4); + buffer[4] = 0; +// strcpy(buffer,"unknown reason"); + } + 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) +{ + int fd = adb_connect("sync:"); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + if(sync_ls(fd, path, do_sync_ls_cb, 0)) { + return 1; + } else { + sync_quit(fd); + return 0; + } +} + +typedef struct copyinfo copyinfo; + +struct copyinfo +{ + copyinfo *next; + const char *src; + const char *dst; + unsigned int time; + unsigned int mode; + unsigned int size; + int flag; + //char data[0]; +}; + +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; + + copyinfo *ci = malloc(sizeof(copyinfo) + ssize + dsize); + if(ci == 0) { + fprintf(stderr,"out of memory\n"); + abort(); + } + + 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); + +// fprintf(stderr,"mkcopyinfo('%s','%s')\n", ci->src, ci->dst); + 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; + +// fprintf(stderr,"local_build_list('%s','%s')\n", lpath, rpath); + + d = opendir(lpath); + if(d == 0) { + fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno)); + return -1; + } + + while((de = readdir(d))) { + char stat_path[PATH_MAX]; + char *name = de->d_name; + + if(name[0] == '.') { + if(name[1] == 0) continue; + if((name[1] == '.') && (name[2] == 0)) continue; + } + + /* + * 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); + stat(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)); + 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; + } + } + } + + closedir(d); + + for(ci = dirlist; ci != 0; ci = next) { + next = ci->next; + local_build_list(filelist, ci->src, ci->dst); + free(ci); + } + + return 0; +} + + +static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps, int listonly) +{ + copyinfo *filelist = 0; + copyinfo *ci, *next; + int pushed = 0; + int skipped = 0; + + if((lpath[0] == 0) || (rpath[0] == 0)) return -1; + if(lpath[strlen(lpath) - 1] != '/') { + int tmplen = strlen(lpath)+2; + char *tmp = 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 = malloc(tmplen); + if(tmp == 0) return -1; + snprintf(tmp, tmplen, "%s/",rpath); + rpath = tmp; + } + + 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; + } + } + 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(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 verify APK */)){ + return 1; + } + 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; +} + + +int do_sync_push(const char *lpath, const char *rpath, int verifyApk) +{ + struct stat st; + unsigned mode; + int fd; + + fd = adb_connect("sync:"); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + if(stat(lpath, &st)) { + fprintf(stderr,"cannot stat '%s': %s\n", lpath, strerror(errno)); + sync_quit(fd); + return 1; + } + + if(S_ISDIR(st.st_mode)) { + BEGIN(); + if(copy_local_dir_remote(fd, lpath, rpath, 0, 0)) { + return 1; + } else { + END(); + sync_quit(fd); + } + } else { + if(sync_readmode(fd, rpath, &mode)) { + return 1; + } + if((mode != 0) && S_ISDIR(mode)) { + /* if we're copying a local file to a remote directory, + ** we *really* want to copy to remotedir + "/" + localfilename + */ + const char *name = adb_dirstop(lpath); + if(name == 0) { + name = lpath; + } else { + name++; + } + int tmplen = strlen(name) + strlen(rpath) + 2; + char *tmp = malloc(strlen(name) + strlen(rpath) + 2); + if(tmp == 0) return 1; + snprintf(tmp, tmplen, "%s/%s", rpath, name); + rpath = tmp; + } + BEGIN(); + if(sync_send(fd, lpath, rpath, st.st_mtime, st.st_mode, verifyApk)) { + return 1; + } else { + END(); + sync_quit(fd); + return 0; + } + } + + return 0; +} + + +typedef struct { + copyinfo **filelist; + copyinfo **dirlist; + const char *rpath; + const char *lpath; +} sync_ls_build_list_cb_args; + +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; + } + + 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; + } + free(dirlist); + dirlist = next; + } + + return 0; +} + +static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath, + int checktimestamps) +{ + copyinfo *filelist = 0; + copyinfo *ci, *next; + int pulled = 0; + int skipped = 0; + + /* Make sure that both directory paths end in a slash. */ + if (rpath[0] == 0 || lpath[0] == 0) return -1; + if (rpath[strlen(rpath) - 1] != '/') { + int tmplen = strlen(rpath) + 2; + char *tmp = malloc(tmplen); + if (tmp == 0) return -1; + snprintf(tmp, tmplen, "%s/", rpath); + rpath = tmp; + } + if (lpath[strlen(lpath) - 1] != '/') { + int tmplen = strlen(lpath) + 2; + char *tmp = malloc(tmplen); + if (tmp == 0) return -1; + snprintf(tmp, tmplen, "%s/", lpath); + lpath = tmp; + } + + fprintf(stderr, "pull: building file list...\n"); + /* Recursively build the list of files to copy. */ + if (remote_build_list(fd, &filelist, rpath, lpath)) { + return -1; + } + +#if 0 + if (checktimestamps) { + for (ci = filelist; ci != 0; ci = ci->next) { + if (sync_start_readtime(fd, ci->dst)) { + return 1; + } + } + 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; + } + } + } +#endif + 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)) { + return 1; + } + pulled++; + } else { + skipped++; + } + free(ci); + } + + fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n", + pulled, (pulled == 1) ? "" : "s", + skipped, (skipped == 1) ? "" : "s"); + + return 0; +} + +int do_sync_pull(const char *rpath, const char *lpath) +{ + unsigned mode; + struct stat st; + + int fd; + + fd = adb_connect("sync:"); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + if(sync_readmode(fd, rpath, &mode)) { + return 1; + } + if(mode == 0) { + fprintf(stderr,"remote object '%s' does not exist\n", rpath); + return 1; + } + + if(S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) { + 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 localdir + "/" + remotefilename + */ + const char *name = adb_dirstop(rpath); + if(name == 0) { + name = rpath; + } else { + name++; + } + int tmplen = strlen(name) + strlen(lpath) + 2; + char *tmp = malloc(tmplen); + if(tmp == 0) return 1; + snprintf(tmp, tmplen, "%s/%s", lpath, name); + lpath = tmp; + } + } + BEGIN(); + if(sync_recv(fd, rpath, lpath)) { + return 1; + } else { + END(); + sync_quit(fd); + return 0; + } + } else if(S_ISDIR(mode)) { + BEGIN(); + if (copy_remote_dir_local(fd, rpath, lpath, 0)) { + return 1; + } else { + END(); + sync_quit(fd); + return 0; + } + } else { + fprintf(stderr,"remote object '%s' not a file or directory\n", rpath); + return 1; + } +} + +int do_sync_sync(const char *lpath, const char *rpath, int listonly) +{ + fprintf(stderr,"syncing %s...\n",rpath); + + int fd = adb_connect("sync:"); + if(fd < 0) { + fprintf(stderr,"error: %s\n", adb_error()); + return 1; + } + + BEGIN(); + if(copy_local_dir_remote(fd, lpath, rpath, 1, listonly)){ + return 1; + } else { + END(); + sync_quit(fd); + return 0; + } +} diff --git a/adb/file_sync_service.c b/adb/file_sync_service.c new file mode 100644 index 0000000..d3e841b --- /dev/null +++ b/adb/file_sync_service.c @@ -0,0 +1,414 @@ +/* + * 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 +#include +#include + +#include +#include +#include +#include + +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_SYNC +#include "adb.h" +#include "file_sync_service.h" + +static int mkdirs(char *name) +{ + int ret; + char *x = name + 1; + + if(name[0] != '/') return -1; + + for(;;) { + x = adb_dirstart(x); + if(x == 0) return 0; + *x = 0; + ret = adb_mkdir(name, 0775); + if((ret < 0) && (errno != EEXIST)) { + D("mkdir(\"%s\") -> %s\n", name, strerror(errno)); + *x = '/'; + return ret; + } + *x++ = '/'; + } + return 0; +} + +static int do_stat(int s, const char *path) +{ + syncmsg msg; + struct stat st; + + 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 writex(s, &msg.stat, sizeof(msg.stat)); +} + +static int do_list(int s, const char *path) +{ + DIR *d; + struct dirent *de; + struct stat st; + syncmsg msg; + int len; + + char tmp[1024 + 256 + 1]; + char *fname; + + len = strlen(path); + memcpy(tmp, path, len); + tmp[len] = '/'; + fname = tmp + len + 1; + + msg.dent.id = ID_DENT; + + d = opendir(path); + if(d == 0) goto done; + + while((de = readdir(d))) { + int len = strlen(de->d_name); + + /* not supposed to be possible, but + if it does happen, let's not buffer overrun */ + if(len > 256) continue; + + 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(writex(s, &msg.dent, sizeof(msg.dent)) || + writex(s, de->d_name, len)) { + return -1; + } + } + } + + closedir(d); + +done: + msg.dent.id = ID_DONE; + msg.dent.mode = 0; + msg.dent.size = 0; + msg.dent.time = 0; + msg.dent.namelen = 0; + return writex(s, &msg.dent, sizeof(msg.dent)); +} + +static int fail_message(int s, const char *reason) +{ + syncmsg msg; + int len = strlen(reason); + + D("sync: failure: %s\n", reason); + + msg.data.id = ID_FAIL; + msg.data.size = htoll(len); + if(writex(s, &msg.data, sizeof(msg.data)) || + writex(s, reason, len)) { + return -1; + } else { + return 0; + } +} + +static int fail_errno(int s) +{ + return fail_message(s, strerror(errno)); +} + +static int handle_send_file(int s, char *path, mode_t mode, char *buffer) +{ + syncmsg msg; + unsigned int timestamp = 0; + int fd; + + fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode); + if(fd < 0 && errno == ENOENT) { + mkdirs(path); + fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL, mode); + } + if(fd < 0 && errno == EEXIST) { + fd = adb_open_mode(path, O_WRONLY, mode); + } + if(fd < 0) { + if(fail_errno(s)) + return -1; + fd = -1; + } + + for(;;) { + unsigned int len; + + if(readx(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); + break; + } + fail_message(s, "invalid data message"); + goto fail; + } + len = ltohl(msg.data.size); + if(len > SYNC_DATA_MAX) { + fail_message(s, "oversize data message"); + goto fail; + } + if(readx(s, buffer, len)) + goto fail; + + if(fd < 0) + continue; + if(writex(fd, buffer, len)) { + int saved_errno = errno; + adb_close(fd); + adb_unlink(path); + fd = -1; + errno = saved_errno; + if(fail_errno(s)) return -1; + } + } + + if(fd >= 0) { + struct utimbuf u; + adb_close(fd); + u.actime = timestamp; + u.modtime = timestamp; + utime(path, &u); + + msg.status.id = ID_OKAY; + msg.status.msglen = 0; + if(writex(s, &msg.status, sizeof(msg.status))) + return -1; + } + return 0; + +fail: + if(fd >= 0) + adb_close(fd); + adb_unlink(path); + return -1; +} + +#ifdef HAVE_SYMLINKS +static int handle_send_link(int s, char *path, char *buffer) +{ + syncmsg msg; + unsigned int len; + int ret; + + if(readx(s, &msg.data, sizeof(msg.data))) + return -1; + + if(msg.data.id != ID_DATA) { + fail_message(s, "invalid data message: expected ID_DATA"); + return -1; + } + + len = ltohl(msg.data.size); + if(len > SYNC_DATA_MAX) { + fail_message(s, "oversize data message"); + return -1; + } + if(readx(s, buffer, len)) + return -1; + + ret = symlink(buffer, path); + if(ret && errno == ENOENT) { + mkdirs(path); + ret = symlink(buffer, path); + } + if(ret) { + fail_errno(s); + return -1; + } + + if(readx(s, &msg.data, sizeof(msg.data))) + return -1; + + if(msg.data.id == ID_DONE) { + msg.status.id = ID_OKAY; + msg.status.msglen = 0; + if(writex(s, &msg.status, sizeof(msg.status))) + return -1; + } else { + fail_message(s, "invalid data message: expected ID_DONE"); + return -1; + } + + return 0; +} +#endif /* HAVE_SYMLINKS */ + +static int do_send(int s, char *path, char *buffer) +{ + char *tmp; + mode_t mode; + int is_link, ret; + + tmp = strrchr(path,','); + if(tmp) { + *tmp = 0; + errno = 0; + mode = strtoul(tmp + 1, NULL, 0); +#ifndef HAVE_SYMLINKS + is_link = 0; +#else + is_link = S_ISLNK(mode); +#endif + mode &= 0777; + } + if(!tmp || errno) { + mode = 0644; + is_link = 0; + } + + adb_unlink(path); + + +#ifdef HAVE_SYMLINKS + if(is_link) + ret = handle_send_link(s, path, buffer); + else { +#else + { +#endif + /* copy user permission bits to "group" and "other" permissions */ + mode |= ((mode >> 3) & 0070); + mode |= ((mode >> 3) & 0007); + + ret = handle_send_file(s, path, mode, buffer); + } + + return ret; +} + +static int do_recv(int s, const char *path, char *buffer) +{ + syncmsg msg; + int fd, r; + + fd = adb_open(path, O_RDONLY); + if(fd < 0) { + if(fail_errno(s)) return -1; + return 0; + } + + 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); + adb_close(fd); + return r; + } + msg.data.size = htoll(r); + if(writex(s, &msg.data, sizeof(msg.data)) || + writex(s, buffer, r)) { + adb_close(fd); + return -1; + } + } + + adb_close(fd); + + msg.data.id = ID_DONE; + msg.data.size = 0; + if(writex(s, &msg.data, sizeof(msg.data))) { + return -1; + } + + return 0; +} + +void file_sync_service(int fd, void *cookie) +{ + syncmsg msg; + char name[1025]; + unsigned namelen; + + char *buffer = malloc(SYNC_DATA_MAX); + if(buffer == 0) goto fail; + + for(;;) { + D("sync: waiting for command\n"); + + if(readx(fd, &msg.req, sizeof(msg.req))) { + fail_message(fd, "command read failure"); + break; + } + namelen = ltohl(msg.req.namelen); + if(namelen > 1024) { + fail_message(fd, "invalid namelen"); + break; + } + if(readx(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; + break; + case ID_LIST: + if(do_list(fd, name)) goto fail; + break; + case ID_SEND: + if(do_send(fd, name, buffer)) goto fail; + break; + case ID_RECV: + if(do_recv(fd, name, buffer)) goto fail; + break; + case ID_QUIT: + goto fail; + default: + fail_message(fd, "unknown command"); + goto fail; + } + } + +fail: + if(buffer != 0) free(buffer); + D("sync: done\n"); + adb_close(fd); +} diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h new file mode 100644 index 0000000..e402e06 --- /dev/null +++ b/adb/file_sync_service.h @@ -0,0 +1,87 @@ +/* + * 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 _FILE_SYNC_SERVICE_H_ +#define _FILE_SYNC_SERVICE_H_ + +#ifdef HAVE_BIG_ENDIAN +static inline unsigned __swap_uint32(unsigned x) +{ + return (((x) & 0xFF000000) >> 24) + | (((x) & 0x00FF0000) >> 8) + | (((x) & 0x0000FF00) << 8) + | (((x) & 0x000000FF) << 24); +} +#define htoll(x) __swap_uint32(x) +#define ltohl(x) __swap_uint32(x) +#define MKID(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((a) << 24)) +#else +#define htoll(x) (x) +#define ltohl(x) (x) +#define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) +#endif + +#define ID_STAT MKID('S','T','A','T') +#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') +#define ID_DONE MKID('D','O','N','E') +#define ID_DATA MKID('D','A','T','A') +#define ID_OKAY MKID('O','K','A','Y') +#define ID_FAIL MKID('F','A','I','L') +#define ID_QUIT MKID('Q','U','I','T') + +typedef union { + 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; + } dent; + struct { + unsigned id; + unsigned size; + } data; + struct { + unsigned id; + unsigned msglen; + } status; +} syncmsg; + + +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, int verifyApk); +int do_sync_sync(const char *lpath, const char *rpath, int listonly); +int do_sync_pull(const char *rpath, const char *lpath); + +#define SYNC_DATA_MAX (64*1024) + +#endif diff --git a/adb/framebuffer_service.c b/adb/framebuffer_service.c new file mode 100644 index 0000000..20c08d2 --- /dev/null +++ b/adb/framebuffer_service.c @@ -0,0 +1,180 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "fdevent.h" +#include "adb.h" + +#include +#include +#include + +/* TODO: +** - sync with vsync to avoid tearing +*/ +/* This version number defines the format of the fbinfo struct. + It must match versioning in ddms where this data is consumed. */ +#define DDMS_RAWIMAGE_VERSION 1 +struct fbinfo { + unsigned int version; + unsigned int bpp; + unsigned int size; + unsigned int width; + unsigned int height; + unsigned int red_offset; + unsigned int red_length; + unsigned int blue_offset; + unsigned int blue_length; + unsigned int green_offset; + unsigned int green_length; + unsigned int alpha_offset; + unsigned int alpha_length; +} __attribute__((packed)); + +void framebuffer_service(int fd, void *cookie) +{ + struct fbinfo fbinfo; + unsigned int i; + char buf[640]; + int fd_screencap; + int w, h, f; + int fds[2]; + + if (pipe(fds) < 0) goto done; + + pid_t pid = fork(); + if (pid < 0) goto done; + + if (pid == 0) { + dup2(fds[1], STDOUT_FILENO); + close(fds[0]); + close(fds[1]); + const char* command = "screencap"; + const char *args[2] = {command, NULL}; + execvp(command, (char**)args); + exit(1); + } + + fd_screencap = fds[0]; + + /* read w, h & format */ + if(readx(fd_screencap, &w, 4)) goto done; + if(readx(fd_screencap, &h, 4)) goto done; + if(readx(fd_screencap, &f, 4)) goto done; + + fbinfo.version = DDMS_RAWIMAGE_VERSION; + /* see hardware/hardware.h */ + switch (f) { + case 1: /* RGBA_8888 */ + fbinfo.bpp = 32; + fbinfo.size = w * h * 4; + fbinfo.width = w; + fbinfo.height = h; + fbinfo.red_offset = 0; + fbinfo.red_length = 8; + fbinfo.green_offset = 8; + fbinfo.green_length = 8; + fbinfo.blue_offset = 16; + fbinfo.blue_length = 8; + fbinfo.alpha_offset = 24; + fbinfo.alpha_length = 8; + break; + case 2: /* RGBX_8888 */ + fbinfo.bpp = 32; + fbinfo.size = w * h * 4; + fbinfo.width = w; + fbinfo.height = h; + fbinfo.red_offset = 0; + fbinfo.red_length = 8; + fbinfo.green_offset = 8; + fbinfo.green_length = 8; + fbinfo.blue_offset = 16; + fbinfo.blue_length = 8; + fbinfo.alpha_offset = 24; + fbinfo.alpha_length = 0; + break; + case 3: /* RGB_888 */ + fbinfo.bpp = 24; + fbinfo.size = w * h * 3; + fbinfo.width = w; + fbinfo.height = h; + fbinfo.red_offset = 0; + fbinfo.red_length = 8; + fbinfo.green_offset = 8; + fbinfo.green_length = 8; + fbinfo.blue_offset = 16; + fbinfo.blue_length = 8; + fbinfo.alpha_offset = 24; + fbinfo.alpha_length = 0; + break; + case 4: /* RGB_565 */ + fbinfo.bpp = 16; + fbinfo.size = w * h * 2; + fbinfo.width = w; + fbinfo.height = h; + fbinfo.red_offset = 11; + fbinfo.red_length = 5; + fbinfo.green_offset = 5; + fbinfo.green_length = 6; + fbinfo.blue_offset = 0; + fbinfo.blue_length = 5; + fbinfo.alpha_offset = 0; + fbinfo.alpha_length = 0; + break; + case 5: /* BGRA_8888 */ + fbinfo.bpp = 32; + fbinfo.size = w * h * 4; + fbinfo.width = w; + fbinfo.height = h; + fbinfo.red_offset = 16; + fbinfo.red_length = 8; + fbinfo.green_offset = 8; + fbinfo.green_length = 8; + fbinfo.blue_offset = 0; + fbinfo.blue_length = 8; + fbinfo.alpha_offset = 24; + fbinfo.alpha_length = 8; + break; + default: + goto done; + } + + /* write header */ + if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done; + + /* write data */ + for(i = 0; i < fbinfo.size; i += sizeof(buf)) { + if(readx(fd_screencap, buf, sizeof(buf))) goto done; + if(writex(fd, buf, sizeof(buf))) goto done; + } + if(readx(fd_screencap, buf, fbinfo.size % sizeof(buf))) goto done; + if(writex(fd, buf, fbinfo.size % sizeof(buf))) goto done; + +done: + TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0)); + + close(fds[0]); + close(fds[1]); + close(fd); +} diff --git a/adb/get_my_path_darwin.c b/adb/get_my_path_darwin.c new file mode 100644 index 0000000..5b95d15 --- /dev/null +++ b/adb/get_my_path_darwin.c @@ -0,0 +1,30 @@ +/* + * 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 +#include + +void get_my_path(char *s, size_t maxLen) +{ + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + CFDictionaryRef dict; + dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); + CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, + CFSTR("CFBundleExecutable")); + CFStringGetCString(value, s, maxLen, kCFStringEncodingUTF8); +} + diff --git a/adb/get_my_path_freebsd.c b/adb/get_my_path_freebsd.c new file mode 100644 index 0000000..b06ec66 --- /dev/null +++ b/adb/get_my_path_freebsd.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009 bsdroid project + * Alexey Tarasov + * + * 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 +#include +#include +#include + +void +get_my_path(char *exe, size_t maxLen) +{ + char proc[64]; + + snprintf(proc, sizeof(proc), "/proc/%d/file", getpid()); + + int err = readlink(proc, exe, maxLen - 1); + + exe[err > 0 ? err : 0] = '\0'; +} + diff --git a/adb/get_my_path_linux.c b/adb/get_my_path_linux.c new file mode 100644 index 0000000..179c3dd --- /dev/null +++ b/adb/get_my_path_linux.c @@ -0,0 +1,33 @@ +/* + * 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 +#include +#include +#include + +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/get_my_path_windows.c b/adb/get_my_path_windows.c new file mode 100644 index 0000000..ddf2816 --- /dev/null +++ b/adb/get_my_path_windows.c @@ -0,0 +1,34 @@ +/* + * 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 +#include +#include + +void get_my_path(char *exe, size_t maxLen) +{ + char *r; + + /* XXX: should be GetModuleFileNameA */ + if (GetModuleFileName(NULL, exe, maxLen) > 0) { + r = strrchr(exe, '\\'); + if (r != NULL) + *r = '\0'; + } else { + exe[0] = '\0'; + } +} + diff --git a/adb/jdwp_service.c b/adb/jdwp_service.c new file mode 100644 index 0000000..cd62b55 --- /dev/null +++ b/adb/jdwp_service.c @@ -0,0 +1,735 @@ +/* implement the "debug-ports" and "track-debug-ports" device services */ +#include "sysdeps.h" +#define TRACE_TAG TRACE_JDWP +#include "adb.h" +#include +#include +#include +#include + +/* 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" + 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. + + + JDWP thread @vm-debug-control + | | + |-------------------------------> | + | hello I'm in process | + | | + | | + + the connection is kept alive. it will be closed automatically if + the JDWP process terminates (this allows adbd to detect dead + processes). + + adbd thus maintains a list of "active" JDWP processes. it can send + its content to clients through the "device:debug-ports" service, + or even updates through the "device:track-debug-ports" service. + + when a debugger wants to connect, it simply runs the command + equivalent to "adb forward tcp: jdwp:" + + "jdwp:" is a new forward destination format used to target + a given JDWP process on the device. when sutch a request arrives, + adbd does the following: + + - first, it calls socketpair() to create a pair of equivalent + sockets. + + - it attaches the first socket in the pair to a local socket + which is itself attached to the transport's remote socket: + + + - it sends the file descriptor of the second socket directly + to the JDWP process with the help of sendmsg() + + + JDWP thread @vm-debug-control + | | + | <----------------------| + | OK, try this file descriptor | + | | + | | + + then, the JDWP thread uses this new socket descriptor as its + pass-through connection to the debugger (and receives the + JDWP-Handshake message, answers to it, etc...) + + this gives the following graphics: + ____________________________________ + | | + | ADB Server (host) | + | | + Debugger <---> LocalSocket <----> RemoteSocket | + | ^^ | + |___________________________||_______| + || + Transport || + (TCP for emulator - USB for device) || + || + ___________________________||_______ + | || | + | ADBD (device) || | + | VV | + JDWP <======> LocalSocket <----> RemoteSocket | + | | + |____________________________________| + + due to the way adb works, this doesn't need a special socket + type or fancy handling of socket termination if either the debugger + or the JDWP process closes the connection. + + THIS IS THE SIMPLEST IMPLEMENTATION I COULD FIND, IF YOU HAPPEN + TO HAVE A BETTER IDEA, LET ME KNOW - Digit + +**********************************************************************/ + +/** JDWP PID List Support Code + ** for each JDWP process, we record its pid and its connected socket + **/ + +#define MAX_OUT_FDS 4 + +#if !ADB_HOST + +#include +#include + +typedef struct JdwpProcess JdwpProcess; +struct JdwpProcess { + JdwpProcess* next; + JdwpProcess* prev; + int pid; + int socket; + fdevent* fde; + + char in_buff[4]; /* input character to read PID */ + int in_len; /* number from JDWP process */ + + int out_fds[MAX_OUT_FDS]; /* output array of file descriptors */ + int out_count; /* to send to the JDWP process */ +}; + +static JdwpProcess _jdwp_list; + +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; + + /* skip transient connections */ + if (proc->pid < 0) + continue; + + len = snprintf(p, end-p, "%d\n", proc->pid); + if (p + len >= end) + 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(); + } +} + + +static void jdwp_process_event(int, unsigned, void*); /* forward */ + + +static JdwpProcess* +jdwp_process_alloc( int socket ) +{ + JdwpProcess* proc = calloc(1,sizeof(*proc)); + + if (proc == NULL) { + D("not enough memory to create new JDWP process\n"); + return NULL; + } + + 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; +} + + +static void +jdwp_process_event( int socket, unsigned events, void* _proc ) +{ + JdwpProcess* proc = _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; + } + /* 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); + goto CloseProcess; + } + + /* all is well, keep reading to detect connection closure */ + D("Adding pid %d to jdwp process list\n", proc->pid); + jdwp_process_list_updated(); + } + 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]; + + for (;;) { + int len = recv(socket, buf, sizeof(buf), 0); + + if (len <= 0) { + if (len < 0 && errno == EINTR) + continue; + 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 ); + } + } + + CloseProcess: + if (proc->pid >= 0) + D( "remove pid %d to jdwp process list\n", proc->pid ); + jdwp_process_free(proc); + return; + } + } + + 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; + + 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_level = SOL_SOCKET; + 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)); + 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)); + goto CloseProcess; + } + + D("sent file descriptor %d to JDWP process %d\n", + fd, proc->pid); + + for (n = 1; n < proc->out_count; n++) + proc->out_fds[n-1] = proc->out_fds[n]; + + if (fcntl(proc->socket, F_SETFL, flags) == -1) { + D("failed to set O_NONBLOCK flag for socket %d: %s\n", + proc->pid, strerror(errno)); + goto CloseProcess; + } + + if (--proc->out_count == 0) + fdevent_del( proc->fde, FDE_WRITE ); + } + } +} + + +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 ) { + if (proc->pid == pid) { + goto FoundIt; + } + } + D("search failed !!\n"); + 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; + } + + 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 + ** + ** we do implement a custom asocket to receive the data + **/ + +/* name of the debug control Unix socket */ +#define JDWP_CONTROL_NAME "\0jdwp-control" +#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME)-1) + +typedef struct { + int listen_socket; + fdevent* fde; + +} JdwpControl; + + +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; + + if (pathlen >= maxpath) { + D( "vm debug control socket name too long (%d extra chars)\n", + pathlen+1-maxpath ); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, sockname, socknamelen); + + s = socket( AF_UNIX, SOCK_STREAM, 0 ); + if (s < 0) { + D( "could not create vm debug control socket. %d: %s\n", + errno, strerror(errno)); + return -1; + } + + 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) ); + adb_close(s); + return -1; + } + + if ( listen(s, 4) < 0 ) { + D("listen failed in jdwp control socket: %d: %s\n", + errno, strerror(errno)); + adb_close(s); + return -1; + } + + control->listen_socket = s; + + control->fde = fdevent_create(s, jdwp_control_event, control); + if (control->fde == NULL) { + D( "could not create fdevent for jdwp control socket\n" ); + 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); + return 0; +} + + +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; + } + /* the socket is probably closed ? */ + D( "weird accept() failed on jdwp control socket: %s\n", + strerror(errno) ); + return; + } + } + while (s < 0); + + proc = jdwp_process_alloc( s ); + if (proc == NULL) + return; + } +} + + +static JdwpControl _jdwp_control; + +/** "jdwp" local service implementation + ** this simply returns the list of known JDWP process pids + **/ + +typedef struct { + asocket socket; + int pass; +} JdwpSocket; + +static void +jdwp_socket_close( asocket* s ) +{ + asocket* peer = s->peer; + + remove_socket(s); + + if (peer) { + peer->peer = NULL; + peer->close(peer); + } + free(s); +} + +static int +jdwp_socket_enqueue( asocket* s, apacket* p ) +{ + /* you can't write to this asocket */ + put_apacket(p); + s->peer->close(s->peer); + return -1; +} + + +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(); + p->len = jdwp_process_list((char*)p->data, MAX_PAYLOAD); + peer->enqueue(peer, p); + jdwp->pass = 1; + } + else { + peer->close(peer); + } +} + +asocket* +create_jdwp_service_socket( void ) +{ + JdwpSocket* s = calloc(sizeof(*s),1); + + if (s == NULL) + return NULL; + + install_local_socket(&s->socket); + + s->socket.ready = jdwp_socket_ready; + s->socket.enqueue = jdwp_socket_enqueue; + s->socket.close = jdwp_socket_close; + s->pass = 0; + + return &s->socket; +} + +/** "track-jdwp" local service implementation + ** this periodically sends the list of known JDWP process pids + ** to the client... + **/ + +typedef struct JdwpTracker JdwpTracker; + +struct JdwpTracker { + asocket socket; + JdwpTracker* next; + JdwpTracker* prev; + int need_update; +}; + +static JdwpTracker _jdwp_trackers_list; + + +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; + memcpy(p->data, buffer, len); + p->len = len; + peer->enqueue( peer, p ); + } +} + +static void +jdwp_tracker_close( asocket* s ) +{ + JdwpTracker* tracker = (JdwpTracker*) s; + asocket* peer = s->peer; + + if (peer) { + peer->peer = NULL; + peer->close(peer); + } + + remove_socket(s); + + tracker->prev->next = tracker->next; + tracker->next->prev = tracker->prev; + + free(s); +} + +static void +jdwp_tracker_ready( asocket* s ) +{ + JdwpTracker* t = (JdwpTracker*) s; + + if (t->need_update) { + apacket* p = get_apacket(); + t->need_update = 0; + p->len = jdwp_process_list_msg((char*)p->data, sizeof(p->data)); + s->peer->enqueue(s->peer, p); + } +} + +static int +jdwp_tracker_enqueue( asocket* s, apacket* p ) +{ + /* you can't write to this socket */ + put_apacket(p); + s->peer->close(s->peer); + return -1; +} + + +asocket* +create_jdwp_tracker_service_socket( void ) +{ + JdwpTracker* t = calloc(sizeof(*t),1); + + if (t == NULL) + return NULL; + + t->next = &_jdwp_trackers_list; + t->prev = t->next->prev; + + t->next->prev = t; + t->prev->next = t; + + install_local_socket(&t->socket); + + 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; +} + + +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 ); +} + +#endif /* !ADB_HOST */ + diff --git a/adb/log_service.c b/adb/log_service.c new file mode 100644 index 0000000..af24356 --- /dev/null +++ b/adb/log_service.c @@ -0,0 +1,92 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "sysdeps.h" +#include "adb.h" + +#define LOG_FILE_DIR "/dev/log/" + +void write_log_entry(int fd, struct logger_entry *buf); + +void log_service(int fd, void *cookie) +{ + /* get the name of the log filepath to read */ + char * log_filepath = cookie; + + /* open the log file. */ + int logfd = unix_open(log_filepath, O_RDONLY); + if (logfd < 0) { + goto done; + } + + // temp buffer to read the entries + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); + struct logger_entry *entry = (struct logger_entry *) buf; + + while (1) { + int ret; + + ret = unix_read(logfd, entry, LOGGER_ENTRY_MAX_LEN); + if (ret < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + // perror("logcat read"); + goto done; + } + else if (!ret) { + // fprintf(stderr, "read: Unexpected EOF!\n"); + goto done; + } + + /* NOTE: driver guarantees we read exactly one full entry */ + + entry->msg[entry->len] = '\0'; + + write_log_entry(fd, entry); + } + +done: + unix_close(fd); + free(log_filepath); +} + +/* returns the full path to the log file in a newly allocated string */ +char * get_log_file_path(const char * log_name) { + char *log_device = malloc(strlen(LOG_FILE_DIR) + strlen(log_name) + 1); + + strcpy(log_device, LOG_FILE_DIR); + strcat(log_device, log_name); + + return log_device; +} + + +/* prints one log entry into the file descriptor fd */ +void write_log_entry(int fd, struct logger_entry *buf) +{ + size_t size = sizeof(struct logger_entry) + buf->len; + + writex(fd, buf, size); +} diff --git a/adb/mutex_list.h b/adb/mutex_list.h new file mode 100644 index 0000000..ff72751 --- /dev/null +++ b/adb/mutex_list.h @@ -0,0 +1,25 @@ +/* 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(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 new file mode 100644 index 0000000..c9d3c24 --- /dev/null +++ b/adb/protocol.txt @@ -0,0 +1,271 @@ + +--- a replacement for aproto ------------------------------------------- + +When it comes down to it, aproto's primary purpose is to forward +various streams between the host computer and client device (in either +direction). + +This replacement further simplifies the concept, reducing the protocol +to an extremely straightforward model optimized to accomplish the +forwarding of these streams and removing additional state or +complexity. + +The host side becomes a simple comms bridge with no "UI", which will +be used by either commandline or interactive tools to communicate with +a device or emulator that is connected to the bridge. + +The protocol is designed to be straightforward and well-defined enough +that if it needs to be reimplemented in another environment (Java +perhaps), there should not problems ensuring perfect interoperability. + +The protocol discards the layering aproto has and should allow the +implementation to be much more robust. + + +--- protocol overview and basics --------------------------------------- + +The transport layer deals in "messages", which consist of a 24 byte +header followed (optionally) by a payload. The header consists of 6 +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 */ +}; + +Receipt of an invalid message header, corrupt message payload, or an +unrecognized command MUST result in the closing of the remote +connection. The protocol depends on shared state and any break in the +message stream will result in state getting out of sync. + +The following sections describe the six defined message types in +detail. Their format is COMMAND(arg0, arg1, payload) where the payload +is represented by a quoted string or an empty string if none should be +sent. + +The identifiers "local-id" and "remote-id" are always relative to the +*sender* of the message, so for a receiver, the meanings are effectively +reversed. + + + +--- CONNECT(version, maxdata, "system-identity-string") ---------------- + +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 +is willing to accept. + +Currently, version=0x01000000 and maxdata=4096 + +Both sides send a CONNECT message when the connection between them is +established. Until a CONNECT message is received no other messages may +be sent. Any messages received before a CONNECT message MUST be ignored. + +If a CONNECT message is received with an unknown version or insufficiently +large maxdata value, the connection with the other side must be closed. + +The system identity string should be "::" +where systemtype is "bootloader", "device", or "host", serialno is some +kind of unique ID (or empty), and banner is a human-readable version +or identifier string. The banner is used to transmit useful properties. + + +--- AUTH(type, 0, "data") ---------------------------------------------- + +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 +AUTH packet where type is SIGNATURE(2) and data is the signature. If the +signature verification succeeds, the sender replies with a CONNECT packet. + +If the signature verification fails, the sender replies with a new AUTH +packet and a new random token, so that the recipient can retry signing +with a different private key. + +Once the recipient has tried all its private keys, it can reply with an +AUTH packet where type is RSAPUBLICKEY(3) and data is the public key. If +possible, an on-screen confirmation may be displayed for the user to +confirm they want to install the public key on the device. + + +--- OPEN(local-id, 0, "destination") ----------------------------------- + +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. + +The OPEN message MUST result in either a READY message indicating that +the connection has been established (and identifying the other end) or +a CLOSE message, indicating failure. An OPEN message also implies +a READY message sent at the same time. + +Common destination naming conventions include: + +* "tcp::" - host may be omitted to indicate localhost +* "udp::" - host may be omitted to indicate localhost +* "local-dgram:" +* "local-stream:" +* "shell" - local shell service +* "upload" - service for pushing files across (like aproto's /sync) +* "fs-bridge" - FUSE protocol filesystem bridge + + +--- READY(local-id, remote-id, "") ------------------------------------- + +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. + +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 +closed while this message was in-flight. + +The local-id is ignored on all but the first READY message (where it +is used to establish the connection). Nonetheless, the local-id MUST +not change on later READY messages sent to the same stream. + + + +--- WRITE(0, remote-id, "data") ---------------------------------------- + +The WRITE message sends data to the recipient's stream identified by +remote-id. The payload MUST be <= maxdata in length. + +A WRITE 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 +closed while this message was in-flight. + +A WRITE message may not be sent until a READY message is received. +Once a WRITE message is sent, an additional WRITE message may not be +sent until another READY message has been received. Recipients of +a WRITE message that is in violation of this requirement will CLOSE +the connection. + + +--- CLOSE(local-id, remote-id, "") ------------------------------------- + +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 +if this CLOSE indicates a failed OPEN. + +A CLOSE message containing a remote-id which does not map to an open +stream on the recipient's side is ignored. The stream may have +already been closed by the recipient while this message was in-flight. + +The recipient should not respond to a CLOSE message in any way. The +recipient should cancel pending WRITEs or CLOSEs, but this is not a +requirement, since they will be ignored. + + +--- SYNC(online, sequence, "") ----------------------------------------- + +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. + +* 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 +* when the io pump receives a matching SYNC(1, token), it once again + starts accepting messages to forward to the remote side + + +--- message command constants ------------------------------------------ + +#define A_SYNC 0x434e5953 +#define A_CNXN 0x4e584e43 +#define A_AUTH 0x48545541 +#define A_OPEN 0x4e45504f +#define A_OKAY 0x59414b4f +#define A_CLSE 0x45534c43 +#define A_WRTE 0x45545257 + + + +--- implementation details --------------------------------------------- + +The core of the bridge program will use three threads. One thread +will be a select/epoll loop to handle io between various inbound and +outbound connections and the connection to the remote side. + +The remote side connection will be implemented as two threads (one for +reading, one for writing) and a datagram socketpair to provide the +channel between the main select/epoll thread and the remote connection +threadpair. The reason for this is that for usb connections, the +kernel interface on linux and osx does not allow you to do meaningful +nonblocking IO. + +The endian swapping for the message headers will happen (as needed) in +the remote connection threadpair and that the rest of the program will +always treat message header values as native-endian. + +The bridge program will be able to have a number of mini-servers +compiled in. They will be published under known names (examples +"shell", "fs-bridge", etc) and upon receiving an OPEN() to such a +service, the bridge program will create a stream socketpair and spawn +a thread or subprocess to handle the io. + + +--- simplified / embedded implementation ------------------------------- + +For limited environments, like the bootloader, it is allowable to +support a smaller, fixed number of channels using pre-assigned channel +ID numbers such that only one stream may be connected to a bootloader +endpoint at any given time. The protocol remains unchanged, but the +"embedded" version of it is less dynamic. + +The bootloader will support two streams. A "bootloader:debug" stream, +which may be opened to get debug messages from the bootloader and a +"bootloader:control", stream which will support the set of basic +bootloader commands. + +Example command stream dialogues: + "flash_kernel,2515049,........\n" "okay\n" + "flash_ramdisk,5038,........\n" "fail,flash write error\n" + "bogus_command......" + + +--- future expansion --------------------------------------------------- + +I plan on providing either a message or a special control stream so that +the client device could ask the host computer to setup inbound socket +translations on the fly on behalf of the client device. + + +The initial design does handshaking to provide flow control, with a +message flow that looks like: + + >OPEN WRITE WRITE WRITE +server: "OKAY" + +client: +server: "FAIL" + diff --git a/adb/remount_service.c b/adb/remount_service.c new file mode 100644 index 0000000..ad61284 --- /dev/null +++ b/adb/remount_service.c @@ -0,0 +1,120 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_ADB +#include "adb.h" + + +static int system_ro = 1; + +/* Returns the device used to mount a directory in /proc/mounts */ +static char *find_mount(const char *dir) +{ + int fd; + int res; + int size; + char *token = NULL; + const char delims[] = "\n"; + char buf[4096]; + + fd = unix_open("/proc/mounts", O_RDONLY); + if (fd < 0) + return NULL; + + buf[sizeof(buf) - 1] = '\0'; + size = adb_read(fd, buf, sizeof(buf) - 1); + adb_close(fd); + + token = strtok(buf, delims); + + while (token) { + char mount_dev[256]; + char mount_dir[256]; + int mount_freq; + int mount_passno; + + res = sscanf(token, "%255s %255s %*s %*s %d %d\n", + mount_dev, mount_dir, &mount_freq, &mount_passno); + mount_dev[255] = 0; + mount_dir[255] = 0; + if (res == 4 && (strcmp(dir, mount_dir) == 0)) + return strdup(mount_dev); + + token = strtok(NULL, delims); + } + return NULL; +} + +/* Init mounts /system as read only, remount to enable writes. */ +static int remount_system() +{ + char *dev; + int fd; + int OFF = 0; + + if (system_ro == 0) { + return 0; + } + + dev = find_mount("/system"); + + if (!dev) + return -1; + + fd = unix_open(dev, O_RDONLY); + if (fd < 0) + return -1; + + ioctl(fd, BLKROSET, &OFF); + adb_close(fd); + + system_ro = mount(dev, "/system", "none", MS_REMOUNT, NULL); + + free(dev); + + return system_ro; +} + +static void write_string(int fd, const char* str) +{ + writex(fd, str, strlen(str)); +} + +void remount_service(int fd, void *cookie) +{ + int ret = remount_system(); + + if (!ret) + write_string(fd, "remount succeeded\n"); + else { + char buffer[200]; + snprintf(buffer, sizeof(buffer), "remount failed: %s\n", strerror(errno)); + write_string(fd, buffer); + } + + adb_close(fd); +} + diff --git a/adb/services.c b/adb/services.c new file mode 100644 index 0000000..42952fd --- /dev/null +++ b/adb/services.c @@ -0,0 +1,583 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_SERVICES +#include "adb.h" +#include "file_sync_service.h" + +#if ADB_HOST +# ifndef HAVE_WINSOCK +# include +# include +# include +# endif +#else +# include +# include +#endif + +typedef struct stinfo stinfo; + +struct stinfo { + void (*func)(int fd, void *cookie); + int fd; + void *cookie; +}; + + +void *service_bootstrap_func(void *x) +{ + stinfo *sti = x; + sti->func(sti->fd, sti->cookie); + free(sti); + return 0; +} + +#if !ADB_HOST + +void restart_root_service(int fd, void *cookie) +{ + char buf[100]; + char value[PROPERTY_VALUE_MAX]; + + if (getuid() == 0) { + snprintf(buf, sizeof(buf), "adbd is already running as root\n"); + writex(fd, buf, strlen(buf)); + adb_close(fd); + } else { + property_get("ro.debuggable", value, ""); + if (strcmp(value, "1") != 0) { + snprintf(buf, sizeof(buf), "adbd cannot run as root in production builds\n"); + writex(fd, buf, strlen(buf)); + adb_close(fd); + return; + } + + property_set("service.adb.root", "1"); + snprintf(buf, sizeof(buf), "restarting adbd as root\n"); + writex(fd, buf, strlen(buf)); + adb_close(fd); + } +} + +void restart_tcp_service(int fd, void *cookie) +{ + char buf[100]; + char value[PROPERTY_VALUE_MAX]; + int port = (int)cookie; + + if (port <= 0) { + snprintf(buf, sizeof(buf), "invalid port\n"); + writex(fd, buf, strlen(buf)); + adb_close(fd); + return; + } + + snprintf(value, sizeof(value), "%d", port); + property_set("service.adb.tcp.port", value); + snprintf(buf, sizeof(buf), "restarting in TCP mode port: %d\n", port); + writex(fd, buf, strlen(buf)); + adb_close(fd); +} + +void restart_usb_service(int fd, void *cookie) +{ + char buf[100]; + + property_set("service.adb.tcp.port", "0"); + snprintf(buf, sizeof(buf), "restarting in USB mode\n"); + writex(fd, buf, strlen(buf)); + adb_close(fd); +} + +void reboot_service(int fd, void *arg) +{ + char buf[100]; + char property_val[PROPERTY_VALUE_MAX]; + int ret; + + sync(); + + ret = snprintf(property_val, sizeof(property_val), "reboot,%s", (char *) arg); + if (ret >= (int) sizeof(property_val)) { + snprintf(buf, sizeof(buf), "reboot string too long. length=%d\n", ret); + writex(fd, buf, strlen(buf)); + goto cleanup; + } + + ret = property_set(ANDROID_RB_PROPERTY, property_val); + if (ret < 0) { + snprintf(buf, sizeof(buf), "reboot failed: %d\n", ret); + writex(fd, buf, strlen(buf)); + goto cleanup; + } + // Don't return early. Give the reboot command time to take effect + // to avoid messing up scripts which do "adb reboot && adb wait-for-device" + while(1) { pause(); } +cleanup: + free(arg); + adb_close(fd); +} + +#endif + +static int create_service_thread(void (*func)(int, void *), void *cookie) +{ + stinfo *sti; + adb_thread_t t; + int s[2]; + + if(adb_socketpair(s)) { + printf("cannot create service socket pair\n"); + return -1; + } + + sti = malloc(sizeof(stinfo)); + if(sti == 0) fatal("cannot allocate stinfo"); + sti->func = func; + sti->cookie = cookie; + sti->fd = s[1]; + + if(adb_thread_create( &t, service_bootstrap_func, sti)){ + free(sti); + adb_close(s[0]); + adb_close(s[1]); + printf("cannot create service thread\n"); + return -1; + } + + D("service thread started, %d:%d\n",s[0], s[1]); + return s[0]; +} + +#if !ADB_HOST +static int create_subprocess(const char *cmd, const char *arg0, const char *arg1, pid_t *pid) +{ +#ifdef HAVE_WIN32_PROC + D("create_subprocess(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1); + fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1); + return -1; +#else /* !HAVE_WIN32_PROC */ + char *devname; + int ptm; + + ptm = unix_open("/dev/ptmx", O_RDWR); // | O_NOCTTY); + if(ptm < 0){ + printf("[ cannot open /dev/ptmx - %s ]\n",strerror(errno)); + return -1; + } + fcntl(ptm, F_SETFD, FD_CLOEXEC); + + if(grantpt(ptm) || unlockpt(ptm) || + ((devname = (char*) ptsname(ptm)) == 0)){ + printf("[ trouble with /dev/ptmx - %s ]\n", strerror(errno)); + adb_close(ptm); + return -1; + } + + *pid = fork(); + if(*pid < 0) { + printf("- fork failed: %s -\n", strerror(errno)); + adb_close(ptm); + return -1; + } + + if(*pid == 0){ + int pts; + + setsid(); + + pts = unix_open(devname, O_RDWR); + if(pts < 0) { + fprintf(stderr, "child failed to open pseudo-term slave: %s\n", devname); + exit(-1); + } + + dup2(pts, 0); + dup2(pts, 1); + dup2(pts, 2); + + adb_close(pts); + adb_close(ptm); + + // set OOM adjustment to zero + char text[64]; + snprintf(text, sizeof text, "/proc/%d/oom_adj", getpid()); + int fd = adb_open(text, O_WRONLY); + if (fd >= 0) { + adb_write(fd, "0", 1); + adb_close(fd); + } else { + D("adb: unable to open %s\n", text); + } + execl(cmd, cmd, arg0, arg1, NULL); + fprintf(stderr, "- exec '%s' failed: %s (%d) -\n", + cmd, strerror(errno), errno); + exit(-1); + } else { + // Don't set child's OOM adjustment to zero. + // Let the child do it itself, as sometimes the parent starts + // running before the child has a /proc/pid/oom_adj. + // """adb: unable to open /proc/644/oom_adj""" seen in some logs. + return ptm; + } +#endif /* !HAVE_WIN32_PROC */ +} +#endif /* !ABD_HOST */ + +#if ADB_HOST +#define SHELL_COMMAND "/bin/sh" +#else +#define SHELL_COMMAND "/bin/adb_shell" +#endif + +#if !ADB_HOST +static void subproc_waiter_service(int fd, void *cookie) +{ + pid_t pid = (pid_t)cookie; + + D("entered. fd=%d of pid=%d\n", fd, pid); + for (;;) { + 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 = writex(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd)); + D("notified shell exit via fd=%d for pid=%d res=%d errno=%d\n", + SHELL_EXIT_NOTIFY_FD, pid, res, errno); + } +} + +static int create_subproc_thread(const char *name) +{ + stinfo *sti; + adb_thread_t t; + int ret_fd; + pid_t pid; + if(name) { + ret_fd = create_subprocess(SHELL_COMMAND, "-c", name, &pid); + } else { + ret_fd = create_subprocess(SHELL_COMMAND, "-", 0, &pid); + } + D("create_subprocess() ret_fd=%d pid=%d\n", ret_fd, pid); + + sti = malloc(sizeof(stinfo)); + if(sti == 0) fatal("cannot allocate stinfo"); + sti->func = subproc_waiter_service; + sti->cookie = (void*)pid; + sti->fd = ret_fd; + + if(adb_thread_create( &t, service_bootstrap_func, sti)){ + free(sti); + adb_close(ret_fd); + printf("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 ret = -1; + + if(!strncmp(name, "tcp:", 4)) { + int port = atoi(name + 4); + name = strchr(name + 4, ':'); + if(name == 0) { + ret = socket_loopback_client(port, SOCK_STREAM); + if (ret >= 0) + disable_tcp_nagle(ret); + } else { +#if ADB_HOST + ret = socket_network_client(name + 1, port, SOCK_STREAM); +#else + return -1; +#endif + } +#ifndef HAVE_WINSOCK /* 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); + } else if(!strncmp(name, "framebuffer:", 12)) { + 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, "log:", 4)) { + ret = create_service_thread(log_service, get_log_file_path(name + 4)); + } else if(!HOST && !strncmp(name, "shell:", 6)) { + if(name[6]) { + ret = create_subproc_thread(name + 6); + } else { + ret = create_subproc_thread(0); + } + } else if(!strncmp(name, "sync:", 5)) { + ret = create_service_thread(file_sync_service, NULL); + } else if(!strncmp(name, "remount:", 8)) { + ret = create_service_thread(remount_service, NULL); + } else if(!strncmp(name, "reboot:", 7)) { + void* arg = strdup(name + 7); + if(arg == 0) return -1; + ret = create_service_thread(reboot_service, arg); + } else if(!strncmp(name, "root:", 5)) { + ret = create_service_thread(restart_root_service, NULL); + } else if(!strncmp(name, "backup:", 7)) { + char* arg = strdup(name+7); + if (arg == NULL) return -1; + ret = backup_service(BACKUP, arg); + } else if(!strncmp(name, "restore:", 8)) { + ret = backup_service(RESTORE, NULL); + } else if(!strncmp(name, "tcpip:", 6)) { + int port; + if (sscanf(name + 6, "%d", &port) == 0) { + port = 0; + } + ret = create_service_thread(restart_tcp_service, (void *)port); + } else if(!strncmp(name, "usb:", 4)) { + ret = create_service_thread(restart_usb_service, NULL); +#endif + } + if (ret >= 0) { + close_on_exec(ret); + } + return ret; +} + +#if ADB_HOST +struct state_info { + transport_type transport; + char* serial; + int state; +}; + +static void wait_for_state(int fd, void* cookie) +{ + struct state_info* sinfo = cookie; + char* err = "unknown error"; + + D("wait_for_state %d\n", sinfo->state); + + atransport *t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &err); + if(t != 0) { + writex(fd, "OKAY", 4); + } else { + sendfailmsg(fd, err); + } + + if (sinfo->serial) + free(sinfo->serial); + free(sinfo); + adb_close(fd); + D("wait_for_state is done\n"); +} + +static void connect_device(char* host, char* buffer, int buffer_size) +{ + int port, fd; + char* portstr = strchr(host, ':'); + char hostbuf[100]; + char serial[100]; + int ret; + + strncpy(hostbuf, host, sizeof(hostbuf) - 1); + if (portstr) { + if (portstr - host >= (ptrdiff_t)sizeof(hostbuf)) { + snprintf(buffer, buffer_size, "bad host name %s", host); + return; + } + // zero terminate the host at the point we found the colon + hostbuf[portstr - host] = 0; + if (sscanf(portstr + 1, "%d", &port) == 0) { + snprintf(buffer, buffer_size, "bad port number %s", portstr); + return; + } + } else { + port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; + } + + snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port); + + fd = socket_network_client(hostbuf, port, SOCK_STREAM); + if (fd < 0) { + snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port); + return; + } + + D("client: connected on remote on fd %d\n", fd); + close_on_exec(fd); + disable_tcp_nagle(fd); + + ret = register_socket_transport(fd, serial, port, 0); + if (ret < 0) { + adb_close(fd); + snprintf(buffer, buffer_size, "already connected to %s", serial); + } else { + snprintf(buffer, buffer_size, "connected to %s", serial); + } +} + +void connect_emulator(char* port_spec, char* buffer, int buffer_size) +{ + char* port_separator = strchr(port_spec, ','); + if (!port_separator) { + snprintf(buffer, buffer_size, + "unable to parse '%s' as ,", + port_spec); + return; + } + + // Zero-terminate console port and make port_separator point to 2nd port. + *port_separator++ = 0; + int console_port = strtol(port_spec, NULL, 0); + int adb_port = strtol(port_separator, NULL, 0); + if (!(console_port > 0 && adb_port > 0)) { + *(port_separator - 1) = ','; + snprintf(buffer, buffer_size, + "Invalid port numbers: Expected positive numbers, got '%s'", + port_spec); + return; + } + + /* Check if the emulator is already known. + * Note: There's a small but harmless race condition here: An emulator not + * present just yet could be registered by another invocation right + * after doing this check here. However, local_connect protects + * against double-registration too. From here, a better error message + * can be produced. In the case of the race condition, the very specific + * error message won't be shown, but the data doesn't get corrupted. */ + atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port); + if (known_emulator != NULL) { + snprintf(buffer, buffer_size, + "Emulator on port %d already registered.", adb_port); + return; + } + + /* Check if more emulators can be registered. Similar unproblematic + * race condition as above. */ + int candidate_slot = get_available_local_transport_index(); + if (candidate_slot < 0) { + snprintf(buffer, buffer_size, "Cannot accept more emulators."); + return; + } + + /* Preconditions met, try to connect to the emulator. */ + if (!local_connect_arbitrary_ports(console_port, adb_port)) { + snprintf(buffer, buffer_size, + "Connected to emulator on ports %d,%d", console_port, adb_port); + } else { + snprintf(buffer, buffer_size, + "Could not connect to emulator on ports %d,%d", + console_port, adb_port); + } +} + +static void connect_service(int fd, void* cookie) +{ + char buf[4096]; + char resp[4096]; + char *host = cookie; + + if (!strncmp(host, "emu:", 4)) { + connect_emulator(host + 4, buf, sizeof(buf)); + } else { + connect_device(host, buf, sizeof(buf)); + } + + // Send response for emulator and device + snprintf(resp, sizeof(resp), "%04x%s",(unsigned)strlen(buf), buf); + writex(fd, resp, strlen(resp)); + adb_close(fd); +} +#endif + +#if ADB_HOST +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-"))) { + struct state_info* sinfo = malloc(sizeof(struct state_info)); + + if (serial) + sinfo->serial = strdup(serial); + else + sinfo->serial = NULL; + + name += strlen("wait-for-"); + + if (!strncmp(name, "local", strlen("local"))) { + sinfo->transport = kTransportLocal; + sinfo->state = CS_DEVICE; + } else if (!strncmp(name, "usb", strlen("usb"))) { + sinfo->transport = kTransportUsb; + sinfo->state = CS_DEVICE; + } else if (!strncmp(name, "any", strlen("any"))) { + sinfo->transport = kTransportAny; + sinfo->state = CS_DEVICE; + } else { + free(sinfo); + return NULL; + } + + int fd = create_service_thread(wait_for_state, sinfo); + return create_local_socket(fd); + } else if (!strncmp(name, "connect:", 8)) { + const char *host = name + 8; + int fd = create_service_thread(connect_service, (void *)host); + return create_local_socket(fd); + } + return NULL; +} +#endif /* ADB_HOST */ diff --git a/adb/sockets.c b/adb/sockets.c new file mode 100644 index 0000000..f17608b --- /dev/null +++ b/adb/sockets.c @@ -0,0 +1,867 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_SOCKETS +#include "adb.h" + +ADB_MUTEX_DEFINE( socket_list_lock ); + +static void local_socket_close_locked(asocket *s); + +int sendfailmsg(int fd, const char *reason) +{ + char buf[9]; + int len; + len = strlen(reason); + if(len > 0xffff) len = 0xffff; + snprintf(buf, sizeof buf, "FAIL%04x", len); + if(writex(fd, buf, 8)) return -1; + return writex(fd, reason, len); +} + +//extern int online; + +static unsigned local_socket_next_id = 1; + +static asocket local_socket_list = { + .next = &local_socket_list, + .prev = &local_socket_list, +}; + +/* the the list of currently closing local sockets. +** these have no peer anymore, but still packets to +** write to their fd. +*/ +static asocket local_socket_closing_list = { + .next = &local_socket_closing_list, + .prev = &local_socket_closing_list, +}; + +asocket *find_local_socket(unsigned id) +{ + asocket *s; + asocket *result = NULL; + + adb_mutex_lock(&socket_list_lock); + for (s = local_socket_list.next; s != &local_socket_list; s = s->next) { + if (s->id == 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; + s->prev->next = s; + s->next->prev = s; +} + + +void install_local_socket(asocket *s) +{ + adb_mutex_lock(&socket_list_lock); + + s->id = local_socket_next_id++; + insert_local_socket(s, &local_socket_list); + + adb_mutex_unlock(&socket_list_lock); +} + +void remove_socket(asocket *s) +{ + // socket_list_lock should already be held + if (s->prev && s->next) + { + s->prev->next = s->next; + s->next->prev = s->prev; + s->next = 0; + s->prev = 0; + s->id = 0; + } +} + +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); +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); + 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); + + 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) { + goto enqueue; + } + + /* 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) { + 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) ); + s->close(s); + return 1; /* not ready (error) */ + } else { + break; + } + } + + if(p->len == 0) { + put_apacket(p); + return 0; /* ready for more data */ + } + +enqueue: + p->next = 0; + 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 */ + fdevent_add(&s->fde, FDE_WRITE); + + return 1; /* not ready (backlog) */ +} + +static void local_socket_ready(asocket *s) +{ + /* far side is ready for data, pay attention to + readable events */ + fdevent_add(&s->fde, FDE_READ); +// D("LS(%d): ready()\n", s->id); +} + +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) +{ + apacket *p, *n; + int exit_on_close = s->exit_on_close; + + D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd); + + /* 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); + n = p->next; + put_apacket(p); + } + remove_socket(s); + free(s); + + if (exit_on_close) { + D("local_socket_destroy: exiting\n"); + exit(1); + } +} + + +static void local_socket_close_locked(asocket *s) +{ + D("entered. 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); + 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); + } + s->peer = 0; + } + + /* 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; + local_socket_destroy(s); + D("LS(%d): closed\n", id); + return; + } + + /* otherwise, put on the closing list + */ + D("LS(%d): closing\n", 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); + insert_local_socket(s, &local_socket_closing_list); +} + +static void local_socket_event_func(int fd, unsigned ev, void *_s) +{ + asocket *s = _s; + + D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev); + + /* put the FDE_WRITE processing before the FDE_READ + ** in order to simplify the code. + */ + if(ev & FDE_WRITE){ + apacket *p; + + while((p = s->pkt_first) != 0) { + while(p->len > 0) { + int r = adb_write(fd, p->ptr, p->len); + if(r > 0) { + p->ptr += r; + p->len -= r; + continue; + } + if(r < 0) { + /* returning here is ok because FDE_READ will + ** be processed in the next iteration loop + */ + if(errno == EAGAIN) return; + if(errno == EINTR) continue; + } + D(" closing after write because r=%d and errno is %d\n", r, errno); + s->close(s); + return; + } + + if(p->len == 0) { + s->pkt_first = p->next; + if(s->pkt_first == 0) s->pkt_last = 0; + put_apacket(p); + } + } + + /* if we sent the last packet of a closing socket, + ** we can now destroy it. + */ + if (s->closing) { + D(" closing because 'closing' is set after write\n"); + s->close(s); + return; + } + + /* no more packets queued, so we can ignore + ** writable events again and tell our peer + ** to resume writing + */ + fdevent_del(&s->fde, FDE_WRITE); + s->peer->ready(s->peer); + } + + + if(ev & FDE_READ){ + apacket *p = get_apacket(); + unsigned char *x = p->data; + size_t avail = MAX_PAYLOAD; + int r; + int is_eof = 0; + + while(avail > 0) { + r = adb_read(fd, x, avail); + D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, avail); + if(r > 0) { + avail -= r; + x += r; + continue; + } + if(r < 0) { + if(errno == EAGAIN) break; + if(errno == EINTR) continue; + } + + /* r = 0 or unhandled error */ + 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); + if((avail == MAX_PAYLOAD) || (s->peer == 0)) { + put_apacket(p); + } else { + p->len = MAX_PAYLOAD - avail; + + r = s->peer->enqueue(s->peer, p); + D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->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. + */ + 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() + */ + 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); + s->close(s); + } + } + + 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. + */ +// s->close(s); + D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd); + + return; + } +} + +asocket *create_local_socket(int fd) +{ + asocket *s = calloc(1, sizeof(asocket)); + if (s == NULL) fatal("cannot allocate socket"); + s->fd = fd; + s->enqueue = local_socket_enqueue; + s->ready = local_socket_ready; + s->close = local_socket_close; + install_local_socket(s); + + fdevent_install(&s->fde, fd, local_socket_event_func, s); +/* fdevent_add(&s->fde, FDE_ERROR); */ + //fprintf(stderr, "Created local socket in create_local_socket \n"); + D("LS(%d): created (fd=%d)\n", s->id, s->fd); + return s; +} + +asocket *create_local_service_socket(const char *name) +{ + asocket *s; + int fd; + +#if !ADB_HOST + if (!strcmp(name,"jdwp")) { + return create_jdwp_service_socket(); + } + if (!strcmp(name,"track-jdwp")) { + return create_jdwp_tracker_service_socket(); + } +#endif + fd = service_to_fd(name); + if(fd < 0) return 0; + + s = create_local_socket(fd); + D("LS(%d): bound to '%s' via %d\n", s->id, name, fd); + +#if !ADB_HOST + if ((!strncmp(name, "root:", 5) && getuid() != 0) + || !strncmp(name, "usb:", 4) + || !strncmp(name, "tcpip:", 6)) { + D("LS(%d): enabling exit_on_close\n", s->id); + s->exit_on_close = 1; + } +#endif + + return s; +} + +#if ADB_HOST +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); + return s; + } + + return s; +} +#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 +*/ +typedef struct aremotesocket { + asocket socket; + adisconnect disconnect; +} aremotesocket; + +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); + p->msg.command = A_WRTE; + p->msg.arg0 = s->peer->id; + p->msg.arg1 = s->id; + p->msg.data_length = p->len; + send_packet(p, s->transport); + 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(); + 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_close(asocket *s) +{ + D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n", + s->id, s->fd, s->peer?s->peer->fd:-1); + apacket *p = get_apacket(); + p->msg.command = A_CLSE; + if(s->peer) { + p->msg.arg0 = s->peer->id; + 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); + s->peer->close(s->peer); + } + p->msg.arg1 = s->id; + send_packet(p, s->transport); + D("RS(%d): closed\n", s->id); + remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect ); + free(s); +} + +static void remote_socket_disconnect(void* _s, atransport* t) +{ + asocket* s = _s; + asocket* peer = s->peer; + + D("remote_socket_disconnect RS(%d)\n", s->id); + if (peer) { + peer->peer = NULL; + peer->close(peer); + } + remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect ); + free(s); +} + +asocket *create_remote_socket(unsigned id, atransport *t) +{ + asocket *s = calloc(1, sizeof(aremotesocket)); + adisconnect* dis = &((aremotesocket*)s)->disconnect; + + if (s == NULL) fatal("cannot allocate socket"); + s->id = id; + s->enqueue = remote_socket_enqueue; + s->ready = remote_socket_ready; + 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); + 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(); + int len = strlen(destination) + 1; + + if(len > (MAX_PAYLOAD-1)) { + fatal("destination oversized"); + } + + D("LS(%d): connect('%s')\n", s->id, destination); + p->msg.command = A_OPEN; + p->msg.arg0 = s->id; + p->msg.data_length = len; + 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) +{ + s->ready = local_socket_ready; + s->close = local_socket_close; + adb_write(s->fd, "OKAY", 4); + s->ready(s); +} + +/* 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) +{ + s->ready = local_socket_ready; + s->close = local_socket_close; + sendfailmsg(s->fd, "closed"); + s->close(s); +} + +unsigned unhex(unsigned 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; + } + + n = (n << 4) | c; + } + + return n; +} + +#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])); + +/* 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). */ +char *skip_host_serial(char *service) { + char *first_colon, *serial_end; + int i; + + for (i = 0; i < num_prefixes; i++) { + if (!strncmp(service, prefixes[i].str, prefixes[i].len)) + return strchr(service + prefixes[i].len, ':'); + } + + first_colon = strchr(service, ':'); + if (!first_colon) { + /* No colon in service string. */ + return NULL; + } + serial_end = first_colon; + if (isdigit(serial_end[1])) { + 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; + } + } + return serial_end; +} + +static int smart_socket_enqueue(asocket *s, apacket *p) +{ + unsigned len; +#if ADB_HOST + char *service = NULL; + char* serial = NULL; + transport_type ttype = kTransportAny; +#endif + + D("SS(%d): enqueue %d\n", s->id, p->len); + + if(s->pkt_first == 0) { + s->pkt_first = p; + s->pkt_last = p; + } else { + if((s->pkt_first->len + p->len) > MAX_PAYLOAD) { + D("SS(%d): overflow\n", s->id); + put_apacket(p); + goto fail; + } + + 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; + + len = unhex(p->data, 4); + if((len < 1) || (len > 1024)) { + D("SS(%d): bad size (%d)\n", 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); + return 0; + } + + p->data[len + 4] = 0; + + D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4)); + +#if ADB_HOST + 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); + if (serial_end) { + *serial_end = 0; // terminate string + serial = service; + service = serial_end + 1; + } + } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) { + ttype = kTransportUsb; + service += strlen("host-usb:"); + } else if (!strncmp(service, "host-local:", strlen("host-local:"))) { + ttype = kTransportLocal; + service += strlen("host-local:"); + } else if (!strncmp(service, "host:", strlen("host:"))) { + ttype = kTransportAny; + service += strlen("host:"); + } else { + service = NULL; + } + + if (service) { + 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, ttype, serial, s->peer->fd, s) == 0) { + /* XXX fail message? */ + D( "SS(%d): handled host service '%s'\n", s->id, service ); + goto fail; + } + if (!strncmp(service, "transport", strlen("transport"))) { + D( "SS(%d): okay transport\n", 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. + */ + s2 = create_host_service_socket(service, serial); + if(s2 == 0) { + D( "SS(%d): couldn't create host service '%s'\n", s->id, service ); + sendfailmsg(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. + */ + adb_write(s->peer->fd, "OKAY", 4); + + s->peer->ready = local_socket_ready; + s->peer->close = local_socket_close; + s->peer->peer = s2; + s2->peer = s->peer; + s->peer = 0; + D( "SS(%d): okay\n", s->id ); + s->close(s); + + /* initial state is "ready" */ + s2->ready(s2); + return 0; + } +#else /* !ADB_HOST */ + if (s->transport == NULL) { + char* error_string = "unknown failure"; + s->transport = acquire_one_transport (CS_ANY, + kTransportAny, NULL, &error_string); + + if (s->transport == NULL) { + sendfailmsg(s->peer->fd, error_string); + goto fail; + } + } +#endif + + if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) { + /* if there's no remote we fail the connection + ** right here and terminate it + */ + sendfailmsg(s->peer->fd, "device offline (x)"); + 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 + */ + s->peer->ready = local_socket_ready_notify; + s->peer->close = local_socket_close_notify; + s->peer->peer = 0; + /* give him our transport and upref it */ + s->peer->transport = s->transport; + + 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 + */ + s->close(s); + return -1; +} + +static void smart_socket_ready(asocket *s) +{ + D("SS(%d): ready\n", s->id); +} + +static void smart_socket_close(asocket *s) +{ + D("SS(%d): closed\n", s->id); + if(s->pkt_first){ + put_apacket(s->pkt_first); + } + if(s->peer) { + s->peer->peer = 0; + s->peer->close(s->peer); + s->peer = 0; + } + free(s); +} + +static asocket *create_smart_socket(void) +{ + D("Creating smart socket \n"); + asocket *s = calloc(1, sizeof(asocket)); + if (s == NULL) fatal("cannot allocate socket"); + s->enqueue = smart_socket_enqueue; + s->ready = smart_socket_ready; + s->close = smart_socket_close; + + D("SS(%d)\n", s->id); + return s; +} + +void connect_to_smartsocket(asocket *s) +{ + D("Connecting to smart socket \n"); + asocket *ss = create_smart_socket(); + s->peer = ss; + ss->peer = s; + s->ready(s); +} diff --git a/adb/sockets.dia b/adb/sockets.dia new file mode 100644 index 0000000000000000000000000000000000000000..c626f20f0aa3ba846658cd95bb60d1792128afc5 GIT binary patch literal 2333 zcmV+&3F7u2iwFP!000001MOW)kK;BHzR#}^JV3A;%+Mlbz2bBSNPtBSv&bQnOD+a& z)3$bG(M!?Y?Mr_9QV(~xUbbHJWO~cMJZzWvsVMSOiC?k&<4;ff(0E`X4tR7o!N{B# zEL!n(5N+=!e|-MeM{n}e{ktF60sTS!*$TQh)Dcmfyt|w1q&)mEojyK3qVN<`$ps37 zBZ}GdFB*n)s*I+S`*()%x`8#7H2Ez5lu9XrDLKJ;|(cyA=<) zFdk@lH+jEFUz2Ix%(T$Xg|-iL%a(%CFU?Jx>gD@=nm#a5Zgzj*aiC1(=}<9gXeasm zl}&CHE2C(8|Naw`l8N`(Y2_cjv{!esjX-iw#WsklSgL=*tTXVr8@ji?)Y^KewRNGj z^&nmxxR8PdvWl?eJY+P=KsGdtS%08-MME_{F1cYM7Zb;FEz>5p@0v5)?~k_$q~upz zlH$k)Av^|axvPQ%ksN$sRO>qyYSIo7vs(R@-B+X7QC_RbK zc=8efsrV;Y-%Wlqi+xk}Mzy3^ywW)b{yc&lL@=>G_{g+fyXMl{jO$gJ5yIc>W^R4#ichPo?4%ZBXa?SWBmhp;sTEmM#qk%Ez0wo_iGZpx>Jm zg>KaJT&#AqLZ#0{Iy3vVuY&uet6s+k4bQV?PlQbnhSw2_598esBzuy2eTcYvoC_Bl zUgfXkGh;*dL3p~G?0LlF16{F+5zA8&J*ZG1YvLDv6af?c@Xy@V=L|e=lv6nH@U-XR zunShb&SI34SS5j0^55&H5K}cLpV6N$!>=rSU@}v5v$Fm#xSP*V0zq#&&Jhf_^S)5rRep* z8W&VAJo-1?&x&DqQ}F-&Xn?B6j~5X~L#jRm=)+0_!5|FesSb`s$9+C)UoSg!hpBn) zXt@&Gqx+JHx`a09QL#{2-G{#6q!u1ZUT$VamMLEY4uuEnx*`*Bfa%CwYz;tC!vu9%6);UW|3 z)K+Zdy9&lZzGKg^g*^3+U_>0v!pczR+UP1SatpGG@4XdWMX)`W41^UBRzO$*VFiR0 z5LQ4~0bvD%6%bZHSOH-LgcVp924UrE2rDMc7*J9`NdYAVloU`>KuKw-r2MW%%bJR{ z@zMA**#1ekpMr?hXewkRO~pgLR!wDw%w*FDv0Z!aAmZeheI*qxj0k8%5N07{(WBB+rF8mPAP9lr2g&t*S)lw9zmEbc5Fb)K+jy`Vzy$7ZJq z8QAn9WYPALK4v0&D8v;7t~J7nN0FLDNmNt`iq78{J4F-u;;Q7S#%jELn=EoouOEx^ zW6k2RR&~=}>4e54GHaxrci2R}-mQ1DOdeJ^W`WEI)=Xyg{#7mQjP_a^y2!;>IIYg^ zTcv!J-1wBCyYaog|()j40oFX(*6}0%<6ah5~6QkcI+j zD4?bH*`$~}Y#eAHV3Q&wqd*=Pq@}>zxkXya^zBN>_Ruv^AK8|h79S*fl@}k)vh~SS zM#iaZJ41Rk8nGoLlJ4Ed?0RqCp#K(1x4=TJ-FuTC_8q z5q*;F8OD0~(93iolBh%$az79YVZCfv=UAhirAX|OnL20E(kk)N`I{3Qkl33~sOdyv zv&Lh!3Eb{b>cVcC-r^&ymAVobl1t5VqJp6;#=(+Ngf2 zwdmY>*XUu5shw>K?qKRGr!E_wx6XJN%38xNyc_Sr^9Gceb-!j=Yiy3RArE8Czj}s*srQex(E6&8}mgdI$;23*OeXre#hg1Rn1io;0J z84!%!wyp~%BaouwWw~@j?_*|yWRa&GqM23(Q?XEjF}M7rmYs)Ot-oC|$e}hl$CiUgA?MiDm~)Ir$~lgA zYIZH@>dRI!QM4YK)7WwJer8XyZuk!V86Fk6s{v(c9K8vGi`;oqZ1RKGyU1jc$#_i+ zEZH=Qa-w)6sYzL359ZR5cMq#Uw z*iSJmrn +#include +#include +#include +#include +#include +#include +#include +#include + +#define OS_PATH_SEPARATOR '\\' +#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__ void adb_mutex_unlock( adb_mutex_t* lock ) +{ + LeaveCriticalSection( lock ); +} + +typedef struct { unsigned tid; } adb_thread_t; + +typedef void* (*adb_thread_func_t)(void* arg); + +typedef void (*win_thread_func_t)(void* arg); + +static __inline__ int adb_thread_create( adb_thread_t *thread, adb_thread_func_t func, void* arg) +{ + thread->tid = _beginthread( (win_thread_func_t)func, 0, arg ); + if (thread->tid == (unsigned)-1L) { + return -1; + } + return 0; +} + +static __inline__ void close_on_exec(int fd) +{ + /* nothing really */ +} + +extern void disable_tcp_nagle(int fd); + +#define lstat stat /* no symlinks on Win32 */ + +#define S_ISLNK(m) 0 /* no symlinks on Win32 */ + +static __inline__ int adb_unlink(const char* path) +{ + int rc = unlink(path); + + if (rc == -1 && errno == EACCES) { + /* unlink returns EACCES when the file is read-only, so we first */ + /* try to make it writable, then unlink again... */ + rc = chmod(path, _S_IREAD|_S_IWRITE ); + if (rc == 0) + rc = unlink(path); + } + return rc; +} +#undef unlink +#define unlink ___xxx_unlink + +static __inline__ int adb_mkdir(const char* path, int mode) +{ + return _mkdir(path); +} +#undef mkdir +#define mkdir ___xxx_mkdir + +extern int adb_open(const char* path, int options); +extern int adb_creat(const char* path, int mode); +extern int adb_read(int fd, void* buf, int len); +extern int adb_write(int fd, const void* buf, int len); +extern int adb_lseek(int fd, int pos, int where); +extern int adb_shutdown(int fd); +extern int adb_close(int fd); + +static __inline__ int unix_close(int fd) +{ + return close(fd); +} +#undef close +#define close ____xxx_close + +static __inline__ int unix_read(int fd, void* buf, size_t len) +{ + return read(fd, buf, len); +} +#undef read +#define read ___xxx_read + +static __inline__ int unix_write(int fd, const void* buf, size_t len) +{ + return write(fd, buf, len); +} +#undef write +#define write ___xxx_write + +static __inline__ int adb_open_mode(const char* path, int options, int mode) +{ + return adb_open(path, options); +} + +static __inline__ int unix_open(const char* path, int options,...) +{ + if ((options & O_CREAT) == 0) + { + return open(path, options); + } + else + { + int mode; + va_list args; + va_start( args, options ); + mode = va_arg( args, int ); + va_end( args ); + return open(path, options, mode); + } +} +#define open ___xxx_unix_open + + +/* normally provided by */ +extern void* load_file(const char* pathname, unsigned* psize); + +/* normally provided by */ +extern int socket_loopback_client(int port, int type); +extern int socket_network_client(const char *host, int port, int type); +extern int socket_loopback_server(int port, int type); +extern int socket_inaddr_any_server(int port, int type); + +/* normally provided by "fdevent.h" */ + +#define FDE_READ 0x0001 +#define FDE_WRITE 0x0002 +#define FDE_ERROR 0x0004 +#define FDE_DONT_CLOSE 0x0080 + +typedef struct fdevent fdevent; + +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(); + +struct fdevent { + fdevent *next; + fdevent *prev; + + int fd; + int force_eof; + + unsigned short state; + unsigned short events; + + fd_func func; + void *arg; +}; + +static __inline__ void adb_sleep_ms( int mseconds ) +{ + Sleep( mseconds ); +} + +extern int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen); + +#undef accept +#define accept ___xxx_accept + +static __inline__ int adb_socket_setbufsize( int fd, int bufsize ) +{ + int opt = bufsize; + return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char*)&opt, sizeof(opt)); +} + +extern int adb_socketpair( int sv[2] ); + +static __inline__ char* adb_dirstart( const char* path ) +{ + char* p = strchr(path, '/'); + char* p2 = strchr(path, '\\'); + + if ( !p ) + p = p2; + else if ( p2 && p2 > p ) + p = p2; + + return p; +} + +static __inline__ char* adb_dirstop( const char* path ) +{ + char* p = strrchr(path, '/'); + char* p2 = strrchr(path, '\\'); + + if ( !p ) + p = p2; + else if ( p2 && p2 > p ) + p = p2; + + return p; +} + +static __inline__ int adb_is_absolute_host_path( const char* path ) +{ + return isalpha(path[0]) && path[1] == ':' && path[2] == '\\'; +} + +extern char* adb_strtok_r(char *str, const char *delim, char **saveptr); + +#else /* !_WIN32 a.k.a. Unix */ + +#include "fdevent.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * . (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 + +#define OS_PATH_SEPARATOR '/' +#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__ void close_on_exec(int fd) +{ + fcntl( fd, F_SETFD, FD_CLOEXEC ); +} + +static __inline__ int unix_open(const char* path, int options,...) +{ + if ((options & O_CREAT) == 0) + { + return TEMP_FAILURE_RETRY( open(path, options) ); + } + else + { + int mode; + va_list args; + va_start( args, options ); + mode = va_arg( args, int ); + va_end( args ); + return TEMP_FAILURE_RETRY( open( path, options, mode ) ); + } +} + +static __inline__ int adb_open_mode( const char* pathname, int options, int mode ) +{ + return TEMP_FAILURE_RETRY( open( pathname, options, mode ) ); +} + + +static __inline__ int adb_open( const char* pathname, int options ) +{ + int fd = TEMP_FAILURE_RETRY( open( pathname, options ) ); + if (fd < 0) + return -1; + close_on_exec( fd ); + return fd; +} +#undef open +#define open ___xxx_open + +static __inline__ int adb_shutdown(int fd) +{ + return shutdown(fd, SHUT_RDWR); +} +#undef shutdown +#define shutdown ____xxx_shutdown + +static __inline__ int adb_close(int fd) +{ + return close(fd); +} +#undef close +#define close ____xxx_close + + +static __inline__ int adb_read(int fd, void* buf, size_t len) +{ + return TEMP_FAILURE_RETRY( read( fd, buf, len ) ); +} + +#undef read +#define read ___xxx_read + +static __inline__ int adb_write(int fd, const void* buf, size_t len) +{ + return TEMP_FAILURE_RETRY( write( fd, buf, len ) ); +} +#undef write +#define write ___xxx_write + +static __inline__ int adb_lseek(int fd, int pos, int where) +{ + return lseek(fd, pos, where); +} +#undef lseek +#define lseek ___xxx_lseek + +static __inline__ int adb_unlink(const char* path) +{ + return unlink(path); +} +#undef unlink +#define unlink ___xxx_unlink + +static __inline__ int adb_creat(const char* path, int mode) +{ + int fd = TEMP_FAILURE_RETRY( creat( path, mode ) ); + + if ( fd < 0 ) + return -1; + + close_on_exec(fd); + return fd; +} +#undef creat +#define creat ___xxx_creat + +static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen) +{ + int fd; + + fd = TEMP_FAILURE_RETRY( accept( serverfd, addr, addrlen ) ); + if (fd >= 0) + close_on_exec(fd); + + return fd; +} + +#undef accept +#define accept ___xxx_accept + +#define unix_read adb_read +#define unix_write adb_write +#define unix_close adb_close + +typedef pthread_t adb_thread_t; + +typedef void* (*adb_thread_func_t)( void* arg ); + +static __inline__ int adb_thread_create( adb_thread_t *pthread, adb_thread_func_t start, void* arg ) +{ + pthread_attr_t attr; + + pthread_attr_init (&attr); + pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); + + return pthread_create( pthread, &attr, start, arg ); +} + +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__ void disable_tcp_nagle(int fd) +{ + int on = 1; + setsockopt( fd, IPPROTO_TCP, TCP_NODELAY, (void*)&on, sizeof(on) ); +} + + +static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] ) +{ + return socketpair( d, type, protocol, sv ); +} + +static __inline__ int adb_socketpair( int sv[2] ) +{ + int rc; + + rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv ); + if (rc < 0) + return -1; + + close_on_exec( sv[0] ); + close_on_exec( sv[1] ); + return 0; +} + +#undef socketpair +#define socketpair ___xxx_socketpair + +static __inline__ void adb_sleep_ms( int mseconds ) +{ + usleep( mseconds*1000 ); +} + +static __inline__ int adb_mkdir(const char* path, int mode) +{ + return mkdir(path, mode); +} +#undef mkdir +#define mkdir ___xxx_mkdir + +static __inline__ void adb_sysdeps_init(void) +{ +} + +static __inline__ char* adb_dirstart(const char* path) +{ + return strchr(path, '/'); +} + +static __inline__ char* adb_dirstop(const char* path) +{ + return strrchr(path, '/'); +} + +static __inline__ int adb_is_absolute_host_path( const char* path ) +{ + return path[0] == '/'; +} + +static __inline__ char* adb_strtok_r(char *str, const char *delim, char **saveptr) +{ + return strtok_r(str, delim, saveptr); +} +#undef strtok_r +#define strtok_r ___xxx_strtok_r + +#endif /* !_WIN32 */ + +#endif /* _ADB_SYSDEPS_H */ diff --git a/adb/sysdeps_win32.c b/adb/sysdeps_win32.c new file mode 100644 index 0000000..2105b16 --- /dev/null +++ b/adb/sysdeps_win32.c @@ -0,0 +1,2220 @@ +#include "sysdeps.h" +#include +#include +#include +#include +#define TRACE_TAG TRACE_SYSDEPS +#include "adb.h" + +extern void fatal(const char *fmt, ...); + +#define assert(cond) do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0) + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** replaces libs/cutils/load_file.c *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +void *load_file(const char *fn, unsigned *_sz) +{ + HANDLE file; + char *data; + DWORD file_size; + + file = CreateFile( fn, + 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; + } + } + } + CloseHandle( file ); + + *_sz = (unsigned) file_size; + return data; +} + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** common file descriptor handling *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +typedef const struct FHClassRec_* FHClass; + +typedef struct FHRec_* FH; + +typedef struct EventHookRec_* EventHook; + +typedef struct FHClassRec_ +{ + void (*_fh_init) ( FH f ); + int (*_fh_close)( FH f ); + int (*_fh_lseek)( FH f, int pos, int origin ); + int (*_fh_read) ( FH f, void* buf, int len ); + int (*_fh_write)( FH f, const void* buf, int len ); + void (*_fh_hook) ( FH f, int events, EventHook hook ); + +} FHClassRec; + +/* used to emulate unix-domain socket pairs */ +typedef struct SocketPairRec_* SocketPair; + +typedef struct FHRec_ +{ + FHClass clazz; + int used; + int eof; + union { + HANDLE handle; + SOCKET socket; + SocketPair pair; + } u; + + HANDLE event; + int mask; + + char name[32]; + +} FHRec; + +#define fh_handle u.handle +#define fh_socket u.socket +#define fh_pair u.pair + +#define WIN32_FH_BASE 100 + +#define WIN32_MAX_FHS 128 + +static adb_mutex_t _win32_lock; +static FHRec _win32_fhs[ WIN32_MAX_FHS ]; +static int _win32_fh_count; + +static FH +_fh_from_int( int fd ) +{ + FH f; + + fd -= WIN32_FH_BASE; + + if (fd < 0 || fd >= _win32_fh_count) { + D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE ); + errno = EBADF; + return NULL; + } + + f = &_win32_fhs[fd]; + + if (f->used == 0) { + D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE ); + errno = EBADF; + return NULL; + } + + return f; +} + + +static int +_fh_to_int( FH f ) +{ + if (f && f->used && f >= _win32_fhs && f < _win32_fhs + WIN32_MAX_FHS) + return (int)(f - _win32_fhs) + WIN32_FH_BASE; + + return -1; +} + +static FH +_fh_alloc( FHClass clazz ) +{ + int nn; + FH f = NULL; + + adb_mutex_lock( &_win32_lock ); + + if (_win32_fh_count < WIN32_MAX_FHS) { + f = &_win32_fhs[ _win32_fh_count++ ]; + goto Exit; + } + + for (nn = 0; nn < WIN32_MAX_FHS; nn++) { + if ( _win32_fhs[nn].clazz == NULL) { + f = &_win32_fhs[nn]; + goto Exit; + } + } + D( "_fh_alloc: no more free file descriptors\n" ); +Exit: + if (f) { + f->clazz = clazz; + f->used = 1; + f->eof = 0; + clazz->_fh_init(f); + } + adb_mutex_unlock( &_win32_lock ); + return f; +} + + +static int +_fh_close( FH f ) +{ + if ( f->used ) { + f->clazz->_fh_close( f ); + f->used = 0; + f->eof = 0; + f->clazz = NULL; + } + return 0; +} + +/* forward definitions */ +static const FHClassRec _fh_file_class; +static const FHClassRec _fh_socket_class; + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** file-based descriptor handling *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +static void +_fh_file_init( FH f ) +{ + f->fh_handle = INVALID_HANDLE_VALUE; +} + +static int +_fh_file_close( FH f ) +{ + CloseHandle( f->fh_handle ); + f->fh_handle = INVALID_HANDLE_VALUE; + return 0; +} + +static int +_fh_file_read( FH f, void* buf, int len ) +{ + 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 ); + errno = EIO; + return -1; + } else if (read_bytes < (DWORD)len) { + f->eof = 1; + } + return (int)read_bytes; +} + +static int +_fh_file_write( FH f, const void* buf, int len ) +{ + 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 ); + errno = EIO; + return -1; + } else if (wrote_bytes < (DWORD)len) { + f->eof = 1; + } + return (int)wrote_bytes; +} + +static int +_fh_file_lseek( FH f, int pos, int origin ) +{ + DWORD method; + DWORD result; + + switch (origin) + { + case SEEK_SET: method = FILE_BEGIN; break; + case SEEK_CUR: method = FILE_CURRENT; break; + case SEEK_END: method = FILE_END; break; + default: + errno = EINVAL; + return -1; + } + + result = SetFilePointer( f->fh_handle, pos, NULL, method ); + if (result == INVALID_SET_FILE_POINTER) { + errno = EIO; + return -1; + } else { + f->eof = 0; + } + return (int)result; +} + +static void _fh_file_hook( FH f, int event, EventHook eventhook ); /* forward */ + +static const FHClassRec _fh_file_class = +{ + _fh_file_init, + _fh_file_close, + _fh_file_lseek, + _fh_file_read, + _fh_file_write, + _fh_file_hook +}; + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** file-based descriptor handling *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +int adb_open(const char* path, int options) +{ + FH f; + + DWORD desiredAccess = 0; + DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + + switch (options) { + case O_RDONLY: + desiredAccess = GENERIC_READ; + break; + case O_WRONLY: + desiredAccess = GENERIC_WRITE; + break; + case O_RDWR: + desiredAccess = GENERIC_READ | GENERIC_WRITE; + break; + default: + D("adb_open: invalid options (0x%0x)\n", options); + errno = EINVAL; + return -1; + } + + f = _fh_alloc( &_fh_file_class ); + if ( !f ) { + errno = ENOMEM; + return -1; + } + + f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING, + 0, NULL ); + + if ( f->fh_handle == INVALID_HANDLE_VALUE ) { + _fh_close(f); + D( "adb_open: could not open '%s':", path ); + switch (GetLastError()) { + case ERROR_FILE_NOT_FOUND: + D( "file not found\n" ); + errno = ENOENT; + return -1; + + case ERROR_PATH_NOT_FOUND: + D( "path not found\n" ); + errno = ENOTDIR; + return -1; + + default: + D( "unknown error\n" ); + 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) ); + return _fh_to_int(f); +} + +/* ignore mode on Win32 */ +int adb_creat(const char* path, int mode) +{ + FH f; + + f = _fh_alloc( &_fh_file_class ); + if ( !f ) { + errno = ENOMEM; + return -1; + } + + f->fh_handle = CreateFile( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, + NULL ); + + if ( f->fh_handle == INVALID_HANDLE_VALUE ) { + _fh_close(f); + D( "adb_creat: could not open '%s':", path ); + switch (GetLastError()) { + case ERROR_FILE_NOT_FOUND: + D( "file not found\n" ); + errno = ENOENT; + return -1; + + case ERROR_PATH_NOT_FOUND: + D( "path not found\n" ); + errno = ENOTDIR; + return -1; + + default: + D( "unknown error\n" ); + 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) ); + return _fh_to_int(f); +} + + +int adb_read(int fd, void* buf, int len) +{ + FH f = _fh_from_int(fd); + + if (f == NULL) { + return -1; + } + + return f->clazz->_fh_read( f, buf, len ); +} + + +int adb_write(int fd, const void* buf, int len) +{ + FH f = _fh_from_int(fd); + + if (f == NULL) { + return -1; + } + + return f->clazz->_fh_write(f, buf, len); +} + + +int adb_lseek(int fd, int pos, int where) +{ + FH f = _fh_from_int(fd); + + if (!f) { + return -1; + } + + return f->clazz->_fh_lseek(f, pos, where); +} + + +int adb_shutdown(int fd) +{ + FH f = _fh_from_int(fd); + + if (!f) { + return -1; + } + + D( "adb_shutdown: %s\n", f->name); + shutdown( f->fh_socket, SD_BOTH ); + return 0; +} + + +int adb_close(int fd) +{ + FH f = _fh_from_int(fd); + + if (!f) { + return -1; + } + + D( "adb_close: %s\n", f->name); + _fh_close(f); + return 0; +} + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** socket-based file descriptors *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +static void +_socket_set_errno( void ) +{ + switch (WSAGetLastError()) { + case 0: errno = 0; break; + case WSAEWOULDBLOCK: errno = EAGAIN; break; + case WSAEINTR: errno = EINTR; break; + default: + D( "_socket_set_errno: unhandled value %d\n", WSAGetLastError() ); + errno = EINVAL; + } +} + +static void +_fh_socket_init( FH f ) +{ + f->fh_socket = INVALID_SOCKET; + f->event = WSACreateEvent(); + f->mask = 0; +} + +static int +_fh_socket_close( FH f ) +{ + /* gently tell any peer that we're closing the socket */ + shutdown( f->fh_socket, SD_BOTH ); + closesocket( f->fh_socket ); + f->fh_socket = INVALID_SOCKET; + CloseHandle( f->event ); + f->mask = 0; + return 0; +} + +static int +_fh_socket_lseek( FH f, int pos, int origin ) +{ + errno = EPIPE; + return -1; +} + +static int +_fh_socket_read( FH f, void* buf, int len ) +{ + int result = recv( f->fh_socket, buf, len, 0 ); + if (result == SOCKET_ERROR) { + _socket_set_errno(); + result = -1; + } + return result; +} + +static int +_fh_socket_write( FH f, const void* buf, int len ) +{ + int result = send( f->fh_socket, buf, len, 0 ); + if (result == SOCKET_ERROR) { + _socket_set_errno(); + result = -1; + } + return result; +} + +static void _fh_socket_hook( FH f, int event, EventHook hook ); /* forward */ + +static const FHClassRec _fh_socket_class = +{ + _fh_socket_init, + _fh_socket_close, + _fh_socket_lseek, + _fh_socket_read, + _fh_socket_write, + _fh_socket_hook +}; + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** replacement for libs/cutils/socket_xxxx.c *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +#include + +static int _winsock_init; + +static void +_cleanup_winsock( void ) +{ + WSACleanup(); +} + +static void +_init_winsock( void ) +{ + if (!_winsock_init) { + WSADATA wsaData; + int rc = WSAStartup( MAKEWORD(2,2), &wsaData); + if (rc != 0) { + fatal( "adb: could not initialize Winsock\n" ); + } + atexit( _cleanup_winsock ); + _winsock_init = 1; + } +} + +int socket_loopback_client(int port, int type) +{ + FH f = _fh_alloc( &_fh_socket_class ); + struct sockaddr_in addr; + SOCKET s; + + if (!f) + return -1; + + 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) { + D("socket_loopback_client: could not create socket\n" ); + _fh_close(f); + return -1; + } + + f->fh_socket = s; + if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + D("socket_loopback_client: could not connect to %s:%d\n", type != SOCK_STREAM ? "udp" : "tcp", port ); + _fh_close(f); + return -1; + } + snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port ); + D( "socket_loopback_client: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) ); + return _fh_to_int(f); +} + +#define LISTEN_BACKLOG 4 + +int socket_loopback_server(int port, int type) +{ + FH f = _fh_alloc( &_fh_socket_class ); + struct sockaddr_in addr; + SOCKET s; + int n; + + if (!f) { + return -1; + } + + 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) return -1; + + f->fh_socket = s; + + n = 1; + setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n)); + + if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + _fh_close(f); + return -1; + } + if (type == SOCK_STREAM) { + int ret; + + ret = listen(s, LISTEN_BACKLOG); + if (ret < 0) { + _fh_close(f); + return -1; + } + } + snprintf( f->name, sizeof(f->name), "%d(lo-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port ); + D( "socket_loopback_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) ); + return _fh_to_int(f); +} + + +int socket_network_client(const char *host, int port, int type) +{ + FH f = _fh_alloc( &_fh_socket_class ); + struct hostent *hp; + struct sockaddr_in addr; + SOCKET s; + + if (!f) + return -1; + + if (!_winsock_init) + _init_winsock(); + + hp = gethostbyname(host); + if(hp == 0) { + _fh_close(f); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = hp->h_addrtype; + addr.sin_port = htons(port); + memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); + + s = socket(hp->h_addrtype, type, 0); + if(s == INVALID_SOCKET) { + _fh_close(f); + return -1; + } + f->fh_socket = s; + + if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + _fh_close(f); + return -1; + } + + snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port ); + D( "socket_network_client: host '%s' port %d type %s => fd %d\n", host, port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) ); + return _fh_to_int(f); +} + + +int socket_inaddr_any_server(int port, int type) +{ + FH f = _fh_alloc( &_fh_socket_class ); + struct sockaddr_in addr; + SOCKET s; + int n; + + if (!f) + return -1; + + 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_ANY); + + s = socket(AF_INET, type, 0); + if(s == INVALID_SOCKET) { + _fh_close(f); + return -1; + } + + f->fh_socket = s; + n = 1; + setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n)); + + if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + _fh_close(f); + return -1; + } + + if (type == SOCK_STREAM) { + int ret; + + ret = listen(s, LISTEN_BACKLOG); + if (ret < 0) { + _fh_close(f); + return -1; + } + } + snprintf( f->name, sizeof(f->name), "%d(any-server:%s%d)", _fh_to_int(f), type != SOCK_STREAM ? "udp:" : "", port ); + D( "socket_inaddr_server: port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp", _fh_to_int(f) ); + return _fh_to_int(f); +} + +#undef accept +int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen) +{ + FH serverfh = _fh_from_int(serverfd); + FH fh; + + if ( !serverfh || serverfh->clazz != &_fh_socket_class ) { + D( "adb_socket_accept: invalid fd %d\n", serverfd ); + return -1; + } + + fh = _fh_alloc( &_fh_socket_class ); + if (!fh) { + D( "adb_socket_accept: not enough memory to allocate accepted socket descriptor\n" ); + return -1; + } + + fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen ); + if (fh->fh_socket == INVALID_SOCKET) { + _fh_close( fh ); + D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, GetLastError() ); + return -1; + } + + snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", _fh_to_int(fh), serverfh->name ); + D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, _fh_to_int(fh) ); + return _fh_to_int(fh); +} + + +void disable_tcp_nagle(int fd) +{ + FH fh = _fh_from_int(fd); + int on = 1; + + if ( !fh || fh->clazz != &_fh_socket_class ) + return; + + setsockopt( fh->fh_socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on) ); +} + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** emulated socketpairs *****/ +/***** *****/ +/**************************************************************************/ +/**************************************************************************/ + +/* 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.. + */ + +#define BIP_BUFFER_SIZE 4096 + +#if 0 +#include +# 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 ); + } + + BIPD(( "bip_buffer_write: exec %d->%d len %d\n", bip->fdin, bip->fdout, len )); + + 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 += 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; + } + } + + 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 ); + } + +Exit: + assert( count > 0 ); + + if ( !bip->can_read ) { + bip->can_read = 1; + SetEvent( bip->evt_read ); + } + + 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 + } + + 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 += 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; + } + + bip->can_read = 0; + ResetEvent( bip->evt_read ); + +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; + } + return 0; +} + +static int +_fh_socketpair_lseek( FH f, int pos, int origin ) +{ + errno = ESPIPE; + return -1; +} + +static int +_fh_socketpair_read( FH f, void* buf, int len ) +{ + SocketPair pair = f->fh_pair; + BipBuffer bip; + + 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] ) +{ + FH fa, fb; + SocketPair pair; + + fa = _fh_alloc( &_fh_socketpair_class ); + fb = _fh_alloc( &_fh_socketpair_class ); + + if (!fa || !fb) + goto Fail; + + pair = malloc( sizeof(*pair) ); + if (pair == NULL) { + D("adb_socketpair: not enough memory to allocate pipes\n" ); + goto Fail; + } + + 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; + + sv[0] = _fh_to_int(fa); + sv[1] = _fh_to_int(fb); + + 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] ); + return 0; + +Fail: + _fh_close(fb); + _fh_close(fa); + return -1; +} + +/**************************************************************************/ +/**************************************************************************/ +/***** *****/ +/***** 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; + else { + hook = 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; + } +} + + +#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); + EventHook *pnode; + EventHook node; + + if (f == NULL) /* invalid arg */ { + D("event_looper_hook: invalid fd=%d\n", fd); + return; + } + + pnode = event_looper_find_p( looper, f ); + node = *pnode; + if ( node == NULL ) { + node = event_hook_alloc( f ); + node->next = *pnode; + *pnode = node; + } + + 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); + 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.", 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: %d", 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", + 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 = 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; + + _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 ); +} + +/* Windows doesn't have strtok_r. Use the one from bionic. */ + +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 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. + */ + +char * +adb_strtok_r(char *s, const char *delim, char **last) +{ + char *spanp; + int c, sc; + char *tok; + + + if (s == NULL && (s = *last) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *last = NULL; + return (NULL); + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *last = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} diff --git a/adb/test_track_devices.c b/adb/test_track_devices.c new file mode 100644 index 0000000..77b3ad9 --- /dev/null +++ b/adb/test_track_devices.c @@ -0,0 +1,97 @@ +/* a simple test program, connects to ADB server, and opens a track-devices session */ +#include +#include +#include +#include +#include +#include + +static void +panic( const char* msg ) +{ + fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno)); + exit(1); +} + +static int +unix_write( int fd, const char* buf, int len ) +{ + int result = 0; + while (len > 0) { + int len2 = write(fd, buf, len); + if (len2 < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + result += len2; + len -= len2; + buf += len2; + } + return result; +} + +static int +unix_read( int fd, char* buf, int len ) +{ + int result = 0; + while (len > 0) { + int len2 = read(fd, buf, len); + if (len2 < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + result += len2; + len -= len2; + buf += len2; + } + return result; +} + + +int main( void ) +{ + int ret, s; + struct sockaddr_in server; + char buffer[1024]; + const char* request = "host:track-devices"; + int len; + + memset( &server, 0, sizeof(server) ); + server.sin_family = AF_INET; + server.sin_port = htons(5037); + server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + 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 */ + len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request ); + if (unix_write(s, buffer, len) < 0) + panic( "could not send request" ); + + /* read the OKAY answer */ + if (unix_read(s, buffer, 4) != 4) + panic( "could not read request" ); + + printf( "server answer: %.*s\n", 4, buffer ); + + /* now loop */ + for (;;) { + char head[5] = "0000"; + + if (unix_read(s, head, 4) < 0) + panic("could not read length"); + + if ( sscanf( head, "%04x", &len ) != 1 ) + panic("could not decode length"); + + if (unix_read(s, buffer, len) != len) + panic("could not read data"); + + printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer ); + } + close(s); +} diff --git a/adb/test_track_jdwp.c b/adb/test_track_jdwp.c new file mode 100644 index 0000000..8ecc6b8 --- /dev/null +++ b/adb/test_track_jdwp.c @@ -0,0 +1,97 @@ +/* a simple test program, connects to ADB server, and opens a track-devices session */ +#include +#include +#include +#include +#include +#include + +static void +panic( const char* msg ) +{ + fprintf(stderr, "PANIC: %s: %s\n", msg, strerror(errno)); + exit(1); +} + +static int +unix_write( int fd, const char* buf, int len ) +{ + int result = 0; + while (len > 0) { + int len2 = write(fd, buf, len); + if (len2 < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + result += len2; + len -= len2; + buf += len2; + } + return result; +} + +static int +unix_read( int fd, char* buf, int len ) +{ + int result = 0; + while (len > 0) { + int len2 = read(fd, buf, len); + if (len2 < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + result += len2; + len -= len2; + buf += len2; + } + return result; +} + + +int main( void ) +{ + int ret, s; + struct sockaddr_in server; + char buffer[1024]; + const char* request = "track-jdwp"; + int len; + + memset( &server, 0, sizeof(server) ); + server.sin_family = AF_INET; + server.sin_port = htons(5037); + server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + 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 */ + len = snprintf( buffer, sizeof buffer, "%04x%s", strlen(request), request ); + if (unix_write(s, buffer, len) < 0) + panic( "could not send request" ); + + /* read the OKAY answer */ + if (unix_read(s, buffer, 4) != 4) + panic( "could not read request" ); + + printf( "server answer: %.*s\n", 4, buffer ); + + /* now loop */ + for (;;) { + char head[5] = "0000"; + + if (unix_read(s, head, 4) < 0) + panic("could not read length"); + + if ( sscanf( head, "%04x", &len ) != 1 ) + panic("could not decode length"); + + if (unix_read(s, buffer, len) != len) + panic("could not read data"); + + printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer ); + } + close(s); +} diff --git a/adb/transport.c b/adb/transport.c new file mode 100644 index 0000000..224fe55 --- /dev/null +++ b/adb/transport.c @@ -0,0 +1,1232 @@ +/* + * 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 +#include +#include +#include +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_TRANSPORT +#include "adb.h" + +static void transport_unref(atransport *t); + +static atransport transport_list = { + .next = &transport_list, + .prev = &transport_list, +}; + +static atransport pending_list = { + .next = &pending_list, + .prev = &pending_list, +}; + +ADB_MUTEX_DEFINE( transport_lock ); + +#if ADB_TRACE +#define MAX_DUMP_HEX_LEN 16 +static void dump_hex( const unsigned char* ptr, size_t len ) +{ + int nn, len2 = len; + // Build a string instead of logging each character. + // MAX chars in 2 digit hex, one space, MAX chars, one '\0'. + char buffer[MAX_DUMP_HEX_LEN *2 + 1 + MAX_DUMP_HEX_LEN + 1 ], *pb = buffer; + + if (len2 > MAX_DUMP_HEX_LEN) len2 = MAX_DUMP_HEX_LEN; + + for (nn = 0; nn < len2; nn++) { + sprintf(pb, "%02x", ptr[nn]); + pb += 2; + } + sprintf(pb++, " "); + + for (nn = 0; nn < len2; nn++) { + int c = ptr[nn]; + if (c < 32 || c > 127) + c = '.'; + *pb++ = c; + } + *pb++ = '\0'; + DR("%s\n", buffer); +} +#endif + +void +kick_transport(atransport* t) +{ + if (t && !t->kicked) + { + int kicked; + + adb_mutex_lock(&transport_lock); + kicked = t->kicked; + if (!kicked) + t->kicked = 1; + adb_mutex_unlock(&transport_lock); + + if (!kicked) + t->kick(t); + } +} + +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; + } +} + +#if ADB_TRACE +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; + + for (n = 0; n < 4; n++) { + int b = (command >> (n*8)) & 255; + if (b < 32 || b >= 127) + break; + cmd[n] = (char)b; + } + if (n == 4) { + cmd[4] = 0; + } else { + /* There is some non-ASCII name in the command, so dump + * the hexadecimal value instead */ + snprintf(cmd, sizeof cmd, "%08x", command); + } + + if (p->msg.arg0 < 256U) + snprintf(arg0, sizeof arg0, "%d", p->msg.arg0); + else + snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0); + + if (p->msg.arg1 < 256U) + snprintf(arg1, sizeof arg1, "%d", p->msg.arg1); + 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); +} +#endif /* ADB_TRACE */ + +static int +read_packet(int fd, const char* name, apacket** ppacket) +{ + char *p = (char*)ppacket; /* really read a packet address */ + int r; + int len = sizeof(*ppacket); + char buff[8]; + if (!name) { + snprintf(buff, sizeof buff, "fd=%d", fd); + name = buff; + } + while(len > 0) { + r = adb_read(fd, p, len); + 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; + return -1; + } + } + +#if ADB_TRACE + if (ADB_TRACING) { + dump_packet(name, "from remote", *ppacket); + } +#endif + return 0; +} + +static int +write_packet(int fd, const char* name, apacket** ppacket) +{ + char *p = (char*) ppacket; /* we really write the packet address */ + int r, len = sizeof(ppacket); + char buff[8]; + if (!name) { + snprintf(buff, sizeof buff, "fd=%d", fd); + name = buff; + } + +#if ADB_TRACE + if (ADB_TRACING) { + dump_packet(name, "to remote", *ppacket); + } +#endif + len = sizeof(ppacket); + while(len > 0) { + r = adb_write(fd, p, len); + 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; + return -1; + } + } + return 0; +} + +static void transport_socket_events(int fd, unsigned events, void *_t) +{ + atransport *t = _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); + } else { + handle_packet(p, (atransport *) _t); + } + } +} + +void send_packet(apacket *p, atransport *t) +{ + unsigned char *x; + unsigned sum; + unsigned count; + + 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; + + 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"); + } + + 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. +*/ + +static void *output_thread(void *_t) +{ + atransport *t = _t; + apacket *p; + + D("%s: starting transport output thread on fd %d, SYNC online (%d)\n", + 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)) { + put_apacket(p); + D("%s: failed to write SYNC packet\n", t->serial); + goto oops; + } + + D("%s: data pump started\n", t->serial); + for(;;) { + 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)){ + put_apacket(p); + D("%s: failed to write apacket to transport\n", t->serial); + goto oops; + } + } else { + D("%s: remote read failed for transport\n", t->serial); + put_apacket(p); + break; + } + } + + D("%s: SYNC offline for transport\n", 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)) { + put_apacket(p); + D("%s: failed to write SYNC apacket to transport", t->serial); + } + +oops: + D("%s: transport output thread is exiting\n", t->serial); + kick_transport(t); + transport_unref(t); + return 0; +} + +static void *input_thread(void *_t) +{ + atransport *t = _t; + apacket *p; + int active = 0; + + D("%s: starting transport input thread, reading from fd %d\n", + 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 ); + break; + } + if(p->msg.command == A_SYNC){ + if(p->msg.arg0 == 0) { + D("%s: transport SYNC offline\n", t->serial); + put_apacket(p); + break; + } else { + if(p->msg.arg1 == t->sync_token) { + D("%s: transport SYNC online\n", t->serial); + active = 1; + } else { + D("%s: transport ignoring SYNC %d != %d\n", + t->serial, p->msg.arg1, t->sync_token); + } + } + } else { + if(active) { + D("%s: transport got packet, sending to remote\n", t->serial); + t->write_to_remote(p, t); + } else { + D("%s: transport ignoring packet while offline\n", 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); + kick_transport(t); + transport_unref(t); + return 0; +} + + +static int transport_registration_send = -1; +static int transport_registration_recv = -1; +static fdevent transport_registration_fde; + + +#if ADB_HOST +static int list_transports_msg(char* buffer, size_t bufferlen) +{ + char head[5]; + int len; + + len = list_transports(buffer+4, bufferlen-4, 0); + snprintf(head, sizeof(head), "%04x", len); + memcpy(buffer, head, 4); + len += 4; + return len; +} + +/* this adds support required by the 'track-devices' service. + * this is used to send the content of "list_transport" to any + * number of client connections that want it through a single + * live TCP connection + */ +typedef struct device_tracker device_tracker; +struct device_tracker { + asocket socket; + int update_needed; + device_tracker* next; +}; + +/* linked list of all device trackers */ +static device_tracker* device_tracker_list; + +static void +device_tracker_remove( device_tracker* tracker ) +{ + device_tracker** pnode = &device_tracker_list; + device_tracker* node = *pnode; + + adb_mutex_lock( &transport_lock ); + while (node) { + if (node == tracker) { + *pnode = node->next; + break; + } + pnode = &node->next; + node = *pnode; + } + adb_mutex_unlock( &transport_lock ); +} + +static void +device_tracker_close( asocket* socket ) +{ + device_tracker* tracker = (device_tracker*) socket; + asocket* peer = socket->peer; + + D( "device tracker %p removed\n", tracker); + if (peer) { + peer->peer = NULL; + peer->close(peer); + } + device_tracker_remove(tracker); + free(tracker); +} + +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); + return -1; +} + +static int +device_tracker_send( device_tracker* tracker, + const char* buffer, + int len ) +{ + apacket* p = get_apacket(); + asocket* peer = tracker->socket.peer; + + memcpy(p->data, buffer, len); + p->len = len; + return peer->enqueue( peer, p ); +} + + +static void +device_tracker_ready( asocket* socket ) +{ + device_tracker* tracker = (device_tracker*) socket; + + /* we want to send the device list when the tracker connects + * for the first time, even if no update occured */ + if (tracker->update_needed > 0) { + char buffer[1024]; + int len; + + tracker->update_needed = 0; + + len = list_transports_msg(buffer, sizeof(buffer)); + device_tracker_send(tracker, buffer, len); + } +} + + +asocket* +create_device_tracker(void) +{ + device_tracker* tracker = calloc(1,sizeof(*tracker)); + + if(tracker == 0) fatal("cannot allocate device tracker"); + + D( "device tracker %p created\n", tracker); + + tracker->socket.enqueue = device_tracker_enqueue; + tracker->socket.ready = device_tracker_ready; + tracker->socket.close = device_tracker_close; + tracker->update_needed = 1; + + 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(void) +{ + char buffer[1024]; + int len; + device_tracker* tracker; + + len = list_transports_msg(buffer, sizeof(buffer)); + + tracker = device_tracker_list; + while (tracker != NULL) { + device_tracker* next = tracker->next; + /* note: this may destroy the tracker if the connection is closed */ + device_tracker_send(tracker, buffer, len); + tracker = next; + } +} +#else +void update_transports(void) +{ + // nothing to do on the device side +} +#endif // ADB_HOST + +typedef struct tmsg tmsg; +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; + + while(len > 0) { + r = adb_read(fd, p, len); + if(r > 0) { + len -= 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)); + return -1; + } + } + return 0; +} + +static int +transport_write_action(int fd, struct tmsg* m) +{ + char *p = (char*)m; + int len = sizeof(*m); + int r; + + while(len > 0) { + r = adb_write(fd, p, len); + if(r > 0) { + len -= 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)); + return -1; + } + } + return 0; +} + +static void transport_registration_func(int _fd, unsigned ev, void *data) +{ + tmsg m; + adb_thread_t output_thread_ptr; + adb_thread_t input_thread_ptr; + int s[2]; + atransport *t; + + if(!(ev & FDE_READ)) { + return; + } + + 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); + + /* 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); + t->next->prev = t->prev; + t->prev->next = t->next; + adb_mutex_unlock(&transport_lock); + + 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); + + memset(t,0xee,sizeof(atransport)); + free(t); + + update_transports(); + return; + } + + /* don't create transport threads for inaccessible devices */ + if (t->connection_state != CS_NOPERM) { + /* initial references are the two threads */ + t->ref_count = 2; + + if(adb_socketpair(s)) { + fatal_errno("cannot open transport socketpair"); + } + + D("transport: %s (%d,%d) starting\n", 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_set(&(t->transport_fde), FDE_READ); + + if(adb_thread_create(&input_thread_ptr, input_thread, t)){ + fatal_errno("cannot create input thread"); + } + + if(adb_thread_create(&output_thread_ptr, output_thread, t)){ + fatal_errno("cannot create output thread"); + } + } + + adb_mutex_lock(&transport_lock); + /* remove from pending list */ + t->next->prev = t->prev; + t->prev->next = t->next; + /* put us on the master device list */ + t->next = &transport_list; + t->prev = transport_list.prev; + t->next->prev = t; + t->prev->next = t; + adb_mutex_unlock(&transport_lock); + + t->disconnects.next = t->disconnects.prev = &t->disconnects; + + update_transports(); +} + +void init_transport_registration(void) +{ + int s[2]; + + if(adb_socketpair(s)){ + fatal_errno("cannot open transport registration socketpair"); + } + + transport_registration_send = s[0]; + transport_registration_recv = s[1]; + + fdevent_install(&transport_registration_fde, + transport_registration_recv, + transport_registration_func, + 0); + + fdevent_set(&transport_registration_fde, FDE_READ); +} + +/* the fdevent select pump is single threaded */ +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)) { + fatal_errno("cannot write transport registration socket\n"); + } +} + +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)) { + fatal_errno("cannot write transport registration socket\n"); + } +} + + +static void transport_unref_locked(atransport *t) +{ + 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); + } + t->close(t); + remove_transport(t); + } else { + D("transport: %s unref (count=%d)\n", 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_char_is_invalid(char ch) +{ + if ('A' <= ch && ch <= 'Z') + return 0; + if ('a' <= ch && ch <= 'z') + return 0; + if ('0' <= ch && ch <= '9') + return 0; + return 1; +} + +static int qual_match(const char *to_test, + const char *prefix, const char *qual, int 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 (prefix) { + while (*prefix) { + if (*prefix++ != *to_test++) + return 0; + } + } + + while (*qual) { + char ch = *qual++; + if (sanitize_qual && qual_char_is_invalid(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(int state, transport_type ttype, const char* serial, char** error_out) +{ + atransport *t; + atransport *result = NULL; + int ambiguous = 0; + +retry: + if (error_out) + *error_out = "device not found"; + + adb_mutex_lock(&transport_lock); + for (t = transport_list.next; t != &transport_list; t = t->next) { + if (t->connection_state == CS_NOPERM) { + if (error_out) + *error_out = "insufficient permissions for device"; + continue; + } + + /* 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, 0) || + qual_match(serial, "model:", t->model, 1) || + qual_match(serial, "device:", t->device, 0)) { + if (result) { + if (error_out) + *error_out = "more than one device"; + ambiguous = 1; + result = NULL; + break; + } + result = t; + } + } else { + if (ttype == kTransportUsb && t->type == kTransportUsb) { + if (result) { + if (error_out) + *error_out = "more than one device"; + ambiguous = 1; + result = NULL; + break; + } + result = t; + } else if (ttype == kTransportLocal && t->type == kTransportLocal) { + if (result) { + if (error_out) + *error_out = "more than one emulator"; + ambiguous = 1; + result = NULL; + break; + } + result = t; + } else if (ttype == kTransportAny) { + if (result) { + if (error_out) + *error_out = "more than one device and emulator"; + ambiguous = 1; + result = NULL; + break; + } + result = t; + } + } + } + adb_mutex_unlock(&transport_lock); + + if (result) { + if (result->connection_state == CS_UNAUTHORIZED) { + if (error_out) + *error_out = "device unauthorized. Please check the confirmation dialog on your device."; + result = NULL; + } + + /* offline devices are ignored -- they are either being born or dying */ + if (result && result->connection_state == CS_OFFLINE) { + if (error_out) + *error_out = "device offline"; + result = NULL; + } + /* check for required connection state */ + if (result && state != CS_ANY && result->connection_state != state) { + if (error_out) + *error_out = "invalid device state"; + result = NULL; + } + } + + if (result) { + /* found one that we can take */ + if (error_out) + *error_out = NULL; + } else if (state != CS_ANY && (serial || !ambiguous)) { + adb_sleep_ms(1000); + goto retry; + } + + return result; +} + +#if ADB_HOST +static const char *statename(atransport *t) +{ + switch(t->connection_state){ + case CS_OFFLINE: return "offline"; + case CS_BOOTLOADER: return "bootloader"; + case CS_DEVICE: return "device"; + case CS_HOST: return "host"; + case CS_RECOVERY: return "recovery"; + case CS_SIDELOAD: return "sideload"; + case CS_NOPERM: return "no permissions"; + case CS_UNAUTHORIZED: return "unauthorized"; + default: return "unknown"; + } +} + +static void add_qual(char **buf, size_t *buf_size, + const char *prefix, const char *qual, int sanitize_qual) +{ + size_t len; + int prefix_len; + + if (!buf || !*buf || !buf_size || !*buf_size || !qual || !*qual) + return; + + len = snprintf(*buf, *buf_size, "%s%n%s", prefix, &prefix_len, qual); + + if (sanitize_qual) { + char *cp; + for (cp = *buf + prefix_len; cp < *buf + len; cp++) { + if (qual_char_is_invalid(*cp)) + *cp = '_'; + } + } + + *buf_size -= len; + *buf += len; +} + +static size_t format_transport(atransport *t, char *buf, size_t bufsize, + int long_listing) +{ + const char* serial = t->serial; + if (!serial || !serial[0]) + serial = "????????????"; + + if (!long_listing) { + return snprintf(buf, bufsize, "%s\t%s\n", serial, statename(t)); + } else { + size_t len, remaining = bufsize; + + len = snprintf(buf, remaining, "%-22s %s", serial, statename(t)); + remaining -= len; + buf += len; + + add_qual(&buf, &remaining, " ", t->devpath, 0); + add_qual(&buf, &remaining, " product:", t->product, 0); + add_qual(&buf, &remaining, " model:", t->model, 1); + add_qual(&buf, &remaining, " device:", t->device, 0); + + len = snprintf(buf, remaining, "\n"); + remaining -= len; + + return bufsize - remaining; + } +} + +int list_transports(char *buf, size_t bufsize, int long_listing) +{ + char* p = buf; + char* end = buf + bufsize; + int len; + atransport *t; + + /* XXX OVERRUN PROBLEMS XXX */ + adb_mutex_lock(&transport_lock); + for(t = transport_list.next; t != &transport_list; t = t->next) { + len = format_transport(t, p, end - p, long_listing); + if (p + len >= end) { + /* discard last line if buffer is too short */ + break; + } + p += len; + } + p[0] = 0; + adb_mutex_unlock(&transport_lock); + return p - buf; +} + + +/* hack for osx */ +void close_usb_devices() +{ + atransport *t; + + adb_mutex_lock(&transport_lock); + for(t = transport_list.next; t != &transport_list; t = t->next) { + if ( !t->kicked ) { + t->kicked = 1; + t->kick(t); + } + } + adb_mutex_unlock(&transport_lock); +} +#endif // ADB_HOST + +int register_socket_transport(int s, const char *serial, int port, int local) +{ + atransport *t = calloc(1, sizeof(atransport)); + atransport *n; + char buff[32]; + + if (!serial) { + snprintf(buff, sizeof buff, "T-%p", t); + serial = buff; + } + D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port); + if (init_socket_transport(t, s, port, local) < 0) { + free(t); + return -1; + } + + adb_mutex_lock(&transport_lock); + for (n = pending_list.next; n != &pending_list; n = n->next) { + if (n->serial && !strcmp(serial, n->serial)) { + adb_mutex_unlock(&transport_lock); + free(t); + return -1; + } + } + + for (n = transport_list.next; n != &transport_list; n = n->next) { + if (n->serial && !strcmp(serial, n->serial)) { + adb_mutex_unlock(&transport_lock); + free(t); + return -1; + } + } + + t->next = &pending_list; + t->prev = pending_list.prev; + t->next->prev = t; + t->prev->next = t; + t->serial = strdup(serial); + adb_mutex_unlock(&transport_lock); + + register_transport(t); + return 0; +} + +#if ADB_HOST +atransport *find_transport(const char *serial) +{ + atransport *t; + + adb_mutex_lock(&transport_lock); + for(t = transport_list.next; t != &transport_list; t = t->next) { + if (t->serial && !strcmp(serial, t->serial)) { + break; + } + } + adb_mutex_unlock(&transport_lock); + + if (t != &transport_list) + return t; + else + return 0; +} + +void unregister_transport(atransport *t) +{ + adb_mutex_lock(&transport_lock); + t->next->prev = t->prev; + t->prev->next = t->next; + adb_mutex_unlock(&transport_lock); + + kick_transport(t); + transport_unref(t); +} + +// unregisters all non-emulator TCP transports +void unregister_all_tcp_transports() +{ + atransport *t, *next; + adb_mutex_lock(&transport_lock); + for (t = transport_list.next; t != &transport_list; t = next) { + next = t->next; + if (t->type == kTransportLocal && t->adb_port == 0) { + t->next->prev = t->prev; + t->prev->next = next; + // we cannot call kick_transport when holding transport_lock + if (!t->kicked) + { + t->kicked = 1; + t->kick(t); + } + transport_unref_locked(t); + } + } + + adb_mutex_unlock(&transport_lock); +} + +#endif + +void register_usb_transport(usb_handle *usb, const char *serial, const char *devpath, unsigned writeable) +{ + atransport *t = calloc(1, sizeof(atransport)); + D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb, + serial ? serial : ""); + init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM)); + if(serial) { + t->serial = strdup(serial); + } + if(devpath) { + t->devpath = strdup(devpath); + } + + adb_mutex_lock(&transport_lock); + t->next = &pending_list; + t->prev = pending_list.prev; + t->next->prev = t; + t->prev->next = t; + adb_mutex_unlock(&transport_lock); + + register_transport(t); +} + +/* this should only be used for transports with connection_state == CS_NOPERM */ +void unregister_usb_transport(usb_handle *usb) +{ + atransport *t; + adb_mutex_lock(&transport_lock); + for(t = transport_list.next; t != &transport_list; t = t->next) { + if (t->usb == usb && t->connection_state == CS_NOPERM) { + t->next->prev = t->prev; + t->prev->next = t->next; + break; + } + } + adb_mutex_unlock(&transport_lock); +} + +#undef TRACE_TAG +#define TRACE_TAG TRACE_RWX + +int readx(int fd, void *ptr, size_t len) +{ + char *p = ptr; + int r; +#if ADB_TRACE + int len0 = len; +#endif + D("readx: fd=%d wanted=%d\n", fd, (int)len); + while(len > 0) { + r = adb_read(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + if (r < 0) { + D("readx: fd=%d error %d: %s\n", fd, errno, strerror(errno)); + if (errno == EINTR) + continue; + } else { + D("readx: fd=%d disconnected\n", fd); + } + return -1; + } + } + +#if ADB_TRACE + D("readx: fd=%d wanted=%d got=%d\n", fd, len0, len0 - len); + dump_hex( ptr, len0 ); +#endif + return 0; +} + +int writex(int fd, const void *ptr, size_t len) +{ + char *p = (char*) ptr; + int r; + +#if ADB_TRACE + D("writex: fd=%d len=%d: ", fd, (int)len); + dump_hex( ptr, len ); +#endif + while(len > 0) { + r = adb_write(fd, p, len); + if(r > 0) { + len -= r; + p += r; + } else { + if (r < 0) { + D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno)); + if (errno == EINTR) + continue; + } else { + D("writex: fd=%d disconnected\n", fd); + } + return -1; + } + } + return 0; +} + +int check_header(apacket *p) +{ + if(p->msg.magic != (p->msg.command ^ 0xffffffff)) { + D("check_header(): invalid magic\n"); + return -1; + } + + if(p->msg.data_length > MAX_PAYLOAD) { + D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length); + 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) { + return -1; + } else { + return 0; + } +} diff --git a/adb/transport.h b/adb/transport.h new file mode 100644 index 0000000..992e052 --- /dev/null +++ b/adb/transport.h @@ -0,0 +1,26 @@ +/* + * 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 __TRANSPORT_H +#define __TRANSPORT_H + +/* convenience wrappers around read/write that will retry on +** EINTR and/or short read/write. Returns 0 on success, -1 +** on error or EOF. +*/ +int readx(int fd, void *ptr, size_t len); +int writex(int fd, const void *ptr, size_t len); +#endif /* __TRANSPORT_H */ diff --git a/adb/transport_local.c b/adb/transport_local.c new file mode 100644 index 0000000..1cfa24d --- /dev/null +++ b/adb/transport_local.c @@ -0,0 +1,444 @@ +/* + * 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 +#include +#include +#include + +#include "sysdeps.h" +#include +#if !ADB_HOST +#include +#endif + +#define TRACE_TAG TRACE_TRANSPORT +#include "adb.h" + +#ifdef HAVE_BIG_ENDIAN +#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24) +static inline void fix_endians(apacket *p) +{ + p->msg.command = H4(p->msg.command); + p->msg.arg0 = H4(p->msg.arg0); + p->msg.arg1 = H4(p->msg.arg1); + p->msg.data_length = H4(p->msg.data_length); + p->msg.data_check = H4(p->msg.data_check); + p->msg.magic = H4(p->msg.magic); +} +#else +#define fix_endians(p) do {} while (0) +#endif + +#if ADB_HOST +/* 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 16 + +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(readx(t->sfd, &p->msg, sizeof(amessage))){ + D("remote local: read terminated (message)\n"); + return -1; + } + + fix_endians(p); + +#if 0 && defined HAVE_BIG_ENDIAN + D("read remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n", + p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic); +#endif + if(check_header(p)) { + D("bad header: terminated (data)\n"); + return -1; + } + + if(readx(t->sfd, p->data, p->msg.data_length)){ + D("remote local: terminated (data)\n"); + return -1; + } + + if(check_data(p)) { + D("bad data: terminated (data)\n"); + return -1; + } + + return 0; +} + +static int remote_write(apacket *p, atransport *t) +{ + int length = p->msg.data_length; + + fix_endians(p); + +#if 0 && defined HAVE_BIG_ENDIAN + D("write remote packet: %04x arg0=%0x arg1=%0x data_length=%0x data_check=%0x magic=%0x\n", + p->msg.command, p->msg.arg0, p->msg.arg1, p->msg.data_length, p->msg.data_check, p->msg.magic); +#endif + if(writex(t->sfd, &p->msg, sizeof(amessage) + length)) { + D("remote local: write terminated\n"); + return -1; + } + + return 0; +} + + +int local_connect(int port) { + return local_connect_arbitrary_ports(port-1, port); +} + +int local_connect_arbitrary_ports(int console_port, int adb_port) +{ + char buf[64]; + int fd = -1; + +#if ADB_HOST + const char *host = getenv("ADBHOST"); + if (host) { + fd = socket_network_client(host, adb_port, SOCK_STREAM); + } +#endif + if (fd < 0) { + fd = socket_loopback_client(adb_port, SOCK_STREAM); + } + + if (fd >= 0) { + D("client: connected on remote on fd %d\n", fd); + close_on_exec(fd); + disable_tcp_nagle(fd); + snprintf(buf, sizeof buf, "%s%d", LOCAL_CLIENT_PREFIX, console_port); + register_socket_transport(fd, buf, adb_port, 1); + return 0; + } + return -1; +} + + +static void *client_socket_thread(void *x) +{ +#if ADB_HOST + int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; + int count = ADB_LOCAL_TRANSPORT_MAX; + + D("transport: client_socket_thread() starting\n"); + + /* try to connect to any number of running emulator instances */ + /* this is only done when ADB starts up. later, each new emulator */ + /* will send a message to ADB to indicate that is is starting up */ + for ( ; count > 0; count--, port += 2 ) { + (void) local_connect(port); + } +#endif + return 0; +} + +static void *server_socket_thread(void * arg) +{ + int serverfd, fd; + struct sockaddr addr; + socklen_t alen; + int port = (int)arg; + + D("transport: server_socket_thread() starting\n"); + serverfd = -1; + for(;;) { + if(serverfd == -1) { + serverfd = socket_inaddr_any_server(port, SOCK_STREAM); + if(serverfd < 0) { + D("server: cannot bind socket yet\n"); + adb_sleep_ms(1000); + 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); + if(fd >= 0) { + D("server: new connection on fd %d\n", fd); + close_on_exec(fd); + disable_tcp_nagle(fd); + register_socket_transport(fd, "host", port, 1); + } + } + D("transport: server_socket_thread() exiting\n"); + return 0; +} + +/* This is relevant only for ADB daemon running inside the emulator. */ +#if !ADB_HOST +/* + * 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 +#undef open +#undef write +#define open ___xxx_open +#define write ___xxx_write + +/* A worker thread that monitors host connections, and registers a transport for + * every new host connection. This thread replaces server_socket_thread on + * condition that adbd daemon runs inside the emulator, and emulator uses QEMUD + * pipe to communicate with adbd daemon inside the guest. This is done in order + * to provide more robust communication channel between ADB host and guest. The + * main issue with server_socket_thread approach is that it runs on top of TCP, + * and thus is sensitive to network disruptions. For instance, the + * ConnectionManager may decide to reset all network connections, in which case + * the connection between ADB host and guest will be lost. To make ADB traffic + * independent from the network, we use here 'adb' QEMUD service to transfer data + * between the host, and the guest. See external/qemu/android/adb-*.* that + * implements the emulator's side of the protocol. Another advantage of using + * QEMUD approach is that ADB will be up much sooner, since it doesn't depend + * anymore on network being set up. + * The guest side of the protocol contains the following phases: + * - Connect with adb QEMUD service. In this phase a handle to 'adb' QEMUD service + * is opened, and it becomes clear whether or not emulator supports that + * protocol. + * - Wait for the ADB host to create connection with the guest. This is done by + * sending an 'accept' request to the adb QEMUD service, and waiting on + * response. + * - When new ADB host connection is accepted, the connection with adb QEMUD + * service is registered as the transport, and a 'start' request is sent to the + * adb QEMUD service, indicating that the guest is ready to receive messages. + * Note that the guest will ignore messages sent down from the emulator before + * the transport registration is completed. That's why we need to send the + * 'start' request after the transport is registered. + */ +static void *qemu_socket_thread(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)arg; + int res, fd; + char tmp[256]; + char con_name[32]; + + D("transport: qemu_socket_thread() starting\n"); + + /* adb QEMUD service connection request. */ + snprintf(con_name, sizeof(con_name), "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. */ + adb_thread_t thr; + D("adb service is not available. Falling back to TCP socket.\n"); + adb_thread_create(&thr, server_socket_thread, arg); + return 0; + } + + for(;;) { + /* + * Wait till the host creates a new connection. + */ + + /* Send the 'accept' request. */ + res = adb_write(fd, _accept_req, strlen(_accept_req)); + if ((size_t)res == 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"); + 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)); + } + + /* 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; + } + } else { + D("Unable to send the '%s' request to ADB service.\n", _accept_req); + return 0; + } + } + D("transport: qemu_socket_thread() exiting\n"); + return 0; +} +#endif // !ADB_HOST + +void local_init(int port) +{ + adb_thread_t thr; + void* (*func)(void *); + + if(HOST) { + func = client_socket_thread; + } else { +#if ADB_HOST + func = server_socket_thread; +#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. */ + func = qemu_socket_thread; + } else { + /* Running inside the device: use TCP socket as the transport. */ + func = server_socket_thread; + } +#endif // !ADB_HOST + } + + D("transport: local %s init\n", HOST ? "client" : "server"); + + if(adb_thread_create(&thr, func, (void *)port)) { + fatal_errno("cannot create local socket %s thread", + HOST ? "client" : "server"); + } +} + +static void remote_kick(atransport *t) +{ + int fd = t->sfd; + t->sfd = -1; + adb_shutdown(fd); + adb_close(fd); + +#if ADB_HOST + if(HOST) { + int nn; + adb_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 +} + +static void remote_close(atransport *t) +{ + adb_close(t->fd); +} + + +#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) +{ + 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]; + } + } + return NULL; +} + +atransport* find_emulator_transport_by_adb_port(int adb_port) +{ + adb_mutex_lock( &local_transports_lock ); + atransport* result = find_emulator_transport_by_adb_port_locked(adb_port); + adb_mutex_unlock( &local_transports_lock ); + return result; +} + +/* Only call this function if you already hold local_transports_lock. */ +int get_available_local_transport_index_locked() +{ + int i; + for (i = 0; i < ADB_LOCAL_TRANSPORT_MAX; i++) { + if (local_transports[i] == NULL) { + return i; + } + } + return -1; +} + +int get_available_local_transport_index() +{ + adb_mutex_lock( &local_transports_lock ); + int result = get_available_local_transport_index_locked(); + adb_mutex_unlock( &local_transports_lock ); + return result; +} +#endif + +int init_socket_transport(atransport *t, int s, int adb_port, int local) +{ + int fail = 0; + + t->kick = remote_kick; + t->close = remote_close; + t->read_from_remote = remote_read; + t->write_to_remote = remote_write; + t->sfd = s; + t->sync_token = 1; + t->connection_state = CS_OFFLINE; + t->type = kTransportLocal; + t->adb_port = 0; + +#if ADB_HOST + if (HOST && 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 ); + } +#endif + return fail; +} diff --git a/adb/transport_usb.c b/adb/transport_usb.c new file mode 100644 index 0000000..c5e1408 --- /dev/null +++ b/adb/transport_usb.c @@ -0,0 +1,148 @@ +/* + * 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 +#include +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_TRANSPORT +#include "adb.h" + +#if ADB_HOST +#include "usb_vendors.h" +#endif + +#ifdef HAVE_BIG_ENDIAN +#define H4(x) (((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24) +static inline void fix_endians(apacket *p) +{ + p->msg.command = H4(p->msg.command); + p->msg.arg0 = H4(p->msg.arg0); + p->msg.arg1 = H4(p->msg.arg1); + p->msg.data_length = H4(p->msg.data_length); + p->msg.data_check = H4(p->msg.data_check); + p->msg.magic = H4(p->msg.magic); +} +unsigned host_to_le32(unsigned n) +{ + return H4(n); +} +#else +#define fix_endians(p) do {} while (0) +unsigned host_to_le32(unsigned n) +{ + return n; +} +#endif + +static int remote_read(apacket *p, atransport *t) +{ + if(usb_read(t->usb, &p->msg, sizeof(amessage))){ + D("remote usb: read terminated (message)\n"); + return -1; + } + + fix_endians(p); + + if(check_header(p)) { + D("remote usb: check_header failed\n"); + return -1; + } + + if(p->msg.data_length) { + if(usb_read(t->usb, p->data, p->msg.data_length)){ + D("remote usb: terminated (data)\n"); + return -1; + } + } + + if(check_data(p)) { + D("remote usb: check_data failed\n"); + return -1; + } + + return 0; +} + +static int remote_write(apacket *p, atransport *t) +{ + unsigned size = p->msg.data_length; + + fix_endians(p); + + if(usb_write(t->usb, &p->msg, sizeof(amessage))) { + D("remote usb: 1 - write terminated\n"); + 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"); + return -1; + } + + return 0; +} + +static void remote_close(atransport *t) +{ + usb_close(t->usb); + t->usb = 0; +} + +static void remote_kick(atransport *t) +{ + usb_kick(t->usb); +} + +void init_usb_transport(atransport *t, usb_handle *h, int state) +{ + D("transport: usb\n"); + t->close = remote_close; + t->kick = remote_kick; + t->read_from_remote = remote_read; + t->write_to_remote = remote_write; + t->sync_token = 1; + t->connection_state = state; + t->type = kTransportUsb; + t->usb = h; + +#if ADB_HOST + HOST = 1; +#else + HOST = 0; +#endif +} + +#if ADB_HOST +int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol) +{ + unsigned i; + for (i = 0; i < vendorIdCount; i++) { + if (vid == vendorIds[i]) { + if (usb_class == ADB_CLASS && usb_subclass == ADB_SUBCLASS && + usb_protocol == ADB_PROTOCOL) { + return 1; + } + + return 0; + } + } + + return 0; +} +#endif diff --git a/adb/usb_libusb.c b/adb/usb_libusb.c new file mode 100644 index 0000000..06ff5dc --- /dev/null +++ b/adb/usb_libusb.c @@ -0,0 +1,657 @@ +/* + * Copyright (C) 2009 bsdroid project + * Alexey Tarasov + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sysdeps.h" + +#define TRACE_TAG TRACE_USB +#include "adb.h" + +static adb_mutex_t usb_lock = ADB_MUTEX_INITIALIZER; +static libusb_context *ctx = NULL; + +struct usb_handle +{ + usb_handle *prev; + usb_handle *next; + + libusb_device *dev; + libusb_device_handle *devh; + int interface; + uint8_t dev_bus; + uint8_t dev_addr; + + int zero_mask; + unsigned char end_point_address[2]; + char serial[128]; + + adb_cond_t notify; + adb_mutex_t lock; +}; + +static struct usb_handle handle_list = { + .prev = &handle_list, + .next = &handle_list, +}; + +void +usb_cleanup() +{ + libusb_exit(ctx); +} + +void +report_bulk_libusb_error(int r) +{ + switch (r) { + case LIBUSB_ERROR_TIMEOUT: + D("Transfer timeout\n"); + break; + + case LIBUSB_ERROR_PIPE: + D("Control request is not supported\n"); + break; + + case LIBUSB_ERROR_OVERFLOW: + D("Device offered more data\n"); + break; + + case LIBUSB_ERROR_NO_DEVICE : + D("Device was disconnected\n"); + break; + + default: + D("Error %d during transfer\n", r); + break; + }; +} + +static int +usb_bulk_write(usb_handle *uh, const void *data, int len) +{ + int r = 0; + int transferred = 0; + + r = libusb_bulk_transfer(uh->devh, uh->end_point_address[1], (void *)data, len, + &transferred, 0); + + if (r != 0) { + D("usb_bulk_write(): "); + report_bulk_libusb_error(r); + return r; + } + + return (transferred); +} + +static int +usb_bulk_read(usb_handle *uh, void *data, int len) +{ + int r = 0; + int transferred = 0; + + r = libusb_bulk_transfer(uh->devh, uh->end_point_address[0], data, len, + &transferred, 0); + + if (r != 0) { + D("usb_bulk_read(): "); + report_bulk_libusb_error(r); + return r; + } + + return (transferred); +} + +int +usb_write(struct usb_handle *uh, const void *_data, int len) +{ + unsigned char *data = (unsigned char*) _data; + int n; + int need_zero = 0; + + if (uh->zero_mask == 1) { + if (!(len & uh->zero_mask)) { + need_zero = 1; + } + } + + D("usb_write(): %p:%d -> transport %p\n", _data, len, uh); + + while (len > 0) { + int xfer = (len > 4096) ? 4096 : len; + + n = usb_bulk_write(uh, data, xfer); + + if (n != xfer) { + D("usb_write(): failed for transport %p (%d bytes left)\n", uh, len); + return -1; + } + + len -= xfer; + data += xfer; + } + + if (need_zero){ + n = usb_bulk_write(uh, _data, 0); + + if (n < 0) { + D("usb_write(): failed to finish operation for transport %p\n", uh); + } + return n; + } + + return 0; +} + +int +usb_read(struct usb_handle *uh, void *_data, int len) +{ + unsigned char *data = (unsigned char*) _data; + int n; + + D("usb_read(): %p:%d <- transport %p\n", _data, len, uh); + + while (len > 0) { + int xfer = (len > 4096) ? 4096 : len; + + n = usb_bulk_read(uh, data, xfer); + + if (n != xfer) { + if (n > 0) { + data += n; + len -= n; + continue; + } + + D("usb_read(): failed for transport %p (%d bytes left)\n", uh, len); + return -1; + } + + len -= xfer; + data += xfer; + } + + return 0; + } + +int +usb_close(struct usb_handle *h) +{ + D("usb_close(): closing transport %p\n", h); + adb_mutex_lock(&usb_lock); + + h->next->prev = h->prev; + h->prev->next = h->next; + h->prev = NULL; + h->next = NULL; + + libusb_release_interface(h->devh, h->interface); + libusb_close(h->devh); + libusb_unref_device(h->dev); + + adb_mutex_unlock(&usb_lock); + + free(h); + + return (0); +} + +void usb_kick(struct usb_handle *h) +{ + D("usb_cick(): kicking transport %p\n", h); + + adb_mutex_lock(&h->lock); + unregister_usb_transport(h); + adb_mutex_unlock(&h->lock); + + h->next->prev = h->prev; + h->prev->next = h->next; + h->prev = NULL; + h->next = NULL; + + libusb_release_interface(h->devh, h->interface); + libusb_close(h->devh); + libusb_unref_device(h->dev); + free(h); +} + +int +check_usb_interface(libusb_interface *interface, + libusb_device_descriptor *desc, + struct usb_handle *uh) +{ + int e; + + if (interface->num_altsetting == 0) { + D("check_usb_interface(): No interface settings\n"); + return -1; + } + + libusb_interface_descriptor *idesc = &interface->altsetting[0]; + + if (idesc->bNumEndpoints != 2) { + D("check_usb_interface(): Interface have not 2 endpoints, ignoring\n"); + return -1; + } + + for (e = 0; e < idesc->bNumEndpoints; e++) { + libusb_endpoint_descriptor *edesc = &idesc->endpoint[e]; + + if (edesc->bmAttributes != LIBUSB_TRANSFER_TYPE_BULK) { + D("check_usb_interface(): Endpoint (%u) is not bulk (%u), ignoring\n", + edesc->bmAttributes, LIBUSB_TRANSFER_TYPE_BULK); + return -1; + } + + if (edesc->bEndpointAddress & LIBUSB_ENDPOINT_IN) + uh->end_point_address[0] = edesc->bEndpointAddress; + else + uh->end_point_address[1] = edesc->bEndpointAddress; + + /* aproto 01 needs 0 termination */ + if (idesc->bInterfaceProtocol == 0x01) { + uh->zero_mask = edesc->wMaxPacketSize - 1; + D("check_usb_interface(): Forced Android interface protocol v.1\n"); + } + } + + D("check_usb_interface(): Device: %04x:%04x " + "iclass: %x, isclass: %x, iproto: %x ep: %x/%x-> ", + desc->idVendor, desc->idProduct, idesc->bInterfaceClass, + idesc->bInterfaceSubClass, idesc->bInterfaceProtocol, + uh->end_point_address[0], uh->end_point_address[1]); + + if (!is_adb_interface(desc->idVendor, desc->idProduct, + idesc->bInterfaceClass, idesc->bInterfaceSubClass, + idesc->bInterfaceProtocol)) + { + D("not matches\n"); + return -1; + } + + D("matches\n"); + return 1; +} + +int +check_usb_interfaces(libusb_config_descriptor *config, + libusb_device_descriptor *desc, struct usb_handle *uh) +{ + int i; + + for (i = 0; i < config->bNumInterfaces; ++i) { + if (check_usb_interface(&config->interface[i], desc, uh) != -1) { + /* found some interface and saved information about it */ + D("check_usb_interfaces(): Interface %d of %04x:%04x " + "matches Android device\n", i, desc->idVendor, + desc->idProduct); + + return i; + } + } + + return -1; +} + +int +register_device(struct usb_handle *uh, const char *serial) +{ + D("register_device(): Registering %p [%s] as USB transport\n", + uh, serial); + + struct usb_handle *usb= NULL; + + usb = calloc(1, sizeof(struct usb_handle)); + memcpy(usb, uh, sizeof(struct usb_handle)); + strcpy(usb->serial, uh->serial); + + adb_cond_init(&usb->notify, 0); + adb_mutex_init(&usb->lock, 0); + + adb_mutex_lock(&usb_lock); + + usb->next = &handle_list; + usb->prev = handle_list.prev; + usb->prev->next = usb; + usb->next->prev = usb; + + adb_mutex_unlock(&usb_lock); + + register_usb_transport(usb, serial, NULL, 1); + + return (1); +} + +int +already_registered(usb_handle *uh) +{ + struct usb_handle *usb= NULL; + int exists = 0; + + adb_mutex_lock(&usb_lock); + + for (usb = handle_list.next; usb != &handle_list; usb = usb->next) { + if ((usb->dev_bus == uh->dev_bus) && + (usb->dev_addr == uh->dev_addr)) + { + exists = 1; + break; + } + } + + adb_mutex_unlock(&usb_lock); + + return exists; +} + +void +check_device(libusb_device *dev) +{ + struct usb_handle uh; + int i = 0; + int found = -1; + char serial[256] = {0}; + + libusb_device_descriptor desc; + libusb_config_descriptor *config = NULL; + + int r = libusb_get_device_descriptor(dev, &desc); + + if (r != LIBUSB_SUCCESS) { + D("check_device(): Failed to get device descriptor\n"); + return; + } + + if ((desc.idVendor == 0) && (desc.idProduct == 0)) + return; + + D("check_device(): Probing usb device %04x:%04x\n", + desc.idVendor, desc.idProduct); + + if (!is_adb_interface (desc.idVendor, desc.idProduct, + ADB_CLASS, ADB_SUBCLASS, ADB_PROTOCOL)) + { + D("check_device(): Ignored due unknown vendor id\n"); + return; + } + + uh.dev_bus = libusb_get_bus_number(dev); + uh.dev_addr = libusb_get_device_address(dev); + + if (already_registered(&uh)) { + D("check_device(): Device (bus: %d, address: %d) " + "is already registered\n", uh.dev_bus, uh.dev_addr); + return; + } + + D("check_device(): Device bus: %d, address: %d\n", + uh.dev_bus, uh.dev_addr); + + r = libusb_get_active_config_descriptor(dev, &config); + + if (r != 0) { + if (r == LIBUSB_ERROR_NOT_FOUND) { + D("check_device(): Device %4x:%4x is unconfigured\n", + desc.idVendor, desc.idProduct); + return; + } + + D("check_device(): Failed to get configuration for %4x:%4x\n", + desc.idVendor, desc.idProduct); + return; + } + + if (config == NULL) { + D("check_device(): Sanity check failed after " + "getting active config\n"); + return; + } + + if (config->interface != NULL) { + found = check_usb_interfaces(config, &desc, &uh); + } + + /* not needed anymore */ + libusb_free_config_descriptor(config); + + r = libusb_open(dev, &uh.devh); + uh.dev = dev; + + if (r != 0) { + switch (r) { + case LIBUSB_ERROR_NO_MEM: + D("check_device(): Memory allocation problem\n"); + break; + + case LIBUSB_ERROR_ACCESS: + D("check_device(): Permissions problem, " + "current user priveleges are messed up?\n"); + break; + + case LIBUSB_ERROR_NO_DEVICE: + D("check_device(): Device disconected, bad cable?\n"); + break; + + default: + D("check_device(): libusb triggered error %d\n", r); + } + // skip rest + found = -1; + } + + if (found >= 0) { + D("check_device(): Device matches Android interface\n"); + // read the device's serial number + memset(serial, 0, sizeof(serial)); + uh.interface = found; + + r = libusb_claim_interface(uh.devh, uh.interface); + + if (r < 0) { + D("check_device(): Failed to claim interface %d\n", + uh.interface); + + goto fail; + } + + if (desc.iSerialNumber) { + // reading serial + uint16_t buffer[128] = {0}; + uint16_t languages[128] = {0}; + int languageCount = 0; + + memset(languages, 0, sizeof(languages)); + r = libusb_control_transfer(uh.devh, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_STRING << 8, + 0, (uint8_t *)languages, sizeof(languages), 0); + + if (r <= 0) { + D("check_device(): Failed to get languages count\n"); + goto fail; + } + + languageCount = (r - 2) / 2; + + for (i = 1; i <= languageCount; ++i) { + memset(buffer, 0, sizeof(buffer)); + + r = libusb_control_transfer(uh.devh, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, + LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc.iSerialNumber, + languages[i], (uint8_t *)buffer, sizeof(buffer), 0); + + if (r > 0) { /* converting serial */ + int j = 0; + r /= 2; + + for (j = 1; j < r; ++j) + serial[j - 1] = buffer[j]; + + serial[j - 1] = '\0'; + break; /* languagesCount cycle */ + } + } + + if (register_device(&uh, serial) == 0) { + D("check_device(): Failed to register device\n"); + goto fail_interface; + } + + libusb_ref_device(dev); + } + } + + return; + +fail_interface: + libusb_release_interface(uh.devh, uh.interface); + +fail: + libusb_close(uh.devh); + uh.devh = NULL; +} + +int +check_device_connected(struct usb_handle *uh) +{ + int r = libusb_kernel_driver_active(uh->devh, uh->interface); + + if (r == LIBUSB_ERROR_NO_DEVICE) + return 0; + + if (r < 0) + return -1; + + return 1; +} + +void +kick_disconnected() +{ + struct usb_handle *usb= NULL; + + adb_mutex_lock(&usb_lock); + + for (usb = handle_list.next; usb != &handle_list; usb = usb->next) { + + if (check_device_connected(usb) == 0) { + D("kick_disconnected(): Transport %p is not online anymore\n", + usb); + + usb_kick(usb); + } + } + + adb_mutex_unlock(&usb_lock); +} + +void +scan_usb_devices() +{ + D("scan_usb_devices(): started\n"); + + libusb_device **devs= NULL; + libusb_device *dev= NULL; + ssize_t cnt = libusb_get_device_list(ctx, &devs); + + if (cnt < 0) { + D("scan_usb_devices(): Failed to get device list (error: %d)\n", + cnt); + + return; + } + + int i = 0; + + while ((dev = devs[i++]) != NULL) { + check_device(dev); + } + + libusb_free_device_list(devs, 1); +} + +void * +device_poll_thread(void* unused) +{ + D("device_poll_thread(): Created USB scan thread\n"); + + for (;;) { + sleep(5); + kick_disconnected(); + scan_usb_devices(); + } + + /* never reaching this point */ + return (NULL); +} + +static void +sigalrm_handler(int signo) +{ + /* nothing */ +} + +void +usb_init() +{ + D("usb_init(): started\n"); + adb_thread_t tid; + struct sigaction actions; + + int r = libusb_init(&ctx); + + if (r != LIBUSB_SUCCESS) { + err(EX_IOERR, "Failed to init libusb\n"); + } + + memset(&actions, 0, sizeof(actions)); + + sigemptyset(&actions.sa_mask); + + actions.sa_flags = 0; + actions.sa_handler = sigalrm_handler; + + sigaction(SIGALRM, &actions, NULL); + + /* initial device scan */ + scan_usb_devices(); + + /* starting USB event polling thread */ + if (adb_thread_create(&tid, device_poll_thread, NULL)) { + err(EX_IOERR, "cannot create USB scan thread\n"); + } + + D("usb_init(): finished\n"); +} + diff --git a/adb/usb_linux.c b/adb/usb_linux.c new file mode 100644 index 0000000..7bf2057 --- /dev/null +++ b/adb/usb_linux.c @@ -0,0 +1,715 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) +#include +#else +#include +#endif +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_USB +#include "adb.h" + + +/* usb scan debugging is waaaay too verbose */ +#define DBGX(x...) + +ADB_MUTEX_DEFINE( usb_lock ); + +struct usb_handle +{ + usb_handle *prev; + usb_handle *next; + + char fname[64]; + int desc; + unsigned char ep_in; + unsigned char ep_out; + + unsigned zero_mask; + unsigned writeable; + + struct usbdevfs_urb urb_in; + struct usbdevfs_urb urb_out; + + int urb_in_busy; + int urb_out_busy; + int dead; + + adb_cond_t notify; + adb_mutex_t lock; + + // for garbage collecting disconnected devices + int mark; + + // ID of thread currently in REAPURB + pthread_t reaper_thread; +}; + +static usb_handle handle_list = { + .prev = &handle_list, + .next = &handle_list, +}; + +static int known_device(const char *dev_name) +{ + usb_handle *usb; + + adb_mutex_lock(&usb_lock); + for(usb = handle_list.next; usb != &handle_list; usb = usb->next){ + if(!strcmp(usb->fname, dev_name)) { + // set mark flag to indicate this device is still alive + usb->mark = 1; + adb_mutex_unlock(&usb_lock); + return 1; + } + } + adb_mutex_unlock(&usb_lock); + return 0; +} + +static void kick_disconnected_devices() +{ + usb_handle *usb; + + adb_mutex_lock(&usb_lock); + // kick any devices in the device list that were not found in the device scan + for(usb = handle_list.next; usb != &handle_list; usb = usb->next){ + if (usb->mark == 0) { + usb_kick(usb); + } else { + usb->mark = 0; + } + } + adb_mutex_unlock(&usb_lock); + +} + +static void register_device(const char *dev_name, const char *devpath, + unsigned char ep_in, unsigned char ep_out, + int ifc, int serial_index, unsigned zero_mask); + +static inline int badname(const char *name) +{ + while(*name) { + if(!isdigit(*name++)) return 1; + } + return 0; +} + +static void find_usb_device(const char *base, + void (*register_device_callback) + (const char *, const char *, unsigned char, unsigned char, int, int, unsigned)) +{ + char busname[32], devname[32]; + unsigned char local_ep_in, local_ep_out; + DIR *busdir , *devdir ; + struct dirent *de; + int fd ; + + busdir = opendir(base); + if(busdir == 0) return; + + while((de = readdir(busdir)) != 0) { + if(badname(de->d_name)) continue; + + snprintf(busname, sizeof busname, "%s/%s", base, de->d_name); + devdir = opendir(busname); + if(devdir == 0) continue; + +// DBGX("[ scanning %s ]\n", busname); + while((de = readdir(devdir))) { + 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; + size_t desclength; + + if(badname(de->d_name)) continue; + snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name); + + if(known_device(devname)) { + DBGX("skipping %s\n", devname); + continue; + } + +// DBGX("[ scanning %s ]\n", devname); + if((fd = unix_open(devname, O_RDONLY)) < 0) { + continue; + } + + desclength = adb_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 %d is too small\n", desclength); + adb_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)) { + adb_close(fd); + continue; + } + + vid = device->idVendor; + pid = device->idProduct; + DBGX("[ %s is V:%04x P:%04x ]\n", devname, 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"); + adb_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 = NULL; + + DBGX("looking for bulk endpoints\n"); + // looks like ADB... + ep1 = (struct usb_endpoint_descriptor *)bufptr; + bufptr += USB_DT_ENDPOINT_SIZE; + ep2 = (struct usb_endpoint_descriptor *)bufptr; + bufptr += USB_DT_ENDPOINT_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. + 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)) { + char *slash; + ssize_t link_len; + snprintf(pathbuf, sizeof(pathbuf), "/sys/dev/char/%d:%d", + major(st.st_rdev), minor(st.st_rdev)); + link_len = readlink(pathbuf, link, sizeof(link) - 1); + if (link_len > 0) { + link[link_len] = '\0'; + slash = strrchr(link, '/'); + if (slash) { + snprintf(pathbuf, sizeof(pathbuf), + "usb:%s", slash + 1); + devpath = pathbuf; + } + } + } + + register_device_callback(devname, devpath, + local_ep_in, local_ep_out, + interface->bInterfaceNumber, device->iSerialNumber, zero_mask); + break; + } + } else { + bufptr += length; + } + } // end of while + + adb_close(fd); + } // end of devdir while + closedir(devdir); + } //end of busdir while + closedir(busdir); +} + +void usb_cleanup() +{ +} + +static int usb_bulk_write(usb_handle *h, const void *data, int len) +{ + struct usbdevfs_urb *urb = &h->urb_out; + int res; + struct timeval tv; + struct timespec ts; + + memset(urb, 0, sizeof(*urb)); + urb->type = USBDEVFS_URB_TYPE_BULK; + urb->endpoint = h->ep_out; + urb->status = -1; + urb->buffer = (void*) data; + urb->buffer_length = len; + + D("++ write ++\n"); + + adb_mutex_lock(&h->lock); + if(h->dead) { + res = -1; + goto fail; + } + do { + res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb); + } while((res < 0) && (errno == EINTR)); + + if(res < 0) { + goto fail; + } + + res = -1; + h->urb_out_busy = 1; + for(;;) { + /* time out after five seconds */ + gettimeofday(&tv, NULL); + ts.tv_sec = tv.tv_sec + 5; + ts.tv_nsec = tv.tv_usec * 1000L; + res = pthread_cond_timedwait(&h->notify, &h->lock, &ts); + if(res < 0 || h->dead) { + break; + } + if(h->urb_out_busy == 0) { + if(urb->status == 0) { + res = urb->actual_length; + } + break; + } + } +fail: + adb_mutex_unlock(&h->lock); + D("-- write --\n"); + return res; +} + +static int usb_bulk_read(usb_handle *h, void *data, int len) +{ + struct usbdevfs_urb *urb = &h->urb_in; + struct usbdevfs_urb *out = NULL; + int res; + + 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; + + + adb_mutex_lock(&h->lock); + if(h->dead) { + res = -1; + goto fail; + } + do { + res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb); + } while((res < 0) && (errno == EINTR)); + + if(res < 0) { + goto fail; + } + + h->urb_in_busy = 1; + for(;;) { + D("[ reap urb - wait ]\n"); + h->reaper_thread = pthread_self(); + adb_mutex_unlock(&h->lock); + res = ioctl(h->desc, USBDEVFS_REAPURB, &out); + int saved_errno = errno; + adb_mutex_lock(&h->lock); + h->reaper_thread = 0; + if(h->dead) { + res = -1; + break; + } + if(res < 0) { + if(saved_errno == EINTR) { + continue; + } + D("[ reap urb - error ]\n"); + break; + } + 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 = 0; + if(urb->status == 0) { + res = urb->actual_length; + } else { + res = -1; + } + break; + } + if(out == &h->urb_out) { + D("[ reap urb - OUT compelete ]\n"); + h->urb_out_busy = 0; + adb_cond_broadcast(&h->notify); + } + } +fail: + adb_mutex_unlock(&h->lock); + return res; +} + + +int usb_write(usb_handle *h, const void *_data, int len) +{ + unsigned char *data = (unsigned char*) _data; + int n; + int need_zero = 0; + + if(h->zero_mask) { + /* if we need 0-markers and our transfer + ** is an even multiple of the packet size, + ** we make note of it + */ + if(!(len & h->zero_mask)) { + need_zero = 1; + } + } + + while(len > 0) { + int xfer = (len > 4096) ? 4096 : len; + + n = usb_bulk_write(h, data, xfer); + if(n != xfer) { + D("ERROR: n = %d, errno = %d (%s)\n", + n, errno, strerror(errno)); + return -1; + } + + len -= xfer; + data += xfer; + } + + if(need_zero){ + n = usb_bulk_write(h, _data, 0); + return 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 > 4096) ? 4096 : len; + + D("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname); + n = usb_bulk_read(h, data, xfer); + D("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname); + if(n != xfer) { + if((errno == ETIMEDOUT) && (h->desc != -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) +{ + D("[ kicking %p (fd = %d) ]\n", h, h->desc); + adb_mutex_lock(&h->lock); + if(h->dead == 0) { + h->dead = 1; + + 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->desc, USBDEVFS_DISCARDURB, &h->urb_in); + ioctl(h->desc, USBDEVFS_DISCARDURB, &h->urb_out); + h->urb_in.status = -ENODEV; + h->urb_out.status = -ENODEV; + h->urb_in_busy = 0; + h->urb_out_busy = 0; + adb_cond_broadcast(&h->notify); + } else { + unregister_usb_transport(h); + } + } + adb_mutex_unlock(&h->lock); +} + +int usb_close(usb_handle *h) +{ + D("[ usb close ... ]\n"); + adb_mutex_lock(&usb_lock); + h->next->prev = h->prev; + h->prev->next = h->next; + h->prev = 0; + h->next = 0; + + adb_close(h->desc); + D("[ usb closed %p (fd = %d) ]\n", h, h->desc); + adb_mutex_unlock(&usb_lock); + + free(h); + return 0; +} + +static void register_device(const char *dev_name, const char *devpath, + unsigned char ep_in, unsigned char ep_out, + int interface, int serial_index, unsigned zero_mask) +{ + usb_handle* usb = 0; + int n = 0; + char serial[256]; + + /* 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 'o handles with a matching + ** name, we have no further work to do. + */ + adb_mutex_lock(&usb_lock); + for(usb = handle_list.next; usb != &handle_list; usb = usb->next){ + if(!strcmp(usb->fname, dev_name)) { + adb_mutex_unlock(&usb_lock); + return; + } + } + adb_mutex_unlock(&usb_lock); + + D("[ usb located new device %s (%d/%d/%d) ]\n", + dev_name, ep_in, ep_out, interface); + usb = calloc(1, sizeof(usb_handle)); + strcpy(usb->fname, dev_name); + usb->ep_in = ep_in; + usb->ep_out = ep_out; + usb->zero_mask = zero_mask; + usb->writeable = 1; + + adb_cond_init(&usb->notify, 0); + adb_mutex_init(&usb->lock, 0); + /* initialize mark to 1 so we don't get garbage collected after the device scan */ + usb->mark = 1; + usb->reaper_thread = 0; + + usb->desc = unix_open(usb->fname, O_RDWR); + if(usb->desc < 0) { + /* if we fail, see if have read-only access */ + usb->desc = unix_open(usb->fname, O_RDONLY); + if(usb->desc < 0) goto fail; + usb->writeable = 0; + D("[ usb open read-only %s fd = %d]\n", usb->fname, usb->desc); + } else { + D("[ usb open %s fd = %d]\n", usb->fname, usb->desc); + n = ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface); + if(n != 0) goto fail; + } + + /* read the device's serial number */ + serial[0] = 0; + memset(serial, 0, sizeof(serial)); + if (serial_index) { + struct usbdevfs_ctrltransfer ctrl; + __u16 buffer[128]; + __u16 languages[128]; + int i, result; + int languageCount = 0; + + memset(languages, 0, sizeof(languages)); + memset(&ctrl, 0, sizeof(ctrl)); + + // read list of supported languages + ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; + ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; + ctrl.wValue = (USB_DT_STRING << 8) | 0; + ctrl.wIndex = 0; + ctrl.wLength = sizeof(languages); + ctrl.data = languages; + ctrl.timeout = 1000; + + result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl); + if (result > 0) + languageCount = (result - 2) / 2; + + for (i = 1; i <= languageCount; i++) { + memset(buffer, 0, sizeof(buffer)); + memset(&ctrl, 0, sizeof(ctrl)); + + ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE; + ctrl.bRequest = USB_REQ_GET_DESCRIPTOR; + ctrl.wValue = (USB_DT_STRING << 8) | serial_index; + ctrl.wIndex = __le16_to_cpu(languages[i]); + ctrl.wLength = sizeof(buffer); + ctrl.data = buffer; + ctrl.timeout = 1000; + + result = ioctl(usb->desc, USBDEVFS_CONTROL, &ctrl); + if (result > 0) { + int i; + // skip first word, and copy the rest to the serial string, changing shorts to bytes. + result /= 2; + for (i = 1; i < result; i++) + serial[i - 1] = __le16_to_cpu(buffer[i]); + serial[i - 1] = 0; + break; + } + } + } + + /* add to the end of the active handles */ + adb_mutex_lock(&usb_lock); + usb->next = &handle_list; + usb->prev = handle_list.prev; + usb->prev->next = usb; + usb->next->prev = usb; + adb_mutex_unlock(&usb_lock); + + register_usb_transport(usb, serial, devpath, usb->writeable); + return; + +fail: + D("[ usb open %s error=%d, err_str = %s]\n", + usb->fname, errno, strerror(errno)); + if(usb->desc >= 0) { + adb_close(usb->desc); + } + free(usb); +} + +void* device_poll_thread(void* unused) +{ + D("Created device thread\n"); + for(;;) { + /* XXX use inotify */ + find_usb_device("/dev/bus/usb", register_device); + kick_disconnected_devices(); + sleep(1); + } + return NULL; +} + +static void sigalrm_handler(int signo) +{ + // don't need to do anything here +} + +void usb_init() +{ + adb_thread_t tid; + struct sigaction actions; + + memset(&actions, 0, sizeof(actions)); + sigemptyset(&actions.sa_mask); + actions.sa_flags = 0; + actions.sa_handler = sigalrm_handler; + sigaction(SIGALRM,& actions, NULL); + + if(adb_thread_create(&tid, device_poll_thread, NULL)){ + fatal_errno("cannot create input thread"); + } +} diff --git a/adb/usb_linux_client.c b/adb/usb_linux_client.c new file mode 100644 index 0000000..fb1dad0 --- /dev/null +++ b/adb/usb_linux_client.c @@ -0,0 +1,492 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_USB +#include "adb.h" + +#define MAX_PACKET_SIZE_FS 64 +#define MAX_PACKET_SIZE_HS 512 + +#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 */ +}; + +static const struct { + struct usb_functionfs_descs_head header; + struct { + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor_no_audio source; + struct usb_endpoint_descriptor_no_audio sink; + } __attribute__((packed)) fs_descs, hs_descs; +} __attribute__((packed)) descriptors = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), + .length = cpu_to_le32(sizeof(descriptors)), + .fs_count = 3, + .hs_count = 3, + }, + .fs_descs = { + .intf = { + .bLength = sizeof(descriptors.fs_descs.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(descriptors.fs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + .sink = { + .bLength = sizeof(descriptors.fs_descs.sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + }, + .hs_descs = { + .intf = { + .bLength = sizeof(descriptors.hs_descs.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(descriptors.hs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_HS, + }, + .sink = { + .bLength = sizeof(descriptors.hs_descs.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 (1) { + // 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 = adb_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) +{ + int n; + + D("about to read (fd=%d, len=%d)\n", h->fd, len); + n = adb_read(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 void usb_adb_kick(usb_handle *h) +{ + D("usb_kick\n"); + adb_mutex_lock(&h->lock); + adb_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; + adb_thread_t tid; + int fd; + + h = calloc(1, sizeof(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. + 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(&tid, usb_adb_open_thread, h)){ + fatal_errno("cannot create usb thread"); + } +} + + +static void init_functionfs(struct usb_handle *h) +{ + ssize_t ret; + + 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, &descriptors, sizeof(descriptors)); + 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 (1) { + // wait until the USB device needs opening + adb_mutex_lock(&usb->lock); + while (usb->control != -1) + adb_cond_wait(&usb->notify, &usb->lock); + adb_mutex_unlock(&usb->lock); + + while (1) { + init_functionfs(usb); + + if (usb->control >= 0) + break; + + adb_sleep_ms(1000); + } + + 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 char *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) +{ + int n; + + D("about to write (fd=%d, len=%d)\n", h->bulk_in, len); + n = bulk_write(h->bulk_in, data, len); + if (n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->bulk_in, n, errno, strerror(errno)); + return -1; + } + D("[ done fd=%d ]\n", h->bulk_in); + return 0; +} + +static int bulk_read(int bulk_out, char *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=%d count=%d ]\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) +{ + int n; + + D("about to read (fd=%d, len=%d)\n", h->bulk_out, len); + n = bulk_read(h->bulk_out, data, len); + if (n != len) { + D("ERROR: fd = %d, n = %d, errno = %d (%s)\n", + h->bulk_out, n, errno, 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) ]", 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); + + adb_mutex_lock(&h->lock); + adb_close(h->control); + adb_close(h->bulk_out); + adb_close(h->bulk_in); + h->control = 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() +{ + usb_handle *h; + adb_thread_t tid; + + D("[ usb_init - using FunctionFS ]\n"); + + h = calloc(1, sizeof(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(&tid, 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(); +} + +void usb_cleanup() +{ +} + +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.c b/adb/usb_osx.c new file mode 100644 index 0000000..45ce444 --- /dev/null +++ b/adb/usb_osx.c @@ -0,0 +1,545 @@ +/* + * 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 + +#include +#include +#include +#include +#include + +#include "sysdeps.h" + +#include + +#define TRACE_TAG TRACE_USB +#include "adb.h" +#include "usb_vendors.h" + +#define DBG D + +static IONotificationPortRef notificationPort = 0; +static io_iterator_t* notificationIterators; + +struct usb_handle +{ + UInt8 bulkIn; + UInt8 bulkOut; + IOUSBInterfaceInterface **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(IOUSBInterfaceInterface **iface, + UInt16 vendor, UInt16 product); + +static int +InitUSB() +{ + CFMutableDictionaryRef matchingDict; + CFRunLoopSourceRef runLoopSource; + SInt32 vendor, if_subclass, if_protocol; + unsigned i; + + //* 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); + + memset(notificationIterators, 0, sizeof(notificationIterators)); + + //* loop through all supported vendors + for (i = 0; i < vendorIdCount; i++) { + //* 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; + } + + //* Match based on vendor id, interface subclass and protocol + vendor = vendorIds[i]; + if_subclass = ADB_SUBCLASS; + if_protocol = ADB_PROTOCOL; + CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), + CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt32Type, &vendor)); + CFDictionarySetValue(matchingDict, CFSTR(kUSBInterfaceSubClass), + CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt32Type, &if_subclass)); + CFDictionarySetValue(matchingDict, CFSTR(kUSBInterfaceProtocol), + CFNumberCreate(kCFAllocatorDefault, + kCFNumberSInt32Type, &if_protocol)); + IOServiceAddMatchingNotification( + notificationPort, + kIOFirstMatchNotification, + matchingDict, + AndroidInterfaceAdded, + NULL, + ¬ificationIterators[i]); + + //* Iterate over set of matching interfaces to access already-present + //* devices and to arm the notification + AndroidInterfaceAdded(NULL, notificationIterators[i]); + } + + 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; + 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; + } + + //* 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:%lX", 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((IOUSBInterfaceInterface**)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); + } +} + +//* TODO: simplify this further since we only register to get ADB interface +//* subclass+protocol events +static usb_handle* +CheckInterface(IOUSBInterfaceInterface **interface, UInt16 vendor, UInt16 product) +{ + usb_handle* handle = NULL; + 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 = calloc(1, sizeof(usb_handle)); + + //* 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 = 0; endpoint <= interfaceNumEndpoints; endpoint++) { + UInt8 transferType; + UInt16 maxPacketSize; + UInt8 interval; + UInt8 number; + UInt8 direction; + + kr = (*interface)->GetPipeProperties(interface, endpoint, &direction, + &number, &transferType, &maxPacketSize, &interval); + + if (kIOReturnSuccess == kr) { + if (kUSBBulk != transferType) + continue; + + if (kUSBIn == direction) + handle->bulkIn = endpoint; + + if (kUSBOut == direction) + handle->bulkOut = endpoint; + + handle->zero_mask = maxPacketSize - 1; + } else { + DBG("ERR: FindDeviceInterface - could not get pipe properties\n"); + goto err_get_pipe_props; + } + } + + 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) +{ + unsigned i; + + 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; + + for (i = 0; i < vendorIdCount; i++) { + IOObjectRelease(notificationIterators[i]); + } + IONotificationPortDestroy(notificationPort); + + DBG("RunLoopThread done\n"); + return NULL; +} + + +static int initialized = 0; +void usb_init() +{ + if (!initialized) + { + adb_thread_t tid; + + notificationIterators = (io_iterator_t*)malloc( + vendorIdCount * sizeof(io_iterator_t)); + + adb_mutex_init(&start_lock, NULL); + adb_cond_init(&start_cond, NULL); + + if(adb_thread_create(&tid, RunLoopThread, NULL)) + 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 = 1; + } +} + +void usb_cleanup() +{ + DBG("usb_cleanup\n"); + close_usb_devices(); + if (currentRunLoop) + CFRunLoopStop(currentRunLoop); + + if (notificationIterators != NULL) { + free(notificationIterators); + notificationIterators = NULL; + } +} + +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 (0 == result) + return 0; + else { + DBG("ERR: usb_read failed with status %d\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_vendors.c b/adb/usb_vendors.c new file mode 100644 index 0000000..68bb232 --- /dev/null +++ b/adb/usb_vendors.c @@ -0,0 +1,310 @@ +/* + * 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. + */ + +#include "usb_vendors.h" + +#include + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include "windows.h" +# include "shlobj.h" +#else +# include +# include +#endif + +#include "sysdeps.h" +#include "adb.h" + +#define ANDROID_PATH ".android" +#define ANDROID_ADB_INI "adb_usb.ini" + +#define TRACE_TAG TRACE_USB + +// Google's USB Vendor ID +#define VENDOR_ID_GOOGLE 0x18d1 +// Intel's USB Vendor ID +#define VENDOR_ID_INTEL 0x8087 +// HTC's USB Vendor ID +#define VENDOR_ID_HTC 0x0bb4 +// Samsung's USB Vendor ID +#define VENDOR_ID_SAMSUNG 0x04e8 +// Motorola's USB Vendor ID +#define VENDOR_ID_MOTOROLA 0x22b8 +// LG's USB Vendor ID +#define VENDOR_ID_LGE 0x1004 +// Huawei's USB Vendor ID +#define VENDOR_ID_HUAWEI 0x12D1 +// Acer's USB Vendor ID +#define VENDOR_ID_ACER 0x0502 +// Sony Ericsson's USB Vendor ID +#define VENDOR_ID_SONY_ERICSSON 0x0FCE +// Foxconn's USB Vendor ID +#define VENDOR_ID_FOXCONN 0x0489 +// Dell's USB Vendor ID +#define VENDOR_ID_DELL 0x413c +// Nvidia's USB Vendor ID +#define VENDOR_ID_NVIDIA 0x0955 +// Garmin-Asus's USB Vendor ID +#define VENDOR_ID_GARMIN_ASUS 0x091E +// Sharp's USB Vendor ID +#define VENDOR_ID_SHARP 0x04dd +// ZTE's USB Vendor ID +#define VENDOR_ID_ZTE 0x19D2 +// Kyocera's USB Vendor ID +#define VENDOR_ID_KYOCERA 0x0482 +// Pantech's USB Vendor ID +#define VENDOR_ID_PANTECH 0x10A9 +// Qualcomm's USB Vendor ID +#define VENDOR_ID_QUALCOMM 0x05c6 +// On-The-Go-Video's USB Vendor ID +#define VENDOR_ID_OTGV 0x2257 +// NEC's USB Vendor ID +#define VENDOR_ID_NEC 0x0409 +// Panasonic Mobile Communication's USB Vendor ID +#define VENDOR_ID_PMC 0x04DA +// Toshiba's USB Vendor ID +#define VENDOR_ID_TOSHIBA 0x0930 +// SK Telesys's USB Vendor ID +#define VENDOR_ID_SK_TELESYS 0x1F53 +// KT Tech's USB Vendor ID +#define VENDOR_ID_KT_TECH 0x2116 +// Asus's USB Vendor ID +#define VENDOR_ID_ASUS 0x0b05 +// Philips's USB Vendor ID +#define VENDOR_ID_PHILIPS 0x0471 +// Texas Instruments's USB Vendor ID +#define VENDOR_ID_TI 0x0451 +// Funai's USB Vendor ID +#define VENDOR_ID_FUNAI 0x0F1C +// Gigabyte's USB Vendor ID +#define VENDOR_ID_GIGABYTE 0x0414 +// IRiver's USB Vendor ID +#define VENDOR_ID_IRIVER 0x2420 +// Compal's USB Vendor ID +#define VENDOR_ID_COMPAL 0x1219 +// T & A Mobile Phones' USB Vendor ID +#define VENDOR_ID_T_AND_A 0x1BBB +// LenovoMobile's USB Vendor ID +#define VENDOR_ID_LENOVOMOBILE 0x2006 +// Lenovo's USB Vendor ID +#define VENDOR_ID_LENOVO 0x17EF +// Vizio's USB Vendor ID +#define VENDOR_ID_VIZIO 0xE040 +// K-Touch's USB Vendor ID +#define VENDOR_ID_K_TOUCH 0x24E3 +// Pegatron's USB Vendor ID +#define VENDOR_ID_PEGATRON 0x1D4D +// Archos's USB Vendor ID +#define VENDOR_ID_ARCHOS 0x0E79 +// Positivo's USB Vendor ID +#define VENDOR_ID_POSITIVO 0x1662 +// Fujitsu's USB Vendor ID +#define VENDOR_ID_FUJITSU 0x04C5 +// Lumigon's USB Vendor ID +#define VENDOR_ID_LUMIGON 0x25E3 +// Quanta's USB Vendor ID +#define VENDOR_ID_QUANTA 0x0408 +// INQ Mobile's USB Vendor ID +#define VENDOR_ID_INQ_MOBILE 0x2314 +// Sony's USB Vendor ID +#define VENDOR_ID_SONY 0x054C +// Lab126's USB Vendor ID +#define VENDOR_ID_LAB126 0x1949 +// Yulong Coolpad's USB Vendor ID +#define VENDOR_ID_YULONG_COOLPAD 0x1EBF +// Kobo's USB Vendor ID +#define VENDOR_ID_KOBO 0x2237 +// Teleepoch's USB Vendor ID +#define VENDOR_ID_TELEEPOCH 0x2340 +// AnyDATA's USB Vendor ID +#define VENDOR_ID_ANYDATA 0x16D5 +// Harris's USB Vendor ID +#define VENDOR_ID_HARRIS 0x19A5 +// OPPO's USB Vendor ID +#define VENDOR_ID_OPPO 0x22D9 +// Xiaomi's USB Vendor ID +#define VENDOR_ID_XIAOMI 0x2717 +// BYD's USB Vendor ID +#define VENDOR_ID_BYD 0x19D1 +// OUYA's USB Vendor ID +#define VENDOR_ID_OUYA 0x2836 +// Haier's USB Vendor ID +#define VENDOR_ID_HAIER 0x201E +// Hisense's USB Vendor ID +#define VENDOR_ID_HISENSE 0x109b +// MTK's USB Vendor ID +#define VENDOR_ID_MTK 0x0e8d +// B&N Nook's USB Vendor ID +#define VENDOR_ID_NOOK 0x2080 +// Qisda's USB Vendor ID +#define VENDOR_ID_QISDA 0x1D45 +// ECS's USB Vendor ID +#define VENDOR_ID_ECS 0x03fc + + +/** built-in vendor list */ +int builtInVendorIds[] = { + VENDOR_ID_GOOGLE, + VENDOR_ID_INTEL, + VENDOR_ID_HTC, + VENDOR_ID_SAMSUNG, + VENDOR_ID_MOTOROLA, + VENDOR_ID_LGE, + VENDOR_ID_HUAWEI, + VENDOR_ID_ACER, + VENDOR_ID_SONY_ERICSSON, + VENDOR_ID_FOXCONN, + VENDOR_ID_DELL, + VENDOR_ID_NVIDIA, + VENDOR_ID_GARMIN_ASUS, + VENDOR_ID_SHARP, + VENDOR_ID_ZTE, + VENDOR_ID_KYOCERA, + VENDOR_ID_PANTECH, + VENDOR_ID_QUALCOMM, + VENDOR_ID_OTGV, + VENDOR_ID_NEC, + VENDOR_ID_PMC, + VENDOR_ID_TOSHIBA, + VENDOR_ID_SK_TELESYS, + VENDOR_ID_KT_TECH, + VENDOR_ID_ASUS, + VENDOR_ID_PHILIPS, + VENDOR_ID_TI, + VENDOR_ID_FUNAI, + VENDOR_ID_GIGABYTE, + VENDOR_ID_IRIVER, + VENDOR_ID_COMPAL, + VENDOR_ID_T_AND_A, + VENDOR_ID_LENOVOMOBILE, + VENDOR_ID_LENOVO, + VENDOR_ID_VIZIO, + VENDOR_ID_K_TOUCH, + VENDOR_ID_PEGATRON, + VENDOR_ID_ARCHOS, + VENDOR_ID_POSITIVO, + VENDOR_ID_FUJITSU, + VENDOR_ID_LUMIGON, + VENDOR_ID_QUANTA, + VENDOR_ID_INQ_MOBILE, + VENDOR_ID_SONY, + VENDOR_ID_LAB126, + VENDOR_ID_YULONG_COOLPAD, + VENDOR_ID_KOBO, + VENDOR_ID_TELEEPOCH, + VENDOR_ID_ANYDATA, + VENDOR_ID_HARRIS, + VENDOR_ID_OPPO, + VENDOR_ID_XIAOMI, + VENDOR_ID_BYD, + VENDOR_ID_OUYA, + VENDOR_ID_HAIER, + VENDOR_ID_HISENSE, + VENDOR_ID_MTK, + VENDOR_ID_NOOK, + VENDOR_ID_QISDA, + VENDOR_ID_ECS, +}; + +#define BUILT_IN_VENDOR_COUNT (sizeof(builtInVendorIds)/sizeof(builtInVendorIds[0])) + +/* max number of supported vendor ids (built-in + 3rd party). increase as needed */ +#define VENDOR_COUNT_MAX 128 + +int vendorIds[VENDOR_COUNT_MAX]; +unsigned vendorIdCount = 0; + +int get_adb_usb_ini(char* buff, size_t len); + +void usb_vendors_init(void) +{ + if (VENDOR_COUNT_MAX < BUILT_IN_VENDOR_COUNT) { + fprintf(stderr, "VENDOR_COUNT_MAX not big enough for built-in vendor list.\n"); + exit(2); + } + + /* add the built-in vendors at the beginning of the array */ + memcpy(vendorIds, builtInVendorIds, sizeof(builtInVendorIds)); + + /* default array size is the number of built-in vendors */ + vendorIdCount = BUILT_IN_VENDOR_COUNT; + + if (VENDOR_COUNT_MAX == BUILT_IN_VENDOR_COUNT) + return; + + char temp[PATH_MAX]; + if (get_adb_usb_ini(temp, sizeof(temp)) == 0) { + FILE * f = fopen(temp, "rt"); + + if (f != NULL) { + /* The vendor id file is pretty basic. 1 vendor id per line. + Lines starting with # are comments */ + while (fgets(temp, sizeof(temp), f) != NULL) { + if (temp[0] == '#') + continue; + + long value = strtol(temp, NULL, 0); + if (errno == EINVAL || errno == ERANGE || value > INT_MAX || value < 0) { + fprintf(stderr, "Invalid content in %s. Quitting.\n", ANDROID_ADB_INI); + exit(2); + } + + vendorIds[vendorIdCount++] = (int)value; + + /* make sure we don't go beyond the array */ + if (vendorIdCount == VENDOR_COUNT_MAX) { + break; + } + } + fclose(f); + } + } +} + +/* Utils methods */ + +/* builds the path to the adb vendor id file. returns 0 if success */ +int build_path(char* buff, size_t len, const char* format, const char* home) +{ + if (snprintf(buff, len, format, home, ANDROID_PATH, ANDROID_ADB_INI) >= (signed)len) { + return 1; + } + + return 0; +} + +/* fills buff with the path to the adb vendor id file. returns 0 if success */ +int get_adb_usb_ini(char* buff, size_t len) +{ +#ifdef _WIN32 + const char* home = getenv("ANDROID_SDK_HOME"); + if (home != NULL) { + return build_path(buff, len, "%s\\%s\\%s", home); + } else { + char path[MAX_PATH]; + SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, path); + return build_path(buff, len, "%s\\%s\\%s", path); + } +#else + const char* home = getenv("HOME"); + if (home == NULL) + home = "/tmp"; + + return build_path(buff, len, "%s/%s/%s", home); +#endif +} diff --git a/adb/usb_vendors.h b/adb/usb_vendors.h new file mode 100644 index 0000000..cee23a1 --- /dev/null +++ b/adb/usb_vendors.h @@ -0,0 +1,25 @@ +/* + * 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 __USB_VENDORS_H +#define __USB_VENDORS_H + +extern int vendorIds[]; +extern unsigned vendorIdCount; + +void usb_vendors_init(void); + +#endif diff --git a/adb/usb_windows.c b/adb/usb_windows.c new file mode 100644 index 0000000..4936b77 --- /dev/null +++ b/adb/usb_windows.c @@ -0,0 +1,515 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "sysdeps.h" + +#define TRACE_TAG TRACE_USB +#include "adb.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. +*/ +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(); + +/// 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(); + +/// Cleans up this module +void usb_cleanup(); + +/// 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); + +/// Gets interface (device) name for an opened usb handle +const char *usb_name(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; +} + +void usb_init() { + adb_thread_t tid; + + if(adb_thread_create(&tid, device_poll_thread, NULL)) { + fatal_errno("cannot create input thread"); + } +} + +void usb_cleanup() { +} + +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; + + // 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) { + free(ret); + errno = GetLastError(); + return NULL; + } + + // Open read pipe (endpoint) + ret->adb_read_pipe = + AdbOpenDefaultBulkReadEndpoint(ret->adb_interface, + AdbOpenAccessTypeReadWrite, + AdbOpenSharingModeReadWrite); + if (NULL != ret->adb_read_pipe) { + // Open write pipe (endpoint) + ret->adb_write_pipe = + AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface, + AdbOpenAccessTypeReadWrite, + AdbOpenSharingModeReadWrite); + if (NULL != ret->adb_write_pipe) { + // Save interface name + unsigned long name_len = 0; + + // First get expected name length + AdbGetInterfaceName(ret->adb_interface, + NULL, + &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); + } + } + } + } + + // Something went wrong. + int saved_errno = GetLastError(); + usb_cleanup_handle(ret); + free(ret); + SetLastError(saved_errno); + + return NULL; +} + +int usb_write(usb_handle* handle, const void* data, int len) { + unsigned long time_out = 5000; + unsigned long written = 0; + int ret; + + D("usb_write %d\n", len); + if (NULL != handle) { + // Perform write + ret = AdbWriteEndpointSync(handle->adb_write_pipe, + (void*)data, + (unsigned long)len, + &written, + time_out); + int saved_errno = GetLastError(); + + if (ret) { + // 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(handle->zero_mask && (len & handle->zero_mask) == 0) { + // Send a zero length packet + AdbWriteEndpointSync(handle->adb_write_pipe, + (void*)data, + 0, + &written, + time_out); + } + return 0; + } + } else { + // assume ERROR_INVALID_HANDLE indicates we are disconnected + if (saved_errno == ERROR_INVALID_HANDLE) + usb_kick(handle); + } + errno = saved_errno; + } else { + D("usb_write NULL handle\n"); + SetLastError(ERROR_INVALID_HANDLE); + } + + D("usb_write failed: %d\n", errno); + + return -1; +} + +int usb_read(usb_handle *handle, void* data, int len) { + unsigned long time_out = 0; + unsigned long read = 0; + int ret; + + D("usb_read %d\n", len); + if (NULL != handle) { + while (len > 0) { + int xfer = (len > 4096) ? 4096 : len; + + ret = AdbReadEndpointSync(handle->adb_read_pipe, + (void*)data, + (unsigned long)xfer, + &read, + time_out); + int saved_errno = GetLastError(); + D("usb_write got: %ld, expected: %d, errno: %d\n", read, xfer, saved_errno); + if (ret) { + data += read; + len -= read; + + if (len == 0) + return 0; + } else { + // assume ERROR_INVALID_HANDLE indicates we are disconnected + if (saved_errno == ERROR_INVALID_HANDLE) + usb_kick(handle); + break; + } + errno = saved_errno; + } + } else { + D("usb_read NULL handle\n"); + SetLastError(ERROR_INVALID_HANDLE); + } + + D("usb_read failed: %d\n", errno); + + return -1; +} + +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) + AdbCloseHandle(handle->adb_read_pipe); + if (NULL != handle->adb_interface) + AdbCloseHandle(handle->adb_interface); + + handle->interface_name = NULL; + handle->adb_write_pipe = NULL; + handle->adb_read_pipe = NULL; + handle->adb_interface = NULL; + } +} + +void usb_kick(usb_handle* handle) { + if (NULL != handle) { + adb_mutex_lock(&usb_lock); + + usb_cleanup_handle(handle); + + adb_mutex_unlock(&usb_lock); + } else { + SetLastError(ERROR_INVALID_HANDLE); + errno = ERROR_INVALID_HANDLE; + } +} + +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; +} + +const char *usb_name(usb_handle* handle) { + if (NULL == handle) { + SetLastError(ERROR_INVALID_HANDLE); + errno = ERROR_INVALID_HANDLE; + return NULL; + } + + return (const char*)handle->interface_name; +} + +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)) { + return 0; + } + + // Then check interface properties + USB_INTERFACE_DESCRIPTOR interf_desc; + + if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface, + &interf_desc)) { + 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; + } + } + + 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) + 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\n"); + usb_cleanup_handle(handle); + free(handle); + } + } else { + usb_cleanup_handle(handle); + free(handle); + } + } + } + + entry_buffer_size = sizeof(entry_buffer); + } + + AdbCloseHandle(enum_handle); +} diff --git a/include/android/api-level.h b/include/android/api-level.h new file mode 100644 index 0000000..611fdfb --- /dev/null +++ b/include/android/api-level.h @@ -0,0 +1,33 @@ +/* + * 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. + */ +#ifndef ANDROID_API_LEVEL_H +#define ANDROID_API_LEVEL_H + +#define __ANDROID_API__ 10 + +#endif /* ANDROID_API_LEVEL_H */ diff --git a/include/android/log.h b/include/android/log.h new file mode 100644 index 0000000..0ea4c29 --- /dev/null +++ b/include/android/log.h @@ -0,0 +1,128 @@ +/* + * 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 + +#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__) + __attribute__ ((format(printf, 3, 4))) +#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 SIGTRAP 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)) + __attribute__ ((format(printf, 3, 4))) +#endif + ; + +#ifdef __cplusplus +} +#endif + +#endif /* _ANDROID_LOG_H */ diff --git a/include/cutils/android_reboot.h b/include/cutils/android_reboot.h new file mode 100644 index 0000000..8c30e8e --- /dev/null +++ b/include/cutils/android_reboot.h @@ -0,0 +1,34 @@ +/* + * 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 __CUTILS_ANDROID_REBOOT_H__ +#define __CUTILS_ANDROID_REBOOT_H__ + +__BEGIN_DECLS + +/* Commands */ +#define ANDROID_RB_RESTART 0xDEAD0001 +#define ANDROID_RB_POWEROFF 0xDEAD0002 +#define ANDROID_RB_RESTART2 0xDEAD0003 + +/* Properties */ +#define ANDROID_RB_PROPERTY "sys.powerctl" + +int android_reboot(int cmd, int flags, char *arg); + +__END_DECLS + +#endif /* __CUTILS_ANDROID_REBOOT_H__ */ diff --git a/include/cutils/aref.h b/include/cutils/aref.h new file mode 100644 index 0000000..460ac02 --- /dev/null +++ b/include/cutils/aref.h @@ -0,0 +1,62 @@ +/* + * 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 +#include + +#ifdef ANDROID_SMP +#include +#else +#include +#endif + +__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 new file mode 100644 index 0000000..25b233e --- /dev/null +++ b/include/cutils/ashmem.h @@ -0,0 +1,45 @@ +/* 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 + +#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 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-arm.h b/include/cutils/atomic-arm.h new file mode 100644 index 0000000..172a0cd --- /dev/null +++ b/include/cutils/atomic-arm.h @@ -0,0 +1,176 @@ +/* + * 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_CUTILS_ATOMIC_ARM_H +#define ANDROID_CUTILS_ATOMIC_ARM_H + +#include + +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +extern ANDROID_ATOMIC_INLINE void android_compiler_barrier() +{ + __asm__ __volatile__ ("" : : : "memory"); +} + +extern ANDROID_ATOMIC_INLINE void android_memory_barrier() +{ +#if ANDROID_SMP == 0 + android_compiler_barrier(); +#else + __asm__ __volatile__ ("dmb" : : : "memory"); +#endif +} + +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier() +{ +#if ANDROID_SMP == 0 + android_compiler_barrier(); +#else + __asm__ __volatile__ ("dmb st" : : : "memory"); +#endif +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_acquire_load(volatile const int32_t *ptr) +{ + int32_t value = *ptr; + android_memory_barrier(); + return value; +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_release_load(volatile const int32_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_acquire_store(int32_t value, volatile int32_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern ANDROID_ATOMIC_INLINE +void android_atomic_release_store(int32_t value, volatile int32_t *ptr) +{ + android_memory_barrier(); + *ptr = value; +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + int32_t prev, status; + do { + __asm__ __volatile__ ("ldrex %0, [%3]\n" + "mov %1, #0\n" + "teq %0, %4\n" +#ifdef __thumb2__ + "it eq\n" +#endif + "strexeq %1, %5, [%3]" + : "=&r" (prev), "=&r" (status), "+m"(*ptr) + : "r" (ptr), "Ir" (old_value), "r" (new_value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev != old_value; +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_acquire_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + int status = android_atomic_cas(old_value, new_value, ptr); + android_memory_barrier(); + return status; +} + +extern ANDROID_ATOMIC_INLINE +int android_atomic_release_cas(int32_t old_value, int32_t new_value, + volatile int32_t *ptr) +{ + android_memory_barrier(); + return android_atomic_cas(old_value, new_value, ptr); +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_add(int32_t increment, volatile int32_t *ptr) +{ + int32_t prev, tmp, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ("ldrex %0, [%4]\n" + "add %1, %0, %5\n" + "strex %2, %1, [%4]" + : "=&r" (prev), "=&r" (tmp), + "=&r" (status), "+m" (*ptr) + : "r" (ptr), "Ir" (increment) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE int32_t android_atomic_inc(volatile int32_t *addr) +{ + return android_atomic_add(1, addr); +} + +extern ANDROID_ATOMIC_INLINE int32_t android_atomic_dec(volatile int32_t *addr) +{ + return android_atomic_add(-1, addr); +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_and(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, tmp, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ("ldrex %0, [%4]\n" + "and %1, %0, %5\n" + "strex %2, %1, [%4]" + : "=&r" (prev), "=&r" (tmp), + "=&r" (status), "+m" (*ptr) + : "r" (ptr), "Ir" (value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE +int32_t android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, tmp, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ("ldrex %0, [%4]\n" + "orr %1, %0, %5\n" + "strex %2, %1, [%4]" + : "=&r" (prev), "=&r" (tmp), + "=&r" (status), "+m" (*ptr) + : "r" (ptr), "Ir" (value) + : "cc"); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +#endif /* ANDROID_CUTILS_ATOMIC_ARM_H */ diff --git a/include/cutils/atomic-inline.h b/include/cutils/atomic-inline.h new file mode 100644 index 0000000..0b13138 --- /dev/null +++ b/include/cutils/atomic-inline.h @@ -0,0 +1,72 @@ +/* + * 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_CUTILS_ATOMIC_INLINE_H +#define ANDROID_CUTILS_ATOMIC_INLINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Inline declarations and macros for some special-purpose atomic + * operations. These are intended for rare circumstances where a + * memory barrier needs to be issued inline rather than as a function + * call. + * + * Most code should not use these. + * + * Anything that does include this file must set ANDROID_SMP to either + * 0 or 1, indicating compilation for UP or SMP, respectively. + * + * Macros defined in this header: + * + * void ANDROID_MEMBAR_FULL(void) + * Full memory barrier. Provides a compiler reordering barrier, and + * on SMP systems emits an appropriate instruction. + */ + +#if !defined(ANDROID_SMP) +# error "Must define ANDROID_SMP before including atomic-inline.h" +#endif + +#if defined(__arm__) +#include +#elif defined(__i386__) || defined(__x86_64__) +#include +#elif defined(__mips__) +#include +#else +#error atomic operations are unsupported +#endif + +#if ANDROID_SMP == 0 +#define ANDROID_MEMBAR_FULL android_compiler_barrier +#else +#define ANDROID_MEMBAR_FULL android_memory_barrier +#endif + +#if ANDROID_SMP == 0 +#define ANDROID_MEMBAR_STORE android_compiler_barrier +#else +#define ANDROID_MEMBAR_STORE android_memory_store_barrier +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ANDROID_CUTILS_ATOMIC_INLINE_H */ diff --git a/include/cutils/atomic-mips.h b/include/cutils/atomic-mips.h new file mode 100644 index 0000000..f9d3e25 --- /dev/null +++ b/include/cutils/atomic-mips.h @@ -0,0 +1,199 @@ +/* + * 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_CUTILS_ATOMIC_MIPS_H +#define ANDROID_CUTILS_ATOMIC_MIPS_H + +#include + +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +extern ANDROID_ATOMIC_INLINE void android_compiler_barrier(void) +{ + __asm__ __volatile__ ("" : : : "memory"); +} + +#if ANDROID_SMP == 0 +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) +{ + android_compiler_barrier(); +} +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) +{ + android_compiler_barrier(); +} +#else +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) +{ + __asm__ __volatile__ ("sync" : : : "memory"); +} +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) +{ + __asm__ __volatile__ ("sync" : : : "memory"); +} +#endif + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_acquire_load(volatile const int32_t *ptr) +{ + int32_t value = *ptr; + android_memory_barrier(); + return value; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_release_load(volatile const int32_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern ANDROID_ATOMIC_INLINE void +android_atomic_acquire_store(int32_t value, volatile int32_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern ANDROID_ATOMIC_INLINE void +android_atomic_release_store(int32_t value, volatile int32_t *ptr) +{ + android_memory_barrier(); + *ptr = value; +} + +extern ANDROID_ATOMIC_INLINE int +android_atomic_cas(int32_t old_value, int32_t new_value, volatile int32_t *ptr) +{ + int32_t prev, status; + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " li %[status], 1\n" + " bne %[prev], %[old], 9f\n" + " move %[status], %[new_value]\n" + " sc %[status], (%[ptr])\n" + "9:\n" + : [prev] "=&r" (prev), [status] "=&r" (status) + : [ptr] "r" (ptr), [old] "r" (old_value), [new_value] "r" (new_value) + ); + } while (__builtin_expect(status == 0, 0)); + return prev != old_value; +} + +extern ANDROID_ATOMIC_INLINE int +android_atomic_acquire_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + int status = android_atomic_cas(old_value, new_value, ptr); + android_memory_barrier(); + return status; +} + +extern ANDROID_ATOMIC_INLINE int +android_atomic_release_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + android_memory_barrier(); + return android_atomic_cas(old_value, new_value, ptr); +} + + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_swap(int32_t new_value, volatile int32_t *ptr) +{ + int32_t prev, status; + do { + __asm__ __volatile__ ( + " move %[status], %[new_value]\n" + " ll %[prev], (%[ptr])\n" + " sc %[status], (%[ptr])\n" + : [prev] "=&r" (prev), [status] "=&r" (status) + : [ptr] "r" (ptr), [new_value] "r" (new_value) + ); + } while (__builtin_expect(status == 0, 0)); + android_memory_barrier(); + return prev; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_add(int32_t increment, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " addu %[status], %[prev], %[inc]\n" + " sc %[status], (%[ptr])\n" + : [status] "=&r" (status), [prev] "=&r" (prev) + : [ptr] "r" (ptr), [inc] "Ir" (increment) + ); + } while (__builtin_expect(status == 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_inc(volatile int32_t *addr) +{ + return android_atomic_add(1, addr); +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_dec(volatile int32_t *addr) +{ + return android_atomic_add(-1, addr); +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_and(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " and %[status], %[prev], %[value]\n" + " sc %[status], (%[ptr])\n" + : [prev] "=&r" (prev), [status] "=&r" (status) + : [ptr] "r" (ptr), [value] "Ir" (value) + ); + } while (__builtin_expect(status == 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + android_memory_barrier(); + do { + __asm__ __volatile__ ( + " ll %[prev], (%[ptr])\n" + " or %[status], %[prev], %[value]\n" + " sc %[status], (%[ptr])\n" + : [prev] "=&r" (prev), [status] "=&r" (status) + : [ptr] "r" (ptr), [value] "Ir" (value) + ); + } while (__builtin_expect(status == 0, 0)); + return prev; +} + +#endif /* ANDROID_CUTILS_ATOMIC_MIPS_H */ diff --git a/include/cutils/atomic-x86.h b/include/cutils/atomic-x86.h new file mode 100644 index 0000000..9480f57 --- /dev/null +++ b/include/cutils/atomic-x86.h @@ -0,0 +1,153 @@ +/* + * 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_CUTILS_ATOMIC_X86_H +#define ANDROID_CUTILS_ATOMIC_X86_H + +#include + +#ifndef ANDROID_ATOMIC_INLINE +#define ANDROID_ATOMIC_INLINE inline __attribute__((always_inline)) +#endif + +extern ANDROID_ATOMIC_INLINE void android_compiler_barrier(void) +{ + __asm__ __volatile__ ("" : : : "memory"); +} + +#if ANDROID_SMP == 0 +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) +{ + android_compiler_barrier(); +} +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) +{ + android_compiler_barrier(); +} +#else +extern ANDROID_ATOMIC_INLINE void android_memory_barrier(void) +{ + __asm__ __volatile__ ("mfence" : : : "memory"); +} +extern ANDROID_ATOMIC_INLINE void android_memory_store_barrier(void) +{ + android_compiler_barrier(); +} +#endif + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_acquire_load(volatile const int32_t *ptr) +{ + int32_t value = *ptr; + android_compiler_barrier(); + return value; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_release_load(volatile const int32_t *ptr) +{ + android_memory_barrier(); + return *ptr; +} + +extern ANDROID_ATOMIC_INLINE void +android_atomic_acquire_store(int32_t value, volatile int32_t *ptr) +{ + *ptr = value; + android_memory_barrier(); +} + +extern ANDROID_ATOMIC_INLINE void +android_atomic_release_store(int32_t value, volatile int32_t *ptr) +{ + android_compiler_barrier(); + *ptr = value; +} + +extern ANDROID_ATOMIC_INLINE int +android_atomic_cas(int32_t old_value, int32_t new_value, volatile int32_t *ptr) +{ + int32_t prev; + __asm__ __volatile__ ("lock; cmpxchgl %1, %2" + : "=a" (prev) + : "q" (new_value), "m" (*ptr), "0" (old_value) + : "memory"); + return prev != old_value; +} + +extern ANDROID_ATOMIC_INLINE int +android_atomic_acquire_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + /* Loads are not reordered with other loads. */ + return android_atomic_cas(old_value, new_value, ptr); +} + +extern ANDROID_ATOMIC_INLINE int +android_atomic_release_cas(int32_t old_value, + int32_t new_value, + volatile int32_t *ptr) +{ + /* Stores are not reordered with other stores. */ + return android_atomic_cas(old_value, new_value, ptr); +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_add(int32_t increment, volatile int32_t *ptr) +{ + __asm__ __volatile__ ("lock; xaddl %0, %1" + : "+r" (increment), "+m" (*ptr) + : : "memory"); + /* increment now holds the old value of *ptr */ + return increment; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_inc(volatile int32_t *addr) +{ + return android_atomic_add(1, addr); +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_dec(volatile int32_t *addr) +{ + return android_atomic_add(-1, addr); +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_and(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + do { + prev = *ptr; + status = android_atomic_cas(prev, prev & value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +extern ANDROID_ATOMIC_INLINE int32_t +android_atomic_or(int32_t value, volatile int32_t *ptr) +{ + int32_t prev, status; + do { + prev = *ptr; + status = android_atomic_cas(prev, prev | value, ptr); + } while (__builtin_expect(status != 0, 0)); + return prev; +} + +#endif /* ANDROID_CUTILS_ATOMIC_X86_H */ diff --git a/include/cutils/atomic.h b/include/cutils/atomic.h new file mode 100644 index 0000000..ae42eb8 --- /dev/null +++ b/include/cutils/atomic.h @@ -0,0 +1,121 @@ +/* + * 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 +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * A handful of basic atomic operations. The appropriate pthread + * functions should be used instead of these whenever possible. + * + * 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. + */ +int32_t android_atomic_inc(volatile int32_t* addr); +int32_t android_atomic_dec(volatile int32_t* addr); +int32_t android_atomic_add(int32_t value, volatile int32_t* addr); +int32_t android_atomic_and(int32_t value, volatile int32_t* addr); +int32_t android_atomic_or(int32_t value, volatile int32_t* addr); + +/* + * Perform an atomic load with "acquire" or "release" ordering. + * + * This is only necessary if you need the memory barrier. A 32-bit read + * from a 32-bit aligned address is atomic on all supported platforms. + */ +int32_t android_atomic_acquire_load(volatile const int32_t* addr); +int32_t android_atomic_release_load(volatile const int32_t* addr); + +/* + * Perform an atomic store with "acquire" or "release" ordering. + * + * This is only necessary if you need the memory barrier. A 32-bit write + * to a 32-bit aligned address is atomic on all supported platforms. + */ +void android_atomic_acquire_store(int32_t value, volatile int32_t* addr); +void android_atomic_release_store(int32_t value, volatile int32_t* addr); + +/* + * 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. + */ +int android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr); +int android_atomic_release_cas(int32_t oldvalue, int32_t newvalue, + volatile int32_t* addr); + +/* + * 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 + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_CUTILS_ATOMIC_H diff --git a/include/cutils/bitops.h b/include/cutils/bitops.h new file mode 100644 index 0000000..c26dc54 --- /dev/null +++ b/include/cutils/bitops.h @@ -0,0 +1,120 @@ +/* + * 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 __CUTILS_BITOPS_H +#define __CUTILS_BITOPS_H + +#include +#include +#include +#include + +__BEGIN_DECLS + +/* + * Bitmask Operations + * + * Note this doesn't provide any locking/exclusion, and isn't atomic. + * Additionally no bounds checking is done on the bitmask array. + * + * Example: + * + * int num_resources; + * unsigned int resource_bits[BITS_TO_WORDS(num_resources)]; + * bitmask_init(resource_bits, num_resources); + * ... + * int bit = bitmask_ffz(resource_bits, num_resources); + * bitmask_set(resource_bits, bit); + * ... + * if (bitmask_test(resource_bits, bit)) { ... } + * ... + * bitmask_clear(resource_bits, bit); + * + */ + +#define BITS_PER_WORD (sizeof(unsigned int) * 8) +#define BITS_TO_WORDS(x) (((x) + BITS_PER_WORD - 1) / BITS_PER_WORD) +#define BIT_IN_WORD(x) ((x) % BITS_PER_WORD) +#define BIT_WORD(x) ((x) / BITS_PER_WORD) +#define BIT_MASK(x) (1 << BIT_IN_WORD(x)) + +static inline void bitmask_init(unsigned int *bitmask, int num_bits) +{ + memset(bitmask, 0, BITS_TO_WORDS(num_bits)*sizeof(unsigned int)); +} + +static inline int bitmask_ffz(unsigned int *bitmask, int num_bits) +{ + int bit, result; + unsigned int i; + + for (i = 0; i < BITS_TO_WORDS(num_bits); i++) { + bit = ffs(~bitmask[i]); + if (bit) { + // ffs is 1-indexed, return 0-indexed result + bit--; + result = BITS_PER_WORD * i + bit; + if (result >= num_bits) + return -1; + return result; + } + } + return -1; +} + +static inline int bitmask_weight(unsigned int *bitmask, int num_bits) +{ + int i; + int weight = 0; + + for (i = 0; i < BITS_TO_WORDS(num_bits); i++) + weight += __builtin_popcount(bitmask[i]); + return weight; +} + +static inline void bitmask_set(unsigned int *bitmask, int bit) +{ + bitmask[BIT_WORD(bit)] |= BIT_MASK(bit); +} + +static inline void bitmask_clear(unsigned int *bitmask, int bit) +{ + bitmask[BIT_WORD(bit)] &= ~BIT_MASK(bit); +} + +static inline bool bitmask_test(unsigned int *bitmask, int bit) +{ + return bitmask[BIT_WORD(bit)] & BIT_MASK(bit); +} + +static inline int popcount(unsigned int x) +{ + return __builtin_popcount(x); +} + +static inline int popcountl(unsigned long x) +{ + return __builtin_popcountl(x); +} + +static inline int popcountll(unsigned long long x) +{ + return __builtin_popcountll(x); +} + +__END_DECLS + +#endif /* __CUTILS_BITOPS_H */ diff --git a/include/cutils/compiler.h b/include/cutils/compiler.h new file mode 100644 index 0000000..70f884a --- /dev/null +++ b/include/cutils/compiler.h @@ -0,0 +1,44 @@ +/* + * 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_COMPILER_H +#define ANDROID_CUTILS_COMPILER_H + +/* + * helps the compiler's optimizer predicting branches + */ + +#ifdef __cplusplus +# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), true )) +# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), false )) +#else +# define CC_LIKELY( exp ) (__builtin_expect( !!(exp), 1 )) +# define CC_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 )) +#endif + +/** + * exports marked symbols + * + * if used on a C++ class declaration, this macro must be inserted + * after the "class" keyword. For instance: + * + * template + * class ANDROID_API Singleton { } + */ + +#define ANDROID_API __attribute__((visibility("default"))) + +#endif // ANDROID_CUTILS_COMPILER_H diff --git a/include/cutils/config_utils.h b/include/cutils/config_utils.h new file mode 100644 index 0000000..2dea6f1 --- /dev/null +++ b/include/cutils/config_utils.h @@ -0,0 +1,64 @@ +/* + * 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_CONFIG_UTILS_H +#define __CUTILS_CONFIG_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct cnode cnode; + + +struct cnode +{ + cnode *next; + cnode *first_child; + cnode *last_child; + const char *name; + const char *value; +}; + +/* parse a text string into a config node tree */ +void config_load(cnode *root, char *data); + +/* parse a file into a config node tree */ +void config_load_file(cnode *root, const char *fn); + +/* create a single config node */ +cnode* config_node(const char *name, const char *value); + +/* locate a named child of a config node */ +cnode* config_find(cnode *root, const char *name); + +/* look up a child by name and return the boolean value */ +int config_bool(cnode *root, const char *name, int _default); + +/* look up a child by name and return the string value */ +const char* config_str(cnode *root, const char *name, const char *_default); + +/* add a named child to a config node (or modify it if it already exists) */ +void config_set(cnode *root, const char *name, const char *value); + +/* free a config node tree */ +void config_free(cnode *root); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/cutils/cpu_info.h b/include/cutils/cpu_info.h new file mode 100644 index 0000000..78c1884 --- /dev/null +++ b/include/cutils/cpu_info.h @@ -0,0 +1,34 @@ +/* + * 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_CPU_INFO_H +#define __CUTILS_CPU_INFO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* returns a string contiaining an ASCII representation of the CPU serial number, +** or NULL if cpu info not available. +** The string is a static variable, so don't call free() on it. +*/ +extern const char* get_cpu_serial_number(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_CPU_INFO_H */ diff --git a/include/cutils/debugger.h b/include/cutils/debugger.h new file mode 100644 index 0000000..4eda523 --- /dev/null +++ b/include/cutils/debugger.h @@ -0,0 +1,58 @@ +/* + * 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 + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEBUGGER_SOCKET_NAME "android:debuggerd" + +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; + +typedef struct { + debugger_action_t action; + pid_t tid; + uintptr_t abort_msg_address; +} 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 only to the specified file (requires root). + * Returns 0 on success, -1 on error. + */ +int dump_backtrace_to_file(pid_t tid, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_DEBUGGER_H */ diff --git a/include/cutils/dir_hash.h b/include/cutils/dir_hash.h new file mode 100644 index 0000000..fbb4d02 --- /dev/null +++ b/include/cutils/dir_hash.h @@ -0,0 +1,26 @@ +/* + * 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. + */ + +typedef enum { + SHA_1, +} HashAlgorithm; + +int get_file_hash(HashAlgorithm algorithm, const char *path, + char *output_string, size_t max_output_string); + +int get_recursive_hash_manifest(HashAlgorithm algorithm, + const char *directory_path, + char **output_string); diff --git a/include/cutils/fs.h b/include/cutils/fs.h new file mode 100644 index 0000000..d1d4cf2 --- /dev/null +++ b/include/cutils/fs.h @@ -0,0 +1,70 @@ +/* + * 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 + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * . (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/hashmap.h b/include/cutils/hashmap.h new file mode 100644 index 0000000..5cb344c --- /dev/null +++ b/include/cutils/hashmap.h @@ -0,0 +1,150 @@ +/* + * 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. + */ + +/** + * Hash map. + */ + +#ifndef __HASHMAP_H +#define __HASHMAP_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** A hash map. */ +typedef struct Hashmap Hashmap; + +/** + * Creates a new hash map. Returns NULL if memory allocation fails. + * + * @param initialCapacity number of expected entries + * @param hash function which hashes keys + * @param equals function which compares keys for equality + */ +Hashmap* hashmapCreate(size_t initialCapacity, + int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)); + +/** + * Frees the hash map. Does not free the keys or values themselves. + */ +void hashmapFree(Hashmap* map); + +/** + * Hashes the memory pointed to by key with the given size. Useful for + * implementing hash functions. + */ +int hashmapHash(void* key, size_t keySize); + +/** + * Puts value for the given key in the map. Returns pre-existing value if + * any. + * + * If memory allocation fails, this function returns NULL, the map's size + * does not increase, and errno is set to ENOMEM. + */ +void* hashmapPut(Hashmap* map, void* key, void* value); + +/** + * Gets a value from the map. Returns NULL if no entry for the given key is + * found or if the value itself is NULL. + */ +void* hashmapGet(Hashmap* map, void* key); + +/** + * Returns true if the map contains an entry for the given key. + */ +bool hashmapContainsKey(Hashmap* map, void* key); + +/** + * Gets the value for a key. If a value is not found, this function gets a + * value and creates an entry using the given callback. + * + * If memory allocation fails, the callback is not called, this function + * returns NULL, and errno is set to ENOMEM. + */ +void* hashmapMemoize(Hashmap* map, void* key, + void* (*initialValue)(void* key, void* context), void* context); + +/** + * Removes an entry from the map. Returns the removed value or NULL if no + * entry was present. + */ +void* hashmapRemove(Hashmap* map, void* key); + +/** + * Gets the number of entries in this map. + */ +size_t hashmapSize(Hashmap* map); + +/** + * Invokes the given callback on each entry in the map. Stops iterating if + * the callback returns false. + */ +void hashmapForEach(Hashmap* map, + bool (*callback)(void* key, void* value, void* context), + void* context); + +/** + * Concurrency support. + */ + +/** + * Locks the hash map so only the current thread can access it. + */ +void hashmapLock(Hashmap* map); + +/** + * Unlocks the hash map so other threads can access it. + */ +void hashmapUnlock(Hashmap* map); + +/** + * Key utilities. + */ + +/** + * Hashes int keys. 'key' is a pointer to int. + */ +int hashmapIntHash(void* key); + +/** + * Compares two int keys for equality. + */ +bool hashmapIntEquals(void* keyA, void* keyB); + +/** + * For debugging. + */ + +/** + * Gets current capacity. + */ +size_t hashmapCurrentCapacity(Hashmap* map); + +/** + * Counts the number of entry collisions. + */ +size_t hashmapCountCollisions(Hashmap* map); + +#ifdef __cplusplus +} +#endif + +#endif /* __HASHMAP_H */ diff --git a/include/cutils/iosched_policy.h b/include/cutils/iosched_policy.h new file mode 100644 index 0000000..07c5d1f --- /dev/null +++ b/include/cutils/iosched_policy.h @@ -0,0 +1,38 @@ +/* + * 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_IOSCHED_POLICY_H +#define __CUTILS_IOSCHED_POLICY_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + IoSchedClass_NONE, + IoSchedClass_RT, + IoSchedClass_BE, + IoSchedClass_IDLE, +} IoSchedClass; + +extern int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio); +extern int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_IOSCHED_POLICY_H */ diff --git a/include/cutils/jstring.h b/include/cutils/jstring.h new file mode 100644 index 0000000..ee0018f --- /dev/null +++ b/include/cutils/jstring.h @@ -0,0 +1,43 @@ +/* + * 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_STRING16_H +#define __CUTILS_STRING16_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint16_t char16_t; + +extern char * strndup16to8 (const char16_t* s, size_t n); +extern size_t strnlen16to8 (const char16_t* s, size_t n); +extern char * strncpy16to8 (char *dest, const char16_t*s, size_t n); + +extern char16_t * strdup8to16 (const char* s, size_t *out_len); +extern size_t strlen8to16 (const char* utf8Str); +extern char16_t * strcpy8to16 (char16_t *dest, const char*s, size_t *out_len); +extern char16_t * strcpylen8to16 (char16_t *dest, const char*s, int length, + size_t *out_len); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_STRING16_H */ diff --git a/include/cutils/klog.h b/include/cutils/klog.h new file mode 100644 index 0000000..ba728ac --- /dev/null +++ b/include/cutils/klog.h @@ -0,0 +1,40 @@ +/* + * 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 + +__BEGIN_DECLS + +void klog_init(void); +void klog_set_level(int level); +void klog_close(void); +void klog_write(int level, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); + +__END_DECLS + +#define KLOG_ERROR(tag,x...) klog_write(3, "<3>" tag ": " x) +#define KLOG_WARNING(tag,x...) klog_write(4, "<4>" tag ": " x) +#define KLOG_NOTICE(tag,x...) klog_write(5, "<5>" tag ": " x) +#define KLOG_INFO(tag,x...) klog_write(6, "<6>" tag ": " x) +#define KLOG_DEBUG(tag,x...) klog_write(7, "<7>" tag ": " x) + +#define KLOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */ + +#endif diff --git a/include/cutils/list.h b/include/cutils/list.h new file mode 100644 index 0000000..3881fc9 --- /dev/null +++ b/include/cutils/list.h @@ -0,0 +1,59 @@ +/* + * 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_LIST_H_ +#define _CUTILS_LIST_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct listnode +{ + struct listnode *next; + struct listnode *prev; +}; + +#define node_to_item(node, container, member) \ + (container *) (((char*) (node)) - offsetof(container, member)) + +#define list_declare(name) \ + struct listnode name = { \ + .next = &name, \ + .prev = &name, \ + } + +#define list_for_each(node, list) \ + for (node = (list)->next; node != (list); node = node->next) + +#define list_for_each_reverse(node, list) \ + for (node = (list)->prev; node != (list); node = node->prev) + +void list_init(struct listnode *list); +void list_add_tail(struct listnode *list, struct listnode *item); +void list_remove(struct listnode *item); + +#define list_empty(list) ((list) == (list)->next) +#define list_head(list) ((list)->next) +#define list_tail(list) ((list)->prev) + +#ifdef __cplusplus +}; +#endif /* __cplusplus */ + +#endif diff --git a/include/cutils/log.h b/include/cutils/log.h new file mode 100644 index 0000000..0e0248e --- /dev/null +++ b/include/cutils/log.h @@ -0,0 +1 @@ +#include diff --git a/include/cutils/memory.h b/include/cutils/memory.h new file mode 100644 index 0000000..e725cdd --- /dev/null +++ b/include/cutils/memory.h @@ -0,0 +1,42 @@ +/* + * 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 ANDROID_CUTILS_MEMORY_H +#define ANDROID_CUTILS_MEMORY_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* size is given in bytes and must be multiple of 2 */ +void android_memset16(uint16_t* dst, uint16_t value, size_t size); + +/* size is given in bytes and must be multiple of 4 */ +void android_memset32(uint32_t* dst, uint32_t value, size_t size); + +#if !HAVE_STRLCPY +/* Declaration of strlcpy() for platforms that don't already have it. */ +size_t strlcpy(char *dst, const char *src, size_t size); +#endif + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ANDROID_CUTILS_MEMORY_H diff --git a/include/cutils/misc.h b/include/cutils/misc.h new file mode 100644 index 0000000..2c48dfa --- /dev/null +++ b/include/cutils/misc.h @@ -0,0 +1,48 @@ +/* + * 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_MISC_H +#define __CUTILS_MISC_H + +#ifdef __cplusplus +extern "C" { +#endif + + /* Load an entire file into a malloc'd chunk of memory + * that is length_of_file + 1 (null terminator). If + * sz is non-zero, return the size of the file via sz. + * Returns 0 on failure. + */ +extern void *load_file(const char *fn, unsigned *sz); + + /* Connects your process to the system debugger daemon + * so that on a crash it may be logged or interactively + * debugged (depending on system settings). + */ +extern void debuggerd_connect(void); + + + /* This is the range of UIDs (and GIDs) that are reserved + * for assigning to applications. + */ +#define FIRST_APPLICATION_UID 10000 +#define LAST_APPLICATION_UID 99999 + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_MISC_H */ diff --git a/include/cutils/multiuser.h b/include/cutils/multiuser.h new file mode 100644 index 0000000..635ddb1 --- /dev/null +++ b/include/cutils/multiuser.h @@ -0,0 +1,41 @@ +/* + * 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 + +#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 new file mode 100644 index 0000000..268c5d3 --- /dev/null +++ b/include/cutils/native_handle.h @@ -0,0 +1,69 @@ +/* + * 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/open_memstream.h b/include/cutils/open_memstream.h new file mode 100644 index 0000000..b7998be --- /dev/null +++ b/include/cutils/open_memstream.h @@ -0,0 +1,36 @@ +/* + * 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_OPEN_MEMSTREAM_H__ +#define __CUTILS_OPEN_MEMSTREAM_H__ + +#include + +#ifndef HAVE_OPEN_MEMSTREAM + +#ifdef __cplusplus +extern "C" { +#endif + +FILE* open_memstream(char** bufp, size_t* sizep); + +#ifdef __cplusplus +} +#endif + +#endif /*!HAVE_OPEN_MEMSTREAM*/ + +#endif /*__CUTILS_OPEN_MEMSTREAM_H__*/ diff --git a/include/cutils/partition_utils.h b/include/cutils/partition_utils.h new file mode 100644 index 0000000..597df92 --- /dev/null +++ b/include/cutils/partition_utils.h @@ -0,0 +1,27 @@ +/* + * 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 __CUTILS_PARTITION_WIPED_H__ +#define __CUTILS_PARTITION_WIPED_H__ + +__BEGIN_DECLS + +int partition_wiped(char *source); +void erase_footer(const char *dev_path, long long size); + +__END_DECLS + +#endif /* __CUTILS_PARTITION_WIPED_H__ */ diff --git a/include/cutils/process_name.h b/include/cutils/process_name.h new file mode 100644 index 0000000..1e72e5c --- /dev/null +++ b/include/cutils/process_name.h @@ -0,0 +1,42 @@ +/* + * 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 new file mode 100644 index 0000000..2c70165 --- /dev/null +++ b/include/cutils/properties.h @@ -0,0 +1,90 @@ +/* + * 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 +#include +#include + +#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_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 HAVE_SYSTEM_PROPERTY_SERVER +/* + * We have an external property server instead of built-in libc support. + * Used by the simulator. + */ +#define SYSTEM_PROPERTY_PIPE_NAME "/tmp/android-sysprop" + +enum { + kSystemPropertyUnknown = 0, + kSystemPropertyGet, + kSystemPropertySet, + kSystemPropertyList +}; +#endif /*HAVE_SYSTEM_PROPERTY_SERVER*/ + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/cutils/qtaguid.h b/include/cutils/qtaguid.h new file mode 100644 index 0000000..f8550fd --- /dev/null +++ b/include/cutils/qtaguid.h @@ -0,0 +1,66 @@ +/* + * 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 __CUTILS_QTAGUID_H +#define __CUTILS_QTAGUID_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Set tags (and owning UIDs) for network sockets. +*/ +extern int qtaguid_tagSocket(int sockfd, int tag, uid_t uid); + +/* + * Untag a network socket before closing. +*/ +extern int qtaguid_untagSocket(int sockfd); + +/* + * For the given uid, switch counter sets. + * The kernel only keeps a limited number of sets. + * 2 for now. + */ +extern int qtaguid_setCounterSet(int counterSetNum, uid_t uid); + +/* + * Delete all tag info that relates to the given tag an uid. + * If the tag is 0, then ALL info about the uid is freeded. + * The delete data also affects active tagged socketd, which are + * then untagged. + * The calling process can only operate on its own tags. + * Unless it is part of the happy AID_NET_BW_ACCT group. + * In which case it can clobber everything. + */ +extern int qtaguid_deleteTagData(int tag, uid_t uid); + +/* + * Enable/disable qtaguid functionnality at a lower level. + * When pacified, the kernel will accept commands but do nothing. + */ +extern int qtaguid_setPacifier(int on); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_QTAG_UID_H */ diff --git a/include/cutils/record_stream.h b/include/cutils/record_stream.h new file mode 100644 index 0000000..bfac87a --- /dev/null +++ b/include/cutils/record_stream.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +/* + * A simple utility for reading fixed records out of a stream fd + */ + +#ifndef _CUTILS_RECORD_STREAM_H +#define _CUTILS_RECORD_STREAM_H + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct RecordStream RecordStream; + +extern RecordStream *record_stream_new(int fd, size_t maxRecordLen); +extern void record_stream_free(RecordStream *p_rs); + +extern int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, + size_t *p_outRecordLen); + +#ifdef __cplusplus +} +#endif + + +#endif /*_CUTILS_RECORD_STREAM_H*/ + diff --git a/include/cutils/sched_policy.h b/include/cutils/sched_policy.h new file mode 100644 index 0000000..ba84ce3 --- /dev/null +++ b/include/cutils/sched_policy.h @@ -0,0 +1,61 @@ +/* + * 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 new file mode 100644 index 0000000..19cae0c --- /dev/null +++ b/include/cutils/sockets.h @@ -0,0 +1,112 @@ +/* + * 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 +#include +#include +#include + +#ifdef HAVE_WINSOCK +#include +typedef int socklen_t; +#elif HAVE_SYS_SOCKET_H +#include +#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] = ANDROID_SOCKET_ENV_PREFIX; + const char *val; + int fd; + + /* build our environment variable, counting cycles like a wolf ... */ +#if HAVE_STRLCPY + strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1, + name, + sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX)); +#else /* for the host, which may lack the almightly strncpy ... */ + strncpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1, + name, + sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX)); + key[sizeof(key)-1] = '\0'; +#endif + + val = getenv(key); + if (!val) + return -1; + + errno = 0; + 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_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/str_parms.h b/include/cutils/str_parms.h new file mode 100644 index 0000000..247c996 --- /dev/null +++ b/include/cutils/str_parms.h @@ -0,0 +1,49 @@ +/* + * 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 __CUTILS_STR_PARMS_H +#define __CUTILS_STR_PARMS_H + +#include + +struct str_parms; + +struct str_parms *str_parms_create(void); +struct str_parms *str_parms_create_str(const char *_string); +void str_parms_destroy(struct str_parms *str_parms); + +void str_parms_del(struct str_parms *str_parms, const char *key); + +int str_parms_add_str(struct str_parms *str_parms, const char *key, + const char *value); +int str_parms_add_int(struct str_parms *str_parms, const char *key, int value); + +int str_parms_add_float(struct str_parms *str_parms, const char *key, + float value); + +int str_parms_get_str(struct str_parms *str_parms, const char *key, + char *out_val, int len); +int str_parms_get_int(struct str_parms *str_parms, const char *key, + int *out_val); +int str_parms_get_float(struct str_parms *str_parms, const char *key, + float *out_val); + +char *str_parms_to_str(struct str_parms *str_parms); + +/* debug */ +void str_parms_dump(struct str_parms *str_parms); + +#endif /* __CUTILS_STR_PARMS_H */ diff --git a/include/cutils/threads.h b/include/cutils/threads.h new file mode 100644 index 0000000..acf8f48 --- /dev/null +++ b/include/cutils/threads.h @@ -0,0 +1,146 @@ +/* + * 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_THREADS_H +#define _LIBS_CUTILS_THREADS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/***********************************************************************/ +/***********************************************************************/ +/***** *****/ +/***** local thread storage *****/ +/***** *****/ +/***********************************************************************/ +/***********************************************************************/ + +#ifdef HAVE_PTHREADS + +#include + +typedef struct { + pthread_mutex_t lock; + int has_tls; + pthread_key_t tls; + +} thread_store_t; + +#define THREAD_STORE_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0 } + +#elif defined HAVE_WIN32_THREADS + +#include + +typedef struct { + int lock_init; + int has_tls; + DWORD tls; + CRITICAL_SECTION lock; + +} thread_store_t; + +#define THREAD_STORE_INITIALIZER { 0, 0, 0, {0, 0, 0, 0, 0, 0} } + +#else +# error "no thread_store_t implementation for your platform !!" +#endif + +typedef void (*thread_store_destruct_t)(void* value); + +extern void* thread_store_get(thread_store_t* store); + +extern void thread_store_set(thread_store_t* store, + void* value, + thread_store_destruct_t destroy); + +/***********************************************************************/ +/***********************************************************************/ +/***** *****/ +/***** mutexes *****/ +/***** *****/ +/***********************************************************************/ +/***********************************************************************/ + +#ifdef HAVE_PTHREADS + +typedef pthread_mutex_t mutex_t; + +#define MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER + +static __inline__ void mutex_lock(mutex_t* lock) +{ + pthread_mutex_lock(lock); +} +static __inline__ void mutex_unlock(mutex_t* lock) +{ + pthread_mutex_unlock(lock); +} +static __inline__ int mutex_init(mutex_t* lock) +{ + return pthread_mutex_init(lock, NULL); +} +static __inline__ void mutex_destroy(mutex_t* lock) +{ + pthread_mutex_destroy(lock); +} +#endif + +#ifdef HAVE_WIN32_THREADS +typedef struct { + int init; + CRITICAL_SECTION lock[1]; +} mutex_t; + +#define MUTEX_INITIALIZER { 0, {{ NULL, 0, 0, NULL, NULL, 0 }} } + +static __inline__ void mutex_lock(mutex_t* lock) +{ + if (!lock->init) { + lock->init = 1; + InitializeCriticalSection( lock->lock ); + lock->init = 2; + } else while (lock->init != 2) + Sleep(10); + + EnterCriticalSection(lock->lock); +} + +static __inline__ void mutex_unlock(mutex_t* lock) +{ + LeaveCriticalSection(lock->lock); +} +static __inline__ int mutex_init(mutex_t* lock) +{ + InitializeCriticalSection(lock->lock); + lock->init = 2; + return 0; +} +static __inline__ void mutex_destroy(mutex_t* lock) +{ + if (lock->init) { + lock->init = 0; + DeleteCriticalSection(lock->lock); + } +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBS_CUTILS_THREADS_H */ diff --git a/include/cutils/trace.h b/include/cutils/trace.h new file mode 100644 index 0000000..1c8f107 --- /dev/null +++ b/include/cutils/trace.h @@ -0,0 +1,294 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#ifdef ANDROID_SMP +#include +#else +#include +#endif + +__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_LAST ATRACE_TAG_RS + +// 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 + +#ifdef HAVE_ANDROID_OS +/** + * Maximum size of a message that can be logged to the trace buffer. + * Note this message includes a tag, the pid, and the string given as the name. + * Names should be kept short to get the most use of the trace buffer. + */ +#define ATRACE_MESSAGE_LENGTH 1024 + +/** + * 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 volatile int32_t 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(!android_atomic_acquire_load(&atrace_is_ready))) { + 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))) { + char buf[ATRACE_MESSAGE_LENGTH]; + size_t len; + + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "B|%d|%s", getpid(), name); + write(atrace_marker_fd, buf, len); + } +} + +/** + * 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))) { + char buf[ATRACE_MESSAGE_LENGTH]; + size_t len; + + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%d", getpid(), + name, cookie); + write(atrace_marker_fd, buf, len); + } +} + +/** + * 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))) { + char buf[ATRACE_MESSAGE_LENGTH]; + size_t len; + + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%d", getpid(), + name, cookie); + write(atrace_marker_fd, buf, len); + } +} + + +/** + * 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))) { + char buf[ATRACE_MESSAGE_LENGTH]; + size_t len; + + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%d", + getpid(), name, value); + write(atrace_marker_fd, buf, len); + } +} + +/** + * 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))) { + char buf[ATRACE_MESSAGE_LENGTH]; + size_t len; + + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%lld", + getpid(), name, value); + write(atrace_marker_fd, buf, len); + } +} + +#else // not HAVE_ANDROID_OS + +#define ATRACE_INIT() +#define ATRACE_GET_ENABLED_TAGS() +#define ATRACE_ENABLED() 0 +#define ATRACE_BEGIN(name) +#define ATRACE_END() +#define ATRACE_ASYNC_BEGIN(name, cookie) +#define ATRACE_ASYNC_END(name, cookie) +#define ATRACE_INT(name, value) + +#endif // not HAVE_ANDROID_OS + +__END_DECLS + +#endif // _LIBS_CUTILS_TRACE_H diff --git a/include/cutils/tztime.h b/include/cutils/tztime.h new file mode 100644 index 0000000..dbdbd60 --- /dev/null +++ b/include/cutils/tztime.h @@ -0,0 +1,24 @@ +/* + * 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_TZTIME_H +#define _CUTILS_TZTIME_H + +// TODO: fix both callers to just include themselves. +#include + +#endif /* __CUTILS_TZTIME_H */ + diff --git a/include/cutils/uevent.h b/include/cutils/uevent.h new file mode 100644 index 0000000..4cca7e5 --- /dev/null +++ b/include/cutils/uevent.h @@ -0,0 +1,35 @@ +/* + * 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 __CUTILS_UEVENT_H +#define __CUTILS_UEVENT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int uevent_open_socket(int buf_sz, bool passcred); +ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length); +ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid); + +#ifdef __cplusplus +} +#endif + +#endif /* __CUTILS_UEVENT_H */ diff --git a/include/hardware/qemu_pipe.h b/include/hardware/qemu_pipe.h new file mode 100644 index 0000000..1a67022 --- /dev/null +++ b/include/hardware/qemu_pipe.h @@ -0,0 +1,91 @@ +/* + * 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_HARDWARE_QEMU_PIPE_H +#define ANDROID_INCLUDE_HARDWARE_QEMU_PIPE_H + +#include +#include +#include +#include +#include /* for pthread_once() */ +#include +#include +#include + +#ifndef D +# define D(...) do{}while(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. + * E.g. 'opengles' or 'camera'. + * + * On success, return a valid file descriptor + * Returns -1 on error, and errno gives the error code, 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) +{ + char buff[256]; + int buffLen; + int fd, ret; + + if (pipeName == NULL || pipeName[0] == '\0') { + errno = EINVAL; + return -1; + } + + snprintf(buff, sizeof buff, "pipe:%s", pipeName); + + fd = open("/dev/qemu_pipe", O_RDWR); + if (fd < 0) { + D("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__, strerror(errno)); + //errno = ENOSYS; + return -1; + } + + buffLen = strlen(buff); + + ret = TEMP_FAILURE_RETRY(write(fd, buff, buffLen+1)); + if (ret != buffLen+1) { + D("%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; +} + +#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_PIPE_H */ diff --git a/include/log/event_tag_map.h b/include/log/event_tag_map.h new file mode 100644 index 0000000..1653c61 --- /dev/null +++ b/include/log/event_tag_map.h @@ -0,0 +1,50 @@ +/* + * 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 new file mode 100644 index 0000000..7faddea --- /dev/null +++ b/include/log/log.h @@ -0,0 +1,563 @@ +/* + * 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_LOG_LOG_H +#define _LIBS_LOG_LOG_H + +#include +#include +#include +#include +#ifdef HAVE_PTHREADS +#include +#endif +#include + +#include +#include + +#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 + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose log message using the current LOG_TAG. + */ +#ifndef ALOGV +#if LOG_NDEBUG +#define ALOGV(...) ((void)0) +#else +#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef ALOGV_IF +#if LOG_NDEBUG +#define ALOGV_IF(cond, ...) ((void)0) +#else +#define ALOGV_IF(cond, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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 +#if LOG_NDEBUG +#define SLOGV(...) ((void)0) +#else +#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef SLOGV_IF +#if LOG_NDEBUG +#define SLOGV_IF(cond, ...) ((void)0) +#else +#define SLOGV_IF(cond, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose radio log message using the current LOG_TAG. + */ +#ifndef RLOGV +#if LOG_NDEBUG +#define RLOGV(...) ((void)0) +#else +#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef RLOGV_IF +#if LOG_NDEBUG +#define RLOGV_IF(cond, ...) ((void)0) +#else +#define RLOGV_IF(cond, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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, ...) \ + ( (CONDITION(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, +} AndroidEventLogType; + + +#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_STRING +#define LOG_EVENT_STRING(_tag, _value) \ + ((void) 0) /* not implemented -- must combine len with string */ +#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) + +// TODO: remove these prototypes and their users +#define android_testLog(prio, tag) (1) +#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_MAIN = 0, + LOG_ID_RADIO = 1, + LOG_ID_EVENTS = 2, + LOG_ID_SYSTEM = 3, + + LOG_ID_MAX +} log_id_t; + +/* + * 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, ...); + + +#ifdef __cplusplus +} +#endif + +#endif // _LIBS_CUTILS_LOG_H diff --git a/include/log/logd.h b/include/log/logd.h new file mode 100644 index 0000000..379c373 --- /dev/null +++ b/include/log/logd.h @@ -0,0 +1,49 @@ +/* + * 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 + +/* the rest is only used internally by the system */ +#include +#include +#include +#include +#include +#ifdef HAVE_PTHREADS +#include +#endif +#include +#include + +#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); + +#ifdef __cplusplus +} +#endif + +#endif /* _LOGD_H */ diff --git a/include/log/logger.h b/include/log/logger.h new file mode 100644 index 0000000..04f3fb0 --- /dev/null +++ b/include/log/logger.h @@ -0,0 +1,81 @@ +/* utils/logger.h +** +** Copyright 2007, 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_LOGGER_H +#define _UTILS_LOGGER_H + +#include + +/* + * 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 */ +}; + +/* + * 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 + */ +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 */ +}; + +#define LOGGER_LOG_MAIN "log/main" +#define LOGGER_LOG_RADIO "log/radio" +#define LOGGER_LOG_EVENTS "log/events" +#define LOGGER_LOG_SYSTEM "log/system" + +/* + * The maximum size of the log entry payload that can be + * written to the kernel logger driver. An attempt to write + * more than this amount to /dev/log/* 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) + +#ifdef HAVE_IOCTL + +#include + +#define __LOGGERIO 0xAE + +#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ +#define LOGGER_GET_VERSION _IO(__LOGGERIO, 5) /* abi version */ +#define LOGGER_SET_VERSION _IO(__LOGGERIO, 6) /* abi version */ + +#endif // HAVE_IOCTL + +#endif /* _UTILS_LOGGER_H */ diff --git a/include/log/logprint.h b/include/log/logprint.h new file mode 100644 index 0000000..481c96e --- /dev/null +++ b/include/log/logprint.h @@ -0,0 +1,156 @@ +/* + * 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 +#include +#include +#include + +#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, +} 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); + +void 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/include/log/uio.h new file mode 100644 index 0000000..01a74d2 --- /dev/null +++ b/include/log/uio.h @@ -0,0 +1,48 @@ +/* + * 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. + */ + +// +// implementation of sys/uio.h for platforms that don't have it (Win32) +// +#ifndef _LIBS_CUTILS_UIO_H +#define _LIBS_CUTILS_UIO_H + +#ifdef HAVE_SYS_UIO_H +#include +#else + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct iovec { + const void* iov_base; + size_t iov_len; +}; + +extern int readv( int fd, struct iovec* vecs, int count ); +extern int writev( int fd, const struct iovec* vecs, int count ); + +#ifdef __cplusplus +} +#endif + +#endif /* !HAVE_SYS_UIO_H */ + +#endif /* _LIBS_UTILS_UIO_H */ + diff --git a/include/mincrypt/hash-internal.h b/include/mincrypt/hash-internal.h new file mode 100644 index 0000000..96806f7 --- /dev/null +++ b/include/mincrypt/hash-internal.h @@ -0,0 +1,40 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: mschilder@google.com (Marius Schilder) + +#ifndef SECURITY_UTIL_LITE_HASH_INTERNAL_H__ +#define SECURITY_UTIL_LITE_HASH_INTERNAL_H__ + +#include + +#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 // SECURITY_UTIL_LITE_HASH_INTERNAL_H__ diff --git a/include/mincrypt/rsa.h b/include/mincrypt/rsa.h new file mode 100644 index 0000000..cc0e800 --- /dev/null +++ b/include/mincrypt/rsa.h @@ -0,0 +1,58 @@ +/* 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 _EMBEDDED_RSA_H_ +#define _EMBEDDED_RSA_H_ + +#include + +#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 diff --git a/include/mincrypt/sha.h b/include/mincrypt/sha.h new file mode 100644 index 0000000..120ddcb --- /dev/null +++ b/include/mincrypt/sha.h @@ -0,0 +1,30 @@ +// Copyright 2005 Google Inc. All Rights Reserved. +// Author: mschilder@google.com (Marius Schilder) + +#ifndef SECURITY_UTIL_LITE_SHA1_H__ +#define SECURITY_UTIL_LITE_SHA1_H__ + +#include +#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 // SECURITY_UTIL_LITE_SHA1_H__ diff --git a/include/mincrypt/sha256.h b/include/mincrypt/sha256.h new file mode 100644 index 0000000..0f3efb7 --- /dev/null +++ b/include/mincrypt/sha256.h @@ -0,0 +1,29 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: mschilder@google.com (Marius Schilder) + +#ifndef SECURITY_UTIL_LITE_SHA256_H__ +#define SECURITY_UTIL_LITE_SHA256_H__ + +#include +#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 // SECURITY_UTIL_LITE_SHA256_H__ diff --git a/include/private/android_filesystem_capability.h b/include/private/android_filesystem_capability.h new file mode 100644 index 0000000..0505cda --- /dev/null +++ b/include/private/android_filesystem_capability.h @@ -0,0 +1,117 @@ +/* + * 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. + */ + +/* + * Taken from linux/capability.h, with minor modifications + */ + +#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H +#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H + +#include + +#define __user +#define __u32 uint32_t +#define __le32 uint32_t + +#define _LINUX_CAPABILITY_VERSION_1 0x19980330 +#define _LINUX_CAPABILITY_U32S_1 1 +#define _LINUX_CAPABILITY_VERSION_2 0x20071026 +#define _LINUX_CAPABILITY_U32S_2 2 +#define _LINUX_CAPABILITY_VERSION_3 0x20080522 +#define _LINUX_CAPABILITY_U32S_3 2 + +typedef struct __user_cap_header_struct { + __u32 version; + int pid; +} __user *cap_user_header_t; + +typedef struct __user_cap_data_struct { + __u32 effective; + __u32 permitted; + __u32 inheritable; +} __user *cap_user_data_t; + +#define VFS_CAP_REVISION_MASK 0xFF000000 +#define VFS_CAP_REVISION_SHIFT 24 +#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK +#define VFS_CAP_FLAGS_EFFECTIVE 0x000001 +#define VFS_CAP_REVISION_1 0x01000000 +#define VFS_CAP_U32_1 1 +#define XATTR_CAPS_SZ_1 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_1)) +#define VFS_CAP_REVISION_2 0x02000000 +#define VFS_CAP_U32_2 2 +#define XATTR_CAPS_SZ_2 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_2)) +#define XATTR_CAPS_SZ XATTR_CAPS_SZ_2 +#define VFS_CAP_U32 VFS_CAP_U32_2 +#define VFS_CAP_REVISION VFS_CAP_REVISION_2 + +struct vfs_cap_data { + __le32 magic_etc; + struct { + __le32 permitted; + __le32 inheritable; + } data[VFS_CAP_U32]; +}; + +#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1 +#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_1 +#define CAP_CHOWN 0 +#define CAP_DAC_OVERRIDE 1 +#define CAP_DAC_READ_SEARCH 2 +#define CAP_FOWNER 3 +#define CAP_FSETID 4 +#define CAP_KILL 5 +#define CAP_SETGID 6 +#define CAP_SETUID 7 +#define CAP_SETPCAP 8 +#define CAP_LINUX_IMMUTABLE 9 +#define CAP_NET_BIND_SERVICE 10 +#define CAP_NET_BROADCAST 11 +#define CAP_NET_ADMIN 12 +#define CAP_NET_RAW 13 +#define CAP_IPC_LOCK 14 +#define CAP_IPC_OWNER 15 +#define CAP_SYS_MODULE 16 +#define CAP_SYS_RAWIO 17 +#define CAP_SYS_CHROOT 18 +#define CAP_SYS_PTRACE 19 +#define CAP_SYS_PACCT 20 +#define CAP_SYS_ADMIN 21 +#define CAP_SYS_BOOT 22 +#define CAP_SYS_NICE 23 +#define CAP_SYS_RESOURCE 24 +#define CAP_SYS_TIME 25 +#define CAP_SYS_TTY_CONFIG 26 +#define CAP_MKNOD 27 +#define CAP_LEASE 28 +#define CAP_AUDIT_WRITE 29 +#define CAP_AUDIT_CONTROL 30 +#define CAP_SETFCAP 31 +#define CAP_MAC_OVERRIDE 32 +#define CAP_MAC_ADMIN 33 +#define CAP_SYSLOG 34 +#define CAP_WAKE_ALARM 35 +#define CAP_LAST_CAP CAP_WAKE_ALARM +#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP) +#define CAP_TO_INDEX(x) ((x) >> 5) +#define CAP_TO_MASK(x) (1 << ((x) & 31)) + +#undef __user +#undef __u32 +#undef __le32 + +#endif diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h new file mode 100644 index 0000000..0ed0d78 --- /dev/null +++ b/include/private/android_filesystem_config.h @@ -0,0 +1,301 @@ +/* + * 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. + */ + +/* This file is used to define the properties of the filesystem +** images generated by build tools (mkbootfs and mkyaffs2image) and +** by the device side of adb. +*/ + +#ifndef _ANDROID_FILESYSTEM_CONFIG_H_ +#define _ANDROID_FILESYSTEM_CONFIG_H_ + +#include +#include +#include +#include + +#ifdef HAVE_ANDROID_OS +#include +#else +#include "android_filesystem_capability.h" +#endif + +/* This is the master Users and Groups config for the platform. + * DO NOT EVER RENUMBER + */ + +#define AID_ROOT 0 /* traditional unix root user */ + +#define AID_SYSTEM 1000 /* system server */ + +#define AID_RADIO 1001 /* telephony subsystem, RIL */ +#define AID_BLUETOOTH 1002 /* bluetooth subsystem */ +#define AID_GRAPHICS 1003 /* graphics devices */ +#define AID_INPUT 1004 /* input devices */ +#define AID_AUDIO 1005 /* audio devices */ +#define AID_CAMERA 1006 /* camera devices */ +#define AID_LOG 1007 /* log devices */ +#define AID_COMPASS 1008 /* compass device */ +#define AID_MOUNT 1009 /* mountd socket */ +#define AID_WIFI 1010 /* wifi subsystem */ +#define AID_ADB 1011 /* android debug bridge (adbd) */ +#define AID_INSTALL 1012 /* group for installing packages */ +#define AID_MEDIA 1013 /* mediaserver process */ +#define AID_DHCP 1014 /* dhcp client */ +#define AID_SDCARD_RW 1015 /* external storage write access */ +#define AID_VPN 1016 /* vpn system */ +#define AID_KEYSTORE 1017 /* keystore subsystem */ +#define AID_USB 1018 /* USB devices */ +#define AID_DRM 1019 /* DRM server */ +#define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */ +#define AID_GPS 1021 /* GPS daemon */ +#define AID_UNUSED1 1022 /* deprecated, DO NOT USE */ +#define AID_MEDIA_RW 1023 /* internal media storage write access */ +#define AID_MTP 1024 /* MTP USB driver access */ +#define AID_UNUSED2 1025 /* deprecated, DO NOT USE */ +#define AID_DRMRPC 1026 /* group for drm rpc */ +#define AID_NFC 1027 /* nfc subsystem */ +#define AID_SDCARD_R 1028 /* external storage read access */ +#define AID_CLAT 1029 /* clat part of nat464 */ +#define AID_LOOP_RADIO 1030 /* loop radio devices */ +#define AID_MEDIA_DRM 1031 /* MediaDrm plugins */ +#define AID_PACKAGE_INFO 1032 /* access to installed package details */ +#define AID_SDCARD_PICS 1033 /* external storage photos access */ +#define AID_SDCARD_AV 1034 /* external storage audio/video access */ +#define AID_SDCARD_ALL 1035 /* access all users external storage */ + +#define AID_SHELL 2000 /* adb and debug shell user */ +#define AID_CACHE 2001 /* cache access */ +#define AID_DIAG 2002 /* access to diagnostic resources */ + +/* The 3000 series are intended for use as supplemental group id's only. + * They indicate special Android capabilities that the kernel is aware of. */ +#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */ +#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */ +#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */ +#define AID_NET_RAW 3004 /* can create raw INET sockets */ +#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_MISC 9998 /* access to misc storage */ +#define AID_NOBODY 9999 + +#define AID_APP 10000 /* first 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_USER 100000 /* offset for uid ranges for each user */ + +#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 */ + +#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES) +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, }, + + { "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, }, + + { "misc", AID_MISC, }, + { "nobody", AID_NOBODY, }, +}; + +#define android_id_count \ + (sizeof(android_ids) / sizeof(android_ids[0])) + +struct fs_path_config { + unsigned mode; + unsigned uid; + unsigned gid; + uint64_t capabilities; + const char *prefix; +}; + +/* Rules for directories. +** These rules are applied based on "first match", so they +** should start with the most specific path and work their +** way up to the root. +*/ + +static const struct fs_path_config android_dirs[] = { + { 00770, AID_SYSTEM, AID_CACHE, 0, "cache" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/dalvik-cache" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" }, + { 00771, AID_SHELL, AID_SHELL, 0, "data/local/tmp" }, + { 00771, AID_SHELL, AID_SHELL, 0, "data/local" }, + { 01771, AID_SYSTEM, AID_MISC, 0, "data/misc" }, + { 00770, AID_DHCP, AID_DHCP, 0, "data/misc/dhcp" }, + { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" }, + { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" }, + { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" }, + { 00750, AID_ROOT, AID_SHELL, 0, "sbin" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" }, + { 00755, AID_ROOT, AID_SHELL, 0, "vendor" }, + { 00777, AID_ROOT, AID_ROOT, 0, "sdcard" }, + { 00755, AID_ROOT, AID_ROOT, 0, 0 }, +}; + +/* Rules for files. +** These rules are applied based on "first match", so they +** should start with the most specific path and work their +** way up to the root. Prefixes ending in * denotes wildcard +** and will allow partial matches. +*/ +static const struct fs_path_config android_files[] = { + { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.rc" }, + { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.goldfish.sh" }, + { 00440, AID_ROOT, AID_SHELL, 0, "system/etc/init.trout.rc" }, + { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.ril" }, + { 00550, AID_ROOT, AID_SHELL, 0, "system/etc/init.testmenu" }, + { 00550, AID_DHCP, AID_SHELL, 0, "system/etc/dhcpcd/dhcpcd-run-hooks" }, + { 00444, AID_RADIO, AID_AUDIO, 0, "system/etc/AudioPara4.csv" }, + { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" }, + { 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" }, + { 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_APP, AID_APP, 0, "data/data/*" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/bin/ping" }, + + /* the following file is INTENTIONALLY set-gid and not set-uid. + * Do not change. */ + { 02750, AID_ROOT, AID_INET, 0, "system/bin/netcfg" }, + + /* the following five files are INTENTIONALLY set-uid, but they + * are NOT included on user builds. */ + { 06755, AID_ROOT, AID_ROOT, 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" }, + { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/tcpdump" }, + { 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, (1 << CAP_SETUID) | (1 << CAP_SETGID), "system/bin/run-as" }, + + { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, + { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/xbin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "system/vendor/bin/*" }, + { 00755, AID_ROOT, AID_SHELL, 0, "vendor/bin/*" }, + { 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, "charger*" }, + { 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" }, + { 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" }, + { 00644, AID_ROOT, AID_ROOT, 0, 0 }, +}; + +static inline void fs_config(const char *path, int dir, + unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities) +{ + const struct fs_path_config *pc; + int plen; + + if (path[0] == '/') { + path++; + } + + pc = dir ? android_dirs : android_files; + plen = strlen(path); + for(; pc->prefix; pc++){ + int len = strlen(pc->prefix); + if (dir) { + if(plen < len) continue; + if(!strncmp(pc->prefix, path, len)) break; + continue; + } + /* If name ends in * then allow partial matches. */ + if (pc->prefix[len -1] == '*') { + if(!strncmp(pc->prefix, path, len - 1)) break; + } else if (plen == len){ + if(!strncmp(pc->prefix, path, len)) break; + } + } + *uid = pc->uid; + *gid = pc->gid; + *mode = (*mode & (~07777)) | pc->mode; + *capabilities = pc->capabilities; + +#if 0 + fprintf(stderr,"< '%s' '%s' %d %d %o >\n", + path, pc->prefix ? pc->prefix : "", *uid, *gid, *mode); +#endif +} +#endif +#endif diff --git a/include/sys/system_properties.h b/include/sys/system_properties.h new file mode 100644 index 0000000..01c3db3 --- /dev/null +++ b/include/sys/system_properties.h @@ -0,0 +1,102 @@ +/* + * 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. + */ + +#ifndef _INCLUDE_SYS_SYSTEM_PROPERTIES_H +#define _INCLUDE_SYS_SYSTEM_PROPERTIES_H + +#include + +__BEGIN_DECLS + +typedef struct prop_info prop_info; + +#define PROP_NAME_MAX 32 +#define PROP_VALUE_MAX 92 + +/* Look up a system property by name, copying its value and a +** \0 terminator to the provided pointer. The total bytes +** copied will be no greater than PROP_VALUE_MAX. Returns +** the string length of the value. A property that is not +** defined is identical to a property with a length 0 value. +*/ +int __system_property_get(const char *name, char *value); + +/* Set a system property by name. +**/ +int __system_property_set(const char *key, const char *value); + +/* Return a pointer to the system property named name, if it +** exists, or NULL if there is no such property. Use +** __system_property_read() to obtain the string value from +** the returned prop_info pointer. +** +** It is safe to cache the prop_info pointer to avoid future +** lookups. These returned pointers will remain valid for +** the lifetime of the system. +*/ +const prop_info *__system_property_find(const char *name); + +/* Read the value of a system property. Returns the length +** of the value. Copies the value and \0 terminator into +** the provided value pointer. Total length (including +** terminator) will be no greater that PROP_VALUE_MAX. +** +** If name is nonzero, up to PROP_NAME_MAX bytes will be +** copied into the provided name pointer. The name will +** be \0 terminated. +*/ +int __system_property_read(const prop_info *pi, char *name, char *value); + +/* Return a prop_info for the nth system property, or NULL if +** there is no nth property. Use __system_property_read() to +** read the value of this property. +** +** Please do not call this method. It only exists to provide +** backwards compatibility to NDK apps. Its implementation +** is inefficient and order of results may change from call +** to call. +*/ +const prop_info *__system_property_find_nth(unsigned n); + +/* Pass a prop_info for each system property to the provided +** callback. Use __system_property_read() to read the value +** of this property. +** +** This method is for inspecting and debugging the property +** system. Please use __system_property_find() instead. +** +** Order of results may change from call to call. This is +** not a bug. +*/ +int __system_property_foreach( + void (*propfn)(const prop_info *pi, void *cookie), + void *cookie); + +__END_DECLS + +#endif diff --git a/libcutils/Android.mk b/libcutils/Android.mk new file mode 100644 index 0000000..0fd5a57 --- /dev/null +++ b/libcutils/Android.mk @@ -0,0 +1,155 @@ +# +# 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) + +ifeq ($(TARGET_CPU_SMP),true) + targetSmpFlag := -DANDROID_SMP=1 +else + targetSmpFlag := -DANDROID_SMP=0 +endif +hostSmpFlag := -DANDROID_SMP=0 + +commonSources := \ + hashmap.c \ + atomic.c.arm \ + native_handle.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 \ + config_utils.c \ + cpu_info.c \ + load_file.c \ + list.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 \ + +commonHostSources := \ + ashmem-host.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 +endif + + +# Static library for host +# ======================================================== +LOCAL_MODULE := libcutils +LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c +LOCAL_LDLIBS := -lpthread +LOCAL_STATIC_LIBRARIES := liblog +LOCAL_CFLAGS += $(hostSmpFlag) +include $(BUILD_HOST_STATIC_LIBRARY) + + +# Static library for host, 64-bit +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE := lib64cutils +LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c +LOCAL_LDLIBS := -lpthread +LOCAL_STATIC_LIBRARIES := lib64log +LOCAL_CFLAGS += $(hostSmpFlag) -m64 +include $(BUILD_HOST_STATIC_LIBRARY) + + +# Shared and static library for target +# ======================================================== + +# This is needed in LOCAL_C_INCLUDES to access the C library's private +# header named +# +libcutils_c_includes := bionic/libc/private + +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.c \ + uevent.c + +ifeq ($(TARGET_ARCH),arm) +LOCAL_SRC_FILES += arch-arm/memset32.S +else # !arm +ifeq ($(TARGET_ARCH_VARIANT),x86-atom) +LOCAL_CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32 +LOCAL_SRC_FILES += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c +else # !x86-atom +ifeq ($(TARGET_ARCH),mips) +LOCAL_SRC_FILES += arch-mips/android_memset.c +else # !mips +LOCAL_SRC_FILES += memory.c +endif # !mips +endif # !x86-atom +endif # !arm + +LOCAL_C_INCLUDES := $(libcutils_c_includes) $(KERNEL_HEADERS) +LOCAL_STATIC_LIBRARIES := liblog +LOCAL_CFLAGS += $(targetSmpFlag) +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 += $(targetSmpFlag) +LOCAL_C_INCLUDES := $(libcutils_c_includes) +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := tst_str_parms +LOCAL_CFLAGS += -DTEST_STR_PARMS +LOCAL_SRC_FILES := str_parms.c hashmap.c memory.c +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_MODULE_TAGS := optional +include $(BUILD_EXECUTABLE) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libcutils/MODULE_LICENSE_APACHE2 b/libcutils/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 diff --git a/libcutils/Makefile b/libcutils/Makefile new file mode 100644 index 0000000..c56f86c --- /dev/null +++ b/libcutils/Makefile @@ -0,0 +1,79 @@ +TARGET = libcutils.a +INSTALL_DIR = ../lib + +# TARGET_ARCH := arm +TARGET_CPU_SMP := false + +# Omits atomic.c.arm, ashmem-dev.c debugger.c uevent.c +SRCS += \ + hashmap.c \ + native_handle.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 \ + config_utils.c \ + cpu_info.c \ + load_file.c \ + list.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 \ + android_reboot.c \ + klog.c \ + partition_utils.c \ + properties.c \ + qtaguid.c \ + trace.c \ + +CC = $(CROSS_COMPILE)gcc +AR = $(CROSS_COMPILE)ar + +ifeq ($(TARGET_ARCH),arm) +SRCS += arch-arm/memset32.S +else # !arm +ifeq ($(TARGET_ARCH_VARIANT),x86-atom) +CFLAGS += -DHAVE_MEMSET16 -DHAVE_MEMSET32 +SRCS += arch-x86/android_memset16.S arch-x86/android_memset32.S memory.c +else # !x86-atom +ifeq ($(TARGET_ARCH),mips) +SRCS += arch-mips/android_memset.c +else # !mips +SRCS += memory.c +endif # !mips +endif # !x86-atom +endif # !arm + +ifeq ($(TARGET_CPU_SMP),true) +CFLAGS += -DANDROID_SMP=1 +else +CFLAGS += -DANDROID_SMP=0 +endif + +CFLAGS += -I../include +CFLAGS += -DHAVE_PTHREADS + +OBJS = $(SRCS:.c=.o) + +all: $(TARGET) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(TARGET): $(OBJS) + $(AR) r $@ $(OBJS) + [ -f $(INSTALL_DIR) ] || mkdir -p $(INSTALL_DIR) + cp $(TARGET) $(INSTALL_DIR) + +.PHONY: clean +clean: + rm -rf $(OBJS) $(TARGET) diff --git a/libcutils/NOTICE b/libcutils/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/libcutils/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-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. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c new file mode 100644 index 0000000..33251f2 --- /dev/null +++ b/libcutils/android_reboot.c @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Check to see if /proc/mounts contains any writeable filesystems + * backed by a block device. + * Return true if none found, else return false. + */ +static int remount_ro_done(void) +{ + FILE *f; + char mount_dev[256]; + char mount_dir[256]; + char mount_type[256]; + char mount_opts[256]; + int mount_freq; + int mount_passno; + int match; + int found_rw_fs = 0; + + f = fopen("/proc/mounts", "r"); + if (! f) { + /* If we can't read /proc/mounts, just give up */ + return 1; + } + + do { + match = fscanf(f, "%255s %255s %255s %255s %d %d\n", + mount_dev, mount_dir, mount_type, + mount_opts, &mount_freq, &mount_passno); + mount_dev[255] = 0; + mount_dir[255] = 0; + mount_type[255] = 0; + mount_opts[255] = 0; + if ((match == 6) && !strncmp(mount_dev, "/dev/block", 10) && strstr(mount_opts, "rw")) { + found_rw_fs = 1; + break; + } + } while (match != EOF); + + fclose(f); + + return !found_rw_fs; +} + +/* Remounting filesystems read-only is difficult when there are files + * opened for writing or pending deletes on the filesystem. There is + * no way to force the remount with the mount(2) syscall. The magic sysrq + * 'u' command does an emergency remount read-only on all writable filesystems + * that have a block device (i.e. not tmpfs filesystems) by calling + * emergency_remount(), which knows how to force the remount to read-only. + * Unfortunately, that is asynchronous, and just schedules the work and + * returns. The best way to determine if it is done is to read /proc/mounts + * repeatedly until there are no more writable filesystems mounted on + * block devices. + */ +static void remount_ro(void) +{ + int fd, cnt = 0; + + /* Trigger the remount of the filesystems as read-only, + * which also marks them clean. + */ + fd = open("/proc/sysrq-trigger", O_WRONLY); + if (fd < 0) { + return; + } + write(fd, "u", 1); + close(fd); + + + /* Now poll /proc/mounts till it's done */ + while (!remount_ro_done() && (cnt < 50)) { + usleep(100000); + cnt++; + } + + return; +} + + +int android_reboot(int cmd, int flags, char *arg) +{ + int ret; + + sync(); + remount_ro(); + + switch (cmd) { + case ANDROID_RB_RESTART: + ret = reboot(RB_AUTOBOOT); + break; + + case ANDROID_RB_POWEROFF: + ret = reboot(RB_POWER_OFF); + break; + + case ANDROID_RB_RESTART2: +#if 0 + ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_RESTART2, arg); +#endif + break; + + default: + ret = -1; + } + + return ret; +} + diff --git a/libcutils/arch-arm/memset32.S b/libcutils/arch-arm/memset32.S new file mode 100644 index 0000000..4697265 --- /dev/null +++ b/libcutils/arch-arm/memset32.S @@ -0,0 +1,93 @@ +/* + * 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. + */ +/* + * memset32.S + * + */ + + .text + .align + + .global android_memset32 + .type android_memset32, %function + .global android_memset16 + .type android_memset16, %function + + /* + * Optimized memset32 and memset16 for ARM. + * + * void android_memset16(uint16_t* dst, uint16_t value, size_t size); + * void android_memset32(uint32_t* dst, uint32_t value, size_t size); + * + */ + +android_memset16: + .fnstart + cmp r2, #1 + bxle lr + + /* expand the data to 32 bits */ + mov r1, r1, lsl #16 + orr r1, r1, r1, lsr #16 + + /* align to 32 bits */ + tst r0, #2 + strneh r1, [r0], #2 + subne r2, r2, #2 + .fnend + +android_memset32: + .fnstart + .save {lr} + str lr, [sp, #-4]! + + /* align the destination to a cache-line */ + mov r12, r1 + mov lr, r1 + rsb r3, r0, #0 + ands r3, r3, #0x1C + beq .Laligned32 + cmp r3, r2 + andhi r3, r2, #0x1C + sub r2, r2, r3 + + /* conditionally writes 0 to 7 words (length in r3) */ + movs r3, r3, lsl #28 + stmcsia r0!, {r1, lr} + stmcsia r0!, {r1, lr} + stmmiia r0!, {r1, lr} + movs r3, r3, lsl #2 + strcs r1, [r0], #4 + +.Laligned32: + mov r3, r1 +1: subs r2, r2, #32 + stmhsia r0!, {r1,r3,r12,lr} + stmhsia r0!, {r1,r3,r12,lr} + bhs 1b + add r2, r2, #32 + + /* conditionally stores 0 to 30 bytes */ + movs r2, r2, lsl #28 + stmcsia r0!, {r1,r3,r12,lr} + stmmiia r0!, {r1,lr} + movs r2, r2, lsl #2 + strcs r1, [r0], #4 + strmih lr, [r0], #2 + + ldr lr, [sp], #4 + bx lr + .fnend diff --git a/libcutils/arch-mips/android_memset.c b/libcutils/arch-mips/android_memset.c new file mode 100644 index 0000000..bbc99fe --- /dev/null +++ b/libcutils/arch-mips/android_memset.c @@ -0,0 +1,31 @@ +/* + * 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 + +/* Use mips-assembler versions supplied by bionic/libc/arch-mips/string/memset.S: */ +void _memset16(uint16_t* dst, uint16_t value, size_t size); +void _memset32(uint32_t* dst, uint32_t value, size_t size); + +void android_memset16(uint16_t* dst, uint16_t value, size_t size) +{ + _memset16(dst, value, size); +} + +void android_memset32(uint32_t* dst, uint32_t value, size_t size) +{ + _memset32(dst, value, size); +} diff --git a/libcutils/arch-x86/android_memset16.S b/libcutils/arch-x86/android_memset16.S new file mode 100644 index 0000000..b1f09cb --- /dev/null +++ b/libcutils/arch-x86/android_memset16.S @@ -0,0 +1,32 @@ +/* + * 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. + */ +/* + * Contributed by: Intel Corporation + */ + +#if defined(USE_SSE2) + +# include "cache_wrapper.S" +# undef __i686 +# define USE_AS_ANDROID +# define sse2_memset16_atom android_memset16 +# include "sse2-memset16-atom.S" + +#else + +# include "memset16.S" + +#endif diff --git a/libcutils/arch-x86/android_memset32.S b/libcutils/arch-x86/android_memset32.S new file mode 100644 index 0000000..1fb2ffe --- /dev/null +++ b/libcutils/arch-x86/android_memset32.S @@ -0,0 +1,33 @@ +/* + * 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. + */ +/* + * Contributed by: Intel Corporation + */ + +#if defined(USE_SSE2) + +# include "cache_wrapper.S" +# undef __i686 +# define USE_AS_ANDROID +# define sse2_memset32_atom android_memset32 +# include "sse2-memset32-atom.S" + +#else + +# include "memset32.S" + +#endif + diff --git a/libcutils/arch-x86/cache_wrapper.S b/libcutils/arch-x86/cache_wrapper.S new file mode 100644 index 0000000..508fdd3 --- /dev/null +++ b/libcutils/arch-x86/cache_wrapper.S @@ -0,0 +1,24 @@ +/* + * 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. + */ +/* + * Contributed by: Intel Corporation + */ + +/* Values are optimized for Atom */ +#define SHARED_CACHE_SIZE (512*1024) /* Atom L2 Cache */ +#define DATA_CACHE_SIZE (24*1024) /* Atom L1 Data Cache */ +#define SHARED_CACHE_SIZE_HALF (SHARED_CACHE_SIZE / 2) +#define DATA_CACHE_SIZE_HALF (DATA_CACHE_SIZE / 2) diff --git a/libcutils/arch-x86/sse2-memset16-atom.S b/libcutils/arch-x86/sse2-memset16-atom.S new file mode 100644 index 0000000..cafec82 --- /dev/null +++ b/libcutils/arch-x86/sse2-memset16-atom.S @@ -0,0 +1,722 @@ +/* + * 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. + */ +/* + * Contributed by: Intel Corporation + */ + +#ifndef L +# define L(label) .L##label +#endif + +#ifndef ALIGN +# define ALIGN(n) .p2align n +#endif + +#ifndef cfi_startproc +# define cfi_startproc .cfi_startproc +#endif + +#ifndef cfi_endproc +# define cfi_endproc .cfi_endproc +#endif + +#ifndef cfi_rel_offset +# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off +#endif + +#ifndef cfi_restore +# define cfi_restore(reg) .cfi_restore reg +#endif + +#ifndef cfi_adjust_cfa_offset +# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off +#endif + +#ifndef ENTRY +# define ENTRY(name) \ + .type name, @function; \ + .globl name; \ + .p2align 4; \ +name: \ + cfi_startproc +#endif + +#ifndef END +# define END(name) \ + cfi_endproc; \ + .size name, .-name +#endif + +#define CFI_PUSH(REG) \ + cfi_adjust_cfa_offset (4); \ + cfi_rel_offset (REG, 0) + +#define CFI_POP(REG) \ + cfi_adjust_cfa_offset (-4); \ + cfi_restore (REG) + +#define PUSH(REG) pushl REG; CFI_PUSH (REG) +#define POP(REG) popl REG; CFI_POP (REG) + +#ifdef USE_AS_BZERO16 +# define DEST PARMS +# define LEN DEST+4 +#else +# define DEST PARMS +# define CHR DEST+4 +# define LEN CHR+4 +#endif + +#if 1 +# define SETRTNVAL +#else +# define SETRTNVAL movl DEST(%esp), %eax +#endif + +#ifdef SHARED +# define ENTRANCE PUSH (%ebx); +# define RETURN_END POP (%ebx); ret +# define RETURN RETURN_END; CFI_PUSH (%ebx) +# define PARMS 8 /* Preserve EBX. */ +# define JMPTBL(I, B) I - B + +/* Load an entry in a jump table into EBX and branch to it. TABLE is a + jump table with relative offsets. */ +# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \ + /* We first load PC into EBX. */ \ + call __i686.get_pc_thunk.bx; \ + /* Get the address of the jump table. */ \ + add $(TABLE - .), %ebx; \ + /* Get the entry and convert the relative offset to the \ + absolute address. */ \ + add (%ebx,%ecx,4), %ebx; \ + /* We loaded the jump table and adjuested EDX. Go. */ \ + jmp *%ebx + + .section .gnu.linkonce.t.__i686.get_pc_thunk.bx,"ax",@progbits + .globl __i686.get_pc_thunk.bx + .hidden __i686.get_pc_thunk.bx + ALIGN (4) + .type __i686.get_pc_thunk.bx,@function +__i686.get_pc_thunk.bx: + movl (%esp), %ebx + ret +#else +# define ENTRANCE +# define RETURN_END ret +# define RETURN RETURN_END +# define PARMS 4 +# define JMPTBL(I, B) I + +/* Branch to an entry in a jump table. TABLE is a jump table with + absolute offsets. */ +# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \ + jmp *TABLE(,%ecx,4) +#endif + + .section .text.sse2,"ax",@progbits + ALIGN (4) +ENTRY (sse2_memset16_atom) + ENTRANCE + + movl LEN(%esp), %ecx +#ifdef USE_AS_ANDROID + shr $1, %ecx +#endif +#ifdef USE_AS_BZERO16 + xor %eax, %eax +#else + movzwl CHR(%esp), %eax + mov %eax, %edx + shl $16, %eax + or %edx, %eax +#endif + movl DEST(%esp), %edx + cmp $32, %ecx + jae L(32wordsormore) + +L(write_less32words): + lea (%edx, %ecx, 2), %edx + BRANCH_TO_JMPTBL_ENTRY (L(table_less32words)) + + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_less32words): + .int JMPTBL (L(write_0words), L(table_less32words)) + .int JMPTBL (L(write_1words), L(table_less32words)) + .int JMPTBL (L(write_2words), L(table_less32words)) + .int JMPTBL (L(write_3words), L(table_less32words)) + .int JMPTBL (L(write_4words), L(table_less32words)) + .int JMPTBL (L(write_5words), L(table_less32words)) + .int JMPTBL (L(write_6words), L(table_less32words)) + .int JMPTBL (L(write_7words), L(table_less32words)) + .int JMPTBL (L(write_8words), L(table_less32words)) + .int JMPTBL (L(write_9words), L(table_less32words)) + .int JMPTBL (L(write_10words), L(table_less32words)) + .int JMPTBL (L(write_11words), L(table_less32words)) + .int JMPTBL (L(write_12words), L(table_less32words)) + .int JMPTBL (L(write_13words), L(table_less32words)) + .int JMPTBL (L(write_14words), L(table_less32words)) + .int JMPTBL (L(write_15words), L(table_less32words)) + .int JMPTBL (L(write_16words), L(table_less32words)) + .int JMPTBL (L(write_17words), L(table_less32words)) + .int JMPTBL (L(write_18words), L(table_less32words)) + .int JMPTBL (L(write_19words), L(table_less32words)) + .int JMPTBL (L(write_20words), L(table_less32words)) + .int JMPTBL (L(write_21words), L(table_less32words)) + .int JMPTBL (L(write_22words), L(table_less32words)) + .int JMPTBL (L(write_23words), L(table_less32words)) + .int JMPTBL (L(write_24words), L(table_less32words)) + .int JMPTBL (L(write_25words), L(table_less32words)) + .int JMPTBL (L(write_26words), L(table_less32words)) + .int JMPTBL (L(write_27words), L(table_less32words)) + .int JMPTBL (L(write_28words), L(table_less32words)) + .int JMPTBL (L(write_29words), L(table_less32words)) + .int JMPTBL (L(write_30words), L(table_less32words)) + .int JMPTBL (L(write_31words), L(table_less32words)) + .popsection + + ALIGN (4) +L(write_28words): + movl %eax, -56(%edx) + movl %eax, -52(%edx) +L(write_24words): + movl %eax, -48(%edx) + movl %eax, -44(%edx) +L(write_20words): + movl %eax, -40(%edx) + movl %eax, -36(%edx) +L(write_16words): + movl %eax, -32(%edx) + movl %eax, -28(%edx) +L(write_12words): + movl %eax, -24(%edx) + movl %eax, -20(%edx) +L(write_8words): + movl %eax, -16(%edx) + movl %eax, -12(%edx) +L(write_4words): + movl %eax, -8(%edx) + movl %eax, -4(%edx) +L(write_0words): + SETRTNVAL + RETURN + + ALIGN (4) +L(write_29words): + movl %eax, -58(%edx) + movl %eax, -54(%edx) +L(write_25words): + movl %eax, -50(%edx) + movl %eax, -46(%edx) +L(write_21words): + movl %eax, -42(%edx) + movl %eax, -38(%edx) +L(write_17words): + movl %eax, -34(%edx) + movl %eax, -30(%edx) +L(write_13words): + movl %eax, -26(%edx) + movl %eax, -22(%edx) +L(write_9words): + movl %eax, -18(%edx) + movl %eax, -14(%edx) +L(write_5words): + movl %eax, -10(%edx) + movl %eax, -6(%edx) +L(write_1words): + mov %ax, -2(%edx) + SETRTNVAL + RETURN + + ALIGN (4) +L(write_30words): + movl %eax, -60(%edx) + movl %eax, -56(%edx) +L(write_26words): + movl %eax, -52(%edx) + movl %eax, -48(%edx) +L(write_22words): + movl %eax, -44(%edx) + movl %eax, -40(%edx) +L(write_18words): + movl %eax, -36(%edx) + movl %eax, -32(%edx) +L(write_14words): + movl %eax, -28(%edx) + movl %eax, -24(%edx) +L(write_10words): + movl %eax, -20(%edx) + movl %eax, -16(%edx) +L(write_6words): + movl %eax, -12(%edx) + movl %eax, -8(%edx) +L(write_2words): + movl %eax, -4(%edx) + SETRTNVAL + RETURN + + ALIGN (4) +L(write_31words): + movl %eax, -62(%edx) + movl %eax, -58(%edx) +L(write_27words): + movl %eax, -54(%edx) + movl %eax, -50(%edx) +L(write_23words): + movl %eax, -46(%edx) + movl %eax, -42(%edx) +L(write_19words): + movl %eax, -38(%edx) + movl %eax, -34(%edx) +L(write_15words): + movl %eax, -30(%edx) + movl %eax, -26(%edx) +L(write_11words): + movl %eax, -22(%edx) + movl %eax, -18(%edx) +L(write_7words): + movl %eax, -14(%edx) + movl %eax, -10(%edx) +L(write_3words): + movl %eax, -6(%edx) + movw %ax, -2(%edx) + SETRTNVAL + RETURN + + ALIGN (4) + +L(32wordsormore): + shl $1, %ecx + test $0x01, %edx + jz L(aligned2bytes) + mov %eax, (%edx) + mov %eax, -4(%edx, %ecx) + sub $2, %ecx + add $1, %edx + rol $8, %eax +L(aligned2bytes): +#ifdef USE_AS_BZERO16 + pxor %xmm0, %xmm0 +#else + movd %eax, %xmm0 + pshufd $0, %xmm0, %xmm0 +#endif + testl $0xf, %edx + jz L(aligned_16) +/* ECX > 32 and EDX is not 16 byte aligned. */ +L(not_aligned_16): + movdqu %xmm0, (%edx) + movl %edx, %eax + and $-16, %edx + add $16, %edx + sub %edx, %eax + add %eax, %ecx + movd %xmm0, %eax + + ALIGN (4) +L(aligned_16): + cmp $128, %ecx + jae L(128bytesormore) + +L(aligned_16_less128bytes): + add %ecx, %edx + shr $1, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + ALIGN (4) +L(128bytesormore): +#ifdef SHARED_CACHE_SIZE + PUSH (%ebx) + mov $SHARED_CACHE_SIZE, %ebx +#else +# ifdef SHARED + call __i686.get_pc_thunk.bx + add $_GLOBAL_OFFSET_TABLE_, %ebx + mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx +# else + PUSH (%ebx) + mov __x86_shared_cache_size, %ebx +# endif +#endif + cmp %ebx, %ecx + jae L(128bytesormore_nt_start) + + +#ifdef DATA_CACHE_SIZE + POP (%ebx) +# define RESTORE_EBX_STATE CFI_PUSH (%ebx) + cmp $DATA_CACHE_SIZE, %ecx +#else +# ifdef SHARED +# define RESTORE_EBX_STATE + call __i686.get_pc_thunk.bx + add $_GLOBAL_OFFSET_TABLE_, %ebx + cmp __x86_data_cache_size@GOTOFF(%ebx), %ecx +# else + POP (%ebx) +# define RESTORE_EBX_STATE CFI_PUSH (%ebx) + cmp __x86_data_cache_size, %ecx +# endif +#endif + + jae L(128bytes_L2_normal) + subl $128, %ecx +L(128bytesormore_normal): + sub $128, %ecx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + lea 128(%edx), %edx + jb L(128bytesless_normal) + + + sub $128, %ecx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + lea 128(%edx), %edx + jae L(128bytesormore_normal) + +L(128bytesless_normal): + lea 128(%ecx), %ecx + add %ecx, %edx + shr $1, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + ALIGN (4) +L(128bytes_L2_normal): + prefetcht0 0x380(%edx) + prefetcht0 0x3c0(%edx) + sub $128, %ecx + movdqa %xmm0, (%edx) + movaps %xmm0, 0x10(%edx) + movaps %xmm0, 0x20(%edx) + movaps %xmm0, 0x30(%edx) + movaps %xmm0, 0x40(%edx) + movaps %xmm0, 0x50(%edx) + movaps %xmm0, 0x60(%edx) + movaps %xmm0, 0x70(%edx) + add $128, %edx + cmp $128, %ecx + jae L(128bytes_L2_normal) + +L(128bytesless_L2_normal): + add %ecx, %edx + shr $1, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + RESTORE_EBX_STATE +L(128bytesormore_nt_start): + sub %ebx, %ecx + mov %ebx, %eax + and $0x7f, %eax + add %eax, %ecx + movd %xmm0, %eax + ALIGN (4) +L(128bytesormore_shared_cache_loop): + prefetcht0 0x3c0(%edx) + prefetcht0 0x380(%edx) + sub $0x80, %ebx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + add $0x80, %edx + cmp $0x80, %ebx + jae L(128bytesormore_shared_cache_loop) + cmp $0x80, %ecx + jb L(shared_cache_loop_end) + ALIGN (4) +L(128bytesormore_nt): + sub $0x80, %ecx + movntdq %xmm0, (%edx) + movntdq %xmm0, 0x10(%edx) + movntdq %xmm0, 0x20(%edx) + movntdq %xmm0, 0x30(%edx) + movntdq %xmm0, 0x40(%edx) + movntdq %xmm0, 0x50(%edx) + movntdq %xmm0, 0x60(%edx) + movntdq %xmm0, 0x70(%edx) + add $0x80, %edx + cmp $0x80, %ecx + jae L(128bytesormore_nt) + sfence +L(shared_cache_loop_end): +#if defined DATA_CACHE_SIZE || !defined SHARED + POP (%ebx) +#endif + add %ecx, %edx + shr $1, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_16_128bytes): + .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_2bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_6bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_10bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_14bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_18bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_22bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_26bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_30bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_34bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_38bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_42bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_46bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_50bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_54bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_58bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_62bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_66bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_70bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_74bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_78bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_82bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_86bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_90bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_94bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_98bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_102bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_106bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_110bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_114bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_118bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_122bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_126bytes), L(table_16_128bytes)) + .popsection + + + ALIGN (4) +L(aligned_16_112bytes): + movdqa %xmm0, -112(%edx) +L(aligned_16_96bytes): + movdqa %xmm0, -96(%edx) +L(aligned_16_80bytes): + movdqa %xmm0, -80(%edx) +L(aligned_16_64bytes): + movdqa %xmm0, -64(%edx) +L(aligned_16_48bytes): + movdqa %xmm0, -48(%edx) +L(aligned_16_32bytes): + movdqa %xmm0, -32(%edx) +L(aligned_16_16bytes): + movdqa %xmm0, -16(%edx) +L(aligned_16_0bytes): + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_114bytes): + movdqa %xmm0, -114(%edx) +L(aligned_16_98bytes): + movdqa %xmm0, -98(%edx) +L(aligned_16_82bytes): + movdqa %xmm0, -82(%edx) +L(aligned_16_66bytes): + movdqa %xmm0, -66(%edx) +L(aligned_16_50bytes): + movdqa %xmm0, -50(%edx) +L(aligned_16_34bytes): + movdqa %xmm0, -34(%edx) +L(aligned_16_18bytes): + movdqa %xmm0, -18(%edx) +L(aligned_16_2bytes): + movw %ax, -2(%edx) + SETRTNVAL + RETURN + + ALIGN (4) +L(aligned_16_116bytes): + movdqa %xmm0, -116(%edx) +L(aligned_16_100bytes): + movdqa %xmm0, -100(%edx) +L(aligned_16_84bytes): + movdqa %xmm0, -84(%edx) +L(aligned_16_68bytes): + movdqa %xmm0, -68(%edx) +L(aligned_16_52bytes): + movdqa %xmm0, -52(%edx) +L(aligned_16_36bytes): + movdqa %xmm0, -36(%edx) +L(aligned_16_20bytes): + movdqa %xmm0, -20(%edx) +L(aligned_16_4bytes): + movl %eax, -4(%edx) + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_118bytes): + movdqa %xmm0, -118(%edx) +L(aligned_16_102bytes): + movdqa %xmm0, -102(%edx) +L(aligned_16_86bytes): + movdqa %xmm0, -86(%edx) +L(aligned_16_70bytes): + movdqa %xmm0, -70(%edx) +L(aligned_16_54bytes): + movdqa %xmm0, -54(%edx) +L(aligned_16_38bytes): + movdqa %xmm0, -38(%edx) +L(aligned_16_22bytes): + movdqa %xmm0, -22(%edx) +L(aligned_16_6bytes): + movl %eax, -6(%edx) + movw %ax, -2(%edx) + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_120bytes): + movdqa %xmm0, -120(%edx) +L(aligned_16_104bytes): + movdqa %xmm0, -104(%edx) +L(aligned_16_88bytes): + movdqa %xmm0, -88(%edx) +L(aligned_16_72bytes): + movdqa %xmm0, -72(%edx) +L(aligned_16_56bytes): + movdqa %xmm0, -56(%edx) +L(aligned_16_40bytes): + movdqa %xmm0, -40(%edx) +L(aligned_16_24bytes): + movdqa %xmm0, -24(%edx) +L(aligned_16_8bytes): + movq %xmm0, -8(%edx) + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_122bytes): + movdqa %xmm0, -122(%edx) +L(aligned_16_106bytes): + movdqa %xmm0, -106(%edx) +L(aligned_16_90bytes): + movdqa %xmm0, -90(%edx) +L(aligned_16_74bytes): + movdqa %xmm0, -74(%edx) +L(aligned_16_58bytes): + movdqa %xmm0, -58(%edx) +L(aligned_16_42bytes): + movdqa %xmm0, -42(%edx) +L(aligned_16_26bytes): + movdqa %xmm0, -26(%edx) +L(aligned_16_10bytes): + movq %xmm0, -10(%edx) + movw %ax, -2(%edx) + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_124bytes): + movdqa %xmm0, -124(%edx) +L(aligned_16_108bytes): + movdqa %xmm0, -108(%edx) +L(aligned_16_92bytes): + movdqa %xmm0, -92(%edx) +L(aligned_16_76bytes): + movdqa %xmm0, -76(%edx) +L(aligned_16_60bytes): + movdqa %xmm0, -60(%edx) +L(aligned_16_44bytes): + movdqa %xmm0, -44(%edx) +L(aligned_16_28bytes): + movdqa %xmm0, -28(%edx) +L(aligned_16_12bytes): + movq %xmm0, -12(%edx) + movl %eax, -4(%edx) + SETRTNVAL + RETURN + + + ALIGN (4) +L(aligned_16_126bytes): + movdqa %xmm0, -126(%edx) +L(aligned_16_110bytes): + movdqa %xmm0, -110(%edx) +L(aligned_16_94bytes): + movdqa %xmm0, -94(%edx) +L(aligned_16_78bytes): + movdqa %xmm0, -78(%edx) +L(aligned_16_62bytes): + movdqa %xmm0, -62(%edx) +L(aligned_16_46bytes): + movdqa %xmm0, -46(%edx) +L(aligned_16_30bytes): + movdqa %xmm0, -30(%edx) +L(aligned_16_14bytes): + movq %xmm0, -14(%edx) + movl %eax, -6(%edx) + movw %ax, -2(%edx) + SETRTNVAL + RETURN + +END (sse2_memset16_atom) diff --git a/libcutils/arch-x86/sse2-memset32-atom.S b/libcutils/arch-x86/sse2-memset32-atom.S new file mode 100644 index 0000000..4a52484 --- /dev/null +++ b/libcutils/arch-x86/sse2-memset32-atom.S @@ -0,0 +1,513 @@ +/* + * 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. + */ +/* + * Contributed by: Intel Corporation + */ + +#ifndef L +# define L(label) .L##label +#endif + +#ifndef ALIGN +# define ALIGN(n) .p2align n +#endif + +#ifndef cfi_startproc +# define cfi_startproc .cfi_startproc +#endif + +#ifndef cfi_endproc +# define cfi_endproc .cfi_endproc +#endif + +#ifndef cfi_rel_offset +# define cfi_rel_offset(reg, off) .cfi_rel_offset reg, off +#endif + +#ifndef cfi_restore +# define cfi_restore(reg) .cfi_restore reg +#endif + +#ifndef cfi_adjust_cfa_offset +# define cfi_adjust_cfa_offset(off) .cfi_adjust_cfa_offset off +#endif + +#ifndef ENTRY +# define ENTRY(name) \ + .type name, @function; \ + .globl name; \ + .p2align 4; \ +name: \ + cfi_startproc +#endif + +#ifndef END +# define END(name) \ + cfi_endproc; \ + .size name, .-name +#endif + +#define CFI_PUSH(REG) \ + cfi_adjust_cfa_offset (4); \ + cfi_rel_offset (REG, 0) + +#define CFI_POP(REG) \ + cfi_adjust_cfa_offset (-4); \ + cfi_restore (REG) + +#define PUSH(REG) pushl REG; CFI_PUSH (REG) +#define POP(REG) popl REG; CFI_POP (REG) + +#ifdef USE_AS_BZERO32 +# define DEST PARMS +# define LEN DEST+4 +#else +# define DEST PARMS +# define DWDS DEST+4 +# define LEN DWDS+4 +#endif + +#ifdef USE_AS_WMEMSET32 +# define SETRTNVAL movl DEST(%esp), %eax +#else +# define SETRTNVAL +#endif + +#ifdef SHARED +# define ENTRANCE PUSH (%ebx); +# define RETURN_END POP (%ebx); ret +# define RETURN RETURN_END; CFI_PUSH (%ebx) +# define PARMS 8 /* Preserve EBX. */ +# define JMPTBL(I, B) I - B + +/* Load an entry in a jump table into EBX and branch to it. TABLE is a + jump table with relative offsets. */ +# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \ + /* We first load PC into EBX. */ \ + call __i686.get_pc_thunk.bx; \ + /* Get the address of the jump table. */ \ + add $(TABLE - .), %ebx; \ + /* Get the entry and convert the relative offset to the \ + absolute address. */ \ + add (%ebx,%ecx,4), %ebx; \ + /* We loaded the jump table and adjuested EDX. Go. */ \ + jmp *%ebx + + .section .gnu.linkonce.t.__i686.get_pc_thunk.bx,"ax",@progbits + .globl __i686.get_pc_thunk.bx + .hidden __i686.get_pc_thunk.bx + ALIGN (4) + .type __i686.get_pc_thunk.bx,@function +__i686.get_pc_thunk.bx: + movl (%esp), %ebx + ret +#else +# define ENTRANCE +# define RETURN_END ret +# define RETURN RETURN_END +# define PARMS 4 +# define JMPTBL(I, B) I + +/* Branch to an entry in a jump table. TABLE is a jump table with + absolute offsets. */ +# define BRANCH_TO_JMPTBL_ENTRY(TABLE) \ + jmp *TABLE(,%ecx,4) +#endif + + .section .text.sse2,"ax",@progbits + ALIGN (4) +ENTRY (sse2_memset32_atom) + ENTRANCE + + movl LEN(%esp), %ecx +#ifdef USE_AS_ANDROID + shr $2, %ecx +#endif +#ifdef USE_AS_BZERO32 + xor %eax, %eax +#else + mov DWDS(%esp), %eax + mov %eax, %edx +#endif + movl DEST(%esp), %edx + cmp $16, %ecx + jae L(16dbwordsormore) + +L(write_less16dbwords): + lea (%edx, %ecx, 4), %edx + BRANCH_TO_JMPTBL_ENTRY (L(table_less16dbwords)) + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_less16dbwords): + .int JMPTBL (L(write_0dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_1dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_2dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_3dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_4dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_5dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_6dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_7dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_8dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_9dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_10dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_11dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_12dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_13dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_14dbwords), L(table_less16dbwords)) + .int JMPTBL (L(write_15dbwords), L(table_less16dbwords)) + .popsection + + ALIGN (4) +L(write_15dbwords): + movl %eax, -60(%edx) +L(write_14dbwords): + movl %eax, -56(%edx) +L(write_13dbwords): + movl %eax, -52(%edx) +L(write_12dbwords): + movl %eax, -48(%edx) +L(write_11dbwords): + movl %eax, -44(%edx) +L(write_10dbwords): + movl %eax, -40(%edx) +L(write_9dbwords): + movl %eax, -36(%edx) +L(write_8dbwords): + movl %eax, -32(%edx) +L(write_7dbwords): + movl %eax, -28(%edx) +L(write_6dbwords): + movl %eax, -24(%edx) +L(write_5dbwords): + movl %eax, -20(%edx) +L(write_4dbwords): + movl %eax, -16(%edx) +L(write_3dbwords): + movl %eax, -12(%edx) +L(write_2dbwords): + movl %eax, -8(%edx) +L(write_1dbwords): + movl %eax, -4(%edx) +L(write_0dbwords): + SETRTNVAL + RETURN + + ALIGN (4) +L(16dbwordsormore): + test $3, %edx + jz L(aligned4bytes) + mov %eax, (%edx) + mov %eax, -4(%edx, %ecx, 4) + sub $1, %ecx + rol $24, %eax + add $1, %edx + test $3, %edx + jz L(aligned4bytes) + ror $8, %eax + add $1, %edx + test $3, %edx + jz L(aligned4bytes) + ror $8, %eax + add $1, %edx +L(aligned4bytes): + shl $2, %ecx + +#ifdef USE_AS_BZERO32 + pxor %xmm0, %xmm0 +#else + movd %eax, %xmm0 + pshufd $0, %xmm0, %xmm0 +#endif + testl $0xf, %edx + jz L(aligned_16) +/* ECX > 32 and EDX is not 16 byte aligned. */ +L(not_aligned_16): + movdqu %xmm0, (%edx) + movl %edx, %eax + and $-16, %edx + add $16, %edx + sub %edx, %eax + add %eax, %ecx + movd %xmm0, %eax + ALIGN (4) +L(aligned_16): + cmp $128, %ecx + jae L(128bytesormore) + +L(aligned_16_less128bytes): + add %ecx, %edx + shr $2, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + ALIGN (4) +L(128bytesormore): +#ifdef SHARED_CACHE_SIZE + PUSH (%ebx) + mov $SHARED_CACHE_SIZE, %ebx +#else +# ifdef SHARED + call __i686.get_pc_thunk.bx + add $_GLOBAL_OFFSET_TABLE_, %ebx + mov __x86_shared_cache_size@GOTOFF(%ebx), %ebx +# else + PUSH (%ebx) + mov __x86_shared_cache_size, %ebx +# endif +#endif + cmp %ebx, %ecx + jae L(128bytesormore_nt_start) + +#ifdef DATA_CACHE_SIZE + POP (%ebx) +# define RESTORE_EBX_STATE CFI_PUSH (%ebx) + cmp $DATA_CACHE_SIZE, %ecx +#else +# ifdef SHARED +# define RESTORE_EBX_STATE + call __i686.get_pc_thunk.bx + add $_GLOBAL_OFFSET_TABLE_, %ebx + cmp __x86_data_cache_size@GOTOFF(%ebx), %ecx +# else + POP (%ebx) +# define RESTORE_EBX_STATE CFI_PUSH (%ebx) + cmp __x86_data_cache_size, %ecx +# endif +#endif + + jae L(128bytes_L2_normal) + subl $128, %ecx +L(128bytesormore_normal): + sub $128, %ecx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + lea 128(%edx), %edx + jb L(128bytesless_normal) + + + sub $128, %ecx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + lea 128(%edx), %edx + jae L(128bytesormore_normal) + +L(128bytesless_normal): + lea 128(%ecx), %ecx + add %ecx, %edx + shr $2, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + ALIGN (4) +L(128bytes_L2_normal): + prefetcht0 0x380(%edx) + prefetcht0 0x3c0(%edx) + sub $128, %ecx + movdqa %xmm0, (%edx) + movaps %xmm0, 0x10(%edx) + movaps %xmm0, 0x20(%edx) + movaps %xmm0, 0x30(%edx) + movaps %xmm0, 0x40(%edx) + movaps %xmm0, 0x50(%edx) + movaps %xmm0, 0x60(%edx) + movaps %xmm0, 0x70(%edx) + add $128, %edx + cmp $128, %ecx + jae L(128bytes_L2_normal) + +L(128bytesless_L2_normal): + add %ecx, %edx + shr $2, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + RESTORE_EBX_STATE +L(128bytesormore_nt_start): + sub %ebx, %ecx + mov %ebx, %eax + and $0x7f, %eax + add %eax, %ecx + movd %xmm0, %eax + ALIGN (4) +L(128bytesormore_shared_cache_loop): + prefetcht0 0x3c0(%edx) + prefetcht0 0x380(%edx) + sub $0x80, %ebx + movdqa %xmm0, (%edx) + movdqa %xmm0, 0x10(%edx) + movdqa %xmm0, 0x20(%edx) + movdqa %xmm0, 0x30(%edx) + movdqa %xmm0, 0x40(%edx) + movdqa %xmm0, 0x50(%edx) + movdqa %xmm0, 0x60(%edx) + movdqa %xmm0, 0x70(%edx) + add $0x80, %edx + cmp $0x80, %ebx + jae L(128bytesormore_shared_cache_loop) + cmp $0x80, %ecx + jb L(shared_cache_loop_end) + + ALIGN (4) +L(128bytesormore_nt): + sub $0x80, %ecx + movntdq %xmm0, (%edx) + movntdq %xmm0, 0x10(%edx) + movntdq %xmm0, 0x20(%edx) + movntdq %xmm0, 0x30(%edx) + movntdq %xmm0, 0x40(%edx) + movntdq %xmm0, 0x50(%edx) + movntdq %xmm0, 0x60(%edx) + movntdq %xmm0, 0x70(%edx) + add $0x80, %edx + cmp $0x80, %ecx + jae L(128bytesormore_nt) + sfence +L(shared_cache_loop_end): +#if defined DATA_CACHE_SIZE || !defined SHARED + POP (%ebx) +#endif + add %ecx, %edx + shr $2, %ecx + BRANCH_TO_JMPTBL_ENTRY (L(table_16_128bytes)) + + .pushsection .rodata.sse2,"a",@progbits + ALIGN (2) +L(table_16_128bytes): + .int JMPTBL (L(aligned_16_0bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_4bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_8bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_12bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_16bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_20bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_24bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_28bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_32bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_36bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_40bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_44bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_48bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_52bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_56bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_60bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_64bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_68bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_72bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_76bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_80bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_84bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_88bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_92bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_96bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_100bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_104bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_108bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_112bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_116bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_120bytes), L(table_16_128bytes)) + .int JMPTBL (L(aligned_16_124bytes), L(table_16_128bytes)) + .popsection + + ALIGN (4) +L(aligned_16_112bytes): + movdqa %xmm0, -112(%edx) +L(aligned_16_96bytes): + movdqa %xmm0, -96(%edx) +L(aligned_16_80bytes): + movdqa %xmm0, -80(%edx) +L(aligned_16_64bytes): + movdqa %xmm0, -64(%edx) +L(aligned_16_48bytes): + movdqa %xmm0, -48(%edx) +L(aligned_16_32bytes): + movdqa %xmm0, -32(%edx) +L(aligned_16_16bytes): + movdqa %xmm0, -16(%edx) +L(aligned_16_0bytes): + SETRTNVAL + RETURN + + ALIGN (4) +L(aligned_16_116bytes): + movdqa %xmm0, -116(%edx) +L(aligned_16_100bytes): + movdqa %xmm0, -100(%edx) +L(aligned_16_84bytes): + movdqa %xmm0, -84(%edx) +L(aligned_16_68bytes): + movdqa %xmm0, -68(%edx) +L(aligned_16_52bytes): + movdqa %xmm0, -52(%edx) +L(aligned_16_36bytes): + movdqa %xmm0, -36(%edx) +L(aligned_16_20bytes): + movdqa %xmm0, -20(%edx) +L(aligned_16_4bytes): + movl %eax, -4(%edx) + SETRTNVAL + RETURN + + ALIGN (4) +L(aligned_16_120bytes): + movdqa %xmm0, -120(%edx) +L(aligned_16_104bytes): + movdqa %xmm0, -104(%edx) +L(aligned_16_88bytes): + movdqa %xmm0, -88(%edx) +L(aligned_16_72bytes): + movdqa %xmm0, -72(%edx) +L(aligned_16_56bytes): + movdqa %xmm0, -56(%edx) +L(aligned_16_40bytes): + movdqa %xmm0, -40(%edx) +L(aligned_16_24bytes): + movdqa %xmm0, -24(%edx) +L(aligned_16_8bytes): + movq %xmm0, -8(%edx) + SETRTNVAL + RETURN + + ALIGN (4) +L(aligned_16_124bytes): + movdqa %xmm0, -124(%edx) +L(aligned_16_108bytes): + movdqa %xmm0, -108(%edx) +L(aligned_16_92bytes): + movdqa %xmm0, -92(%edx) +L(aligned_16_76bytes): + movdqa %xmm0, -76(%edx) +L(aligned_16_60bytes): + movdqa %xmm0, -60(%edx) +L(aligned_16_44bytes): + movdqa %xmm0, -44(%edx) +L(aligned_16_28bytes): + movdqa %xmm0, -28(%edx) +L(aligned_16_12bytes): + movq %xmm0, -12(%edx) + movl %eax, -4(%edx) + SETRTNVAL + RETURN + +END (sse2_memset32_atom) diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c new file mode 100644 index 0000000..8b71f87 --- /dev/null +++ b/libcutils/ashmem-dev.c @@ -0,0 +1,90 @@ +/* + * 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. + */ + +/* + * Implementation of the user-space ashmem API for devices, which have our + * ashmem-enabled kernel. See ashmem-sim.c for the "fake" tmp-based version, + * used by the simulator. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ASHMEM_DEVICE "/dev/ashmem" + +/* + * ashmem_create_region - creates a new ashmem region and returns the file + * descriptor, or <0 on error + * + * `name' is an optional label to give the region (visible in /proc/pid/maps) + * `size' is the size of the region, in page-aligned bytes + */ +int ashmem_create_region(const char *name, size_t size) +{ + int fd, ret; + + fd = open(ASHMEM_DEVICE, O_RDWR); + if (fd < 0) + return fd; + + if (name) { + char buf[ASHMEM_NAME_LEN]; + + strlcpy(buf, name, sizeof(buf)); + ret = ioctl(fd, ASHMEM_SET_NAME, buf); + if (ret < 0) + goto error; + } + + ret = ioctl(fd, ASHMEM_SET_SIZE, size); + if (ret < 0) + goto error; + + return fd; + +error: + close(fd); + return ret; +} + +int ashmem_set_prot_region(int fd, int prot) +{ + return 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); +} + +int ashmem_unpin_region(int fd, size_t offset, size_t len) +{ + struct ashmem_pin pin = { offset, len }; + return ioctl(fd, ASHMEM_UNPIN, &pin); +} + +int ashmem_get_size_region(int fd) +{ + return ioctl(fd, ASHMEM_GET_SIZE, NULL); +} diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c new file mode 100644 index 0000000..f03e130 --- /dev/null +++ b/libcutils/ashmem-host.c @@ -0,0 +1,114 @@ +/* + * 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. + */ + +/* + * Implementation of the user-space ashmem API for the simulator, which lacks + * an ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int ashmem_create_region(const char *ignored, size_t size) +{ + static const char txt[] = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + char name[64]; + unsigned int retries = 0; + pid_t pid = getpid(); + int fd; + + srand(time(NULL) + pid); + +retry: + /* not beautiful, its just wolf-like loop unrolling */ + snprintf(name, sizeof(name), "/tmp/android-ashmem-%d-%c%c%c%c%c%c%c%c", + pid, + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))], + txt[(int) ((sizeof(txt) - 1) * (rand() / (RAND_MAX + 1.0)))]); + + /* open O_EXCL & O_CREAT: we are either the sole owner or we fail */ + fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd == -1) { + /* unlikely, but if we failed because `name' exists, retry */ + if (errno == EEXIST && ++retries < 6) + goto retry; + return -1; + } + + /* truncate the file to `len' bytes */ + if (ftruncate(fd, size) == -1) + goto error; + + if (unlink(name) == -1) + goto error; + + return fd; +error: + close(fd); + return -1; +} + +int ashmem_set_prot_region(int fd, int prot) +{ + return 0; +} + +int ashmem_pin_region(int fd, size_t offset, size_t len) +{ + return ASHMEM_NOT_PURGED; +} + +int ashmem_unpin_region(int fd, size_t offset, size_t len) +{ + return ASHMEM_IS_UNPINNED; +} + +int ashmem_get_size_region(int fd) +{ + struct stat buf; + int result; + + result = fstat(fd, &buf); + if (result == -1) { + return -1; + } + + // 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; + } + + return (int)buf.st_size; // TODO: care about overflow (> 2GB file)? +} diff --git a/libcutils/atomic.c b/libcutils/atomic.c new file mode 100644 index 0000000..1484ef8 --- /dev/null +++ b/libcutils/atomic.c @@ -0,0 +1,19 @@ +/* + * 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 ANDROID_ATOMIC_INLINE + +#include diff --git a/libcutils/config_utils.c b/libcutils/config_utils.c new file mode 100644 index 0000000..fc5ca78 --- /dev/null +++ b/libcutils/config_utils.c @@ -0,0 +1,329 @@ +/* + * 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 +#include +#include +#include +#include + +#include +#include + +cnode* config_node(const char *name, const char *value) +{ + cnode *node; + + node = calloc(sizeof(cnode), 1); + if(node) { + node->name = name ? name : ""; + node->value = value ? value : ""; + } + + return node; +} + +cnode* config_find(cnode *root, const char *name) +{ + cnode *node, *match = NULL; + + /* we walk the whole list, as we need to return the last (newest) entry */ + for(node = root->first_child; node; node = node->next) + if(!strcmp(node->name, name)) + match = node; + + return match; +} + +static cnode* _config_create(cnode *root, const char *name) +{ + cnode *node; + + node = config_node(name, NULL); + + if(root->last_child) + root->last_child->next = node; + else + root->first_child = node; + + root->last_child = node; + + return node; +} + +int config_bool(cnode *root, const char *name, int _default) +{ + cnode *node; + + node = config_find(root, name); + if(!node) + return _default; + + switch(node->value[0]) { + case 'y': + case 'Y': + case '1': + return 1; + default: + return 0; + } +} + +const char* config_str(cnode *root, const char *name, const char *_default) +{ + cnode *node; + + node = config_find(root, name); + if(!node) + return _default; + return node->value; +} + +void config_set(cnode *root, const char *name, const char *value) +{ + cnode *node; + + node = config_find(root, name); + if(node) + node->value = value; + else { + node = _config_create(root, name); + node->value = value; + } +} + +#define T_EOF 0 +#define T_TEXT 1 +#define T_DOT 2 +#define T_OBRACE 3 +#define T_CBRACE 4 + +typedef struct +{ + char *data; + char *text; + int len; + char next; +} cstate; + +static int _lex(cstate *cs, int value) +{ + char c; + char *s; + char *data; + + data = cs->data; + + if(cs->next != 0) { + c = cs->next; + cs->next = 0; + goto got_c; + } + +restart: + for(;;) { + c = *data++; + got_c: + if(isspace(c)) + continue; + + switch(c) { + case 0: + return T_EOF; + + case '#': + for(;;) { + switch(*data) { + case 0: + cs->data = data; + return T_EOF; + case '\n': + cs->data = data + 1; + goto restart; + default: + data++; + } + } + break; + + case '.': + cs->data = data; + return T_DOT; + + case '{': + cs->data = data; + return T_OBRACE; + + case '}': + cs->data = data; + return T_CBRACE; + + default: + s = data - 1; + + if(value) { + for(;;) { + if(*data == 0) { + cs->data = data; + break; + } + if(*data == '\n') { + cs->data = data + 1; + *data-- = 0; + break; + } + data++; + } + + /* strip trailing whitespace */ + while(data > s){ + if(!isspace(*data)) break; + *data-- = 0; + } + + goto got_text; + } else { + for(;;) { + if(isspace(*data)) { + *data = 0; + cs->data = data + 1; + goto got_text; + } + switch(*data) { + case 0: + cs->data = data; + goto got_text; + case '.': + case '{': + case '}': + cs->next = *data; + *data = 0; + cs->data = data + 1; + goto got_text; + default: + data++; + } + } + } + } + } + +got_text: + cs->text = s; + return T_TEXT; +} + +#if 0 +char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" }; + +static int lex(cstate *cs, int value) +{ + int tok = _lex(cs, value); + printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok], + tok == T_TEXT ? cs->text : ""); + return tok; +} +#else +#define lex(cs,v) _lex(cs,v) +#endif + +static int parse_expr(cstate *cs, cnode *node); + +static int parse_block(cstate *cs, cnode *node) +{ + for(;;){ + switch(lex(cs, 0)){ + case T_TEXT: + if(parse_expr(cs, node)) return -1; + continue; + + case T_CBRACE: + return 0; + + default: + return -1; + } + } +} + +static int parse_expr(cstate *cs, cnode *root) +{ + cnode *node; + + /* last token was T_TEXT */ + node = config_find(root, cs->text); + if(!node || *node->value) + node = _config_create(root, cs->text); + + for(;;) { + switch(lex(cs, 1)) { + case T_DOT: + if(lex(cs, 0) != T_TEXT) + return -1; + node = _config_create(node, cs->text); + continue; + + case T_TEXT: + node->value = cs->text; + return 0; + + case T_OBRACE: + return parse_block(cs, node); + + default: + return -1; + } + } +} + +void config_load(cnode *root, char *data) +{ + if(data != 0) { + cstate cs; + cs.data = data; + cs.next = 0; + + for(;;) { + switch(lex(&cs, 0)) { + case T_TEXT: + if(parse_expr(&cs, root)) + return; + break; + default: + return; + } + } + } +} + +void config_load_file(cnode *root, const char *fn) +{ + char *data; + data = load_file(fn, 0); + config_load(root, data); +} + +void config_free(cnode *root) +{ + cnode *cur = root->first_child; + + while (cur) { + cnode *prev = cur; + config_free(cur); + cur = cur->next; + free(prev); + } +} diff --git a/libcutils/cpu_info.c b/libcutils/cpu_info.c new file mode 100644 index 0000000..23dda8a --- /dev/null +++ b/libcutils/cpu_info.c @@ -0,0 +1,83 @@ +/* libs/cutils/cpu_info.c +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include + +// we cache the serial number here. +// this is also used as a fgets() line buffer when we are reading /proc/cpuinfo +static char serial_number[100] = { 0 }; + +extern const char* get_cpu_serial_number(void) +{ + if (serial_number[0] == 0) + { + FILE* file; + char* chp, *end; + char* whitespace; + int length; + + // read serial number from /proc/cpuinfo + file = fopen("proc/cpuinfo", "r"); + if (! file) + return NULL; + + while ((chp = fgets(serial_number, sizeof(serial_number), file)) != NULL) + { + // look for something like "Serial : 999206122a03591c" + + if (strncmp(chp, "Serial", 6) != 0) + continue; + + chp = strchr(chp, ':'); + if (!chp) + continue; + + // skip colon and whitespace + while ( *(++chp) == ' ') {} + + // truncate trailing whitespace + end = chp; + while (*end && *end != ' ' && *end != '\t' && *end != '\n' && *end != '\r') + ++end; + *end = 0; + + whitespace = strchr(chp, ' '); + if (whitespace) + *whitespace = 0; + whitespace = strchr(chp, '\t'); + if (whitespace) + *whitespace = 0; + whitespace = strchr(chp, '\r'); + if (whitespace) + *whitespace = 0; + whitespace = strchr(chp, '\n'); + if (whitespace) + *whitespace = 0; + + // shift serial number to beginning of the buffer + memmove(serial_number, chp, strlen(chp) + 1); + break; + } + + fclose(file); + } + + return (serial_number[0] ? serial_number : NULL); +} diff --git a/libcutils/debugger.c b/libcutils/debugger.c new file mode 100644 index 0000000..9425006 --- /dev/null +++ b/libcutils/debugger.c @@ -0,0 +1,87 @@ +/* + * 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 +#include + +#include +#include + +int dump_tombstone(pid_t tid, char* pathbuf, size_t pathlen) { + int s = socket_local_client(DEBUGGER_SOCKET_NAME, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + if (s < 0) { + return -1; + } + + debugger_msg_t msg; + msg.tid = tid; + msg.action = DEBUGGER_ACTION_DUMP_TOMBSTONE; + + int result = 0; + if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) { + result = -1; + } else { + char ack; + if (TEMP_FAILURE_RETRY(read(s, &ack, 1)) != 1) { + result = -1; + } else { + if (pathbuf && pathlen) { + ssize_t n = TEMP_FAILURE_RETRY(read(s, pathbuf, pathlen - 1)); + if (n <= 0) { + result = -1; + } else { + pathbuf[n] = '\0'; + } + } + } + } + TEMP_FAILURE_RETRY(close(s)); + return result; +} + +int dump_backtrace_to_file(pid_t tid, int fd) { + int s = socket_local_client(DEBUGGER_SOCKET_NAME, + ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); + if (s < 0) { + return -1; + } + + debugger_msg_t msg; + msg.tid = tid; + msg.action = DEBUGGER_ACTION_DUMP_BACKTRACE; + + int result = 0; + if (TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))) != sizeof(msg)) { + result = -1; + } else { + char ack; + if (TEMP_FAILURE_RETRY(read(s, &ack, 1)) != 1) { + result = -1; + } else { + char buffer[4096]; + ssize_t n; + while ((n = TEMP_FAILURE_RETRY(read(s, buffer, sizeof(buffer)))) > 0) { + if (TEMP_FAILURE_RETRY(write(fd, buffer, n)) != n) { + result = -1; + break; + } + } + } + } + TEMP_FAILURE_RETRY(close(s)); + return result; +} diff --git a/libcutils/dir_hash.c b/libcutils/dir_hash.c new file mode 100644 index 0000000..be14af6 --- /dev/null +++ b/libcutils/dir_hash.c @@ -0,0 +1,334 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +/** + * Copies, if it fits within max_output_string bytes, into output_string + * a hash of the contents, size, permissions, uid, and gid of the file + * specified by path, using the specified algorithm. Returns the length + * of the output string, or a negative number if the buffer is too short. + */ +int get_file_hash(HashAlgorithm algorithm, const char *path, + char *output_string, size_t max_output_string) { + SHA1_CTX context; + struct stat sb; + unsigned char md[SHA1_DIGEST_LENGTH]; + int used; + size_t n; + + if (algorithm != SHA_1) { + errno = EINVAL; + return -1; + } + + if (stat(path, &sb) != 0) { + return -1; + } + + if (S_ISLNK(sb.st_mode)) { + char buf[PATH_MAX]; + int len; + + len = readlink(path, buf, sizeof(buf)); + if (len < 0) { + return -1; + } + + SHA1Init(&context); + SHA1Update(&context, (unsigned char *) buf, len); + SHA1Final(md, &context); + } else if (S_ISREG(sb.st_mode)) { + char buf[10000]; + FILE *f = fopen(path, "rb"); + int len; + + if (f == NULL) { + return -1; + } + + SHA1Init(&context); + + while ((len = fread(buf, 1, sizeof(buf), f)) > 0) { + SHA1Update(&context, (unsigned char *) buf, len); + } + + if (ferror(f)) { + fclose(f); + return -1; + } + + fclose(f); + SHA1Final(md, &context); + } + + if (S_ISLNK(sb.st_mode) || S_ISREG(sb.st_mode)) { + used = b64_ntop(md, SHA1_DIGEST_LENGTH, + output_string, max_output_string); + if (used < 0) { + errno = ENOSPC; + return -1; + } + + n = snprintf(output_string + used, max_output_string - used, + " %d 0%o %d %d", (int) sb.st_size, sb.st_mode, + (int) sb.st_uid, (int) sb.st_gid); + } else { + n = snprintf(output_string, max_output_string, + "- - 0%o %d %d", sb.st_mode, + (int) sb.st_uid, (int) sb.st_gid); + } + + if (n >= max_output_string - used) { + errno = ENOSPC; + return -(used + n); + } + + return used + n; +} + +struct list { + char *name; + struct list *next; +}; + +static int cmp(const void *a, const void *b) { + struct list *const *ra = a; + struct list *const *rb = b; + + return strcmp((*ra)->name, (*rb)->name); +} + +static int recurse(HashAlgorithm algorithm, const char *directory_path, + struct list **out) { + struct list *list = NULL; + struct list *f; + + struct dirent *de; + DIR *d = opendir(directory_path); + + if (d == NULL) { + return -1; + } + + while ((de = readdir(d)) != NULL) { + if (strcmp(de->d_name, ".") == 0) { + continue; + } + if (strcmp(de->d_name, "..") == 0) { + continue; + } + + char *name = malloc(strlen(de->d_name) + 1); + struct list *node = malloc(sizeof(struct list)); + + if (name == NULL || node == NULL) { + struct list *next; + for (f = list; f != NULL; f = next) { + next = f->next; + free(f->name); + free(f); + } + + free(name); + free(node); + return -1; + } + + strcpy(name, de->d_name); + + node->name = name; + node->next = list; + list = node; + } + + closedir(d); + + for (f = list; f != NULL; f = f->next) { + struct stat sb; + char *name; + char outstr[NAME_MAX + 100]; + char *keep; + struct list *res; + + name = malloc(strlen(f->name) + strlen(directory_path) + 2); + if (name == NULL) { + struct list *next; + for (f = list; f != NULL; f = f->next) { + next = f->next; + free(f->name); + free(f); + } + for (f = *out; f != NULL; f = f->next) { + next = f->next; + free(f->name); + free(f); + } + *out = NULL; + return -1; + } + + sprintf(name, "%s/%s", directory_path, f->name); + + int len = get_file_hash(algorithm, name, + outstr, sizeof(outstr)); + if (len < 0) { + // should not happen + return -1; + } + + keep = malloc(len + strlen(name) + 3); + res = malloc(sizeof(struct list)); + + if (keep == NULL || res == NULL) { + struct list *next; + for (f = list; f != NULL; f = f->next) { + next = f->next; + free(f->name); + free(f); + } + for (f = *out; f != NULL; f = f->next) { + next = f->next; + free(f->name); + free(f); + } + *out = NULL; + + free(keep); + free(res); + return -1; + } + + sprintf(keep, "%s %s\n", name, outstr); + + res->name = keep; + res->next = *out; + *out = res; + + if ((stat(name, &sb) == 0) && S_ISDIR(sb.st_mode)) { + if (recurse(algorithm, name, out) < 0) { + struct list *next; + for (f = list; f != NULL; f = next) { + next = f->next; + free(f->name); + free(f); + } + + return -1; + } + } + } + + struct list *next; + for (f = list; f != NULL; f = next) { + next = f->next; + + free(f->name); + free(f); + } +} + +/** + * Allocates a string containing the names and hashes of all files recursively + * reached under the specified directory_path, using the specified algorithm. + * The string is returned as *output_string; the return value is the length + * of the string, or a negative number if there was a failure. + */ +int get_recursive_hash_manifest(HashAlgorithm algorithm, + const char *directory_path, + char **output_string) { + struct list *out = NULL; + struct list *r; + struct list **list; + int count = 0; + int len = 0; + int retlen = 0; + int i; + char *buf; + + if (recurse(algorithm, directory_path, &out) < 0) { + return -1; + } + + for (r = out; r != NULL; r = r->next) { + count++; + len += strlen(r->name); + } + + list = malloc(count * sizeof(struct list *)); + if (list == NULL) { + struct list *next; + for (r = out; r != NULL; r = next) { + next = r->next; + free(r->name); + free(r); + } + return -1; + } + + count = 0; + for (r = out; r != NULL; r = r->next) { + list[count++] = r; + } + + qsort(list, count, sizeof(struct list *), cmp); + + buf = malloc(len + 1); + if (buf == NULL) { + struct list *next; + for (r = out; r != NULL; r = next) { + next = r->next; + free(r->name); + free(r); + } + free(list); + return -1; + } + + for (i = 0; i < count; i++) { + int n = strlen(list[i]->name); + + strcpy(buf + retlen, list[i]->name); + retlen += n; + } + + free(list); + + struct list *next; + for (r = out; r != NULL; r = next) { + next = r->next; + + free(r->name); + free(r); + } + + *output_string = buf; + return retlen; +} diff --git a/libcutils/dlmalloc_stubs.c b/libcutils/dlmalloc_stubs.c new file mode 100644 index 0000000..c327a55 --- /dev/null +++ b/libcutils/dlmalloc_stubs.c @@ -0,0 +1,35 @@ +/* + * 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 "../../../bionic/libc/bionic/dlmalloc.h" +#include "cutils/log.h" + +/* + * Stubs for functions defined in bionic/libc/bionic/dlmalloc.c. These + * are used in host builds, as the host libc will not contain these + * functions. + */ +void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*), + void* arg) +{ + ALOGW("Called host unimplemented stub: dlmalloc_inspect_all"); +} + +int dlmalloc_trim(size_t unused) +{ + ALOGW("Called host unimplemented stub: dlmalloc_trim"); + return 0; +} diff --git a/libcutils/fs.c b/libcutils/fs.c new file mode 100644 index 0000000..286a8eb --- /dev/null +++ b/libcutils/fs.c @@ -0,0 +1,237 @@ +/* + * 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 "cutils" + +/* These defines are only needed because prebuilt headers are out of date */ +#define __USE_XOPEN2K8 1 +#define _ATFILE_SOURCE 1 +#define _GNU_SOURCE 1 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) { + // Check if path needs to be created + struct stat sb; + if (TEMP_FAILURE_RETRY(lstat(path, &sb)) == -1) { + if (errno == ENOENT) { + goto create; + } else { + ALOGE("Failed to lstat(%s): %s", path, strerror(errno)); + return -1; + } + } + + // Exists, verify status + if (!S_ISDIR(sb.st_mode)) { + ALOGE("Not a directory: %s", 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)); + return -1; + } + } + +fixup: + if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) { + ALOGE("Failed to chmod(%s, %d): %s", path, mode, strerror(errno)); + return -1; + } + if (TEMP_FAILURE_RETRY(chown(path, uid, gid)) == -1) { + ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno)); + return -1; + } + + return 0; +} + +int fs_read_atomic_int(const char* path, int* out_value) { + int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY)); + if (fd == -1) { + ALOGE("Failed to read %s: %s", path, strerror(errno)); + return -1; + } + + char buf[BUF_SIZE]; + if (TEMP_FAILURE_RETRY(read(fd, buf, BUF_SIZE)) == -1) { + ALOGE("Failed to read %s: %s", path, strerror(errno)); + goto fail; + } + if (sscanf(buf, "%d", out_value) != 1) { + ALOGE("Failed to parse %s: %s", path, strerror(errno)); + goto fail; + } + close(fd); + return 0; + +fail: + close(fd); + *out_value = -1; + return -1; +} + +int fs_write_atomic_int(const char* path, int value) { + char temp[PATH_MAX]; + if (snprintf(temp, PATH_MAX, "%s.XXXXXX", path) >= PATH_MAX) { + ALOGE("Path too long"); + return -1; + } + + int fd = TEMP_FAILURE_RETRY(mkstemp(temp)); + if (fd == -1) { + ALOGE("Failed to open %s: %s", temp, strerror(errno)); + return -1; + } + + char buf[BUF_SIZE]; + int len = snprintf(buf, BUF_SIZE, "%d", value) + 1; + if (len > BUF_SIZE) { + ALOGE("Value %d too large: %s", value, strerror(errno)); + goto fail; + } + if (TEMP_FAILURE_RETRY(write(fd, buf, len)) < len) { + ALOGE("Failed to write %s: %s", temp, strerror(errno)); + goto fail; + } + if (close(fd) == -1) { + ALOGE("Failed to close %s: %s", temp, strerror(errno)); + goto fail_closed; + } + + if (rename(temp, path) == -1) { + ALOGE("Failed to rename %s to %s: %s", temp, path, strerror(errno)); + goto fail_closed; + } + + return 0; + +fail: + close(fd); +fail_closed: + unlink(temp); + return -1; +} + +#ifndef __APPLE__ + +int fs_mkdirs(const char* path, mode_t mode) { + int res = 0; + int fd = 0; + struct stat sb; + char* buf = strdup(path); + + if (*buf != '/') { + ALOGE("Relative paths are not allowed: %s", buf); + res = -EINVAL; + goto done; + } + + if ((fd = open("/", 0)) == -1) { + ALOGE("Failed to open(/): %s", strerror(errno)); + res = -errno; + goto done; + } + + char* segment = buf + 1; + char* p = segment; + while (*p != '\0') { + if (*p == '/') { + *p = '\0'; + + if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) { + ALOGE("Invalid path: %s", buf); + res = -EINVAL; + goto done_close; + } + + if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) { + if (errno == ENOENT) { + /* Nothing there yet; let's create it! */ + if (mkdirat(fd, segment, mode) != 0) { + if (errno == EEXIST) { + /* We raced with someone; ignore */ + } else { + ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno)); + res = -errno; + goto done_close; + } + } + } else { + ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno)); + res = -errno; + goto done_close; + } + } else { + if (S_ISLNK(sb.st_mode)) { + ALOGE("Symbolic links are not allowed: %s", buf); + res = -ELOOP; + goto done_close; + } + if (!S_ISDIR(sb.st_mode)) { + ALOGE("Existing segment not a directory: %s", buf); + res = -ENOTDIR; + goto done_close; + } + } + + /* Yay, segment is ready for us to step into */ + int next_fd; + if ((next_fd = openat(fd, segment, 0)) == -1) { + ALOGE("Failed to openat(%s): %s", buf, strerror(errno)); + res = -errno; + goto done_close; + } + + close(fd); + fd = next_fd; + + *p = '/'; + segment = p + 1; + } + p++; + } + +done_close: + close(fd); +done: + free(buf); + return res; +} + +#endif diff --git a/libcutils/hashmap.c b/libcutils/hashmap.c new file mode 100644 index 0000000..65539ea --- /dev/null +++ b/libcutils/hashmap.c @@ -0,0 +1,351 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +typedef struct Entry Entry; +struct Entry { + void* key; + int hash; + void* value; + Entry* next; +}; + +struct Hashmap { + Entry** buckets; + size_t bucketCount; + int (*hash)(void* key); + bool (*equals)(void* keyA, void* keyB); + mutex_t lock; + size_t size; +}; + +Hashmap* hashmapCreate(size_t initialCapacity, + int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) { + assert(hash != NULL); + assert(equals != NULL); + + Hashmap* map = malloc(sizeof(Hashmap)); + if (map == NULL) { + return NULL; + } + + // 0.75 load factor. + size_t minimumBucketCount = initialCapacity * 4 / 3; + map->bucketCount = 1; + while (map->bucketCount <= minimumBucketCount) { + // Bucket count must be power of 2. + map->bucketCount <<= 1; + } + + map->buckets = calloc(map->bucketCount, sizeof(Entry*)); + if (map->buckets == NULL) { + free(map); + return NULL; + } + + map->size = 0; + + map->hash = hash; + map->equals = equals; + + mutex_init(&map->lock); + + return map; +} + +/** + * Hashes the given key. + */ +static inline int hashKey(Hashmap* map, void* key) { + int h = map->hash(key); + + // We apply this secondary hashing discovered by Doug Lea to defend + // against bad hashes. + h += ~(h << 9); + h ^= (((unsigned int) h) >> 14); + h += (h << 4); + h ^= (((unsigned int) h) >> 10); + + return h; +} + +size_t hashmapSize(Hashmap* map) { + return map->size; +} + +static inline size_t calculateIndex(size_t bucketCount, int hash) { + return ((size_t) hash) & (bucketCount - 1); +} + +static void expandIfNecessary(Hashmap* map) { + // If the load factor exceeds 0.75... + if (map->size > (map->bucketCount * 3 / 4)) { + // Start off with a 0.33 load factor. + size_t newBucketCount = map->bucketCount << 1; + Entry** newBuckets = calloc(newBucketCount, sizeof(Entry*)); + if (newBuckets == NULL) { + // Abort expansion. + return; + } + + // Move over existing entries. + size_t i; + for (i = 0; i < map->bucketCount; i++) { + Entry* entry = map->buckets[i]; + while (entry != NULL) { + Entry* next = entry->next; + size_t index = calculateIndex(newBucketCount, entry->hash); + entry->next = newBuckets[index]; + newBuckets[index] = entry; + entry = next; + } + } + + // Copy over internals. + free(map->buckets); + map->buckets = newBuckets; + map->bucketCount = newBucketCount; + } +} + +void hashmapLock(Hashmap* map) { + mutex_lock(&map->lock); +} + +void hashmapUnlock(Hashmap* map) { + mutex_unlock(&map->lock); +} + +void hashmapFree(Hashmap* map) { + size_t i; + for (i = 0; i < map->bucketCount; i++) { + Entry* entry = map->buckets[i]; + while (entry != NULL) { + Entry* next = entry->next; + free(entry); + entry = next; + } + } + free(map->buckets); + mutex_destroy(&map->lock); + free(map); +} + +int hashmapHash(void* key, size_t keySize) { + int h = keySize; + char* data = (char*) key; + size_t i; + for (i = 0; i < keySize; i++) { + h = h * 31 + *data; + data++; + } + return h; +} + +static Entry* createEntry(void* key, int hash, void* value) { + Entry* entry = malloc(sizeof(Entry)); + if (entry == NULL) { + return NULL; + } + entry->key = key; + entry->hash = hash; + entry->value = value; + entry->next = NULL; + return entry; +} + +static inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB, + bool (*equals)(void*, void*)) { + if (keyA == keyB) { + return true; + } + if (hashA != hashB) { + return false; + } + return equals(keyA, keyB); +} + +void* hashmapPut(Hashmap* map, void* key, void* value) { + int hash = hashKey(map, key); + size_t index = calculateIndex(map->bucketCount, hash); + + Entry** p = &(map->buckets[index]); + while (true) { + Entry* current = *p; + + // Add a new entry. + if (current == NULL) { + *p = createEntry(key, hash, value); + if (*p == NULL) { + errno = ENOMEM; + return NULL; + } + map->size++; + expandIfNecessary(map); + return NULL; + } + + // Replace existing entry. + if (equalKeys(current->key, current->hash, key, hash, map->equals)) { + void* oldValue = current->value; + current->value = value; + return oldValue; + } + + // Move to next entry. + p = ¤t->next; + } +} + +void* hashmapGet(Hashmap* map, void* key) { + int hash = hashKey(map, key); + size_t index = calculateIndex(map->bucketCount, hash); + + Entry* entry = map->buckets[index]; + while (entry != NULL) { + if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) { + return entry->value; + } + entry = entry->next; + } + + return NULL; +} + +bool hashmapContainsKey(Hashmap* map, void* key) { + int hash = hashKey(map, key); + size_t index = calculateIndex(map->bucketCount, hash); + + Entry* entry = map->buckets[index]; + while (entry != NULL) { + if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) { + return true; + } + entry = entry->next; + } + + return false; +} + +void* hashmapMemoize(Hashmap* map, void* key, + void* (*initialValue)(void* key, void* context), void* context) { + int hash = hashKey(map, key); + size_t index = calculateIndex(map->bucketCount, hash); + + Entry** p = &(map->buckets[index]); + while (true) { + Entry* current = *p; + + // Add a new entry. + if (current == NULL) { + *p = createEntry(key, hash, NULL); + if (*p == NULL) { + errno = ENOMEM; + return NULL; + } + void* value = initialValue(key, context); + (*p)->value = value; + map->size++; + expandIfNecessary(map); + return value; + } + + // Return existing value. + if (equalKeys(current->key, current->hash, key, hash, map->equals)) { + return current->value; + } + + // Move to next entry. + p = ¤t->next; + } +} + +void* hashmapRemove(Hashmap* map, void* key) { + int hash = hashKey(map, key); + size_t index = calculateIndex(map->bucketCount, hash); + + // Pointer to the current entry. + Entry** p = &(map->buckets[index]); + Entry* current; + while ((current = *p) != NULL) { + if (equalKeys(current->key, current->hash, key, hash, map->equals)) { + void* value = current->value; + *p = current->next; + free(current); + map->size--; + return value; + } + + p = ¤t->next; + } + + return NULL; +} + +void hashmapForEach(Hashmap* map, + bool (*callback)(void* key, void* value, void* context), + void* context) { + size_t i; + for (i = 0; i < map->bucketCount; i++) { + Entry* entry = map->buckets[i]; + while (entry != NULL) { + Entry *next = entry->next; + if (!callback(entry->key, entry->value, context)) { + return; + } + entry = next; + } + } +} + +size_t hashmapCurrentCapacity(Hashmap* map) { + size_t bucketCount = map->bucketCount; + return bucketCount * 3 / 4; +} + +size_t hashmapCountCollisions(Hashmap* map) { + size_t collisions = 0; + size_t i; + for (i = 0; i < map->bucketCount; i++) { + Entry* entry = map->buckets[i]; + while (entry != NULL) { + if (entry->next != NULL) { + collisions++; + } + entry = entry->next; + } + } + return collisions; +} + +int hashmapIntHash(void* key) { + // Return the key value itself. + return *((int*) key); +} + +bool hashmapIntEquals(void* keyA, void* keyB) { + int a = *((int*) keyA); + int b = *((int*) keyB); + return a == b; +} diff --git a/libcutils/iosched_policy.c b/libcutils/iosched_policy.c new file mode 100644 index 0000000..f350f58 --- /dev/null +++ b/libcutils/iosched_policy.c @@ -0,0 +1,67 @@ + +/* libs/cutils/iosched_policy.c +** +** Copyright 2007, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SCHED_H + +#include + +extern int ioprio_set(int which, int who, int ioprio); + +enum { + WHO_PROCESS = 1, + WHO_PGRP, + WHO_USER, +}; + +#define CLASS_SHIFT 13 +#define IOPRIO_NORM 4 + +int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio) { +#ifdef HAVE_ANDROID_OS + if (ioprio_set(WHO_PROCESS, pid, ioprio | (clazz << CLASS_SHIFT))) { + return -1; + } +#endif + return 0; +} + +int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio) { +#ifdef HAVE_ANDROID_OS + int rc; + + if ((rc = ioprio_get(WHO_PROCESS, pid)) < 0) { + return -1; + } + + *clazz = (rc >> CLASS_SHIFT); + *ioprio = (rc & 0xff); +#else + *clazz = IoSchedClass_NONE; + *ioprio = 0; +#endif + return 0; +} + +#endif /* HAVE_SCHED_H */ diff --git a/libcutils/klog.c b/libcutils/klog.c new file mode 100644 index 0000000..d69fb10 --- /dev/null +++ b/libcutils/klog.c @@ -0,0 +1,66 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +static int klog_fd = -1; +static int klog_level = KLOG_DEFAULT_LEVEL; + +void klog_set_level(int level) { + klog_level = level; +} + +void klog_init(void) +{ + static const char *name = "/dev/__kmsg__"; + + if (klog_fd >= 0) return; /* Already initialized */ + + if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { + klog_fd = open(name, O_WRONLY); + if (klog_fd < 0) + return; + fcntl(klog_fd, F_SETFD, FD_CLOEXEC); + unlink(name); + } +} + +#define LOG_BUF_MAX 512 + +void klog_write(int level, const char *fmt, ...) +{ + char buf[LOG_BUF_MAX]; + va_list ap; + + if (level > klog_level) return; + if (klog_fd < 0) klog_init(); + if (klog_fd < 0) return; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_MAX, fmt, ap); + buf[LOG_BUF_MAX - 1] = 0; + va_end(ap); + write(klog_fd, buf, strlen(buf)); +} diff --git a/libcutils/list.c b/libcutils/list.c new file mode 100644 index 0000000..e13452d --- /dev/null +++ b/libcutils/list.c @@ -0,0 +1,37 @@ +/* + * 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 + +void list_init(struct listnode *node) +{ + node->next = node; + node->prev = node; +} + +void list_add_tail(struct listnode *head, struct listnode *item) +{ + item->next = head; + item->prev = head->prev; + head->prev->next = item; + head->prev = item; +} + +void list_remove(struct listnode *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; +} diff --git a/libcutils/load_file.c b/libcutils/load_file.c new file mode 100644 index 0000000..99f2965 --- /dev/null +++ b/libcutils/load_file.c @@ -0,0 +1,51 @@ +/* libs/cutils/load_file.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 +#include +#include + +void *load_file(const char *fn, unsigned *_sz) +{ + char *data; + int sz; + int fd; + + data = 0; + fd = open(fn, O_RDONLY); + if(fd < 0) return 0; + + sz = lseek(fd, 0, SEEK_END); + if(sz < 0) goto oops; + + if(lseek(fd, 0, SEEK_SET) != 0) goto oops; + + data = (char*) malloc(sz + 1); + if(data == 0) goto oops; + + if(read(fd, data, sz) != sz) goto oops; + close(fd); + data[sz] = 0; + + if(_sz) *_sz = sz; + return data; + +oops: + close(fd); + if(data != 0) free(data); + return 0; +} diff --git a/libcutils/loghack.h b/libcutils/loghack.h new file mode 100644 index 0000000..750cab0 --- /dev/null +++ b/libcutils/loghack.h @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/** + * This is a temporary hack to enable logging from cutils. + */ + +#ifndef _CUTILS_LOGHACK_H +#define _CUTILS_LOGHACK_H + +#ifdef HAVE_ANDROID_OS +#include +#else +#include +#define ALOG(level, ...) \ + ((void)printf("cutils:" level "/" LOG_TAG ": " __VA_ARGS__)) +#define ALOGV(...) ALOG("V", __VA_ARGS__) +#define ALOGD(...) ALOG("D", __VA_ARGS__) +#define ALOGI(...) ALOG("I", __VA_ARGS__) +#define ALOGW(...) ALOG("W", __VA_ARGS__) +#define ALOGE(...) ALOG("E", __VA_ARGS__) +#define LOG_ALWAYS_FATAL(...) do { ALOGE(__VA_ARGS__); exit(1); } while (0) +#endif + +#endif // _CUTILS_LOGHACK_H diff --git a/libcutils/memory.c b/libcutils/memory.c new file mode 100644 index 0000000..6486b45 --- /dev/null +++ b/libcutils/memory.c @@ -0,0 +1,91 @@ +/* + * 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 + +#if !HAVE_MEMSET16 +void android_memset16(uint16_t* dst, uint16_t value, size_t size) +{ + size >>= 1; + while (size--) { + *dst++ = value; + } +} +#endif + +#if !HAVE_MEMSET32 +void android_memset32(uint32_t* dst, uint32_t value, size_t size) +{ + size >>= 2; + while (size--) { + *dst++ = value; + } +} +#endif + +#if !HAVE_STRLCPY +/* + * Copyright (c) 1998 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +/* Implementation of strlcpy() for platforms that don't already have it. */ + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} +#endif diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c new file mode 100644 index 0000000..7c74bb8 --- /dev/null +++ b/libcutils/multiuser.c @@ -0,0 +1,29 @@ +/* + * 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 + +userid_t multiuser_get_user_id(uid_t uid) { + return uid / MULTIUSER_APP_PER_USER_RANGE; +} + +appid_t multiuser_get_app_id(uid_t uid) { + return uid % MULTIUSER_APP_PER_USER_RANGE; +} + +uid_t multiuser_get_uid(userid_t userId, appid_t appId) { + return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE); +} diff --git a/libcutils/native_handle.c b/libcutils/native_handle.c new file mode 100644 index 0000000..4089968 --- /dev/null +++ b/libcutils/native_handle.c @@ -0,0 +1,60 @@ +/* + * 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 LOG_TAG "NativeHandle" + +#include +#include +#include +#include +#include + +#include +#include + +native_handle_t* native_handle_create(int numFds, int numInts) +{ + native_handle_t* h = malloc( + sizeof(native_handle_t) + sizeof(int)*(numFds+numInts)); + + h->version = sizeof(native_handle_t); + h->numFds = numFds; + h->numInts = numInts; + return h; +} + +int native_handle_delete(native_handle_t* h) +{ + if (h) { + if (h->version != sizeof(native_handle_t)) + return -EINVAL; + free(h); + } + return 0; +} + +int native_handle_close(const native_handle_t* h) +{ + if (h->version != sizeof(native_handle_t)) + return -EINVAL; + + const int numFds = h->numFds; + int i; + for (i=0 ; idata[i]); + } + return 0; +} diff --git a/libcutils/open_memstream.c b/libcutils/open_memstream.c new file mode 100644 index 0000000..5b4388a --- /dev/null +++ b/libcutils/open_memstream.c @@ -0,0 +1,381 @@ +/* + * 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 HAVE_OPEN_MEMSTREAM + +/* + * Implementation of the POSIX open_memstream() function, which Linux has + * but BSD lacks. + * + * Summary: + * - Works like a file-backed FILE* opened with fopen(name, "w"), but the + * backing is a chunk of memory rather than a file. + * - The buffer expands as you write more data. Seeking past the end + * of the file and then writing to it zero-fills the gap. + * - The values at "*bufp" and "*sizep" should be considered read-only, + * and are only valid immediately after an fflush() or fclose(). + * - A '\0' is maintained just past the end of the file. This is not included + * in "*sizep". (The behavior w.r.t. fseek() is not clearly defined. + * The spec says the null byte is written when a write() advances EOF, + * but it looks like glibc ensures the null byte is always found at EOF, + * even if you just seeked backwards. The example on the opengroup.org + * page suggests that this is the expected behavior. The null must be + * present after a no-op fflush(), which we can't see, so we have to save + * and restore it. Annoying, but allows file truncation.) + * - After fclose(), the caller must eventually free(*bufp). + * + * This is built out of funopen(), which BSD has but Linux lacks. There is + * no flush() operator, so we need to keep the user pointers up to date + * after each operation. + * + * I don't think Windows has any of the above, but we don't need to use + * them there, so we just supply a stub. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +# define DBUG(x) printf x +#else +# define DBUG(x) ((void)0) +#endif + +#ifdef HAVE_FUNOPEN + +/* + * Definition of a seekable, write-only memory stream. + */ +typedef struct { + char** bufp; /* pointer to buffer pointer */ + size_t* sizep; /* pointer to eof */ + + size_t allocSize; /* size of buffer */ + size_t eof; /* furthest point we've written to */ + size_t offset; /* current write offset */ + char saved; /* required by NUL handling */ +} MemStream; + +#define kInitialSize 1024 + +/* + * Ensure that we have enough storage to write "size" bytes at the + * current offset. We also have to take into account the extra '\0' + * that we maintain just past EOF. + * + * Returns 0 on success. + */ +static int ensureCapacity(MemStream* stream, int writeSize) +{ + DBUG(("+++ ensureCap off=%d size=%d\n", stream->offset, writeSize)); + + size_t neededSize = stream->offset + writeSize + 1; + if (neededSize <= stream->allocSize) + return 0; + + size_t newSize; + + if (stream->allocSize == 0) { + newSize = kInitialSize; + } else { + newSize = stream->allocSize; + newSize += newSize / 2; /* expand by 3/2 */ + } + + if (newSize < neededSize) + newSize = neededSize; + DBUG(("+++ realloc %p->%p to size=%d\n", + stream->bufp, *stream->bufp, newSize)); + char* newBuf = (char*) realloc(*stream->bufp, newSize); + if (newBuf == NULL) + return -1; + + *stream->bufp = newBuf; + stream->allocSize = newSize; + return 0; +} + +/* + * Write data to a memstream, expanding the buffer if necessary. + * + * If we previously seeked beyond EOF, zero-fill the gap. + * + * Returns the number of bytes written. + */ +static int write_memstream(void* cookie, const char* buf, int size) +{ + MemStream* stream = (MemStream*) cookie; + + if (ensureCapacity(stream, size) < 0) + return -1; + + /* seeked past EOF earlier? */ + if (stream->eof < stream->offset) { + DBUG(("+++ zero-fill gap from %d to %d\n", + stream->eof, stream->offset-1)); + memset(*stream->bufp + stream->eof, '\0', + stream->offset - stream->eof); + } + + /* copy data, advance write pointer */ + memcpy(*stream->bufp + stream->offset, buf, size); + stream->offset += size; + + if (stream->offset > stream->eof) { + /* EOF has advanced, update it and append null byte */ + DBUG(("+++ EOF advanced to %d, appending nul\n", stream->offset)); + assert(stream->offset < stream->allocSize); + stream->eof = stream->offset; + } else { + /* within previously-written area; save char we're about to stomp */ + DBUG(("+++ within written area, saving '%c' at %d\n", + *(*stream->bufp + stream->offset), stream->offset)); + stream->saved = *(*stream->bufp + stream->offset); + } + *(*stream->bufp + stream->offset) = '\0'; + *stream->sizep = stream->offset; + + return size; +} + +/* + * Seek within a memstream. + * + * Returns the new offset, or -1 on failure. + */ +static fpos_t seek_memstream(void* cookie, fpos_t offset, int whence) +{ + MemStream* stream = (MemStream*) cookie; + off_t newPosn = (off_t) offset; + + if (whence == SEEK_CUR) { + newPosn += stream->offset; + } else if (whence == SEEK_END) { + newPosn += stream->eof; + } + + if (newPosn < 0 || ((fpos_t)((size_t) newPosn)) != newPosn) { + /* bad offset - negative or huge */ + DBUG(("+++ bogus seek offset %ld\n", (long) newPosn)); + errno = EINVAL; + return (fpos_t) -1; + } + + if (stream->offset < stream->eof) { + /* + * We were pointing to an area we'd already written to, which means + * we stomped on a character and must now restore it. + */ + DBUG(("+++ restoring char '%c' at %d\n", + stream->saved, stream->offset)); + *(*stream->bufp + stream->offset) = stream->saved; + } + + stream->offset = (size_t) newPosn; + + if (stream->offset < stream->eof) { + /* + * We're seeked backward into the stream. Preserve the character + * at EOF and stomp it with a NUL. + */ + stream->saved = *(*stream->bufp + stream->offset); + *(*stream->bufp + stream->offset) = '\0'; + *stream->sizep = stream->offset; + } else { + /* + * We're positioned at, or possibly beyond, the EOF. We want to + * publish the current EOF, not the current position. + */ + *stream->sizep = stream->eof; + } + + return newPosn; +} + +/* + * Close the memstream. We free everything but the data buffer. + */ +static int close_memstream(void* cookie) +{ + free(cookie); + return 0; +} + +/* + * Prepare a memstream. + */ +FILE* open_memstream(char** bufp, size_t* sizep) +{ + FILE* fp; + MemStream* stream; + + if (bufp == NULL || sizep == NULL) { + errno = EINVAL; + return NULL; + } + + stream = (MemStream*) calloc(1, sizeof(MemStream)); + if (stream == NULL) + return NULL; + + fp = funopen(stream, + NULL, write_memstream, seek_memstream, close_memstream); + if (fp == NULL) { + free(stream); + return NULL; + } + + *sizep = 0; + *bufp = NULL; + stream->bufp = bufp; + stream->sizep = sizep; + + return fp; +} + +#else /*not HAVE_FUNOPEN*/ +FILE* open_memstream(char** bufp, size_t* sizep) +{ + abort(); +} +#endif /*HAVE_FUNOPEN*/ + + + +#if 0 +#define _GNU_SOURCE +#include +#include +#include + +/* + * Simple regression test. + * + * To test on desktop Linux with valgrind, it's possible to make a simple + * change to open_memstream() to use fopencookie instead: + * + * cookie_io_functions_t iofuncs = + * { NULL, write_memstream, seek_memstream, close_memstream }; + * fp = fopencookie(stream, "w", iofuncs); + * + * (Some tweaks to seek_memstream are also required, as that takes a + * pointer to an offset rather than an offset, and returns 0 or -1.) + */ +int testMemStream(void) +{ + FILE *stream; + char *buf; + size_t len; + off_t eob; + + printf("Test1\n"); + + /* std example */ + stream = open_memstream(&buf, &len); + fprintf(stream, "hello my world"); + fflush(stream); + printf("buf=%s, len=%zu\n", buf, len); + eob = ftello(stream); + fseeko(stream, 0, SEEK_SET); + fprintf(stream, "good-bye"); + fseeko(stream, eob, SEEK_SET); + fclose(stream); + printf("buf=%s, len=%zu\n", buf, len); + free(buf); + + printf("Test2\n"); + + /* std example without final seek-to-end */ + stream = open_memstream(&buf, &len); + fprintf(stream, "hello my world"); + fflush(stream); + printf("buf=%s, len=%zu\n", buf, len); + eob = ftello(stream); + fseeko(stream, 0, SEEK_SET); + fprintf(stream, "good-bye"); + //fseeko(stream, eob, SEEK_SET); + fclose(stream); + printf("buf=%s, len=%zu\n", buf, len); + free(buf); + + printf("Test3\n"); + + /* fancy example; should expand buffer with writes */ + static const int kCmpLen = 1024 + 128; + char* cmp = malloc(kCmpLen); + memset(cmp, 0, 1024); + memset(cmp+1024, 0xff, kCmpLen-1024); + sprintf(cmp, "This-is-a-tes1234"); + sprintf(cmp + 1022, "abcdef"); + + stream = open_memstream (&buf, &len); + setvbuf(stream, NULL, _IONBF, 0); /* note: crashes in glibc with this */ + fprintf(stream, "This-is-a-test"); + fseek(stream, -1, SEEK_CUR); /* broken in glibc; can use {13,SEEK_SET} */ + fprintf(stream, "1234"); + fseek(stream, 1022, SEEK_SET); + fputc('a', stream); + fputc('b', stream); + fputc('c', stream); + fputc('d', stream); + fputc('e', stream); + fputc('f', stream); + fflush(stream); + + if (memcmp(buf, cmp, len+1) != 0) { + printf("mismatch\n"); + } else { + printf("match\n"); + } + + printf("Test4\n"); + stream = open_memstream (&buf, &len); + fseek(stream, 5000, SEEK_SET); + fseek(stream, 4096, SEEK_SET); + fseek(stream, -1, SEEK_SET); /* should have no effect */ + fputc('x', stream); + if (ftell(stream) == 4097) + printf("good\n"); + else + printf("BAD: offset is %ld\n", ftell(stream)); + + printf("DONE\n"); + + return 0; +} + +/* expected output: +Test1 +buf=hello my world, len=14 +buf=good-bye world, len=14 +Test2 +buf=hello my world, len=14 +buf=good-bye, len=8 +Test3 +match +Test4 +good +DONE +*/ + +#endif + +#endif /*!HAVE_OPEN_MEMSTREAM*/ diff --git a/libcutils/partition_utils.c b/libcutils/partition_utils.c new file mode 100644 index 0000000..10539fa --- /dev/null +++ b/libcutils/partition_utils.c @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include /* for BLKGETSIZE */ +#include + +static int only_one_char(char *buf, int len, char c) +{ + int i, ret; + + ret = 1; + for (i=0; i +#include +#include +#ifdef HAVE_ANDROID_OS +#include +#endif +#include +#include +#include +#include + +#if defined(HAVE_PRCTL) +#include +#endif + +#define PROCESS_NAME_DEVICE "/sys/qemu_trace/process_name" + +static const char* process_name = "unknown"; +static int running_in_emulator = -1; + +void set_process_name(const char* new_name) { +#ifdef HAVE_ANDROID_OS + 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(HAVE_PRCTL) + 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 + +#ifdef HAVE_ANDROID_OS + // 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 new file mode 100644 index 0000000..28d8b2f --- /dev/null +++ b/libcutils/properties.c @@ -0,0 +1,339 @@ +/* + * 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" + +#include +#include +#include +#include +#include +#include + +#include +#include "loghack.h" + +#ifdef HAVE_LIBC_SYSTEM_PROPERTIES + +#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ +#include + +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); + memcpy(value, default_value, len + 1); + } + 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); +} + +#elif defined(HAVE_SYSTEM_PROPERTY_SERVER) + +/* + * The Linux simulator provides a "system property server" that uses IPC + * to set/get/list properties. The file descriptor is shared by all + * threads in the process, so we use a mutex to ensure that requests + * from multiple threads don't get interleaved. + */ +#include +#include +#include +#include +#include + +static pthread_once_t gInitOnce = PTHREAD_ONCE_INIT; +static pthread_mutex_t gPropertyFdLock = PTHREAD_MUTEX_INITIALIZER; +static int gPropFd = -1; + +/* + * Connect to the properties server. + * + * Returns the socket descriptor on success. + */ +static int connectToServer(const char* fileName) +{ + int sock = -1; + int cc; + + struct sockaddr_un addr; + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + ALOGW("UNIX domain socket create failed (errno=%d)\n", errno); + return -1; + } + + /* connect to socket; fails if file doesn't exist */ + strcpy(addr.sun_path, fileName); // max 108 bytes + addr.sun_family = AF_UNIX; + cc = connect(sock, (struct sockaddr*) &addr, SUN_LEN(&addr)); + if (cc < 0) { + // ENOENT means socket file doesn't exist + // ECONNREFUSED means socket exists but nobody is listening + //ALOGW("AF_UNIX connect failed for '%s': %s\n", + // fileName, strerror(errno)); + close(sock); + return -1; + } + + return sock; +} + +/* + * Perform one-time initialization. + */ +static void init(void) +{ + assert(gPropFd == -1); + + gPropFd = connectToServer(SYSTEM_PROPERTY_PIPE_NAME); + if (gPropFd < 0) { + //ALOGW("not connected to system property server\n"); + } else { + //ALOGV("Connected to system property server\n"); + } +} + +int property_get(const char *key, char *value, const char *default_value) +{ + char sendBuf[1+PROPERTY_KEY_MAX]; + char recvBuf[1+PROPERTY_VALUE_MAX]; + int len = -1; + + //ALOGV("PROPERTY GET [%s]\n", key); + + pthread_once(&gInitOnce, init); + if (gPropFd < 0) { + /* this mimics the behavior of the device implementation */ + if (default_value != NULL) { + strcpy(value, default_value); + len = strlen(value); + } + return len; + } + + if (strlen(key) >= PROPERTY_KEY_MAX) return -1; + + memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind + + sendBuf[0] = (char) kSystemPropertyGet; + strcpy(sendBuf+1, key); + + pthread_mutex_lock(&gPropertyFdLock); + if (write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) { + pthread_mutex_unlock(&gPropertyFdLock); + return -1; + } + if (read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) { + pthread_mutex_unlock(&gPropertyFdLock); + return -1; + } + pthread_mutex_unlock(&gPropertyFdLock); + + /* first byte is 0 if value not defined, 1 if found */ + if (recvBuf[0] == 0) { + if (default_value != NULL) { + strcpy(value, default_value); + len = strlen(value); + } else { + /* + * If the value isn't defined, hand back an empty string and + * a zero length, rather than a failure. This seems wrong, + * since you can't tell the difference between "undefined" and + * "defined but empty", but it's what the device does. + */ + value[0] = '\0'; + len = 0; + } + } else if (recvBuf[0] == 1) { + strcpy(value, recvBuf+1); + len = strlen(value); + } else { + ALOGE("Got strange response to property_get request (%d)\n", + recvBuf[0]); + assert(0); + return -1; + } + //ALOGV("PROP [found=%d def='%s'] (%d) [%s]: [%s]\n", + // recvBuf[0], default_value, len, key, value); + + return len; +} + + +int property_set(const char *key, const char *value) +{ + char sendBuf[1+PROPERTY_KEY_MAX+PROPERTY_VALUE_MAX]; + char recvBuf[1]; + int result = -1; + + //ALOGV("PROPERTY SET [%s]: [%s]\n", key, value); + + pthread_once(&gInitOnce, init); + if (gPropFd < 0) + return -1; + + if (strlen(key) >= PROPERTY_KEY_MAX) return -1; + if (strlen(value) >= PROPERTY_VALUE_MAX) return -1; + + memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind + + sendBuf[0] = (char) kSystemPropertySet; + strcpy(sendBuf+1, key); + strcpy(sendBuf+1+PROPERTY_KEY_MAX, value); + + pthread_mutex_lock(&gPropertyFdLock); + if (write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) { + pthread_mutex_unlock(&gPropertyFdLock); + return -1; + } + if (read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) { + pthread_mutex_unlock(&gPropertyFdLock); + return -1; + } + pthread_mutex_unlock(&gPropertyFdLock); + + if (recvBuf[0] != 1) + return -1; + return 0; +} + +int property_list(void (*propfn)(const char *key, const char *value, void *cookie), + void *cookie) +{ + //ALOGV("PROPERTY LIST\n"); + pthread_once(&gInitOnce, init); + if (gPropFd < 0) + return -1; + + return 0; +} + +#else + +/* SUPER-cheesy place-holder implementation for Win32 */ + +#include + +static mutex_t env_lock = MUTEX_INITIALIZER; + +int property_get(const char *key, char *value, const char *default_value) +{ + char ename[PROPERTY_KEY_MAX + 6]; + char *p; + int len; + + len = strlen(key); + if(len >= PROPERTY_KEY_MAX) return -1; + memcpy(ename, "PROP_", 5); + memcpy(ename + 5, key, len + 1); + + mutex_lock(&env_lock); + + p = getenv(ename); + if(p == 0) p = ""; + len = strlen(p); + if(len >= PROPERTY_VALUE_MAX) { + len = PROPERTY_VALUE_MAX - 1; + } + + if((len == 0) && default_value) { + len = strlen(default_value); + memcpy(value, default_value, len + 1); + } else { + memcpy(value, p, len); + value[len] = 0; + } + + mutex_unlock(&env_lock); + + return len; +} + + +int property_set(const char *key, const char *value) +{ + char ename[PROPERTY_KEY_MAX + 6]; + char *p; + int len; + int r; + + if(strlen(value) >= PROPERTY_VALUE_MAX) return -1; + + len = strlen(key); + if(len >= PROPERTY_KEY_MAX) return -1; + memcpy(ename, "PROP_", 5); + memcpy(ename + 5, key, len + 1); + + mutex_lock(&env_lock); +#ifdef HAVE_MS_C_RUNTIME + { + char temp[256]; + snprintf( temp, sizeof(temp), "%s=%s", ename, value); + putenv(temp); + r = 0; + } +#else + r = setenv(ename, value, 1); +#endif + mutex_unlock(&env_lock); + + return r; +} + +int property_list(void (*propfn)(const char *key, const char *value, void *cookie), + void *cookie) +{ + return 0; +} + +#endif diff --git a/libcutils/qtaguid.c b/libcutils/qtaguid.c new file mode 100644 index 0000000..5bb8176 --- /dev/null +++ b/libcutils/qtaguid.c @@ -0,0 +1,177 @@ +/* libcutils/qtaguid.c +** +** 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. +*/ + +// #define LOG_NDEBUG 0 + +#define LOG_TAG "qtaguid" + +#include +#include +#include +#include +#include +#include +#include +#include + +static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl"; +static const int CTRL_MAX_INPUT_LEN = 128; +static const char *GLOBAL_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/passive"; +static const char *TAG_PACIFIER_PARAM = "/sys/module/xt_qtaguid/parameters/tag_tracking_passive"; + +/* + * One per proccess. + * Once the device is open, this process will have its socket tags tracked. + * And on exit or untimely death, all socket tags will be removed. + * A process can only open /dev/xt_qtaguid once. + * It should not close it unless it is really done with all the socket tags. + * Failure to open it will be visible when socket tagging will be attempted. + */ +static int resTrackFd = -1; +pthread_once_t resTrackInitDone = PTHREAD_ONCE_INIT; + +/* 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)); + } +} + +/* + * Returns: + * 0 on success. + * -errno on failure. + */ +static int write_ctrl(const char *cmd) { + int fd, res, savedErrno; + + ALOGV("write_ctrl(%s)", cmd); + + fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY)); + if (fd < 0) { + return -errno; + } + + res = TEMP_FAILURE_RETRY(write(fd, cmd, strlen(cmd))); + if (res < 0) { + savedErrno = errno; + } else { + savedErrno = 0; + } + if (res < 0) { + ALOGI("Failed write_ctrl(%s) res=%d errno=%d", cmd, res, savedErrno); + } + close(fd); + return -savedErrno; +} + +static int write_param(const char *param_path, const char *value) { + int param_fd; + int res; + + param_fd = TEMP_FAILURE_RETRY(open(param_path, O_WRONLY)); + if (param_fd < 0) { + return -errno; + } + res = TEMP_FAILURE_RETRY(write(param_fd, value, strlen(value))); + if (res < 0) { + return -errno; + } + close(param_fd); + return 0; +} + +int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int res; + uint64_t kTag = ((uint64_t)tag << 32); + + pthread_once(&resTrackInitDone, qtaguid_resTrack); + + snprintf(lineBuf, sizeof(lineBuf), "t %d %llu %d", sockfd, kTag, uid); + + ALOGV("Tagging socket %d with tag %llx{%u,0} for uid %d", sockfd, kTag, tag, uid); + + res = write_ctrl(lineBuf); + if (res < 0) { + ALOGI("Tagging socket %d with tag %llx(%d) for uid %d failed errno=%d", + sockfd, kTag, tag, uid, res); + } + + return res; +} + +int qtaguid_untagSocket(int sockfd) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int res; + + ALOGV("Untagging socket %d", sockfd); + + snprintf(lineBuf, sizeof(lineBuf), "u %d", sockfd); + res = write_ctrl(lineBuf); + if (res < 0) { + ALOGI("Untagging socket %d failed errno=%d", sockfd, res); + } + + return res; +} + +int qtaguid_setCounterSet(int counterSetNum, uid_t uid) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int res; + + ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid); + + snprintf(lineBuf, sizeof(lineBuf), "s %d %d", counterSetNum, uid); + res = write_ctrl(lineBuf); + return res; +} + +int qtaguid_deleteTagData(int tag, uid_t uid) { + char lineBuf[CTRL_MAX_INPUT_LEN]; + int fd, cnt = 0, res = 0; + uint64_t kTag = (uint64_t)tag << 32; + + ALOGV("Deleting tag data with tag %llx{%d,0} for uid %d", kTag, tag, uid); + + pthread_once(&resTrackInitDone, qtaguid_resTrack); + + snprintf(lineBuf, sizeof(lineBuf), "d %llu %d", kTag, uid); + res = write_ctrl(lineBuf); + if (res < 0) { + ALOGI("Deleteing tag data with tag %llx/%d for uid %d failed with cnt=%d errno=%d", + kTag, tag, uid, cnt, errno); + } + + return res; +} + +int qtaguid_setPacifier(int on) { + int param_fd; + int res; + const char *value; + + value = on ? "Y" : "N"; + if (write_param(GLOBAL_PACIFIER_PARAM, value) < 0) { + return -errno; + } + if (write_param(TAG_PACIFIER_PARAM, value) < 0) { + return -errno; + } + return 0; +} diff --git a/libcutils/record_stream.c b/libcutils/record_stream.c new file mode 100644 index 0000000..6994904 --- /dev/null +++ b/libcutils/record_stream.c @@ -0,0 +1,186 @@ +/* libs/cutils/record_stream.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 +#include +#include +#include +#include +#include +#include +#ifdef HAVE_WINSOCK +#include /* for ntohl */ +#else +#include +#endif + +#define HEADER_SIZE 4 + +struct RecordStream { + int fd; + size_t maxRecordLen; + + unsigned char *buffer; + + unsigned char *unconsumed; + unsigned char *read_end; + unsigned char *buffer_end; +}; + + +extern RecordStream *record_stream_new(int fd, size_t maxRecordLen) +{ + RecordStream *ret; + + assert (maxRecordLen <= 0xffff); + + ret = (RecordStream *)calloc(1, sizeof(RecordStream)); + + ret->fd = fd; + ret->maxRecordLen = maxRecordLen; + ret->buffer = (unsigned char *)malloc (maxRecordLen + HEADER_SIZE); + + ret->unconsumed = ret->buffer; + ret->read_end = ret->buffer; + ret->buffer_end = ret->buffer + maxRecordLen + HEADER_SIZE; + + return ret; +} + + +extern void record_stream_free(RecordStream *rs) +{ + free(rs->buffer); + free(rs); +} + + +/* returns NULL; if there isn't a full record in the buffer */ +static unsigned char * getEndOfRecord (unsigned char *p_begin, + unsigned char *p_end) +{ + size_t len; + unsigned char * p_ret; + + if (p_end < p_begin + HEADER_SIZE) { + return NULL; + } + + //First four bytes are length + len = ntohl(*((uint32_t *)p_begin)); + + p_ret = p_begin + HEADER_SIZE + len; + + if (p_end < p_ret) { + return NULL; + } + + return p_ret; +} + +static void *getNextRecord (RecordStream *p_rs, size_t *p_outRecordLen) +{ + unsigned char *record_start, *record_end; + + record_end = getEndOfRecord (p_rs->unconsumed, p_rs->read_end); + + if (record_end != NULL) { + /* one full line in the buffer */ + record_start = p_rs->unconsumed + HEADER_SIZE; + p_rs->unconsumed = record_end; + + *p_outRecordLen = record_end - record_start; + + return record_start; + } + + return NULL; +} + +/** + * Reads the next record from stream fd + * Records are prefixed by a 16-bit big endian length value + * Records may not be larger than maxRecordLen + * + * Doesn't guard against EINTR + * + * p_outRecord and p_outRecordLen may not be NULL + * + * Return 0 on success, -1 on fail + * Returns 0 with *p_outRecord set to NULL on end of stream + * Returns -1 / errno = EAGAIN if it needs to read again + */ +int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, + size_t *p_outRecordLen) +{ + void *ret; + + ssize_t countRead; + + /* is there one record already in the buffer? */ + ret = getNextRecord (p_rs, p_outRecordLen); + + if (ret != NULL) { + *p_outRecord = ret; + return 0; + } + + // if the buffer is full and we don't have a full record + if (p_rs->unconsumed == p_rs->buffer + && p_rs->read_end == p_rs->buffer_end + ) { + // this should never happen + //ALOGE("max record length exceeded\n"); + assert (0); + errno = EFBIG; + return -1; + } + + if (p_rs->unconsumed != p_rs->buffer) { + // move remainder to the beginning of the buffer + size_t toMove; + + toMove = p_rs->read_end - p_rs->unconsumed; + if (toMove) { + memmove(p_rs->buffer, p_rs->unconsumed, toMove); + } + + p_rs->read_end = p_rs->buffer + toMove; + p_rs->unconsumed = p_rs->buffer; + } + + countRead = read (p_rs->fd, p_rs->read_end, p_rs->buffer_end - p_rs->read_end); + + if (countRead <= 0) { + /* note: end-of-stream drops through here too */ + *p_outRecord = NULL; + return countRead; + } + + p_rs->read_end += countRead; + + ret = getNextRecord (p_rs, p_outRecordLen); + + if (ret == NULL) { + /* not enough of a buffer to for a whole command */ + errno = EAGAIN; + return -1; + } + + *p_outRecord = ret; + return 0; +} diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c new file mode 100644 index 0000000..d20d217 --- /dev/null +++ b/libcutils/sched_policy.c @@ -0,0 +1,362 @@ + +/* libs/cutils/sched_policy.c +** +** 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. +*/ + +#define LOG_TAG "SchedPolicy" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged. + * Call this any place a SchedPolicy is used as an input parameter. + * Returns the possibly re-mapped policy. + */ +static inline SchedPolicy _policy(SchedPolicy p) +{ + return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p; +} + +#if defined(HAVE_ANDROID_OS) && defined(HAVE_SCHED_H) && defined(HAVE_PTHREADS) + +#include +#include + +#ifndef SCHED_NORMAL + #define SCHED_NORMAL 0 +#endif + +#ifndef SCHED_BATCH + #define SCHED_BATCH 3 +#endif + +#define POLICY_DEBUG 0 + +#define CAN_SET_SP_SYSTEM 0 // non-zero means to implement set_sched_policy(tid, SP_SYSTEM) + +static pthread_once_t the_once = PTHREAD_ONCE_INIT; + +static int __sys_supports_schedgroups = -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; +#if CAN_SET_SP_SYSTEM +static int system_cgroup_fd = -1; +#endif + +/* Add tid to the scheduling group defined by the policy */ +static int add_tid_to_cgroup(int tid, SchedPolicy policy) +{ + 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; +#if CAN_SET_SP_SYSTEM + case SP_SYSTEM: + fd = system_cgroup_fd; + break; +#endif + default: + fd = -1; + break; + } + + if (fd < 0) { + SLOGE("add_tid_to_cgroup failed; policy=%d\n", policy); + return -1; + } + + // specialized itoa -- works for tid > 0 + char text[22]; + char *end = text + sizeof(text) - 1; + char *ptr = end; + *ptr = '\0'; + while (tid > 0) { + *--ptr = '0' + (tid % 10); + tid = tid / 10; + } + + if (write(fd, ptr, end - ptr) < 0) { + /* + * If the thread is in the process of exiting, + * don't flag an error + */ + if (errno == ESRCH) + return 0; + SLOGW("add_tid_to_cgroup failed to write '%s' (%s); policy=%d\n", + ptr, strerror(errno), policy); + return -1; + } + + return 0; +} + +static void __initialize(void) { + char* filename; + if (!access("/dev/cpuctl/tasks", F_OK)) { + __sys_supports_schedgroups = 1; + +#if CAN_SET_SP_SYSTEM + filename = "/dev/cpuctl/tasks"; + system_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC); + if (system_cgroup_fd < 0) { + SLOGV("open of %s failed: %s\n", filename, strerror(errno)); + } +#endif + + filename = "/dev/cpuctl/apps/tasks"; + fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC); + if (fg_cgroup_fd < 0) { + SLOGE("open of %s failed: %s\n", filename, strerror(errno)); + } + + filename = "/dev/cpuctl/apps/bg_non_interactive/tasks"; + bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC); + if (bg_cgroup_fd < 0) { + SLOGE("open of %s failed: %s\n", filename, strerror(errno)); + } + } else { + __sys_supports_schedgroups = 0; + } +} + +/* + * Try to get the scheduler group. + * + * The data from /proc//cgroup looks (something) like: + * 2:cpu:/bg_non_interactive + * 1:cpuacct:/ + * + * We return the part after the "/", which will be an empty string for + * the default cgroup. If the string is longer than "bufLen", the string + * will be truncated. + */ +static int getSchedulerGroup(int tid, char* buf, size_t bufLen) +{ +#ifdef HAVE_ANDROID_OS + char pathBuf[32]; + char lineBuf[256]; + FILE *fp; + + snprintf(pathBuf, sizeof(pathBuf), "/proc/%d/cgroup", tid); + if (!(fp = fopen(pathBuf, "r"))) { + return -1; + } + + while(fgets(lineBuf, sizeof(lineBuf) -1, fp)) { + char *next = lineBuf; + char *subsys; + char *grp; + size_t len; + + /* Junk the first field */ + if (!strsep(&next, ":")) { + goto out_bad_data; + } + + if (!(subsys = strsep(&next, ":"))) { + goto out_bad_data; + } + + if (strcmp(subsys, "cpu")) { + /* Not the subsys we're looking for */ + continue; + } + + if (!(grp = strsep(&next, ":"))) { + goto out_bad_data; + } + grp++; /* Drop the leading '/' */ + len = strlen(grp); + grp[len-1] = '\0'; /* Drop the trailing '\n' */ + + if (bufLen <= len) { + len = bufLen - 1; + } + strncpy(buf, grp, len); + buf[len] = '\0'; + fclose(fp); + return 0; + } + + SLOGE("Failed to find cpu subsys"); + fclose(fp); + return -1; + out_bad_data: + SLOGE("Bad cgroup data {%s}", lineBuf); + fclose(fp); + return -1; +#else + errno = ENOSYS; + return -1; +#endif +} + +int get_sched_policy(int tid, SchedPolicy *policy) +{ +#ifdef HAVE_GETTID + if (tid == 0) { + tid = gettid(); + } +#endif + pthread_once(&the_once, __initialize); + + if (__sys_supports_schedgroups) { + char grpBuf[32]; + if (getSchedulerGroup(tid, grpBuf, sizeof(grpBuf)) < 0) + return -1; + if (grpBuf[0] == '\0') { + *policy = SP_SYSTEM; + } else if (!strcmp(grpBuf, "apps/bg_non_interactive")) { + *policy = SP_BACKGROUND; + } else if (!strcmp(grpBuf, "apps")) { + *policy = SP_FOREGROUND; + } else { + errno = ERANGE; + return -1; + } + } else { + int rc = sched_getscheduler(tid); + if (rc < 0) + return -1; + else if (rc == SCHED_NORMAL) + *policy = SP_FOREGROUND; + else if (rc == SCHED_BATCH) + *policy = SP_BACKGROUND; + else { + errno = ERANGE; + return -1; + } + } + return 0; +} + +int set_sched_policy(int tid, SchedPolicy policy) +{ +#ifdef HAVE_GETTID + if (tid == 0) { + tid = gettid(); + } +#endif + policy = _policy(policy); + pthread_once(&the_once, __initialize); + +#if POLICY_DEBUG + char statfile[64]; + char statline[1024]; + char thread_name[255]; + int fd; + + sprintf(statfile, "/proc/%d/stat", tid); + memset(thread_name, 0, sizeof(thread_name)); + + fd = open(statfile, O_RDONLY); + if (fd >= 0) { + int rc = read(fd, statline, 1023); + close(fd); + statline[rc] = 0; + char *p = statline; + char *q; + + for (p = statline; *p != '('; p++); + p++; + for (q = p; *q != ')'; q++); + + strncpy(thread_name, p, (q-p)); + } + switch (policy) { + case SP_BACKGROUND: + SLOGD("vvv tid %d (%s)", tid, thread_name); + break; + case SP_FOREGROUND: + case SP_AUDIO_APP: + case SP_AUDIO_SYS: + SLOGD("^^^ tid %d (%s)", tid, thread_name); + break; + case SP_SYSTEM: + SLOGD("/// tid %d (%s)", tid, thread_name); + break; + default: + SLOGD("??? tid %d (%s)", tid, thread_name); + break; + } +#endif + + if (__sys_supports_schedgroups) { + if (add_tid_to_cgroup(tid, policy)) { + if (errno != ESRCH && errno != ENOENT) + return -errno; + } + } else { + struct sched_param param; + + param.sched_priority = 0; + sched_setscheduler(tid, + (policy == SP_BACKGROUND) ? + SCHED_BATCH : SCHED_NORMAL, + ¶m); + } + + return 0; +} + +#else + +/* Stubs for non-Android targets. */ + +int set_sched_policy(int tid, SchedPolicy policy) +{ + return 0; +} + +int get_sched_policy(int tid, SchedPolicy *policy) +{ + *policy = SP_SYSTEM_DEFAULT; + return 0; +} + +#endif + +const char *get_sched_policy_name(SchedPolicy policy) +{ + policy = _policy(policy); + static const char * const strings[SP_CNT] = { + [SP_BACKGROUND] = "bg", + [SP_FOREGROUND] = "fg", + [SP_SYSTEM] = " ", + [SP_AUDIO_APP] = "aa", + [SP_AUDIO_SYS] = "as", + }; + 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 new file mode 100644 index 0000000..7d5dab4 --- /dev/null +++ b/libcutils/socket_inaddr_any_server.c @@ -0,0 +1,70 @@ +/* libs/cutils/socket_inaddr_any_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 + +#include +#include +#include +#include +#include + +#ifndef HAVE_WINSOCK +#include +#include +#include +#include +#endif + +#define LISTEN_BACKLOG 4 + +/* open listen() port on any interface */ +int socket_inaddr_any_server(int port, int type) +{ + struct sockaddr_in addr; + size_t alen; + 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, &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_local.h b/libcutils/socket_local.h new file mode 100644 index 0000000..45b9856 --- /dev/null +++ b/libcutils/socket_local.h @@ -0,0 +1,39 @@ +/* + * 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 __SOCKET_LOCAL_H +#define __SOCKET_LOCAL_H + +#define FILESYSTEM_SOCKET_PREFIX "/tmp/" +#define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/" + +/* + * Set up a given sockaddr_un, to have it refer to the given + * name in the given namespace. The namespace must be one + * of ANDROID_SOCKET_NAMESPACE_ABSTRACT, + * ANDROID_SOCKET_NAMESPACE_RESERVED, or + * ANDROID_SOCKET_NAMESPACE_FILESYSTEM. Upon success, + * the pointed at sockaddr_un is filled in and the pointed at + * socklen_t is set to indicate the final length. This function + * will fail if the namespace is invalid (not one of the indicated + * constants) or if the name is too long. + * + * @return 0 on success or -1 on failure + */ +int socket_make_sockaddr_un(const char *name, int namespaceId, + struct sockaddr_un *p_addr, socklen_t *alen); + +#endif diff --git a/libcutils/socket_local_client.c b/libcutils/socket_local_client.c new file mode 100644 index 0000000..036ce2e --- /dev/null +++ b/libcutils/socket_local_client.c @@ -0,0 +1,167 @@ +/* + * 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 + +#include +#include +#include +#include +#include + +#ifdef HAVE_WINSOCK + +int socket_local_client(const char *name, int namespaceId, int type) +{ + errno = ENOSYS; + return -1; +} + +#else /* !HAVE_WINSOCK */ + +#include +#include +#include +#include + +#include "socket_local.h" + +#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: +#ifdef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE + 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 /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/ + /* 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 /*HAVE_LINUX_LOCAL_SOCKET_NAMESPACE*/ + 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) +{ + struct sockaddr_un addr; + socklen_t alen; + size_t namelen; + 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 /* !HAVE_WINSOCK */ diff --git a/libcutils/socket_local_server.c b/libcutils/socket_local_server.c new file mode 100644 index 0000000..4971b1b --- /dev/null +++ b/libcutils/socket_local_server.c @@ -0,0 +1,124 @@ +/* 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 + +#include +#include +#include +#include +#include + +#ifdef HAVE_WINSOCK + +int socket_local_server(const char *name, int namespaceId, int type) +{ + errno = ENOSYS; + return -1; +} + +#else /* !HAVE_WINSOCK */ + +#include +#include +#include +#include +#include + +#include "socket_local.h" + +#define LISTEN_BACKLOG 4 + + +/** + * 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 */ +#ifndef HAVE_LINUX_LOCAL_SOCKET_NAMESPACE + 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_STREAM) { + int ret; + + ret = listen(s, LISTEN_BACKLOG); + + if (ret < 0) { + close(s); + return -1; + } + } + + return s; +} + +#endif /* !HAVE_WINSOCK */ diff --git a/libcutils/socket_loopback_client.c b/libcutils/socket_loopback_client.c new file mode 100644 index 0000000..cb82c5e --- /dev/null +++ b/libcutils/socket_loopback_client.c @@ -0,0 +1,59 @@ +/* libs/cutils/socket_loopback_client.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 + +#include +#include +#include +#include +#include + +#ifndef HAVE_WINSOCK +#include +#include +#include +#include +#endif + +/* 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; + socklen_t alen; + 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 new file mode 100644 index 0000000..3208488 --- /dev/null +++ b/libcutils/socket_loopback_server.c @@ -0,0 +1,71 @@ +/* libs/cutils/socket_loopback_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 + +#include +#include +#include +#include +#include + +#define LISTEN_BACKLOG 4 + +#ifndef HAVE_WINSOCK +#include +#include +#include +#include +#endif + +/* open listen() port on loopback interface */ +int socket_loopback_server(int port, int type) +{ + struct sockaddr_in addr; + size_t alen; + 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, &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_network_client.c b/libcutils/socket_network_client.c new file mode 100644 index 0000000..a64006c --- /dev/null +++ b/libcutils/socket_network_client.c @@ -0,0 +1,65 @@ +/* libs/cutils/socket_network_client.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 + +#include +#include +#include +#include +#include + +#ifndef HAVE_WINSOCK +#include +#include +#include +#include +#include +#endif + + +/* Connect to port on the IP interface. type is + * SOCK_STREAM or SOCK_DGRAM. + * return is a file descriptor or -1 on error + */ +int socket_network_client(const char *host, int port, int type) +{ + struct hostent *hp; + struct sockaddr_in addr; + socklen_t alen; + int s; + + hp = gethostbyname(host); + if(hp == 0) return -1; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = hp->h_addrtype; + addr.sin_port = htons(port); + memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); + + s = socket(hp->h_addrtype, 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/sockets.c b/libcutils/sockets.c new file mode 100644 index 0000000..b5a1b3d --- /dev/null +++ b/libcutils/sockets.c @@ -0,0 +1,44 @@ +/* + * 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 +#include + +#ifdef HAVE_ANDROID_OS +/* For the socket trust (credentials) check */ +#include +#endif + +bool socket_peer_is_trusted(int fd) +{ +#ifdef HAVE_ANDROID_OS + 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/str_parms.c b/libcutils/str_parms.c new file mode 100644 index 0000000..9e1d2dc --- /dev/null +++ b/libcutils/str_parms.c @@ -0,0 +1,372 @@ +/* + * 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 "str_params" +//#define LOG_NDEBUG 0 + +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct str_parms { + Hashmap *map; +}; + + +static bool str_eq(void *key_a, void *key_b) +{ + return !strcmp((const char *)key_a, (const char *)key_b); +} + +/* use djb hash unless we find it inadequate */ +static int str_hash_fn(void *str) +{ + uint32_t hash = 5381; + char *p; + + for (p = str; p && *p; p++) + hash = ((hash << 5) + hash) + *p; + return (int)hash; +} + +struct str_parms *str_parms_create(void) +{ + struct str_parms *str_parms; + + str_parms = calloc(1, sizeof(struct str_parms)); + if (!str_parms) + return NULL; + + str_parms->map = hashmapCreate(5, str_hash_fn, str_eq); + if (!str_parms->map) + goto err; + + return str_parms; + +err: + free(str_parms); + return NULL; +} + +struct remove_ctxt { + struct str_parms *str_parms; + const char *key; +}; + +static bool remove_pair(void *key, void *value, void *context) +{ + struct remove_ctxt *ctxt = context; + bool should_continue; + + /* + * - if key is not supplied, then we are removing all entries, + * so remove key and continue (i.e. return true) + * - if key is supplied and matches, then remove it and don't + * continue (return false). Otherwise, return true and keep searching + * for key. + * + */ + if (!ctxt->key) { + should_continue = true; + goto do_remove; + } else if (!strcmp(ctxt->key, key)) { + should_continue = false; + goto do_remove; + } + + return true; + +do_remove: + hashmapRemove(ctxt->str_parms->map, key); + free(key); + free(value); + return should_continue; +} + +void str_parms_del(struct str_parms *str_parms, const char *key) +{ + struct remove_ctxt ctxt = { + .str_parms = str_parms, + .key = key, + }; + hashmapForEach(str_parms->map, remove_pair, &ctxt); +} + +void str_parms_destroy(struct str_parms *str_parms) +{ + struct remove_ctxt ctxt = { + .str_parms = str_parms, + }; + + hashmapForEach(str_parms->map, remove_pair, &ctxt); + hashmapFree(str_parms->map); + free(str_parms); +} + +struct str_parms *str_parms_create_str(const char *_string) +{ + struct str_parms *str_parms; + char *str; + char *kvpair; + char *tmpstr; + int items = 0; + + str_parms = str_parms_create(); + if (!str_parms) + goto err_create_str_parms; + + str = strdup(_string); + if (!str) + goto err_strdup; + + ALOGV("%s: source string == '%s'\n", __func__, _string); + + kvpair = strtok_r(str, ";", &tmpstr); + while (kvpair && *kvpair) { + char *eq = strchr(kvpair, '='); /* would love strchrnul */ + char *value; + char *key; + void *old_val; + + if (eq == kvpair) + goto next_pair; + + if (eq) { + key = strndup(kvpair, eq - kvpair); + if (*(++eq)) + value = strdup(eq); + else + value = strdup(""); + } else { + key = strdup(kvpair); + value = strdup(""); + } + + /* if we replaced a value, free it */ + old_val = hashmapPut(str_parms->map, key, value); + if (old_val) { + free(old_val); + free(key); + } + + items++; +next_pair: + kvpair = strtok_r(NULL, ";", &tmpstr); + } + + if (!items) + ALOGV("%s: no items found in string\n", __func__); + + free(str); + + return str_parms; + +err_strdup: + str_parms_destroy(str_parms); +err_create_str_parms: + return NULL; +} + +int str_parms_add_str(struct str_parms *str_parms, const char *key, + const char *value) +{ + void *old_val; + void *tmp_key; + void *tmp_val; + + tmp_key = strdup(key); + tmp_val = strdup(value); + old_val = hashmapPut(str_parms->map, tmp_key, tmp_val); + + if (old_val) { + free(old_val); + free(tmp_key); + } else if (errno == ENOMEM) { + free(tmp_key); + free(tmp_val); + return -ENOMEM; + } + return 0; +} + +int str_parms_add_int(struct str_parms *str_parms, const char *key, int value) +{ + char val_str[12]; + int ret; + + ret = snprintf(val_str, sizeof(val_str), "%d", value); + if (ret < 0) + return -EINVAL; + + ret = str_parms_add_str(str_parms, key, val_str); + return ret; +} + +int str_parms_add_float(struct str_parms *str_parms, const char *key, + float value) +{ + char val_str[23]; + int ret; + + ret = snprintf(val_str, sizeof(val_str), "%.10f", value); + if (ret < 0) + return -EINVAL; + + ret = str_parms_add_str(str_parms, key, val_str); + return ret; +} + +int str_parms_get_str(struct str_parms *str_parms, const char *key, char *val, + int len) +{ + char *value; + + value = hashmapGet(str_parms->map, (void *)key); + if (value) + return strlcpy(val, value, len); + + return -ENOENT; +} + +int str_parms_get_int(struct str_parms *str_parms, const char *key, int *val) +{ + char *value; + char *end; + + value = hashmapGet(str_parms->map, (void *)key); + if (!value) + return -ENOENT; + + *val = (int)strtol(value, &end, 0); + if (*value != '\0' && *end == '\0') + return 0; + + return -EINVAL; +} + +int str_parms_get_float(struct str_parms *str_parms, const char *key, + float *val) +{ + float out; + char *value; + char *end; + + value = hashmapGet(str_parms->map, (void *)key); + if (!value) + return -ENOENT; + + out = strtof(value, &end); + if (*value != '\0' && *end == '\0') + return 0; + + return -EINVAL; +} + +static bool combine_strings(void *key, void *value, void *context) +{ + char **old_str = context; + char *new_str; + int ret; + + ret = asprintf(&new_str, "%s%s%s=%s", + *old_str ? *old_str : "", + *old_str ? ";" : "", + (char *)key, + (char *)value); + if (*old_str) + free(*old_str); + + if (ret >= 0) { + *old_str = new_str; + return true; + } + + *old_str = NULL; + return false; +} + +char *str_parms_to_str(struct str_parms *str_parms) +{ + char *str = NULL; + + if (hashmapSize(str_parms->map) > 0) + hashmapForEach(str_parms->map, combine_strings, &str); + else + str = strdup(""); + return str; +} + +static bool dump_entry(void *key, void *value, void *context) +{ + ALOGI("key: '%s' value: '%s'\n", (char *)key, (char *)value); + return true; +} + +void str_parms_dump(struct str_parms *str_parms) +{ + hashmapForEach(str_parms->map, dump_entry, str_parms); +} + +#ifdef TEST_STR_PARMS +static void test_str_parms_str(const char *str) +{ + struct str_parms *str_parms; + char *out_str; + int ret; + + str_parms = str_parms_create_str(str); + str_parms_add_str(str_parms, "dude", "woah"); + str_parms_add_str(str_parms, "dude", "woah"); + str_parms_del(str_parms, "dude"); + str_parms_dump(str_parms); + out_str = str_parms_to_str(str_parms); + str_parms_destroy(str_parms); + ALOGI("%s: '%s' stringified is '%s'", __func__, str, out_str); + free(out_str); +} + +int main(void) +{ + struct str_parms *str_parms; + + test_str_parms_str(""); + test_str_parms_str(";"); + test_str_parms_str("="); + test_str_parms_str("=;"); + test_str_parms_str("=bar"); + test_str_parms_str("=bar;"); + test_str_parms_str("foo="); + test_str_parms_str("foo=;"); + test_str_parms_str("foo=bar"); + test_str_parms_str("foo=bar;"); + test_str_parms_str("foo=bar;baz"); + test_str_parms_str("foo=bar;baz="); + test_str_parms_str("foo=bar;baz=bat"); + test_str_parms_str("foo=bar;baz=bat;"); + test_str_parms_str("foo=bar;baz=bat;foo=bar"); + + return 0; +} +#endif diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c new file mode 100644 index 0000000..1a8ba86 --- /dev/null +++ b/libcutils/strdup16to8.c @@ -0,0 +1,168 @@ +/* libs/cutils/strdup16to8.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 /* for SIZE_MAX */ + +#include +#include +#include + + +/** + * Given a UTF-16 string, compute the length of the corresponding UTF-8 + * string in bytes. + */ +extern size_t strnlen16to8(const char16_t* utf16Str, size_t len) +{ + size_t utf8Len = 0; + + /* A small note on integer overflow. The result can + * potentially be as big as 3*len, which will overflow + * for len > SIZE_MAX/3. + * + * Moreover, the result of a strnlen16to8 is typically used + * to allocate a destination buffer to strncpy16to8 which + * requires one more byte to terminate the UTF-8 copy, and + * this is generally done by careless users by incrementing + * the result without checking for integer overflows, e.g.: + * + * dst = malloc(strnlen16to8(utf16,len)+1) + * + * Due to this, the following code will try to detect + * overflows, and never return more than (SIZE_MAX-1) + * when it detects one. A careless user will try to malloc + * SIZE_MAX bytes, which will return NULL which can at least + * be detected appropriately. + * + * As far as I know, this function is only used by strndup16(), + * but better be safe than sorry. + */ + + /* Fast path for the usual case where 3*len is < SIZE_MAX-1. + */ + if (len < (SIZE_MAX-1)/3) { + while (len--) { + unsigned int uic = *utf16Str++; + + if (uic > 0x07ff) + utf8Len += 3; + else if (uic > 0x7f || uic == 0) + utf8Len += 2; + else + utf8Len++; + } + return utf8Len; + } + + /* The slower but paranoid version */ + while (len--) { + unsigned int uic = *utf16Str++; + size_t utf8Cur = utf8Len; + + if (uic > 0x07ff) + utf8Len += 3; + else if (uic > 0x7f || uic == 0) + utf8Len += 2; + else + utf8Len++; + + if (utf8Len < utf8Cur) /* overflow detected */ + return SIZE_MAX-1; + } + + /* don't return SIZE_MAX to avoid common user bug */ + if (utf8Len == SIZE_MAX) + utf8Len = SIZE_MAX-1; + + return utf8Len; +} + + +/** + * Convert a Java-Style UTF-16 string + length to a JNI-Style UTF-8 string. + * + * This basically means: embedded \0's in the UTF-16 string are encoded + * as "0xc0 0x80" + * + * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1, + * not just "len". + * + * Please note, a terminated \0 is always added, so your result will always + * be "strlen16to8() + 1" bytes long. + */ +extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len) +{ + char* utf8cur = utf8Str; + + /* Note on overflows: We assume the user did check the result of + * strnlen16to8() properly or at a minimum checked the result of + * its malloc(SIZE_MAX) in case of overflow. + */ + while (len--) { + unsigned int uic = *utf16Str++; + + if (uic > 0x07ff) { + *utf8cur++ = (uic >> 12) | 0xe0; + *utf8cur++ = ((uic >> 6) & 0x3f) | 0x80; + *utf8cur++ = (uic & 0x3f) | 0x80; + } else if (uic > 0x7f || uic == 0) { + *utf8cur++ = (uic >> 6) | 0xc0; + *utf8cur++ = (uic & 0x3f) | 0x80; + } else { + *utf8cur++ = uic; + + if (uic == 0) { + break; + } + } + } + + *utf8cur = '\0'; + + return utf8Str; +} + +/** + * Convert a UTF-16 string to UTF-8. + * + */ +char * strndup16to8 (const char16_t* s, size_t n) +{ + char* ret; + size_t len; + + if (s == NULL) { + return NULL; + } + + len = strnlen16to8(s, n); + + /* We are paranoid, and we check for SIZE_MAX-1 + * too since it is an overflow value for our + * strnlen16to8 implementation. + */ + if (len >= SIZE_MAX-1) + return NULL; + + ret = malloc(len + 1); + if (ret == NULL) + return NULL; + + strncpy16to8 (ret, s, n); + + return ret; +} diff --git a/libcutils/strdup8to16.c b/libcutils/strdup8to16.c new file mode 100644 index 0000000..63e5ca4 --- /dev/null +++ b/libcutils/strdup8to16.c @@ -0,0 +1,214 @@ +/* libs/cutils/strdup8to16.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 +#include +#include +#include + +/* See http://www.unicode.org/reports/tr22/ for discussion + * on invalid sequences + */ + +#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) + +/* note: macro expands to multiple lines */ +#define UTF8_SHIFT_AND_MASK(unicode, byte) \ + (unicode)<<=6; (unicode) |= (0x3f & (byte)); + +#define UNICODE_UPPER_LIMIT 0x10fffd + +/** + * out_len is an out parameter (which may not be null) containing the + * length of the UTF-16 string (which may contain embedded \0's) + */ + +extern char16_t * strdup8to16 (const char* s, size_t *out_len) +{ + char16_t *ret; + size_t len; + + if (s == NULL) return NULL; + + len = strlen8to16(s); + + // fail on overflow + if (len && SIZE_MAX/len < sizeof(char16_t)) + return NULL; + + // no plus-one here. UTF-16 strings are not null terminated + ret = (char16_t *) malloc (sizeof(char16_t) * len); + + return strcpy8to16 (ret, s, out_len); +} + +/** + * Like "strlen", but for strings encoded with Java's modified UTF-8. + * + * The value returned is the number of UTF-16 characters required + * to represent this string. + */ +extern size_t strlen8to16 (const char* utf8Str) +{ + size_t len = 0; + int ic; + int expected = 0; + + while ((ic = *utf8Str++) != '\0') { + /* bytes that start 0? or 11 are lead bytes and count as characters.*/ + /* bytes that start 10 are extention bytes and are not counted */ + + if ((ic & 0xc0) == 0x80) { + /* count the 0x80 extention bytes. if we have more than + * expected, then start counting them because strcpy8to16 + * will insert UTF16_REPLACEMENT_CHAR's + */ + expected--; + if (expected < 0) { + len++; + } + } else { + len++; + expected = UTF8_SEQ_LENGTH(ic) - 1; + + /* this will result in a surrogate pair */ + if (expected == 3) { + len++; + } + } + } + + return len; +} + + + +/* + * Retrieve the next UTF-32 character from a UTF-8 string. + * + * Stops at inner \0's + * + * Returns UTF16_REPLACEMENT_CHAR if an invalid sequence is encountered + * + * Advances "*pUtf8Ptr" to the start of the next character. + */ +static inline uint32_t getUtf32FromUtf8(const char** pUtf8Ptr) +{ + uint32_t ret; + int seq_len; + int i; + + /* Mask for leader byte for lengths 1, 2, 3, and 4 respectively*/ + static const char leaderMask[4] = {0xff, 0x1f, 0x0f, 0x07}; + + /* Bytes that start with bits "10" are not leading characters. */ + if (((**pUtf8Ptr) & 0xc0) == 0x80) { + (*pUtf8Ptr)++; + return UTF16_REPLACEMENT_CHAR; + } + + /* note we tolerate invalid leader 11111xxx here */ + seq_len = UTF8_SEQ_LENGTH(**pUtf8Ptr); + + ret = (**pUtf8Ptr) & leaderMask [seq_len - 1]; + + if (**pUtf8Ptr == '\0') return ret; + + (*pUtf8Ptr)++; + for (i = 1; i < seq_len ; i++, (*pUtf8Ptr)++) { + if ((**pUtf8Ptr) == '\0') return UTF16_REPLACEMENT_CHAR; + if (((**pUtf8Ptr) & 0xc0) != 0x80) return UTF16_REPLACEMENT_CHAR; + + UTF8_SHIFT_AND_MASK(ret, **pUtf8Ptr); + } + + return ret; +} + + +/** + * out_len is an out parameter (which may not be null) containing the + * length of the UTF-16 string (which may contain embedded \0's) + */ + +extern char16_t * strcpy8to16 (char16_t *utf16Str, const char*utf8Str, + size_t *out_len) +{ + char16_t *dest = utf16Str; + + while (*utf8Str != '\0') { + uint32_t ret; + + ret = getUtf32FromUtf8(&utf8Str); + + if (ret <= 0xffff) { + *dest++ = (char16_t) ret; + } else if (ret <= UNICODE_UPPER_LIMIT) { + /* Create surrogate pairs */ + /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */ + + *dest++ = 0xd800 | ((ret - 0x10000) >> 10); + *dest++ = 0xdc00 | ((ret - 0x10000) & 0x3ff); + } else { + *dest++ = UTF16_REPLACEMENT_CHAR; + } + } + + *out_len = dest - utf16Str; + + return utf16Str; +} + +/** + * length is the number of characters in the UTF-8 string. + * out_len is an out parameter (which may not be null) containing the + * length of the UTF-16 string (which may contain embedded \0's) + */ + +extern char16_t * strcpylen8to16 (char16_t *utf16Str, const char*utf8Str, + int length, size_t *out_len) +{ + /* TODO: Share more of this code with the method above. Only 2 lines changed. */ + + char16_t *dest = utf16Str; + + const char *end = utf8Str + length; /* This line */ + while (utf8Str < end) { /* and this line changed. */ + uint32_t ret; + + ret = getUtf32FromUtf8(&utf8Str); + + if (ret <= 0xffff) { + *dest++ = (char16_t) ret; + } else if (ret <= UNICODE_UPPER_LIMIT) { + /* Create surrogate pairs */ + /* See http://en.wikipedia.org/wiki/UTF-16/UCS-2#Method_for_code_points_in_Plane_1.2C_Plane_2 */ + + *dest++ = 0xd800 | ((ret - 0x10000) >> 10); + *dest++ = 0xdc00 | ((ret - 0x10000) & 0x3ff); + } else { + *dest++ = UTF16_REPLACEMENT_CHAR; + } + } + + *out_len = dest - utf16Str; + + return utf16Str; +} diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk new file mode 100644 index 0000000..6571161 --- /dev/null +++ b/libcutils/tests/Android.mk @@ -0,0 +1 @@ +include $(all-subdir-makefiles) diff --git a/libcutils/tests/memset_mips/Android.mk b/libcutils/tests/memset_mips/Android.mk new file mode 100644 index 0000000..c22fca9 --- /dev/null +++ b/libcutils/tests/memset_mips/Android.mk @@ -0,0 +1,23 @@ +# Copyright 2012 The Android Open Source Project + +ifeq ($(TARGET_ARCH),mips) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + test_memset.c \ + android_memset_dumb.S \ + android_memset_test.S \ + memset_cmips.S \ + memset_omips.S + +LOCAL_MODULE:= test_memset + +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) + +endif diff --git a/libcutils/tests/memset_mips/android_memset_dumb.S b/libcutils/tests/memset_mips/android_memset_dumb.S new file mode 100644 index 0000000..c8a1a37 --- /dev/null +++ b/libcutils/tests/memset_mips/android_memset_dumb.S @@ -0,0 +1,36 @@ + .global android_memset16_dumb + .type android_memset16_dumb, @function +android_memset16_dumb: + .ent android_memset16_dumb + + .set noreorder + beqz $a2,9f + srl $a2,1 + +1: sh $a1,($a0) + subu $a2,1 + bnez $a2,1b + addu $a0,2 + .set reorder + +9: j $ra + .end android_memset16_dumb + .size android_memset16_dumb,.-android_memset16_dumb + + .global android_memset32_dumb + .type android_memset32_dumb, @function +android_memset32_dumb: + .ent android_memset32_dumb + .set noreorder + beqz $a2,9f + srl $a2,2 + +1: sw $a1,($a0) + subu $a2,1 + bnez $a2,1b + addu $a0,4 + .set reorder + +9: j $ra + .end android_memset32_dumb + .size android_memset32_dumb,.-android_memset32_dumb diff --git a/libcutils/tests/memset_mips/android_memset_test.S b/libcutils/tests/memset_mips/android_memset_test.S new file mode 100644 index 0000000..e918843 --- /dev/null +++ b/libcutils/tests/memset_mips/android_memset_test.S @@ -0,0 +1,152 @@ +/* + * 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. + */ + +#ifdef NDEBUG +#define DBG # +#else +#define DBG +#endif + + .text + .align + + /* + * Optimized memset16 for MIPS + * + * void android_memset16_test(uint16_t* dst, uint16_t value, size_t size); + * + */ + + .global android_memset16_test + .type android_memset16_test, @function +android_memset16_test: + .ent android_memset16_test + .set noreorder + + /* Check parameters */ +DBG andi $t0,$a0,1 /* $a0 must be halfword aligned */ +DBG tne $t0 +DBG lui $t1,0xffff /* $a1 must be 16bits */ +DBG and $t1,$a1 +DBG tne $t1 +DBG andi $t2,$a2,1 /* $a2 must be even */ +DBG tne $t2 + +#if (__mips==32) && (__mips_isa_rev>=2) + ins $a2,$0,0,1 +#else + li $t0,~1 + and $a2,$t0 +#endif + + move $t8,$ra + blez $a2,9f /* Anything to do? */ + andi $t0,$a0,2 /* Check dst alignment */ + /* Expand value to 32 bits and check destination alignment */ +#if (__mips==32) && (__mips_isa_rev>=2) + beqz $t0,.Laligned32 /* dst is 32 bit aligned */ + ins $a1,$a1,16,16 +#else + sll $t2,$a1,16 + beqz $t0,.Laligned32 /* dst is 32 bit aligned */ + or $a1,$t2 +#endif + sh $a1,($a0) /* do one halfword to get aligned */ + subu $a2,2 + addu $a0,2 + +.Laligned32: + and $t1,$a2,63 /* is there enough left to do a full 64 byte loop? */ + beq $a2,$t1,1f + subu $t2,$a2,$t1 /* $t2 is the number of bytes to do in loop64 */ + addu $t3,$a0,$t2 /* $t3 is the end marker for loop64 */ + subu $a2,$t2 +.Lloop64: + addu $a0,64 + sw $a1,-64($a0) + sw $a1,-60($a0) + sw $a1,-56($a0) + sw $a1,-52($a0) + sw $a1,-48($a0) + sw $a1,-44($a0) + sw $a1,-40($a0) + sw $a1,-36($a0) + sw $a1,-32($a0) + sw $a1,-28($a0) + sw $a1,-24($a0) + sw $a1,-20($a0) + sw $a1,-16($a0) + sw $a1,-12($a0) + sw $a1,-8($a0) + bne $a0,$t3,.Lloop64 + sw $a1,-4($a0) + + /* Do the last 0..62 bytes */ +1: li $t0,64+12 + andi $t1,$a2,0x3c /* $t1 how many bytes to store using sw */ + bal 1f + subu $t0,$t1 /* 64+12-$t0 is offset to jump from 1f */ +1: addu $ra,$t0 + j $ra + subu $a2,$t1 +2: sw $a1,60($a0) + sw $a1,56($a0) + sw $a1,52($a0) + sw $a1,48($a0) + sw $a1,44($a0) + sw $a1,40($a0) + sw $a1,36($a0) + sw $a1,32($a0) + sw $a1,28($a0) + sw $a1,24($a0) + sw $a1,20($a0) + sw $a1,16($a0) + sw $a1,12($a0) + sw $a1,8($a0) + sw $a1,4($a0) + sw $a1,0($a0) + + beqz $a2,9f + addu $a0,$t1 + sh $a1,($a0) + +9: j $t8 + nop + .end android_memset16_test + .size android_memset16_test,.-android_memset16_test + + /* + * Optimized memset32 for MIPS + * + * void android_memset32_test(uint32_t* dst, uint32_t value, size_t size); + * + */ + .global android_memset32_test + .type android_memset32_test, @function +android_memset32_test: + .ent android_memset32_test + .set noreorder + + /* Check parameters */ +DBG andi $t0,$a0,3 /* $a0 must be word aligned */ +DBG tne $t0 +DBG andi $t2,$a2,3 /* $a2 must be a multiple of 4 bytes */ +DBG tne $t2 + + b .Laligned32 + move $t8,$ra + .end android_memset32_test + .size android_memset32_test,.-android_memset32_test diff --git a/libcutils/tests/memset_mips/memset_cmips.S b/libcutils/tests/memset_mips/memset_cmips.S new file mode 100644 index 0000000..f8f3a91 --- /dev/null +++ b/libcutils/tests/memset_mips/memset_cmips.S @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2009 + * MIPS Technologies, Inc., California. + * + * 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 MIPS Technologies, 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 MIPS TECHNOLOGIES, 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 THE MIPS TECHNOLOGIES, 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. + */ + +/************************************************************************ + * + * memset.S, version "64h" with 1 cache line horizon for "pref 30" and 14 nops + * Version: "043009" + * + ************************************************************************/ + + +/************************************************************************ + * Include files + ************************************************************************/ + +#include "machine/asm.h" + +/* + * This routine could be optimized for MIPS64. The current code only + * uses MIPS32 instructions. + */ + +#if defined(__MIPSEB__) +# define SWHI swl /* high part is left in big-endian */ +#endif + +#if defined(__MIPSEL__) +# define SWHI swr /* high part is right in little-endian */ +#endif + +#if !(defined(XGPROF) || defined(XPROF)) +#undef SETUP_GP +#define SETUP_GP +#endif + +LEAF(memset_cmips,0) + + .set noreorder + .set noat + + addu t0,a0,a2 # t0 is the "past the end" address + slti AT,a2,4 # is a2 less than 4? + bne AT,zero,.Llast4 # if yes, go to last4 + move v0,a0 # memset returns the dst pointer + + beq a1,zero,.Lset0 + subu v1,zero,a0 + + # smear byte into 32 bit word +#if (__mips==32) && (__mips_isa_rev>=2) + ins a1, a1, 8, 8 # Replicate fill byte into half-word. + ins a1, a1, 16, 16 # Replicate fill byte into word. +#else + and a1,0xff + sll AT,a1,8 + or a1,AT + sll AT,a1,16 + or a1,AT +#endif + +.Lset0: andi v1,v1,0x3 # word-unaligned address? + beq v1,zero,.Laligned # v1 is the unalignment count + subu a2,a2,v1 + SWHI a1,0(a0) + addu a0,a0,v1 + +# Here we have the "word-aligned" a0 (until the "last4") +.Laligned: + andi t8,a2,0x3f # any 64-byte chunks? + # t8 is the byte count past 64-byte chunks + beq a2,t8,.Lchk8w # when a2==t8, no 64-byte chunks + # There will be at most 1 32-byte chunk then + subu a3,a2,t8 # subtract from a2 the reminder + # Here a3 counts bytes in 16w chunks + addu a3,a0,a3 # Now a3 is the final dst after 64-byte chunks + +# Find out, if there are any 64-byte chunks after which will be still at least +# 96 bytes left. The value "96" is calculated as needed buffer for +# "pref 30,64(a0)" prefetch, which can be used as "pref 30,0(a0)" after +# incrementing "a0" by 64. +# For "a2" below 160 there will be no such "pref 30 safe" 64-byte chunk. +# + sltiu v1,a2,160 + bgtz v1,.Lloop16w_nopref30 # skip "pref 30,0(a0)" + subu t7,a2,96 # subtract "pref 30 unsafe" region + # below we have at least 1 64-byte chunk which is "pref 30 safe" + andi t6,t7,0x3f # t6 is past "64-byte safe chunks" reminder + subu t5,t7,t6 # subtract from t7 the reminder + # Here t5 counts bytes in 16w "safe" chunks + addu t4,a0,t5 # Now t4 is the dst after 64-byte "safe" chunks + +# Don't use "pref 30,0(a0)" for a0 in a "middle" of a cache line +# pref 30,0(a0) +# Here we are in the region, where it is safe to use "pref 30,64(a0)" +.Lloop16w: + addiu a0,a0,64 + pref 30,-32(a0) # continue setting up the dest, addr 64-32 + sw a1,-64(a0) + sw a1,-60(a0) + sw a1,-56(a0) + sw a1,-52(a0) + sw a1,-48(a0) + sw a1,-44(a0) + sw a1,-40(a0) + sw a1,-36(a0) + nop + nop # the extra nop instructions help to balance + nop # cycles needed for "store" + "fill" + "evict" + nop # For 64byte store there are needed 8 fill + nop # and 8 evict cycles, i.e. at least 32 instr. + nop + nop + pref 30,0(a0) # continue setting up the dest, addr 64-0 + sw a1,-32(a0) + sw a1,-28(a0) + sw a1,-24(a0) + sw a1,-20(a0) + sw a1,-16(a0) + sw a1,-12(a0) + sw a1,-8(a0) + sw a1,-4(a0) + nop + nop + nop + nop # NOTE: adding 14 nop-s instead of 12 nop-s + nop # gives better results for "fast" memory + nop + bne a0,t4,.Lloop16w + nop + + beq a0,a3,.Lchk8w # maybe no more 64-byte chunks? + nop # this "delayed slot" is useless ... + +.Lloop16w_nopref30: # there could be up to 3 "64-byte nopref30" chunks + addiu a0,a0,64 + sw a1,-64(a0) + sw a1,-60(a0) + sw a1,-56(a0) + sw a1,-52(a0) + sw a1,-48(a0) + sw a1,-44(a0) + sw a1,-40(a0) + sw a1,-36(a0) + sw a1,-32(a0) + sw a1,-28(a0) + sw a1,-24(a0) + sw a1,-20(a0) + sw a1,-16(a0) + sw a1,-12(a0) + sw a1,-8(a0) + bne a0,a3,.Lloop16w_nopref30 + sw a1,-4(a0) + +.Lchk8w: # t8 here is the byte count past 64-byte chunks + + andi t7,t8,0x1f # is there a 32-byte chunk? + # the t7 is the reminder count past 32-bytes + beq t8,t7,.Lchk1w # when t8==t7, no 32-byte chunk + move a2,t7 + + sw a1,0(a0) + sw a1,4(a0) + sw a1,8(a0) + sw a1,12(a0) + sw a1,16(a0) + sw a1,20(a0) + sw a1,24(a0) + sw a1,28(a0) + addiu a0,a0,32 + +.Lchk1w: + andi t8,a2,0x3 # now t8 is the reminder past 1w chunks + beq a2,t8,.Llast4 + subu a3,a2,t8 # a3 is the count of bytes in 1w chunks + addu a3,a0,a3 # now a3 is the dst address past the 1w chunks + +# copying in words (4-byte chunks) +.LwordCopy_loop: + addiu a0,a0,4 + bne a0,a3,.LwordCopy_loop + sw a1,-4(a0) + +.Llast4:beq a0,t0,.Llast4e +.Llast4l:addiu a0,a0,1 + bne a0,t0,.Llast4l + sb a1,-1(a0) + +.Llast4e: + j ra + nop + + .set at + .set reorder + +END(memset_cmips) + + +/************************************************************************ + * Implementation : Static functions + ************************************************************************/ + diff --git a/libcutils/tests/memset_mips/memset_omips.S b/libcutils/tests/memset_mips/memset_omips.S new file mode 100644 index 0000000..4c47001 --- /dev/null +++ b/libcutils/tests/memset_mips/memset_omips.S @@ -0,0 +1,90 @@ +/* Copyright (C) 2002, 2003 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Hartvig Ekner , 2002. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* void *memset_omips(void *s, int c, size_t n). */ + +#include "machine/asm.h" + +#ifdef __mips64 +#error mips32 code being compiled for mips64! +#endif + +#if defined(__MIPSEB__) +#error big-endian is not supported in Broadcom MIPS Android platform +# define SWHI swl /* high part is left in big-endian */ +#else +# define SWHI swr /* high part is right in little-endian */ +#endif + +LEAF (memset_omips,0) + .set noreorder + + slti t1, a2, 8 # Less than 8? + bne t1, zero, .Llast8 + move v0, a0 # Setup exit value before too late + + beq a1, zero, .Lueven # If zero pattern, no need to extend + andi a1, 0xff # Avoid problems with bogus arguments + sll t0, a1, 8 + or a1, t0 + sll t0, a1, 16 + or a1, t0 # a1 is now pattern in full word + +.Lueven: + subu t0, zero, a0 # Unaligned address? + andi t0, 0x3 + beq t0, zero, .Lchkw + subu a2, t0 + SWHI a1, 0(a0) # Yes, handle first unaligned part + addu a0, t0 # Now both a0 and a2 are updated + +.Lchkw: + andi t0, a2, 0x7 # Enough left for one loop iteration? + beq t0, a2, .Lchkl + subu a3, a2, t0 + addu a3, a0 # a3 is last loop address +1 + move a2, t0 # a2 is now # of bytes left after loop +.Lloopw: + addiu a0, 8 # Handle 2 words pr. iteration + sw a1, -8(a0) + bne a0, a3, .Lloopw + sw a1, -4(a0) + +.Lchkl: + andi t0, a2, 0x4 # Check if there is at least a full + beq t0, zero, .Llast8 # word remaining after the loop + subu a2, t0 + sw a1, 0(a0) # Yes... + addiu a0, 4 + +.Llast8: + blez a2, .Lexit # Handle last 8 bytes (if cnt>0) + addu a3, a2, a0 # a3 is last address +1 +.Llst8l: + addiu a0, 1 + bne a0, a3, .Llst8l + sb a1, -1(a0) +.Lexit: + j ra # Bye, bye + nop + + .set reorder +END (memset_omips) + + diff --git a/libcutils/tests/memset_mips/test_memset.c b/libcutils/tests/memset_mips/test_memset.c new file mode 100644 index 0000000..9705c65 --- /dev/null +++ b/libcutils/tests/memset_mips/test_memset.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include + +/* + * All systems must implement or emulate the rdhwr instruction to read + * the userlocal register. Systems that emulate also return teh count register + * when accessing register $2 so this should work on most systems + */ +#define USE_RDHWR + +#ifdef USE_RDHWR +#define UNITS "cycles" +#define SCALE 2 /* Most CPU's */ +static inline uint32_t +get_count(void) +{ + uint32_t res; + asm volatile (".set push; .set mips32r2; rdhwr %[res],$2; .set pop" : [res] "=r" (res) : : "memory"); + return res; +} +#else +#define UNITS "ns" +#define SCALE 1 +static inline uint32_t +get_count(void) +{ + struct timespec now; + uint32_t res; + clock_gettime(CLOCK_REALTIME, &now); + res = (uint32_t)(now.tv_sec * 1000000000LL + now.tv_nsec); + // printf ("now=%d.%09d res=%d\n", (int)now.tv_sec, (int)now.tv_nsec, res); + return res; +} +#endif + +uint32_t overhead; +void +measure_overhead(void) +{ + int i; + uint32_t start, stop, delta; + for (i = 0; i < 32; i++) { + start = get_count(); + stop = get_count(); + delta = stop - start; + if (overhead == 0 || delta < overhead) + overhead = delta; + } + printf("overhead is %d"UNITS"\n", overhead); +} + +uint32_t +timeone(void (*fn)(), void *d, uint32_t val, uint32_t bytes) +{ + uint32_t start, stop, delta; + start = get_count(); + (*fn)(d, val, bytes); + stop = get_count(); + delta = stop - start - overhead; + // printf ("start=0x%08x stop=0x%08x delta=0x%08x\n", start, stop, delta); + return delta * SCALE; +} + +/* define VERIFY to check that memset only touches the bytes it's supposed to */ +/*#define VERIFY*/ + +/* + * Using a big arena means that memset will most likely miss in the cache + * NB Enabling verification effectively warms up the cache... + */ +#define ARENASIZE 0x1000000 +#ifdef VERIFY +char arena[ARENASIZE+8]; /* Allow space for guard words */ +#else +char arena[ARENASIZE]; +#endif + +void +testone(char *tag, void (*fn)(), int trials, int minbytes, int maxbytes, int size, int threshold) +{ + int offset; + void *d; + void *p; + uint32_t v, notv = 0; + uint32_t n; + int i, units; + int totalunits = 0, totalbytes = 0, samples = 0; + + /* Reset RNG to ensure each test uses same random values */ + srand(0); /* FIXME should be able to use some other seed than 0 */ + + for (i = 0; i < trials; i++) { + n = minbytes + (rand() % (maxbytes-minbytes)); /* How many bytes to do */ + offset = ((rand() % (ARENASIZE-n))); /* Where to start */ + +#ifdef VERIFY + offset += 4; /* Allow space for guard word at beginning */ +#endif + v = rand(); + + /* Adjust alignment and sizes based on transfer size */ + switch (size) { + case 1: + v &= 0xff; + notv = ~v & 0xff; + break; + case 2: + v &= 0xffff; + notv = ~v & 0xffff; + offset &= ~1; + n &= ~1; + break; + case 4: + notv = ~v; + offset &= ~3; + n &= ~3; + break; + } + + d = &arena[offset]; + +#ifdef VERIFY + /* Initialise the area and guard words */ + for (p = &arena[offset-4]; p < (void *)&arena[offset+n+4]; p = (void *)((uint32_t)p + size)) { + if (size == 1) + *(uint8_t *)p = notv; + else if (size == 2) + *(uint16_t *)p = notv; + else if (size == 4) + *(uint32_t *)p = notv; + } +#endif + units = timeone(fn, d, v, n); +#ifdef VERIFY + /* Check the area and guard words */ + for (p = &arena[offset-4]; p < (void *)&arena[offset+n+4]; p = (void *)((uint32_t)p + size)) { + uint32_t got = 0; + if (size == 1) + got = *(uint8_t *)p; + else if (size == 2) + got = *(uint16_t *)p; + else if (size == 4) + got = *(uint32_t *)p; + if (p < (void *)&arena[offset]) { + if (got != notv) + printf ("%s: verify failure: preguard:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, got, n); + } + else if (p < (void *)&arena[offset+n]) { + if (got != v) + printf ("%s: verify failure: arena:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, n); + } + else { + if (got != notv) + printf ("%s: verify failure: postguard:%p d=%p v=%08x got=%08x n=%d\n", tag, p, d, v, n); + } + } +#endif + + /* If the cycle count looks reasonable include it in the statistics */ + if (units < threshold) { + totalbytes += n; + totalunits += units; + samples++; + } + } + + printf("%s: samples=%d avglen=%d avg" UNITS "=%d bp"UNITS"=%g\n", + tag, samples, totalbytes/samples, totalunits/samples, (double)totalbytes/(double)totalunits); +} + +extern void android_memset32_dumb(uint32_t* dst, uint32_t value, size_t size); +extern void android_memset16_dumb(uint32_t* dst, uint16_t value, size_t size); +extern void android_memset32_test(uint32_t* dst, uint32_t value, size_t size); +extern void android_memset16_test(uint32_t* dst, uint16_t value, size_t size); +extern void memset_cmips(void* dst, int value, size_t size); +extern void memset_omips(void* dst, int value, size_t size); + +int +main(int argc, char **argv) +{ + int i; + struct { + char *type; + int trials; + int minbytes, maxbytes; + } *pp, params[] = { + {"small", 10000, 0, 64}, + {"medium", 10000, 64, 512}, + {"large", 10000, 512, 1280}, + {"varied", 10000, 0, 1280}, + }; +#define NPARAMS (sizeof(params)/sizeof(params[0])) + struct { + char *name; + void (*fn)(); + int size; + } *fp, functions[] = { + {"dmemset16", (void (*)())android_memset16_dumb, 2}, + {"tmemset16", (void (*)())android_memset16_test, 2}, + {"lmemset16", (void (*)())android_memset16, 2}, + + {"dmemset32", (void (*)())android_memset32_dumb, 4}, + {"tmemset32", (void (*)())android_memset32_test, 4}, + {"lmemset32", (void (*)())android_memset32, 4}, + + {"cmemset", (void (*)())memset_cmips, 1}, + {"omemset", (void (*)())memset_omips, 1}, + {"lmemset", (void (*)())memset, 1}, + }; +#define NFUNCTIONS (sizeof(functions)/sizeof(functions[0])) + char tag[40]; + int threshold; + + measure_overhead(); + + /* Warm up the page cache */ + memset(arena, 0xff, ARENASIZE); /* use 0xff now to avoid COW later */ + + for (fp = functions; fp < &functions[NFUNCTIONS]; fp++) { + (fp->fn)(arena, 0xffffffff, ARENASIZE); /* one call to get the code into Icache */ + for (pp = params; pp < ¶ms[NPARAMS]; pp++) { + sprintf(tag, "%10s: %7s %4d-%4d", fp->name, pp->type, pp->minbytes, pp->maxbytes); + + /* Set the cycle threshold */ + threshold = pp->maxbytes * 4 * 10; /* reasonable for cycles and ns */ + testone(tag, fp->fn, pp->trials, pp->minbytes, pp->maxbytes, fp->size, threshold); + } + printf ("\n"); + } + + return 0; +} diff --git a/libcutils/threads.c b/libcutils/threads.c new file mode 100644 index 0000000..42cc928 --- /dev/null +++ b/libcutils/threads.c @@ -0,0 +1,84 @@ +/* libs/cutils/threads.c +** +** 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 + +#ifdef HAVE_PTHREADS +void* thread_store_get( thread_store_t* store ) +{ + const pthread_key_t k = store->tls; + + if (!store->has_tls) + return NULL; + + return pthread_getspecific( store->tls ); +} + +extern void thread_store_set( thread_store_t* store, + void* value, + thread_store_destruct_t destroy) +{ + pthread_mutex_lock( &store->lock ); + if (!store->has_tls) { + if (pthread_key_create( &store->tls, destroy) != 0) { + pthread_mutex_unlock(&store->lock); + return; + } + store->has_tls = 1; + } + pthread_mutex_unlock( &store->lock ); + + pthread_setspecific( store->tls, value ); +} + +#endif + +#ifdef HAVE_WIN32_THREADS +void* thread_store_get( thread_store_t* store ) +{ + if (!store->has_tls) + return NULL; + + return (void*) TlsGetValue( store->tls ); +} + +void thread_store_set( thread_store_t* store, + void* value, + thread_store_destruct_t destroy ) +{ + /* XXX: can't use destructor on thread exit */ + if (!store->lock_init) { + store->lock_init = -1; + InitializeCriticalSection( &store->lock ); + store->lock_init = -2; + } else while (store->lock_init != -2) { + Sleep(10); /* 10ms */ + } + + EnterCriticalSection( &store->lock ); + if (!store->has_tls) { + store->tls = TlsAlloc(); + if (store->tls == TLS_OUT_OF_INDEXES) { + LeaveCriticalSection( &store->lock ); + return; + } + store->has_tls = 1; + } + LeaveCriticalSection( &store->lock ); + + TlsSetValue( store->tls, value ); +} +#endif diff --git a/libcutils/trace.c b/libcutils/trace.c new file mode 100644 index 0000000..9754a44 --- /dev/null +++ b/libcutils/trace.c @@ -0,0 +1,186 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "cutils-trace" +#include + +volatile int32_t atrace_is_ready = 0; +int atrace_marker_fd = -1; +uint64_t atrace_enabled_tags = ATRACE_TAG_NOT_READY; +static bool atrace_is_debuggable = false; +static volatile int32_t atrace_is_enabled = 1; +static pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT; +static pthread_mutex_t atrace_tags_mutex = PTHREAD_MUTEX_INITIALIZER; + +// Set whether this process is debuggable, which determines whether +// application-level tracing is allowed when the ro.debuggable system property +// is not set to '1'. +void atrace_set_debuggable(bool debuggable) +{ + atrace_is_debuggable = debuggable; + atrace_update_tags(); +} + +// Set whether tracing is enabled in this process. This is used to prevent +// the Zygote process from tracing. +void atrace_set_tracing_enabled(bool enabled) +{ + android_atomic_release_store(enabled ? 1 : 0, &atrace_is_enabled); + atrace_update_tags(); +} + +// Check whether the given command line matches one of the comma-separated +// values listed in the app_cmdlines property. +static bool atrace_is_cmdline_match(const char* cmdline) +{ + 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) { + return true; + } + + start = end; + } + + return false; +} + +// Determine whether application-level tracing is enabled for this process. +static bool atrace_is_app_tracing_enabled() +{ + bool sys_debuggable = false; + bool proc_debuggable = false; + char value[PROPERTY_VALUE_MAX]; + 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"); + if (file) { + char cmdline[4096]; + if (fgets(cmdline, sizeof(cmdline), file)) { + result = atrace_is_cmdline_match(cmdline); + } else { + ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno); + } + fclose(file); + } else { + ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno), + errno); + } + } + + return result; +} + +// Read the sysprop and return the value tags should be set to +static uint64_t atrace_get_property() +{ + char value[PROPERTY_VALUE_MAX]; + char *endptr; + uint64_t tags; + + property_get("debug.atrace.tags.enableflags", value, "0"); + errno = 0; + tags = strtoull(value, &endptr, 0); + if (value[0] == '\0' || *endptr != '\0') { + ALOGE("Error parsing trace property: Not a number: %s", value); + return 0; + } else if (errno == ERANGE || tags == ULLONG_MAX) { + ALOGE("Error parsing trace property: Number too large: %s", value); + return 0; + } + + // Only set the "app" tag if this process was selected for app-level debug + // tracing. + if (atrace_is_app_tracing_enabled()) { + tags |= ATRACE_TAG_APP; + } else { + tags &= ~ATRACE_TAG_APP; + } + + return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK; +} + +// Update tags if tracing is ready. Useful as a sysprop change callback. +void atrace_update_tags() +{ + uint64_t tags; + if (CC_UNLIKELY(android_atomic_acquire_load(&atrace_is_ready))) { + if (android_atomic_acquire_load(&atrace_is_enabled)) { + tags = atrace_get_property(); + pthread_mutex_lock(&atrace_tags_mutex); + atrace_enabled_tags = tags; + pthread_mutex_unlock(&atrace_tags_mutex); + } else { + // Tracing is disabled for this process, so we simply don't + // initialize the tags. + pthread_mutex_lock(&atrace_tags_mutex); + atrace_enabled_tags = ATRACE_TAG_NOT_READY; + pthread_mutex_unlock(&atrace_tags_mutex); + } + } +} + +static void atrace_init_once() +{ + atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY); + if (atrace_marker_fd == -1) { + ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno); + atrace_enabled_tags = 0; + goto done; + } + + atrace_enabled_tags = atrace_get_property(); + +done: + android_atomic_release_store(1, &atrace_is_ready); +} + +void atrace_setup() +{ + pthread_once(&atrace_once_control, atrace_init_once); +} diff --git a/libcutils/uevent.c b/libcutils/uevent.c new file mode 100644 index 0000000..97a81e3 --- /dev/null +++ b/libcutils/uevent.c @@ -0,0 +1,120 @@ +/* + * 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 + +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * Like recv(), but checks that messages actually originate from the kernel. + */ +ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) +{ + uid_t user = -1; + return uevent_kernel_multicast_uid_recv(socket, buffer, length, &user); +} + +/** + * Like the above, but passes a uid_t in by reference. In the event that this + * fails due to a bad uid check, the uid_t will be set to the uid of the + * socket's peer. + * + * If this method rejects a netlink message from outside the kernel, it + * returns -1, sets errno to EIO, and sets "user" to the UID associated with the + * message. If the peer UID cannot be determined, "user" is set to -1." + */ +ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, + size_t length, uid_t *user) +{ + struct iovec iov = { buffer, length }; + struct sockaddr_nl addr; + char control[CMSG_SPACE(sizeof(struct ucred))]; + struct msghdr hdr = { + &addr, + sizeof(addr), + &iov, + 1, + control, + sizeof(control), + 0, + }; + + *user = -1; + ssize_t n = recvmsg(socket, &hdr, 0); + if (n <= 0) { + return n; + } + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr); + if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { + /* ignoring netlink message with no sender credentials */ + goto out; + } + + struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg); + *user = cred->uid; + if (cred->uid != 0) { + /* ignoring netlink message from non-root user */ + goto out; + } + + if (addr.nl_groups == 0 || addr.nl_pid != 0) { + /* ignoring non-kernel or unicast netlink message */ + goto out; + } + + return n; + +out: + /* clear residual potentially malicious data */ + bzero(buffer, length); + errno = EIO; + return -1; +} + +int uevent_open_socket(int buf_sz, bool passcred) +{ + struct sockaddr_nl addr; + int on = passcred; + int s; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_pid = getpid(); + addr.nl_groups = 0xffffffff; + + s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if(s < 0) + return -1; + + setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)); + setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + + if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(s); + return -1; + } + + return s; +} diff --git a/liblog/Android.mk b/liblog/Android.mk new file mode 100644 index 0000000..6bfb119 --- /dev/null +++ b/liblog/Android.mk @@ -0,0 +1,81 @@ +# +# 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) + +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 +WITH_MINGW := +ifeq ($(HOST_OS),windows) + ifeq ($(strip $(USE_CYGWIN)),) + WITH_MINGW := true + endif +endif +# USE_MINGW is defined when we build against Mingw on Linux +ifneq ($(strip $(USE_MINGW)),) + WITH_MINGW := true +endif + +ifndef WITH_MINGW + liblog_sources += \ + logprint.c \ + event_tag_map.c +else + liblog_sources += \ + uio.c +endif + +liblog_host_sources := $(liblog_sources) fake_log_device.c + + +# Shared and static library for host +# ======================================================== +LOCAL_MODULE := liblog +LOCAL_SRC_FILES := $(liblog_host_sources) +LOCAL_LDLIBS := -lpthread +LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 +include $(BUILD_HOST_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := liblog +LOCAL_WHOLE_STATIC_LIBRARIES := liblog +include $(BUILD_HOST_SHARED_LIBRARY) + + +# Static library for host, 64-bit +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE := lib64log +LOCAL_SRC_FILES := $(liblog_host_sources) +LOCAL_LDLIBS := -lpthread +LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -m64 +include $(BUILD_HOST_STATIC_LIBRARY) + + +# Shared and static library for target +# ======================================================== +include $(CLEAR_VARS) +LOCAL_MODULE := liblog +LOCAL_SRC_FILES := $(liblog_sources) +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := liblog +LOCAL_WHOLE_STATIC_LIBRARIES := liblog +include $(BUILD_SHARED_LIBRARY) diff --git a/liblog/Makefile b/liblog/Makefile new file mode 100644 index 0000000..37e6d3b --- /dev/null +++ b/liblog/Makefile @@ -0,0 +1,27 @@ +TARGET = liblog.a +INSTALL_DIR = ../lib + +SRCS += \ + logd_write.c \ + uio.c + +CC = $(CROSS_COMPILE)gcc +AR = $(CROSS_COMPILE)ar + +CFLAGS := -I../include + +OBJS = $(SRCS:.c=.o) + +all: $(TARGET) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(TARGET): $(OBJS) + $(AR) r $@ $(OBJS) + [ -f $(INSTALL_DIR) ] || mkdir -p $(INSTALL_DIR) + cp $(TARGET) $(INSTALL_DIR) + +.PHONY: clean +clean: + rm -rf $(OBJS) $(TARGET) diff --git a/liblog/NOTICE b/liblog/NOTICE new file mode 100644 index 0000000..c5b1efa --- /dev/null +++ b/liblog/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-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. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c new file mode 100644 index 0000000..f3d1e2f --- /dev/null +++ b/liblog/event_tag_map.c @@ -0,0 +1,438 @@ +/* + * 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 +#include + +#include +#include +#include +#include +#include +#include + +#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); +static void dumpTags(const 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) +{ + EventTag* tagArray = NULL; + + /* 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; +} + +/* + * Dump the tag array for debugging. + */ +static void dumpTags(const EventTagMap* map) +{ + int i; + + for (i = 0; i < map->numTags; i++) { + const EventTag* tag = &map->tagArray[i]; + printf(" %3d: %6d '%s'\n", i, tag->tagIndex, tag->tagStr); + } +} + diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c new file mode 100644 index 0000000..5283619 --- /dev/null +++ b/liblog/fake_log_device.c @@ -0,0 +1,685 @@ +/* + * 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. + */ +/* + * Intercepts log messages intended for the Android log device. + * When running in the context of the simulator, the messages are + * passed on to the underlying (fake) log device. When not in the + * simulator, messages are printed to stderr. + */ +#include + +#include +#include +#include +#include +#include + +#ifdef HAVE_PTHREADS +#include +#endif + +#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */ + +#define kTagSetSize 16 /* arbitrary */ + +#if 0 +#define TRACE(...) printf("fake_log_device: " __VA_ARGS__) +#else +#define TRACE(...) ((void)0) +#endif + +/* from the long-dead utils/Log.cpp */ +typedef enum { + FORMAT_OFF = 0, + FORMAT_BRIEF, + FORMAT_PROCESS, + FORMAT_TAG, + FORMAT_THREAD, + FORMAT_RAW, + FORMAT_TIME, + FORMAT_THREADTIME, + FORMAT_LONG +} LogFormat; + + +/* + * Log driver state. + */ +typedef struct LogState { + /* the fake fd that's seen by the user */ + int fakeFd; + + /* a printable name for this fake device */ + char *debugName; + + /* nonzero if this is a binary log */ + int isBinary; + + /* global minimum priority */ + int globalMinPriority; + + /* output format */ + LogFormat outputFormat; + + /* tags and priorities */ + struct { + char tag[kMaxTagLen]; + int minPriority; + } tagSet[kTagSetSize]; +} LogState; + + +#ifdef HAVE_PTHREADS +/* + * Locking. Since we're emulating a device, we need to be prepared + * to have multiple callers at the same time. This lock is used + * to both protect the fd list and to prevent LogStates from being + * freed out from under a user. + */ +static pthread_mutex_t fakeLogDeviceLock = PTHREAD_MUTEX_INITIALIZER; + +static void lock() +{ + pthread_mutex_lock(&fakeLogDeviceLock); +} + +static void unlock() +{ + pthread_mutex_unlock(&fakeLogDeviceLock); +} +#else // !HAVE_PTHREADS +#define lock() ((void)0) +#define unlock() ((void)0) +#endif // !HAVE_PTHREADS + + +/* + * File descriptor management. + */ +#define FAKE_FD_BASE 10000 +#define MAX_OPEN_LOGS 16 +static LogState *openLogTable[MAX_OPEN_LOGS]; + +/* + * Allocate an fd and associate a new LogState with it. + * The fd is available via the fakeFd field of the return value. + */ +static LogState *createLogState() +{ + 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]; + } + } + return NULL; +} + +/* + * Translate an fd to a LogState. + */ +static LogState *fdToLogState(int fd) +{ + if (fd >= FAKE_FD_BASE && fd < FAKE_FD_BASE + MAX_OPEN_LOGS) { + return openLogTable[fd - FAKE_FD_BASE]; + } + return NULL; +} + +/* + * Unregister the fake fd and free the memory it pointed to. + */ +static void deleteFakeFd(int fd) +{ + LogState *ls; + + lock(); + + ls = fdToLogState(fd); + if (ls != NULL) { + openLogTable[fd - FAKE_FD_BASE] = NULL; + free(ls->debugName); + free(ls); + } + + unlock(); +} + +/* + * 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. + * + * We also want to check ANDROID_PRINTF_LOG to determine how the output + * will look. + */ +static void configureInitialState(const char* pathName, LogState* logState) +{ + static const int kDevLogLen = sizeof("/dev/log/") - 1; + + logState->debugName = strdup(pathName); + + /* identify binary logs */ + if (strcmp(pathName + kDevLogLen, "events") == 0) { + logState->isBinary = 1; + } + + /* global min priority defaults to "info" level */ + logState->globalMinPriority = ANDROID_LOG_INFO; + + /* + * This is based on the the long-dead utils/Log.cpp code. + */ + const char* tags = getenv("ANDROID_LOG_TAGS"); + TRACE("Found ANDROID_LOG_TAGS='%s'\n", tags); + if (tags != NULL) { + int entry = 0; + + while (*tags != '\0') { + char tagName[kMaxTagLen]; + int i, minPrio; + + while (isspace(*tags)) + tags++; + + i = 0; + while (*tags != '\0' && !isspace(*tags) && *tags != ':' && + i < kMaxTagLen) + { + tagName[i++] = *tags++; + } + if (i == kMaxTagLen) { + TRACE("ERROR: env tag too long (%d chars max)\n", kMaxTagLen-1); + return; + } + tagName[i] = '\0'; + + /* default priority, if there's no ":" part; also zero out '*' */ + minPrio = ANDROID_LOG_VERBOSE; + if (tagName[0] == '*' && tagName[1] == '\0') { + minPrio = ANDROID_LOG_DEBUG; + tagName[0] = '\0'; + } + + if (*tags == ':') { + tags++; + if (*tags >= '0' && *tags <= '9') { + if (*tags >= ('0' + ANDROID_LOG_SILENT)) + minPrio = ANDROID_LOG_VERBOSE; + else + minPrio = *tags - '\0'; + } else { + switch (*tags) { + case 'v': minPrio = ANDROID_LOG_VERBOSE; break; + case 'd': minPrio = ANDROID_LOG_DEBUG; break; + case 'i': minPrio = ANDROID_LOG_INFO; break; + case 'w': minPrio = ANDROID_LOG_WARN; break; + case 'e': minPrio = ANDROID_LOG_ERROR; break; + case 'f': minPrio = ANDROID_LOG_FATAL; break; + case 's': minPrio = ANDROID_LOG_SILENT; break; + default: minPrio = ANDROID_LOG_DEFAULT; break; + } + } + + tags++; + if (*tags != '\0' && !isspace(*tags)) { + TRACE("ERROR: garbage in tag env; expected whitespace\n"); + TRACE(" env='%s'\n", tags); + return; + } + } + + if (tagName[0] == 0) { + logState->globalMinPriority = minPrio; + TRACE("+++ global min prio %d\n", logState->globalMinPriority); + } else { + logState->tagSet[entry].minPriority = minPrio; + strcpy(logState->tagSet[entry].tag, tagName); + TRACE("+++ entry %d: %s:%d\n", + entry, + logState->tagSet[entry].tag, + logState->tagSet[entry].minPriority); + entry++; + } + } + } + + + /* + * Taken from the long-dead utils/Log.cpp + */ + const char* fstr = getenv("ANDROID_PRINTF_LOG"); + LogFormat format; + if (fstr == NULL) { + format = FORMAT_BRIEF; + } else { + if (strcmp(fstr, "brief") == 0) + format = FORMAT_BRIEF; + else if (strcmp(fstr, "process") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "tag") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "thread") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "raw") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "time") == 0) + format = FORMAT_PROCESS; + else if (strcmp(fstr, "long") == 0) + format = FORMAT_PROCESS; + else + format = (LogFormat) atoi(fstr); // really?! + } + + logState->outputFormat = format; +} + +/* + * Return a human-readable string for the priority level. Always returns + * a valid string. + */ +static const char* getPriorityString(int priority) +{ + /* the first character of each string should be unique */ + static const char* priorityStrings[] = { + "Verbose", "Debug", "Info", "Warn", "Error", "Assert" + }; + int idx; + + idx = (int) priority - (int) ANDROID_LOG_VERBOSE; + if (idx < 0 || + idx >= (int) (sizeof(priorityStrings) / sizeof(priorityStrings[0]))) + return "?unknown?"; + return priorityStrings[idx]; +} + +#ifndef HAVE_WRITEV +/* + * Some platforms like WIN32 do not have writev(). + * Make up something to replace it. + */ +static ssize_t fake_writev(int fd, const struct iovec *iov, int iovcnt) { + int result = 0; + struct iovec* end = iov + iovcnt; + for (; iov < end; iov++) { + int w = write(fd, iov->iov_base, iov->iov_len); + if (w != iov->iov_len) { + if (w < 0) + return w; + return result + w; + } + result += w; + } + return result; +} + +#define writev fake_writev +#endif + + +/* + * Write a filtered log message to stderr. + * + * Log format parsing taken from the long-dead utils/Log.cpp. + */ +static void showLog(LogState *state, + int logPrio, const char* tag, const char* msg) +{ +#if defined(HAVE_LOCALTIME_R) + struct tm tmBuf; +#endif + struct tm* ptm; + char timeBuf[32]; + char prefixBuf[128], suffixBuf[128]; + char priChar; + time_t when; + pid_t pid, tid; + + TRACE("LOG %d: %s %s", logPrio, tag, msg); + + priChar = getPriorityString(logPrio)[0]; + when = time(NULL); + pid = tid = getpid(); // find gettid()? + + /* + * Get the current date/time in pretty form + * + * It's often useful when examining a log with "less" to jump to + * a specific point in the file by searching for the date/time stamp. + * 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. + */ +#if defined(HAVE_LOCALTIME_R) + ptm = localtime_r(&when, &tmBuf); +#else + ptm = localtime(&when); +#endif + //strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", ptm); + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + /* + * Construct a buffer containing the log header and log message. + */ + size_t prefixLen, suffixLen; + + switch (state->outputFormat) { + case FORMAT_TAG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s: ", priChar, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_PROCESS: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d) ", priChar, pid); + suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), + " (%s)\n", tag); + break; + case FORMAT_THREAD: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d:%5d) ", priChar, pid, tid); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_RAW: + prefixBuf[0] = 0; prefixLen = 0; + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_TIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s %-8s\n\t", timeBuf, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_THREADTIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s %5d %5d %c %-8s \n\t", timeBuf, pid, tid, priChar, tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_LONG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "[ %s %5d:%5d %c/%-8s ]\n", + timeBuf, pid, tid, priChar, tag); + strcpy(suffixBuf, "\n\n"); suffixLen = 2; + break; + default: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s(%5d): ", priChar, tag, pid); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + } + + /* + * Figure out how many lines there will be. + */ + const char* end = msg + strlen(msg); + size_t numLines = 0; + const char* p = msg; + while (p < end) { + if (*p++ == '\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 MAX_LINES = ((size_t)~0)/(3*sizeof(struct iovec*)); + struct iovec stackVec[INLINE_VECS]; + struct iovec* vec = stackVec; + size_t numVecs; + + if (numLines > MAX_LINES) + numLines = MAX_LINES; + + 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; + vec = stackVec; + } + } + + /* + * Fill in the iovec pointers. + */ + p = msg; + struct iovec* v = vec; + int totalLen = 0; + while (numLines > 0 && p < end) { + if (prefixLen > 0) { + v->iov_base = prefixBuf; + v->iov_len = prefixLen; + totalLen += prefixLen; + v++; + } + const char* start = p; + while (p < end && *p != '\n') p++; + if ((p-start) > 0) { + v->iov_base = (void*)start; + v->iov_len = p-start; + totalLen += p-start; + v++; + } + if (*p == '\n') p++; + if (suffixLen > 0) { + v->iov_base = suffixBuf; + v->iov_len = suffixLen; + totalLen += suffixLen; + v++; + } + 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* + * because of multi-threading and multi-process issues. + * + * If the file was not opened with O_APPEND, this will produce interleaved + * output when called on the same file from multiple processes. + * + * If the file descriptor is actually a network socket, the writev() + * call may return with a partial write. Putting the writev() call in + * a loop can result in interleaved data. This can be alleviated + * somewhat by wrapping the writev call in the Mutex. + */ + + for(;;) { + 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; + } else { + /* shouldn't happen when writing to file or tty */ + fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", cc, totalLen); + break; + } + } + + /* if we allocated storage for the iovecs, free it */ + if (vec != stackVec) + free(vec); +} + + +/* + * Receive a log message. We happen to know that "vector" has three parts: + * + * priority (1 byte) + * tag (N bytes -- null-terminated ASCII string) + * message (N bytes -- null-terminated ASCII string) + */ +static ssize_t logWritev(int fd, const struct iovec* vector, int count) +{ + LogState* state; + + /* Make sure that no-one frees the LogState while we're using it. + * Also guarantees that only one thread is in showLog() at a given + * time (if it matters). + */ + lock(); + + state = fdToLogState(fd); + if (state == NULL) { + errno = EBADF; + goto error; + } + + if (state->isBinary) { + TRACE("%s: ignoring binary log\n", state->debugName); + goto bail; + } + + if (count != 3) { + TRACE("%s: writevLog with count=%d not expected\n", + state->debugName, count); + goto error; + } + + /* pull out the three fields */ + int logPrio = *(const char*)vector[0].iov_base; + const char* tag = (const char*) vector[1].iov_base; + const char* msg = (const char*) vector[2].iov_base; + + /* see if this log tag is configured */ + int i; + int minPrio = state->globalMinPriority; + for (i = 0; i < kTagSetSize; i++) { + if (state->tagSet[i].minPriority == ANDROID_LOG_UNKNOWN) + break; /* reached end of configured values */ + + if (strcmp(state->tagSet[i].tag, tag) == 0) { + //TRACE("MATCH tag '%s'\n", tag); + minPrio = state->tagSet[i].minPriority; + break; + } + } + + if (logPrio >= minPrio) { + showLog(state, logPrio, tag, msg); + } else { + //TRACE("+++ NOLOG(%d): %s %s", logPrio, tag, msg); + } + +bail: + unlock(); + return vector[0].iov_len + vector[1].iov_len + vector[2].iov_len; +error: + unlock(); + return -1; +} + +/* + * Free up our state and close the fake descriptor. + */ +static int logClose(int fd) +{ + deleteFakeFd(fd); + return 0; +} + +/* + * Open a log output device and return a fake fd. + */ +static int logOpen(const char* pathName, int flags) +{ + LogState *logState; + int fd = -1; + + lock(); + + logState = createLogState(); + if (logState != NULL) { + configureInitialState(pathName, logState); + fd = logState->fakeFd; + } else { + errno = ENFILE; + } + + unlock(); + + return fd; +} + + +/* + * Runtime redirection. If this binary is running in the simulator, + * just pass log messages to the emulated device. If it's running + * outside of the simulator, write the log messages to stderr. + */ + +static int (*redirectOpen)(const char *pathName, int flags) = NULL; +static int (*redirectClose)(int fd) = NULL; +static ssize_t (*redirectWritev)(int fd, const struct iovec* vector, int count) + = NULL; + +static void setRedirects() +{ + const char *ws; + + /* Wrapsim sets this environment variable on children that it's + * created using its LD_PRELOAD wrapper. + */ + ws = getenv("ANDROID_WRAPSIM"); + if (ws != NULL && strcmp(ws, "1") == 0) { + /* We're running inside wrapsim, so we can just write to the device. */ + redirectOpen = (int (*)(const char *pathName, int flags))open; + redirectClose = close; + redirectWritev = writev; + } else { + /* There's no device to delegate to; handle the logging ourselves. */ + redirectOpen = logOpen; + redirectClose = logClose; + redirectWritev = logWritev; + } +} + +int fakeLogOpen(const char *pathName, int flags) +{ + if (redirectOpen == NULL) { + setRedirects(); + } + return redirectOpen(pathName, flags); +} + +int fakeLogClose(int fd) +{ + /* Assume that open() was called first. */ + return redirectClose(fd); +} + +ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count) +{ + /* Assume that open() was called first. */ + return redirectWritev(fd, vector, count); +} diff --git a/liblog/logd_write.c b/liblog/logd_write.c new file mode 100644 index 0000000..fff7cc4 --- /dev/null +++ b/liblog/logd_write.c @@ -0,0 +1,294 @@ +/* + * 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 +#include +#ifdef HAVE_PTHREADS +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define LOG_BUF_SIZE 1024 + +#if FAKE_LOG_DEVICE +// This will be defined when building for the host. +#define log_open(pathname, flags) fakeLogOpen(pathname, flags) +#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count) +#define log_close(filedes) fakeLogClose(filedes) +#else +#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC) +#define log_writev(filedes, vector, count) writev(filedes, vector, count) +#define log_close(filedes) close(filedes) +#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; +#ifdef HAVE_PTHREADS +static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; + +/* + * This is used by the C++ code to decide if it should write logs through + * the C code. Basically, if /dev/log/... 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/"LOGGER_LOG_MAIN, W_OK) == 0) + g_log_status = kLogAvailable; + else + g_log_status = kLogNotAvailable; + } + + return (g_log_status == kLogAvailable); +} + +static int __write_to_log_null(log_id_t log_fd, struct iovec *vec, size_t nr) +{ + return -1; +} + +static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr) +{ + ssize_t ret; + 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 = log_writev(log_fd, vec, nr); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr) +{ +#ifdef HAVE_PTHREADS + pthread_mutex_lock(&log_init_lock); +#endif + + if (write_to_log == __write_to_log_init) { + log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY); + log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY); + log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY); + log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY); + + write_to_log = __write_to_log_kernel; + + if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 || + log_fds[LOG_ID_EVENTS] < 0) { + log_close(log_fds[LOG_ID_MAIN]); + log_close(log_fds[LOG_ID_RADIO]); + log_close(log_fds[LOG_ID_EVENTS]); + log_fds[LOG_ID_MAIN] = -1; + log_fds[LOG_ID_RADIO] = -1; + log_fds[LOG_ID_EVENTS] = -1; + write_to_log = __write_to_log_null; + } + + if (log_fds[LOG_ID_SYSTEM] < 0) { + log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; + } + } + +#ifdef HAVE_PTHREADS + 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) +{ + struct iovec vec[3]; + log_id_t log_id = LOG_ID_MAIN; + char tmp_tag[32]; + + if (!tag) + tag = ""; + + /* XXX: This needs to go! */ + if (!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")) { + log_id = 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; + } + + 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(log_id, vec, 3); +} + +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; + } + + 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); + + __builtin_trap(); /* trap so we have a chance to debug the situation */ +} + +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); +} diff --git a/liblog/logprint.c b/liblog/logprint.c new file mode 100644 index 0000000..508c825 --- /dev/null +++ b/liblog/logprint.c @@ -0,0 +1,1016 @@ +/* //device/libs/cutils/logprint.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. +*/ + +#define _GNU_SOURCE /* for asprintf */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +typedef struct FilterInfo_t { + char *mTag; + android_LogPriority mPri; + struct FilterInfo_t *p_next; +} FilterInfo; + +struct AndroidLogFormat_t { + android_LogPriority global_pri; + FilterInfo *filters; + AndroidLogPrintFormat format; +}; + +static FilterInfo * filterinfo_new(const char * tag, android_LogPriority pri) +{ + FilterInfo *p_ret; + + p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo)); + p_ret->mTag = strdup(tag); + p_ret->mPri = pri; + + return p_ret; +} + +static void filterinfo_free(FilterInfo *p_info) +{ + if (p_info == NULL) { + return; + } + + free(p_info->mTag); + p_info->mTag = NULL; +} + +/* + * 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 char filterPriToChar (android_LogPriority pri) +{ + switch (pri) { + case ANDROID_LOG_VERBOSE: return 'V'; + case ANDROID_LOG_DEBUG: return 'D'; + case ANDROID_LOG_INFO: return 'I'; + case ANDROID_LOG_WARN: return 'W'; + case ANDROID_LOG_ERROR: return 'E'; + case ANDROID_LOG_FATAL: return 'F'; + case ANDROID_LOG_SILENT: return 'S'; + + case ANDROID_LOG_DEFAULT: + case ANDROID_LOG_UNKNOWN: + default: return '?'; + } +} + +static android_LogPriority filterPriForTag( + AndroidLogFormat *p_format, const char *tag) +{ + FilterInfo *p_curFilter; + + for (p_curFilter = p_format->filters + ; p_curFilter != NULL + ; p_curFilter = p_curFilter->p_next + ) { + if (0 == strcmp(tag, p_curFilter->mTag)) { + if (p_curFilter->mPri == ANDROID_LOG_DEFAULT) { + return p_format->global_pri; + } else { + return p_curFilter->mPri; + } + } + } + + return p_format->global_pri; +} + +/** for debugging */ +static void dumpFilters(AndroidLogFormat *p_format) +{ + FilterInfo *p_fi; + + for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) { + char cPri = filterPriToChar(p_fi->mPri); + if (p_fi->mPri == ANDROID_LOG_DEFAULT) { + cPri = filterPriToChar(p_format->global_pri); + } + fprintf(stderr,"%s:%c\n", p_fi->mTag, cPri); + } + + fprintf(stderr,"*:%c\n", filterPriToChar(p_format->global_pri)); + +} + +/** + * 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) +{ + return pri >= filterPriForTag(p_format, tag); +} + +AndroidLogFormat *android_log_format_new() +{ + AndroidLogFormat *p_ret; + + p_ret = calloc(1, sizeof(AndroidLogFormat)); + + p_ret->global_pri = ANDROID_LOG_VERBOSE; + p_ret->format = FORMAT_BRIEF; + + return p_ret; +} + +void android_log_format_free(AndroidLogFormat *p_format) +{ + FilterInfo *p_info, *p_info_old; + + p_info = p_format->filters; + + while (p_info != NULL) { + p_info_old = p_info; + p_info = p_info->p_next; + + free(p_info_old); + } + + free(p_format); +} + + + +void android_log_setPrintFormat(AndroidLogFormat *p_format, + AndroidLogPrintFormat format) +{ + p_format->format=format; +} + +/** + * Returns FORMAT_OFF on invalid string + */ +AndroidLogPrintFormat android_log_formatFromString(const char * formatString) +{ + static AndroidLogPrintFormat format; + + if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF; + else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS; + else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG; + else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD; + else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW; + else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME; + else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME; + else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG; + else format = FORMAT_OFF; + + return format; +} + +/** + * 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) +{ + size_t i=0; + size_t tagNameLength; + android_LogPriority pri = ANDROID_LOG_DEFAULT; + + tagNameLength = strcspn(filterExpression, ":"); + + if (tagNameLength == 0) { + goto error; + } + + if(filterExpression[tagNameLength] == ':') { + pri = filterCharToPri(filterExpression[tagNameLength+1]); + + if (pri == ANDROID_LOG_UNKNOWN) { + goto error; + } + } + + if(0 == strncmp("*", filterExpression, tagNameLength)) { + // This filter expression refers to the global filter + // The default level for this is DEBUG if the priority + // is unspecified + if (pri == ANDROID_LOG_DEFAULT) { + pri = ANDROID_LOG_DEBUG; + } + + p_format->global_pri = pri; + } else { + // for filter expressions that don't refer to the global + // filter, the default is verbose if the priority is unspecified + if (pri == ANDROID_LOG_DEFAULT) { + pri = ANDROID_LOG_VERBOSE; + } + + char *tagName; + +// Presently HAVE_STRNDUP is never defined, so the second case is always taken +// Darwin doesn't have strnup, everything else does +#ifdef HAVE_STRNDUP + tagName = strndup(filterExpression, tagNameLength); +#else + //a few extra bytes copied... + tagName = strdup(filterExpression); + tagName[tagNameLength] = '\0'; +#endif /*HAVE_STRNDUP*/ + + FilterInfo *p_fi = filterinfo_new(tagName, pri); + free(tagName); + + p_fi->p_next = p_format->filters; + p_format->filters = p_fi; + } + + return 0; +error: + return -1; +} + + +/** + * filterString: a comma/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) +{ + char *filterStringCopy = strdup (filterString); + char *p_cur = filterStringCopy; + char *p_ret; + int err; + + // Yes, I'm using strsep + while (NULL != (p_ret = strsep(&p_cur, " \t,"))) { + // ignore whitespace-only entries + if(p_ret[0] != '\0') { + err = android_log_addFilterRule(p_format, p_ret); + + if (err < 0) { + goto error; + } + } + } + + free (filterStringCopy); + return 0; +error: + free (filterStringCopy); + return -1; +} + +static inline char * strip_end(char *str) +{ + char *end = str + strlen(str) - 1; + + while (end >= str && isspace(*end)) + *end-- = '\0'; + return str; +} + +/** + * 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) +{ + entry->tv_sec = buf->sec; + entry->tv_nsec = buf->nsec; + entry->pid = buf->pid; + entry->tid = buf->tid; + + /* + * format: \0\0 + * + * tag str + * starts at buf->msg+1 + * msg + * starts at buf->msg+1+len(tag)+1 + * + * The message may have been truncated by the kernel log driver. + * When that happens, we must null-terminate the message ourselves. + */ + if (buf->len < 3) { + // An well-formed entry must consist of at least a priority + // and two null characters + fprintf(stderr, "+++ LOG: entry too small\n"); + return -1; + } + + int msgStart = -1; + int msgEnd = -1; + + int i; + for (i = 1; i < buf->len; i++) { + if (buf->msg[i] == '\0') { + if (msgStart == -1) { + msgStart = i + 1; + } else { + msgEnd = i; + break; + } + } + } + + if (msgStart == -1) { + fprintf(stderr, "+++ LOG: malformed log message\n"); + return -1; + } + if (msgEnd == -1) { + // incoming message not null-terminated; force it + msgEnd = buf->len - 1; + buf->msg[msgEnd] = '\0'; + } + + entry->priority = buf->msg[0]; + entry->tag = buf->msg + 1; + entry->message = buf->msg + msgStart; + entry->messageLen = msgEnd - msgStart; + + return 0; +} + +/* + * 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, high; + + low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); + high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24); + return ((long long) high << 32) | (long long) low; +} + + +/* + * Recursively convert binary log data to printable form. + * + * This needs to be recursive because you can have lists of lists. + * + * If we run out of room, we stop processing immediately. It's important + * for us to check for space on every output element to avoid producing + * garbled output. + * + * Returns 0 on success, 1 on buffer full, -1 on failure. + */ +static int android_log_printBinaryEvent(const unsigned char** pEventData, + size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen) +{ + const unsigned char* eventData = *pEventData; + size_t eventDataLen = *pEventDataLen; + char* outBuf = *pOutBuf; + size_t outBufLen = *pOutBufLen; + unsigned char type; + size_t outCount; + int result = 0; + + if (eventDataLen < 1) + return -1; + type = *eventData++; + eventDataLen--; + + //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen); + + switch (type) { + case EVENT_TYPE_INT: + /* 32-bit signed int */ + { + int ival; + + 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; + } + } + break; + case EVENT_TYPE_LONG: + /* 64-bit signed long */ + { + long long lval; + + if (eventDataLen < 8) + return -1; + lval = get8LE(eventData); + eventData += 8; + eventDataLen -= 8; + + outCount = snprintf(outBuf, outBufLen, "%lld", lval); + if (outCount < outBufLen) { + outBuf += outCount; + outBufLen -= outCount; + } else { + /* halt output */ + goto no_room; + } + } + break; + case EVENT_TYPE_STRING: + /* UTF-8 chars, not NULL-terminated */ + { + unsigned int strLen; + + if (eventDataLen < 4) + return -1; + strLen = get4LE(eventData); + eventData += 4; + eventDataLen -= 4; + + if (eventDataLen < strLen) + return -1; + + if (strLen < outBufLen) { + memcpy(outBuf, eventData, strLen); + outBuf += strLen; + outBufLen -= strLen; + } else if (outBufLen > 0) { + /* copy what we can */ + memcpy(outBuf, eventData, outBufLen); + outBuf += outBufLen; + outBufLen -= outBufLen; + goto no_room; + } + eventData += strLen; + eventDataLen -= strLen; + break; + } + case EVENT_TYPE_LIST: + /* N items, all different types */ + { + unsigned char count; + int i; + + if (eventDataLen < 1) + return -1; + + count = *eventData++; + eventDataLen--; + + if (outBufLen > 0) { + *outBuf++ = '['; + outBufLen--; + } else { + goto no_room; + } + + for (i = 0; i < count; i++) { + result = android_log_printBinaryEvent(&eventData, &eventDataLen, + &outBuf, &outBufLen); + if (result != 0) + goto bail; + + if (i < count-1) { + if (outBufLen > 0) { + *outBuf++ = ','; + outBufLen--; + } else { + goto no_room; + } + } + } + + if (outBufLen > 0) { + *outBuf++ = ']'; + outBufLen--; + } else { + goto no_room; + } + } + break; + default: + fprintf(stderr, "Unknown binary event type %d\n", type); + return -1; + } + +bail: + *pEventData = eventData; + *pEventDataLen = eventDataLen; + *pOutBuf = outBuf; + *pOutBufLen = outBufLen; + return result; + +no_room: + result = 1; + goto bail; +} + +/** + * Convert a binary log entry to ASCII form. + * + * For convenience we mimic the processLogBuffer API. There is no + * pre-defined output length for the binary data, since we're free to format + * 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) +{ + size_t inCount; + unsigned int tagIndex; + const unsigned char* eventData; + + entry->tv_sec = buf->sec; + entry->tv_nsec = buf->nsec; + entry->priority = ANDROID_LOG_INFO; + entry->pid = buf->pid; + entry->tid = buf->tid; + + /* + * Pull the tag out. + */ + eventData = (const unsigned char*) buf->msg; + inCount = buf->len; + if (inCount < 4) + return -1; + tagIndex = get4LE(eventData); + eventData += 4; + inCount -= 4; + + if (map != NULL) { + entry->tag = android_lookupEventTag(map, tagIndex); + } else { + entry->tag = NULL; + } + + /* + * If we don't have a map, or didn't find the tag number in the map, + * stuff a generated tag value into the start of the output buffer and + * shift the buffer pointers down. + */ + if (entry->tag == NULL) { + int tagLen; + + tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex); + entry->tag = messageBuf; + messageBuf += tagLen+1; + messageBufLen -= tagLen+1; + } + + /* + * Format the event log data into the buffer. + */ + char* outBuf = messageBuf; + size_t outRemaining = messageBufLen-1; /* leave one for nul byte */ + int result; + result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, + &outRemaining); + 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--; + } + /* pretend we ate all the data */ + inCount = 0; + } + + /* eat the silly terminating '\n' */ + if (inCount == 1 && *eventData == '\n') { + eventData++; + inCount--; + } + + if (inCount != 0) { + fprintf(stderr, + "Warning: leftover binary log data (%zu bytes)\n", inCount); + } + + /* + * Terminate the buffer. The NUL byte does not count as part of + * entry->messageLen. + */ + *outBuf = '\0'; + entry->messageLen = outBuf - messageBuf; + assert(entry->messageLen == (messageBufLen-1) - outRemaining); + + entry->message = messageBuf; + + return 0; +} + +/** + * 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 *entry, + size_t *p_outLength) +{ +#if defined(HAVE_LOCALTIME_R) + struct tm tmBuf; +#endif + struct tm* ptm; + char timeBuf[32]; + char headerBuf[128]; + char prefixBuf[128], suffixBuf[128]; + char priChar; + int prefixSuffixIsHeaderFooter = 0; + char * ret = NULL; + + priChar = filterPriToChar(entry->priority); + + /* + * Get the current date/time in pretty form + * + * It's often useful when examining a log with "less" to jump to + * a specific point in the file by searching for the date/time stamp. + * 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. + */ +#if defined(HAVE_LOCALTIME_R) + 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); + + /* + * Construct a buffer containing the log header and log message. + */ + size_t prefixLen, suffixLen; + + switch (p_format->format) { + case FORMAT_TAG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s: ", priChar, entry->tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_PROCESS: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d) ", priChar, entry->pid); + suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), + " (%s)\n", entry->tag); + break; + case FORMAT_THREAD: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d:%5d) ", priChar, entry->pid, entry->tid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_RAW: + prefixBuf[0] = 0; + prefixLen = 0; + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_TIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000, + priChar, entry->tag, entry->pid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_THREADTIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, + entry->pid, entry->tid, priChar, entry->tag); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_LONG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "[ %s.%03ld %5d:%5d %c/%-8s ]\n", + timeBuf, entry->tv_nsec / 1000000, entry->pid, + entry->tid, priChar, entry->tag); + strcpy(suffixBuf, "\n\n"); + suffixLen = 2; + prefixSuffixIsHeaderFooter = 1; + break; + case FORMAT_BRIEF: + default: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s(%5d): ", priChar, entry->tag, entry->pid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + } + /* snprintf has a weird return value. It returns what would have been + * written given a large enough buffer. In the case that the prefix is + * longer then our buffer(128), it messes up the calculations below + * possibly causing heap corruption. To avoid this we double check and + * set the length at the maximum (size minus null byte) + */ + if(prefixLen >= sizeof(prefixBuf)) + prefixLen = sizeof(prefixBuf) - 1; + if(suffixLen >= sizeof(suffixBuf)) + suffixLen = sizeof(suffixBuf) - 1; + + /* the following code is tragically unreadable */ + + size_t numLines; + size_t i; + char *p; + size_t bufferSize; + const char *pm; + + if (prefixSuffixIsHeaderFooter) { + // we're just wrapping message with a header/footer + numLines = 1; + } else { + pm = entry->message; + numLines = 0; + + // The line-end finding here must match the line-end finding + // in for ( ... numLines...) loop below + while (pm < (entry->message + entry->messageLen)) { + if (*pm++ == '\n') numLines++; + } + // plus one line for anything not newline-terminated at the end + if (pm > entry->message && *(pm-1) != '\n') numLines++; + } + + // this is an upper bound--newlines in message may be counted + // extraneously + bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1; + + if (defaultBufferSize >= bufferSize) { + ret = defaultBuffer; + } else { + ret = (char *)malloc(bufferSize); + + if (ret == NULL) { + return ret; + } + } + + ret[0] = '\0'; /* to start strcat off */ + + p = ret; + pm = entry->message; + + if (prefixSuffixIsHeaderFooter) { + strcat(p, prefixBuf); + p += prefixLen; + strncat(p, entry->message, entry->messageLen); + p += entry->messageLen; + strcat(p, suffixBuf); + p += suffixLen; + } else { + while(pm < (entry->message + entry->messageLen)) { + const char *lineStart; + size_t lineLen; + lineStart = pm; + + // Find the next end-of-line in message + while (pm < (entry->message + entry->messageLen) + && *pm != '\n') pm++; + lineLen = pm - lineStart; + + strcat(p, prefixBuf); + p += prefixLen; + strncat(p, lineStart, lineLen); + p += lineLen; + strcat(p, suffixBuf); + p += suffixLen; + + if (*pm == '\n') pm++; + } + } + + if (p_outLength != NULL) { + *p_outLength = p - ret; + } + + return ret; +} + +/** + * Either print or do not print log line, based on filter + * + * Returns count bytes written + */ + +int android_log_printLogLine( + AndroidLogFormat *p_format, + int fd, + const AndroidLogEntry *entry) +{ + int ret; + char defaultBuffer[512]; + char *outBuffer = NULL; + size_t totalLen; + + outBuffer = android_log_formatLogLine(p_format, defaultBuffer, + sizeof(defaultBuffer), entry, &totalLen); + + if (!outBuffer) + return -1; + + do { + ret = write(fd, outBuffer, totalLen); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno); + ret = 0; + goto done; + } + + if (((size_t)ret) < totalLen) { + fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, + (int)totalLen); + goto done; + } + +done: + if (outBuffer != defaultBuffer) { + free(outBuffer); + } + + return ret; +} + + + +void logprint_run_tests() +{ +#if 0 + + fprintf(stderr, "tests disabled\n"); + +#else + + int err; + const char *tag; + AndroidLogFormat *p_format; + + p_format = android_log_format_new(); + + fprintf(stderr, "running tests\n"); + + tag = "random"; + + android_log_addFilterRule(p_format,"*:i"); + + assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + android_log_addFilterRule(p_format, "*"); + assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:v"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "*:i"); + assert (ANDROID_LOG_INFO == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, "random"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:v"); + assert (ANDROID_LOG_VERBOSE == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:d"); + assert (ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) > 0); + android_log_addFilterRule(p_format, "random:w"); + assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + android_log_addFilterRule(p_format, "crap:*"); + assert (ANDROID_LOG_VERBOSE== filterPriForTag(p_format, "crap")); + assert(android_log_shouldPrintLine(p_format, "crap", ANDROID_LOG_VERBOSE) > 0); + + // invalid expression + err = android_log_addFilterRule(p_format, "random:z"); + assert (err < 0); + assert (ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + assert(android_log_shouldPrintLine(p_format, tag, ANDROID_LOG_DEBUG) == 0); + + // Issue #550946 + err = android_log_addFilterString(p_format, " "); + assert(err == 0); + assert(ANDROID_LOG_WARN == filterPriForTag(p_format, "random")); + + // note trailing space + err = android_log_addFilterString(p_format, "*:s random:d "); + assert(err == 0); + assert(ANDROID_LOG_DEBUG == filterPriForTag(p_format, "random")); + + err = android_log_addFilterString(p_format, "*:s random:z"); + assert(err < 0); + + +#if 0 + char *ret; + char defaultBuffer[512]; + + ret = android_log_formatLogLine(p_format, + defaultBuffer, sizeof(defaultBuffer), 0, ANDROID_LOG_ERROR, 123, + 123, 123, "random", "nofile", strlen("Hello"), "Hello", NULL); +#endif + + + fprintf(stderr, "tests complete\n"); +#endif +} diff --git a/liblog/uio.c b/liblog/uio.c new file mode 100644 index 0000000..cfa4cb1 --- /dev/null +++ b/liblog/uio.c @@ -0,0 +1,76 @@ +/* + * 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 HAVE_SYS_UIO_H + +#include +#include + +int readv( int fd, 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 = read( fd, buf, len ); + if (ret < 0) { + if (total == 0) + total = -1; + goto Exit; + } + if (ret == 0) + goto Exit; + + total += ret; + buf += ret; + len -= ret; + } + } +Exit: + return total; +} + +int writev( int fd, const struct iovec* vecs, int count ) +{ + int total = 0; + + for ( ; count > 0; count--, vecs++ ) { + const char* buf = (const char*)vecs->iov_base; + int len = (int)vecs->iov_len; + + while (len > 0) { + int ret = write( fd, buf, len ); + if (ret < 0) { + if (total == 0) + total = -1; + goto Exit; + } + if (ret == 0) + goto Exit; + + total += ret; + buf += ret; + len -= ret; + } + } +Exit: + return total; +} + +#endif /* !HAVE_SYS_UIO_H */ diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk new file mode 100644 index 0000000..090d0e5 --- /dev/null +++ b/libmincrypt/Android.mk @@ -0,0 +1,18 @@ +# Copyright 2008 The Android Open Source Project +# +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := libmincrypt +LOCAL_SRC_FILES := rsa.c sha.c sha256.c +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_MODULE := libmincrypt +LOCAL_SRC_FILES := rsa.c sha.c sha256.c +include $(BUILD_HOST_STATIC_LIBRARY) + + +include $(LOCAL_PATH)/tools/Android.mk \ + $(LOCAL_PATH)/test/Android.mk diff --git a/libmincrypt/Makefile b/libmincrypt/Makefile new file mode 100644 index 0000000..0857333 --- /dev/null +++ b/libmincrypt/Makefile @@ -0,0 +1,28 @@ +TARGET = libmincrypt.a +INSTALL_DIR = ../lib + +SRCS += \ + rsa.c \ + sha.c \ + sha256.c + +CC = $(CROSS_COMPILE)gcc +AR = $(CROSS_COMPILE)ar + +CFLAGS = -I../include + +OBJS = $(SRCS:.c=.o) + +all: $(TARGET) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(TARGET): $(OBJS) + $(AR) r $@ $(OBJS) + [ -f $(INSTALL_DIR) ] || mkdir -p $(INSTALL_DIR) + cp $(TARGET) $(INSTALL_DIR) + +.PHONY: clean +clean: + rm -rf $(OBJS) $(TARGET) diff --git a/libmincrypt/NOTICE b/libmincrypt/NOTICE new file mode 100644 index 0000000..430d3d6 --- /dev/null +++ b/libmincrypt/NOTICE @@ -0,0 +1,23 @@ + 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/rsa.c b/libmincrypt/rsa.c new file mode 100644 index 0000000..9061b3a --- /dev/null +++ b/libmincrypt/rsa.c @@ -0,0 +1,308 @@ +/* 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 new file mode 100644 index 0000000..5bef32e --- /dev/null +++ b/libmincrypt/sha.c @@ -0,0 +1,155 @@ +/* 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 +#include +#include + +#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 new file mode 100644 index 0000000..eb6e308 --- /dev/null +++ b/libmincrypt/sha256.c @@ -0,0 +1,184 @@ +/* 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 +#include +#include + +#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 new file mode 100644 index 0000000..a28ccd8 --- /dev/null +++ b/libmincrypt/test/Android.mk @@ -0,0 +1,10 @@ +# 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_EXECUTABLE) + diff --git a/libmincrypt/test/rsa_test.c b/libmincrypt/test/rsa_test.c new file mode 100644 index 0000000..17862dc --- /dev/null +++ b/libmincrypt/test/rsa_test.c @@ -0,0 +1,838 @@ +/* rsa_test.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. +*/ + +#include +#include +#include +#include + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +// 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, char** argv) { + + 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 new file mode 100644 index 0000000..b61234a --- /dev/null +++ b/libmincrypt/tools/Android.mk @@ -0,0 +1,21 @@ +# 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 +include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java new file mode 100644 index 0000000..7189116 --- /dev/null +++ b/libmincrypt/tools/DumpPublicKey.java @@ -0,0 +1,169 @@ +/* + * 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 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.interfaces.RSAPublicKey; + +/** + * 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 check(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 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 print(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(); + } + + public static void main(String[] args) { + if (args.length < 1) { + System.err.println("Usage: DumpPublicKey certfile ... > source.c"); + System.exit(1); + } + 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)) { + useSHA256 = true; + } else { + System.err.println(args[i] + ": unsupported signature algorithm \"" + + sigAlg + "\""); + System.exit(1); + } + + RSAPublicKey key = (RSAPublicKey) (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 new file mode 100644 index 0000000..7bb3bc8 --- /dev/null +++ b/libmincrypt/tools/DumpPublicKey.mf @@ -0,0 +1 @@ +Main-Class: com.android.dumpkey.DumpPublicKey