OSDN Git Service

logd: optimize code hotspots
authorMark Salyzyn <salyzyn@google.com>
Tue, 17 Nov 2015 02:55:11 +0000 (02:55 +0000)
committerandroid-build-merger <android-build-merger@google.com>
Tue, 17 Nov 2015 02:55:11 +0000 (02:55 +0000)
am: f9162cf711  -s ours

* commit 'f9162cf711abd55ccb1c6f9d5f4805b96471c88f':
  logd: optimize code hotspots

532 files changed:
.gitignore
adb/.clang-format
adb/Android.mk
adb/CPPLINT.cfg
adb/SERVICES.TXT
adb/SYNC.TXT
adb/adb.cpp
adb/adb.h
adb/adb_auth.cpp
adb/adb_auth_client.cpp
adb/adb_auth_host.cpp
adb/adb_client.cpp
adb/adb_client.h
adb/adb_io.cpp
adb/adb_io.h
adb/adb_io_test.cpp
adb/adb_listeners.cpp
adb/adb_listeners.h
adb/adb_main.cpp [deleted file]
adb/adb_trace.cpp [new file with mode: 0644]
adb/adb_trace.h
adb/adb_utils.cpp
adb/adb_utils.h
adb/adb_utils_test.cpp
adb/client/main.cpp [new file with mode: 0644]
adb/commandline.cpp
adb/console.cpp
adb/daemon/main.cpp [new file with mode: 0644]
adb/fdevent.cpp
adb/fdevent.h
adb/fdevent_test.cpp [new file with mode: 0644]
adb/file_sync_client.cpp
adb/file_sync_service.cpp
adb/file_sync_service.h
adb/jdwp_service.cpp
adb/line_printer.cpp [new file with mode: 0644]
adb/line_printer.h [new file with mode: 0644]
adb/mutex_list.h
adb/protocol.txt
adb/qemu_tracing.cpp [deleted file]
adb/remount_service.cpp
adb/services.cpp
adb/services.h [new file with mode: 0644]
adb/set_verity_enable_state_service.cpp
adb/shell_service.cpp [new file with mode: 0644]
adb/shell_service.h [new file with mode: 0644]
adb/shell_service_protocol.cpp [new file with mode: 0644]
adb/shell_service_protocol_test.cpp [new file with mode: 0644]
adb/shell_service_test.cpp [new file with mode: 0644]
adb/socket.h [new file with mode: 0644]
adb/socket_test.cpp [new file with mode: 0644]
adb/sockets.cpp
adb/sysdeps.h
adb/sysdeps_win32.cpp
adb/sysdeps_win32_test.cpp [new file with mode: 0755]
adb/test_adb.py [new file with mode: 0644]
adb/test_track_devices.cpp
adb/tests/test_adb.py [deleted file]
adb/transport.cpp
adb/transport.h
adb/transport_local.cpp
adb/transport_test.cpp
adb/transport_usb.cpp
adb/usb_linux.cpp
adb/usb_linux_client.cpp
adb/usb_osx.cpp
adb/usb_windows.cpp
adf/libadf/adf.c
base/Android.mk
base/file.cpp
base/file_test.cpp
base/include/base/logging.h
base/include/base/parseint.h [new file with mode: 0644]
base/include/base/stringprintf.h
base/include/base/strings.h
base/include/base/test_utils.h [new file with mode: 0644]
base/include/base/utf8.h [new file with mode: 0755]
base/logging.cpp
base/logging_test.cpp
base/parseint_test.cpp [new file with mode: 0644]
base/strings.cpp
base/strings_test.cpp
base/test_utils.cpp
base/utf8.cpp [new file with mode: 0755]
base/utf8_test.cpp [new file with mode: 0755]
crash_reporter/.project_alias [new file with mode: 0644]
crash_reporter/99-crash-reporter.rules [new file with mode: 0644]
crash_reporter/Android.mk [new file with mode: 0644]
crash_reporter/OWNERS [new file with mode: 0644]
crash_reporter/TEST_WARNING [new file with mode: 0644]
crash_reporter/crash_collector.cc [new file with mode: 0644]
crash_reporter/crash_collector.h [new file with mode: 0644]
crash_reporter/crash_collector_test.cc [new file with mode: 0644]
crash_reporter/crash_collector_test.h [new file with mode: 0644]
crash_reporter/crash_reporter.cc [new file with mode: 0644]
crash_reporter/crash_reporter.rc [new file with mode: 0644]
crash_reporter/crash_reporter_logs.conf [new file with mode: 0644]
crash_reporter/crash_reporter_logs_test.cc [new file with mode: 0644]
crash_reporter/crash_sender [new file with mode: 0755]
crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml [new file with mode: 0644]
crash_reporter/init/crash-reporter.conf [new file with mode: 0644]
crash_reporter/init/crash-sender.conf [new file with mode: 0644]
crash_reporter/init/warn-collector.conf [new file with mode: 0644]
crash_reporter/kernel_collector.cc [new file with mode: 0644]
crash_reporter/kernel_collector.h [new file with mode: 0644]
crash_reporter/kernel_collector_test.cc [new file with mode: 0644]
crash_reporter/kernel_collector_test.h [new file with mode: 0644]
crash_reporter/kernel_log_collector.sh [new file with mode: 0644]
crash_reporter/kernel_warning_collector.cc [new file with mode: 0644]
crash_reporter/kernel_warning_collector.h [new file with mode: 0644]
crash_reporter/list_proxies.cc [new file with mode: 0644]
crash_reporter/periodic_scheduler [new file with mode: 0755]
crash_reporter/testrunner.cc [new file with mode: 0644]
crash_reporter/udev_collector.cc [new file with mode: 0644]
crash_reporter/udev_collector.h [new file with mode: 0644]
crash_reporter/udev_collector_test.cc [new file with mode: 0644]
crash_reporter/unclean_shutdown_collector.cc [new file with mode: 0644]
crash_reporter/unclean_shutdown_collector.h [new file with mode: 0644]
crash_reporter/unclean_shutdown_collector_test.cc [new file with mode: 0644]
crash_reporter/user_collector.cc [new file with mode: 0644]
crash_reporter/user_collector.h [new file with mode: 0644]
crash_reporter/user_collector_test.cc [new file with mode: 0644]
crash_reporter/warn_collector.l [new file with mode: 0644]
crash_reporter/warn_collector_test.c [new file with mode: 0644]
crash_reporter/warn_collector_test.sh [new file with mode: 0755]
crash_reporter/warn_collector_test_reporter.sh [new file with mode: 0755]
debuggerd/Android.mk
debuggerd/arm/machine.cpp
debuggerd/arm64/machine.cpp
debuggerd/backtrace.cpp
debuggerd/crasher.c
debuggerd/debuggerd.cpp
debuggerd/debuggerd.rc [new file with mode: 0644]
debuggerd/debuggerd64.rc [new file with mode: 0644]
debuggerd/mips/machine.cpp
debuggerd/mips64/machine.cpp
debuggerd/test/BacktraceMock.h
debuggerd/test/dump_memory_test.cpp
debuggerd/test/log_fake.cpp
debuggerd/test/tombstone_test.cpp [moved from debuggerd/test/dump_maps_test.cpp with 90% similarity]
debuggerd/tombstone.cpp
debuggerd/utility.cpp
debuggerd/utility.h
debuggerd/x86/machine.cpp
debuggerd/x86_64/machine.cpp
fastboot/Android.mk
fastboot/bootimg_utils.cpp
fastboot/bootimg_utils.h
fastboot/engine.cpp [moved from fastboot/engine.c with 78% similarity]
fastboot/engineering_key.p12 [deleted file]
fastboot/fastboot.cpp
fastboot/fastboot.h
fastboot/fastboot_protocol.txt
fastboot/fs.cpp [moved from fastboot/fs.c with 69% similarity]
fastboot/fs.h
fastboot/p12topem.sh [deleted file]
fastboot/protocol.cpp [moved from fastboot/protocol.c with 68% similarity]
fastboot/signfile.sh [deleted file]
fastboot/usb.h
fastboot/usb_linux.cpp [moved from fastboot/usb_linux.c with 93% similarity]
fastboot/usb_osx.cpp [moved from fastboot/usb_osx.c with 96% similarity]
fastboot/usb_windows.cpp [moved from fastboot/usb_windows.c with 100% similarity]
fastboot/usbtest.cpp [moved from fastboot/usbtest.c with 100% similarity]
fastboot/util.cpp [moved from fastboot/util.c with 100% similarity]
fastboot/util_linux.cpp [moved from fastboot/util_linux.c with 98% similarity]
fastboot/util_osx.cpp [moved from fastboot/util_osx.c with 98% similarity]
fastboot/util_windows.cpp [moved from fastboot/util_windows.c with 98% similarity]
fs_mgr/Android.mk
fs_mgr/fs_mgr.c
fs_mgr/fs_mgr_fstab.c
fs_mgr/fs_mgr_priv.h
fs_mgr/fs_mgr_priv_verity.h
fs_mgr/fs_mgr_slotselect.c [new file with mode: 0644]
fs_mgr/fs_mgr_verity.cpp [moved from fs_mgr/fs_mgr_verity.c with 69% similarity]
fs_mgr/include/fs_mgr.h
gatekeeperd/Android.mk
gatekeeperd/SoftGateKeeper.h
gatekeeperd/gatekeeperd.rc [new file with mode: 0644]
gatekeeperd/tests/Android.mk
gatekeeperd/tests/gatekeeper_test.cpp
gpttool/Android.mk [deleted file]
gpttool/gpttool.c [deleted file]
healthd/Android.mk
healthd/BatteryMonitor.cpp
healthd/healthd.cpp
healthd/healthd.h
include/backtrace/Backtrace.h
include/backtrace/BacktraceMap.h
include/binderwrapper/binder_test_base.h [new file with mode: 0644]
include/binderwrapper/binder_wrapper.h [new file with mode: 0644]
include/binderwrapper/stub_binder_wrapper.h [new file with mode: 0644]
include/cutils/android_reboot.h
include/cutils/sockets.h
include/cutils/trace.h
include/log/logger.h
include/log/logprint.h
include/private/android_filesystem_config.h
include/system/radio.h
include/system/window.h
include/utils/AndroidThreads.h
include/utils/BasicHashtable.h [deleted file]
include/utils/ByteOrder.h
include/utils/Compat.h
include/utils/Errors.h
include/utils/FileMap.h
include/utils/JenkinsHash.h
include/utils/LruCache.h
include/utils/Mutex.h
include/utils/String16.h
include/utils/String8.h
include/utils/StrongPointer.h
include/utils/Thread.h
include/utils/Trace.h
include/utils/TypeHelpers.h
include/ziparchive/zip_archive.h
include/ziparchive/zip_writer.h [new file with mode: 0644]
init/Android.mk
init/action.cpp [new file with mode: 0644]
init/action.h [new file with mode: 0644]
init/bootchart.cpp
init/bootchart.h
init/builtins.cpp
init/builtins.h [moved from adb/get_my_path_windows.cpp with 58% similarity]
init/compare-bootcharts.py [new file with mode: 0755]
init/devices.cpp
init/import_parser.cpp [new file with mode: 0644]
init/import_parser.h [new file with mode: 0644]
init/init.cpp
init/init.h
init/init_parser.cpp
init/init_parser.h
init/init_parser_test.cpp
init/keychords.cpp
init/keyword_map.h [new file with mode: 0644]
init/keywords.h [deleted file]
init/log.cpp
init/log.h
init/parser/tokenizer.cpp [new file with mode: 0644]
init/parser/tokenizer.h [new file with mode: 0644]
init/parser/tokenizer_test.cpp [new file with mode: 0644]
init/perfboot.py [new file with mode: 0755]
init/property_service.cpp
init/property_service.h
init/readme.txt
init/service.cpp [new file with mode: 0644]
init/service.h [new file with mode: 0644]
init/signal_handler.cpp
init/ueventd.cpp
init/ueventd_parser.cpp
init/util.cpp
init/util.h
init/util_test.cpp
init/watchdogd.cpp
libbacktrace/Android.build.mk
libbacktrace/Android.mk
libbacktrace/Backtrace.cpp
libbacktrace/BacktraceMap.cpp
libbacktrace/BacktraceOffline.cpp [new file with mode: 0644]
libbacktrace/BacktraceOffline.h [new file with mode: 0644]
libbacktrace/backtrace_offline_test.cpp [new file with mode: 0644]
libbacktrace/backtrace_test.cpp
libbacktrace/map_info.c [deleted file]
libbinderwrapper/Android.mk [new file with mode: 0644]
libbinderwrapper/binder_test_base.cc [new file with mode: 0644]
libbinderwrapper/binder_wrapper.cc [new file with mode: 0644]
libbinderwrapper/real_binder_wrapper.cc [new file with mode: 0644]
libbinderwrapper/real_binder_wrapper.h [new file with mode: 0644]
libbinderwrapper/stub_binder_wrapper.cc [new file with mode: 0644]
libcutils/Android.mk
libcutils/android_reboot.c
libcutils/arch-arm/memset32.S
libcutils/arch-mips/android_memset.c
libcutils/fs_config.c
libcutils/hashmap.c
libcutils/iosched_policy.c
libcutils/process_name.c
libcutils/record_stream.c
libcutils/sched_policy.c
libcutils/socket_inaddr_any_server.c
libcutils/socket_local_client.c
libcutils/socket_local_server.c
libcutils/socket_loopback_client.c
libcutils/socket_loopback_server.c
libcutils/socket_network_client.c
libcutils/sockets.c
libcutils/str_parms.c
libcutils/strdup16to8.c
libcutils/tests/MemsetTest.cpp
liblog/Android.bp [new file with mode: 0644]
liblog/Android.mk
liblog/log_is_loggable.c
liblog/log_read.c
liblog/log_read_kern.c [deleted file]
liblog/logd_write.c
liblog/logd_write_kern.c [deleted file]
liblog/logprint.c
liblog/tests/Android.mk
liblog/tests/libc_test.cpp
libmemtrack/Android.mk
libnativebridge/Android.mk
libnativebridge/native_bridge.cc
libnativebridge/tests/Android.mk
libnativebridge/tests/CodeCacheStatFail_test.cpp [new file with mode: 0644]
libnativebridge/tests/NativeBridgeTest.h
libnativebridge/tests/PreInitializeNativeBridge_test.cpp
libnetutils/dhcptool.c
libnetutils/ifc_utils.c
libpackagelistparser/Android.mk [new file with mode: 0644]
libpackagelistparser/include/packagelistparser/packagelistparser.h [new file with mode: 0644]
libpackagelistparser/packagelistparser.c [new file with mode: 0644]
libpixelflinger/Android.mk
libpixelflinger/arch-arm64/col32cb16blend.S
libpixelflinger/arch-arm64/t32cb16blend.S
libpixelflinger/arch-mips/col32cb16blend.S [new file with mode: 0644]
libpixelflinger/arch-mips/t32cb16blend.S
libpixelflinger/arch-mips64/col32cb16blend.S [new file with mode: 0644]
libpixelflinger/arch-mips64/t32cb16blend.S [new file with mode: 0644]
libpixelflinger/codeflinger/ARMAssemblerInterface.h
libpixelflinger/codeflinger/GGLAssembler.cpp
libpixelflinger/codeflinger/MIPS64Assembler.cpp [new file with mode: 0644]
libpixelflinger/codeflinger/MIPS64Assembler.h [new file with mode: 0644]
libpixelflinger/codeflinger/MIPSAssembler.cpp
libpixelflinger/codeflinger/MIPSAssembler.h
libpixelflinger/codeflinger/mips64_disassem.c [new file with mode: 0644]
libpixelflinger/codeflinger/mips64_disassem.h [new file with mode: 0644]
libpixelflinger/codeflinger/mips_disassem.c
libpixelflinger/codeflinger/mips_opcode.h
libpixelflinger/include/private/pixelflinger/ggl_context.h
libpixelflinger/include/private/pixelflinger/ggl_fixed.h
libpixelflinger/scanline.cpp
libpixelflinger/t32cb16blend.S
libpixelflinger/tests/arch-arm64/assembler/Android.mk
libpixelflinger/tests/arch-arm64/assembler/asm_test_jacket.S
libpixelflinger/tests/arch-arm64/col32cb16blend/Android.mk
libpixelflinger/tests/arch-arm64/t32cb16blend/Android.mk
libpixelflinger/tests/arch-mips/Android.mk [new file with mode: 0644]
libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk [new file with mode: 0644]
libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c [new file with mode: 0644]
libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk [new file with mode: 0644]
libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c [new file with mode: 0644]
libpixelflinger/tests/arch-mips64/Android.mk [new file with mode: 0644]
libpixelflinger/tests/arch-mips64/assembler/Android.mk [new file with mode: 0644]
libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S [new file with mode: 0644]
libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp [new file with mode: 0644]
libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk [new file with mode: 0644]
libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c [new file with mode: 0644]
libpixelflinger/tests/arch-mips64/disassembler/Android.mk [new file with mode: 0644]
libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp [new file with mode: 0644]
libpixelflinger/tests/codegen/codegen.cpp
libpixelflinger/trap.cpp
libsparse/Android.mk
libsparse/backed_block.c
libsparse/simg2img.c
libsparse/simg_dump.py
libsparse/sparse_crc32.h
libsparse/sparse_read.c
libsync/tests/sync_test.cpp
libutils/Android.mk
libutils/BasicHashtable.cpp [deleted file]
libutils/CallStack.cpp
libutils/FileMap.cpp
libutils/JenkinsHash.cpp
libutils/LinearTransform.cpp
libutils/Looper.cpp
libutils/ProcessCallStack.cpp
libutils/SharedBuffer.cpp
libutils/SharedBuffer.h [moved from include/utils/SharedBuffer.h with 100% similarity]
libutils/SharedBufferTest.cpp
libutils/String16.cpp
libutils/String8.cpp
libutils/SystemClock.cpp
libutils/Threads.cpp
libutils/Timers.cpp
libutils/Tokenizer.cpp
libutils/Unicode.cpp
libutils/VectorImpl.cpp
libutils/misc.cpp
libutils/tests/Android.mk
libutils/tests/BasicHashtable_test.cpp [deleted file]
libutils/tests/BitSet_test.cpp
libutils/tests/LruCache_test.cpp
libutils/tests/String8_test.cpp
libutils/tests/StrongPointer_test.cpp [new file with mode: 0644]
libutils/tests/Vector_test.cpp
libziparchive/Android.mk
libziparchive/zip_archive.cc
libziparchive/zip_archive_common.h [new file with mode: 0644]
libziparchive/zip_archive_test.cc
libziparchive/zip_writer.cc [new file with mode: 0644]
libziparchive/zip_writer_test.cc [new file with mode: 0644]
lmkd/Android.mk
lmkd/lmkd.c
lmkd/lmkd.rc [new file with mode: 0644]
logcat/Android.mk
logcat/logcat.cpp
logcat/logcatd.rc [new file with mode: 0644]
logcat/logpersist [moved from logd/logpersist with 88% similarity]
logcat/tests/logcat_test.cpp
logd/Android.mk
logd/CommandListener.cpp
logd/LogAudit.cpp
logd/LogAudit.h
logd/LogBuffer.cpp
logd/LogBuffer.h
logd/LogBufferElement.cpp
logd/LogBufferElement.h
logd/LogCommand.cpp
logd/LogKlog.cpp
logd/LogKlog.h
logd/LogReader.cpp
logd/LogStatistics.cpp
logd/LogStatistics.h
logd/LogTimes.cpp
logd/LogWhiteBlackList.cpp
logd/README.property
logd/logd.rc [new file with mode: 0644]
logd/main.cpp
logd/tests/Android.mk
logd/tests/logd_test.cpp
logwrapper/Android.mk
logwrapper/include/logwrap/logwrap.h
logwrapper/logwrap.c
logwrapper/logwrapper.c
metricsd/Android.mk [new file with mode: 0644]
metricsd/OWNERS [new file with mode: 0644]
metricsd/README [new file with mode: 0644]
metricsd/WATCHLISTS [new file with mode: 0644]
metricsd/c_metrics_library.cc [new file with mode: 0644]
metricsd/collectors/averaged_statistics_collector.cc [new file with mode: 0644]
metricsd/collectors/averaged_statistics_collector.h [new file with mode: 0644]
metricsd/collectors/averaged_statistics_collector_test.cc [new file with mode: 0644]
metricsd/collectors/cpu_usage_collector.cc [new file with mode: 0644]
metricsd/collectors/cpu_usage_collector.h [new file with mode: 0644]
metricsd/collectors/cpu_usage_collector_test.cc [new file with mode: 0644]
metricsd/collectors/disk_usage_collector.cc [new file with mode: 0644]
metricsd/collectors/disk_usage_collector.h [new file with mode: 0644]
metricsd/constants.h [new file with mode: 0644]
metricsd/etc/weaved/commands/metrics.json [new file with mode: 0644]
metricsd/etc/weaved/states/metrics.schema.json [new file with mode: 0644]
metricsd/include/metrics/c_metrics_library.h [new file with mode: 0644]
metricsd/include/metrics/metrics_library.h [new file with mode: 0644]
metricsd/include/metrics/metrics_library_mock.h [new file with mode: 0644]
metricsd/include/metrics/timer.h [new file with mode: 0644]
metricsd/include/metrics/timer_mock.h [new file with mode: 0644]
metricsd/libmetrics-334380.gyp [new file with mode: 0644]
metricsd/libmetrics.gypi [new file with mode: 0644]
metricsd/libmetrics.pc.in [new file with mode: 0644]
metricsd/metrics.gyp [new file with mode: 0644]
metricsd/metrics_client.cc [new file with mode: 0644]
metricsd/metrics_daemon.cc [new file with mode: 0644]
metricsd/metrics_daemon.h [new file with mode: 0644]
metricsd/metrics_daemon.rc [new file with mode: 0644]
metricsd/metrics_daemon_main.cc [new file with mode: 0644]
metricsd/metrics_daemon_test.cc [new file with mode: 0644]
metricsd/metrics_library.cc [new file with mode: 0644]
metricsd/metrics_library_test.cc [new file with mode: 0644]
metricsd/persistent_integer.cc [new file with mode: 0644]
metricsd/persistent_integer.h [new file with mode: 0644]
metricsd/persistent_integer_mock.h [new file with mode: 0644]
metricsd/persistent_integer_test.cc [new file with mode: 0644]
metricsd/serialization/metric_sample.cc [new file with mode: 0644]
metricsd/serialization/metric_sample.h [new file with mode: 0644]
metricsd/serialization/serialization_utils.cc [new file with mode: 0644]
metricsd/serialization/serialization_utils.h [new file with mode: 0644]
metricsd/serialization/serialization_utils_unittest.cc [new file with mode: 0644]
metricsd/timer.cc [new file with mode: 0644]
metricsd/timer_test.cc [new file with mode: 0644]
metricsd/uploader/metrics_hashes.cc [new file with mode: 0644]
metricsd/uploader/metrics_hashes.h [new file with mode: 0644]
metricsd/uploader/metrics_hashes_unittest.cc [new file with mode: 0644]
metricsd/uploader/metrics_log.cc [new file with mode: 0644]
metricsd/uploader/metrics_log.h [new file with mode: 0644]
metricsd/uploader/metrics_log_base.cc [new file with mode: 0644]
metricsd/uploader/metrics_log_base.h [new file with mode: 0644]
metricsd/uploader/metrics_log_base_unittest.cc [new file with mode: 0644]
metricsd/uploader/mock/mock_system_profile_setter.h [new file with mode: 0644]
metricsd/uploader/mock/sender_mock.cc [new file with mode: 0644]
metricsd/uploader/mock/sender_mock.h [new file with mode: 0644]
metricsd/uploader/proto/README [new file with mode: 0644]
metricsd/uploader/proto/chrome_user_metrics_extension.proto [new file with mode: 0644]
metricsd/uploader/proto/histogram_event.proto [new file with mode: 0644]
metricsd/uploader/proto/system_profile.proto [new file with mode: 0644]
metricsd/uploader/proto/user_action_event.proto [new file with mode: 0644]
metricsd/uploader/sender.h [new file with mode: 0644]
metricsd/uploader/sender_http.cc [new file with mode: 0644]
metricsd/uploader/sender_http.h [new file with mode: 0644]
metricsd/uploader/system_profile_cache.cc [new file with mode: 0644]
metricsd/uploader/system_profile_cache.h [new file with mode: 0644]
metricsd/uploader/system_profile_setter.h [new file with mode: 0644]
metricsd/uploader/upload_service.cc [new file with mode: 0644]
metricsd/uploader/upload_service.h [new file with mode: 0644]
metricsd/uploader/upload_service_test.cc [new file with mode: 0644]
mkbootimg/Android.mk
mkbootimg/mkbootimg [new file with mode: 0755]
mkbootimg/mkbootimg.c [deleted file]
rootdir/Android.mk
rootdir/asan.options [new file with mode: 0644]
rootdir/init.environ.rc.in
rootdir/init.rc
rootdir/init.trace.rc [deleted file]
rootdir/init.usb.rc
run-as/run-as.c
sdcard/Android.mk
sdcard/sdcard.c
toolbox/Android.mk
toolbox/df.c [deleted file]
toolbox/ionice.c [deleted file]
toolbox/ls.c [deleted file]
toolbox/lsof.c [deleted file]
toolbox/mount.c [deleted file]
toolbox/ps.c
toolbox/renice.c [deleted file]
toolbox/start.c
toolbox/start_stop.cpp [new file with mode: 0644]
toolbox/stop.c
toolbox/top.c
toolbox/upstream-netbsd/usr.bin/du/du.c [deleted file]
toolbox/uptime.c [deleted file]
toolbox/watchprops.c [deleted file]
trusty/gatekeeper/Android.mk [new file with mode: 0644]
trusty/gatekeeper/gatekeeper_ipc.h [new file with mode: 0644]
trusty/gatekeeper/module.cpp [new file with mode: 0644]
trusty/gatekeeper/trusty_gatekeeper.cpp [new file with mode: 0644]
trusty/gatekeeper/trusty_gatekeeper.h [new file with mode: 0644]
trusty/gatekeeper/trusty_gatekeeper_ipc.c [new file with mode: 0644]
trusty/gatekeeper/trusty_gatekeeper_ipc.h [new file with mode: 0644]
trusty/libtrusty/Android.mk [new file with mode: 0644]
trusty/libtrusty/include/trusty/tipc.h [moved from base/test_utils.h with 74% similarity]
trusty/libtrusty/tipc-test/Android.mk [new file with mode: 0644]
trusty/libtrusty/tipc-test/tipc_test.c [new file with mode: 0644]
trusty/libtrusty/tipc_ioctl.h [moved from adb/qemu_tracing.h with 68% similarity]
trusty/libtrusty/trusty.c [new file with mode: 0644]

index b25c15b..2f836aa 100644 (file)
@@ -1 +1,2 @@
 *~
+*.pyc
index 0395c8e..6737535 100644 (file)
@@ -2,6 +2,7 @@ BasedOnStyle: Google
 AllowShortBlocksOnASingleLine: false
 AllowShortFunctionsOnASingleLine: false
 
+ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
 IndentWidth: 4
index 425bf9b..5ddc937 100644 (file)
@@ -5,19 +5,35 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-ifeq ($(HOST_OS),windows)
-  adb_host_clang := false  # libc++ for mingw not ready yet.
-else
-  adb_host_clang := true
-endif
+adb_host_sanitize :=
+adb_target_sanitize :=
 
 adb_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)-android
 
 ADB_COMMON_CFLAGS := \
-    -Wall -Werror \
+    -Wall -Wextra -Werror \
     -Wno-unused-parameter \
+    -Wno-missing-field-initializers \
+    -Wvla \
     -DADB_REVISION='"$(adb_version)"' \
 
+ADB_COMMON_linux_CFLAGS := \
+    -std=c++14 \
+    -Wexit-time-destructors \
+
+ADB_COMMON_darwin_CFLAGS := \
+    -std=c++14 \
+    -Wexit-time-destructors \
+
+# Define windows.h and tchar.h Unicode preprocessor symbols so that
+# CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
+# build if you accidentally pass char*. Fix by calling like:
+#   std::wstring path_wide;
+#   if (!android::base::UTF8ToWide(path_utf8, &path_wide)) { /* error handling */ }
+#   CreateFileW(path_wide.c_str());
+ADB_COMMON_windows_CFLAGS := \
+    -DUNICODE=1 -D_UNICODE=1 \
+
 # libadb
 # =========================================================
 
@@ -32,6 +48,7 @@ LIBADB_SRC_FILES := \
     adb_auth.cpp \
     adb_io.cpp \
     adb_listeners.cpp \
+    adb_trace.cpp \
     adb_utils.cpp \
     sockets.cpp \
     transport.cpp \
@@ -45,9 +62,17 @@ LIBADB_TEST_SRCS := \
 
 LIBADB_CFLAGS := \
     $(ADB_COMMON_CFLAGS) \
-    -Wno-missing-field-initializers \
     -fvisibility=hidden \
 
+LIBADB_linux_CFLAGS := \
+    $(ADB_COMMON_linux_CFLAGS) \
+
+LIBADB_darwin_CFLAGS := \
+    $(ADB_COMMON_darwin_CFLAGS) \
+
+LIBADB_windows_CFLAGS := \
+    $(ADB_COMMON_windows_CFLAGS) \
+
 LIBADB_darwin_SRC_FILES := \
     fdevent.cpp \
     get_my_path_darwin.cpp \
@@ -59,10 +84,20 @@ LIBADB_linux_SRC_FILES := \
     usb_linux.cpp \
 
 LIBADB_windows_SRC_FILES := \
-    get_my_path_windows.cpp \
     sysdeps_win32.cpp \
     usb_windows.cpp \
 
+LIBADB_TEST_linux_SRCS := \
+    fdevent_test.cpp \
+    socket_test.cpp \
+
+LIBADB_TEST_darwin_SRCS := \
+    fdevent_test.cpp \
+    socket_test.cpp \
+
+LIBADB_TEST_windows_SRCS := \
+    sysdeps_win32_test.cpp \
+
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_MODULE := libadbd
@@ -72,31 +107,39 @@ LOCAL_SRC_FILES := \
     adb_auth_client.cpp \
     fdevent.cpp \
     jdwp_service.cpp \
-    qemu_tracing.cpp \
     usb_linux_client.cpp \
 
-LOCAL_SHARED_LIBRARIES := libbase
+LOCAL_SANITIZE := $(adb_target_sanitize)
+
+# Even though we're building a static library (and thus there's no link step for
+# this to take effect), this adds the includes to our path.
+LOCAL_STATIC_LIBRARIES := libbase
 
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
 LOCAL_MODULE := libadb
+LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1
+LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
+LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
+LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
 LOCAL_SRC_FILES := \
     $(LIBADB_SRC_FILES) \
-    $(LIBADB_$(HOST_OS)_SRC_FILES) \
     adb_auth_host.cpp \
 
-LOCAL_SHARED_LIBRARIES := libbase
+LOCAL_SRC_FILES_darwin := $(LIBADB_darwin_SRC_FILES)
+LOCAL_SRC_FILES_linux := $(LIBADB_linux_SRC_FILES)
+LOCAL_SRC_FILES_windows := $(LIBADB_windows_SRC_FILES)
+
+LOCAL_SANITIZE := $(adb_host_sanitize)
 
 # Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the SSL includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_static
+# this to take effect), this adds the includes to our path.
+LOCAL_STATIC_LIBRARIES := libcrypto_static libbase
 
-ifeq ($(HOST_OS),windows)
-    LOCAL_C_INCLUDES += development/host/windows/usb/api/
-endif
+LOCAL_C_INCLUDES_windows := development/host/windows/usb/api/
+LOCAL_MULTILIB := first
 
 include $(BUILD_HOST_STATIC_LIBRARY)
 
@@ -104,29 +147,51 @@ include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_MODULE := adbd_test
 LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS)
+LOCAL_SRC_FILES := \
+    $(LIBADB_TEST_SRCS) \
+    $(LIBADB_TEST_linux_SRCS) \
+    shell_service.cpp \
+    shell_service_protocol.cpp \
+    shell_service_protocol_test.cpp \
+    shell_service_test.cpp \
+
+LOCAL_SANITIZE := $(adb_target_sanitize)
 LOCAL_STATIC_LIBRARIES := libadbd
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
+LOCAL_SHARED_LIBRARIES := libbase libcutils
 include $(BUILD_NATIVE_TEST)
 
+# adb_test
+# =========================================================
+
 include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
 LOCAL_MODULE := adb_test
+LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp
-LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
+LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
+LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
+LOCAL_SRC_FILES := \
+    $(LIBADB_TEST_SRCS) \
+    services.cpp \
+    shell_service_protocol.cpp \
+    shell_service_protocol_test.cpp \
+
+LOCAL_SRC_FILES_linux := $(LIBADB_TEST_linux_SRCS)
+LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
+LOCAL_SRC_FILES_windows := $(LIBADB_TEST_windows_SRCS)
+LOCAL_SANITIZE := $(adb_host_sanitize)
+LOCAL_SHARED_LIBRARIES := libbase
 LOCAL_STATIC_LIBRARIES := \
     libadb \
     libcrypto_static \
     libcutils \
 
-ifeq ($(HOST_OS),linux)
-  LOCAL_LDLIBS += -lrt -ldl -lpthread
-endif
-
-ifeq ($(HOST_OS),darwin)
-  LOCAL_LDLIBS += -framework CoreFoundation -framework IOKit
-endif
+# Set entrypoint to wmain from sysdeps_win32.cpp instead of main
+LOCAL_LDFLAGS_windows := -municode
+LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
+LOCAL_LDLIBS_darwin := -framework CoreFoundation -framework IOKit
+LOCAL_LDLIBS_windows := -lws2_32 -luserenv
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
 
 include $(BUILD_HOST_NATIVE_TEST)
 
@@ -135,11 +200,14 @@ include $(BUILD_HOST_NATIVE_TEST)
 
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
 LOCAL_MODULE := adb_device_tracker_test
 LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
+LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
+LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
+LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
 LOCAL_SRC_FILES := test_track_devices.cpp
-LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_SANITIZE := $(adb_host_sanitize)
+LOCAL_SHARED_LIBRARIES := libbase
 LOCAL_STATIC_LIBRARIES := libadb libcrypto_static libcutils
 LOCAL_LDLIBS += -lrt -ldl -lpthread
 include $(BUILD_HOST_EXECUTABLE)
@@ -149,51 +217,54 @@ endif
 # =========================================================
 include $(CLEAR_VARS)
 
-ifeq ($(HOST_OS),linux)
-  LOCAL_LDLIBS += -lrt -ldl -lpthread
-  LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
-endif
+LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
 
-ifeq ($(HOST_OS),darwin)
-  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-  LOCAL_CFLAGS += -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
-endif
-
-ifeq ($(HOST_OS),windows)
-  LOCAL_LDLIBS += -lws2_32 -lgdi32
-  EXTRA_STATIC_LIBS := AdbWinApi
-endif
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
 
-LOCAL_CLANG := $(adb_host_clang)
+# Use wmain instead of main
+LOCAL_LDFLAGS_windows := -municode
+LOCAL_LDLIBS_windows := -lws2_32 -lgdi32
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
 
 LOCAL_SRC_FILES := \
-    adb_main.cpp \
+    adb_client.cpp \
+    client/main.cpp \
     console.cpp \
     commandline.cpp \
-    adb_client.cpp \
-    services.cpp \
     file_sync_client.cpp \
+    line_printer.cpp \
+    services.cpp \
+    shell_service_protocol.cpp \
 
 LOCAL_CFLAGS += \
     $(ADB_COMMON_CFLAGS) \
     -D_GNU_SOURCE \
     -DADB_HOST=1 \
 
+LOCAL_CFLAGS_windows := \
+    $(ADB_COMMON_windows_CFLAGS)
+
+LOCAL_CFLAGS_linux := \
+    $(ADB_COMMON_linux_CFLAGS) \
+
+LOCAL_CFLAGS_darwin := \
+    $(ADB_COMMON_darwin_CFLAGS) \
+    -Wno-sizeof-pointer-memaccess -Wno-unused-parameter \
+
 LOCAL_MODULE := adb
 LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_HOST_OS := darwin linux windows
 
+LOCAL_SANITIZE := $(adb_host_sanitize)
 LOCAL_STATIC_LIBRARIES := \
     libadb \
     libbase \
     libcrypto_static \
     libcutils \
     liblog \
-    $(EXTRA_STATIC_LIBS) \
 
-# libc++ not available on windows yet
-ifneq ($(HOST_OS),windows)
-    LOCAL_CXX_STL := libc++_static
-endif
+LOCAL_CXX_STL := libc++_static
 
 # Don't add anything here, we don't want additional shared dependencies
 # on the host adb tool, and shared libraries that link against libc++
@@ -204,12 +275,6 @@ 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
 # =========================================================
@@ -219,15 +284,18 @@ include $(CLEAR_VARS)
 LOCAL_CLANG := true
 
 LOCAL_SRC_FILES := \
-    adb_main.cpp \
+    daemon/main.cpp \
     services.cpp \
     file_sync_service.cpp \
     framebuffer_service.cpp \
     remount_service.cpp \
     set_verity_enable_state_service.cpp \
+    shell_service.cpp \
+    shell_service_protocol.cpp \
 
 LOCAL_CFLAGS := \
     $(ADB_COMMON_CFLAGS) \
+    $(ADB_COMMON_linux_CFLAGS) \
     -DADB_HOST=0 \
     -D_GNU_SOURCE \
     -Wno-deprecated-declarations \
@@ -246,15 +314,20 @@ LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
 LOCAL_C_INCLUDES += system/extras/ext4_utils
 
+LOCAL_SANITIZE := $(adb_target_sanitize)
 LOCAL_STATIC_LIBRARIES := \
     libadbd \
     libbase \
     libfs_mgr \
+    libfec \
+    libfec_rs \
+    libselinux \
     liblog \
-    libcutils \
-    libc \
     libmincrypt \
-    libselinux \
     libext4_utils_static \
+    libsquashfs_utils \
+    libcutils \
+    libbase \
+    libcrypto_static
 
 include $(BUILD_EXECUTABLE)
index 9b906e8..f496490 100644 (file)
@@ -1,2 +1,2 @@
 set noparent
-filter=-build/header_guard,-build/include,-readability/function
+filter=-build/header_guard,-build/include,-readability/function,-whitespace/indent
index 63000f2..30c21f7 100644 (file)
@@ -237,7 +237,7 @@ track-jdwp
     Note that there is no single-shot service to retrieve the list only once.
 
 sync:
-    This starts the file synchronisation service, used to implement "adb push"
+    This starts the file synchronization service, used to implement "adb push"
     and "adb pull". Since this service is pretty complex, it will be detailed
     in a companion document named SYNC.TXT
 
index e74d217..06d7804 100644 (file)
@@ -25,12 +25,9 @@ follows as described in SERVICES.TXT.
 
 The following sync requests are accepted:
 LIST - List the files in a folder
+RECV - Retrieve a file from device
 SEND - Send a file to device
-RECV - Retreive a file from device
-
-Not yet documented:
 STAT - Stat a file
-ULNK - Unlink (remove) a file. (Not currently supported)
 
 For all of the sync request above the must be followed by length number of
 bytes containing an utf-8 string with a remote filename.
@@ -40,7 +37,7 @@ Lists files in the directory specified by the remote filename. The server will
 respond with zero or more directory entries or "dents".
 
 The directory entries will be returned in the following form
-1. A four-byte sync response id beeing "DENT"
+1. A four-byte sync response id "DENT"
 2. A four-byte integer representing file mode.
 3. A four-byte integer representing file size.
 4. A four-byte integer representing last modified time.
@@ -60,13 +57,13 @@ the transfer fails. Some file types will not be deleted, which allows
   adb push disk_image /some_block_device
 to work.
 
-After this the actual file is sent in chunks. Each chucks has the following
+After this the actual file is sent in chunks. Each chunk has the following
 format.
 A sync request with id "DATA" and length equal to the chunk size. After
 follows chunk size number of bytes. This is repeated until the file is
-transfered. Each chunk must not be larger than 64k.
+transferred. Each chunk must not be larger than 64k.
 
-When the file is tranfered a sync request "DONE" is sent, where length is set
+When the file is transferred a sync request "DONE" is sent, where length is set
 to the last modified time for the file. The server responds to this last
 request (but not to chuck requests) with an "OKAY" sync response (length can
 be ignored).
@@ -77,8 +74,8 @@ Retrieves a file from device to a local file. The remote path is the path to
 the file that will be returned. Just as for the SEND sync request the file
 received is split up into chunks. The sync response id is "DATA" and length is
 the chuck size. After follows chunk size number of bytes. This is repeated
-until the file is transfered. Each chuck will not be larger than 64k.
+until the file is transferred. Each chuck will not be larger than 64k.
 
-When the file is transfered a sync resopnse "DONE" is retrieved where the
+When the file is transferred a sync response "DONE" is retrieved where the
 length can be ignored.
 
index c09aee3..c39c178 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
 
 #include "sysdeps.h"
 #include "adb.h"
 #include <time.h>
 
 #include <string>
+#include <vector>
 
+#include <base/logging.h>
+#include <base/macros.h>
 #include <base/stringprintf.h>
 #include <base/strings.h>
 
 #include "adb_auth.h"
 #include "adb_io.h"
 #include "adb_listeners.h"
+#include "adb_utils.h"
 #include "transport.h"
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 #include <sys/mount.h>
 #endif
 
-ADB_MUTEX_DEFINE( D_lock );
-
-int HOST = 0;
-
-#if !ADB_HOST
-const char *adb_device_banner = "device";
-#endif
+std::string adb_version() {
+    // Don't change the format of this --- it's parsed by ddmlib.
+    return android::base::StringPrintf("Android Debug Bridge version %d.%d.%d\n"
+                                       "Revision %s\n",
+                                       ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
+                                       ADB_REVISION);
+}
 
-void fatal(const char *fmt, ...)
-{
+void fatal(const char *fmt, ...) {
     va_list ap;
     va_start(ap, fmt);
     fprintf(stderr, "error: ");
@@ -67,8 +70,7 @@ void fatal(const char *fmt, ...)
     exit(-1);
 }
 
-void fatal_errno(const char *fmt, ...)
-{
+void fatal_errno(const char* fmt, ...) {
     va_list ap;
     va_start(ap, fmt);
     fprintf(stderr, "error: %s: ", strerror(errno));
@@ -78,125 +80,6 @@ void fatal_errno(const char *fmt, ...)
     exit(-1);
 }
 
-#if !ADB_HOST
-void start_device_log(void) {
-    struct tm now;
-    time_t t;
-    tzset();
-    time(&t);
-    localtime_r(&t, &now);
-
-    char timestamp[PATH_MAX];
-    strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now);
-
-    std::string path = android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp, getpid());
-    int fd = unix_open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
-    if (fd == -1) {
-        return;
-    }
-
-    // redirect stdout and stderr to the log file
-    dup2(fd, STDOUT_FILENO);
-    dup2(fd, STDERR_FILENO);
-    fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
-    adb_close(fd);
-}
-#endif
-
-int adb_trace_mask;
-
-std::string get_trace_setting_from_env() {
-    const char* setting = getenv("ADB_TRACE");
-    if (setting == nullptr) {
-        setting = "";
-    }
-
-    return std::string(setting);
-}
-
-#if !ADB_HOST
-std::string get_trace_setting_from_prop() {
-    char buf[PROPERTY_VALUE_MAX];
-    property_get("persist.adb.trace_mask", buf, "");
-    return std::string(buf);
-}
-#endif
-
-std::string get_trace_setting() {
-#if ADB_HOST
-    return get_trace_setting_from_env();
-#else
-    return get_trace_setting_from_prop();
-#endif
-}
-
-// Split the comma/space/colum/semi-column separated list of tags from the trace
-// setting and build the trace mask from it. note that '1' and 'all' are special
-// cases to enable all tracing.
-//
-// adb's trace setting comes from the ADB_TRACE environment variable, whereas
-// adbd's comes from the system property persist.adb.trace_mask.
-void adb_trace_init() {
-    const std::string trace_setting = get_trace_setting();
-
-    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 (trace_setting.empty()) {
-        return;
-    }
-
-    // Use a comma/colon/semi-colon/space separated list
-    const char* p = trace_setting.c_str();
-    while (*p) {
-        int  len, tagn;
-
-        const char* 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
-    start_device_log();
-#endif
-}
-
 apacket* get_apacket(void)
 {
     apacket* p = reinterpret_cast<apacket*>(malloc(sizeof(apacket)));
@@ -215,16 +98,21 @@ void put_apacket(apacket *p)
 
 void handle_online(atransport *t)
 {
-    D("adb: online\n");
+    D("adb: online");
     t->online = 1;
 }
 
 void handle_offline(atransport *t)
 {
-    D("adb: offline\n");
+    D("adb: offline");
     //Close the associated usb
     t->online = 0;
-    run_transport_disconnects(t);
+
+    // This is necessary to avoid a race condition that occurred when a transport closes
+    // while a client socket is still active.
+    close_all_sockets(t);
+
+    t->RunDisconnects();
 }
 
 #if DEBUG_PACKETS
@@ -270,7 +158,7 @@ void print_packet(const char *label, apacket *p)
 
 static void send_ready(unsigned local, unsigned remote, atransport *t)
 {
-    D("Calling send_ready \n");
+    D("Calling send_ready");
     apacket *p = get_apacket();
     p->msg.command = A_OKAY;
     p->msg.arg0 = local;
@@ -280,7 +168,7 @@ static void send_ready(unsigned local, unsigned remote, atransport *t)
 
 static void send_close(unsigned local, unsigned remote, atransport *t)
 {
-    D("Calling send_close \n");
+    D("Calling send_close");
     apacket *p = get_apacket();
     p->msg.command = A_CLSE;
     p->msg.arg0 = local;
@@ -288,45 +176,50 @@ static void send_close(unsigned local, unsigned remote, atransport *t)
     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[] = {
+std::string get_connection_string() {
+    std::vector<std::string> connection_properties;
+
+#if !ADB_HOST
+    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++) {
+
+    for (const auto& prop_name : cnxn_props) {
         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;
+        property_get(prop_name, value, "");
+        connection_properties.push_back(
+            android::base::StringPrintf("%s=%s", prop_name, value));
     }
-
-    return bufsize - remaining + 1;
 #endif
+
+    connection_properties.push_back(android::base::StringPrintf(
+        "features=%s", FeatureSetToString(supported_features()).c_str()));
+
+    return android::base::StringPrintf(
+        "%s::%s", adb_device_banner,
+        android::base::Join(connection_properties, ';').c_str());
 }
 
-void send_connect(atransport *t)
-{
-    D("Calling send_connect \n");
-    apacket *cp = get_apacket();
+void send_connect(atransport* t) {
+    D("Calling send_connect");
+    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));
+    cp->msg.arg0 = t->get_protocol_version();
+    cp->msg.arg1 = t->get_max_payload();
+
+    std::string connection_str = get_connection_string();
+    // Connect and auth packets are limited to MAX_PAYLOAD_V1 because we don't
+    // yet know how much data the other size is willing to accept.
+    if (connection_str.length() > MAX_PAYLOAD_V1) {
+        LOG(FATAL) << "Connection banner is too long (length = "
+                   << connection_str.length() << ")";
+    }
+
+    memcpy(cp->data, connection_str.c_str(), connection_str.length());
+    cp->msg.data_length = connection_str.length();
+
     send_packet(cp, t);
 }
 
@@ -339,16 +232,20 @@ static void qual_overwrite(char** dst, const std::string& src) {
     *dst = strdup(src.c_str());
 }
 
-void parse_banner(const char* banner, atransport* t) {
-    D("parse_banner: %s\n", banner);
+void parse_banner(const std::string& banner, atransport* t) {
+    D("parse_banner: %s", banner.c_str());
 
     // The format is something like:
     // "device::ro.product.name=x;ro.product.model=y;ro.product.device=z;".
     std::vector<std::string> pieces = android::base::Split(banner, ":");
 
+    // Reset the features list or else if the server sends no features we may
+    // keep the existing feature set (http://b/24405971).
+    t->SetFeatures("");
+
     if (pieces.size() > 2) {
         const std::string& props = pieces[2];
-        for (auto& prop : android::base::Split(props, ";")) {
+        for (const auto& prop : android::base::Split(props, ";")) {
             // The list of properties was traditionally ;-terminated rather than ;-separated.
             if (prop.empty()) continue;
 
@@ -363,38 +260,63 @@ void parse_banner(const char* banner, atransport* t) {
                 qual_overwrite(&t->model, value);
             } else if (key == "ro.product.device") {
                 qual_overwrite(&t->device, value);
+            } else if (key == "features") {
+                t->SetFeatures(value);
             }
         }
     }
 
     const std::string& type = pieces[0];
     if (type == "bootloader") {
-        D("setting connection_state to CS_BOOTLOADER\n");
-        t->connection_state = CS_BOOTLOADER;
+        D("setting connection_state to kCsBootloader");
+        t->connection_state = kCsBootloader;
         update_transports();
     } else if (type == "device") {
-        D("setting connection_state to CS_DEVICE\n");
-        t->connection_state = CS_DEVICE;
+        D("setting connection_state to kCsDevice");
+        t->connection_state = kCsDevice;
         update_transports();
     } else if (type == "recovery") {
-        D("setting connection_state to CS_RECOVERY\n");
-        t->connection_state = CS_RECOVERY;
+        D("setting connection_state to kCsRecovery");
+        t->connection_state = kCsRecovery;
         update_transports();
     } else if (type == "sideload") {
-        D("setting connection_state to CS_SIDELOAD\n");
-        t->connection_state = CS_SIDELOAD;
+        D("setting connection_state to kCsSideload");
+        t->connection_state = kCsSideload;
         update_transports();
     } else {
-        D("setting connection_state to CS_HOST\n");
-        t->connection_state = CS_HOST;
+        D("setting connection_state to kCsHost");
+        t->connection_state = kCsHost;
+    }
+}
+
+static void handle_new_connection(atransport* t, apacket* p) {
+    if (t->connection_state != kCsOffline) {
+        t->connection_state = kCsOffline;
+        handle_offline(t);
+    }
+
+    t->update_version(p->msg.arg0, p->msg.arg1);
+    std::string banner(reinterpret_cast<const char*>(p->data),
+                       p->msg.data_length);
+    parse_banner(banner, t);
+
+#if ADB_HOST
+    handle_online(t);
+#else
+    if (!auth_required) {
+        handle_online(t);
+        send_connect(t);
+    } else {
+        send_auth_request(t);
     }
+#endif
 }
 
 void handle_packet(apacket *p, atransport *t)
 {
     asocket *s;
 
-    D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0],
+    D("handle_packet() %c%c%c%c", ((char*) (&(p->msg.command)))[0],
             ((char*) (&(p->msg.command)))[1],
             ((char*) (&(p->msg.command)))[2],
             ((char*) (&(p->msg.command)))[3]);
@@ -404,34 +326,23 @@ void handle_packet(apacket *p, atransport *t)
     case A_SYNC:
         if(p->msg.arg0){
             send_packet(p, t);
-            if(HOST) send_connect(t);
+#if ADB_HOST
+            send_connect(t);
+#endif
         } else {
-            t->connection_state = CS_OFFLINE;
+            t->connection_state = kCsOffline;
             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(reinterpret_cast<const char*>(p->data), t);
-
-        if (HOST || !auth_required) {
-            handle_online(t);
-            if (!HOST) send_connect(t);
-        } else {
-            send_auth_request(t);
-        }
+    case A_CNXN:  // CONNECT(version, maxdata, "system-id-string")
+        handle_new_connection(t, p);
         break;
 
     case A_AUTH:
         if (p->msg.arg0 == ADB_AUTH_TOKEN) {
-            t->connection_state = CS_UNAUTHORIZED;
+            t->connection_state = kCsUnauthorized;
             t->key = adb_auth_nextkey(t->key);
             if (t->key) {
                 send_auth_response(p->data, p->msg.data_length, t);
@@ -457,7 +368,7 @@ void handle_packet(apacket *p, atransport *t)
         if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
             char *name = (char*) p->data;
             name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
-            s = create_local_service_socket(name);
+            s = create_local_service_socket(name, t);
             if(s == 0) {
                 send_close(0, p->msg.arg0, t);
             } else {
@@ -481,9 +392,14 @@ void handle_packet(apacket *p, atransport *t)
                     /* Other READY messages must use the same local-id */
                     s->ready(s);
                 } else {
-                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s\n",
+                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s",
                       p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial);
                 }
+            } else {
+                // When receiving A_OKAY from device for A_OPEN request, the host server may
+                // have closed the local socket because of client disconnection. Then we need
+                // to send A_CLSE back to device to close the service on device.
+                send_close(p->msg.arg1, p->msg.arg0, t);
             }
         }
         break;
@@ -502,7 +418,7 @@ void handle_packet(apacket *p, atransport *t)
                  * socket has a peer on the same transport.
                  */
                 if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) {
-                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s\n",
+                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s",
                       p->msg.arg1, t->serial, s->peer->transport->serial);
                 } else {
                     s->close(s);
@@ -518,7 +434,7 @@ void handle_packet(apacket *p, atransport *t)
                 p->len = p->msg.data_length;
 
                 if(s->enqueue(s, p) == 0) {
-                    D("Enqueue the socket\n");
+                    D("Enqueue the socket");
                     send_ready(s->id, rid, t);
                 }
                 return;
@@ -535,6 +451,119 @@ void handle_packet(apacket *p, atransport *t)
 
 #if ADB_HOST
 
+#ifdef _WIN32
+
+// Try to make a handle non-inheritable and if there is an error, don't output
+// any error info, but leave GetLastError() for the caller to read. This is
+// convenient if the caller is expecting that this may fail and they'd like to
+// ignore such a failure.
+static bool _try_make_handle_noninheritable(HANDLE h) {
+    if (h != INVALID_HANDLE_VALUE && h != NULL) {
+        return SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0) ? true : false;
+    }
+
+    return true;
+}
+
+// Try to make a handle non-inheritable with the expectation that this should
+// succeed, so if this fails, output error info.
+static bool _make_handle_noninheritable(HANDLE h) {
+    if (!_try_make_handle_noninheritable(h)) {
+        // Show the handle value to give us a clue in case we have problems
+        // with pseudo-handle values.
+        fprintf(stderr, "Cannot make handle 0x%p non-inheritable: %s\n",
+                h, SystemErrorCodeToString(GetLastError()).c_str());
+        return false;
+    }
+
+    return true;
+}
+
+// Create anonymous pipe, preventing inheritance of the read pipe and setting
+// security of the write pipe to sa.
+static bool _create_anonymous_pipe(unique_handle* pipe_read_out,
+                                   unique_handle* pipe_write_out,
+                                   SECURITY_ATTRIBUTES* sa) {
+    HANDLE pipe_read_raw = NULL;
+    HANDLE pipe_write_raw = NULL;
+    if (!CreatePipe(&pipe_read_raw, &pipe_write_raw, sa, 0)) {
+        fprintf(stderr, "Cannot create pipe: %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
+        return false;
+    }
+
+    unique_handle pipe_read(pipe_read_raw);
+    pipe_read_raw = NULL;
+    unique_handle pipe_write(pipe_write_raw);
+    pipe_write_raw = NULL;
+
+    if (!_make_handle_noninheritable(pipe_read.get())) {
+        return false;
+    }
+
+    *pipe_read_out = std::move(pipe_read);
+    *pipe_write_out = std::move(pipe_write);
+
+    return true;
+}
+
+// Read from a pipe (that we take ownership of) and write what is returned to
+// GetStdHandle(nStdHandle). Return on error or when the pipe is closed.
+static unsigned _redirect_pipe_thread(HANDLE h, DWORD nStdHandle) {
+    // Take ownership of the HANDLE and close when we're done.
+    unique_handle   read_pipe(h);
+    const HANDLE    write_handle = GetStdHandle(nStdHandle);
+    const char*     output_name = nStdHandle == STD_OUTPUT_HANDLE ?
+                                      "stdout" : "stderr";
+
+    while (true) {
+        char    buf[64 * 1024];
+        DWORD   bytes_read = 0;
+        if (!ReadFile(read_pipe.get(), buf, sizeof(buf), &bytes_read, NULL)) {
+            const DWORD err = GetLastError();
+            // ERROR_BROKEN_PIPE is expected when the subprocess closes
+            // the other end of the pipe.
+            if (err == ERROR_BROKEN_PIPE) {
+                return EXIT_SUCCESS;
+            } else {
+                fprintf(stderr, "Failed to read from %s: %s\n", output_name,
+                        SystemErrorCodeToString(err).c_str());
+                return EXIT_FAILURE;
+            }
+        }
+
+        // Don't try to write if our stdout/stderr was not setup by the
+        // parent process.
+        if (write_handle != NULL && write_handle != INVALID_HANDLE_VALUE) {
+            DWORD   bytes_written = 0;
+            if (!WriteFile(write_handle, buf, bytes_read, &bytes_written,
+                           NULL)) {
+                fprintf(stderr, "Failed to write to %s: %s\n", output_name,
+                        SystemErrorCodeToString(GetLastError()).c_str());
+                return EXIT_FAILURE;
+            }
+
+            if (bytes_written != bytes_read) {
+                fprintf(stderr, "Only wrote %lu of %lu bytes to %s\n",
+                        bytes_written, bytes_read, output_name);
+                return EXIT_FAILURE;
+            }
+        }
+    }
+}
+
+static unsigned __stdcall _redirect_stdout_thread(HANDLE h) {
+    adb_thread_setname("stdout redirect");
+    return _redirect_pipe_thread(h, STD_OUTPUT_HANDLE);
+}
+
+static unsigned __stdcall _redirect_stderr_thread(HANDLE h) {
+    adb_thread_setname("stderr redirect");
+    return _redirect_pipe_thread(h, STD_ERROR_HANDLE);
+}
+
+#endif
+
 int launch_server(int server_port)
 {
 #if defined(_WIN32)
@@ -542,26 +571,42 @@ int launch_server(int server_port)
     /* 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() );
+    // Redirect stdin to Windows /dev/null. If we instead pass an original
+    // stdin/stdout/stderr handle and it is a console handle, when the adb
+    // server starts up, the C Runtime will see a console handle for a process
+    // that isn't connected to a console and it will configure
+    // stdin/stdout/stderr to be closed. At that point, freopen() could be used
+    // to reopen stderr/out, but it would take more massaging to fixup the file
+    // descriptor number that freopen() uses. It's simplest to avoid all of this
+    // complexity by just redirecting stdin to `nul' and then the C Runtime acts
+    // as expected.
+    unique_handle   nul_read(CreateFileW(L"nul", GENERIC_READ,
+            FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING,
+            FILE_ATTRIBUTE_NORMAL, NULL));
+    if (nul_read.get() == INVALID_HANDLE_VALUE) {
+        fprintf(stderr, "Cannot open 'nul': %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
 
-    SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
+    // create pipes with non-inheritable read handle, inheritable write handle
+    unique_handle   ack_read, ack_write;
+    if (!_create_anonymous_pipe(&ack_read, &ack_write, &sa)) {
+        return -1;
+    }
+    unique_handle   stdout_read, stdout_write;
+    if (!_create_anonymous_pipe(&stdout_read, &stdout_write, &sa)) {
+        return -1;
+    }
+    unique_handle   stderr_read, stderr_write;
+    if (!_create_anonymous_pipe(&stderr_read, &stderr_write, &sa)) {
+        return -1;
+    }
 
     /* Some programs want to launch an adb command and collect its output by
      * calling CreateProcess with inheritable stdout/stderr handles, then
@@ -573,30 +618,61 @@ int launch_server(int server_port)
      * the calling process is stuck while read()-ing from the stdout/stderr
      * descriptors, because they're connected to corresponding handles in the
      * adb server process (even if the latter never uses/writes to them).
+     * Note that even if we don't pass these handles in the STARTUPINFO struct,
+     * if they're marked inheritable, they're still inherited, requiring us to
+     * deal with this.
+     *
+     * If we're still having problems with inheriting random handles in the
+     * future, consider using PROC_THREAD_ATTRIBUTE_HANDLE_LIST to explicitly
+     * specify which handles should be inherited: http://blogs.msdn.com/b/oldnewthing/archive/2011/12/16/10248328.aspx
+     *
+     * Older versions of Windows return console pseudo-handles that cannot be
+     * made non-inheritable, so ignore those failures.
      */
-    stdout_handle = GetStdHandle( STD_OUTPUT_HANDLE );
-    stderr_handle = GetStdHandle( STD_ERROR_HANDLE );
-    if (stdout_handle != INVALID_HANDLE_VALUE) {
-        SetHandleInformation( stdout_handle, HANDLE_FLAG_INHERIT, 0 );
-    }
-    if (stderr_handle != INVALID_HANDLE_VALUE) {
-        SetHandleInformation( stderr_handle, HANDLE_FLAG_INHERIT, 0 );
-    }
+    _try_make_handle_noninheritable(GetStdHandle(STD_INPUT_HANDLE));
+    _try_make_handle_noninheritable(GetStdHandle(STD_OUTPUT_HANDLE));
+    _try_make_handle_noninheritable(GetStdHandle(STD_ERROR_HANDLE));
 
+    STARTUPINFOW    startup;
     ZeroMemory( &startup, sizeof(startup) );
     startup.cb = sizeof(startup);
-    startup.hStdInput  = GetStdHandle( STD_INPUT_HANDLE );
-    startup.hStdOutput = pipe_write;
-    startup.hStdError  = GetStdHandle( STD_ERROR_HANDLE );
+    startup.hStdInput  = nul_read.get();
+    startup.hStdOutput = stdout_write.get();
+    startup.hStdError  = stderr_write.get();
     startup.dwFlags    = STARTF_USESTDHANDLES;
 
-    ZeroMemory( &pinfo, sizeof(pinfo) );
+    // Verify that the pipe_write handle value can be passed on the command line
+    // as %d and that the rest of adb code can pass it around in an int.
+    const int ack_write_as_int = cast_handle_to_int(ack_write.get());
+    if (cast_int_to_handle(ack_write_as_int) != ack_write.get()) {
+        // If this fires, either handle values are larger than 32-bits or else
+        // there is a bug in our casting.
+        // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
+        fprintf(stderr, "Cannot fit pipe handle value into 32-bits: 0x%p\n",
+                ack_write.get());
+        return -1;
+    }
+
+    // get path of current program
+    WCHAR       program_path[MAX_PATH];
+    const DWORD module_result = GetModuleFileNameW(NULL, program_path,
+                                                   arraysize(program_path));
+    if ((module_result >= arraysize(program_path)) || (module_result == 0)) {
+        // String truncation or some other error.
+        fprintf(stderr, "Cannot get executable path: %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
+        return -1;
+    }
+
+    WCHAR   args[64];
+    snwprintf(args, arraysize(args),
+              L"adb -P %d fork-server server --reply-fd %d", server_port,
+              ack_write_as_int);
 
-    /* get path of current program */
-    GetModuleFileName( NULL, program_path, sizeof(program_path) );
-    char args[64];
-    snprintf(args, sizeof(args), "adb -P %d fork-server server",  server_port);
-    ret = CreateProcess(
+    PROCESS_INFORMATION   pinfo;
+    ZeroMemory(&pinfo, sizeof(pinfo));
+
+    if (!CreateProcessW(
             program_path,                              /* program path  */
             args,
                                     /* the fork-server argument will set the
@@ -608,41 +684,120 @@ int launch_server(int server_port)
             NULL,                     /* use parent's environment block */
             NULL,                    /* use parent's starting directory */
             &startup,                 /* startup info, i.e. std handles */
-            &pinfo );
+            &pinfo )) {
+        fprintf(stderr, "Cannot create process: %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
+        return -1;
+    }
 
-    CloseHandle( pipe_write );
+    unique_handle   process_handle(pinfo.hProcess);
+    pinfo.hProcess = NULL;
+
+    // Close handles that we no longer need to complete the rest.
+    CloseHandle(pinfo.hThread);
+    pinfo.hThread = NULL;
+
+    nul_read.reset();
+    ack_write.reset();
+    stdout_write.reset();
+    stderr_write.reset();
+
+    // Start threads to read from subprocess stdout/stderr and write to ours
+    // to make subprocess errors easier to diagnose.
+
+    // In the past, reading from a pipe before the child process's C Runtime
+    // started up and called GetFileType() caused a hang: http://blogs.msdn.com/b/oldnewthing/archive/2011/12/02/10243553.aspx#10244216
+    // This is reportedly fixed in Windows Vista: https://support.microsoft.com/en-us/kb/2009703
+    // I was unable to reproduce the problem on Windows XP. It sounds like a
+    // Windows Update may have fixed this: https://www.duckware.com/tech/peeknamedpipe.html
+    unique_handle   stdout_thread(reinterpret_cast<HANDLE>(
+            _beginthreadex(NULL, 0, _redirect_stdout_thread, stdout_read.get(),
+                           0, NULL)));
+    if (stdout_thread.get() == nullptr) {
+        fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+        return -1;
+    }
+    stdout_read.release();  // Transfer ownership to new thread
 
-    if (!ret) {
-        fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
-        CloseHandle( pipe_read );
+    unique_handle   stderr_thread(reinterpret_cast<HANDLE>(
+            _beginthreadex(NULL, 0, _redirect_stderr_thread, stderr_read.get(),
+                           0, NULL)));
+    if (stderr_thread.get() == nullptr) {
+        fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
         return -1;
     }
+    stderr_read.release();  // Transfer ownership to new thread
 
-    CloseHandle( pinfo.hProcess );
-    CloseHandle( pinfo.hThread );
+    bool    got_ack = false;
 
-    /* wait for the "OK\n" message */
+    // Wait for the "OK\n" message, for the pipe to be closed, or other error.
     {
-        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;
+        char    temp[3];
+        DWORD   count = 0;
+
+        if (ReadFile(ack_read.get(), temp, sizeof(temp), &count, NULL)) {
+            const CHAR  expected[] = "OK\n";
+            const DWORD expected_length = arraysize(expected) - 1;
+            if (count == expected_length &&
+                memcmp(temp, expected, expected_length) == 0) {
+                got_ack = true;
+            } else {
+                fprintf(stderr, "ADB server didn't ACK\n");
+            }
+        } else {
+            const DWORD err = GetLastError();
+            // If the ACK was not written and the process exited, GetLastError()
+            // is probably ERROR_BROKEN_PIPE, in which case that info is not
+            // useful to the user.
+            fprintf(stderr, "could not read ok from ADB Server%s\n",
+                    err == ERROR_BROKEN_PIPE ? "" :
+                    android::base::StringPrintf(": %s",
+                            SystemErrorCodeToString(err).c_str()).c_str());
         }
-        if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
-            fprintf(stderr, "ADB server didn't ACK\n" );
-            return -1;
+    }
+
+    // Always try to wait a bit for threads reading stdout/stderr to finish.
+    // If the process started ok, it should close the pipes causing the threads
+    // to finish. If the process had an error, it should exit, also causing
+    // the pipes to be closed. In that case we want to read all of the output
+    // and write it out so that the user can diagnose failures.
+    const DWORD     thread_timeout_ms = 15 * 1000;
+    const HANDLE    threads[] = { stdout_thread.get(), stderr_thread.get() };
+    const DWORD     wait_result = WaitForMultipleObjects(arraysize(threads),
+            threads, TRUE, thread_timeout_ms);
+    if (wait_result == WAIT_TIMEOUT) {
+        // Threads did not finish after waiting a little while. Perhaps the
+        // server didn't close pipes, or it is hung.
+        fprintf(stderr, "Timed-out waiting for threads to finish reading from "
+                "ADB Server\n");
+        // Process handles are signaled when the process exits, so if we wait
+        // on the handle for 0 seconds and it returns 'timeout', that means that
+        // the process is still running.
+        if (WaitForSingleObject(process_handle.get(), 0) == WAIT_TIMEOUT) {
+            // We could TerminateProcess(), but that seems somewhat presumptive.
+            fprintf(stderr, "ADB Server is running: process id %lu\n",
+                    pinfo.dwProcessId);
         }
+        return -1;
+    }
+
+    if (wait_result != WAIT_OBJECT_0) {
+        fprintf(stderr, "Unexpected result waiting for threads: %lu: %s\n",
+                wait_result, SystemErrorCodeToString(GetLastError()).c_str());
+        return -1;
+    }
+
+    // For now ignore the thread exit codes and assume they worked properly.
+
+    if (!got_ack) {
+        return -1;
     }
 #else /* !defined(_WIN32) */
     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.
+    // fd[0] will be parent's end, and the child will write on fd[1]
     if (pipe(fd)) {
         fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
         return -1;
@@ -654,16 +809,14 @@ int launch_server(int server_port)
     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);
+        snprintf(str_port, sizeof(str_port), "%d", server_port);
+        char reply_fd[30];
+        snprintf(reply_fd, sizeof(reply_fd), "%d", fd[1]);
         // child process
-        int result = execl(path, "adb", "-P", str_port, "fork-server", "server", NULL);
+        int result = execl(path, "adb", "-P", str_port, "fork-server", "server", "--reply-fd", reply_fd, NULL);
         // this should not return
         fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
     } else  {
@@ -696,7 +849,7 @@ int launch_server(int server_port)
 // Try to handle a network forwarding request.
 // This returns 1 on success, 0 on failure, and -1 to indicate this is not
 // a forwarding-related request.
-int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd)
+int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd)
 {
     if (!strcmp(service, "list-forward")) {
         // Create the list of forward redirections.
@@ -718,56 +871,52 @@ int handle_forward_request(const char* service, transport_type ttype, char* seri
         return 1;
     }
 
-    if (!strncmp(service, "forward:",8) ||
-        !strncmp(service, "killforward:",12)) {
-        char *local, *remote;
-        atransport *transport;
-
-        int createForward = strncmp(service, "kill", 4);
-        int no_rebind = 0;
-
-        local = strchr(service, ':') + 1;
-
-        // Handle forward:norebind:<local>... here
-        if (createForward && !strncmp(local, "norebind:", 9)) {
-            no_rebind = 1;
-            local = strchr(local, ':') + 1;
+    if (!strncmp(service, "forward:", 8) || !strncmp(service, "killforward:", 12)) {
+        // killforward:local
+        // forward:(norebind:)?local;remote
+        bool kill_forward = false;
+        bool no_rebind = false;
+        if (android::base::StartsWith(service, "killforward:")) {
+            kill_forward = true;
+            service += 12;
+        } else {
+            service += 8;   // skip past "forward:"
+            if (android::base::StartsWith(service, "norebind:")) {
+                no_rebind = true;
+                service += 9;
+            }
         }
 
-        remote = strchr(local,';');
-
-        if (createForward) {
-            // Check forward: parameter format: '<local>;<remote>'
-            if(remote == 0) {
-                SendFail(reply_fd, "malformed forward spec");
-                return 1;
-            }
+        std::vector<std::string> pieces = android::base::Split(service, ";");
 
-            *remote++ = 0;
-            if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')) {
-                SendFail(reply_fd, "malformed forward spec");
+        if (kill_forward) {
+            // Check killforward: parameter format: '<local>'
+            if (pieces.size() != 1 || pieces[0].empty()) {
+                SendFail(reply_fd, android::base::StringPrintf("bad killforward: %s", service));
                 return 1;
             }
         } else {
-            // Check killforward: parameter format: '<local>'
-            if (local[0] == 0) {
-                SendFail(reply_fd, "malformed forward spec");
+            // Check forward: parameter format: '<local>;<remote>'
+            if (pieces.size() != 2 || pieces[0].empty() || pieces[1].empty() || pieces[1][0] == '*') {
+                SendFail(reply_fd, android::base::StringPrintf("bad forward: %s", service));
                 return 1;
             }
         }
 
         std::string error_msg;
-        transport = acquire_one_transport(CS_ANY, ttype, serial, &error_msg);
+        atransport* transport = acquire_one_transport(type, serial, nullptr, &error_msg);
         if (!transport) {
             SendFail(reply_fd, error_msg);
             return 1;
         }
 
-        install_status_t r;
-        if (createForward) {
-            r = install_listener(local, remote, transport, no_rebind);
+        std::string error;
+        InstallStatus r;
+        if (kill_forward) {
+            r = remove_listener(pieces[0].c_str(), transport);
         } else {
-            r = remove_listener(local, transport);
+            r = install_listener(pieces[0], pieces[1].c_str(), transport,
+                                 no_rebind, &error);
         }
         if (r == INSTALL_STATUS_OK) {
 #if ADB_HOST
@@ -780,15 +929,18 @@ int handle_forward_request(const char* service, transport_type ttype, char* seri
 
         std::string message;
         switch (r) {
-          case INSTALL_STATUS_OK: message = " "; break;
+          case INSTALL_STATUS_OK: message = "success (!)"; break;
           case INSTALL_STATUS_INTERNAL_ERROR: message = "internal error"; break;
           case INSTALL_STATUS_CANNOT_BIND:
-            message = android::base::StringPrintf("cannot bind to socket: %s", strerror(errno));
+            message = android::base::StringPrintf("cannot bind listener: %s",
+                                                  error.c_str());
             break;
           case INSTALL_STATUS_CANNOT_REBIND:
-            message = android::base::StringPrintf("cannot rebind existing socket: %s", strerror(errno));
+            message = android::base::StringPrintf("cannot rebind existing socket");
+            break;
+          case INSTALL_STATUS_LISTENER_NOT_FOUND:
+            message = android::base::StringPrintf("listener '%s' not found", service);
             break;
-          case INSTALL_STATUS_LISTENER_NOT_FOUND: message = "listener not found"; break;
         }
         SendFail(reply_fd, message);
         return 1;
@@ -796,24 +948,38 @@ int handle_forward_request(const char* service, transport_type ttype, char* seri
     return 0;
 }
 
-int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
-{
-    if(!strcmp(service, "kill")) {
-        fprintf(stderr,"adb server killed by remote request\n");
+#if ADB_HOST
+static int SendOkay(int fd, const std::string& s) {
+    SendOkay(fd);
+    SendProtocolString(fd, s);
+    return 0;
+}
+#endif
+
+int handle_host_request(const char* service, TransportType type,
+                        const char* serial, int reply_fd, asocket* s) {
+    if (strcmp(service, "kill") == 0) {
+        fprintf(stderr, "adb server killed by remote request\n");
         fflush(stdout);
         SendOkay(reply_fd);
-        usb_cleanup();
+
+        // On Windows, if the process exits with open sockets that
+        // shutdown(SD_SEND) has not been called on, TCP RST segments will be
+        // sent to the peers which will cause their next recv() to error-out
+        // with WSAECONNRESET. In the case of this code, that means the client
+        // may not read the OKAY sent above.
+        adb_shutdown(reply_fd);
+
         exit(0);
     }
 
 #if ADB_HOST
-    atransport *transport = NULL;
     // "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"))) {
-        transport_type type = kTransportAny;
+        TransportType type = kTransportAny;
 
         if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
             type = kTransportUsb;
@@ -826,14 +992,13 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r
             serial = service;
         }
 
-        std::string error_msg = "unknown failure";
-        transport = acquire_one_transport(CS_ANY, type, serial, &error_msg);
-
-        if (transport) {
-            s->transport = transport;
+        std::string error;
+        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        if (t != nullptr) {
+            s->transport = t;
             SendOkay(reply_fd);
         } else {
-            SendFail(reply_fd, error_msg);
+            SendFail(reply_fd, error);
         }
         return 1;
     }
@@ -842,89 +1007,94 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r
     if (!strncmp(service, "devices", 7)) {
         bool long_listing = (strcmp(service+7, "-l") == 0);
         if (long_listing || service[7] == 0) {
-            D("Getting device list...\n");
+            D("Getting device list...");
             std::string device_list = list_transports(long_listing);
-            D("Sending device list...\n");
-            SendOkay(reply_fd);
-            SendProtocolString(reply_fd, device_list);
-            return 0;
+            D("Sending device list...");
+            return SendOkay(reply_fd, device_list);
         }
         return 1;
     }
 
-    // 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();
+    if (!strcmp(service, "features")) {
+        std::string error;
+        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        if (t != nullptr) {
+            SendOkay(reply_fd, FeatureSetToString(t->features()));
         } 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);
+            SendFail(reply_fd, error);
+        }
+        return 0;
+    }
 
-            if (t) {
-                unregister_transport(t);
-            } else {
-                snprintf(buffer, sizeof(buffer), "No such device %s", serial);
-            }
+    // remove TCP transport
+    if (!strncmp(service, "disconnect:", 11)) {
+        const std::string address(service + 11);
+        if (address.empty()) {
+            kick_all_tcp_devices();
+            return SendOkay(reply_fd, "disconnected everything");
         }
 
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, buffer);
-        return 0;
+        std::string serial;
+        std::string host;
+        int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+        std::string error;
+        if (!parse_host_and_port(address, &serial, &host, &port, &error)) {
+            return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
+                                                                  address.c_str(), error.c_str()));
+        }
+        atransport* t = find_transport(serial.c_str());
+        if (t == nullptr) {
+            return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
+                                                                  serial.c_str()));
+        }
+        kick_transport(t);
+        return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
     }
 
-    // returns our value for ADB_SERVER_VERSION
+    // Returns our value for ADB_SERVER_VERSION.
     if (!strcmp(service, "version")) {
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
-        return 0;
+        return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
     }
 
-    if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
-        const char *out = "unknown";
-        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
-        if (transport && transport->serial) {
-            out = transport->serial;
+    // These always report "unknown" rather than the actual error, for scripts.
+    if (!strcmp(service, "get-serialno")) {
+        std::string error;
+        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        if (t) {
+            return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
+        } else {
+            return SendFail(reply_fd, error);
         }
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, out);
-        return 0;
     }
-    if(!strncmp(service,"get-devpath",strlen("get-devpath"))) {
-        const char *out = "unknown";
-        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
-        if (transport && transport->devpath) {
-            out = transport->devpath;
+    if (!strcmp(service, "get-devpath")) {
+        std::string error;
+        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        if (t) {
+            return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
+        } else {
+            return SendFail(reply_fd, error);
         }
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, out);
-        return 0;
     }
-    // indicates a new emulator instance has started
-    if (!strncmp(service,"emulator:",9)) {
+    if (!strcmp(service, "get-state")) {
+        std::string error;
+        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
+        if (t) {
+            return SendOkay(reply_fd, t->connection_state_name());
+        } else {
+            return SendFail(reply_fd, error);
+        }
+    }
+
+    // Indicates a new emulator instance has started.
+    if (!strncmp(service, "emulator:", 9)) {
         int  port = atoi(service+9);
         local_connect(port);
         /* we don't even need to send a reply */
         return 0;
     }
-
-    if(!strncmp(service,"get-state",strlen("get-state"))) {
-        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, transport ? transport->connection_state_name() : "unknown");
-        return 0;
-    }
 #endif // ADB_HOST
 
-    int ret = handle_forward_request(service, ttype, serial, reply_fd);
+    int ret = handle_forward_request(service, type, serial, reply_fd);
     if (ret >= 0)
       return ret - 1;
     return -1;
index fd9d0e6..491fff3 100644 (file)
--- a/adb/adb.h
+++ b/adb/adb.h
 #include <limits.h>
 #include <sys/types.h>
 
+#include <string>
+
+#include <base/macros.h>
+
 #include "adb_trace.h"
 #include "fdevent.h"
+#include "socket.h"
 
-#define MAX_PAYLOAD 4096
+constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
+constexpr size_t MAX_PAYLOAD_V2 = 256 * 1024;
+constexpr size_t MAX_PAYLOAD = MAX_PAYLOAD_V2;
 
 #define A_SYNC 0x434e5953
 #define A_CNXN 0x4e584e43
 #define ADB_VERSION_MAJOR 1
 #define ADB_VERSION_MINOR 0
 
+std::string adb_version();
+
 // Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 32
+#define ADB_SERVER_VERSION 35
 
-struct atransport;
+class atransport;
 struct usb_handle;
 
 struct amessage {
@@ -66,78 +75,6 @@ struct apacket
     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);
-
-        /* shutdown is called by the peer before it goes away.
-        ** the socket should not do any further calls on its peer.
-        ** Always followed by a call to close. Optional, i.e. can be NULL.
-        */
-    void (*shutdown)(asocket *s);
-
-        /* close is called by the peer when it has gone away.
-        ** we are not allowed to make any further calls on the
-        ** peer once our close method is called.
-        */
-    void (*close)(asocket *s);
-
-        /* A socket is bound to atransport */
-    atransport *transport;
-};
-
-
 /* 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
@@ -147,73 +84,38 @@ 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.
-*/
-enum transport_type {
-        kTransportUsb,
-        kTransportLocal,
-        kTransportAny,
-        kTransportHost,
+// 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.
+enum TransportType {
+    kTransportUsb,
+    kTransportLocal,
+    kTransportAny,
+    kTransportHost,
 };
 
 #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;
-
-    const char* connection_state_name() const;
+enum ConnectionState {
+    kCsAny = -1,
+    kCsOffline = 0,
+    kCsBootloader,
+    kCsDevice,
+    kCsHost,
+    kCsRecovery,
+    kCsNoPerm,  // Insufficient permissions to communicate with the device.
+    kCsSideload,
+    kCsUnauthorized,
 };
 
-
 /* A listener is an entity which binds to a local port
 ** and, upon receiving a connection on that port, creates
 ** an asocket to connect the new local connection to a
@@ -240,39 +142,29 @@ struct alistener
 
 void print_packet(const char *label, apacket *p);
 
-asocket *find_local_socket(unsigned local_id, unsigned remote_id);
-void install_local_socket(asocket *s);
-void remove_socket(asocket *s);
-void close_all_sockets(atransport *t);
-
-asocket *create_local_socket(int fd);
-asocket *create_local_service_socket(const char *destination);
 
-asocket *create_remote_socket(unsigned id, atransport *t);
-void connect_to_remote(asocket *s, const char *destination);
-void connect_to_smartsocket(asocket *s);
 
-void fatal(const char *fmt, ...);
-void fatal_errno(const char *fmt, ...);
+void fatal(const char *fmt, ...) __attribute__((noreturn));
+void fatal_errno(const char *fmt, ...) __attribute__((noreturn));
 
 void handle_packet(apacket *p, atransport *t);
 
 void get_my_path(char *s, size_t maxLen);
 int launch_server(int server_port);
-int adb_main(int is_daemon, int server_port);
+int adb_main(int is_daemon, int server_port, int ack_reply_fd);
 
 /* 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);
+void init_usb_transport(atransport *t, usb_handle *usb, ConnectionState state);
 
 #if ADB_HOST
 atransport* find_emulator_transport_by_adb_port(int adb_port);
 #endif
 
-int service_to_fd(const char *name);
+int service_to_fd(const char* name, const atransport* transport);
 #if ADB_HOST
 asocket *host_service_to_socket(const char*  name, const char *serial);
 #endif
@@ -284,7 +176,7 @@ asocket*  create_jdwp_tracker_service_socket();
 int       create_jdwp_connection_fd(int  jdwp_pid);
 #endif
 
-int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd);
+int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd);
 
 #if !ADB_HOST
 void framebuffer_service(int fd, void *cookie);
@@ -319,12 +211,11 @@ void put_apacket(apacket *p);
 
 
 void local_init(int port);
-int  local_connect(int  port);
-int  local_connect_arbitrary_ports(int console_port, int adb_port);
+void local_connect(int port);
+int  local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error);
 
 /* usb host/client interface */
 void usb_init();
-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);
@@ -337,26 +228,13 @@ int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_
 
 int adb_commandline(int argc, const char **argv);
 
-int connection_state(atransport *t);
+ConnectionState 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 const char* adb_device_banner;
 
-extern const char *adb_device_banner;
-extern int HOST;
+#if !ADB_HOST
 extern int SHELL_EXIT_NOTIFY_FD;
-
-enum subproc_mode {
-    SUBPROC_PTY = 0,
-    SUBPROC_RAW = 1,
-} ;
+#endif // !ADB_HOST
 
 #define CHUNK_SIZE (64*1024)
 
@@ -371,11 +249,13 @@ enum subproc_mode {
 #define USB_FFS_ADB_IN    USB_FFS_ADB_EP(ep2)
 #endif
 
-int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s);
+int handle_host_request(const char* service, TransportType type, const char* serial, int reply_fd, asocket *s);
 
 void handle_online(atransport *t);
 void handle_offline(atransport *t);
 
 void send_connect(atransport *t);
 
+void parse_banner(const std::string&, atransport* t);
+
 #endif
index cff26d6..1ffab09 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
 
 #include "sysdeps.h"
 #include "adb_auth.h"
@@ -32,13 +32,13 @@ bool auth_required = true;
 
 void send_auth_request(atransport *t)
 {
-    D("Calling send_auth_request\n");
+    D("Calling send_auth_request");
     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);
+        D("Error generating token ret=%d", ret);
         return;
     }
 
@@ -52,13 +52,13 @@ void send_auth_request(atransport *t)
 
 void send_auth_response(uint8_t *token, size_t token_size, atransport *t)
 {
-    D("Calling send_auth_response\n");
+    D("Calling send_auth_response");
     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");
+        D("Error signing the token");
         put_apacket(p);
         return;
     }
@@ -71,13 +71,13 @@ void send_auth_response(uint8_t *token, size_t token_size, atransport *t)
 
 void send_auth_publickey(atransport *t)
 {
-    D("Calling send_auth_publickey\n");
+    D("Calling send_auth_publickey");
     apacket *p = get_apacket();
     int ret;
 
-    ret = adb_auth_get_userkey(p->data, sizeof(p->data));
+    ret = adb_auth_get_userkey(p->data, MAX_PAYLOAD_V1);
     if (!ret) {
-        D("Failed to get user public key\n");
+        D("Failed to get user public key");
         put_apacket(p);
         return;
     }
index 8e7d38b..463b496 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_AUTH
+#define TRACE_TAG AUTH
 
 #include "sysdeps.h"
 #include "adb_auth.h"
@@ -47,20 +47,20 @@ 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 struct adisconnect usb_disconnect = { usb_disconnected, nullptr};
 static atransport* usb_transport;
 static bool needs_retry = false;
 
 static void read_keys(const char *file, struct listnode *list)
 {
     FILE *f;
-    char buf[MAX_PAYLOAD];
+    char buf[MAX_PAYLOAD_V1];
     char *sep;
     int ret;
 
     f = fopen(file, "re");
     if (!f) {
-        D("Can't open '%s'\n", file);
+        D("Can't open '%s'", file);
         return;
     }
 
@@ -69,7 +69,7 @@ static void read_keys(const char *file, struct listnode *list)
         auto key = reinterpret_cast<adb_public_key*>(
             calloc(1, sizeof(adb_public_key) + 4));
         if (key == nullptr) {
-            D("Can't malloc key\n");
+            D("Can't malloc key");
             break;
         }
 
@@ -79,13 +79,13 @@ static void read_keys(const char *file, struct listnode *list)
 
         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);
+            D("%s: Invalid base64 data ret=%d", file, ret);
             free(key);
             continue;
         }
 
         if (key->key.len != RSANUMWORDS) {
-            D("%s: Invalid key len %d\n", file, key->key.len);
+            D("%s: Invalid key len %d", file, key->key.len);
             free(key);
             continue;
         }
@@ -117,7 +117,7 @@ static void load_keys(struct listnode *list)
 
     while ((path = *paths++)) {
         if (!stat(path, &buf)) {
-            D("Loading keys from '%s'\n", path);
+            D("Loading keys from '%s'", path);
             read_keys(path, list);
         }
     }
@@ -163,8 +163,7 @@ int adb_auth_verify(uint8_t* token, uint8_t* sig, int siglen)
 
 static void usb_disconnected(void* unused, atransport* t)
 {
-    D("USB disconnect\n");
-    remove_transport_disconnect(usb_transport, &usb_disconnect);
+    D("USB disconnect");
     usb_transport = NULL;
     needs_retry = false;
 }
@@ -177,7 +176,7 @@ static void adb_auth_event(int fd, unsigned events, void *data)
     if (events & FDE_READ) {
         ret = unix_read(fd, response, sizeof(response));
         if (ret <= 0) {
-            D("Framework disconnect\n");
+            D("Framework disconnect");
             if (usb_transport)
                 fdevent_remove(&usb_transport->auth_fde);
             framework_fd = -1;
@@ -191,22 +190,22 @@ static void adb_auth_event(int fd, unsigned events, void *data)
 
 void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t)
 {
-    char msg[MAX_PAYLOAD];
+    char msg[MAX_PAYLOAD_V1];
     int ret;
 
     if (!usb_transport) {
         usb_transport = t;
-        add_transport_disconnect(t, &usb_disconnect);
+        t->AddDisconnect(&usb_disconnect);
     }
 
     if (framework_fd < 0) {
-        D("Client not connected\n");
+        D("Client not connected");
         needs_retry = true;
         return;
     }
 
     if (key[len - 1] != '\0') {
-        D("Key must be a null-terminated string\n");
+        D("Key must be a null-terminated string");
         return;
     }
 
@@ -215,11 +214,11 @@ void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t)
         D("Key too long. ret=%d", ret);
         return;
     }
-    D("Sending '%s'\n", msg);
+    D("Sending '%s'", msg);
 
     ret = unix_write(framework_fd, msg, ret);
     if (ret < 0) {
-        D("Failed to write PK, errno=%d\n", errno);
+        D("Failed to write PK, errno=%d", errno);
         return;
     }
 
@@ -237,7 +236,7 @@ static void adb_auth_listener(int fd, unsigned events, void *data)
 
     s = adb_socket_accept(fd, &addr, &alen);
     if (s < 0) {
-        D("Failed to accept: errno=%d\n", errno);
+        D("Failed to accept: errno=%d", errno);
         return;
     }
 
@@ -252,7 +251,7 @@ static void adb_auth_listener(int fd, unsigned events, void *data)
 void adbd_cloexec_auth_socket() {
     int fd = android_get_control_socket("adbd");
     if (fd == -1) {
-        D("Failed to get adbd socket\n");
+        D("Failed to get adbd socket");
         return;
     }
     fcntl(fd, F_SETFD, FD_CLOEXEC);
@@ -261,12 +260,12 @@ void adbd_cloexec_auth_socket() {
 void adbd_auth_init(void) {
     int fd = android_get_control_socket("adbd");
     if (fd == -1) {
-        D("Failed to get adbd socket\n");
+        D("Failed to get adbd socket");
         return;
     }
 
     if (listen(fd, 4) == -1) {
-        D("Failed to listen on '%d'\n", fd);
+        D("Failed to listen on '%d'", fd);
         return;
     }
 
index 61a3777..e11bff0 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_AUTH
+#define TRACE_TAG AUTH
 
 #include "sysdeps.h"
 #include "adb_auth.h"
@@ -157,29 +157,29 @@ static int write_public_keyfile(RSA *private_key, const char *private_key_path)
 {
     RSAPublicKey pkey;
     FILE *outfile = NULL;
-    char path[PATH_MAX], info[MAX_PAYLOAD];
+    char path[PATH_MAX], info[MAX_PAYLOAD_V1];
     uint8_t* encoded = nullptr;
     size_t encoded_length;
     int ret = 0;
 
     if (snprintf(path, sizeof(path), "%s.pub", private_key_path) >=
         (int)sizeof(path)) {
-        D("Path too long while writing public key\n");
+        D("Path too long while writing public key");
         return 0;
     }
 
     if (!RSA_to_RSAPublicKey(private_key, &pkey)) {
-        D("Failed to convert to publickey\n");
+        D("Failed to convert to publickey");
         return 0;
     }
 
     outfile = fopen(path, "w");
     if (!outfile) {
-        D("Failed to open '%s'\n", path);
+        D("Failed to open '%s'", path);
         return 0;
     }
 
-    D("Writing public key to '%s'\n", path);
+    D("Writing public key to '%s'", path);
 
 #if defined(OPENSSL_IS_BORINGSSL)
     if (!EVP_EncodedLength(&encoded_length, sizeof(pkey))) {
@@ -226,10 +226,10 @@ static int generate_key(const char *file)
     FILE *f = NULL;
     int ret = 0;
 
-    D("generate_key '%s'\n", file);
+    D("generate_key '%s'", file);
 
     if (!pkey || !exponent || !rsa) {
-        D("Failed to allocate key\n");
+        D("Failed to allocate key");
         goto out;
     }
 
@@ -241,7 +241,7 @@ static int generate_key(const char *file)
 
     f = fopen(file, "w");
     if (!f) {
-        D("Failed to open '%s'\n", file);
+        D("Failed to open '%s'", file);
         umask(old_mask);
         goto out;
     }
@@ -249,12 +249,12 @@ static int generate_key(const char *file)
     umask(old_mask);
 
     if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
-        D("Failed to write key\n");
+        D("Failed to write key");
         goto out;
     }
 
     if (!write_public_keyfile(rsa, file)) {
-        D("Failed to write public key\n");
+        D("Failed to write public key");
         goto out;
     }
 
@@ -271,11 +271,11 @@ out:
 
 static int read_key(const char *file, struct listnode *list)
 {
-    D("read_key '%s'\n", file);
+    D("read_key '%s'", file);
 
     FILE* fp = fopen(file, "r");
     if (!fp) {
-        D("Failed to open '%s': %s\n", file, strerror(errno));
+        D("Failed to open '%s': %s", file, strerror(errno));
         return 0;
     }
 
@@ -283,7 +283,7 @@ static int read_key(const char *file, struct listnode *list)
     key->rsa = RSA_new();
 
     if (!PEM_read_RSAPrivateKey(fp, &key->rsa, NULL, NULL)) {
-        D("Failed to read key\n");
+        D("Failed to read key");
         fclose(fp);
         RSA_free(key->rsa);
         delete key;
@@ -301,11 +301,20 @@ static int get_user_keyfilepath(char *filename, size_t len)
     char android_dir[PATH_MAX];
     struct stat buf;
 #ifdef _WIN32
-    char path[PATH_MAX];
+    std::string home_str;
     home = getenv("ANDROID_SDK_HOME");
     if (!home) {
-        SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, path);
-        home = path;
+        WCHAR path[MAX_PATH];
+        const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);
+        if (FAILED(hr)) {
+            D("SHGetFolderPathW failed: %s",
+              SystemErrorCodeToString(hr).c_str());
+            return -1;
+        }
+        if (!android::base::WideToUTF8(path, &home_str)) {
+            return -1;
+        }
+        home = home_str.c_str();
     }
     format = "%s\\%s";
 #else
@@ -315,7 +324,7 @@ static int get_user_keyfilepath(char *filename, size_t len)
     format = "%s/%s";
 #endif
 
-    D("home '%s'\n", home);
+    D("home '%s'", home);
 
     if (snprintf(android_dir, sizeof(android_dir), format, home,
                         ANDROID_PATH) >= (int)sizeof(android_dir))
@@ -343,11 +352,11 @@ static int get_user_key(struct listnode *list)
         return 0;
     }
 
-    D("user key '%s'\n", path);
+    D("user key '%s'", path);
 
     if (stat(path, &buf) == -1) {
         if (!generate_key(path)) {
-            D("Failed to generate new key\n");
+            D("Failed to generate new key");
             return 0;
         }
     }
@@ -361,9 +370,9 @@ static void get_vendor_keys(struct listnode* key_list) {
         return;
     }
 
-    for (auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
+    for (const auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
         if (!read_key(path.c_str(), key_list)) {
-            D("Failed to read '%s'\n", path.c_str());
+            D("Failed to read '%s'", path.c_str());
         }
     }
 }
@@ -375,7 +384,7 @@ int adb_auth_sign(void *node, const unsigned char* token, size_t token_size,
     struct adb_private_key *key = node_to_item(node, struct adb_private_key, node);
 
     if (token_size != TOKEN_SIZE) {
-        D("Unexpected token size %zd\n", token_size);
+        D("Unexpected token size %zd", token_size);
         return 0;
     }
 
@@ -383,7 +392,7 @@ int adb_auth_sign(void *node, const unsigned char* token, size_t token_size,
         return 0;
     }
 
-    D("adb_auth_sign len=%d\n", len);
+    D("adb_auth_sign len=%d", len);
     return (int)len;
 }
 
@@ -420,15 +429,18 @@ int adb_auth_get_userkey(unsigned char *data, size_t len)
     strcat(path, ".pub");
 
     // TODO(danalbert): ReadFileToString
+    // Note that on Windows, load_file() does not do CR/LF translation, but
+    // ReadFileToString() uses the C Runtime which uses CR/LF translation by
+    // default (by is overridable with _setmode()).
     unsigned size;
     char* file_data = reinterpret_cast<char*>(load_file(path, &size));
     if (file_data == nullptr) {
-        D("Can't load '%s'\n", path);
+        D("Can't load '%s'", path);
         return 0;
     }
 
     if (len < (size_t)(size + 1)) {
-        D("%s: Content too large ret=%d\n", path, size);
+        D("%s: Content too large ret=%d", path, size);
         free(file_data);
         return 0;
     }
@@ -442,7 +454,6 @@ int adb_auth_get_userkey(unsigned char *data, size_t len)
 }
 
 int adb_auth_keygen(const char* filename) {
-    adb_trace_mask |= (1 << TRACE_AUTH);
     return (generate_key(filename) == 0);
 }
 
@@ -450,13 +461,13 @@ void adb_auth_init(void)
 {
     int ret;
 
-    D("adb_auth_init\n");
+    D("adb_auth_init");
 
     list_init(&key_list);
 
     ret = get_user_key(&key_list);
     if (!ret) {
-        D("Failed to get user key\n");
+        D("Failed to get user key");
         return;
     }
 
index 7bb8e4a..ddeb5f1 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
 
 #include "sysdeps.h"
 #include "adb_client.h"
 
 #include <base/stringprintf.h>
 #include <base/strings.h>
+#include <cutils/sockets.h>
 
 #include "adb_io.h"
+#include "adb_utils.h"
 
-static transport_type __adb_transport = kTransportAny;
+static TransportType __adb_transport = kTransportAny;
 static const char* __adb_serial = NULL;
 
 static int __adb_server_port = DEFAULT_ADB_PORT;
 static const char* __adb_server_name = NULL;
 
-static std::string perror_str(const char* msg) {
-    return android::base::StringPrintf("%s: %s", msg, strerror(errno));
-}
-
-static bool ReadProtocolString(int fd, std::string* s, std::string* error) {
-    char buf[5];
-    if (!ReadFdExactly(fd, buf, 4)) {
-        *error = perror_str("protocol fault (couldn't read status length)");
-        return false;
-    }
-    buf[4] = 0;
-
-    unsigned long len = strtoul(buf, 0, 16);
-    s->resize(len, '\0');
-    if (!ReadFdExactly(fd, &(*s)[0], len)) {
-        *error = perror_str("protocol fault (couldn't read status message)");
-        return false;
-    }
-
-    return true;
-}
-
-void adb_set_transport(transport_type type, const char* serial)
+void adb_set_transport(TransportType type, const char* serial)
 {
     __adb_transport = type;
     __adb_serial = serial;
@@ -80,36 +60,6 @@ void adb_set_tcp_name(const char* hostname)
     __adb_server_name = hostname;
 }
 
-int adb_get_emulator_console_port() {
-    if (__adb_serial) {
-        // The user specified a serial number; is it an emulator?
-        int port;
-        return (sscanf(__adb_serial, "emulator-%d", &port) == 1) ? port : -1;
-    }
-
-    // No specific device was given, so get the list of connected
-    // devices and search for emulators. If there's one, we'll
-    // take it. If there are more than one, that's an error.
-    std::string devices;
-    std::string error;
-    if (!adb_query("host:devices", &devices, &error)) {
-        printf("no emulator connected: %s\n", error.c_str());
-        return -1;
-    }
-
-    int port;
-    size_t emulator_count = 0;
-    for (auto& device : android::base::Split(devices, "\n")) {
-        if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
-            if (++emulator_count > 1) {
-                return -2;
-            }
-        }
-    }
-    if (emulator_count == 0) return -1;
-    return port;
-}
-
 static int switch_socket_transport(int fd, std::string* error) {
     std::string service;
     if (__adb_serial) {
@@ -140,14 +90,14 @@ static int switch_socket_transport(int fd, std::string* error) {
         adb_close(fd);
         return -1;
     }
-    D("Switch transport in progress\n");
+    D("Switch transport in progress");
 
     if (!adb_status(fd, error)) {
         adb_close(fd);
-        D("Switch transport failed: %s\n", error->c_str());
+        D("Switch transport failed: %s", error->c_str());
         return -1;
     }
-    D("Switch transport success\n");
+    D("Switch transport success");
     return 0;
 }
 
@@ -173,29 +123,37 @@ bool adb_status(int fd, std::string* error) {
 }
 
 int _adb_connect(const std::string& service, std::string* error) {
-    D("_adb_connect: %s\n", service.c_str());
+    D("_adb_connect: %s", service.c_str());
     if (service.empty() || service.size() > 1024) {
-        *error = android::base::StringPrintf("bad service name length (%d)",
-                                             static_cast<int>(service.size()));
+        *error = android::base::StringPrintf("bad service name length (%zd)",
+                                             service.size());
         return -1;
     }
 
     int fd;
+    std::string reason;
     if (__adb_server_name) {
-        fd = socket_network_client(__adb_server_name, __adb_server_port, SOCK_STREAM);
+        fd = network_connect(__adb_server_name, __adb_server_port, SOCK_STREAM, 0, &reason);
+        if (fd == -1) {
+            *error = android::base::StringPrintf("can't connect to %s:%d: %s",
+                                                 __adb_server_name, __adb_server_port,
+                                                 reason.c_str());
+            return -2;
+        }
     } else {
-        fd = socket_loopback_client(__adb_server_port, SOCK_STREAM);
-    }
-    if (fd < 0) {
-        *error = perror_str("cannot connect to daemon");
-        return -2;
+        fd = network_loopback_client(__adb_server_port, SOCK_STREAM, &reason);
+        if (fd == -1) {
+            *error = android::base::StringPrintf("cannot connect to daemon: %s",
+                                                 reason.c_str());
+            return -2;
+        }
     }
 
     if (memcmp(&service[0],"host",4) != 0 && switch_socket_transport(fd, error)) {
         return -1;
     }
 
-    if(!SendProtocolString(fd, service)) {
+    if (!SendProtocolString(fd, service)) {
         *error = perror_str("write failure during connection");
         adb_close(fd);
         return -1;
@@ -206,7 +164,7 @@ int _adb_connect(const std::string& service, std::string* error) {
         return -1;
     }
 
-    D("_adb_connect: return fd %d\n", fd);
+    D("_adb_connect: return fd %d", fd);
     return fd;
 }
 
@@ -214,9 +172,10 @@ int adb_connect(const std::string& service, std::string* error) {
     // first query the adb server's version
     int fd = _adb_connect("host:version", error);
 
-    D("adb_connect: service %s\n", service.c_str());
+    D("adb_connect: service %s", service.c_str());
     if (fd == -2 && __adb_server_name) {
         fprintf(stderr,"** Cannot start server on remote host\n");
+        // error is the original network connection error
         return fd;
     } else if (fd == -2) {
         fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
@@ -224,6 +183,10 @@ int adb_connect(const std::string& service, std::string* error) {
     start_server:
         if (launch_server(__adb_server_port)) {
             fprintf(stderr,"* failed to start daemon *\n");
+            // launch_server() has already printed detailed error info, so just
+            // return a generic error string about the overall adb_connect()
+            // that the caller requested.
+            *error = "cannot connect to daemon";
             return -1;
         } else {
             fprintf(stdout,"* daemon started successfully *\n");
@@ -232,33 +195,46 @@ int adb_connect(const std::string& service, std::string* error) {
         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
+        // If a server is already running, check its version matches.
         int version = ADB_SERVER_VERSION - 1;
 
-        // if we have a file descriptor, then parse version result
+        // If we have a file descriptor, then parse version result.
         if (fd >= 0) {
             std::string version_string;
             if (!ReadProtocolString(fd, &version_string, error)) {
-                goto error;
+                adb_close(fd);
+                return -1;
             }
 
+            ReadOrderlyShutdown(fd);
             adb_close(fd);
 
             if (sscanf(&version_string[0], "%04x", &version) != 1) {
-                goto error;
+                *error = android::base::StringPrintf("cannot parse version string: %s",
+                                                     version_string.c_str());
+                return -1;
             }
         } else {
-            // if fd is -1, then check for "unknown host service",
-            // which would indicate a version of adb that does not support the version command
-            if (*error == "unknown host service") {
+            // If fd is -1 check for "unknown host service" which would
+            // indicate a version of adb that does not support the
+            // version command, in which case we should fall-through to kill it.
+            if (*error != "unknown host service") {
                 return fd;
             }
         }
 
         if (version != ADB_SERVER_VERSION) {
-            printf("adb server is out of date.  killing...\n");
+            printf("adb server version (%d) doesn't match this client (%d); killing...\n",
+                   version, ADB_SERVER_VERSION);
             fd = _adb_connect("host:kill", error);
-            adb_close(fd);
+            if (fd >= 0) {
+                ReadOrderlyShutdown(fd);
+                adb_close(fd);
+            } else {
+                // If we couldn't connect to the server or had some other error,
+                // report it, but still try to start the server.
+                fprintf(stderr, "error: %s\n", error->c_str());
+            }
 
             /* XXX can we better detect its death? */
             adb_sleep_ms(2000);
@@ -277,36 +253,36 @@ int adb_connect(const std::string& service, std::string* error) {
     } else if(fd == -2) {
         fprintf(stderr,"** daemon still not running\n");
     }
-    D("adb_connect: return fd %d\n", fd);
+    D("adb_connect: return fd %d", fd);
 
     return fd;
-error:
-    adb_close(fd);
-    return -1;
 }
 
 
-int adb_command(const std::string& service, std::string* error) {
-    int fd = adb_connect(service, error);
+bool adb_command(const std::string& service) {
+    std::string error;
+    int fd = adb_connect(service, &error);
     if (fd < 0) {
-        fprintf(stderr, "error: %s\n", error->c_str());
-        return -1;
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return false;
     }
 
-    if (!adb_status(fd, error)) {
+    if (!adb_status(fd, &error)) {
+        fprintf(stderr, "error: %s\n", error.c_str());
         adb_close(fd);
-        return -1;
+        return false;
     }
 
-    return 0;
+    ReadOrderlyShutdown(fd);
+    adb_close(fd);
+    return true;
 }
 
 bool adb_query(const std::string& service, std::string* result, std::string* error) {
-    D("adb_query: %s\n", service.c_str());
+    D("adb_query: %s", service.c_str());
     int fd = adb_connect(service, error);
     if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error->c_str());
-        return 0;
+        return false;
     }
 
     result->clear();
@@ -314,5 +290,8 @@ bool adb_query(const std::string& service, std::string* result, std::string* err
         adb_close(fd);
         return false;
     }
+
+    ReadOrderlyShutdown(fd);
+    adb_close(fd);
     return true;
 }
index 96416f5..5de0638 100644 (file)
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 #ifndef _ADB_CLIENT_H_
 #define _ADB_CLIENT_H_
 
 
 #include <string>
 
-/* connect to adb, connect to the named service, and return
-** a valid fd for interacting with that service upon success
-** or a negative number on failure
-*/
+// Connect to adb, connect to the named service, and return a valid fd for
+// interacting with that service upon success or a negative number on failure.
 int adb_connect(const std::string& service, std::string* error);
 int _adb_connect(const std::string& service, std::string* error);
 
-/* connect to adb, connect to the named service, return 0 if
-** the connection succeeded AND the service returned OKAY
-*/
-int adb_command(const std::string& service, std::string* error);
+// Connect to adb, connect to the named service, returns true if the connection
+// succeeded AND the service returned OKAY. Outputs any returned error otherwise.
+bool adb_command(const std::string& service);
 
 // Connects to the named adb service and fills 'result' with the response.
 // Returns true on success; returns false and fills 'error' on failure.
 bool adb_query(const std::string& service, std::string* result, std::string* error);
 
-/* Set the preferred transport to connect to.
-*/
-void adb_set_transport(transport_type type, const char* serial);
+// Set the preferred transport to connect to.
+void adb_set_transport(TransportType type, const char* serial);
 
-/* Set TCP specifics of the transport to use
-*/
+// Set TCP specifics of the transport to use.
 void adb_set_tcp_specifics(int server_port);
 
-/* Set TCP Hostname of the transport to use
-*/
+// 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 <serial>
- * with a <serial> that does not designate an emulator)
- */
-int  adb_send_emulator_command(int  argc, const char**  argv);
+// Send commands to the current emulator instance. Will fail if there is not
+// exactly one emulator connected (or if you use -s <serial> with a <serial>
+// that does not designate an emulator).
+int adb_send_emulator_command(int argc, const char** argv, const char* serial);
 
-// Reads a standard adb status response (OKAY|FAIL) and
-// returns true in the event of OKAY, false in the event of FAIL
-// or protocol error.
+// Reads a standard adb status response (OKAY|FAIL) and returns true in the
+// event of OKAY, false in the event of FAIL or protocol error.
 bool adb_status(int fd, std::string* error);
 
 #endif
index 5ae6ec3..a37fbc0 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_RWX
+#define TRACE_TAG RWX
 
 #include "adb_io.h"
 
@@ -32,7 +32,27 @@ bool SendProtocolString(int fd, const std::string& s) {
         length = 0xffff;
     }
 
-    return WriteFdFmt(fd, "%04x", length) && WriteFdExactly(fd, s);
+    // The cost of sending two strings outweighs the cost of formatting.
+    // "adb sync" performance is affected by this.
+    return WriteFdFmt(fd, "%04x%.*s", length, length, s.c_str());
+}
+
+bool ReadProtocolString(int fd, std::string* s, std::string* error) {
+    char buf[5];
+    if (!ReadFdExactly(fd, buf, 4)) {
+        *error = perror_str("protocol fault (couldn't read status length)");
+        return false;
+    }
+    buf[4] = 0;
+
+    unsigned long len = strtoul(buf, 0, 16);
+    s->resize(len, '\0');
+    if (!ReadFdExactly(fd, &(*s)[0], len)) {
+        *error = perror_str("protocol fault (couldn't read status message)");
+        return false;
+    }
+
+    return true;
 }
 
 bool SendOkay(int fd) {
@@ -48,26 +68,24 @@ bool ReadFdExactly(int fd, void* buf, size_t len) {
 
     size_t len0 = len;
 
-    D("readx: fd=%d wanted=%zu\n", fd, len);
+    D("readx: fd=%d wanted=%zu", fd, len);
     while (len > 0) {
         int r = adb_read(fd, p, len);
         if (r > 0) {
             len -= r;
             p += r;
         } else if (r == -1) {
-            D("readx: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+            D("readx: fd=%d error %d: %s", fd, errno, strerror(errno));
             return false;
         } else {
-            D("readx: fd=%d disconnected\n", fd);
+            D("readx: fd=%d disconnected", fd);
             errno = 0;
             return false;
         }
     }
 
-    D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len);
-    if (ADB_TRACING) {
-        dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
-    }
+    VLOG(RWX) << "readx: fd=" << fd << " wanted=" << len0 << " got=" << (len0 - len)
+              << " " << dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
 
     return true;
 }
@@ -76,20 +94,18 @@ bool WriteFdExactly(int fd, const void* buf, size_t len) {
     const char* p = reinterpret_cast<const char*>(buf);
     int r;
 
-    D("writex: fd=%d len=%d: ", fd, (int)len);
-    if (ADB_TRACING) {
-        dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
-    }
+    VLOG(RWX) << "writex: fd=" << fd << " len=" << len
+              << " " << dump_hex(reinterpret_cast<const unsigned char*>(buf), len);
 
     while (len > 0) {
         r = adb_write(fd, p, len);
         if (r == -1) {
-            D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+            D("writex: fd=%d error %d: %s", fd, errno, strerror(errno));
             if (errno == EAGAIN) {
                 adb_sleep_ms(1); // just yield some cpu time
                 continue;
             } else if (errno == EPIPE) {
-                D("writex: fd=%d disconnected\n", fd);
+                D("writex: fd=%d disconnected", fd);
                 errno = 0;
                 return false;
             } else {
@@ -121,3 +137,43 @@ bool WriteFdFmt(int fd, const char* fmt, ...) {
 
     return WriteFdExactly(fd, str);
 }
+
+bool ReadOrderlyShutdown(int fd) {
+    char buf[16];
+
+    // Only call this function if you're sure that the peer does
+    // orderly/graceful shutdown of the socket, closing the socket so that
+    // adb_read() will return 0. If the peer keeps the socket open, adb_read()
+    // will never return.
+    int result = adb_read(fd, buf, sizeof(buf));
+    if (result == -1) {
+        // If errno is EAGAIN, that means this function was called on a
+        // nonblocking socket and it would have blocked (which would be bad
+        // because we'd probably block the main thread where nonblocking IO is
+        // done). Don't do that. If you have a nonblocking socket, use the
+        // fdevent APIs to get called on FDE_READ, and then call this function
+        // if you really need to, but it shouldn't be needed for server sockets.
+        CHECK_NE(errno, EAGAIN);
+
+        // Note that on Windows, orderly shutdown sometimes causes
+        // recv() == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET. That
+        // can be ignored.
+        return false;
+    } else if (result == 0) {
+        // Peer has performed an orderly/graceful shutdown.
+        return true;
+    } else {
+        // Unexpectedly received data. This is essentially a protocol error
+        // because you should not call this function unless you expect no more
+        // data. We don't repeatedly call adb_read() until we get zero because
+        // we don't know how long that would take, but we do know that the
+        // caller wants to close the socket soon.
+        VLOG(RWX) << "ReadOrderlyShutdown(" << fd << ") unexpectedly read "
+                  << dump_hex(buf, result);
+        // Shutdown the socket to prevent the caller from reading or writing to
+        // it which doesn't make sense if we just read and discarded some data.
+        adb_shutdown(fd);
+        errno = EINVAL;
+        return false;
+    }
+}
index 8d50a6d..aa550af 100644 (file)
@@ -30,23 +30,40 @@ bool SendFail(int fd, const std::string& reason);
 // Writes a protocol-format string; a four hex digit length followed by the string data.
 bool SendProtocolString(int fd, const std::string& s);
 
-/*
- * Reads exactly len bytes from fd into buf.
- *
- * Returns false if there is an error or if EOF was reached before len bytes
- * were read. If EOF was found, errno will be set to 0.
- *
- * If this function fails, the contents of buf are undefined.
- */
-bool ReadFdExactly(int fd, void *buf, size_t len);
+// Reads a protocol-format string; a four hex digit length followed by the string data.
+bool ReadProtocolString(int fd, std::string* s, std::string* error);
 
-/*
- * Writes exactly len bytes from buf to fd.
- *
- * Returns false if there is an error or if the fd was closed before the write
- * completed. If the other end of the fd (such as in a socket, pipe, or fifo),
- * is closed, errno will be set to 0.
- */
+// Reads exactly len bytes from fd into buf.
+//
+// Returns false if there is an error or if EOF was reached before len bytes
+// were read. If EOF was found, errno will be set to 0.
+//
+// If this function fails, the contents of buf are undefined.
+bool ReadFdExactly(int fd, void* buf, size_t len);
+
+// Given a client socket, wait for orderly/graceful shutdown. Call this:
+//
+// * Before closing a client socket.
+// * Only when no more data is expected to come in.
+// * Only when the server is not waiting for data from the client (because then
+//   the client and server will deadlock waiting for each other).
+// * Only when the server is expected to close its socket right now.
+// * Don't call shutdown(SHUT_WR) before calling this because that will shutdown
+//   the client socket early, defeating the purpose of calling this.
+//
+// Waiting for orderly/graceful shutdown of the server socket will cause the
+// server socket to close before the client socket. That prevents the client
+// socket from staying in TIME_WAIT which eventually causes subsequent
+// connect()s from the client to fail with WSAEADDRINUSE on Windows.
+// Returns true if it is sure that orderly/graceful shutdown has occurred with
+// no additional data read from the server.
+bool ReadOrderlyShutdown(int fd);
+
+// Writes exactly len bytes from buf to fd.
+//
+// Returns false if there is an error or if the fd was closed before the write
+// completed. If the other end of the fd (such as in a socket, pipe, or fifo),
+// is closed, errno will be set to 0.
 bool WriteFdExactly(int fd, const void* buf, size_t len);
 
 // Same as above, but for strings.
index 8fd5cbf..6928a90 100644 (file)
 #include <string>
 
 #include "base/file.h"
+#include "base/test_utils.h"
 
-class TemporaryFile {
- public:
-  TemporaryFile() {
-    init("/data/local/tmp");
-    if (fd == -1) {
-      init("/tmp");
-    }
-  }
-
-  ~TemporaryFile() {
-    close(fd);
-    unlink(filename);
-  }
-
-  int fd;
-  char filename[1024];
-
- private:
-  void init(const char* tmp_dir) {
-    snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
-    fd = mkstemp(filename);
-  }
-};
+// All of these tests fail on Windows because they use the C Runtime open(),
+// but the adb_io APIs expect file descriptors from adb_open(). This could
+// theoretically be fixed by making adb_read()/adb_write() fallback to using
+// read()/write() if an unrecognized fd is used, and by making adb_open() return
+// fds far from the range that open() returns. But all of that might defeat the
+// purpose of the tests.
 
 TEST(io, ReadFdExactly_whole) {
   const char expected[] = "Foobar";
@@ -59,7 +43,7 @@ TEST(io, ReadFdExactly_whole) {
   ASSERT_NE(-1, tf.fd);
 
   ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   // Test reading the whole file.
   char buf[sizeof(expected)] = {};
@@ -73,7 +57,7 @@ TEST(io, ReadFdExactly_eof) {
   ASSERT_NE(-1, tf.fd);
 
   ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   // Test that not having enough data will fail.
   char buf[sizeof(expected) + 1] = {};
@@ -87,7 +71,7 @@ TEST(io, ReadFdExactly_partial) {
   ASSERT_NE(-1, tf.fd);
 
   ASSERT_TRUE(android::base::WriteStringToFd(input, tf.fd)) << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   // Test reading a partial file.
   char buf[sizeof(input) - 1] = {};
@@ -106,7 +90,7 @@ TEST(io, WriteFdExactly_whole) {
   // Test writing the whole string to the file.
   ASSERT_TRUE(WriteFdExactly(tf.fd, expected, sizeof(expected)))
     << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   std::string s;
   ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
@@ -120,7 +104,7 @@ TEST(io, WriteFdExactly_partial) {
 
   // Test writing a partial string to the file.
   ASSERT_TRUE(WriteFdExactly(tf.fd, buf, sizeof(buf) - 2)) << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   std::string expected(buf);
   expected.pop_back();
@@ -146,7 +130,7 @@ TEST(io, WriteFdExactly_string) {
 
   // Test writing a partial string to the file.
   ASSERT_TRUE(WriteFdExactly(tf.fd, str)) << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   std::string s;
   ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
@@ -159,7 +143,7 @@ TEST(io, WriteFdFmt) {
 
     // Test writing a partial string to the file.
     ASSERT_TRUE(WriteFdFmt(tf.fd, "Foo%s%d", "bar", 123)) << strerror(errno);
-    ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
     std::string s;
     ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
index 3fc4719..d5b1fd5 100644 (file)
 #include <stdlib.h>
 
 #include <base/stringprintf.h>
+#include <cutils/sockets.h>
 
 #include "sysdeps.h"
 #include "transport.h"
 
 int gListenAll = 0; /* Not static because it is used in commandline.c. */
 
-alistener listener_list = {
+static alistener listener_list = {
     .next = &listener_list,
     .prev = &listener_list,
 };
 
-void ss_listener_event_func(int _fd, unsigned ev, void *_l)
-{
+static void ss_listener_event_func(int _fd, unsigned ev, void *_l) {
     asocket *s;
 
     if(ev & FDE_READ) {
@@ -56,7 +56,7 @@ void ss_listener_event_func(int _fd, unsigned ev, void *_l)
     }
 }
 
-void listener_event_func(int _fd, unsigned ev, void* _l)
+static void listener_event_func(int _fd, unsigned ev, void* _l)
 {
     alistener* listener = reinterpret_cast<alistener*>(_l);
     asocket *s;
@@ -101,47 +101,41 @@ static void  free_listener(alistener*  l)
         free((char*)l->connect_to);
 
     if (l->transport) {
-        remove_transport_disconnect(l->transport, &l->disconnect);
+        l->transport->RemoveDisconnect(&l->disconnect);
     }
     free(l);
 }
 
-void listener_disconnect(void* listener, atransport*  t)
-{
-    free_listener(reinterpret_cast<alistener*>(listener));
+static void listener_disconnect(void* arg, atransport*) {
+    alistener* listener = reinterpret_cast<alistener*>(arg);
+    listener->transport = nullptr;
+    free_listener(listener);
 }
 
-int local_name_to_fd(const char *name)
-{
-    int port;
-
-    if(!strncmp("tcp:", name, 4)){
-        int  ret;
-        port = atoi(name + 4);
-
+static int local_name_to_fd(const char* name, std::string* error) {
+    if (!strncmp("tcp:", name, 4)) {
+        int port = atoi(name + 4);
         if (gListenAll > 0) {
-            ret = socket_inaddr_any_server(port, SOCK_STREAM);
+            return network_inaddr_any_server(port, SOCK_STREAM, error);
         } else {
-            ret = socket_loopback_server(port, SOCK_STREAM);
+            return network_loopback_server(port, SOCK_STREAM, error);
         }
-
-        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);
+#if !defined(_WIN32)  // No Unix-domain sockets on Windows.
+    // It's nonsensical to support the "reserved" space on the adb host side
+    if (!strncmp(name, "local:", 6)) {
+        return network_local_server(name + 6,
+                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM, error);
+    } else if (!strncmp(name, "localabstract:", 14)) {
+        return network_local_server(name + 14,
+                ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM, error);
+    } else if (!strncmp(name, "localfilesystem:", 16)) {
+        return network_local_server(name + 16,
+                ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM, error);
     }
 
 #endif
-    printf("unknown local portname '%s'\n", name);
+    *error = android::base::StringPrintf("unknown local portname '%s'", name);
     return -1;
 }
 
@@ -154,19 +148,20 @@ std::string format_listeners() {
             continue;
         }
         //  <device-serial> " " <local-name> " " <remote-name> "\n"
+        // Entries from "adb reverse" have no serial.
         android::base::StringAppendF(&result, "%s %s %s\n",
-                                     l->transport->serial, l->local_name, l->connect_to);
+                                     l->transport->serial ? l->transport->serial : "(reverse)",
+                                     l->local_name, l->connect_to);
     }
     return result;
 }
 
-install_status_t remove_listener(const char *local_name, atransport* transport)
-{
+InstallStatus remove_listener(const char *local_name, atransport* transport) {
     alistener *l;
 
     for (l = listener_list.next; l != &listener_list; l = l->next) {
         if (!strcmp(local_name, l->local_name)) {
-            listener_disconnect(l, l->transport);
+            free_listener(l);
             return INSTALL_STATUS_OK;
         }
     }
@@ -181,14 +176,15 @@ void remove_all_listeners(void)
         // Never remove smart sockets.
         if (l->connect_to[0] == '*')
             continue;
-        listener_disconnect(l, l->transport);
+        free_listener(l);
     }
 }
 
-install_status_t install_listener(const std::string& local_name,
+InstallStatus install_listener(const std::string& local_name,
                                   const char *connect_to,
                                   atransport* transport,
-                                  int no_rebind)
+                                  int no_rebind,
+                                  std::string* error)
 {
     for (alistener* l = listener_list.next; l != &listener_list; l = l->next) {
         if (local_name == l->local_name) {
@@ -196,25 +192,28 @@ install_status_t install_listener(const std::string& local_name,
 
             /* can't repurpose a smartsocket */
             if(l->connect_to[0] == '*') {
+                *error = "cannot repurpose smartsocket";
                 return INSTALL_STATUS_INTERNAL_ERROR;
             }
 
             /* can't repurpose a listener if 'no_rebind' is true */
             if (no_rebind) {
+                *error = "cannot rebind";
                 return INSTALL_STATUS_CANNOT_REBIND;
             }
 
             cto = strdup(connect_to);
             if(cto == 0) {
+                *error = "cannot duplicate string";
                 return INSTALL_STATUS_INTERNAL_ERROR;
             }
 
             free((void*) l->connect_to);
             l->connect_to = cto;
             if (l->transport != transport) {
-                remove_transport_disconnect(l->transport, &l->disconnect);
+                l->transport->RemoveDisconnect(&l->disconnect);
                 l->transport = transport;
-                add_transport_disconnect(l->transport, &l->disconnect);
+                l->transport->AddDisconnect(&l->disconnect);
             }
             return INSTALL_STATUS_OK;
         }
@@ -236,9 +235,8 @@ install_status_t install_listener(const std::string& local_name,
         goto nomem;
     }
 
-    listener->fd = local_name_to_fd(listener->local_name);
+    listener->fd = local_name_to_fd(listener->local_name, error);
     if (listener->fd < 0) {
-        printf("cannot bind '%s': %s\n", listener->local_name, strerror(errno));
         free(listener->local_name);
         free(listener->connect_to);
         free(listener);
@@ -264,7 +262,7 @@ install_status_t install_listener(const std::string& local_name,
     if (transport) {
         listener->disconnect.opaque = listener;
         listener->disconnect.func   = listener_disconnect;
-        add_transport_disconnect(transport, &listener->disconnect);
+        transport->AddDisconnect(&listener->disconnect);
     }
     return INSTALL_STATUS_OK;
 
index 67168ae..fa98eed 100644 (file)
@@ -22,7 +22,7 @@
 #include <string>
 
 // error/status codes for install_listener.
-enum install_status_t {
+enum InstallStatus {
   INSTALL_STATUS_OK = 0,
   INSTALL_STATUS_INTERNAL_ERROR = -1,
   INSTALL_STATUS_CANNOT_BIND = -2,
@@ -30,20 +30,15 @@ enum install_status_t {
   INSTALL_STATUS_LISTENER_NOT_FOUND = -4,
 };
 
-extern alistener listener_list;
-
-void listener_disconnect(void*  _l, atransport*  t);
-void listener_event_func(int _fd, unsigned ev, void *_l);
-void ss_listener_event_func(int _fd, unsigned ev, void *_l);
-
-install_status_t install_listener(const std::string& local_name,
-                                  const char* connect_to,
-                                  atransport* transport,
-                                  int no_rebind);
+InstallStatus install_listener(const std::string& local_name,
+                               const char* connect_to,
+                               atransport* transport,
+                               int no_rebind,
+                               std::string* error);
 
 std::string format_listeners();
 
-install_status_t remove_listener(const char* local_name, atransport* transport);
+InstallStatus remove_listener(const char* local_name, atransport* transport);
 void remove_all_listeners(void);
 
 #endif /* __ADB_LISTENERS_H */
diff --git a/adb/adb_main.cpp b/adb/adb_main.cpp
deleted file mode 100644 (file)
index 45a2158..0000000
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define TRACE_TAG TRACE_ADB
-
-#include "sysdeps.h"
-
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "adb.h"
-#include "adb_auth.h"
-#include "adb_listeners.h"
-#include "transport.h"
-
-#include <base/stringprintf.h>
-
-#if !ADB_HOST
-#include <getopt.h>
-#include <sys/prctl.h>
-
-#include "cutils/properties.h"
-#include "private/android_filesystem_config.h"
-#include "selinux/selinux.h"
-
-#include "qemu_tracing.h"
-#endif
-
-static void adb_cleanup(void)
-{
-    usb_cleanup();
-}
-
-#if defined(_WIN32)
-static BOOL WINAPI ctrlc_handler(DWORD type)
-{
-    exit(STATUS_CONTROL_C_EXIT);
-    return TRUE;
-}
-#endif
-
-#if ADB_HOST
-#ifdef WORKAROUND_BUG6558362
-#include <sched.h>
-#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
-#else /* ADB_HOST */
-static const char *root_seclabel = NULL;
-
-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 bool should_drop_privileges() {
-#if defined(ALLOW_ADBD_ROOT)
-    char value[PROPERTY_VALUE_MAX];
-
-    // The emulator is never secure, so don't drop privileges there.
-    // TODO: this seems like a bug --- shouldn't the emulator behave like a device?
-    property_get("ro.kernel.qemu", value, "");
-    if (strcmp(value, "1") == 0) {
-        return false;
-    }
-
-    // The properties that affect `adb root` and `adb unroot` are ro.secure and
-    // ro.debuggable. In this context the names don't make the expected behavior
-    // particularly obvious.
-    //
-    // ro.debuggable:
-    //   Allowed to become root, but not necessarily the default. Set to 1 on
-    //   eng and userdebug builds.
-    //
-    // ro.secure:
-    //   Drop privileges by default. Set to 1 on userdebug and user builds.
-    property_get("ro.secure", value, "1");
-    bool ro_secure = (strcmp(value, "1") == 0);
-
-    property_get("ro.debuggable", value, "");
-    bool ro_debuggable = (strcmp(value, "1") == 0);
-
-    // Drop privileges if ro.secure is set...
-    bool drop = ro_secure;
-
-    property_get("service.adb.root", value, "");
-    bool adb_root = (strcmp(value, "1") == 0);
-    bool adb_unroot = (strcmp(value, "0") == 0);
-
-    // ...except "adb root" lets you keep privileges in a debuggable build.
-    if (ro_debuggable && adb_root) {
-        drop = false;
-    }
-
-    // ...and "adb unroot" lets you explicitly drop privileges.
-    if (adb_unroot) {
-        drop = true;
-    }
-
-    return drop;
-#else
-    return true; // "adb root" not allowed, always drop privileges.
-#endif /* ALLOW_ADBD_ROOT */
-}
-#endif /* ADB_HOST */
-
-void start_logging(void)
-{
-#if defined(_WIN32)
-    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
-}
-
-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);
-#if defined(_WIN32)
-    SetConsoleCtrlHandler( ctrlc_handler, TRUE );
-#else
-    // 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_init();
-    local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
-    adb_auth_init();
-
-    std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
-    if (install_listener(local_name, "*smartsocket*", NULL, 0)) {
-        exit(1);
-    }
-#else
-    // We need to call this even if auth isn't enabled because the file
-    // descriptor will always be open.
-    adbd_cloexec_auth_socket();
-
-    if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) {
-        auth_required = false;
-    }
-
-    adbd_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");
-    }
-
-    /* add extra groups:
-    ** AID_ADB to access the USB driver
-    ** AID_LOG to read system logs (adb logcat)
-    ** AID_INPUT to diagnose input issues (getevent)
-    ** AID_INET to diagnose network issues (ping)
-    ** AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
-    ** AID_SDCARD_R to allow reading from the SD card
-    ** AID_SDCARD_RW to allow writing to the SD card
-    ** AID_NET_BW_STATS to read out qtaguid statistics
-    */
-    gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET, AID_NET_BT,
-                       AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
-                       AID_NET_BW_STATS };
-    if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
-        exit(1);
-    }
-
-    /* don't listen on a port (default 5037) if running in secure mode */
-    /* don't run as root if we are running in secure mode */
-    if (should_drop_privileges()) {
-        drop_capabilities_bounding_set_if_needed();
-
-        /* then switch user and group to "shell" */
-        if (setgid(AID_SHELL) != 0) {
-            exit(1);
-        }
-        if (setuid(AID_SHELL) != 0) {
-            exit(1);
-        }
-
-        D("Local port disabled\n");
-    } else {
-        if ((root_seclabel != NULL) && (is_selinux_enabled() > 0)) {
-            // b/12587913: fix setcon to allow const pointers
-            if (setcon((char *)root_seclabel) < 0) {
-                exit(1);
-            }
-        }
-        std::string local_name = android::base::StringPrintf("tcp:%d", 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.
-#if defined(_WIN32)
-        DWORD  count;
-        WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ), "OK\n", 3, &count, NULL );
-#else
-        fprintf(stderr, "OK\n");
-#endif
-        start_logging();
-    }
-    D("Event loop starting\n");
-
-    fdevent_loop();
-
-    usb_cleanup();
-
-    return 0;
-}
-
-#if !ADB_HOST
-void close_stdin() {
-    int fd = unix_open("/dev/null", O_RDONLY);
-    if (fd == -1) {
-        perror("failed to open /dev/null, stdin will remain open");
-        return;
-    }
-    dup2(fd, 0);
-    adb_close(fd);
-}
-#endif
-
-// TODO(danalbert): Split this file up into adb_main.cpp and adbd_main.cpp.
-int main(int argc, char **argv) {
-#if ADB_HOST
-    // adb client/server
-    adb_sysdeps_init();
-    adb_trace_init();
-    D("Handling commandline()\n");
-    return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
-#else
-    // adbd
-    while (true) {
-        static struct option opts[] = {
-            {"root_seclabel", required_argument, nullptr, 's'},
-            {"device_banner", required_argument, nullptr, 'b'},
-            {"version", no_argument, nullptr, 'v'},
-        };
-
-        int option_index = 0;
-        int c = getopt_long(argc, argv, "", opts, &option_index);
-        if (c == -1)
-            break;
-        switch (c) {
-        case 's':
-            root_seclabel = optarg;
-            break;
-        case 'b':
-            adb_device_banner = optarg;
-            break;
-        case 'v':
-            printf("Android Debug Bridge Daemon version %d.%d.%d %s\n",
-                   ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
-                   ADB_REVISION);
-            return 0;
-        default:
-            break;
-        }
-    }
-
-    close_stdin();
-
-    adb_trace_init();
-
-    /* If adbd runs inside the emulator this will enable adb tracing via
-     * adb-debug qemud service in the emulator. */
-    adb_qemu_trace_init();
-
-    D("Handling main()\n");
-    return adb_main(0, DEFAULT_ADB_PORT);
-#endif
-}
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
new file mode 100644 (file)
index 0000000..cf99df7
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sysdeps.h"
+#include "adb_trace.h"
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings.h>
+
+#include "adb.h"
+
+#if !ADB_HOST
+#include <cutils/properties.h>
+#endif
+
+#if !ADB_HOST
+const char* adb_device_banner = "device";
+static android::base::LogdLogger gLogdLogger;
+#else
+const char* adb_device_banner = "host";
+#endif
+
+void AdbLogger(android::base::LogId id, android::base::LogSeverity severity,
+               const char* tag, const char* file, unsigned int line,
+               const char* message) {
+    android::base::StderrLogger(id, severity, tag, file, line, message);
+#if !ADB_HOST
+    gLogdLogger(id, severity, tag, file, line, message);
+#endif
+}
+
+
+#if !ADB_HOST
+static std::string get_log_file_name() {
+    struct tm now;
+    time_t t;
+    tzset();
+    time(&t);
+    localtime_r(&t, &now);
+
+    char timestamp[PATH_MAX];
+    strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", &now);
+
+    return android::base::StringPrintf("/data/adb/adb-%s-%d", timestamp,
+                                       getpid());
+}
+
+void start_device_log(void) {
+    int fd = unix_open(get_log_file_name().c_str(),
+                       O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640);
+    if (fd == -1) {
+        return;
+    }
+
+    // Redirect stdout and stderr to the log file.
+    dup2(fd, STDOUT_FILENO);
+    dup2(fd, STDERR_FILENO);
+    fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
+    unix_close(fd);
+}
+#endif
+
+int adb_trace_mask;
+
+std::string get_trace_setting_from_env() {
+    const char* setting = getenv("ADB_TRACE");
+    if (setting == nullptr) {
+        setting = "";
+    }
+
+    return std::string(setting);
+}
+
+#if !ADB_HOST
+std::string get_trace_setting_from_prop() {
+    char buf[PROPERTY_VALUE_MAX];
+    property_get("persist.adb.trace_mask", buf, "");
+    return std::string(buf);
+}
+#endif
+
+std::string get_trace_setting() {
+#if ADB_HOST
+    return get_trace_setting_from_env();
+#else
+    return get_trace_setting_from_prop();
+#endif
+}
+
+// Split the space separated list of tags from the trace setting and build the
+// trace mask from it. note that '1' and 'all' are special cases to enable all
+// tracing.
+//
+// adb's trace setting comes from the ADB_TRACE environment variable, whereas
+// adbd's comes from the system property persist.adb.trace_mask.
+static void setup_trace_mask() {
+    const std::string trace_setting = get_trace_setting();
+    if (trace_setting.empty()) {
+        return;
+    }
+
+    std::unordered_map<std::string, int> trace_flags = {
+        {"1", 0},
+        {"all", 0},
+        {"adb", ADB},
+        {"sockets", SOCKETS},
+        {"packets", PACKETS},
+        {"rwx", RWX},
+        {"usb", USB},
+        {"sync", SYNC},
+        {"sysdeps", SYSDEPS},
+        {"transport", TRANSPORT},
+        {"jdwp", JDWP},
+        {"services", SERVICES},
+        {"auth", AUTH},
+        {"fdevent", FDEVENT},
+        {"shell", SHELL}};
+
+    std::vector<std::string> elements = android::base::Split(trace_setting, " ");
+    for (const auto& elem : elements) {
+        const auto& flag = trace_flags.find(elem);
+        if (flag == trace_flags.end()) {
+            LOG(ERROR) << "Unknown trace flag: " << elem;
+            continue;
+        }
+
+        if (flag->second == 0) {
+            // 0 is used for the special values "1" and "all" that enable all
+            // tracing.
+            adb_trace_mask = ~0;
+            return;
+        } else {
+            adb_trace_mask |= 1 << flag->second;
+        }
+    }
+}
+
+void adb_trace_init(char** argv) {
+#if !ADB_HOST
+    // Don't open log file if no tracing, since this will block
+    // the crypto unmount of /data
+    if (!get_trace_setting().empty()) {
+        if (unix_isatty(STDOUT_FILENO) == 0) {
+            start_device_log();
+        }
+    }
+#endif
+
+    android::base::InitLogging(argv, &AdbLogger);
+    setup_trace_mask();
+
+    VLOG(ADB) << adb_version();
+}
+
+void adb_trace_enable(AdbTrace trace_tag) {
+    adb_trace_mask |= (1 << trace_tag);
+}
index 63d4151..78b2deb 100644 (file)
 #ifndef __ADB_TRACE_H
 #define __ADB_TRACE_H
 
-#if !ADB_HOST
-#include <android/log.h>
-#else
-#include <stdio.h>
-#endif
+#include <base/logging.h>
+#include <base/stringprintf.h>
 
 /* IMPORTANT: if you change the following list, don't
  * forget to update the corresponding 'tags' table in
- * the adb_trace_init() function implemented in adb.c
+ * the adb_trace_init() function implemented in adb_trace.cpp.
  */
 enum AdbTrace {
-    TRACE_ADB = 0,   /* 0x001 */
-    TRACE_SOCKETS,
-    TRACE_PACKETS,
-    TRACE_TRANSPORT,
-    TRACE_RWX,       /* 0x010 */
-    TRACE_USB,
-    TRACE_SYNC,
-    TRACE_SYSDEPS,
-    TRACE_JDWP,      /* 0x100 */
-    TRACE_SERVICES,
-    TRACE_AUTH,
-    TRACE_FDEVENT,
-} ;
-
-#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 */
-#if ADB_HOST
-#  define  D(...)                                      \
-        do {                                           \
-            if (ADB_TRACING) {                         \
-                int save_errno = errno;                \
-                adb_mutex_lock(&D_lock);               \
-                fprintf(stderr, "%16s: %5d:%5lu | ",   \
-                        __FUNCTION__,                  \
-                        getpid(), adb_thread_id());    \
-                errno = save_errno;                    \
-                fprintf(stderr, __VA_ARGS__ );         \
-                fflush(stderr);                        \
-                adb_mutex_unlock(&D_lock);             \
-                errno = save_errno;                    \
-           }                                           \
-        } while (0)
-#  define  DR(...)                                     \
-        do {                                           \
-            if (ADB_TRACING) {                         \
-                int save_errno = errno;                \
-                adb_mutex_lock(&D_lock);               \
-                errno = save_errno;                    \
-                fprintf(stderr, __VA_ARGS__ );         \
-                fflush(stderr);                        \
-                adb_mutex_unlock(&D_lock);             \
-                errno = save_errno;                    \
-           }                                           \
-        } while (0)
-#else
-#  define  D(...)                                      \
-        do {                                           \
-            if (ADB_TRACING) {                         \
-                __android_log_print(                   \
-                    ANDROID_LOG_INFO,                  \
-                    __FUNCTION__,                      \
-                    __VA_ARGS__ );                     \
-            }                                          \
-        } while (0)
-#  define  DR(...)                                     \
-        do {                                           \
-            if (ADB_TRACING) {                         \
-                __android_log_print(                   \
-                    ANDROID_LOG_INFO,                  \
-                    __FUNCTION__,                      \
-                    __VA_ARGS__ );                     \
-            }                                          \
-        } while (0)
-#endif /* ADB_HOST */
+    ADB = 0,   /* 0x001 */
+    SOCKETS,
+    PACKETS,
+    TRANSPORT,
+    RWX,       /* 0x010 */
+    USB,
+    SYNC,
+    SYSDEPS,
+    JDWP,      /* 0x100 */
+    SERVICES,
+    AUTH,
+    FDEVENT,
+    SHELL
+};
+
+#define VLOG_IS_ON(TAG) \
+    ((adb_trace_mask & (1 << TAG)) != 0)
+
+#define VLOG(TAG)         \
+    if (LIKELY(!VLOG_IS_ON(TAG))) \
+        ;                 \
+    else                  \
+        LOG(INFO)
+
+// You must define TRACE_TAG before using this macro.
+#define D(...) \
+    VLOG(TRACE_TAG) << android::base::StringPrintf(__VA_ARGS__)
+
+
+extern int adb_trace_mask;
+void adb_trace_init(char**);
+void adb_trace_enable(AdbTrace trace_tag);
 
 #endif /* __ADB_TRACE_H */
index 604bd57..42f1c7d 100644 (file)
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
 
 #include "adb_utils.h"
 
+#include <libgen.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
 #include <algorithm>
 
+#include <base/logging.h>
 #include <base/stringprintf.h>
+#include <base/strings.h>
 
 #include "adb_trace.h"
 #include "sysdeps.h"
 
+ADB_MUTEX_DEFINE(basename_lock);
+ADB_MUTEX_DEFINE(dirname_lock);
+
 bool getcwd(std::string* s) {
   char* cwd = getcwd(nullptr, 0);
   if (cwd != nullptr) *s = cwd;
@@ -63,7 +69,108 @@ std::string escape_arg(const std::string& s) {
   return result;
 }
 
-void dump_hex(const void* data, size_t byte_count) {
+std::string adb_basename(const std::string& path) {
+  // Copy path because basename may modify the string passed in.
+  std::string result(path);
+
+  // Use lock because basename() may write to a process global and return a
+  // pointer to that. Note that this locking strategy only works if all other
+  // callers to dirname in the process also grab this same lock.
+  adb_mutex_lock(&basename_lock);
+
+  // Note that if std::string uses copy-on-write strings, &str[0] will cause
+  // the copy to be made, so there is no chance of us accidentally writing to
+  // the storage for 'path'.
+  char* name = basename(&result[0]);
+
+  // In case dirname returned a pointer to a process global, copy that string
+  // before leaving the lock.
+  result.assign(name);
+
+  adb_mutex_unlock(&basename_lock);
+
+  return result;
+}
+
+std::string adb_dirname(const std::string& path) {
+  // Copy path because dirname may modify the string passed in.
+  std::string result(path);
+
+  // Use lock because dirname() may write to a process global and return a
+  // pointer to that. Note that this locking strategy only works if all other
+  // callers to dirname in the process also grab this same lock.
+  adb_mutex_lock(&dirname_lock);
+
+  // Note that if std::string uses copy-on-write strings, &str[0] will cause
+  // the copy to be made, so there is no chance of us accidentally writing to
+  // the storage for 'path'.
+  char* parent = dirname(&result[0]);
+
+  // In case dirname returned a pointer to a process global, copy that string
+  // before leaving the lock.
+  result.assign(parent);
+
+  adb_mutex_unlock(&dirname_lock);
+
+  return result;
+}
+
+// Given a relative or absolute filepath, create the parent directory hierarchy
+// as needed. Returns true if the hierarchy is/was setup.
+bool mkdirs(const std::string& path) {
+  // TODO: all the callers do unlink && mkdirs && adb_creat ---
+  // that's probably the operation we should expose.
+
+  // Implementation Notes:
+  //
+  // Pros:
+  // - Uses dirname, so does not need to deal with OS_PATH_SEPARATOR.
+  // - On Windows, uses mingw dirname which accepts '/' and '\\', drive letters
+  //   (C:\foo), UNC paths (\\server\share\dir\dir\file), and Unicode (when
+  //   combined with our adb_mkdir() which takes UTF-8).
+  // - Is optimistic wrt thinking that a deep directory hierarchy will exist.
+  //   So it does as few stat()s as possible before doing mkdir()s.
+  // Cons:
+  // - Recursive, so it uses stack space relative to number of directory
+  //   components.
+
+  if (directory_exists(path)) {
+    return true;
+  }
+
+  // If dirname returned the same path as what we passed in, don't go recursive.
+  // This can happen on Windows when walking up the directory hierarchy and not
+  // finding anything that already exists (unlike POSIX that will eventually
+  // find . or /).
+  const std::string parent(adb_dirname(path));
+
+  if (parent == path) {
+    errno = ENOENT;
+    return false;
+  }
+
+  // Recursively make parent directories of 'path'.
+  if (!mkdirs(parent)) {
+    return false;
+  }
+
+  // Now that the parent directory hierarchy of 'path' has been ensured,
+  // create parent itself.
+  if (adb_mkdir(path, 0775) == -1) {
+    // Can't just check for errno == EEXIST because it might be a file that
+    // exists.
+    const int saved_errno = errno;
+    if (directory_exists(parent)) {
+      return true;
+    }
+    errno = saved_errno;
+    return false;
+  }
+
+  return true;
+}
+
+std::string dump_hex(const void* data, size_t byte_count) {
     byte_count = std::min(byte_count, size_t(16));
 
     const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
@@ -75,12 +182,82 @@ void dump_hex(const void* data, size_t byte_count) {
     line.push_back(' ');
 
     for (size_t i = 0; i < byte_count; ++i) {
-        int c = p[i];
-        if (c < 32 || c > 127) {
-            c = '.';
-        }
-        line.push_back(c);
+        int ch = p[i];
+        line.push_back(isprint(ch) ? ch : '.');
     }
 
-    DR("%s\n", line.c_str());
+    return line;
+}
+
+bool parse_host_and_port(const std::string& address,
+                         std::string* canonical_address,
+                         std::string* host, int* port,
+                         std::string* error) {
+    host->clear();
+
+    bool ipv6 = true;
+    bool saw_port = false;
+    size_t colons = std::count(address.begin(), address.end(), ':');
+    size_t dots = std::count(address.begin(), address.end(), '.');
+    std::string port_str;
+    if (address[0] == '[') {
+      // [::1]:123
+      if (address.rfind("]:") == std::string::npos) {
+        *error = android::base::StringPrintf("bad IPv6 address '%s'", address.c_str());
+        return false;
+      }
+      *host = address.substr(1, (address.find("]:") - 1));
+      port_str = address.substr(address.rfind("]:") + 2);
+      saw_port = true;
+    } else if (dots == 0 && colons >= 2 && colons <= 7) {
+      // ::1
+      *host = address;
+    } else if (colons <= 1) {
+      // 1.2.3.4 or some.accidental.domain.com
+      ipv6 = false;
+      std::vector<std::string> pieces = android::base::Split(address, ":");
+      *host = pieces[0];
+      if (pieces.size() > 1) {
+        port_str = pieces[1];
+        saw_port = true;
+      }
+    }
+
+    if (host->empty()) {
+      *error = android::base::StringPrintf("no host in '%s'", address.c_str());
+      return false;
+    }
+
+    if (saw_port) {
+      if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 || *port > 65535) {
+        *error = android::base::StringPrintf("bad port number '%s' in '%s'",
+                                             port_str.c_str(), address.c_str());
+        return false;
+      }
+    }
+
+    *canonical_address = android::base::StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
+    LOG(DEBUG) << "parsed " << address << " as " << *host << " and " << *port
+               << " (" << *canonical_address << ")";
+    return true;
+}
+
+std::string perror_str(const char* msg) {
+    return android::base::StringPrintf("%s: %s", msg, strerror(errno));
+}
+
+#if !defined(_WIN32)
+bool set_file_block_mode(int fd, bool block) {
+    int flags = fcntl(fd, F_GETFL, 0);
+    if (flags == -1) {
+        PLOG(ERROR) << "failed to fcntl(F_GETFL) for fd " << fd;
+        return false;
+    }
+    flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
+    if (fcntl(fd, F_SETFL, flags) != 0) {
+        PLOG(ERROR) << "failed to fcntl(F_SETFL) for fd " << fd << ", flags " << flags;
+        return false;
+    }
+    return true;
 }
+#endif
index 84f7d0c..537d0e4 100644 (file)
 bool getcwd(std::string* cwd);
 bool directory_exists(const std::string& path);
 
+// Like the regular basename and dirname, but thread-safe on all
+// platforms and capable of correctly handling exotic Windows paths.
+std::string adb_basename(const std::string& path);
+std::string adb_dirname(const std::string& path);
+
+bool mkdirs(const std::string& path);
+
 std::string escape_arg(const std::string& s);
 
-void dump_hex(const void* ptr, size_t byte_count);
+std::string dump_hex(const void* ptr, size_t byte_count);
+
+// Parses 'address' into 'host' and 'port'.
+// If no port is given, takes the default from *port.
+// 'canonical_address' then becomes "host:port" or "[host]:port" as appropriate.
+// Note that no real checking is done that 'host' or 'port' is valid; that's
+// left to getaddrinfo(3).
+// Returns false on failure and sets *error to an appropriate message.
+bool parse_host_and_port(const std::string& address,
+                         std::string* canonical_address,
+                         std::string* host, int* port,
+                         std::string* error);
+
+std::string perror_str(const char* msg);
+
+bool set_file_block_mode(int fd, bool block);
 
 #endif
index 052aea5..93c20cb 100644 (file)
 
 #include "adb_utils.h"
 
+#ifdef _WIN32
+#include <windows.h>
+#include <userenv.h>
+#endif
+
+#include <string>
+
 #include <gtest/gtest.h>
 
+#include <stdlib.h>
+#include <string.h>
+
+#include "sysdeps.h"
+
+#include <base/macros.h>
+#include <base/test_utils.h>
+
+#ifdef _WIN32
+static std::string subdir(const char* parent, const char* child) {
+  std::string str(parent);
+  str += OS_PATH_SEPARATOR;
+  str += child;
+  return str;
+}
+#endif
+
 TEST(adb_utils, directory_exists) {
+#ifdef _WIN32
+  char profiles_dir[MAX_PATH];
+  DWORD cch = arraysize(profiles_dir);
+
+  // On typical Windows 7, returns C:\Users
+  ASSERT_TRUE(GetProfilesDirectoryA(profiles_dir, &cch));
+
+  ASSERT_TRUE(directory_exists(profiles_dir));
+
+  // On modern (English?) Windows, this is a directory symbolic link to
+  // C:\ProgramData. Symbolic links are rare on Windows and the user requires
+  // a special permission (by default granted to Administrative users) to
+  // create symbolic links.
+  ASSERT_FALSE(directory_exists(subdir(profiles_dir, "All Users")));
+
+  // On modern (English?) Windows, this is a directory junction to
+  // C:\Users\Default. Junctions are used throughout user profile directories
+  // for backwards compatibility and they don't require any special permissions
+  // to create.
+  ASSERT_FALSE(directory_exists(subdir(profiles_dir, "Default User")));
+
+  ASSERT_FALSE(directory_exists(subdir(profiles_dir, "does-not-exist")));
+#else
   ASSERT_TRUE(directory_exists("/proc"));
   ASSERT_FALSE(directory_exists("/proc/self")); // Symbolic link.
   ASSERT_FALSE(directory_exists("/proc/does-not-exist"));
+#endif
 }
 
 TEST(adb_utils, escape_arg) {
@@ -50,3 +98,130 @@ TEST(adb_utils, escape_arg) {
   ASSERT_EQ(R"('abc(')", escape_arg("abc("));
   ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
 }
+
+TEST(adb_utils, adb_basename) {
+  EXPECT_EQ("sh", adb_basename("/system/bin/sh"));
+  EXPECT_EQ("sh", adb_basename("sh"));
+  EXPECT_EQ("sh", adb_basename("/system/bin/sh/"));
+}
+
+TEST(adb_utils, adb_dirname) {
+  EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh"));
+  EXPECT_EQ(".", adb_dirname("sh"));
+  EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh/"));
+}
+
+TEST(adb_utils, parse_host_and_port) {
+  std::string canonical_address;
+  std::string host;
+  int port;
+  std::string error;
+
+  // Name, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("www.google.com", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("www.google.com:123", canonical_address);
+  ASSERT_EQ("www.google.com", host);
+  ASSERT_EQ(123, port);
+
+  // Name, explicit port.
+  ASSERT_TRUE(parse_host_and_port("www.google.com:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("www.google.com:666", canonical_address);
+  ASSERT_EQ("www.google.com", host);
+  ASSERT_EQ(666, port);
+
+  // IPv4, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("1.2.3.4", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("1.2.3.4:123", canonical_address);
+  ASSERT_EQ("1.2.3.4", host);
+  ASSERT_EQ(123, port);
+
+  // IPv4, explicit port.
+  ASSERT_TRUE(parse_host_and_port("1.2.3.4:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("1.2.3.4:666", canonical_address);
+  ASSERT_EQ("1.2.3.4", host);
+  ASSERT_EQ(666, port);
+
+  // Simple IPv6, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("::1", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[::1]:123", canonical_address);
+  ASSERT_EQ("::1", host);
+  ASSERT_EQ(123, port);
+
+  // Simple IPv6, explicit port.
+  ASSERT_TRUE(parse_host_and_port("[::1]:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[::1]:666", canonical_address);
+  ASSERT_EQ("::1", host);
+  ASSERT_EQ(666, port);
+
+  // Hairy IPv6, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("fe80::200:5aee:feaa:20a2", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical_address);
+  ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
+  ASSERT_EQ(123, port);
+
+  // Simple IPv6, explicit port.
+  ASSERT_TRUE(parse_host_and_port("[fe80::200:5aee:feaa:20a2]:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical_address);
+  ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
+  ASSERT_EQ(666, port);
+
+  // Invalid IPv4.
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4::", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:hello", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port(":123", &canonical_address, &host, &port, &error));
+
+  // Invalid IPv6.
+  EXPECT_FALSE(parse_host_and_port(":1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("::::::::1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]::", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:hello", &canonical_address, &host, &port, &error));
+
+  // Invalid ports.
+  EXPECT_FALSE(parse_host_and_port("[::1]:-1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:0", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:65536", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:-1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:0", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:65536", &canonical_address, &host, &port, &error));
+}
+
+void test_mkdirs(const std::string basepath) {
+  EXPECT_TRUE(mkdirs(basepath));
+  EXPECT_NE(-1, adb_creat(basepath.c_str(), 0600));
+  EXPECT_FALSE(mkdirs(basepath + "/subdir/"));
+}
+
+TEST(adb_utils, mkdirs) {
+  TemporaryDir td;
+
+  // Absolute paths.
+  test_mkdirs(std::string(td.path) + "/dir/subdir/file");
+
+  // Relative paths.
+  ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
+  test_mkdirs(std::string("relative/subrel/file"));
+}
+
+#if !defined(_WIN32)
+TEST(adb_utils, set_file_block_mode) {
+  int fd = adb_open("/dev/null", O_RDWR | O_APPEND);
+  ASSERT_GE(fd, 0);
+  int flags = fcntl(fd, F_GETFL, 0);
+  ASSERT_EQ(O_RDWR | O_APPEND, (flags & (O_RDWR | O_APPEND)));
+  ASSERT_TRUE(set_file_block_mode(fd, false));
+  int new_flags = fcntl(fd, F_GETFL, 0);
+  ASSERT_EQ(flags | O_NONBLOCK, new_flags);
+  ASSERT_TRUE(set_file_block_mode(fd, true));
+  new_flags = fcntl(fd, F_GETFL, 0);
+  ASSERT_EQ(flags, new_flags);
+  ASSERT_EQ(0, adb_close(fd));
+}
+#endif
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
new file mode 100644 (file)
index 0000000..8d01af3
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG ADB
+
+#include "sysdeps.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// We only build the affinity WAR code for Linux.
+#if defined(__linux__)
+#include <sched.h>
+#endif
+
+#include "base/file.h"
+#include "base/logging.h"
+#include "base/stringprintf.h"
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_listeners.h"
+#include "transport.h"
+
+#if defined(_WIN32)
+static const char kNullFileName[] = "NUL";
+
+static BOOL WINAPI ctrlc_handler(DWORD type) {
+    // TODO: Consider trying to kill a starting up adb server (if we're in
+    // launch_server) by calling GenerateConsoleCtrlEvent().
+    exit(STATUS_CONTROL_C_EXIT);
+    return TRUE;
+}
+
+static std::string GetLogFilePath() {
+    const char log_name[] = "adb.log";
+    WCHAR temp_path[MAX_PATH];
+
+    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992%28v=vs.85%29.aspx
+    DWORD nchars = GetTempPathW(arraysize(temp_path), temp_path);
+    if ((nchars >= arraysize(temp_path)) || (nchars == 0)) {
+        // If string truncation or some other error.
+        fatal("cannot retrieve temporary file path: %s\n",
+              SystemErrorCodeToString(GetLastError()).c_str());
+    }
+
+    std::string temp_path_utf8;
+    if (!android::base::WideToUTF8(temp_path, &temp_path_utf8)) {
+        fatal_errno("cannot convert temporary file path from UTF-16 to UTF-8");
+    }
+
+    return temp_path_utf8 + log_name;
+}
+#else
+static const char kNullFileName[] = "/dev/null";
+
+static std::string GetLogFilePath() {
+    return std::string("/tmp/adb.log");
+}
+#endif
+
+static void close_stdin() {
+    int fd = unix_open(kNullFileName, O_RDONLY);
+    if (fd == -1) {
+        fatal("cannot open '%s': %s", kNullFileName, strerror(errno));
+    }
+    if (dup2(fd, STDIN_FILENO) == -1) {
+        fatal("cannot redirect stdin: %s", strerror(errno));
+    }
+    unix_close(fd);
+}
+
+static void setup_daemon_logging(void) {
+    const std::string log_file_path(GetLogFilePath());
+    int fd = unix_open(log_file_path.c_str(), O_WRONLY | O_CREAT | O_APPEND,
+                       0640);
+    if (fd == -1) {
+        fatal("cannot open '%s': %s", log_file_path.c_str(), strerror(errno));
+    }
+    if (dup2(fd, STDOUT_FILENO) == -1) {
+        fatal("cannot redirect stdout: %s", strerror(errno));
+    }
+    if (dup2(fd, STDERR_FILENO) == -1) {
+        fatal("cannot redirect stderr: %s", strerror(errno));
+    }
+    unix_close(fd);
+
+    fprintf(stderr, "--- adb starting (pid %d) ---\n", getpid());
+    LOG(INFO) << adb_version();
+}
+
+int adb_main(int is_daemon, int server_port, int ack_reply_fd) {
+#if defined(_WIN32)
+    // adb start-server starts us up with stdout and stderr hooked up to
+    // anonymous pipes to. When the C Runtime sees this, it makes stderr and
+    // stdout buffered, but to improve the chance that error output is seen,
+    // unbuffer stdout and stderr just like if we were run at the console.
+    // This also keeps stderr unbuffered when it is redirected to adb.log.
+    if (is_daemon) {
+        if (setvbuf(stdout, NULL, _IONBF, 0) == -1) {
+            fatal("cannot make stdout unbuffered: %s", strerror(errno));
+        }
+        if (setvbuf(stderr, NULL, _IONBF, 0) == -1) {
+            fatal("cannot make stderr unbuffered: %s", strerror(errno));
+        }
+    }
+
+    SetConsoleCtrlHandler(ctrlc_handler, TRUE);
+#else
+    signal(SIGPIPE, SIG_IGN);
+#endif
+
+    init_transport_registration();
+
+    usb_init();
+    local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+    adb_auth_init();
+
+    std::string error;
+    std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
+    if (install_listener(local_name, "*smartsocket*", nullptr, 0, &error)) {
+        fatal("could not install *smartsocket* listener: %s", error.c_str());
+    }
+
+    // Inform our parent that we are up and running.
+    if (is_daemon) {
+        close_stdin();
+        setup_daemon_logging();
+
+        // Any error output written to stderr now goes to adb.log. We could
+        // keep around a copy of the stderr fd and use that to write any errors
+        // encountered by the following code, but that is probably overkill.
+#if defined(_WIN32)
+        const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+        const CHAR ack[] = "OK\n";
+        const DWORD bytes_to_write = arraysize(ack) - 1;
+        DWORD written = 0;
+        if (!WriteFile(ack_reply_handle, ack, bytes_to_write, &written, NULL)) {
+            fatal("adb: cannot write ACK to handle 0x%p: %s", ack_reply_handle,
+                  SystemErrorCodeToString(GetLastError()).c_str());
+        }
+        if (written != bytes_to_write) {
+            fatal("adb: cannot write %lu bytes of ACK: only wrote %lu bytes",
+                  bytes_to_write, written);
+        }
+        CloseHandle(ack_reply_handle);
+#else
+        // TODO(danalbert): Can't use SendOkay because we're sending "OK\n", not
+        // "OKAY".
+        if (!android::base::WriteStringToFd("OK\n", ack_reply_fd)) {
+            fatal_errno("error writing ACK to fd %d", ack_reply_fd);
+        }
+        unix_close(ack_reply_fd);
+#endif
+    }
+
+    D("Event loop starting");
+    fdevent_loop();
+
+    return 0;
+}
+
+int main(int argc, char** argv) {
+    adb_sysdeps_init();
+    adb_trace_init(argv);
+    return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
+}
index 374a2e5..5113eb8 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
 
 #include "sysdeps.h"
 
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <memory>
 #include <string>
+#include <vector>
 
+#include <base/logging.h>
 #include <base/stringprintf.h>
+#include <base/strings.h>
 
 #if !defined(_WIN32)
+#include <signal.h>
+#include <sys/ioctl.h>
 #include <termios.h>
 #include <unistd.h>
 #endif
 #include "adb_io.h"
 #include "adb_utils.h"
 #include "file_sync_service.h"
+#include "services.h"
+#include "shell_service.h"
+#include "transport.h"
 
-static int install_app(transport_type t, const char* serial, int argc, const char** argv);
-static int install_multiple_app(transport_type t, const char* serial, int argc, const char** argv);
-static int uninstall_app(transport_type t, const char* serial, int argc, const char** argv);
+static int install_app(TransportType t, const char* serial, int argc, const char** argv);
+static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
+static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
+static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
+static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
 
-static std::string gProductOutPath;
+static auto& gProductOutPath = *new std::string();
 extern int gListenAll;
 
 static std::string product_file(const char *extra) {
@@ -65,16 +76,9 @@ static std::string product_file(const char *extra) {
                                        gProductOutPath.c_str(), OS_PATH_SEPARATOR_STR, extra);
 }
 
-static void version(FILE* out) {
-    fprintf(out, "Android Debug Bridge version %d.%d.%d\nRevision %s\n",
-            ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_REVISION);
-}
-
 static void help() {
-    version(stderr);
-
+    fprintf(stderr, "%s\n", adb_version().c_str());
     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"
@@ -101,18 +105,19 @@ static void help() {
         "                                 will disconnect from all connected TCP/IP devices.\n"
         "\n"
         "device commands:\n"
-        "  adb push [-p] <local> <remote>\n"
-        "                               - copy file/dir to device\n"
-        "                                 ('-p' to display the transfer progress)\n"
-        "  adb pull [-p] [-a] <remote> [<local>]\n"
-        "                               - copy file/dir from device\n"
-        "                                 ('-p' to display the transfer progress)\n"
-        "                                 ('-a' means copy timestamp and mode)\n"
+        "  adb push <local>... <remote>\n"
+        "                               - copy files/dirs to device\n"
+        "  adb pull [-a] <remote>... <local>\n"
+        "                               - copy files/dirs from device\n"
+        "                                 (-a preserves file timestamp and mode)\n"
         "  adb sync [ <directory> ]     - 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 <command>          - run remote shell command\n"
+        "  adb shell [-e escape] [-Tt] [-x] [command]\n"
+        "                               - run remote shell command (interactive shell if no command given)\n"
+        "                                 (-e: choose escape character, or \"none\"; default '~')\n"
+        "                                 (-T: disable PTY allocation)\n"
+        "                                 (-t: force PTY allocation)\n"
+        "                                 (-x: disable remote exit codes and stdout/stderr separation)\n"
         "  adb emu <command>            - run emulator console command\n"
         "  adb logcat [ <filter-spec> ] - View device log\n"
         "  adb forward --list           - list all forward socket connections.\n"
@@ -138,7 +143,7 @@ static void help() {
         "                                   localabstract:<unix domain socket name>\n"
         "                                   localreserved:<unix domain socket name>\n"
         "                                   localfilesystem:<unix domain socket name>\n"
-        "  adb reverse --norebind <remote> <local>\n"
+        "  adb reverse --no-rebind <remote> <local>\n"
         "                               - same as 'adb reverse <remote> <local>' but fails\n"
         "                                 if <remote> is already reversed.\n"
         "  adb reverse --remove <remote>\n"
@@ -210,11 +215,12 @@ static void help() {
         "  adb reboot sideload          - reboots the device into the sideload mode in recovery program (adb root required).\n"
         "  adb reboot sideload-auto-reboot\n"
         "                               - reboots into the sideload mode, then reboots automatically after the sideload regardless of the result.\n"
-        "  adb reboot-bootloader        - reboots the device into the bootloader\n"
+        "  adb sideload <file>          - sideloads the given package\n"
         "  adb root                     - restarts the adbd daemon with root permissions\n"
         "  adb unroot                   - restarts the adbd daemon without root permissions\n"
         "  adb usb                      - restarts the adbd daemon listening on USB\n"
         "  adb tcpip <port>             - restarts the adbd daemon listening on TCP on the specified port\n"
+        "\n"
         "networking:\n"
         "  adb ppp <tty> [parameters]   - Run PPP over USB.\n"
         " Note: you should not automatically start a PPP connection.\n"
@@ -229,7 +235,7 @@ static void help() {
         "  - If it is \"system\", \"vendor\", \"oem\" or \"data\", only the corresponding partition\n"
         "    is updated.\n"
         "\n"
-        "environmental variables:\n"
+        "environment variables:\n"
         "  ADB_TRACE                    - Print debug information. A comma separated list of the following values\n"
         "                                 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
         "  ANDROID_SERIAL               - The serial number to connect to. -s takes priority over this if given.\n"
@@ -245,17 +251,17 @@ static int usage() {
 #if defined(_WIN32)
 
 // Implemented in sysdeps_win32.cpp.
-void stdin_raw_init(int fd);
-void stdin_raw_restore(int fd);
+void stdin_raw_init();
+void stdin_raw_restore();
 
 #else
 static termios g_saved_terminal_state;
 
-static void stdin_raw_init(int fd) {
-    if (tcgetattr(fd, &g_saved_terminal_state)) return;
+static void stdin_raw_init() {
+    if (tcgetattr(STDIN_FILENO, &g_saved_terminal_state)) return;
 
     termios tio;
-    if (tcgetattr(fd, &tio)) return;
+    if (tcgetattr(STDIN_FILENO, &tio)) return;
 
     cfmakeraw(&tio);
 
@@ -263,27 +269,68 @@ static void stdin_raw_init(int fd) {
     tio.c_cc[VTIME] = 0;
     tio.c_cc[VMIN] = 1;
 
-    tcsetattr(fd, TCSAFLUSH, &tio);
+    tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
 }
 
-static void stdin_raw_restore(int fd) {
-    tcsetattr(fd, TCSAFLUSH, &g_saved_terminal_state);
+static void stdin_raw_restore() {
+    tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_saved_terminal_state);
 }
 #endif
 
-static void read_and_dump(int fd) {
+// Reads from |fd| and prints received data. If |use_shell_protocol| is true
+// this expects that incoming data will use the shell protocol, in which case
+// stdout/stderr are routed independently and the remote exit code will be
+// returned.
+static int read_and_dump(int fd, bool use_shell_protocol=false) {
+    int exit_code = 0;
+    std::unique_ptr<ShellProtocol> protocol;
+    int length = 0;
+    FILE* outfile = stdout;
+
+    char raw_buffer[BUFSIZ];
+    char* buffer_ptr = raw_buffer;
+    if (use_shell_protocol) {
+        protocol.reset(new ShellProtocol(fd));
+        if (!protocol) {
+            LOG(ERROR) << "failed to allocate memory for ShellProtocol object";
+            return 1;
+        }
+        buffer_ptr = protocol->data();
+    }
+
     while (fd >= 0) {
-        D("read_and_dump(): pre adb_read(fd=%d)\n", fd);
-        char buf[BUFSIZ];
-        int len = adb_read(fd, buf, sizeof(buf));
-        D("read_and_dump(): post adb_read(fd=%d): len=%d\n", fd, len);
-        if (len <= 0) {
-            break;
+        if (use_shell_protocol) {
+            if (!protocol->Read()) {
+                break;
+            }
+            switch (protocol->id()) {
+                case ShellProtocol::kIdStdout:
+                    outfile = stdout;
+                    break;
+                case ShellProtocol::kIdStderr:
+                    outfile = stderr;
+                    break;
+                case ShellProtocol::kIdExit:
+                    exit_code = protocol->data()[0];
+                    continue;
+                default:
+                    continue;
+            }
+            length = protocol->data_length();
+        } else {
+            D("read_and_dump(): pre adb_read(fd=%d)", fd);
+            length = adb_read(fd, raw_buffer, sizeof(raw_buffer));
+            D("read_and_dump(): post adb_read(fd=%d): length=%d", fd, length);
+            if (length <= 0) {
+                break;
+            }
         }
 
-        fwrite(buf, 1, len, stdout);
-        fflush(stdout);
+        fwrite(buffer_ptr, 1, length, outfile);
+        fflush(outfile);
     }
+
+    return exit_code;
 }
 
 static void read_status_line(int fd, char* buf, size_t count)
@@ -291,10 +338,7 @@ static void read_status_line(int fd, char* buf, size_t count)
     count--;
     while (count > 0) {
         int len = adb_read(fd, buf, count);
-        if (len == 0) {
-            break;
-        } else if (len < 0) {
-            if (errno == EINTR) continue;
+        if (len <= 0) {
             break;
         }
 
@@ -310,13 +354,32 @@ static void copy_to_file(int inFd, int outFd) {
     if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
     int len;
     long total = 0;
+#ifdef _WIN32
+    int old_stdin_mode = -1;
+    int old_stdout_mode = -1;
+#endif
 
-    D("copy_to_file(%d -> %d)\n", inFd, outFd);
+    D("copy_to_file(%d -> %d)", inFd, outFd);
 
     if (inFd == STDIN_FILENO) {
-        stdin_raw_init(STDIN_FILENO);
+        stdin_raw_init();
+#ifdef _WIN32
+        old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY);
+        if (old_stdin_mode == -1) {
+            fatal_errno("could not set stdin to binary");
+        }
+#endif
     }
 
+#ifdef _WIN32
+    if (outFd == STDOUT_FILENO) {
+        old_stdout_mode = _setmode(STDOUT_FILENO, _O_BINARY);
+        if (old_stdout_mode == -1) {
+            fatal_errno("could not set stdout to binary");
+        }
+    }
+#endif
+
     while (true) {
         if (inFd == STDIN_FILENO) {
             len = unix_read(inFd, buf, BUFSIZE);
@@ -324,15 +387,11 @@ static void copy_to_file(int inFd, int outFd) {
             len = adb_read(inFd, buf, BUFSIZE);
         }
         if (len == 0) {
-            D("copy_to_file() : read 0 bytes; exiting\n");
+            D("copy_to_file() : read 0 bytes; exiting");
             break;
         }
         if (len < 0) {
-            if (errno == EINTR) {
-                D("copy_to_file() : EINTR, retrying\n");
-                continue;
-            }
-            D("copy_to_file() : error %d\n", errno);
+            D("copy_to_file(): read failed: %s", strerror(errno));
             break;
         }
         if (outFd == STDOUT_FILENO) {
@@ -345,105 +404,369 @@ static void copy_to_file(int inFd, int outFd) {
     }
 
     if (inFd == STDIN_FILENO) {
-        stdin_raw_restore(STDIN_FILENO);
+        stdin_raw_restore();
+#ifdef _WIN32
+        if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
+            fatal_errno("could not restore stdin mode");
+        }
+#endif
+    }
+
+#ifdef _WIN32
+    if (outFd == STDOUT_FILENO) {
+        if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
+            fatal_errno("could not restore stdout mode");
+        }
     }
+#endif
 
-    D("copy_to_file() finished after %lu bytes\n", total);
+    D("copy_to_file() finished after %lu bytes", total);
     free(buf);
 }
 
-static void *stdin_read_thread(void *x)
-{
-    int fd, fdi;
-    unsigned char buf[1024];
-    int r, n;
-    int state = 0;
-
-    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;
+static std::string format_host_command(const char* command,
+                                       TransportType type, const char* serial) {
+    if (serial) {
+        return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+    }
+
+    const char* prefix = "host";
+    if (type == kTransportUsb) {
+        prefix = "host-usb";
+    } else if (type == kTransportLocal) {
+        prefix = "host-local";
+    }
+    return android::base::StringPrintf("%s:%s", prefix, command);
+}
+
+// Returns the FeatureSet for the indicated transport.
+static FeatureSet GetFeatureSet(TransportType transport_type, const char* serial) {
+    std::string result, error;
+    if (adb_query(format_host_command("features", transport_type, serial), &result, &error)) {
+        return StringToFeatureSet(result);
+    }
+    return FeatureSet();
+}
+
+static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
+#if !defined(_WIN32)
+    // Old devices can't handle window size changes.
+    if (shell == nullptr) return;
+
+    winsize ws;
+    if (ioctl(fd, TIOCGWINSZ, &ws) == -1) return;
+
+    // Send the new window size as human-readable ASCII for debugging convenience.
+    size_t l = snprintf(shell->data(), shell->data_capacity(), "%dx%d,%dx%d",
+                        ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
+    shell->Write(ShellProtocol::kIdWindowSizeChange, l + 1);
+#endif
+}
+
+// Used to pass multiple values to the stdin read thread.
+struct StdinReadArgs {
+    int stdin_fd, write_fd;
+    bool raw_stdin;
+    std::unique_ptr<ShellProtocol> protocol;
+    char escape_char;
+};
+
+// Loops to read from stdin and push the data to the given FD.
+// The argument should be a pointer to a StdinReadArgs object. This function
+// will take ownership of the object and delete it when finished.
+static void* stdin_read_thread_loop(void* x) {
+    std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x));
+
+#if !defined(_WIN32)
+    // Mask SIGTTIN in case we're in a backgrounded process.
+    sigset_t sigset;
+    sigemptyset(&sigset);
+    sigaddset(&sigset, SIGTTIN);
+    pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
+#endif
+
+#if !defined(_WIN32)
+    // Unblock SIGWINCH for this thread, so our read(2) below will be
+    // interrupted if the window size changes.
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGWINCH);
+    pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
+#endif
+
+    // Set up the initial window size.
+    send_window_size_change(args->stdin_fd, args->protocol);
+
+    char raw_buffer[BUFSIZ];
+    char* buffer_ptr = raw_buffer;
+    size_t buffer_size = sizeof(raw_buffer);
+    if (args->protocol != nullptr) {
+        buffer_ptr = args->protocol->data();
+        buffer_size = args->protocol->data_capacity();
+    }
+
+    // If we need to parse escape sequences, make life easy.
+    if (args->raw_stdin && args->escape_char != '\0') {
+        buffer_size = 1;
+    }
+
+    enum EscapeState { kMidFlow, kStartOfLine, kInEscape };
+    EscapeState state = kStartOfLine;
+
+    while (true) {
+        // Use unix_read() rather than adb_read() for stdin.
+        D("stdin_read_thread_loop(): pre unix_read(fdi=%d,...)", args->stdin_fd);
+#if !defined(_WIN32)
+#undef read
+        int r = read(args->stdin_fd, buffer_ptr, buffer_size);
+        if (r == -1 && errno == EINTR) {
+            send_window_size_change(args->stdin_fd, args->protocol);
+            continue;
+        }
+#define read ___xxx_read
+#else
+        int r = unix_read(args->stdin_fd, buffer_ptr, buffer_size);
+#endif
+        D("stdin_read_thread_loop(): post unix_read(fdi=%d,...)", args->stdin_fd);
+        if (r <= 0) {
+            // Only devices using the shell protocol know to close subprocess
+            // stdin. For older devices we want to just leave the connection
+            // open, otherwise an unpredictable amount of return data could
+            // be lost due to the FD closing before all data has been received.
+            if (args->protocol) {
+                args->protocol->Write(ShellProtocol::kIdCloseStdin, 0);
+            }
             break;
         }
-        for(n = 0; n < r; n++){
-            switch(buf[n]) {
-            case '\n':
-                state = 1;
-                break;
-            case '\r':
-                state = 1;
-                break;
-            case '~':
-                if(state == 1) state++;
-                break;
-            case '.':
-                if(state == 2) {
-                    fprintf(stderr,"\n* disconnect *\n");
-                    stdin_raw_restore(fdi);
-                    exit(0);
+        // If we made stdin raw, check input for escape sequences. In
+        // this situation signals like Ctrl+C are sent remotely rather than
+        // interpreted locally so this provides an emergency out if the remote
+        // process starts ignoring the signal. SSH also does this, see the
+        // "escape characters" section on the ssh man page for more info.
+        if (args->raw_stdin && args->escape_char != '\0') {
+            char ch = buffer_ptr[0];
+            if (ch == args->escape_char) {
+                if (state == kStartOfLine) {
+                    state = kInEscape;
+                    // Swallow the escape character.
+                    continue;
+                } else {
+                    state = kMidFlow;
                 }
-            default:
-                state = 0;
+            } else {
+                if (state == kInEscape) {
+                    if (ch == '.') {
+                        fprintf(stderr,"\r\n[ disconnected ]\r\n");
+                        stdin_raw_restore();
+                        exit(0);
+                    } else {
+                        // We swallowed an escape character that wasn't part of
+                        // a valid escape sequence; time to cough it up.
+                        buffer_ptr[0] = args->escape_char;
+                        buffer_ptr[1] = ch;
+                        ++r;
+                    }
+                }
+                state = (ch == '\n' || ch == '\r') ? kStartOfLine : kMidFlow;
             }
         }
-        r = adb_write(fd, buf, r);
-        if(r <= 0) {
-            break;
+        if (args->protocol) {
+            if (!args->protocol->Write(ShellProtocol::kIdStdin, r)) {
+                break;
+            }
+        } else {
+            if (!WriteFdExactly(args->write_fd, buffer_ptr, r)) {
+                break;
+            }
         }
     }
-    return 0;
+
+    return nullptr;
+}
+
+// Returns a shell service string with the indicated arguments and command.
+static std::string ShellServiceString(bool use_shell_protocol,
+                                      const std::string& type_arg,
+                                      const std::string& command) {
+    std::vector<std::string> args;
+    if (use_shell_protocol) {
+        args.push_back(kShellServiceArgShellProtocol);
+    }
+    if (!type_arg.empty()) {
+        args.push_back(type_arg);
+    }
+
+    // Shell service string can look like: shell[,arg1,arg2,...]:[command].
+    return android::base::StringPrintf("shell%s%s:%s",
+                                       args.empty() ? "" : ",",
+                                       android::base::Join(args, ',').c_str(),
+                                       command.c_str());
 }
 
-static int interactive_shell() {
-    adb_thread_t thr;
-    int fdi;
+// Connects to a shell on the device and read/writes data.
+//
+// Note: currently this function doesn't properly clean up resources; the
+// FD connected to the adb server is never closed and the stdin read thread
+// may never exit.
+//
+// On success returns the remote exit code if |use_shell_protocol| is true,
+// 0 otherwise. On failure returns 1.
+static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
+                       char escape_char,
+                       const std::string& command) {
+    std::string service_string = ShellServiceString(use_shell_protocol,
+                                                    type_arg, command);
+
+    // Make local stdin raw if the device allocates a PTY, which happens if:
+    //   1. We are explicitly asking for a PTY shell, or
+    //   2. We don't specify shell type and are starting an interactive session.
+    bool raw_stdin = (type_arg == kShellServiceArgPty ||
+                      (type_arg.empty() && command.empty()));
 
     std::string error;
-    int fd = adb_connect("shell:", &error);
+    int fd = adb_connect(service_string, &error);
     if (fd < 0) {
         fprintf(stderr,"error: %s\n", error.c_str());
         return 1;
     }
-    fdi = 0; //dup(0);
 
-    int* fds = reinterpret_cast<int*>(malloc(sizeof(int) * 2));
-    if (fds == nullptr) {
-        fprintf(stderr, "couldn't allocate fds array: %s\n", strerror(errno));
+    StdinReadArgs* args = new StdinReadArgs;
+    if (!args) {
+        LOG(ERROR) << "couldn't allocate StdinReadArgs object";
         return 1;
     }
+    args->stdin_fd = STDIN_FILENO;
+    args->write_fd = fd;
+    args->raw_stdin = raw_stdin;
+    args->escape_char = escape_char;
+    if (use_shell_protocol) {
+        args->protocol.reset(new ShellProtocol(args->write_fd));
+    }
 
-    fds[0] = fd;
-    fds[1] = fdi;
+    if (raw_stdin) stdin_raw_init();
 
-    stdin_raw_init(fdi);
-    adb_thread_create(&thr, stdin_read_thread, fds);
-    read_and_dump(fd);
-    stdin_raw_restore(fdi);
-    return 0;
+#if !defined(_WIN32)
+    // Ensure our process is notified if the local window size changes.
+    // We use sigaction(2) to ensure that the SA_RESTART flag is not set,
+    // because the whole reason we're sending signals is to unblock the read(2)!
+    // That also means we don't need to do anything in the signal handler:
+    // the side effect of delivering the signal is all we need.
+    struct sigaction sa;
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = [](int) {};
+    sa.sa_flags = 0;
+    sigaction(SIGWINCH, &sa, nullptr);
+
+    // Now block SIGWINCH in this thread (the main thread) and all threads spawned
+    // from it. The stdin read thread will unblock this signal to ensure that it's
+    // the thread that receives the signal.
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGWINCH);
+    pthread_sigmask(SIG_BLOCK, &mask, nullptr);
+#endif
+
+    // TODO: combine read_and_dump with stdin_read_thread to make life simpler?
+    int exit_code = 1;
+    if (!adb_thread_create(stdin_read_thread_loop, args)) {
+        PLOG(ERROR) << "error starting stdin read thread";
+        delete args;
+    } else {
+        exit_code = read_and_dump(fd, use_shell_protocol);
+    }
+
+    // TODO: properly exit stdin_read_thread_loop and close |fd|.
+
+    // TODO: we should probably install signal handlers for this.
+    // TODO: can we use atexit? even on Windows?
+    if (raw_stdin) stdin_raw_restore();
+
+    return exit_code;
 }
 
+static int adb_shell(int argc, const char** argv,
+                     TransportType transport_type, const char* serial) {
+    FeatureSet features = GetFeatureSet(transport_type, serial);
 
-static std::string format_host_command(const char* command, transport_type type, const char* serial) {
-    if (serial) {
-        return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+    bool use_shell_protocol = CanUseFeature(features, kFeatureShell2);
+    if (!use_shell_protocol) {
+        D("shell protocol not supported, using raw data transfer");
+    } else {
+        D("using shell protocol");
     }
 
-    const char* prefix = "host";
-    if (type == kTransportUsb) {
-        prefix = "host-usb";
-    } else if (type == kTransportLocal) {
-        prefix = "host-local";
+    // Parse shell-specific command-line options.
+    // argv[0] is always "shell".
+    --argc;
+    ++argv;
+    int t_arg_count = 0;
+    char escape_char = '~';
+    while (argc) {
+        if (!strcmp(argv[0], "-e")) {
+            if (argc < 2 || !(strlen(argv[1]) == 1 || strcmp(argv[1], "none") == 0)) {
+                fprintf(stderr, "error: -e requires a single-character argument or 'none'\n");
+                return 1;
+            }
+            escape_char = (strcmp(argv[1], "none") == 0) ? 0 : argv[1][0];
+            argc -= 2;
+            argv += 2;
+        } else if (!strcmp(argv[0], "-T") || !strcmp(argv[0], "-t")) {
+            if (!CanUseFeature(features, kFeatureShell2)) {
+                fprintf(stderr, "error: target doesn't support PTY args -Tt\n");
+                return 1;
+            }
+            // Like ssh, -t arguments are cumulative so that multiple -t's
+            // are needed to force a PTY.
+            if (argv[0][1] == 't') {
+                ++t_arg_count;
+            } else {
+                t_arg_count = -1;
+            }
+            --argc;
+            ++argv;
+        } else if (!strcmp(argv[0], "-x")) {
+            use_shell_protocol = false;
+            --argc;
+            ++argv;
+        } else {
+            break;
+        }
     }
-    return android::base::StringPrintf("%s:%s", prefix, command);
+
+    std::string shell_type_arg;
+    if (CanUseFeature(features, kFeatureShell2)) {
+        if (t_arg_count < 0) {
+            shell_type_arg = kShellServiceArgRaw;
+        } else if (t_arg_count == 0) {
+            // If stdin isn't a TTY, default to a raw shell; this lets
+            // things like `adb shell < my_script.sh` work as expected.
+            // Otherwise leave |shell_type_arg| blank which uses PTY for
+            // interactive shells and raw for non-interactive.
+            if (!unix_isatty(STDIN_FILENO)) {
+                shell_type_arg = kShellServiceArgRaw;
+            }
+        } else if (t_arg_count == 1) {
+            // A single -t arg isn't enough to override implicit -T.
+            if (!unix_isatty(STDIN_FILENO)) {
+                fprintf(stderr,
+                        "Remote PTY will not be allocated because stdin is not a terminal.\n"
+                        "Use multiple -t options to force remote PTY allocation.\n");
+                shell_type_arg = kShellServiceArgRaw;
+            } else {
+                shell_type_arg = kShellServiceArgPty;
+            }
+        } else {
+            shell_type_arg = kShellServiceArgPty;
+        }
+    }
+
+    std::string command;
+    if (argc) {
+        // We don't escape here, just like ssh(1). http://b/20564385.
+        command = android::base::Join(std::vector<const char*>(argv, argv + argc), ' ');
+    }
+
+    return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
 }
 
 static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz,
@@ -463,8 +786,8 @@ static int adb_download_buffer(const char *service, const char *fn, const void*
     const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
 
     if (show_progress) {
-        char *x = strrchr(service, ':');
-        if(x) service = x + 1;
+        const char* x = strrchr(service, ':');
+        if (x) service = x + 1;
     }
 
     while (sz > 0) {
@@ -473,6 +796,7 @@ static int adb_download_buffer(const char *service, const char *fn, const void*
             std::string error;
             adb_status(fd, &error);
             fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
+            adb_close(fd);
             return -1;
         }
         sz -= xfer;
@@ -488,6 +812,7 @@ static int adb_download_buffer(const char *service, const char *fn, const void*
 
     if (!adb_status(fd, &error)) {
         fprintf(stderr,"* error response '%s' *\n", error.c_str());
+        adb_close(fd);
         return -1;
     }
 
@@ -674,7 +999,7 @@ static int ppp(int argc, const char** argv) {
 #endif /* !defined(_WIN32) */
 }
 
-static bool wait_for_device(const char* service, transport_type t, const char* serial) {
+static bool wait_for_device(const char* service, TransportType t, const char* serial) {
     // Was the caller vague about what they'd like us to wait for?
     // If so, check they weren't more specific in their choice of transport type.
     if (strcmp(service, "wait-for-device") == 0) {
@@ -688,22 +1013,29 @@ static bool wait_for_device(const char* service, transport_type t, const char* s
     }
 
     std::string cmd = format_host_command(service, t, serial);
-    std::string error;
-    if (adb_command(cmd, &error)) {
-        D("failure: %s *\n", error.c_str());
-        fprintf(stderr,"error: %s\n", error.c_str());
-        return false;
+    return adb_command(cmd);
+}
+
+// Connects to the device "shell" service with |command| and prints the
+// resulting output.
+static int send_shell_command(TransportType transport_type, const char* serial,
+                              const std::string& command,
+                              bool disable_shell_protocol) {
+    // Only use shell protocol if it's supported and the caller doesn't want
+    // to explicitly disable it.
+    bool use_shell_protocol = false;
+    if (!disable_shell_protocol) {
+        FeatureSet features = GetFeatureSet(transport_type, serial);
+        use_shell_protocol = CanUseFeature(features, kFeatureShell2);
     }
 
-    return true;
-}
+    std::string service_string = ShellServiceString(use_shell_protocol, "",
+                                                    command);
 
-static int send_shell_command(transport_type transport_type, const char* serial,
-                              const std::string& command) {
     int fd;
     while (true) {
         std::string error;
-        fd = adb_connect(command, &error);
+        fd = adb_connect(service_string, &error);
         if (fd >= 0) {
             break;
         }
@@ -712,19 +1044,20 @@ static int send_shell_command(transport_type transport_type, const char* serial,
         wait_for_device("wait-for-device", transport_type, serial);
     }
 
-    read_and_dump(fd);
-    int rc = adb_close(fd);
-    if (rc) {
-        perror("close");
+    int exit_code = read_and_dump(fd, use_shell_protocol);
+
+    if (adb_close(fd) < 0) {
+        PLOG(ERROR) << "failure closing FD " << fd;
     }
-    return rc;
+
+    return exit_code;
 }
 
-static int logcat(transport_type transport, const char* serial, int argc, const char** argv) {
+static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
     char* log_tags = getenv("ANDROID_LOG_TAGS");
     std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
 
-    std::string cmd = "shell:export ANDROID_LOG_TAGS=\"" + quoted + "\"; exec logcat";
+    std::string cmd = "export ANDROID_LOG_TAGS=\"" + quoted + "\"; exec logcat";
 
     if (!strcmp(argv[0], "longcat")) {
         cmd += " -v long";
@@ -736,28 +1069,8 @@ static int logcat(transport_type transport, const char* serial, int argc, const
         cmd += " " + escape_arg(*argv++);
     }
 
-    return send_shell_command(transport, serial, cmd);
-}
-
-static int mkdirs(const char *path)
-{
-    std::string holder(path);
-
-    int ret;
-    char *x = &holder[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;
+    // No need for shell protocol with logcat, always disable for simplicity.
+    return send_shell_command(transport, serial, cmd, true);
 }
 
 static int backup(int argc, const char** argv) {
@@ -767,8 +1080,8 @@ static int backup(int argc, const char** argv) {
     for (int i = 1; i < argc; i++) {
         if (!strcmp("-f", argv[i])) {
             if (i == argc-1) {
-                fprintf(stderr, "adb: -f passed with no filename\n");
-                return usage();
+                fprintf(stderr, "adb: backup -f passed with no filename.\n");
+                return EXIT_FAILURE;
             }
             filename = argv[i+1];
             for (int j = i+2; j <= argc; ) {
@@ -779,15 +1092,18 @@ static int backup(int argc, const char** argv) {
         }
     }
 
-    /* bare "adb backup" or "adb backup -f filename" are not valid invocations */
-    if (argc < 2) return usage();
+    // Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
+    // a list of packages is required.
+    if (argc < 2) {
+        fprintf(stderr, "adb: backup either needs a list of packages or -all/-shared.\n");
+        return EXIT_FAILURE;
+    }
 
     adb_unlink(filename);
-    mkdirs(filename);
     int outFd = adb_creat(filename, 0640);
     if (outFd < 0) {
-        fprintf(stderr, "adb: unable to open file %s\n", filename);
-        return -1;
+        fprintf(stderr, "adb: backup unable to create file '%s': %s\n", filename, strerror(errno));
+        return EXIT_FAILURE;
     }
 
     std::string cmd = "backup:";
@@ -797,21 +1113,23 @@ static int backup(int argc, const char** argv) {
         cmd += " " + escape_arg(*argv++);
     }
 
-    D("backup. filename=%s cmd=%s\n", filename, cmd.c_str());
+    D("backup. filename=%s cmd=%s", filename, cmd.c_str());
     std::string error;
     int fd = adb_connect(cmd, &error);
     if (fd < 0) {
         fprintf(stderr, "adb: unable to connect for backup: %s\n", error.c_str());
         adb_close(outFd);
-        return -1;
+        return EXIT_FAILURE;
     }
 
-    printf("Now unlock your device and confirm the backup operation.\n");
+    printf("Now unlock your device and confirm the backup operation...\n");
+    fflush(stdout);
+
     copy_to_file(fd, outFd);
 
     adb_close(fd);
     adb_close(outFd);
-    return 0;
+    return EXIT_SUCCESS;
 }
 
 static int restore(int argc, const char** argv) {
@@ -851,25 +1169,25 @@ static int restore(int argc, const char** argv) {
  * Given <hint>, try to construct an absolute path to the
  * ANDROID_PRODUCT_OUT dir.
  */
-static std::string find_product_out_path(const char* hint) {
-    if (hint == NULL || hint[0] == '\0') {
+static std::string find_product_out_path(const std::string& hint) {
+    if (hint.empty()) {
         return "";
     }
 
     // If it's already absolute, don't bother doing any work.
-    if (adb_is_absolute_host_path(hint)) {
+    if (adb_is_absolute_host_path(hint.c_str())) {
         return hint;
     }
 
     // If there are any slashes in it, assume it's a relative path;
     // make it absolute.
-    if (adb_dirstart(hint) != nullptr) {
+    if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) {
         std::string cwd;
         if (!getcwd(&cwd)) {
             fprintf(stderr, "adb: getcwd failed: %s\n", strerror(errno));
             return "";
         }
-        return android::base::StringPrintf("%s%s%s", cwd.c_str(), OS_PATH_SEPARATOR_STR, hint);
+        return android::base::StringPrintf("%s%c%s", cwd.c_str(), OS_PATH_SEPARATOR, hint.c_str());
     }
 
     // It's a string without any slashes.  Try to do something with it.
@@ -893,38 +1211,41 @@ static std::string find_product_out_path(const char* hint) {
     path += hint;
     if (!directory_exists(path)) {
         fprintf(stderr, "adb: Couldn't find a product dir based on -p %s; "
-                        "\"%s\" doesn't exist\n", hint, path.c_str());
+                        "\"%s\" doesn't exist\n", hint.c_str(), path.c_str());
         return "";
     }
     return path;
 }
 
-static void parse_push_pull_args(const char **arg, int narg, char const **path1,
-                                 char const **path2, int *show_progress,
-                                 int *copy_attrs) {
-    *show_progress = 0;
-    *copy_attrs = 0;
+static void parse_push_pull_args(const char** arg, int narg,
+                                 std::vector<const char*>* srcs,
+                                 const char** dst, bool* copy_attrs) {
+    *copy_attrs = false;
 
+    srcs->clear();
+    bool ignore_flags = false;
     while (narg > 0) {
-        if (!strcmp(*arg, "-p")) {
-            *show_progress = 1;
-        } else if (!strcmp(*arg, "-a")) {
-            *copy_attrs = 1;
+        if (ignore_flags || *arg[0] != '-') {
+            srcs->push_back(*arg);
         } else {
-            break;
+            if (!strcmp(*arg, "-p")) {
+                // Silently ignore for backwards compatibility.
+            } else if (!strcmp(*arg, "-a")) {
+                *copy_attrs = true;
+            } else if (!strcmp(*arg, "--")) {
+                ignore_flags = true;
+            } else {
+                fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+                exit(1);
+            }
         }
         ++arg;
         --narg;
     }
 
-    if (narg > 0) {
-        *path1 = *arg;
-        ++arg;
-        --narg;
-    }
-
-    if (narg > 0) {
-        *path2 = *arg;
+    if (srcs->size() > 1) {
+        *dst = srcs->back();
+        srcs->pop_back();
     }
 }
 
@@ -951,15 +1272,25 @@ static int adb_query_command(const std::string& command) {
     return 0;
 }
 
+// Disallow stdin, stdout, and stderr.
+static bool _is_valid_ack_reply_fd(const int ack_reply_fd) {
+#ifdef _WIN32
+    const HANDLE ack_reply_handle = cast_int_to_handle(ack_reply_fd);
+    return (GetStdHandle(STD_INPUT_HANDLE) != ack_reply_handle) &&
+           (GetStdHandle(STD_OUTPUT_HANDLE) != ack_reply_handle) &&
+           (GetStdHandle(STD_ERROR_HANDLE) != ack_reply_handle);
+#else
+    return ack_reply_fd > 2;
+#endif
+}
+
 int adb_commandline(int argc, const char **argv) {
     int no_daemon = 0;
     int is_daemon = 0;
     int is_server = 0;
-    int persist = 0;
     int r;
-    transport_type ttype = kTransportAny;
-    const char* serial = NULL;
-    const char* server_port_str = NULL;
+    TransportType transport_type = kTransportAny;
+    int ack_reply_fd = -1;
 
     // If defined, this should be an absolute path to
     // the directory containing all of the various system images
@@ -972,22 +1303,22 @@ int adb_commandline(int argc, const char **argv) {
     }
     // 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");
+    const char* server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
     int server_port = DEFAULT_ADB_PORT;
     if (server_port_str && strlen(server_port_str) > 0) {
-        server_port = (int) strtol(server_port_str, NULL, 0);
+        server_port = strtol(server_port_str, nullptr, 0);
         if (server_port <= 0 || server_port > 65535) {
             fprintf(stderr,
-                    "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number less than 65535. Got \"%s\"\n",
+                    "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number less than 65536. Got \"%s\"\n",
                     server_port_str);
             return usage();
         }
     }
 
-    /* modifiers and flags */
+    // We need to check for -d and -e before we look at $ANDROID_SERIAL.
+    const char* serial = nullptr;
+
     while (argc > 0) {
         if (!strcmp(argv[0],"server")) {
             is_server = 1;
@@ -996,10 +1327,18 @@ int adb_commandline(int argc, const char **argv) {
         } 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 (!strcmp(argv[0], "--reply-fd")) {
+            if (argc < 2) return usage();
+            const char* reply_fd_str = argv[1];
+            argc--;
+            argv++;
+            ack_reply_fd = strtol(reply_fd_str, nullptr, 10);
+            if (!_is_valid_ack_reply_fd(ack_reply_fd)) {
+                fprintf(stderr, "adb: invalid reply fd \"%s\"\n", reply_fd_str);
+                return usage();
+            }
         } else if (!strncmp(argv[0], "-p", 2)) {
-            const char *product = NULL;
+            const char* product = nullptr;
             if (argv[0][2] == '\0') {
                 if (argc < 2) return usage();
                 product = argv[1];
@@ -1023,9 +1362,9 @@ int adb_commandline(int argc, const char **argv) {
                 argv++;
             }
         } else if (!strcmp(argv[0],"-d")) {
-            ttype = kTransportUsb;
+            transport_type = kTransportUsb;
         } else if (!strcmp(argv[0],"-e")) {
-            ttype = kTransportLocal;
+            transport_type = kTransportLocal;
         } else if (!strcmp(argv[0],"-a")) {
             gListenAll = 1;
         } else if (!strncmp(argv[0], "-H", 2)) {
@@ -1070,12 +1409,21 @@ int adb_commandline(int argc, const char **argv) {
         argv++;
     }
 
-    adb_set_transport(ttype, serial);
+    // If none of -d, -e, or -s were specified, try $ANDROID_SERIAL.
+    if (transport_type == kTransportAny && serial == nullptr) {
+        serial = getenv("ANDROID_SERIAL");
+    }
+
+    adb_set_transport(transport_type, serial);
     adb_set_tcp_specifics(server_port);
 
     if (is_server) {
         if (no_daemon || is_daemon) {
-            r = adb_main(is_daemon, server_port);
+            if (is_daemon && (ack_reply_fd == -1)) {
+                fprintf(stderr, "reply fd for adb server to client communication not specified.\n");
+                return usage();
+            }
+            r = adb_main(is_daemon, server_port, ack_reply_fd);
         } else {
             r = launch_server(server_port);
         }
@@ -1093,7 +1441,7 @@ int adb_commandline(int argc, const char **argv) {
     if (!strncmp(argv[0], "wait-for-", strlen("wait-for-"))) {
         const char* service = argv[0];
 
-        if (!wait_for_device(service, ttype, serial)) {
+        if (!wait_for_device(service, transport_type, serial)) {
             return 1;
         }
 
@@ -1144,64 +1492,10 @@ int adb_commandline(int argc, const char **argv) {
         return adb_query_command(query);
     }
     else if (!strcmp(argv[0], "emu")) {
-        return adb_send_emulator_command(argc, argv);
+        return adb_send_emulator_command(argc, argv, serial);
     }
-    else if (!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) {
-        char h = (argv[0][0] == 'h');
-
-        if (h) {
-            printf("\x1b[41;33m");
-            fflush(stdout);
-        }
-
-        if (argc < 2) {
-            D("starting interactive shell\n");
-            r = interactive_shell();
-            if (h) {
-                printf("\x1b[0m");
-                fflush(stdout);
-            }
-            return r;
-        }
-
-        std::string cmd = "shell:";
-        --argc;
-        ++argv;
-        while (argc-- > 0) {
-            // We don't escape here, just like ssh(1). http://b/20564385.
-            cmd += *argv++;
-            if (*argv) cmd += " ";
-        }
-
-        while (true) {
-            D("interactive shell loop. cmd=%s\n", cmd.c_str());
-            std::string error;
-            int fd = adb_connect(cmd, &error);
-            int r;
-            if (fd >= 0) {
-                D("about to read_and_dump(fd=%d)\n", fd);
-                read_and_dump(fd);
-                D("read_and_dump() done.\n");
-                adb_close(fd);
-                r = 0;
-            } else {
-                fprintf(stderr,"error: %s\n", error.c_str());
-                r = -1;
-            }
-
-            if (persist) {
-                fprintf(stderr,"\n- waiting for device -\n");
-                adb_sleep_ms(1000);
-                wait_for_device("wait-for-device", ttype, serial);
-            } else {
-                if (h) {
-                    printf("\x1b[0m");
-                    fflush(stdout);
-                }
-                D("interactive shell loop. return r=%d\n", r);
-                return r;
-            }
-        }
+    else if (!strcmp(argv[0], "shell")) {
+        return adb_shell(argc, argv, transport_type, serial);
     }
     else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
         int exec_in = !strcmp(argv[0], "exec-in");
@@ -1233,11 +1527,23 @@ int adb_commandline(int argc, const char **argv) {
     else if (!strcmp(argv[0], "kill-server")) {
         std::string error;
         int fd = _adb_connect("host:kill", &error);
-        if (fd == -1) {
+        if (fd == -2) {
+            // Failed to make network connection to server. Don't output the
+            // network error since that is expected.
             fprintf(stderr,"* server not running *\n");
+            // Successful exit code because the server is already "killed".
+            return 0;
+        } else if (fd == -1) {
+            // Some other error.
+            fprintf(stderr, "error: %s\n", error.c_str());
             return 1;
+        } else {
+            // Successfully connected, kill command sent, okay status came back.
+            // Server should exit() in a moment, if not already.
+            ReadOrderlyShutdown(fd);
+            adb_close(fd);
+            return 0;
         }
-        return 0;
     }
     else if (!strcmp(argv[0], "sideload")) {
         if (argc != 2) return usage();
@@ -1247,10 +1553,12 @@ int adb_commandline(int argc, const char **argv) {
             return 0;
         }
     }
+    else if (!strcmp(argv[0], "tcpip") && argc > 1) {
+        return adb_connect_command(android::base::StringPrintf("tcpip:%s", argv[1]));
+    }
     else 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") ||
              !strcmp(argv[0], "unroot") ||
@@ -1268,135 +1576,97 @@ int adb_commandline(int argc, const char **argv) {
     }
     else if (!strcmp(argv[0], "bugreport")) {
         if (argc != 1) return usage();
-        return send_shell_command(ttype, serial, "shell:bugreport");
+        // No need for shell protocol with bugreport, always disable for
+        // simplicity.
+        return send_shell_command(transport_type, serial, "bugreport", true);
     }
-    /* adb_command() wrapper commands */
     else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
-        std::string cmd;
-        char host_prefix[64];
-        char reverse = (char) !strcmp(argv[0], "reverse");
-        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();
-        }
+        bool reverse = !strcmp(argv[0], "reverse");
+        ++argv;
+        --argc;
+        if (argc < 1) return usage();
 
         // Determine the <host-prefix> for this command.
+        std::string host_prefix;
         if (reverse) {
-            snprintf(host_prefix, sizeof host_prefix, "reverse");
+            host_prefix = "reverse";
         } else {
             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");
+                host_prefix = android::base::StringPrintf("host-serial:%s", serial);
+            } else if (transport_type == kTransportUsb) {
+                host_prefix = "host-usb";
+            } else if (transport_type == kTransportLocal) {
+                host_prefix = "host-local";
             } else {
-                snprintf(host_prefix, sizeof host_prefix, "host");
-            }
-        }
-
-        // Implement forward --list
-        if (list) {
-            if (argc != 1) {
-                return usage();
+                host_prefix = "host";
             }
-
-            std::string query = android::base::StringPrintf("%s:list-forward", host_prefix);
-            return adb_query_command(query);
         }
 
-        // Implement forward --remove-all
-        else if (remove_all) {
+        std::string cmd;
+        if (strcmp(argv[0], "--list") == 0) {
             if (argc != 1) return usage();
-            cmd = android::base::StringPrintf("%s:killforward-all", host_prefix);
-        }
-
-        // Implement forward --remove <local>
-        else if (remove) {
+            return adb_query_command(host_prefix + ":list-forward");
+        } else if (strcmp(argv[0], "--remove-all") == 0) {
+            if (argc != 1) return usage();
+            cmd = host_prefix + ":killforward-all";
+        } else if (strcmp(argv[0], "--remove") == 0) {
+            // forward --remove <local>
             if (argc != 2) return usage();
-            cmd = android::base::StringPrintf("%s:killforward:%s", host_prefix, argv[1]);
-        }
-        // Or implement one of:
-        //    forward <local> <remote>
-        //    forward --no-rebind <local> <remote>
-        else {
+            cmd = host_prefix + ":killforward:" + argv[1];
+        } else if (strcmp(argv[0], "--no-rebind") == 0) {
+            // forward --no-rebind <local> <remote>
             if (argc != 3) return usage();
-            const char* command = no_rebind ? "forward:norebind" : "forward";
-            cmd = android::base::StringPrintf("%s:%s:%s;%s", host_prefix, command, argv[1], argv[2]);
+            cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
+        } else {
+            // forward <local> <remote>
+            if (argc != 2) return usage();
+            cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
         }
 
-        std::string error;
-        if (adb_command(cmd, &error)) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
-        }
-        return 0;
+        return adb_command(cmd) ? 0 : 1;
     }
     /* do_sync_*() commands */
     else if (!strcmp(argv[0], "ls")) {
         if (argc != 2) return usage();
-        return do_sync_ls(argv[1]);
+        return do_sync_ls(argv[1]) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "push")) {
-        int show_progress = 0;
-        int copy_attrs = 0; // unused
-        const char* lpath = NULL, *rpath = NULL;
+        bool copy_attrs = false;
+        std::vector<const char*> srcs;
+        const char* dst = nullptr;
 
-        parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, &copy_attrs);
-
-        if ((lpath != NULL) && (rpath != NULL)) {
-            return do_sync_push(lpath, rpath, show_progress);
-        }
-
-        return usage();
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
+        if (srcs.empty() || !dst) return usage();
+        return do_sync_push(srcs, dst) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "pull")) {
-        int show_progress = 0;
-        int copy_attrs = 0;
-        const char* rpath = NULL, *lpath = ".";
-
-        parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress, &copy_attrs);
-
-        if (rpath != NULL) {
-            return do_sync_pull(rpath, lpath, show_progress, copy_attrs);
-        }
+        bool copy_attrs = false;
+        std::vector<const char*> srcs;
+        const char* dst = ".";
 
-        return usage();
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
+        if (srcs.empty()) return usage();
+        return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "install")) {
         if (argc < 2) return usage();
-        return install_app(ttype, serial, argc, argv);
+        FeatureSet features = GetFeatureSet(transport_type, serial);
+        if (CanUseFeature(features, kFeatureCmd)) {
+            return install_app(transport_type, serial, argc, argv);
+        }
+        return install_app_legacy(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
         if (argc < 2) return usage();
-        return install_multiple_app(ttype, serial, argc, argv);
+        return install_multiple_app(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) return usage();
-        return uninstall_app(ttype, serial, argc, argv);
+        FeatureSet features = GetFeatureSet(transport_type, serial);
+        if (CanUseFeature(features, kFeatureCmd)) {
+            return uninstall_app(transport_type, serial, argc, argv);
+        }
+        return uninstall_app_legacy(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "sync")) {
         std::string src;
@@ -1428,38 +1698,42 @@ int adb_commandline(int argc, const char **argv) {
         std::string vendor_src_path = product_file("vendor");
         std::string oem_src_path = product_file("oem");
 
-        int rc = 0;
-        if (rc == 0 && (src.empty() || src == "system")) {
-            rc = do_sync_sync(system_src_path, "/system", list_only);
+        bool okay = true;
+        if (okay && (src.empty() || src == "system")) {
+            okay = do_sync_sync(system_src_path, "/system", list_only);
         }
-        if (rc == 0 && (src.empty() || src == "vendor") && directory_exists(vendor_src_path)) {
-            rc = do_sync_sync(vendor_src_path, "/vendor", list_only);
+        if (okay && (src.empty() || src == "vendor") && directory_exists(vendor_src_path)) {
+            okay = do_sync_sync(vendor_src_path, "/vendor", list_only);
         }
-        if (rc == 0 && (src.empty() || src == "oem") && directory_exists(oem_src_path)) {
-            rc = do_sync_sync(oem_src_path, "/oem", list_only);
+        if (okay && (src.empty() || src == "oem") && directory_exists(oem_src_path)) {
+            okay = do_sync_sync(oem_src_path, "/oem", list_only);
         }
-        if (rc == 0 && (src.empty() || src == "data")) {
-            rc = do_sync_sync(data_src_path, "/data", list_only);
+        if (okay && (src.empty() || src == "data")) {
+            okay = do_sync_sync(data_src_path, "/data", list_only);
         }
-        return rc;
+        return okay ? 0 : 1;
     }
     /* passthrough commands */
     else if (!strcmp(argv[0],"get-state") ||
         !strcmp(argv[0],"get-serialno") ||
         !strcmp(argv[0],"get-devpath"))
     {
-        return adb_query_command(format_host_command(argv[0], ttype, serial));
+        return adb_query_command(format_host_command(argv[0], transport_type, serial));
     }
     /* other commands */
     else if (!strcmp(argv[0],"logcat") || !strcmp(argv[0],"lolcat") || !strcmp(argv[0],"longcat")) {
-        return logcat(ttype, serial, argc, argv);
+        return logcat(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0],"ppp")) {
         return ppp(argc, argv);
     }
     else if (!strcmp(argv[0], "start-server")) {
         std::string error;
-        return adb_connect("host:start-server", &error);
+        const int result = adb_connect("host:start-server", &error);
+        if (result < 0) {
+            fprintf(stderr, "error: %s\n", error.c_str());
+        }
+        return result;
     }
     else if (!strcmp(argv[0], "backup")) {
         return backup(argc, argv);
@@ -1469,6 +1743,8 @@ int adb_commandline(int argc, const char **argv) {
     }
     else if (!strcmp(argv[0], "keygen")) {
         if (argc < 2) return usage();
+        // Always print key generation information for keygen command.
+        adb_trace_enable(AUTH);
         return adb_auth_keygen(argv[1]);
     }
     else if (!strcmp(argv[0], "jdwp")) {
@@ -1480,7 +1756,17 @@ int adb_commandline(int argc, const char **argv) {
         return 0;
     }
     else if (!strcmp(argv[0], "version")) {
-        version(stdout);
+        fprintf(stdout, "%s", adb_version().c_str());
+        return 0;
+    }
+    else if (!strcmp(argv[0], "features")) {
+        // Only list the features common to both the adb client and the device.
+        FeatureSet features = GetFeatureSet(transport_type, serial);
+        for (const std::string& name : features) {
+            if (CanUseFeature(features, name)) {
+                printf("%s\n", name.c_str());
+            }
+        }
         return 0;
     }
 
@@ -1488,109 +1774,86 @@ int adb_commandline(int argc, const char **argv) {
     return 1;
 }
 
-static int pm_command(transport_type transport, const char* serial,
-                      int argc, const char** argv)
-{
-    std::string cmd = "shell:pm";
-
+static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
+    // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
+    std::string cmd = "cmd package";
     while (argc-- > 0) {
+        // deny the '-k' option until the remaining data/cache can be removed with adb/UI
+        if (strcmp(*argv, "-k") == 0) {
+            printf(
+                "The -k option uninstalls the application while retaining the data/cache.\n"
+                "At the moment, there is no way to remove the remaining data.\n"
+                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+                "If you truly wish to continue, execute 'adb shell cmd package uninstall -k'.\n");
+            return EXIT_FAILURE;
+        }
         cmd += " " + escape_arg(*argv++);
     }
 
-    return send_shell_command(transport, serial, cmd);
+    return send_shell_command(transport, serial, cmd, false);
 }
 
-static int uninstall_app(transport_type transport, const char* serial, int argc,
-                         const char** argv)
-{
-    /* if the user choose the -k option, we refuse to do it until devices are
-       out with the option to uninstall the remaining data somehow (adb/ui) */
-    if (argc == 3 && strcmp(argv[1], "-k") == 0)
-    {
-        printf(
-            "The -k option uninstalls the application while retaining the data/cache.\n"
-            "At the moment, there is no way to remove the remaining data.\n"
-            "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
-            "If you truly wish to continue, execute 'adb shell pm uninstall -k %s'\n", argv[2]);
-        return -1;
+static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
+    // The last argument must be the APK file
+    const char* file = argv[argc - 1];
+    const char* dot = strrchr(file, '.');
+    bool found_apk = false;
+    struct stat sb;
+    if (dot && !strcasecmp(dot, ".apk")) {
+        if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+            fprintf(stderr, "Invalid APK file: %s\n", file);
+            return EXIT_FAILURE;
+        }
+        found_apk = true;
     }
 
-    /* '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, const char* serial, char* filename)
-{
-    std::string cmd = "shell:rm -f " + escape_arg(filename);
-    return send_shell_command(transport, serial, cmd);
-}
-
-static const char* get_basename(const char* filename)
-{
-    const char* basename = adb_dirstop(filename);
-    if (basename) {
-        basename++;
-        return basename;
-    } else {
-        return filename;
+    if (!found_apk) {
+        fprintf(stderr, "Missing APK file\n");
+        return EXIT_FAILURE;
     }
-}
 
-static int install_app(transport_type transport, const char* serial, int argc,
-                       const char** argv)
-{
-    static const char *const DATA_DEST = "/data/local/tmp/%s";
-    static const char *const SD_DEST = "/sdcard/tmp/%s";
-    const char* where = DATA_DEST;
-    int i;
-    struct stat sb;
-
-    for (i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "-s")) {
-            where = SD_DEST;
-        }
+    int localFd = adb_open(file, O_RDONLY);
+    if (localFd < 0) {
+        fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+        return 1;
     }
 
-    // Find last APK argument.
-    // All other arguments passed through verbatim.
-    int last_apk = -1;
-    for (i = argc - 1; i >= 0; i--) {
-        const char* file = argv[i];
-        char* dot = strrchr(file, '.');
-        if (dot && !strcasecmp(dot, ".apk")) {
-            if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
-                fprintf(stderr, "Invalid APK file: %s\n", file);
-                return -1;
-            }
+    std::string error;
+    std::string cmd = "exec:cmd package";
 
-            last_apk = i;
-            break;
-        }
+    // don't copy the APK name, but, copy the rest of the arguments as-is
+    while (argc-- > 1) {
+        cmd += " " + escape_arg(std::string(*argv++));
     }
 
-    if (last_apk == -1) {
-        fprintf(stderr, "Missing APK file\n");
-        return -1;
-    }
+    // add size parameter [required for streaming installs]
+    // do last to override any user specified value
+    cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
 
-    const char* apk_file = argv[last_apk];
-    char apk_dest[PATH_MAX];
-    snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
-    int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);
-    if (err) {
-        goto cleanup_apk;
-    } else {
-        argv[last_apk] = apk_dest; /* destination name, not source location */
+    int remoteFd = adb_connect(cmd, &error);
+    if (remoteFd < 0) {
+        fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+        adb_close(localFd);
+        return 1;
     }
 
-    err = pm_command(transport, serial, argc, argv);
+    char buf[BUFSIZ];
+    copy_to_file(localFd, remoteFd);
+    read_status_line(remoteFd, buf, sizeof(buf));
 
-cleanup_apk:
-    delete_file(transport, serial, apk_dest);
-    return err;
+    adb_close(localFd);
+    adb_close(remoteFd);
+
+    if (strncmp("Success", buf, 7)) {
+        fprintf(stderr, "Failed to write %s\n", file);
+        fputs(buf, stderr);
+        return 1;
+    }
+    fputs(buf, stderr);
+    return 0;
 }
 
-static int install_multiple_app(transport_type transport, const char* serial, int argc,
+static int install_multiple_app(TransportType transport, const char* serial, int argc,
                                 const char** argv)
 {
     int i;
@@ -1602,11 +1865,11 @@ static int install_multiple_app(transport_type transport, const char* serial, in
     int first_apk = -1;
     for (i = argc - 1; i >= 0; i--) {
         const char* file = argv[i];
-        char* dot = strrchr(file, '.');
+        const char* dot = strrchr(file, '.');
         if (dot && !strcasecmp(dot, ".apk")) {
             if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
                 fprintf(stderr, "Invalid APK file: %s\n", file);
-                return -1;
+                return EXIT_FAILURE;
             }
 
             total_size += sb.st_size;
@@ -1621,11 +1884,7 @@ static int install_multiple_app(transport_type transport, const char* serial, in
         return 1;
     }
 
-#if defined(_WIN32) // Remove when we're using clang for Win32.
-    std::string cmd = android::base::StringPrintf("exec:pm install-create -S %u", (unsigned) total_size);
-#else
     std::string cmd = android::base::StringPrintf("exec:pm install-create -S %" PRIu64, total_size);
-#endif
     for (i = 1; i < first_apk; i++) {
         cmd += " " + escape_arg(argv[i]);
     }
@@ -1635,7 +1894,7 @@ static int install_multiple_app(transport_type transport, const char* serial, in
     int fd = adb_connect(cmd, &error);
     if (fd < 0) {
         fprintf(stderr, "Connect error for create: %s\n", error.c_str());
-        return -1;
+        return EXIT_FAILURE;
     }
     char buf[BUFSIZ];
     read_status_line(fd, buf, sizeof(buf));
@@ -1653,7 +1912,7 @@ static int install_multiple_app(transport_type transport, const char* serial, in
     if (session_id < 0) {
         fprintf(stderr, "Failed to create session\n");
         fputs(buf, stderr);
-        return -1;
+        return EXIT_FAILURE;
     }
 
     // Valid session, now stream the APKs
@@ -1666,15 +1925,9 @@ static int install_multiple_app(transport_type transport, const char* serial, in
             goto finalize_session;
         }
 
-#if defined(_WIN32) // Remove when we're using clang for Win32.
-        std::string cmd = android::base::StringPrintf(
-                "exec:pm install-write -S %u %d %d_%s -",
-                (unsigned) sb.st_size, session_id, i, get_basename(file));
-#else
         std::string cmd = android::base::StringPrintf(
                 "exec:pm install-write -S %" PRIu64 " %d %d_%s -",
-                static_cast<uint64_t>(sb.st_size), session_id, i, get_basename(file));
-#endif
+                static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
 
         int localFd = adb_open(file, O_RDONLY);
         if (localFd < 0) {
@@ -1714,7 +1967,7 @@ finalize_session:
     fd = adb_connect(service, &error);
     if (fd < 0) {
         fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
-        return -1;
+        return EXIT_FAILURE;
     }
     read_status_line(fd, buf, sizeof(buf));
     adb_close(fd);
@@ -1725,6 +1978,88 @@ finalize_session:
     } else {
         fprintf(stderr, "Failed to finalize session\n");
         fputs(buf, stderr);
-        return -1;
+        return EXIT_FAILURE;
+    }
+}
+
+static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
+    std::string cmd = "pm";
+
+    while (argc-- > 0) {
+        cmd += " " + escape_arg(*argv++);
     }
+
+    return send_shell_command(transport, serial, cmd, false);
+}
+
+static int uninstall_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+    /* if the user choose the -k option, we refuse to do it until devices are
+       out with the option to uninstall the remaining data somehow (adb/ui) */
+    int i;
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-k")) {
+            printf(
+                "The -k option uninstalls the application while retaining the data/cache.\n"
+                "At the moment, there is no way to remove the remaining data.\n"
+                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+                "If you truly wish to continue, execute 'adb shell pm uninstall -k'\n.");
+            return EXIT_FAILURE;
+        }
+    }
+
+    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
+    return pm_command(transport, serial, argc, argv);
+}
+
+static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
+    std::string cmd = "rm -f " + escape_arg(filename);
+    return send_shell_command(transport, serial, cmd, false);
+}
+
+static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+    static const char *const DATA_DEST = "/data/local/tmp/%s";
+    static const char *const SD_DEST = "/sdcard/tmp/%s";
+    const char* where = DATA_DEST;
+    int i;
+    struct stat sb;
+
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-s")) {
+            where = SD_DEST;
+        }
+    }
+
+    // Find last APK argument.
+    // All other arguments passed through verbatim.
+    int last_apk = -1;
+    for (i = argc - 1; i >= 0; i--) {
+        const char* file = argv[i];
+        const char* dot = strrchr(file, '.');
+        if (dot && !strcasecmp(dot, ".apk")) {
+            if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+                fprintf(stderr, "Invalid APK file: %s\n", file);
+                return EXIT_FAILURE;
+            }
+
+            last_apk = i;
+            break;
+        }
+    }
+
+    if (last_apk == -1) {
+        fprintf(stderr, "Missing APK file\n");
+        return EXIT_FAILURE;
+    }
+
+    int result = -1;
+    std::vector<const char*> apk_file = {argv[last_apk]};
+    std::string apk_dest = android::base::StringPrintf(
+        where, adb_basename(argv[last_apk]).c_str());
+    if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
+    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
+    result = pm_command(transport, serial, argc, argv);
+
+cleanup_apk:
+    delete_file(transport, serial, apk_dest);
+    return result;
 }
index 452ee41..5a9c6ab 100644 (file)
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 #include "sysdeps.h"
+
+#include <stdio.h>
+
+#include <base/file.h>
+#include <base/logging.h>
+#include <base/strings.h>
+#include <cutils/sockets.h>
+
 #include "adb.h"
 #include "adb_client.h"
-#include <stdio.h>
+#include "adb_io.h"
 
-static int  connect_to_console(void)
-{
-    int  fd, port;
+// Return the console port of the currently connected emulator (if any) or -1 if
+// there is no emulator, and -2 if there is more than one.
+static int adb_get_emulator_console_port(const char* serial) {
+    if (serial) {
+        // The user specified a serial number; is it an emulator?
+        int port;
+        return (sscanf(serial, "emulator-%d", &port) == 1) ? port : -1;
+    }
 
-    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");
+    // No specific device was given, so get the list of connected devices and
+    // search for emulators. If there's one, we'll take it. If there are more
+    // than one, that's an error.
+    std::string devices;
+    std::string error;
+    if (!adb_query("host:devices", &devices, &error)) {
+        fprintf(stderr, "error: no emulator connected: %s\n", error.c_str());
         return -1;
     }
-    fd = socket_loopback_client( port, SOCK_STREAM );
-    if (fd < 0) {
-        fprintf(stderr, "error: could not connect to TCP port %d\n", port);
+
+    int port;
+    size_t emulator_count = 0;
+    for (const auto& device : android::base::Split(devices, "\n")) {
+        if (sscanf(device.c_str(), "emulator-%d", &port) == 1) {
+            if (++emulator_count > 1) {
+                fprintf(
+                    stderr, "error: more than one emulator detected; use -s\n");
+                return -1;
+            }
+        }
+    }
+
+    if (emulator_count == 0) {
+        fprintf(stderr, "error: no emulator detected\n");
         return -1;
     }
-    return  fd;
+
+    return port;
 }
 
+static int connect_to_console(const char* serial) {
+    int port = adb_get_emulator_console_port(serial);
+    if (port == -1) {
+        return -1;
+    }
 
-int  adb_send_emulator_command(int  argc, const char**  argv)
-{
-    int   fd, nn;
+    std::string error;
+    int fd = network_loopback_client(port, SOCK_STREAM, &error);
+    if (fd == -1) {
+        fprintf(stderr, "error: could not connect to TCP port %d: %s\n", port,
+                error.c_str());
+        return -1;
+    }
+    return fd;
+}
 
-    fd = connect_to_console();
-    if (fd < 0)
+int adb_send_emulator_command(int argc, const char** argv, const char* serial) {
+    int fd = connect_to_console(serial);
+    if (fd == -1) {
         return 1;
+    }
 
-#define  QUIT  "quit\n"
+    std::string commands;
 
-    for (nn = 1; nn < argc; nn++) {
-        adb_write( fd, argv[nn], strlen(argv[nn]) );
-        adb_write( fd, (nn == argc-1) ? "\n" : " ", 1 );
+    for (int i = 1; i < argc; i++) {
+        commands.append(argv[i]);
+        commands.append(i == argc - 1 ? "\n" : " ");
     }
-    adb_write( fd, QUIT, sizeof(QUIT)-1 );
+
+    commands.append("quit\n");
+
+    if (!WriteFdExactly(fd, commands)) {
+        fprintf(stderr, "error: cannot write to emulator: %s\n",
+                strerror(errno));
+        adb_close(fd);
+        return 1;
+    }
+
+    // Drain output that the emulator console has sent us to prevent a problem
+    // on Windows where if adb closes the socket without reading all the data,
+    // the emulator's next call to recv() will have an ECONNABORTED error,
+    // preventing the emulator from reading the command that adb has sent.
+    // https://code.google.com/p/android/issues/detail?id=21021
+    int result;
+    do {
+        char buf[BUFSIZ];
+        result = adb_read(fd, buf, sizeof(buf));
+        // Keep reading until zero bytes (orderly/graceful shutdown) or an
+        // error. If 'adb emu kill' is executed, the emulator calls exit() with
+        // the socket open (and shutdown(SD_SEND) was not called), which causes
+        // Windows to send a TCP RST segment which causes adb to get ECONNRESET.
+        // Any other emu command is followed by the quit command that we
+        // appended above, and that causes the emulator to close the socket
+        // which should cause zero bytes (orderly/graceful shutdown) to be
+        // returned.
+    } while (result > 0);
+
     adb_close(fd);
 
     return 0;
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
new file mode 100644 (file)
index 0000000..f4e054e
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG ADB
+
+#include "sysdeps.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/prctl.h>
+
+#include "base/logging.h"
+#include "base/stringprintf.h"
+#include "cutils/properties.h"
+#include "private/android_filesystem_config.h"
+#include "selinux/selinux.h"
+
+#include "adb.h"
+#include "adb_auth.h"
+#include "adb_listeners.h"
+#include "transport.h"
+
+static const char* root_seclabel = nullptr;
+
+static void drop_capabilities_bounding_set_if_needed() {
+#ifdef ALLOW_ADBD_ROOT
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.debuggable", value, "");
+    if (strcmp(value, "1") == 0) {
+        return;
+    }
+#endif
+    for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
+        if (i == CAP_SETUID || i == CAP_SETGID) {
+            // CAP_SETUID CAP_SETGID needed by /system/bin/run-as
+            continue;
+        }
+
+        int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
+
+        // Some kernels don't have file capabilities compiled in, and
+        // prctl(PR_CAPBSET_DROP) returns EINVAL. Don't automatically
+        // die when we see such misconfigured kernels.
+        if ((err < 0) && (errno != EINVAL)) {
+            PLOG(FATAL) << "Could not drop capabilities";
+        }
+    }
+}
+
+static bool should_drop_privileges() {
+#if defined(ALLOW_ADBD_ROOT)
+    char value[PROPERTY_VALUE_MAX];
+
+    // The properties that affect `adb root` and `adb unroot` are ro.secure and
+    // ro.debuggable. In this context the names don't make the expected behavior
+    // particularly obvious.
+    //
+    // ro.debuggable:
+    //   Allowed to become root, but not necessarily the default. Set to 1 on
+    //   eng and userdebug builds.
+    //
+    // ro.secure:
+    //   Drop privileges by default. Set to 1 on userdebug and user builds.
+    property_get("ro.secure", value, "1");
+    bool ro_secure = (strcmp(value, "1") == 0);
+
+    property_get("ro.debuggable", value, "");
+    bool ro_debuggable = (strcmp(value, "1") == 0);
+
+    // Drop privileges if ro.secure is set...
+    bool drop = ro_secure;
+
+    property_get("service.adb.root", value, "");
+    bool adb_root = (strcmp(value, "1") == 0);
+    bool adb_unroot = (strcmp(value, "0") == 0);
+
+    // ...except "adb root" lets you keep privileges in a debuggable build.
+    if (ro_debuggable && adb_root) {
+        drop = false;
+    }
+
+    // ...and "adb unroot" lets you explicitly drop privileges.
+    if (adb_unroot) {
+        drop = true;
+    }
+
+    return drop;
+#else
+    return true; // "adb root" not allowed, always drop privileges.
+#endif // ALLOW_ADBD_ROOT
+}
+
+int adbd_main(int server_port) {
+    umask(0);
+
+    signal(SIGPIPE, SIG_IGN);
+
+    init_transport_registration();
+
+    // We need to call this even if auth isn't enabled because the file
+    // descriptor will always be open.
+    adbd_cloexec_auth_socket();
+
+    if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) {
+        auth_required = false;
+    }
+
+    adbd_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 (adb_external_storage != nullptr) {
+        setenv("EXTERNAL_STORAGE", adb_external_storage, 1);
+    } else {
+        D("Warning: ADB_EXTERNAL_STORAGE is not set.  Leaving EXTERNAL_STORAGE"
+          " unchanged.\n");
+    }
+
+    // Add extra groups:
+    // AID_ADB to access the USB driver
+    // AID_LOG to read system logs (adb logcat)
+    // AID_INPUT to diagnose input issues (getevent)
+    // AID_INET to diagnose network issues (ping)
+    // AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
+    // AID_SDCARD_R to allow reading from the SD card
+    // AID_SDCARD_RW to allow writing to the SD card
+    // AID_NET_BW_STATS to read out qtaguid statistics
+    // AID_READPROC for reading /proc entries across UID boundaries
+    gid_t groups[] = {AID_ADB,      AID_LOG,       AID_INPUT,
+                      AID_INET,     AID_NET_BT,    AID_NET_BT_ADMIN,
+                      AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS,
+                      AID_READPROC };
+    if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
+        PLOG(FATAL) << "Could not set supplemental groups";
+    }
+
+    /* don't listen on a port (default 5037) if running in secure mode */
+    /* don't run as root if we are running in secure mode */
+    if (should_drop_privileges()) {
+        drop_capabilities_bounding_set_if_needed();
+
+        /* then switch user and group to "shell" */
+        if (setgid(AID_SHELL) != 0) {
+            PLOG(FATAL) << "Could not setgid";
+        }
+        if (setuid(AID_SHELL) != 0) {
+            PLOG(FATAL) << "Could not setuid";
+        }
+
+        D("Local port disabled");
+    } else {
+        if (root_seclabel != nullptr) {
+            if (setcon(root_seclabel) < 0) {
+                LOG(FATAL) << "Could not set SELinux context";
+            }
+        }
+        std::string error;
+        std::string local_name =
+            android::base::StringPrintf("tcp:%d", server_port);
+        if (install_listener(local_name, "*smartsocket*", nullptr, 0,
+                             &error)) {
+            LOG(FATAL) << "Could not install *smartsocket* listener: "
+                << error;
+        }
+    }
+
+    bool is_usb = false;
+    if (access(USB_ADB_PATH, F_OK) == 0 || access(USB_FFS_ADB_EP0, F_OK) == 0) {
+        // Listen on USB.
+        usb_init();
+        is_usb = true;
+    }
+
+    // If one of these properties is set, also listen on that port.
+    // If one of the properties isn't set and we couldn't listen on usb, listen
+    // on the default port.
+    char prop_port[PROPERTY_VALUE_MAX];
+    property_get("service.adb.tcp.port", prop_port, "");
+    if (prop_port[0] == '\0') {
+        property_get("persist.adb.tcp.port", prop_port, "");
+    }
+
+    int port;
+    if (sscanf(prop_port, "%d", &port) == 1 && port > 0) {
+        D("using port=%d", port);
+        // Listen on TCP port specified by service.adb.tcp.port property.
+        local_init(port);
+    } else if (!is_usb) {
+        // Listen on default port.
+        local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
+    }
+
+    D("adbd_main(): pre init_jdwp()");
+    init_jdwp();
+    D("adbd_main(): post init_jdwp()");
+
+    D("Event loop starting");
+    fdevent_loop();
+
+    return 0;
+}
+
+static void close_stdin() {
+    int fd = unix_open("/dev/null", O_RDONLY);
+    if (fd == -1) {
+        perror("failed to open /dev/null, stdin will remain open");
+        return;
+    }
+    dup2(fd, STDIN_FILENO);
+    unix_close(fd);
+}
+
+int main(int argc, char** argv) {
+    while (true) {
+        static struct option opts[] = {
+            {"root_seclabel", required_argument, nullptr, 's'},
+            {"device_banner", required_argument, nullptr, 'b'},
+            {"version", no_argument, nullptr, 'v'},
+        };
+
+        int option_index = 0;
+        int c = getopt_long(argc, argv, "", opts, &option_index);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 's':
+            root_seclabel = optarg;
+            break;
+        case 'b':
+            adb_device_banner = optarg;
+            break;
+        case 'v':
+            printf("Android Debug Bridge Daemon version %d.%d.%d %s\n",
+                   ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
+                   ADB_REVISION);
+            return 0;
+        default:
+            // getopt already prints "adbd: invalid option -- %c" for us.
+            return 1;
+        }
+    }
+
+    close_stdin();
+
+    adb_trace_init(argv);
+
+    D("Handling main()");
+    return adbd_main(DEFAULT_ADB_PORT);
+}
index 0c43c5e..46547b9 100644 (file)
@@ -2,74 +2,48 @@
 **
 ** Copyright 2006, Brian Swetland <swetland@frotz.net>
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 
-#define TRACE_TAG TRACE_FDEVENT
+#define TRACE_TAG FDEVENT
 
 #include "sysdeps.h"
 #include "fdevent.h"
 
-#include <errno.h>
 #include <fcntl.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
+#include <poll.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <unistd.h>
 
+#include <list>
+#include <unordered_map>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+
 #include "adb_io.h"
 #include "adb_trace.h"
+#include "adb_utils.h"
 
-/* !!! Do not enable DEBUG for the adb that will run as the server:
-** both stdout and stderr are used to communicate between the client
-** and server. Any extra output will cause failures.
-*/
-#define DEBUG 0   /* non-0 will break adb server */
-
+#if !ADB_HOST
 // This socket is used when a subproc shell service exists.
 // It wakes up the fdevent_loop() and cause the correct handling
 // of the shell's pseudo-tty master. I.e. force close it.
 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
-static void dump_fde(fdevent *fde, const char *info)
-{
-    adb_mutex_lock(&D_lock);
-    fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
-            fde->state & FDE_READ ? 'R' : ' ',
-            fde->state & FDE_WRITE ? 'W' : ' ',
-            fde->state & FDE_ERROR ? 'E' : ' ',
-            info);
-    adb_mutex_unlock(&D_lock);
-}
-#else
-#define dump_fde(fde, info) do { } while(0)
-#endif
+#endif // !ADB_HOST
 
 #define FDE_EVENTMASK  0x00ff
 #define FDE_STATEMASK  0xff00
@@ -78,618 +52,333 @@ static void dump_fde(fdevent *fde, const char *info)
 #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,
-    .fd = -1,
-    .force_eof = 0,
-    .state = 0,
-    .events = 0,
-    .func = nullptr,
-    .arg = nullptr,
-};
-
-static fdevent **fd_table = 0;
-static int fd_table_max = 0;
+struct PollNode {
+  fdevent* fde;
+  ::pollfd pollfd;
 
-#ifdef CRAPTASTIC
-//HAVE_EPOLL
+  PollNode(fdevent* fde) : fde(fde) {
+      memset(&pollfd, 0, sizeof(pollfd));
+      pollfd.fd = fde->fd;
 
-#include <sys/epoll.h>
+#if defined(__linux__)
+      // Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
+      // Then we can avoid leaving many sockets in CLOSE_WAIT state. See http://b/23314034.
+      pollfd.events = POLLRDHUP;
+#endif
+  }
+};
 
-static int epoll_fd = -1;
+// All operations to fdevent should happen only in the main thread.
+// That's why we don't need a lock for fdevent.
+static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>();
+static auto& g_pending_list = *new std::list<fdevent*>();
+static bool main_thread_valid;
+static pthread_t main_thread;
 
-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);
+static void check_main_thread() {
+    if (main_thread_valid) {
+        CHECK_NE(0, pthread_equal(main_thread, pthread_self()));
     }
-
-        /* 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;
+static void set_main_thread() {
+    main_thread_valid = true;
+    main_thread = pthread_self();
+}
 
-#if 0
-    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
-        perror("epoll_ctl() failed\n");
-        exit(1);
+static std::string dump_fde(const fdevent* fde) {
+    std::string state;
+    if (fde->state & FDE_ACTIVE) {
+        state += "A";
     }
-#endif
+    if (fde->state & FDE_PENDING) {
+        state += "P";
+    }
+    if (fde->state & FDE_CREATED) {
+        state += "C";
+    }
+    if (fde->state & FDE_READ) {
+        state += "R";
+    }
+    if (fde->state & FDE_WRITE) {
+        state += "W";
+    }
+    if (fde->state & FDE_ERROR) {
+        state += "E";
+    }
+    if (fde->state & FDE_DONT_CLOSE) {
+        state += "D";
+    }
+    return android::base::StringPrintf("(fdevent %d %s)", fde->fd, state.c_str());
 }
 
-static void fdevent_disconnect(fdevent *fde)
+fdevent *fdevent_create(int fd, fd_func func, void *arg)
 {
-    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);
+    check_main_thread();
+    fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
+    if(fde == 0) return 0;
+    fdevent_install(fde, fd, func, arg);
+    fde->state |= FDE_CREATED;
+    return fde;
 }
 
-static void fdevent_update(fdevent *fde, unsigned events)
+void fdevent_destroy(fdevent *fde)
 {
-    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);
-            }
-        }
+    check_main_thread();
+    if(fde == 0) return;
+    if(!(fde->state & FDE_CREATED)) {
+        LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
     }
+    fdevent_remove(fde);
+    free(fde);
 }
 
-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);
+void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
+    check_main_thread();
+    CHECK_GE(fd, 0);
+    memset(fde, 0, sizeof(fdevent));
+    fde->state = FDE_ACTIVE;
+    fde->fd = fd;
+    fde->func = func;
+    fde->arg = arg;
+    if (!set_file_block_mode(fd, false)) {
+        // Here is not proper to handle the error. If it fails here, some error is
+        // likely to be detected by poll(), then we can let the callback function
+        // to handle it.
+        LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
     }
+    auto pair = g_poll_node_map.emplace(fde->fd, PollNode(fde));
+    CHECK(pair.second) << "install existing fd " << fd;
+    D("fdevent_install %s", dump_fde(fde).c_str());
+}
 
-    for(i = 0; i < n; i++) {
-        struct epoll_event *ev = events + i;
-        fde = ev->data.ptr;
-
-        if(ev->events & EPOLLIN) {
-            fde->events |= FDE_READ;
+void fdevent_remove(fdevent* fde) {
+    check_main_thread();
+    D("fdevent_remove %s", dump_fde(fde).c_str());
+    if (fde->state & FDE_ACTIVE) {
+        g_poll_node_map.erase(fde->fd);
+        if (fde->state & FDE_PENDING) {
+            g_pending_list.remove(fde);
         }
-        if(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);
+        if (!(fde->state & FDE_DONT_CLOSE)) {
+            adb_close(fde->fd);
+            fde->fd = -1;
         }
+        fde->state = 0;
+        fde->events = 0;
     }
 }
 
-#else /* USE_SELECT */
-
-#ifdef HAVE_WINSOCK
-#include <winsock2.h>
-#else
-#include <sys/select.h>
-#endif
-
-static fd_set read_fds;
-static fd_set write_fds;
-static fd_set error_fds;
-
-static int select_n = 0;
-
-static void fdevent_init(void)
-{
-    FD_ZERO(&read_fds);
-    FD_ZERO(&write_fds);
-    FD_ZERO(&error_fds);
-}
-
-static void fdevent_connect(fdevent *fde)
-{
-    if(fde->fd >= select_n) {
-        select_n = fde->fd + 1;
+static void fdevent_update(fdevent* fde, unsigned events) {
+    auto it = g_poll_node_map.find(fde->fd);
+    CHECK(it != g_poll_node_map.end());
+    PollNode& node = it->second;
+    if (events & FDE_READ) {
+        node.pollfd.events |= POLLIN;
+    } else {
+        node.pollfd.events &= ~POLLIN;
     }
-}
-
-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;
+    if (events & FDE_WRITE) {
+        node.pollfd.events |= POLLOUT;
+    } else {
+        node.pollfd.events &= ~POLLOUT;
     }
-    select_n = n + 1;
+    fde->state = (fde->state & FDE_STATEMASK) | events;
 }
 
-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);
+void fdevent_set(fdevent* fde, unsigned events) {
+    check_main_thread();
+    events &= FDE_EVENTMASK;
+    if ((fde->state & FDE_EVENTMASK) == events) {
+        return;
     }
-    if(events & FDE_ERROR) {
-        FD_SET(fde->fd, &error_fds);
-    } else {
-        FD_CLR(fde->fd, &error_fds);
+    CHECK(fde->state & FDE_ACTIVE);
+    fdevent_update(fde, events);
+    D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
+
+    if (fde->state & FDE_PENDING) {
+        // If we are pending, make sure we don't signal an event that is no longer wanted.
+        fde->events &= events;
+        if (fde->events == 0) {
+            g_pending_list.remove(fde);
+            fde->state &= ~FDE_PENDING;
+        }
     }
-
-    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;
+void fdevent_add(fdevent* fde, unsigned events) {
+    check_main_thread();
+    fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
+}
 
-        }
-    }
-    return n;
+void fdevent_del(fdevent* fde, unsigned events) {
+    check_main_thread();
+    fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
 }
 
-#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;
+static std::string dump_pollfds(const std::vector<pollfd>& pollfds) {
+    std::string result;
+    for (const auto& pollfd : pollfds) {
+        std::string op;
+        if (pollfd.events & POLLIN) {
+            op += "R";
         }
-        if(fcntl(i, F_GETFL, NULL) < 0) {
-            SAFE_SPRINTF("b");
+        if (pollfd.events & POLLOUT) {
+            op += "W";
         }
-        SAFE_SPRINTF(" ");
+        android::base::StringAppendF(&result, " %d(%s)", pollfd.fd, op.c_str());
     }
-    D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff);
+    return result;
 }
-#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;
-        }
+static void fdevent_process() {
+    std::vector<pollfd> pollfds;
+    for (const auto& pair : g_poll_node_map) {
+        pollfds.push_back(pair.second.pollfd);
     }
-    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);
+    CHECK_GT(pollfds.size(), 0u);
+    D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
+    int ret = TEMP_FAILURE_RETRY(poll(&pollfds[0], pollfds.size(), -1));
+    if (ret == -1) {
+        PLOG(ERROR) << "poll(), ret = " << ret;
+        return;
     }
-
-    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);
+    for (const auto& pollfd : pollfds) {
+        if (pollfd.revents != 0) {
+            D("for fd %d, revents = %x", pollfd.fd, pollfd.revents);
         }
-    }
-}
-
-#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);
+        unsigned events = 0;
+        if (pollfd.revents & POLLIN) {
+            events |= FDE_READ;
         }
-        if(fd_table_max == 0) {
-            fdevent_init();
-            fd_table_max = 256;
+        if (pollfd.revents & POLLOUT) {
+            events |= FDE_WRITE;
         }
-        while(fd_table_max <= fde->fd) {
-            fd_table_max *= 2;
+        if (pollfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
+            // We fake a read, as the rest of the code assumes that errors will
+            // be detected at that point.
+            events |= FDE_READ | FDE_ERROR;
         }
-        fd_table = reinterpret_cast<fdevent**>(
-            realloc(fd_table, sizeof(fdevent*) * fd_table_max));
-        if(fd_table == 0) {
-            FATAL("could not expand fd_table to %d entries\n", fd_table_max);
+#if defined(__linux__)
+        if (pollfd.revents & POLLRDHUP) {
+            events |= FDE_READ | FDE_ERROR;
+        }
+#endif
+        if (events != 0) {
+            auto it = g_poll_node_map.find(pollfd.fd);
+            CHECK(it != g_poll_node_map.end());
+            fdevent* fde = it->second.fde;
+            CHECK_EQ(fde->fd, pollfd.fd);
+            fde->events |= events;
+            D("%s got events %x", dump_fde(fde).c_str(), events);
+            fde->state |= FDE_PENDING;
+            g_pending_list.push_back(fde);
         }
-        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;
+    CHECK(fde->state & FDE_PENDING);
     fde->state &= (~FDE_PENDING);
-    dump_fde(fde, "callback");
+    D("fdevent_call_fdfunc %s", dump_fde(fde).c_str());
     fde->func(fde->fd, events, fde->arg);
 }
 
+#if !ADB_HOST
 static void fdevent_subproc_event_func(int fd, unsigned ev,
                                        void* /* userdata */)
 {
 
-    D("subproc handling on fd=%d ev=%04x\n", fd, ev);
-
-    // Hook oneself back into the fde's suitable for select() on read.
-    if((fd < 0) || (fd >= fd_table_max)) {
-        FATAL("fd %d out of range for fd_table \n", fd);
-    }
-    fdevent *fde = fd_table[fd];
-    fdevent_add(fde, FDE_READ);
-
-    if(ev & FDE_READ){
-      int subproc_fd;
-
-      if(!ReadFdExactly(fd, &subproc_fd, sizeof(subproc_fd))) {
-          FATAL("Failed to read the subproc's fd from fd=%d\n", fd);
-      }
-      if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) {
-          D("subproc_fd %d out of range 0, fd_table_max=%d\n",
-            subproc_fd, fd_table_max);
-          return;
-      }
-      fdevent *subproc_fde = fd_table[subproc_fd];
-      if(!subproc_fde) {
-          D("subproc_fd %d cleared from fd_table\n", subproc_fd);
-          return;
-      }
-      if(subproc_fde->fd != subproc_fd) {
-          // Already reallocated?
-          D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd);
-          return;
-      }
-
-      subproc_fde->force_eof = 1;
-
-      int rcount = 0;
-      ioctl(subproc_fd, FIONREAD, &rcount);
-      D("subproc with fd=%d  has rcount=%d err=%d\n",
-        subproc_fd, rcount, errno);
-
-      if(rcount) {
-        // If there is data left, it will show up in the select().
-        // This works because there is no other thread reading that
-        // data when in this fd_func().
-        return;
-      }
-
-      D("subproc_fde.state=%04x\n", subproc_fde->state);
-      subproc_fde->events |= FDE_READ;
-      if(subproc_fde->state & FDE_PENDING) {
-        return;
-      }
-      subproc_fde->state |= FDE_PENDING;
-      fdevent_call_fdfunc(subproc_fde);
-    }
-}
-
-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);
-    free(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);
-    }
+    D("subproc handling on fd = %d, ev = %x", fd, ev);
 
-    fde->state = 0;
-    fde->events = 0;
-}
+    CHECK_GE(fd, 0);
 
+    if (ev & FDE_READ) {
+        int subproc_fd;
 
-void fdevent_set(fdevent *fde, unsigned events)
-{
-    events &= FDE_EVENTMASK;
-
-    if((fde->state & FDE_EVENTMASK) == events) return;
+        if(!ReadFdExactly(fd, &subproc_fd, sizeof(subproc_fd))) {
+            LOG(FATAL) << "Failed to read the subproc's fd from " << fd;
+        }
+        auto it = g_poll_node_map.find(subproc_fd);
+        if (it == g_poll_node_map.end()) {
+            D("subproc_fd %d cleared from fd_table", subproc_fd);
+            return;
+        }
+        fdevent* subproc_fde = it->second.fde;
+        if(subproc_fde->fd != subproc_fd) {
+            // Already reallocated?
+            D("subproc_fd(%d) != subproc_fde->fd(%d)", subproc_fd, subproc_fde->fd);
+            return;
+        }
 
-    if(fde->state & FDE_ACTIVE) {
-        fdevent_update(fde, events);
-        dump_fde(fde, "update");
-    }
+        subproc_fde->force_eof = 1;
 
-    fde->state = (fde->state & FDE_STATEMASK) | events;
+        int rcount = 0;
+        ioctl(subproc_fd, FIONREAD, &rcount);
+        D("subproc with fd %d has rcount=%d, err=%d", subproc_fd, rcount, errno);
+        if (rcount != 0) {
+            // If there is data left, it will show up in the select().
+            // This works because there is no other thread reading that
+            // data when in this fd_func().
+            return;
+        }
 
-    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);
+        D("subproc_fde %s", dump_fde(subproc_fde).c_str());
+        subproc_fde->events |= FDE_READ;
+        if(subproc_fde->state & FDE_PENDING) {
+            return;
         }
+        subproc_fde->state |= FDE_PENDING;
+        fdevent_call_fdfunc(subproc_fde);
     }
 }
 
-void fdevent_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");
+        PLOG(FATAL) << "cannot create shell-exit socket-pair";
     }
-    D("socketpair: (%d,%d)", s[0], s[1]);
+    D("fdevent_subproc: socket pair (%d, %d)", s[0], s[1]);
 
     SHELL_EXIT_NOTIFY_FD = s[0];
-    fdevent *fde;
-    fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
-    if(!fde)
-      FATAL("cannot create fdevent for shell-exit handler\n");
+    fdevent *fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
+    CHECK(fde != nullptr) << "cannot create fdevent for shell-exit handler";
     fdevent_add(fde, FDE_READ);
 }
+#endif // !ADB_HOST
 
 void fdevent_loop()
 {
-    fdevent *fde;
+    set_main_thread();
+#if !ADB_HOST
     fdevent_subproc_setup();
+#endif // !ADB_HOST
 
-    for(;;) {
-        D("--- ---- waiting for events\n");
+    while (true) {
+        D("--- --- waiting for events");
 
         fdevent_process();
 
-        while((fde = fdevent_plist_dequeue())) {
+        while (!g_pending_list.empty()) {
+            fdevent* fde = g_pending_list.front();
+            g_pending_list.pop_front();
             fdevent_call_fdfunc(fde);
         }
     }
 }
+
+size_t fdevent_installed_count() {
+    return g_poll_node_map.size();
+}
+
+void fdevent_reset() {
+    g_poll_node_map.clear();
+    g_pending_list.clear();
+    main_thread_valid = false;
+}
index 8d84b29..657fde5 100644 (file)
 #ifndef __FDEVENT_H
 #define __FDEVENT_H
 
+#include <stddef.h>
 #include <stdint.h>  /* for int64_t */
 
 /* events that may be observed */
 #define FDE_READ              0x0001
 #define FDE_WRITE             0x0002
 #define FDE_ERROR             0x0004
-#define FDE_TIMEOUT           0x0008
 
 /* features that may be set (via the events set/add/del interface) */
 #define FDE_DONT_CLOSE        0x0080
 
-struct fdevent;
-
 typedef void (*fd_func)(int fd, unsigned events, void *userdata);
 
+struct fdevent {
+    fdevent *next;
+    fdevent *prev;
+
+    int fd;
+    int force_eof;
+
+    uint16_t state;
+    uint16_t events;
+
+    fd_func func;
+    void *arg;
+};
+
 /* Allocate and initialize a new fdevent object
  * Note: use FD_TIMER as 'fd' to create a fd-less object
  * (used to implement timers).
@@ -64,18 +76,9 @@ void fdevent_set_timeout(fdevent *fde, int64_t  timeout_ms);
 */
 void fdevent_loop();
 
-struct fdevent {
-    fdevent *next;
-    fdevent *prev;
-
-    int fd;
-    int force_eof;
-
-    uint16_t state;
-    uint16_t events;
-
-    fd_func func;
-    void *arg;
-};
+// For debugging only.
+size_t fdevent_installed_count();
+// For debugging only.
+void fdevent_reset();
 
 #endif
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
new file mode 100644 (file)
index 0000000..7fe3d37
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fdevent.h"
+
+#include <gtest/gtest.h>
+
+#include <pthread.h>
+#include <signal.h>
+
+#include <limits>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "adb_io.h"
+
+class FdHandler {
+  public:
+    FdHandler(int read_fd, int write_fd) : read_fd_(read_fd), write_fd_(write_fd) {
+        fdevent_install(&read_fde_, read_fd_, FdEventCallback, this);
+        fdevent_add(&read_fde_, FDE_READ);
+        fdevent_install(&write_fde_, write_fd_, FdEventCallback, this);
+    }
+
+    ~FdHandler() {
+        fdevent_remove(&read_fde_);
+        fdevent_remove(&write_fde_);
+    }
+
+  private:
+    static void FdEventCallback(int fd, unsigned events, void* userdata) {
+        FdHandler* handler = reinterpret_cast<FdHandler*>(userdata);
+        ASSERT_EQ(0u, (events & ~(FDE_READ | FDE_WRITE))) << "unexpected events: " << events;
+        if (events & FDE_READ) {
+            ASSERT_EQ(fd, handler->read_fd_);
+            char c;
+            ASSERT_EQ(1, read(fd, &c, 1));
+            handler->queue_.push(c);
+            fdevent_add(&handler->write_fde_, FDE_WRITE);
+        }
+        if (events & FDE_WRITE) {
+            ASSERT_EQ(fd, handler->write_fd_);
+            ASSERT_FALSE(handler->queue_.empty());
+            char c = handler->queue_.front();
+            handler->queue_.pop();
+            ASSERT_EQ(1, write(fd, &c, 1));
+            if (handler->queue_.empty()) {
+              fdevent_del(&handler->write_fde_, FDE_WRITE);
+            }
+        }
+    }
+
+  private:
+    const int read_fd_;
+    const int write_fd_;
+    fdevent read_fde_;
+    fdevent write_fde_;
+    std::queue<char> queue_;
+};
+
+static void signal_handler(int) {
+    pthread_exit(nullptr);
+}
+
+class FdeventTest : public ::testing::Test {
+  protected:
+    static void SetUpTestCase() {
+        ASSERT_NE(SIG_ERR, signal(SIGUSR1, signal_handler));
+        ASSERT_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN));
+    }
+
+    virtual void SetUp() {
+        fdevent_reset();
+        ASSERT_EQ(0u, fdevent_installed_count());
+    }
+};
+
+struct ThreadArg {
+    int first_read_fd;
+    int last_write_fd;
+    size_t middle_pipe_count;
+};
+
+static void FdEventThreadFunc(ThreadArg* arg) {
+    std::vector<int> read_fds;
+    std::vector<int> write_fds;
+
+    read_fds.push_back(arg->first_read_fd);
+    for (size_t i = 0; i < arg->middle_pipe_count; ++i) {
+        int fds[2];
+        ASSERT_EQ(0, pipe(fds));
+        read_fds.push_back(fds[0]);
+        write_fds.push_back(fds[1]);
+    }
+    write_fds.push_back(arg->last_write_fd);
+
+    std::vector<std::unique_ptr<FdHandler>> fd_handlers;
+    for (size_t i = 0; i < read_fds.size(); ++i) {
+        fd_handlers.push_back(std::unique_ptr<FdHandler>(new FdHandler(read_fds[i], write_fds[i])));
+    }
+
+    fdevent_loop();
+}
+
+TEST_F(FdeventTest, smoke) {
+    const size_t PIPE_COUNT = 10;
+    const size_t MESSAGE_LOOP_COUNT = 100;
+    const std::string MESSAGE = "fdevent_test";
+    int fd_pair1[2];
+    int fd_pair2[2];
+    ASSERT_EQ(0, pipe(fd_pair1));
+    ASSERT_EQ(0, pipe(fd_pair2));
+    pthread_t thread;
+    ThreadArg thread_arg;
+    thread_arg.first_read_fd = fd_pair1[0];
+    thread_arg.last_write_fd = fd_pair2[1];
+    thread_arg.middle_pipe_count = PIPE_COUNT;
+    int writer = fd_pair1[1];
+    int reader = fd_pair2[0];
+
+    ASSERT_EQ(0, pthread_create(&thread, nullptr,
+                                reinterpret_cast<void* (*)(void*)>(FdEventThreadFunc),
+                                &thread_arg));
+
+    for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
+        std::string read_buffer = MESSAGE;
+        std::string write_buffer(MESSAGE.size(), 'a');
+        ASSERT_TRUE(WriteFdExactly(writer, read_buffer.c_str(), read_buffer.size()));
+        ASSERT_TRUE(ReadFdExactly(reader, &write_buffer[0], write_buffer.size()));
+        ASSERT_EQ(read_buffer, write_buffer);
+    }
+
+    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
+    ASSERT_EQ(0, pthread_join(thread, nullptr));
+    ASSERT_EQ(0, close(writer));
+    ASSERT_EQ(0, close(reader));
+}
+
+struct InvalidFdArg {
+    fdevent fde;
+    unsigned expected_events;
+    size_t* happened_event_count;
+};
+
+static void InvalidFdEventCallback(int fd, unsigned events, void* userdata) {
+    InvalidFdArg* arg = reinterpret_cast<InvalidFdArg*>(userdata);
+    ASSERT_EQ(arg->expected_events, events);
+    fdevent_remove(&arg->fde);
+    if (++*(arg->happened_event_count) == 2) {
+        pthread_exit(nullptr);
+    }
+}
+
+static void InvalidFdThreadFunc(void*) {
+    const int INVALID_READ_FD = std::numeric_limits<int>::max() - 1;
+    size_t happened_event_count = 0;
+    InvalidFdArg read_arg;
+    read_arg.expected_events = FDE_READ | FDE_ERROR;
+    read_arg.happened_event_count = &happened_event_count;
+    fdevent_install(&read_arg.fde, INVALID_READ_FD, InvalidFdEventCallback, &read_arg);
+    fdevent_add(&read_arg.fde, FDE_READ);
+
+    const int INVALID_WRITE_FD = std::numeric_limits<int>::max();
+    InvalidFdArg write_arg;
+    write_arg.expected_events = FDE_READ | FDE_ERROR;
+    write_arg.happened_event_count = &happened_event_count;
+    fdevent_install(&write_arg.fde, INVALID_WRITE_FD, InvalidFdEventCallback, &write_arg);
+    fdevent_add(&write_arg.fde, FDE_WRITE);
+    fdevent_loop();
+}
+
+TEST_F(FdeventTest, invalid_fd) {
+    pthread_t thread;
+    ASSERT_EQ(0, pthread_create(&thread, nullptr,
+                                reinterpret_cast<void* (*)(void*)>(InvalidFdThreadFunc),
+                                nullptr));
+    ASSERT_EQ(0, pthread_join(thread, nullptr));
+}
index aded301..a2d2a66 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <time.h>
+#include <unistd.h>
 #include <utime.h>
 
+#include <functional>
+#include <memory>
+#include <vector>
+
 #include "sysdeps.h"
 
 #include "adb.h"
 #include "adb_client.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "file_sync_service.h"
+#include "line_printer.h"
 
-static unsigned long long total_bytes;
-static long long start_time;
+#include <base/file.h>
+#include <base/strings.h>
+#include <base/stringprintf.h>
 
-static long long NOW()
-{
-    struct timeval tv;
-    gettimeofday(&tv, 0);
-    return ((long long) tv.tv_usec) +
-        1000000LL * ((long long) tv.tv_sec);
-}
+struct syncsendbuf {
+    unsigned id;
+    unsigned size;
+    char data[SYNC_DATA_MAX];
+};
 
-static void BEGIN()
-{
-    total_bytes = 0;
-    start_time = NOW();
-}
+class SyncConnection {
+  public:
+    SyncConnection() : total_bytes(0), start_time_ms_(CurrentTimeMs()) {
+        max = SYNC_DATA_MAX; // TODO: decide at runtime.
 
-static void END()
-{
-    long long t = NOW() - start_time;
-    if(total_bytes == 0) return;
+        std::string error;
+        fd = adb_connect("sync:", &error);
+        if (fd < 0) {
+            Error("connect failed: %s", error.c_str());
+        }
+    }
 
-    if (t == 0)  /* prevent division by 0 :-) */
-        t = 1000000;
+    ~SyncConnection() {
+        if (!IsValid()) return;
 
-    fprintf(stderr,"%lld KB/s (%lld bytes in %lld.%03llds)\n",
-            ((total_bytes * 1000000LL) / t) / 1024LL,
-            total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
-}
+        if (SendQuit()) {
+            // We sent a quit command, so the server should be doing orderly
+            // shutdown soon. But if we encountered an error while we were using
+            // the connection, the server might still be sending data (before
+            // doing orderly shutdown), in which case we won't wait for all of
+            // the data nor the coming orderly shutdown. In the common success
+            // case, this will wait for the server to do orderly shutdown.
+            ReadOrderlyShutdown(fd);
+        }
+        adb_close(fd);
+    }
 
-static const char* transfer_progress_format = "\rTransferring: %llu/%llu (%d%%)";
+    bool IsValid() { return fd >= 0; }
 
-static void print_transfer_progress(unsigned long long bytes_current,
-                                    unsigned long long bytes_total) {
-    if (bytes_total == 0) return;
+    bool SendRequest(int id, const char* path_and_mode) {
+        size_t path_length = strlen(path_and_mode);
+        if (path_length > 1024) {
+            Error("SendRequest failed: path too long: %zu", path_length);
+            errno = ENAMETOOLONG;
+            return false;
+        }
 
-    fprintf(stderr, transfer_progress_format, bytes_current, bytes_total,
-            (int) (bytes_current * 100 / bytes_total));
+        // Sending header and payload in a single write makes a noticeable
+        // difference to "adb sync" performance.
+        std::vector<char> buf(sizeof(SyncRequest) + path_length);
+        SyncRequest* req = reinterpret_cast<SyncRequest*>(&buf[0]);
+        req->id = id;
+        req->path_length = path_length;
+        char* data = reinterpret_cast<char*>(req + 1);
+        memcpy(data, path_and_mode, path_length);
 
-    if (bytes_current == bytes_total) {
-        fputc('\n', stderr);
+        return WriteFdExactly(fd, &buf[0], buf.size());
     }
 
-    fflush(stderr);
-}
-
-void sync_quit(int fd)
-{
-    syncmsg msg;
+    // Sending header, payload, and footer in a single write makes a huge
+    // difference to "adb sync" performance.
+    bool SendSmallFile(const char* path_and_mode,
+                       const char* rpath,
+                       const char* data, size_t data_length,
+                       unsigned mtime) {
+        Print(rpath);
 
-    msg.req.id = ID_QUIT;
-    msg.req.namelen = 0;
+        size_t path_length = strlen(path_and_mode);
+        if (path_length > 1024) {
+            Error("SendSmallFile failed: path too long: %zu", path_length);
+            errno = ENAMETOOLONG;
+            return false;
+        }
 
-    WriteFdExactly(fd, &msg.req, sizeof(msg.req));
-}
+        std::vector<char> buf(sizeof(SyncRequest) + path_length +
+                 sizeof(SyncRequest) + data_length +
+                 sizeof(SyncRequest));
+        char* p = &buf[0];
 
-typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char *name, void *cookie);
+        SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
+        req_send->id = ID_SEND;
+        req_send->path_length = path_length;
+        p += sizeof(SyncRequest);
+        memcpy(p, path_and_mode, path_length);
+        p += path_length;
 
-int sync_ls(int fd, const char *path, sync_ls_cb func, void *cookie)
-{
-    syncmsg msg;
-    char buf[257];
-    int len;
+        SyncRequest* req_data = reinterpret_cast<SyncRequest*>(p);
+        req_data->id = ID_DATA;
+        req_data->path_length = data_length;
+        p += sizeof(SyncRequest);
+        memcpy(p, data, data_length);
+        p += data_length;
 
-    len = strlen(path);
-    if(len > 1024) goto fail;
+        SyncRequest* req_done = reinterpret_cast<SyncRequest*>(p);
+        req_done->id = ID_DONE;
+        req_done->path_length = mtime;
+        p += sizeof(SyncRequest);
 
-    msg.req.id = ID_LIST;
-    msg.req.namelen = htoll(len);
+        if (!WriteFdExactly(fd, &buf[0], (p - &buf[0]))) return false;
 
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, path, len)) {
-        goto fail;
+        total_bytes += data_length;
+        return true;
     }
 
-    for(;;) {
-        if(!ReadFdExactly(fd, &msg.dent, sizeof(msg.dent))) break;
-        if(msg.dent.id == ID_DONE) return 0;
-        if(msg.dent.id != ID_DENT) break;
+    bool CopyDone(const char* from, const char* to) {
+        syncmsg msg;
+        if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
+            Error("failed to copy '%s' to '%s': no ID_DONE: %s", from, to, strerror(errno));
+            return false;
+        }
+        if (msg.status.id == ID_OKAY) {
+            return true;
+        }
+        if (msg.status.id != ID_FAIL) {
+            Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
+            return false;
+        }
+        return ReportCopyFailure(from, to, msg);
+    }
 
-        len = ltohl(msg.dent.namelen);
-        if(len > 256) break;
+    bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
+        std::vector<char> buf(msg.status.msglen + 1);
+        if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
+            Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
+                  from, to, strerror(errno));
+            return false;
+        }
+        buf[msg.status.msglen] = 0;
+        Error("failed to copy '%s' to '%s': %s", from, to, &buf[0]);
+        return false;
+    }
 
-        if(!ReadFdExactly(fd, buf, len)) break;
-        buf[len] = 0;
+    std::string TransferRate() {
+        uint64_t ms = CurrentTimeMs() - start_time_ms_;
+        if (total_bytes == 0 || ms == 0) return "";
 
-        func(ltohl(msg.dent.mode),
-             ltohl(msg.dent.size),
-             ltohl(msg.dent.time),
-             buf, cookie);
+        double s = static_cast<double>(ms) / 1000LL;
+        double rate = (static_cast<double>(total_bytes) / s) / (1024*1024);
+        return android::base::StringPrintf(" %.1f MB/s (%" PRId64 " bytes in %.3fs)",
+                                           rate, total_bytes, s);
     }
 
-fail:
-    adb_close(fd);
-    return -1;
-}
+    void Print(const std::string& s) {
+        // TODO: we actually don't want ELIDE; we want "ELIDE if smart, FULL if dumb".
+        line_printer_.Print(s, LinePrinter::ELIDE);
+    }
 
-struct syncsendbuf {
-    unsigned id;
-    unsigned size;
-    char data[SYNC_DATA_MAX];
-};
+    void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+        std::string s;
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
 
-static syncsendbuf send_buffer;
+        Print(s);
+    }
 
-int sync_readtime(int fd, const char *path, unsigned int *timestamp,
-                  unsigned int *mode)
-{
-    syncmsg msg;
-    int len = strlen(path);
+    void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+        std::string s = "adb: error: ";
 
-    msg.req.id = ID_STAT;
-    msg.req.namelen = htoll(len);
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
 
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, path, len)) {
-        return -1;
+        line_printer_.Print(s, LinePrinter::FULL);
     }
 
-    if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat))) {
-        return -1;
-    }
+    void Warning(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+        std::string s = "adb: warning: ";
 
-    if(msg.stat.id != ID_STAT) {
-        return -1;
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
+
+        line_printer_.Print(s, LinePrinter::FULL);
     }
 
-    *timestamp = ltohl(msg.stat.time);
-    *mode = ltohl(msg.stat.mode);
-    return 0;
-}
+    uint64_t total_bytes;
 
-static int sync_start_readtime(int fd, const char *path)
-{
-    syncmsg msg;
-    int len = strlen(path);
+    // TODO: add a char[max] buffer here, to replace syncsendbuf...
+    int fd;
+    size_t max;
 
-    msg.req.id = ID_STAT;
-    msg.req.namelen = htoll(len);
+  private:
+    uint64_t start_time_ms_;
 
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, path, len)) {
-        return -1;
-    }
+    LinePrinter line_printer_;
 
-    return 0;
-}
+    bool SendQuit() {
+        return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
+    }
 
-static int sync_finish_readtime(int fd, unsigned int *timestamp,
-                                unsigned int *mode, unsigned int *size)
-{
-    syncmsg msg;
+    static uint64_t CurrentTimeMs() {
+        struct timeval tv;
+        gettimeofday(&tv, 0); // (Not clock_gettime because of Mac/Windows.)
+        return static_cast<uint64_t>(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
+    }
+};
 
-    if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat)))
-        return -1;
+typedef void (sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name);
 
-    if(msg.stat.id != ID_STAT)
-        return -1;
+static bool sync_ls(SyncConnection& sc, const char* path,
+                    std::function<sync_ls_cb> func) {
+    if (!sc.SendRequest(ID_LIST, path)) return false;
 
-    *timestamp = ltohl(msg.stat.time);
-    *mode = ltohl(msg.stat.mode);
-    *size = ltohl(msg.stat.size);
+    while (true) {
+        syncmsg msg;
+        if (!ReadFdExactly(sc.fd, &msg.dent, sizeof(msg.dent))) return false;
 
-    return 0;
-}
+        if (msg.dent.id == ID_DONE) return true;
+        if (msg.dent.id != ID_DENT) return false;
 
-int sync_readmode(int fd, const char *path, unsigned *mode)
-{
-    syncmsg msg;
-    int len = strlen(path);
+        size_t len = msg.dent.namelen;
+        if (len > 256) return false; // TODO: resize buffer? continue?
 
-    msg.req.id = ID_STAT;
-    msg.req.namelen = htoll(len);
+        char buf[257];
+        if (!ReadFdExactly(sc.fd, buf, len)) return false;
+        buf[len] = 0;
 
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, path, len)) {
-        return -1;
+        func(msg.dent.mode, msg.dent.size, msg.dent.time, buf);
     }
+}
 
-    if(!ReadFdExactly(fd, &msg.stat, sizeof(msg.stat))) {
-        return -1;
+static bool sync_finish_stat(SyncConnection& sc, unsigned int* timestamp,
+                             unsigned int* mode, unsigned int* size) {
+    syncmsg msg;
+    if (!ReadFdExactly(sc.fd, &msg.stat, sizeof(msg.stat)) || msg.stat.id != ID_STAT) {
+        return false;
     }
 
-    if(msg.stat.id != ID_STAT) {
-        return -1;
-    }
+    if (timestamp) *timestamp = msg.stat.time;
+    if (mode) *mode = msg.stat.mode;
+    if (size) *size = msg.stat.size;
 
-    *mode = ltohl(msg.stat.mode);
-    return 0;
+    return true;
 }
 
-static int write_data_file(int fd, const char *path, syncsendbuf *sbuf, int show_progress)
-{
-    int lfd, err = 0;
-    unsigned long long size = 0;
+static bool sync_stat(SyncConnection& sc, const char* path,
+                      unsigned int* timestamp, unsigned int* mode, unsigned int* size) {
+    return sc.SendRequest(ID_STAT, path) && sync_finish_stat(sc, timestamp, mode, size);
+}
 
-    lfd = adb_open(path, O_RDONLY);
-    if(lfd < 0) {
-        fprintf(stderr,"cannot open '%s': %s\n", path, strerror(errno));
-        return -1;
+static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode,
+                          const char* lpath, const char* rpath,
+                          unsigned mtime) {
+    if (!sc.SendRequest(ID_SEND, path_and_mode)) {
+        sc.Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
+        return false;
     }
 
-    if (show_progress) {
-        // Determine local file size.
-        struct stat st;
-        if (stat(path, &st)) {
-            fprintf(stderr,"cannot stat '%s': %s\n", path, strerror(errno));
-            return -1;
-        }
-
-        size = st.st_size;
+    struct stat st;
+    if (stat(lpath, &st) == -1) {
+        sc.Error("cannot stat '%s': %s", lpath, strerror(errno));
+        return false;
     }
 
-    sbuf->id = ID_DATA;
-    for(;;) {
-        int ret;
+    uint64_t total_size = st.st_size;
+    uint64_t bytes_copied = 0;
 
-        ret = adb_read(lfd, sbuf->data, SYNC_DATA_MAX);
-        if(!ret)
-            break;
+    int lfd = adb_open(lpath, O_RDONLY);
+    if (lfd < 0) {
+        sc.Error("cannot open '%s': %s", lpath, strerror(errno));
+        return false;
+    }
 
-        if(ret < 0) {
-            if(errno == EINTR)
-                continue;
-            fprintf(stderr,"cannot read '%s': %s\n", path, strerror(errno));
+    syncsendbuf sbuf;
+    sbuf.id = ID_DATA;
+    while (true) {
+        int ret = adb_read(lfd, sbuf.data, sc.max);
+        if (ret <= 0) {
+            if (ret < 0) {
+                sc.Error("cannot read '%s': %s", lpath, strerror(errno));
+                adb_close(lfd);
+                return false;
+            }
             break;
         }
 
-        sbuf->size = htoll(ret);
-        if(!WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + ret)){
-            err = -1;
-            break;
+        sbuf.size = ret;
+        if (!WriteFdExactly(sc.fd, &sbuf, sizeof(unsigned) * 2 + ret)) {
+            adb_close(lfd);
+            return false;
         }
-        total_bytes += ret;
+        sc.total_bytes += ret;
 
-        if (show_progress) {
-            print_transfer_progress(total_bytes, size);
-        }
+        bytes_copied += ret;
+
+        int percentage = static_cast<int>(bytes_copied * 100 / total_size);
+        sc.Printf("%s: %d%%", rpath, percentage);
     }
 
     adb_close(lfd);
-    return err;
-}
-
-static int write_data_buffer(int fd, char* file_buffer, int size, syncsendbuf *sbuf,
-                             int show_progress)
-{
-    int err = 0;
-    int total = 0;
-
-    sbuf->id = ID_DATA;
-    while (total < size) {
-        int count = size - total;
-        if (count > SYNC_DATA_MAX) {
-            count = SYNC_DATA_MAX;
-        }
-
-        memcpy(sbuf->data, &file_buffer[total], count);
-        sbuf->size = htoll(count);
-        if(!WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + count)){
-            err = -1;
-            break;
-        }
-        total += count;
-        total_bytes += count;
 
-        if (show_progress) {
-            print_transfer_progress(total, size);
-        }
+    syncmsg msg;
+    msg.data.id = ID_DONE;
+    msg.data.size = mtime;
+    if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
+        sc.Error("failed to send ID_DONE message for '%s': %s", rpath, strerror(errno));
+        return false;
     }
 
-    return err;
+    return true;
 }
 
-#if defined(_WIN32)
-extern int write_data_link(int fd, const char *path, syncsendbuf *sbuf) __attribute__((error("no symlinks on Windows")));
-#else
-static int write_data_link(int fd, const char *path, syncsendbuf *sbuf)
+static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
+                      unsigned mtime, mode_t mode)
 {
-    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 = !WriteFdExactly(fd, sbuf, sizeof(unsigned) * 2 + len + 1);
-    if(ret)
-        return -1;
+    std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
 
-    total_bytes += len + 1;
+    if (S_ISLNK(mode)) {
+#if !defined(_WIN32)
+        char buf[PATH_MAX];
+        ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
+        if (data_length == -1) {
+            sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
+            return false;
+        }
+        buf[data_length++] = '\0';
 
-    return 0;
-}
+        if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, buf, data_length, mtime)) return false;
+        return sc.CopyDone(lpath, rpath);
 #endif
-
-static int sync_send(int fd, const char *lpath, const char *rpath,
-                     unsigned mtime, mode_t mode, int show_progress)
-{
-    syncmsg msg;
-    int len, r;
-    syncsendbuf *sbuf = &send_buffer;
-    char* file_buffer = NULL;
-    int size = 0;
-    char tmp[64];
-
-    len = strlen(rpath);
-    if(len > 1024) goto fail;
-
-    snprintf(tmp, sizeof(tmp), ",%d", mode);
-    r = strlen(tmp);
-
-    msg.req.id = ID_SEND;
-    msg.req.namelen = htoll(len + r);
-
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, rpath, len) || !WriteFdExactly(fd, tmp, r)) {
-        free(file_buffer);
-        goto fail;
     }
 
-    if (file_buffer) {
-        write_data_buffer(fd, file_buffer, size, sbuf, show_progress);
-        free(file_buffer);
-    } else if (S_ISREG(mode))
-        write_data_file(fd, lpath, sbuf, show_progress);
-    else if (S_ISLNK(mode))
-        write_data_link(fd, lpath, sbuf);
-    else
-        goto fail;
-
-    msg.data.id = ID_DONE;
-    msg.data.size = htoll(mtime);
-    if(!WriteFdExactly(fd, &msg.data, sizeof(msg.data)))
-        goto fail;
-
-    if(!ReadFdExactly(fd, &msg.status, sizeof(msg.status)))
-        return -1;
-
-    if(msg.status.id != ID_OKAY) {
-        if(msg.status.id == ID_FAIL) {
-            len = ltohl(msg.status.msglen);
-            if(len > 256) len = 256;
-            if(!ReadFdExactly(fd, sbuf->data, len)) {
-                return -1;
-            }
-            sbuf->data[len] = 0;
-        } else
-            strcpy(sbuf->data, "unknown reason");
-
-        fprintf(stderr,"failed to copy '%s' to '%s': %s\n", lpath, rpath, sbuf->data);
-        return -1;
+    if (!S_ISREG(mode)) {
+        sc.Error("local file '%s' has unsupported mode: 0o%o", lpath, mode);
+        return false;
     }
 
-    return 0;
-
-fail:
-    fprintf(stderr,"protocol failure\n");
-    adb_close(fd);
-    return -1;
-}
-
-static int mkdirs(const char *name)
-{
-    int ret;
-    char *x = (char *)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;
+    struct stat st;
+    if (stat(lpath, &st) == -1) {
+        sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
+        return false;
+    }
+    if (st.st_size < SYNC_DATA_MAX) {
+        std::string data;
+        if (!android::base::ReadFileToString(lpath, &data)) {
+            sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
+            return false;
+        }
+        if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, data.data(), data.size(), mtime)) {
+            return false;
+        }
+    } else {
+        if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, rpath, mtime)) {
+            return false;
         }
-        x++;
     }
-    return 0;
+    return sc.CopyDone(lpath, rpath);
 }
 
-int sync_recv(int fd, const char *rpath, const char *lpath, int show_progress)
-{
-    syncmsg msg;
-    int len;
-    int lfd = -1;
-    char *buffer = send_buffer.data;
-    unsigned id;
-    unsigned long long size = 0;
-
-    len = strlen(rpath);
-    if(len > 1024) return -1;
-
-    if (show_progress) {
-        // Determine remote file size.
-        syncmsg stat_msg;
-        stat_msg.req.id = ID_STAT;
-        stat_msg.req.namelen = htoll(len);
-
-        if (!WriteFdExactly(fd, &stat_msg.req, sizeof(stat_msg.req)) ||
-            !WriteFdExactly(fd, rpath, len)) {
-            return -1;
-        }
+static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) {
+    sc.Print(rpath);
 
-        if (!ReadFdExactly(fd, &stat_msg.stat, sizeof(stat_msg.stat))) {
-            return -1;
-        }
+    unsigned size = 0;
+    if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
 
-        if (stat_msg.stat.id != ID_STAT) return -1;
+    if (!sc.SendRequest(ID_RECV, rpath)) return false;
 
-        size = ltohl(stat_msg.stat.size);
+    adb_unlink(lpath);
+    if (!mkdirs(adb_dirname(lpath))) {
+        sc.Error("failed to create parent directory '%s': %s",
+                 adb_dirname(lpath).c_str(), strerror(errno));
+        return false;
     }
 
-    msg.req.id = ID_RECV;
-    msg.req.namelen = htoll(len);
-    if(!WriteFdExactly(fd, &msg.req, sizeof(msg.req)) ||
-       !WriteFdExactly(fd, rpath, len)) {
-        return -1;
+    int lfd = adb_creat(lpath, 0644);
+    if (lfd < 0) {
+        sc.Error("cannot create '%s': %s", lpath, strerror(errno));
+        return false;
     }
 
-    if(!ReadFdExactly(fd, &msg.data, sizeof(msg.data))) {
-        return -1;
-    }
-    id = msg.data.id;
-
-    if((id == ID_DATA) || (id == ID_DONE)) {
-        adb_unlink(lpath);
-        mkdirs(lpath);
-        lfd = adb_creat(lpath, 0644);
-        if(lfd < 0) {
-            fprintf(stderr,"cannot create '%s': %s\n", lpath, strerror(errno));
-            return -1;
+    uint64_t bytes_copied = 0;
+    while (true) {
+        syncmsg msg;
+        if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
+            adb_close(lfd);
+            adb_unlink(lpath);
+            return false;
         }
-        goto handle_data;
-    } else {
-        goto remote_error;
-    }
 
-    for(;;) {
-        if(!ReadFdExactly(fd, &msg.data, sizeof(msg.data))) {
-            return -1;
-        }
-        id = msg.data.id;
-
-    handle_data:
-        len = ltohl(msg.data.size);
-        if(id == ID_DONE) break;
-        if(id != ID_DATA) goto remote_error;
-        if(len > SYNC_DATA_MAX) {
-            fprintf(stderr,"data overrun\n");
+        if (msg.data.id == ID_DONE) break;
+
+        if (msg.data.id != ID_DATA) {
             adb_close(lfd);
-            return -1;
+            adb_unlink(lpath);
+            sc.ReportCopyFailure(rpath, lpath, msg);
+            return false;
         }
 
-        if(!ReadFdExactly(fd, buffer, len)) {
+        if (msg.data.size > sc.max) {
+            sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
             adb_close(lfd);
-            return -1;
+            adb_unlink(lpath);
+            return false;
         }
 
-        if(!WriteFdExactly(lfd, buffer, len)) {
-            fprintf(stderr,"cannot write '%s': %s\n", rpath, strerror(errno));
+        char buffer[SYNC_DATA_MAX];
+        if (!ReadFdExactly(sc.fd, buffer, msg.data.size)) {
             adb_close(lfd);
-            return -1;
+            adb_unlink(lpath);
+            return false;
         }
 
-        total_bytes += len;
-
-        if (show_progress) {
-            print_transfer_progress(total_bytes, size);
+        if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
+            sc.Error("cannot write '%s': %s", lpath, strerror(errno));
+            adb_close(lfd);
+            adb_unlink(lpath);
+            return false;
         }
-    }
 
-    adb_close(lfd);
-    return 0;
+        sc.total_bytes += msg.data.size;
 
-remote_error:
-    adb_close(lfd);
-    adb_unlink(lpath);
+        bytes_copied += msg.data.size;
 
-    if(id == ID_FAIL) {
-        len = ltohl(msg.data.size);
-        if(len > 256) len = 256;
-        if(!ReadFdExactly(fd, buffer, len)) {
-            return -1;
-        }
-        buffer[len] = 0;
-    } else {
-        memcpy(buffer, &id, 4);
-        buffer[4] = 0;
+        int percentage = static_cast<int>(bytes_copied * 100 / size);
+        sc.Printf("%s: %d%%", rpath, percentage);
     }
-    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);
+    adb_close(lfd);
+    return true;
 }
 
-int do_sync_ls(const char* path) {
-    std::string error;
-    int fd = adb_connect("sync:", &error);
-    if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error.c_str());
-        return 1;
-    }
+bool do_sync_ls(const char* path) {
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
 
-    if(sync_ls(fd, path, do_sync_ls_cb, 0)) {
-        return 1;
-    } else {
-        sync_quit(fd);
-        return 0;
-    }
+    return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time,
+                                const char* name) {
+        printf("%08x %08x %08x %s\n", mode, size, time, name);
+    });
 }
 
 struct copyinfo
 {
-    copyinfo *next;
-    const char *src;
-    const char *dst;
+    std::string lpath;
+    std::string rpath;
     unsigned int time;
     unsigned int mode;
-    unsigned int size;
-    int flag;
+    uint64_t size;
+    bool skip;
 };
 
-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 = reinterpret_cast<copyinfo*>(
-        malloc(sizeof(copyinfo) + ssize + dsize));
-    if(ci == 0) {
-        fprintf(stderr,"out of memory\n");
-        abort();
+static void ensure_trailing_separator(std::string& lpath, std::string& rpath) {
+    if (!adb_is_separator(lpath.back())) {
+        lpath.push_back(OS_PATH_SEPARATOR);
+    }
+    if (rpath.back() != '/') {
+        rpath.push_back('/');
     }
-
-    ci->next = 0;
-    ci->time = 0;
-    ci->mode = 0;
-    ci->size = 0;
-    ci->flag = 0;
-    ci->src = (const char*)(ci + 1);
-    ci->dst = ci->src + ssize;
-    snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name);
-    snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name);
-
-    return ci;
 }
 
+static copyinfo mkcopyinfo(std::string lpath, std::string rpath,
+                           const std::string& name, unsigned int mode) {
+    copyinfo result;
+    result.lpath = std::move(lpath);
+    result.rpath = std::move(rpath);
+    ensure_trailing_separator(result.lpath, result.rpath);
+    result.lpath.append(name);
+    result.rpath.append(name);
 
-static int local_build_list(copyinfo **filelist,
-                            const char *lpath, const char *rpath)
-{
-    DIR *d;
-    struct dirent *de;
-    struct stat st;
-    copyinfo *dirlist = 0;
-    copyinfo *ci, *next;
-
-    d = opendir(lpath);
-    if(d == 0) {
-        fprintf(stderr,"cannot open '%s': %s\n", lpath, strerror(errno));
-        return -1;
+    if (S_ISDIR(mode)) {
+        ensure_trailing_separator(result.lpath, result.rpath);
     }
 
-    while((de = readdir(d))) {
-        char stat_path[PATH_MAX];
-        char *name = de->d_name;
+    result.time = 0;
+    result.mode = mode;
+    result.size = 0;
+    result.skip = false;
+    return result;
+}
+
+static bool IsDotOrDotDot(const char* name) {
+    return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
+}
+
+static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* filelist,
+                             const std::string& lpath,
+                             const std::string& rpath) {
+    std::vector<copyinfo> dirlist;
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir);
+    if (!dir) {
+        sc.Error("cannot open '%s': %s", lpath.c_str(), strerror(errno));
+        return false;
+    }
 
-        if(name[0] == '.') {
-            if(name[1] == 0) continue;
-            if((name[1] == '.') && (name[2] == 0)) continue;
+    bool empty_dir = true;
+    dirent* de;
+    while ((de = readdir(dir.get()))) {
+        if (IsDotOrDotDot(de->d_name)) {
+            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))
+        empty_dir = false;
+        std::string stat_path = lpath + de->d_name;
+
+        struct stat st;
+        if (lstat(stat_path.c_str(), &st) == -1) {
+            sc.Error("cannot lstat '%s': %s", stat_path.c_str(),
+                     strerror(errno));
             continue;
-        strcpy(stat_path, lpath);
-        strcat(stat_path, de->d_name);
-
-        if(!lstat(stat_path, &st)) {
-            if (S_ISDIR(st.st_mode)) {
-                ci = mkcopyinfo(lpath, rpath, name, 1);
-                ci->next = dirlist;
-                dirlist = ci;
+        }
+
+        copyinfo ci = mkcopyinfo(lpath, rpath, de->d_name, st.st_mode);
+        if (S_ISDIR(st.st_mode)) {
+            dirlist.push_back(ci);
+        } else {
+            if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
+                sc.Error("skipping special file '%s'", lpath.c_str());
             } else {
-                ci = mkcopyinfo(lpath, rpath, name, 0);
-                if(lstat(ci->src, &st)) {
-                    fprintf(stderr,"cannot stat '%s': %s\n", ci->src, strerror(errno));
-                    free(ci);
-                    closedir(d);
-                    return -1;
-                }
-                if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
-                    fprintf(stderr, "skipping special file '%s'\n", ci->src);
-                    free(ci);
-                } else {
-                    ci->time = st.st_mtime;
-                    ci->mode = st.st_mode;
-                    ci->size = st.st_size;
-                    ci->next = *filelist;
-                    *filelist = ci;
-                }
+                ci.time = st.st_mtime;
+                ci.size = st.st_size;
+                filelist->push_back(ci);
             }
-        } else {
-            fprintf(stderr, "cannot lstat '%s': %s\n",stat_path , strerror(errno));
         }
     }
 
-    closedir(d);
+    // Close this directory and recurse.
+    dir.reset();
 
-    for(ci = dirlist; ci != 0; ci = next) {
-        next = ci->next;
-        local_build_list(filelist, ci->src, ci->dst);
-        free(ci);
+    // Add the current directory to the list if it was empty, to ensure that
+    // it gets created.
+    if (empty_dir) {
+        // TODO(b/25566053): Make pushing empty directories work.
+        // TODO(b/25457350): We don't preserve permissions on directories.
+        sc.Warning("skipping empty directory '%s'", lpath.c_str());
+        copyinfo ci = mkcopyinfo(adb_dirname(lpath), adb_dirname(rpath),
+                                 adb_basename(lpath), S_IFDIR);
+        ci.skip = true;
+        filelist->push_back(ci);
+        return true;
     }
 
-    return 0;
+    for (const copyinfo& ci : dirlist) {
+        local_build_list(sc, filelist, ci.lpath, ci.rpath);
+    }
+
+    return true;
 }
 
+static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
+                                  std::string rpath, bool check_timestamps,
+                                  bool list_only) {
+    // Make sure that both directory paths end in a slash.
+    // Both paths are known to be nonempty, so we don't need to check.
+    ensure_trailing_separator(lpath, rpath);
 
-static int copy_local_dir_remote(int fd, const char *lpath, const char *rpath, int checktimestamps, int listonly)
-{
-    copyinfo *filelist = 0;
-    copyinfo *ci, *next;
+    // Recursively build the list of files to copy.
+    std::vector<copyinfo> filelist;
     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 = reinterpret_cast<char*>(malloc(tmplen));
-        if(tmp == 0) return -1;
-        snprintf(tmp, tmplen, "%s/",lpath);
-        lpath = tmp;
-    }
-    if(rpath[strlen(rpath) - 1] != '/') {
-        int tmplen = strlen(rpath)+2;
-        char *tmp = reinterpret_cast<char*>(malloc(tmplen));
-        if(tmp == 0) return -1;
-        snprintf(tmp, tmplen, "%s/",rpath);
-        rpath = tmp;
+    if (!local_build_list(sc, &filelist, lpath, rpath)) {
+        return false;
     }
 
-    if(local_build_list(&filelist, lpath, rpath)) {
-        return -1;
-    }
-
-    if(checktimestamps){
-        for(ci = filelist; ci != 0; ci = ci->next) {
-            if(sync_start_readtime(fd, ci->dst)) {
-                return 1;
+    if (check_timestamps) {
+        for (const copyinfo& ci : filelist) {
+            if (!sc.SendRequest(ID_STAT, ci.rpath.c_str())) {
+                return false;
             }
         }
-        for(ci = filelist; ci != 0; ci = ci->next) {
+        for (copyinfo& ci : filelist) {
             unsigned int timestamp, mode, size;
-            if(sync_finish_readtime(fd, &timestamp, &mode, &size))
-                return 1;
-            if(size == ci->size) {
+            if (!sync_finish_stat(sc, &timestamp, &mode, &size)) {
+                return false;
+            }
+            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;
+                if ((S_ISREG(ci.mode & mode) && timestamp == ci.time) ||
+                        (S_ISLNK(ci.mode & mode) && timestamp >= ci.time)) {
+                    ci.skip = true;
+                }
             }
         }
     }
-    for(ci = filelist; ci != 0; ci = next) {
-        next = ci->next;
-        if(ci->flag == 0) {
-            fprintf(stderr,"%spush: %s -> %s\n", listonly ? "would " : "", ci->src, ci->dst);
-            if(!listonly &&
-               sync_send(fd, ci->src, ci->dst, ci->time, ci->mode,
-                         0 /* no show progress */)) {
-                return 1;
+
+    for (const copyinfo& ci : filelist) {
+        if (!ci.skip) {
+            if (list_only) {
+                sc.Error("would push: %s -> %s", ci.lpath.c_str(),
+                         ci.rpath.c_str());
+            } else {
+                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time,
+                               ci.mode)) {
+                    return false;
+                }
             }
             pushed++;
         } else {
             skipped++;
         }
-        free(ci);
     }
 
-    fprintf(stderr,"%d file%s pushed. %d file%s skipped.\n",
-            pushed, (pushed == 1) ? "" : "s",
-            skipped, (skipped == 1) ? "" : "s");
-
-    return 0;
+    sc.Printf("%s: %d file%s pushed. %d file%s skipped.%s\n", rpath.c_str(),
+              pushed, (pushed == 1) ? "" : "s", skipped,
+              (skipped == 1) ? "" : "s", sc.TransferRate().c_str());
+    return true;
 }
 
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
 
-int do_sync_push(const char *lpath, const char *rpath, int show_progress)
-{
-    struct stat st;
-    unsigned mode;
-
-    std::string error;
-    int fd = adb_connect("sync:", &error);
-    if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error.c_str());
-        return 1;
-    }
-
-    if(stat(lpath, &st)) {
-        fprintf(stderr,"cannot stat '%s': %s\n", lpath, strerror(errno));
-        sync_quit(fd);
-        return 1;
-    }
+    bool success = true;
+    unsigned dst_mode;
+    if (!sync_stat(sc, dst, nullptr, &dst_mode, nullptr)) return false;
+    bool dst_exists = (dst_mode != 0);
+    bool dst_isdir = S_ISDIR(dst_mode);
 
-    if(S_ISDIR(st.st_mode)) {
-        BEGIN();
-        if(copy_local_dir_remote(fd, lpath, rpath, 0, 0)) {
-            return 1;
+    if (!dst_isdir) {
+        if (srcs.size() > 1) {
+            sc.Error("target '%s' is not a directory", dst);
+            return false;
         } else {
-            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++;
+            size_t dst_len = strlen(dst);
+
+            // A path that ends with a slash doesn't have to be a directory if
+            // it doesn't exist yet.
+            if (dst[dst_len - 1] == '/' && dst_exists) {
+                sc.Error("failed to access '%s': Not a directory", dst);
+                return false;
             }
-            int  tmplen = strlen(name) + strlen(rpath) + 2;
-            char *tmp = reinterpret_cast<char*>(
-                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, show_progress)) {
-            return 1;
-        } else {
-            END();
-            sync_quit(fd);
-            return 0;
         }
     }
 
-    return 0;
-}
-
+    for (const char* src_path : srcs) {
+        const char* dst_path = dst;
+        struct stat st;
+        if (stat(src_path, &st) == -1) {
+            sc.Error("cannot stat '%s': %s", src_path, strerror(errno));
+            success = false;
+            continue;
+        }
 
-struct sync_ls_build_list_cb_args {
-    copyinfo **filelist;
-    copyinfo **dirlist;
-    const char *rpath;
-    const char *lpath;
-};
+        if (S_ISDIR(st.st_mode)) {
+            std::string dst_dir = dst;
 
-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;
+            // If the destination path existed originally, the source directory
+            // should be copied as a child of the destination.
+            if (dst_exists) {
+                if (!dst_isdir) {
+                    sc.Error("target '%s' is not a directory", dst);
+                    return false;
+                }
+                // dst is a POSIX path, so we don't want to use the sysdeps
+                // helpers here.
+                if (dst_dir.back() != '/') {
+                    dst_dir.push_back('/');
+                }
+                dst_dir.append(adb_basename(src_path));
+            }
 
-        /* Don't try recursing down "." or ".." */
-        if (name[0] == '.') {
-            if (name[1] == '\0') return;
-            if ((name[1] == '.') && (name[2] == '\0')) return;
+            success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(),
+                                             false, false);
+            continue;
         }
 
-        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);
+        std::string path_holder;
+        if (dst_isdir) {
+            // If we're copying a local file to a remote directory,
+            // we really want to copy to remote_dir + "/" + local_filename.
+            path_holder = android::base::StringPrintf(
+                "%s/%s", dst_path, adb_basename(src_path).c_str());
+            dst_path = path_holder.c_str();
+        }
+        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
     }
+
+    sc.Print("\n");
+    return success;
 }
 
-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;
+static bool remote_build_list(SyncConnection& sc,
+                              std::vector<copyinfo>* filelist,
+                              const std::string& rpath,
+                              const std::string& lpath) {
+    std::vector<copyinfo> dirlist;
+    bool empty_dir = true;
+
+    // Put the files/dirs in rpath on the lists.
+    auto callback = [&](unsigned mode, unsigned size, unsigned time,
+                        const char* name) {
+        if (IsDotOrDotDot(name)) {
+            return;
+        }
+
+        // We found a child that isn't '.' or '..'.
+        empty_dir = false;
+
+        copyinfo ci = mkcopyinfo(lpath, rpath, name, mode);
+        if (S_ISDIR(mode)) {
+            dirlist.push_back(ci);
+        } else if (S_ISREG(mode) || S_ISLNK(mode)) {
+            ci.time = time;
+            ci.size = size;
+            filelist->push_back(ci);
+        } else {
+            sc.Warning("skipping special file '%s'\n", name);
+        }
+    };
 
-    args.filelist = filelist;
-    args.dirlist = &dirlist;
-    args.rpath = rpath;
-    args.lpath = lpath;
+    if (!sync_ls(sc, rpath.c_str(), callback)) {
+        return false;
+    }
 
-    /* Put the files/dirs in rpath on the lists. */
-    if (sync_ls(syncfd, rpath, sync_ls_build_list_cb, (void *)&args)) {
-        return 1;
+    // Add the current directory to the list if it was empty, to ensure that
+    // it gets created.
+    if (empty_dir) {
+        filelist->push_back(mkcopyinfo(adb_dirname(lpath), adb_dirname(rpath),
+                                       adb_basename(rpath), S_IFDIR));
+        return true;
     }
 
-    /* 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;
+    // Recurse into each directory we found.
+    while (!dirlist.empty()) {
+        copyinfo current = dirlist.back();
+        dirlist.pop_back();
+        if (!remote_build_list(sc, filelist, current.rpath, current.lpath)) {
+            return false;
         }
-        free(dirlist);
-        dirlist = next;
     }
 
-    return 0;
+    return true;
 }
 
-static int set_time_and_mode(const char *lpath, time_t time, unsigned int mode)
-{
+static int set_time_and_mode(const std::string& lpath, time_t time,
+                             unsigned int mode) {
     struct utimbuf times = { time, time };
-    int r1 = utime(lpath, &times);
+    int r1 = utime(lpath.c_str(), &times);
 
     /* use umask for permissions */
-    mode_t mask=umask(0000);
+    mode_t mask = umask(0000);
     umask(mask);
-    int r2 = chmod(lpath, mode & ~mask);
+    int r2 = chmod(lpath.c_str(), mode & ~mask);
 
-    return r1 ? : r2;
+    return r1 ? r1 : r2;
 }
 
-/* Return a copy of the path string with / appended if needed */
-static char *add_slash_to_path(const char *path)
-{
-    if (path[strlen(path) - 1] != '/') {
-        size_t len = strlen(path) + 2;
-        char *path_with_slash = reinterpret_cast<char*>(malloc(len));
-        if (path_with_slash == NULL)
-            return NULL;
-        snprintf(path_with_slash, len, "%s/", path);
-        return path_with_slash;
-    } else {
-        return strdup(path);
+static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
+                                  std::string lpath, bool copy_attrs) {
+    // Make sure that both directory paths end in a slash.
+    // Both paths are known to be nonempty, so we don't need to check.
+    ensure_trailing_separator(lpath, rpath);
+
+    // Recursively build the list of files to copy.
+    sc.Print("pull: building file list...");
+    std::vector<copyinfo> filelist;
+    if (!remote_build_list(sc, &filelist, rpath.c_str(), lpath.c_str())) {
+        return false;
     }
-}
 
-static int copy_remote_dir_local(int fd, const char *rpath, const char *lpath,
-                                 int copy_attrs)
-{
-    copyinfo *filelist = 0;
-    copyinfo *ci, *next;
     int pulled = 0;
     int skipped = 0;
-    char *rpath_clean = NULL;
-    char *lpath_clean = NULL;
-    int ret = 0;
-
-    if (rpath[0] == '\0' || lpath[0] == '\0') {
-        ret = -1;
-        goto finish;
-    }
-
-    /* Make sure that both directory paths end in a slash. */
-    rpath_clean = add_slash_to_path(rpath);
-    if (!rpath_clean) {
-        ret = -1;
-        goto finish;
-    }
-    lpath_clean = add_slash_to_path(lpath);
-    if (!lpath_clean) {
-        ret = -1;
-        goto finish;
-    }
-
-    /* Recursively build the list of files to copy. */
-    fprintf(stderr, "pull: building file list...\n");
-    if (remote_build_list(fd, &filelist, rpath_clean, lpath_clean)) {
-        ret = -1;
-        goto finish;
-    }
+    for (const copyinfo &ci : filelist) {
+        if (!ci.skip) {
+            sc.Printf("pull: %s -> %s", ci.rpath.c_str(), ci.lpath.c_str());
+
+            if (S_ISDIR(ci.mode)) {
+                // Entry is for an empty directory, create it and continue.
+                // TODO(b/25457350): We don't preserve permissions on directories.
+                if (!mkdirs(ci.lpath))  {
+                    sc.Error("failed to create directory '%s': %s",
+                             ci.lpath.c_str(), strerror(errno));
+                    return false;
+                }
+                pulled++;
+                continue;
+            }
 
-    for (ci = filelist; ci != 0; ci = next) {
-        next = ci->next;
-        if (ci->flag == 0) {
-            fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
-            if (sync_recv(fd, ci->src, ci->dst, 0 /* no show progress */)) {
-                ret = -1;
-                goto finish;
+            if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str())) {
+                return false;
             }
 
-            if (copy_attrs && set_time_and_mode(ci->dst, ci->time, ci->mode)) {
-                ret = -1;
-                goto finish;
+            if (copy_attrs && set_time_and_mode(ci.lpath, ci.time, ci.mode)) {
+                return false;
             }
             pulled++;
         } else {
             skipped++;
         }
-        free(ci);
     }
 
-    fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
-            pulled, (pulled == 1) ? "" : "s",
-            skipped, (skipped == 1) ? "" : "s");
-
-finish:
-    free(lpath_clean);
-    free(rpath_clean);
-    return ret;
+    sc.Printf("%s: %d file%s pulled. %d file%s skipped.%s\n", rpath.c_str(),
+              pulled, (pulled == 1) ? "" : "s", skipped,
+              (skipped == 1) ? "" : "s", sc.TransferRate().c_str());
+    return true;
 }
 
-int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int copy_attrs)
-{
-    unsigned mode, time;
-    struct stat st;
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+                  bool copy_attrs) {
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
 
-    std::string error;
-    int fd = adb_connect("sync:", &error);
-    if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error.c_str());
-        return 1;
+    bool success = true;
+    struct stat st;
+    bool dst_exists = true;
+
+    if (stat(dst, &st) == -1) {
+        dst_exists = false;
+
+        // If we're only pulling one path, the destination path might point to
+        // a path that doesn't exist yet.
+        if (srcs.size() == 1 && errno == ENOENT) {
+            // However, its parent must exist.
+            struct stat parent_st;
+            if (stat(adb_dirname(dst).c_str(), &parent_st) == -1) {
+                sc.Error("cannot create file/directory '%s': %s", dst, strerror(errno));
+                return false;
+            }
+        } else {
+            sc.Error("failed to access '%s': %s", dst, strerror(errno));
+            return false;
+        }
     }
 
-    if(sync_readtime(fd, rpath, &time, &mode)) {
-        return 1;
-    }
-    if(mode == 0) {
-        fprintf(stderr,"remote object '%s' does not exist\n", rpath);
-        return 1;
-    }
+    bool dst_isdir = dst_exists && S_ISDIR(st.st_mode);
+    if (!dst_isdir) {
+        if (srcs.size() > 1) {
+            sc.Error("target '%s' is not a directory", dst);
+            return false;
+        } else {
+            size_t dst_len = strlen(dst);
 
-    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 = reinterpret_cast<char*>(malloc(tmplen));
-                if(tmp == 0) return 1;
-                snprintf(tmp, tmplen, "%s/%s", lpath, name);
-                lpath = tmp;
+            // A path that ends with a slash doesn't have to be a directory if
+            // it doesn't exist yet.
+            if (adb_is_separator(dst[dst_len - 1]) && dst_exists) {
+                sc.Error("failed to access '%s': Not a directory", dst);
+                return false;
             }
         }
-        BEGIN();
-        if (sync_recv(fd, rpath, lpath, show_progress)) {
-            return 1;
-        } else {
-            if (copy_attrs && set_time_and_mode(lpath, time, mode))
-                return 1;
-            END();
-            sync_quit(fd);
-            return 0;
+    }
+
+    for (const char* src_path : srcs) {
+        const char* dst_path = dst;
+        unsigned src_mode, src_time;
+        if (!sync_stat(sc, src_path, &src_time, &src_mode, nullptr)) {
+            return false;
+        }
+        if (src_mode == 0) {
+            sc.Error("remote object '%s' does not exist", src_path);
+            success = false;
+            continue;
         }
-    } else if(S_ISDIR(mode)) {
-        BEGIN();
-        if (copy_remote_dir_local(fd, rpath, lpath, copy_attrs)) {
-            return 1;
+
+        if (S_ISREG(src_mode)) {
+            std::string path_holder;
+            if (dst_isdir) {
+                // If we're copying a remote file to a local directory, we
+                // really want to copy to local_dir + OS_PATH_SEPARATOR +
+                // basename(remote).
+                path_holder = android::base::StringPrintf(
+                    "%s%c%s", dst_path, OS_PATH_SEPARATOR,
+                    adb_basename(src_path).c_str());
+                dst_path = path_holder.c_str();
+            }
+            if (!sync_recv(sc, src_path, dst_path)) {
+                success = false;
+                continue;
+            } else {
+                if (copy_attrs &&
+                    set_time_and_mode(dst_path, src_time, src_mode) != 0) {
+                    success = false;
+                    continue;
+                }
+            }
+        } else if (S_ISDIR(src_mode)) {
+            std::string dst_dir = dst;
+
+            // If the destination path existed originally, the source directory
+            // should be copied as a child of the destination.
+            if (dst_exists) {
+                if (!dst_isdir) {
+                    sc.Error("target '%s' is not a directory", dst);
+                    return false;
+                }
+                if (!adb_is_separator(dst_dir.back())) {
+                    dst_dir.push_back(OS_PATH_SEPARATOR);
+                }
+                dst_dir.append(adb_basename(src_path));
+            }
+
+            success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(),
+                                             copy_attrs);
+            continue;
         } else {
-            END();
-            sync_quit(fd);
-            return 0;
+            sc.Error("remote object '%s' not a file or directory", src_path);
+            success = false;
+            continue;
         }
-    } else {
-        fprintf(stderr,"remote object '%s' not a file or directory\n", rpath);
-        return 1;
     }
-}
 
-int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only)
-{
-    fprintf(stderr, "syncing %s...\n", rpath.c_str());
+    sc.Print("\n");
+    return success;
+}
 
-    std::string error;
-    int fd = adb_connect("sync:", &error);
-    if (fd < 0) {
-        fprintf(stderr, "error: %s\n", error.c_str());
-        return 1;
-    }
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
+    SyncConnection sc;
+    if (!sc.IsValid()) return false;
 
-    BEGIN();
-    if (copy_local_dir_remote(fd, lpath.c_str(), rpath.c_str(), 1, list_only)) {
-        return 1;
-    } else {
-        END();
-        sync_quit(fd);
-        return 0;
-    }
+    return copy_local_dir_remote(sc, lpath, rpath, true, list_only);
 }
index 5d17335..7484a7c 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_SYNC
+#define TRACE_TAG SYNC
 
 #include "sysdeps.h"
 #include "file_sync_service.h"
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "private/android_filesystem_config.h"
 
-static bool should_use_fs_config(const char* path) {
+#include <base/stringprintf.h>
+#include <base/strings.h>
+
+static bool should_use_fs_config(const std::string& path) {
     // TODO: use fs_config to configure permissions on /data.
-    return strncmp("/system/", path, strlen("/system/")) == 0 ||
-           strncmp("/vendor/", path, strlen("/vendor/")) == 0 ||
-           strncmp("/oem/", path, strlen("/oem/")) == 0;
+    return android::base::StartsWith(path, "/system/") ||
+           android::base::StartsWith(path, "/vendor/") ||
+           android::base::StartsWith(path, "/oem/");
 }
 
-static int mkdirs(char *name)
-{
-    int ret;
-    char *x = name + 1;
+static bool secure_mkdirs(const std::string& path) {
     uid_t uid = -1;
     gid_t gid = -1;
     unsigned int mode = 0775;
     uint64_t cap = 0;
 
-    if(name[0] != '/') return -1;
+    if (path[0] != '/') return false;
+
+    std::vector<std::string> path_components = android::base::Split(path, "/");
+    std::string partial_path;
+    for (const auto& path_component : path_components) {
+        if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
+        partial_path += path_component;
 
-    for(;;) {
-        x = adb_dirstart(x);
-        if(x == 0) return 0;
-        *x = 0;
-        if (should_use_fs_config(name)) {
-            fs_config(name, 1, NULL, &uid, &gid, &mode, &cap);
+        if (should_use_fs_config(partial_path)) {
+            fs_config(partial_path.c_str(), 1, nullptr, &uid, &gid, &mode, &cap);
         }
-        ret = adb_mkdir(name, mode);
-        if((ret < 0) && (errno != EEXIST)) {
-            D("mkdir(\"%s\") -> %s\n", name, strerror(errno));
-            *x = '/';
-            return ret;
-        } else if(ret == 0) {
-            ret = chown(name, uid, gid);
-            if (ret < 0) {
-                *x = '/';
-                return ret;
+        if (adb_mkdir(partial_path.c_str(), mode) == -1) {
+            if (errno != EEXIST) {
+                return false;
+            }
+        } else {
+            if (chown(partial_path.c_str(), uid, gid) == -1) {
+                return false;
             }
-            selinux_android_restorecon(name, 0);
+            // Not all filesystems support setting SELinux labels. http://b/23530370.
+            selinux_android_restorecon(partial_path.c_str(), 0);
         }
-        *x++ = '/';
     }
-    return 0;
+    return true;
 }
 
-static int do_stat(int s, const char *path)
-{
+static bool 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 WriteFdExactly(s, &msg.stat, sizeof(msg.stat)) ? 0 : -1;
-}
-
-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;
+    memset(&st, 0, sizeof(st));
+    // TODO: add a way to report that the stat failed!
+    lstat(path, &st);
+    msg.stat.mode = st.st_mode;
+    msg.stat.size = st.st_size;
+    msg.stat.time = st.st_mtime;
+
+    return WriteFdExactly(s, &msg.stat, sizeof(msg.stat));
+}
 
-    len = strlen(path);
-    memcpy(tmp, path, len);
-    tmp[len] = '/';
-    fname = tmp + len + 1;
+static bool do_list(int s, const char* path) {
+    dirent* de;
 
+    syncmsg msg;
     msg.dent.id = ID_DENT;
 
-    d = opendir(path);
-    if(d == 0) goto done;
-
-    while((de = readdir(d))) {
-        int len = strlen(de->d_name);
+    std::unique_ptr<DIR, int(*)(DIR*)> d(opendir(path), closedir);
+    if (!d) goto done;
 
-            /* not supposed to be possible, but
-               if it does happen, let's not buffer overrun */
-        if(len > 256) continue;
+    while ((de = readdir(d.get()))) {
+        std::string filename(android::base::StringPrintf("%s/%s", path, de->d_name));
 
-        strcpy(fname, de->d_name);
-        if(lstat(tmp, &st) == 0) {
-            msg.dent.mode = htoll(st.st_mode);
-            msg.dent.size = htoll(st.st_size);
-            msg.dent.time = htoll(st.st_mtime);
-            msg.dent.namelen = htoll(len);
-
-            if(!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
-               !WriteFdExactly(s, de->d_name, len)) {
-                closedir(d);
-                return -1;
+        struct stat st;
+        if (lstat(filename.c_str(), &st) == 0) {
+            size_t d_name_length = strlen(de->d_name);
+            msg.dent.mode = st.st_mode;
+            msg.dent.size = st.st_size;
+            msg.dent.time = st.st_mtime;
+            msg.dent.namelen = d_name_length;
+
+            if (!WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ||
+                    !WriteFdExactly(s, de->d_name, d_name_length)) {
+                return false;
             }
         }
     }
 
-    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 WriteFdExactly(s, &msg.dent, sizeof(msg.dent)) ? 0 : -1;
+    return WriteFdExactly(s, &msg.dent, sizeof(msg.dent));
 }
 
-static int fail_message(int s, const char *reason)
-{
-    syncmsg msg;
-    int len = strlen(reason);
-
-    D("sync: failure: %s\n", reason);
+static bool SendSyncFail(int fd, const std::string& reason) {
+    D("sync: failure: %s", reason.c_str());
 
+    syncmsg msg;
     msg.data.id = ID_FAIL;
-    msg.data.size = htoll(len);
-    if(!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
-       !WriteFdExactly(s, reason, len)) {
-        return -1;
-    } else {
-        return 0;
-    }
+    msg.data.size = reason.size();
+    return WriteFdExactly(fd, &msg.data, sizeof(msg.data)) && WriteFdExactly(fd, reason);
 }
 
-static int fail_errno(int s)
-{
-    return fail_message(s, strerror(errno));
+static bool SendSyncFailErrno(int fd, const std::string& reason) {
+    return SendSyncFail(fd, android::base::StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
 }
 
-static int handle_send_file(int s, char *path, uid_t uid,
-        gid_t gid, mode_t mode, char *buffer, bool do_unlink)
-{
+static bool handle_send_file(int s, const char* path, uid_t uid,
+                             gid_t gid, mode_t mode, std::vector<char>& buffer, bool do_unlink) {
     syncmsg msg;
     unsigned int timestamp = 0;
-    int fd;
-
-    fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
-    if(fd < 0 && errno == ENOENT) {
-        if(mkdirs(path) != 0) {
-            if(fail_errno(s))
-                return -1;
-            fd = -1;
-        } else {
-            fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+
+    int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
+    if (fd < 0 && errno == ENOENT) {
+        if (!secure_mkdirs(adb_dirname(path))) {
+            SendSyncFailErrno(s, "secure_mkdirs failed");
+            goto fail;
         }
+        fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
     }
-    if(fd < 0 && errno == EEXIST) {
+    if (fd < 0 && errno == EEXIST) {
         fd = adb_open_mode(path, O_WRONLY | O_CLOEXEC, mode);
     }
-    if(fd < 0) {
-        if(fail_errno(s))
-            return -1;
-        fd = -1;
+    if (fd < 0) {
+        SendSyncFailErrno(s, "couldn't create file");
+        goto fail;
     } else {
-        if(fchown(fd, uid, gid) != 0) {
-            fail_errno(s);
-            errno = 0;
+        if (fchown(fd, uid, gid) == -1) {
+            SendSyncFailErrno(s, "fchown failed");
+            goto fail;
         }
 
-        /*
-         * fchown clears the setuid bit - restore it if present.
-         * Ignore the result of calling fchmod. It's not supported
-         * by all filesystems. b/12441485
-         */
+        // Not all filesystems support setting SELinux labels. http://b/23530370.
+        selinux_android_restorecon(path, 0);
+
+        // fchown clears the setuid bit - restore it if present.
+        // Ignore the result of calling fchmod. It's not supported
+        // by all filesystems. b/12441485
         fchmod(fd, mode);
     }
 
-    for(;;) {
+    while (true) {
         unsigned int len;
 
-        if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
-            goto fail;
+        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
 
-        if(msg.data.id != ID_DATA) {
-            if(msg.data.id == ID_DONE) {
-                timestamp = ltohl(msg.data.size);
+        if (msg.data.id != ID_DATA) {
+            if (msg.data.id == ID_DONE) {
+                timestamp = msg.data.size;
                 break;
             }
-            fail_message(s, "invalid data message");
+            SendSyncFail(s, "invalid data message");
             goto fail;
         }
-        len = ltohl(msg.data.size);
-        if(len > SYNC_DATA_MAX) {
-            fail_message(s, "oversize data message");
+        len = msg.data.size;
+        if (len > buffer.size()) { // TODO: resize buffer?
+            SendSyncFail(s, "oversize data message");
             goto fail;
         }
-        if(!ReadFdExactly(s, buffer, len))
-            goto fail;
 
-        if(fd < 0)
-            continue;
-        if(!WriteFdExactly(fd, buffer, len)) {
-            int saved_errno = errno;
-            adb_close(fd);
-            if (do_unlink) adb_unlink(path);
-            fd = -1;
-            errno = saved_errno;
-            if(fail_errno(s)) return -1;
+        if (!ReadFdExactly(s, &buffer[0], len)) goto fail;
+
+        if (!WriteFdExactly(fd, &buffer[0], len)) {
+            SendSyncFailErrno(s, "write failed");
+            goto fail;
         }
     }
 
-    if(fd >= 0) {
-        struct utimbuf u;
-        adb_close(fd);
-        selinux_android_restorecon(path, 0);
-        u.actime = timestamp;
-        u.modtime = timestamp;
-        utime(path, &u);
+    adb_close(fd);
 
-        msg.status.id = ID_OKAY;
-        msg.status.msglen = 0;
-        if(!WriteFdExactly(s, &msg.status, sizeof(msg.status)))
-            return -1;
-    }
-    return 0;
+    utimbuf u;
+    u.actime = timestamp;
+    u.modtime = timestamp;
+    utime(path, &u);
+
+    msg.status.id = ID_OKAY;
+    msg.status.msglen = 0;
+    return WriteFdExactly(s, &msg.status, sizeof(msg.status));
 
 fail:
-    if(fd >= 0)
-        adb_close(fd);
+    if (fd >= 0) adb_close(fd);
     if (do_unlink) adb_unlink(path);
-    return -1;
+    return false;
 }
 
 #if defined(_WIN32)
-extern int handle_send_link(int s, char *path, char *buffer) __attribute__((error("no symlinks on Windows")));
+extern bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) __attribute__((error("no symlinks on Windows")));
 #else
-static int handle_send_link(int s, char *path, char *buffer)
-{
+static bool handle_send_link(int s, const std::string& path, std::vector<char>& buffer) {
     syncmsg msg;
     unsigned int len;
     int ret;
 
-    if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
-        return -1;
+    if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
 
-    if(msg.data.id != ID_DATA) {
-        fail_message(s, "invalid data message: expected ID_DATA");
-        return -1;
+    if (msg.data.id != ID_DATA) {
+        SendSyncFail(s, "invalid data message: expected ID_DATA");
+        return false;
     }
 
-    len = ltohl(msg.data.size);
-    if(len > SYNC_DATA_MAX) {
-        fail_message(s, "oversize data message");
-        return -1;
+    len = msg.data.size;
+    if (len > buffer.size()) { // TODO: resize buffer?
+        SendSyncFail(s, "oversize data message");
+        return false;
     }
-    if(!ReadFdExactly(s, buffer, len))
-        return -1;
-
-    ret = symlink(buffer, path);
-    if(ret && errno == ENOENT) {
-        if(mkdirs(path) != 0) {
-            fail_errno(s);
-            return -1;
+    if (!ReadFdExactly(s, &buffer[0], len)) return false;
+
+    ret = symlink(&buffer[0], path.c_str());
+    if (ret && errno == ENOENT) {
+        if (!secure_mkdirs(adb_dirname(path))) {
+            SendSyncFailErrno(s, "secure_mkdirs failed");
+            return false;
         }
-        ret = symlink(buffer, path);
+        ret = symlink(&buffer[0], path.c_str());
     }
-    if(ret) {
-        fail_errno(s);
-        return -1;
+    if (ret) {
+        SendSyncFailErrno(s, "symlink failed");
+        return false;
     }
 
-    if(!ReadFdExactly(s, &msg.data, sizeof(msg.data)))
-        return -1;
+    if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) return false;
 
-    if(msg.data.id == ID_DONE) {
+    if (msg.data.id == ID_DONE) {
         msg.status.id = ID_OKAY;
         msg.status.msglen = 0;
-        if(!WriteFdExactly(s, &msg.status, sizeof(msg.status)))
-            return -1;
+        if (!WriteFdExactly(s, &msg.status, sizeof(msg.status))) return false;
     } else {
-        fail_message(s, "invalid data message: expected ID_DONE");
-        return -1;
+        SendFail(s, "invalid data message: expected ID_DONE");
+        return false;
     }
 
-    return 0;
+    return true;
 }
 #endif
 
-static int do_send(int s, char *path, char *buffer)
-{
-    unsigned int mode;
-    bool is_link = false;
-    bool do_unlink;
-
-    char* tmp = strrchr(path,',');
-    if(tmp) {
-        *tmp = 0;
-        errno = 0;
-        mode = strtoul(tmp + 1, NULL, 0);
-        is_link = S_ISLNK((mode_t) mode);
-        mode &= 0777;
+static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) {
+    // 'spec' is of the form "/some/path,0755". Break it up.
+    size_t comma = spec.find_last_of(',');
+    if (comma == std::string::npos) {
+        SendFail(s, "missing , in ID_SEND");
+        return false;
     }
-    if(!tmp || errno) {
-        mode = 0644;
-        is_link = 0;
-        do_unlink = true;
-    } else {
-        struct stat st;
-        /* Don't delete files before copying if they are not "regular" */
-        do_unlink = lstat(path, &st) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
-        if (do_unlink) {
-            adb_unlink(path);
-        }
+
+    std::string path = spec.substr(0, comma);
+
+    errno = 0;
+    mode_t mode = strtoul(spec.substr(comma + 1).c_str(), nullptr, 0);
+    if (errno != 0) {
+        SendFail(s, "bad mode");
+        return false;
     }
 
-    if (is_link) {
-        return handle_send_link(s, path, buffer);
+    // Don't delete files before copying if they are not "regular" or symlinks.
+    struct stat st;
+    bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) || S_ISLNK(st.st_mode);
+    if (do_unlink) {
+        adb_unlink(path.c_str());
     }
 
-    uid_t uid = -1;
-    gid_t gid = -1;
-    uint64_t cap = 0;
+    if (S_ISLNK(mode)) {
+        return handle_send_link(s, path.c_str(), buffer);
+    }
 
-    /* copy user permission bits to "group" and "other" permissions */
+    // Copy user permission bits to "group" and "other" permissions.
+    mode &= 0777;
     mode |= ((mode >> 3) & 0070);
     mode |= ((mode >> 3) & 0007);
 
-    tmp = path;
-    if(*tmp == '/') {
-        tmp++;
-    }
+    uid_t uid = -1;
+    gid_t gid = -1;
+    uint64_t cap = 0;
     if (should_use_fs_config(path)) {
-        fs_config(tmp, 0, NULL, &uid, &gid, &mode, &cap);
+        unsigned int broken_api_hack = mode;
+        fs_config(path.c_str(), 0, nullptr, &uid, &gid, &broken_api_hack, &cap);
+        mode = broken_api_hack;
     }
-    return handle_send_file(s, path, uid, gid, mode, buffer, do_unlink);
+    return handle_send_file(s, path.c_str(), uid, gid, mode, buffer, do_unlink);
 }
 
-static int do_recv(int s, const char *path, char *buffer)
-{
-    syncmsg msg;
-    int fd, r;
-
-    fd = adb_open(path, O_RDONLY | O_CLOEXEC);
-    if(fd < 0) {
-        if(fail_errno(s)) return -1;
-        return 0;
+static bool do_recv(int s, const char* path, std::vector<char>& buffer) {
+    int fd = adb_open(path, O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        SendSyncFailErrno(s, "open failed");
+        return false;
     }
 
+    syncmsg msg;
     msg.data.id = ID_DATA;
-    for(;;) {
-        r = adb_read(fd, buffer, SYNC_DATA_MAX);
-        if(r <= 0) {
-            if(r == 0) break;
-            if(errno == EINTR) continue;
-            r = fail_errno(s);
+    while (true) {
+        int r = adb_read(fd, &buffer[0], buffer.size());
+        if (r <= 0) {
+            if (r == 0) break;
+            SendSyncFailErrno(s, "read failed");
             adb_close(fd);
-            return r;
+            return false;
         }
-        msg.data.size = htoll(r);
-        if(!WriteFdExactly(s, &msg.data, sizeof(msg.data)) ||
-           !WriteFdExactly(s, buffer, r)) {
+        msg.data.size = r;
+        if (!WriteFdExactly(s, &msg.data, sizeof(msg.data)) || !WriteFdExactly(s, &buffer[0], r)) {
             adb_close(fd);
-            return -1;
+            return false;
         }
     }
 
@@ -404,66 +341,62 @@ static int do_recv(int s, const char *path, char *buffer)
 
     msg.data.id = ID_DONE;
     msg.data.size = 0;
-    if(!WriteFdExactly(s, &msg.data, sizeof(msg.data))) {
-        return -1;
-    }
-
-    return 0;
+    return WriteFdExactly(s, &msg.data, sizeof(msg.data));
 }
 
-void file_sync_service(int fd, void *cookie)
-{
-    syncmsg msg;
+static bool handle_sync_command(int fd, std::vector<char>& buffer) {
+    D("sync: waiting for request");
+
+    SyncRequest request;
+    if (!ReadFdExactly(fd, &request, sizeof(request))) {
+        SendSyncFail(fd, "command read failure");
+        return false;
+    }
+    size_t path_length = request.path_length;
+    if (path_length > 1024) {
+        SendSyncFail(fd, "path too long");
+        return false;
+    }
     char name[1025];
-    unsigned namelen;
+    if (!ReadFdExactly(fd, name, path_length)) {
+        SendSyncFail(fd, "filename read failure");
+        return false;
+    }
+    name[path_length] = 0;
+
+    const char* id = reinterpret_cast<const char*>(&request.id);
+    D("sync: '%.4s' '%s'", id, name);
+
+    switch (request.id) {
+      case ID_STAT:
+        if (!do_stat(fd, name)) return false;
+        break;
+      case ID_LIST:
+        if (!do_list(fd, name)) return false;
+        break;
+      case ID_SEND:
+        if (!do_send(fd, name, buffer)) return false;
+        break;
+      case ID_RECV:
+        if (!do_recv(fd, name, buffer)) return false;
+        break;
+      case ID_QUIT:
+        return false;
+      default:
+        SendSyncFail(fd, android::base::StringPrintf("unknown command '%.4s' (%08x)",
+                                                     id, request.id));
+        return false;
+    }
 
-    char *buffer = reinterpret_cast<char*>(malloc(SYNC_DATA_MAX));
-    if(buffer == 0) goto fail;
+    return true;
+}
 
-    for(;;) {
-        D("sync: waiting for command\n");
+void file_sync_service(int fd, void* cookie) {
+    std::vector<char> buffer(SYNC_DATA_MAX);
 
-        if(!ReadFdExactly(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(!ReadFdExactly(fd, name, namelen)) {
-            fail_message(fd, "filename read failure");
-            break;
-        }
-        name[namelen] = 0;
-
-        msg.req.namelen = 0;
-        D("sync: '%s' '%s'\n", (char*) &msg.req, name);
-
-        switch(msg.req.id) {
-        case ID_STAT:
-            if(do_stat(fd, name)) goto fail;
-            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;
-        }
+    while (handle_sync_command(fd, buffer)) {
     }
 
-fail:
-    if(buffer != 0) free(buffer);
-    D("sync: done\n");
+    D("sync: done");
     adb_close(fd);
 }
index 344eb98..38382c1 100644 (file)
 #define _FILE_SYNC_SERVICE_H_
 
 #include <string>
-
-#define htoll(x) (x)
-#define ltohl(x) (x)
+#include <vector>
 
 #define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
 
 #define ID_STAT MKID('S','T','A','T')
 #define ID_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_FAIL MKID('F','A','I','L')
 #define ID_QUIT MKID('Q','U','I','T')
 
+struct SyncRequest {
+    uint32_t id;  // ID_STAT, et cetera.
+    uint32_t path_length;  // <= 1024
+    // Followed by 'path_length' bytes of path (not NUL-terminated).
+} __attribute__((packed)) ;
+
 union syncmsg {
-    unsigned id;
-    struct {
-        unsigned id;
-        unsigned namelen;
-    } req;
-    struct {
+    struct __attribute__((packed)) {
         unsigned id;
         unsigned mode;
         unsigned size;
         unsigned time;
     } stat;
-    struct {
+    struct __attribute__((packed)) {
         unsigned id;
         unsigned mode;
         unsigned size;
         unsigned time;
         unsigned namelen;
     } dent;
-    struct {
+    struct __attribute__((packed)) {
         unsigned id;
         unsigned size;
     } data;
-    struct {
+    struct __attribute__((packed)) {
         unsigned id;
         unsigned msglen;
     } status;
-} ;
+};
 
+void file_sync_service(int fd, void* cookie);
+bool do_sync_ls(const char* path);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst);
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+                  bool copy_attrs);
 
-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 show_progress);
-int do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
-int do_sync_pull(const char *rpath, const char *lpath, int show_progress, int pullTime);
+bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
 
 #define SYNC_DATA_MAX (64*1024)
 
index c0f7ec2..cc2d44e 100644 (file)
 
 /* implement the "debug-ports" and "track-debug-ports" device services */
 
-#define TRACE_TAG TRACE_JDWP
+#define TRACE_TAG JDWP
 
 #include "sysdeps.h"
 
 #include <errno.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include "adb.h"
+#include "adb_utils.h"
 
 /* here's how these things work.
 
@@ -217,7 +219,7 @@ jdwp_process_alloc( int  socket )
         calloc(1, sizeof(*proc)));
 
     if (proc == NULL) {
-        D("not enough memory to create new JDWP process\n");
+        D("not enough memory to create new JDWP process");
         return NULL;
     }
 
@@ -228,7 +230,7 @@ jdwp_process_alloc( int  socket )
 
     proc->fde = fdevent_create( socket, jdwp_process_event, proc );
     if (proc->fde == NULL) {
-        D("could not create fdevent for new JDWP process\n" );
+        D("could not create fdevent for new JDWP process" );
         free(proc);
         return NULL;
     }
@@ -270,13 +272,13 @@ jdwp_process_event( int  socket, unsigned  events, void*  _proc )
                     if (errno == EAGAIN)
                         return;
                     /* this can fail here if the JDWP process crashes very fast */
-                    D("weird unknown JDWP process failure: %s\n",
+                    D("weird unknown JDWP process failure: %s",
                       strerror(errno));
 
                     goto CloseProcess;
                 }
                 if (len == 0) {  /* end of stream ? */
-                    D("weird end-of-stream from unknown JDWP process\n");
+                    D("weird end-of-stream from unknown JDWP process");
                     goto CloseProcess;
                 }
                 p            += len;
@@ -288,12 +290,12 @@ jdwp_process_event( int  socket, unsigned  events, void*  _proc )
             temp[4] = 0;
 
             if (sscanf( temp, "%04x", &proc->pid ) != 1) {
-                D("could not decode JDWP %p PID number: '%s'\n", proc, temp);
+                D("could not decode JDWP %p PID number: '%s'", proc, temp);
                 goto CloseProcess;
             }
 
             /* all is well, keep reading to detect connection closure */
-            D("Adding pid %d to jdwp process list\n", proc->pid);
+            D("Adding pid %d to jdwp process list", proc->pid);
             jdwp_process_list_updated();
         }
         else
@@ -311,27 +313,28 @@ jdwp_process_event( int  socket, unsigned  events, void*  _proc )
                     if (len < 0 && errno == EAGAIN)
                         return;
                     else {
-                        D("terminating JDWP %d connection: %s\n", proc->pid,
+                        D("terminating JDWP %d connection: %s", proc->pid,
                           strerror(errno));
                         break;
                     }
                 }
                 else {
-                    D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n",
+                    D( "ignoring unexpected JDWP %d control socket activity (%d bytes)",
                        proc->pid, len );
                 }
             }
 
         CloseProcess:
-            if (proc->pid >= 0)
-                D( "remove pid %d to jdwp process list\n", proc->pid );
+            if (proc->pid >= 0) {
+                D( "remove pid %d to jdwp process list", 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",
+        D("trying to write to JDWP pid controli (count=%d first=%d) %d",
           proc->pid, proc->out_count, proc->out_fds[0]);
         if (proc->out_count > 0) {
             int  fd = proc->out_fds[0];
@@ -341,7 +344,6 @@ jdwp_process_event( int  socket, unsigned  events, void*  _proc )
             struct iovec     iov;
             char             dummy = '!';
             char             buffer[sizeof(struct cmsghdr) + sizeof(int)];
-            int flags;
 
             iov.iov_base       = &dummy;
             iov.iov_len        = 1;
@@ -359,18 +361,8 @@ jdwp_process_event( int  socket, unsigned  events, void*  _proc )
             cmsg->cmsg_type  = SCM_RIGHTS;
             ((int*)CMSG_DATA(cmsg))[0] = fd;
 
-            flags = fcntl(proc->socket,F_GETFL,0);
-
-            if (flags == -1) {
-                D("failed to get cntl flags for socket %d: %s\n",
-                  proc->pid, strerror(errno));
-                goto CloseProcess;
-
-            }
-
-            if (fcntl(proc->socket, F_SETFL, flags & ~O_NONBLOCK) == -1) {
-                D("failed to remove O_NONBLOCK flag for socket %d: %s\n",
-                  proc->pid, strerror(errno));
+            if (!set_file_block_mode(proc->socket, true)) {
+                VLOG(JDWP) << "failed to set blocking mode for fd " << proc->socket;
                 goto CloseProcess;
             }
 
@@ -382,20 +374,19 @@ jdwp_process_event( int  socket, unsigned  events, void*  _proc )
                 }
                 if (errno == EINTR)
                     continue;
-                D("sending new file descriptor to JDWP %d failed: %s\n",
+                D("sending new file descriptor to JDWP %d failed: %s",
                   proc->pid, strerror(errno));
                 goto CloseProcess;
             }
 
-            D("sent file descriptor %d to JDWP process %d\n",
+            D("sent file descriptor %d to JDWP process %d",
               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));
+            if (!set_file_block_mode(proc->socket, false)) {
+                VLOG(JDWP) << "failed to set non-blocking mode for fd " << proc->socket;
                 goto CloseProcess;
             }
 
@@ -411,13 +402,13 @@ create_jdwp_connection_fd(int  pid)
 {
     JdwpProcess*  proc = _jdwp_list.next;
 
-    D("looking for pid %d in JDWP process list\n", pid);
+    D("looking for pid %d in JDWP process list", pid);
     for ( ; proc != &_jdwp_list; proc = proc->next ) {
         if (proc->pid == pid) {
             goto FoundIt;
         }
     }
-    D("search failed !!\n");
+    D("search failed !!");
     return -1;
 
 FoundIt:
@@ -425,13 +416,13 @@ FoundIt:
         int  fds[2];
 
         if (proc->out_count >= MAX_OUT_FDS) {
-            D("%s: too many pending JDWP connection for pid %d\n",
+            D("%s: too many pending JDWP connection for pid %d",
               __FUNCTION__, pid);
             return -1;
         }
 
         if (adb_socketpair(fds) < 0) {
-            D("%s: socket pair creation failed: %s\n",
+            D("%s: socket pair creation failed: %s",
               __FUNCTION__, strerror(errno));
             return -1;
         }
@@ -476,7 +467,7 @@ jdwp_control_init( JdwpControl*  control,
     int                  pathlen = socknamelen;
 
     if (pathlen >= maxpath) {
-        D( "vm debug control socket name too long (%d extra chars)\n",
+        D( "vm debug control socket name too long (%d extra chars)",
            pathlen+1-maxpath );
         return -1;
     }
@@ -487,7 +478,7 @@ jdwp_control_init( JdwpControl*  control,
 
     s = socket( AF_UNIX, SOCK_STREAM, 0 );
     if (s < 0) {
-        D( "could not create vm debug control socket. %d: %s\n",
+        D( "could not create vm debug control socket. %d: %s",
            errno, strerror(errno));
         return -1;
     }
@@ -495,14 +486,14 @@ jdwp_control_init( JdwpControl*  control,
     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",
+        D( "could not bind vm debug control socket: %d: %s",
            errno, strerror(errno) );
         adb_close(s);
         return -1;
     }
 
     if ( listen(s, 4) < 0 ) {
-        D("listen failed in jdwp control socket: %d: %s\n",
+        D("listen failed in jdwp control socket: %d: %s",
           errno, strerror(errno));
         adb_close(s);
         return -1;
@@ -512,7 +503,7 @@ jdwp_control_init( JdwpControl*  control,
 
     control->fde = fdevent_create(s, jdwp_control_event, control);
     if (control->fde == NULL) {
-        D( "could not create fdevent for jdwp control socket\n" );
+        D( "could not create fdevent for jdwp control socket" );
         adb_close(s);
         return -1;
     }
@@ -521,7 +512,7 @@ jdwp_control_init( JdwpControl*  control,
     fdevent_add(control->fde, FDE_READ);
     close_on_exec(s);
 
-    D("jdwp control socket started (%d)\n", control->listen_socket);
+    D("jdwp control socket started (%d)", control->listen_socket);
     return 0;
 }
 
@@ -544,11 +535,11 @@ jdwp_control_event( int  s, unsigned  events, void*  _control )
                     continue;
                 if (errno == ECONNABORTED) {
                     /* oops, the JDWP process died really quick */
-                    D("oops, the JDWP process died really quick\n");
+                    D("oops, the JDWP process died really quick");
                     return;
                 }
                 /* the socket is probably closed ? */
-                D( "weird accept() failed on jdwp control socket: %s\n",
+                D( "weird accept() failed on jdwp control socket: %s",
                    strerror(errno) );
                 return;
             }
@@ -608,7 +599,7 @@ jdwp_socket_ready( asocket*  s )
     */
     if (jdwp->pass == 0) {
         apacket*  p = get_apacket();
-        p->len = jdwp_process_list((char*)p->data, MAX_PAYLOAD);
+        p->len = jdwp_process_list((char*)p->data, s->get_max_payload());
         peer->enqueue(peer, p);
         jdwp->pass = 1;
     }
@@ -695,7 +686,7 @@ jdwp_tracker_ready( asocket*  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));
+        p->len = jdwp_process_list_msg((char*)p->data, s->get_max_payload());
         s->peer->enqueue(s->peer, p);
     }
 }
diff --git a/adb/line_printer.cpp b/adb/line_printer.cpp
new file mode 100644 (file)
index 0000000..4c57c7e
--- /dev/null
@@ -0,0 +1,161 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "line_printer.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <sys/time.h>
+#endif
+
+// Make sure printf is really adb_printf which works for UTF-8 on Windows.
+#include <sysdeps.h>
+
+// Stuff from ninja's util.h that's needed below.
+#include <vector>
+using namespace std;
+string ElideMiddle(const string& str, size_t width) {
+  const int kMargin = 3;  // Space for "...".
+  string result = str;
+  if (result.size() + kMargin > width) {
+    size_t elide_size = (width - kMargin) / 2;
+    result = result.substr(0, elide_size)
+      + "..."
+      + result.substr(result.size() - elide_size, elide_size);
+  }
+  return result;
+}
+
+LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) {
+#ifndef _WIN32
+  const char* term = getenv("TERM");
+  smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb";
+#else
+  // Disable output buffer.  It'd be nice to use line buffering but
+  // MSDN says: "For some systems, [_IOLBF] provides line
+  // buffering. However, for Win32, the behavior is the same as _IOFBF
+  // - Full Buffering."
+  setvbuf(stdout, NULL, _IONBF, 0);
+  console_ = GetStdHandle(STD_OUTPUT_HANDLE);
+  CONSOLE_SCREEN_BUFFER_INFO csbi;
+  smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
+#endif
+}
+
+void LinePrinter::Print(string to_print, LineType type) {
+  if (console_locked_) {
+    line_buffer_ = to_print;
+    line_type_ = type;
+    return;
+  }
+
+  if (smart_terminal_) {
+    printf("\r");  // Print over previous line, if any.
+    // On Windows, calling a C library function writing to stdout also handles
+    // pausing the executable when the "Pause" key or Ctrl-S is pressed.
+  }
+
+  if (smart_terminal_ && type == ELIDE) {
+#ifdef _WIN32
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    GetConsoleScreenBufferInfo(console_, &csbi);
+
+    // TODO: std::wstring to_print_wide; if (!android::base::UTF8ToWide(to_print, &to_print_wide)...
+    // TODO: wstring ElideMiddle.
+    to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
+    // We don't want to have the cursor spamming back and forth, so instead of
+    // printf use WriteConsoleOutput which updates the contents of the buffer,
+    // but doesn't move the cursor position.
+    COORD buf_size = { csbi.dwSize.X, 1 };
+    COORD zero_zero = { 0, 0 };
+    SMALL_RECT target = {
+      csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
+      static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
+      csbi.dwCursorPosition.Y
+    };
+    vector<CHAR_INFO> char_data(csbi.dwSize.X);
+    for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
+      // TODO: UnicodeChar instead of AsciiChar, to_print_wide[i].
+      char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
+      char_data[i].Attributes = csbi.wAttributes;
+    }
+    // TODO: WriteConsoleOutputW.
+    WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
+#else
+    // Limit output to width of the terminal if provided so we don't cause
+    // line-wrapping.
+    winsize size;
+    if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
+      to_print = ElideMiddle(to_print, size.ws_col);
+    }
+    printf("%s", to_print.c_str());
+    printf("\x1B[K");  // Clear to end of line.
+    fflush(stdout);
+#endif
+
+    have_blank_line_ = false;
+  } else {
+    printf("%s\n", to_print.c_str());
+  }
+}
+
+void LinePrinter::PrintOrBuffer(const char* data, size_t size) {
+  if (console_locked_) {
+    output_buffer_.append(data, size);
+  } else {
+    // Avoid printf and C strings, since the actual output might contain null
+    // bytes like UTF-16 does (yuck).
+    fwrite(data, 1, size, stdout);
+  }
+}
+
+void LinePrinter::PrintOnNewLine(const string& to_print) {
+  if (console_locked_ && !line_buffer_.empty()) {
+    output_buffer_.append(line_buffer_);
+    output_buffer_.append(1, '\n');
+    line_buffer_.clear();
+  }
+  if (!have_blank_line_) {
+    PrintOrBuffer("\n", 1);
+  }
+  if (!to_print.empty()) {
+    PrintOrBuffer(&to_print[0], to_print.size());
+  }
+  have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n';
+}
+
+void LinePrinter::SetConsoleLocked(bool locked) {
+  if (locked == console_locked_)
+    return;
+
+  if (locked)
+    PrintOnNewLine("");
+
+  console_locked_ = locked;
+
+  if (!locked) {
+    PrintOnNewLine(output_buffer_);
+    if (!line_buffer_.empty()) {
+      Print(line_buffer_, line_type_);
+    }
+    output_buffer_.clear();
+    line_buffer_.clear();
+  }
+}
diff --git a/adb/line_printer.h b/adb/line_printer.h
new file mode 100644 (file)
index 0000000..3d0a5bd
--- /dev/null
@@ -0,0 +1,71 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef NINJA_LINE_PRINTER_H_
+#define NINJA_LINE_PRINTER_H_
+
+#include <stddef.h>
+#include <string>
+
+/// Prints lines of text, possibly overprinting previously printed lines
+/// if the terminal supports it.
+struct LinePrinter {
+  LinePrinter();
+
+  bool is_smart_terminal() const { return smart_terminal_; }
+  void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
+
+  enum LineType {
+    FULL,
+    ELIDE
+  };
+  /// Overprints the current line. If type is ELIDE, elides to_print to fit on
+  /// one line.
+  void Print(std::string to_print, LineType type);
+
+  /// Prints a string on a new line, not overprinting previous output.
+  void PrintOnNewLine(const std::string& to_print);
+
+  /// Lock or unlock the console.  Any output sent to the LinePrinter while the
+  /// console is locked will not be printed until it is unlocked.
+  void SetConsoleLocked(bool locked);
+
+ private:
+  /// Whether we can do fancy terminal control codes.
+  bool smart_terminal_;
+
+  /// Whether the caret is at the beginning of a blank line.
+  bool have_blank_line_;
+
+  /// Whether console is locked.
+  bool console_locked_;
+
+  /// Buffered current line while console is locked.
+  std::string line_buffer_;
+
+  /// Buffered line type while console is locked.
+  LineType line_type_;
+
+  /// Buffered console output while console is locked.
+  std::string output_buffer_;
+
+#ifdef _WIN32
+  void* console_;
+#endif
+
+  /// Print the given data to the console, or buffer it if it is locked.
+  void PrintOrBuffer(const char *data, size_t size);
+};
+
+#endif  // NINJA_LINE_PRINTER_H_
index ff72751..b59c9f2 100644 (file)
@@ -6,6 +6,8 @@
 #ifndef ADB_MUTEX
 #error ADB_MUTEX not defined when including this file
 #endif
+ADB_MUTEX(basename_lock)
+ADB_MUTEX(dirname_lock)
 ADB_MUTEX(socket_list_lock)
 ADB_MUTEX(transport_lock)
 #if ADB_HOST
@@ -13,13 +15,4 @@ 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
index c9d3c24..5c7c6ba 100644 (file)
@@ -60,11 +60,14 @@ 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
+Currently, version=0x01000000 and maxdata=256*1024. Older versions of adb
+hard-coded maxdata=4096, so CONNECT and AUTH packets sent to a device must not
+be larger than that because they're sent before the CONNECT from the device
+that tells the adb server what maxdata the device can support.
 
 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.
+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.
@@ -133,7 +136,7 @@ not change on later READY messages sent to the same stream.
 
 
 
---- WRITE(0, remote-id, "data") ----------------------------------------
+--- WRITE(local-id, remote-id, "data") ---------------------------------
 
 The WRITE message sends data to the recipient's stream identified by
 remote-id.  The payload MUST be <= maxdata in length.
diff --git a/adb/qemu_tracing.cpp b/adb/qemu_tracing.cpp
deleted file mode 100644 (file)
index f31eae8..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Implements ADB tracing inside the emulator.
- */
-
-#include <stdarg.h>
-
-#include "sysdeps.h"
-#include "qemu_tracing.h"
-
-/*
- * Redefine open and write for qemu_pipe.h that contains inlined references
- * to those routines. We will redifine them back after qemu_pipe.h inclusion.
- */
-
-#undef open
-#undef write
-#define open    adb_open
-#define write   adb_write
-#include <hardware/qemu_pipe.h>
-#undef open
-#undef write
-#define open    ___xxx_open
-#define write   ___xxx_write
-
-/* A handle to adb-debug qemud service in the emulator. */
-int   adb_debug_qemu = -1;
-
-/* Initializes connection with the adb-debug qemud service in the emulator. */
-int adb_qemu_trace_init(void)
-{
-    char con_name[32];
-
-    if (adb_debug_qemu >= 0) {
-        return 0;
-    }
-
-    /* adb debugging QEMUD service connection request. */
-    snprintf(con_name, sizeof(con_name), "qemud:adb-debug");
-    adb_debug_qemu = qemu_pipe_open(con_name);
-    return (adb_debug_qemu >= 0) ? 0 : -1;
-}
-
-void adb_qemu_trace(const char* fmt, ...)
-{
-    va_list args;
-    va_start(args, fmt);
-    char msg[1024];
-
-    if (adb_debug_qemu >= 0) {
-        vsnprintf(msg, sizeof(msg), fmt, args);
-        adb_write(adb_debug_qemu, msg, strlen(msg));
-    }
-}
index 2d4ab58..8f1c9b0 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
 
 #include "sysdeps.h"
 
@@ -35,8 +35,6 @@
 #include "cutils/properties.h"
 #include "fs_mgr.h"
 
-const std::string kFstab_Prefix = "/fstab.";
-
 // Returns the device used to mount a directory in /proc/mounts.
 static std::string find_proc_mount(const char* dir) {
     std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
@@ -58,7 +56,7 @@ static std::string find_fstab_mount(const char* dir) {
     char propbuf[PROPERTY_VALUE_MAX];
 
     property_get("ro.hardware", propbuf, "");
-    std::string fstab_filename = kFstab_Prefix + propbuf;
+    std::string fstab_filename = std::string("/fstab.") + propbuf;
     struct fstab* fstab = fs_mgr_read_fstab(fstab_filename.c_str());
     struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, dir);
     std::string dev = rec ? std::string(rec->blk_device) : "";
@@ -84,7 +82,7 @@ bool make_block_device_writable(const std::string& dev) {
 
     int OFF = 0;
     bool result = (ioctl(fd, BLKROSET, &OFF) != -1);
-    adb_close(fd);
+    unix_close(fd);
     return result;
 }
 
index 1847447..19a6726 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_SERVICES
+#define TRACE_TAG SERVICES
 
 #include "sysdeps.h"
 
@@ -34,6 +34,7 @@
 #include <base/file.h>
 #include <base/stringprintf.h>
 #include <base/strings.h>
+#include <cutils/sockets.h>
 
 #if !ADB_HOST
 #include "cutils/android_reboot.h"
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "file_sync_service.h"
 #include "remount_service.h"
+#include "services.h"
+#include "shell_service.h"
 #include "transport.h"
 
 struct stinfo {
@@ -52,10 +56,10 @@ struct stinfo {
     void *cookie;
 };
 
-
 void *service_bootstrap_func(void *x)
 {
     stinfo* sti = reinterpret_cast<stinfo*>(x);
+    adb_thread_setname(android::base::StringPrintf("service %d", sti->fd));
     sti->func(sti->fd, sti->cookie);
     free(sti);
     return 0;
@@ -135,7 +139,7 @@ static bool reboot_service_impl(int fd, const char* arg) {
         const char* const command_file = "/cache/recovery/command";
         // Ensure /cache/recovery exists.
         if (adb_mkdir(recovery_dir, 0770) == -1 && errno != EEXIST) {
-            D("Failed to create directory '%s': %s\n", recovery_dir, strerror(errno));
+            D("Failed to create directory '%s': %s", recovery_dir, strerror(errno));
             return false;
         }
 
@@ -180,18 +184,57 @@ void reboot_service(int fd, void* arg)
     adb_close(fd);
 }
 
-void reverse_service(int fd, void* arg)
-{
-    const char* command = reinterpret_cast<const char*>(arg);
+int reverse_service(const char* command) {
+    int s[2];
+    if (adb_socketpair(s)) {
+        PLOG(ERROR) << "cannot create service socket pair.";
+        return -1;
+    }
+    VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
+    if (handle_forward_request(command, kTransportAny, nullptr, s[1]) < 0) {
+        SendFail(s[1], "not a reverse forwarding command");
+    }
+    adb_close(s[1]);
+    return s[0];
+}
 
-    if (handle_forward_request(command, kTransportAny, NULL, fd) < 0) {
-        SendFail(fd, "not a reverse forwarding command");
+// Shell service string can look like:
+//   shell[,arg1,arg2,...]:[command]
+static int ShellService(const std::string& args, const atransport* transport) {
+    size_t delimiter_index = args.find(':');
+    if (delimiter_index == std::string::npos) {
+        LOG(ERROR) << "No ':' found in shell service arguments: " << args;
+        return -1;
     }
-    free(arg);
-    adb_close(fd);
+
+    const std::string service_args = args.substr(0, delimiter_index);
+    const std::string command = args.substr(delimiter_index + 1);
+
+    // Defaults:
+    //   PTY for interactive, raw for non-interactive.
+    //   No protocol.
+    SubprocessType type(command.empty() ? SubprocessType::kPty
+                                        : SubprocessType::kRaw);
+    SubprocessProtocol protocol = SubprocessProtocol::kNone;
+
+    for (const std::string& arg : android::base::Split(service_args, ",")) {
+        if (arg == kShellServiceArgRaw) {
+            type = SubprocessType::kRaw;
+        } else if (arg == kShellServiceArgPty) {
+            type = SubprocessType::kPty;
+        } else if (arg == kShellServiceArgShellProtocol) {
+            protocol = SubprocessProtocol::kShell;
+        }
+        else if (!arg.empty()) {
+            LOG(ERROR) << "Unsupported shell service arguments: " << args;
+            return -1;
+        }
+    }
+
+    return StartSubprocess(command.c_str(), type, protocol);
 }
 
-#endif
+#endif  // !ADB_HOST
 
 static int create_service_thread(void (*func)(int, void *), void *cookie)
 {
@@ -210,8 +253,7 @@ static int create_service_thread(void (*func)(int, void *), void *cookie)
     sti->cookie = cookie;
     sti->fd = s[1];
 
-    adb_thread_t t;
-    if (adb_thread_create(&t, service_bootstrap_func, sti)) {
+    if (!adb_thread_create(service_bootstrap_func, sti)) {
         free(sti);
         adb_close(s[0]);
         adb_close(s[1]);
@@ -219,228 +261,30 @@ static int create_service_thread(void (*func)(int, void *), void *cookie)
         return -1;
     }
 
-    D("service thread started, %d:%d\n",s[0], s[1]);
+    D("service thread started, %d:%d",s[0], s[1]);
     return s[0];
 }
 
-#if !ADB_HOST
-
-static void init_subproc_child()
-{
-    setsid();
-
-    // Set OOM score adjustment to prevent killing
-    int fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC);
-    if (fd >= 0) {
-        adb_write(fd, "0", 1);
-        adb_close(fd);
-    } else {
-       D("adb: unable to update oom_score_adj\n");
-    }
-}
-
-static int create_subproc_pty(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
-{
-    D("create_subproc_pty(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
-#if defined(_WIN32)
-    fprintf(stderr, "error: create_subproc_pty not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
-    return -1;
-#else
-    int ptm;
-
-    ptm = unix_open("/dev/ptmx", O_RDWR | O_CLOEXEC); // | O_NOCTTY);
-    if(ptm < 0){
-        printf("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
-        return -1;
-    }
-
-    char devname[64];
-    if(grantpt(ptm) || unlockpt(ptm) || ptsname_r(ptm, devname, sizeof(devname)) != 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) {
-        init_subproc_child();
-
-        int pts = unix_open(devname, O_RDWR | O_CLOEXEC);
-        if (pts < 0) {
-            fprintf(stderr, "child failed to open pseudo-term slave: %s\n", devname);
-            exit(-1);
-        }
-
-        dup2(pts, STDIN_FILENO);
-        dup2(pts, STDOUT_FILENO);
-        dup2(pts, STDERR_FILENO);
-
-        adb_close(pts);
-        adb_close(ptm);
-
-        execl(cmd, cmd, arg0, arg1, NULL);
-        fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
-                cmd, strerror(errno), errno);
-        exit(-1);
-    } else {
-        return ptm;
-    }
-#endif /* !defined(_WIN32) */
-}
-
-static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
-{
-    D("create_subproc_raw(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
-#if defined(_WIN32)
-    fprintf(stderr, "error: create_subproc_raw not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
-    return -1;
-#else
-
-    // 0 is parent socket, 1 is child socket
-    int sv[2];
-    if (adb_socketpair(sv) < 0) {
-        printf("[ cannot create socket pair - %s ]\n", strerror(errno));
-        return -1;
-    }
-    D("socketpair: (%d,%d)", sv[0], sv[1]);
-
-    *pid = fork();
-    if (*pid < 0) {
-        printf("- fork failed: %s -\n", strerror(errno));
-        adb_close(sv[0]);
-        adb_close(sv[1]);
-        return -1;
-    }
-
-    if (*pid == 0) {
-        adb_close(sv[0]);
-        init_subproc_child();
-
-        dup2(sv[1], STDIN_FILENO);
-        dup2(sv[1], STDOUT_FILENO);
-        dup2(sv[1], STDERR_FILENO);
-
-        adb_close(sv[1]);
-
-        execl(cmd, cmd, arg0, arg1, NULL);
-        fprintf(stderr, "- exec '%s' failed: %s (%d) -\n",
-                cmd, strerror(errno), errno);
-        exit(-1);
-    } else {
-        adb_close(sv[1]);
-        return sv[0];
-    }
-#endif /* !defined(_WIN32) */
-}
-#endif  /* !ABD_HOST */
-
-#if ADB_HOST
-#define SHELL_COMMAND "/bin/sh"
-#else
-#define SHELL_COMMAND "/system/bin/sh"
-#endif
-
-#if !ADB_HOST
-static void subproc_waiter_service(int fd, void *cookie)
-{
-    pid_t pid = (pid_t) (uintptr_t) cookie;
-
-    D("entered. fd=%d of pid=%d\n", fd, pid);
-    while (true) {
-        int status;
-        pid_t p = waitpid(pid, &status, 0);
-        if (p == pid) {
-            D("fd=%d, post waitpid(pid=%d) status=%04x\n", fd, p, status);
-            if (WIFSIGNALED(status)) {
-                D("*** Killed by signal %d\n", WTERMSIG(status));
-                break;
-            } else if (!WIFEXITED(status)) {
-                D("*** Didn't exit!!. status %d\n", status);
-                break;
-            } else if (WEXITSTATUS(status) >= 0) {
-                D("*** Exit code %d\n", WEXITSTATUS(status));
-                break;
-            }
-         }
-    }
-    D("shell exited fd=%d of pid=%d err=%d\n", fd, pid, errno);
-    if (SHELL_EXIT_NOTIFY_FD >=0) {
-      int res;
-      res = WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd)) ? 0 : -1;
-      D("notified shell exit via fd=%d for pid=%d res=%d errno=%d\n",
-        SHELL_EXIT_NOTIFY_FD, pid, res, errno);
-    }
-}
-
-static int create_subproc_thread(const char *name, const subproc_mode mode)
-{
-    adb_thread_t t;
-    int ret_fd;
-    pid_t pid = -1;
-
-    const char *arg0, *arg1;
-    if (name == 0 || *name == 0) {
-        arg0 = "-"; arg1 = 0;
-    } else {
-        arg0 = "-c"; arg1 = name;
-    }
-
-    switch (mode) {
-    case SUBPROC_PTY:
-        ret_fd = create_subproc_pty(SHELL_COMMAND, arg0, arg1, &pid);
-        break;
-    case SUBPROC_RAW:
-        ret_fd = create_subproc_raw(SHELL_COMMAND, arg0, arg1, &pid);
-        break;
-    default:
-        fprintf(stderr, "invalid subproc_mode %d\n", mode);
-        return -1;
-    }
-    D("create_subproc ret_fd=%d pid=%d\n", ret_fd, pid);
-
-    stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
-    if(sti == 0) fatal("cannot allocate stinfo");
-    sti->func = subproc_waiter_service;
-    sti->cookie = (void*) (uintptr_t) pid;
-    sti->fd = ret_fd;
-
-    if (adb_thread_create(&t, service_bootstrap_func, sti)) {
-        free(sti);
-        adb_close(ret_fd);
-        fprintf(stderr, "cannot create service thread\n");
-        return -1;
-    }
-
-    D("service thread started, fd=%d pid=%d\n", ret_fd, pid);
-    return ret_fd;
-}
-#endif
-
-int service_to_fd(const char *name)
-{
+int service_to_fd(const char* name, const atransport* transport) {
     int ret = -1;
 
     if(!strncmp(name, "tcp:", 4)) {
         int port = atoi(name + 4);
         name = strchr(name + 4, ':');
         if(name == 0) {
-            ret = socket_loopback_client(port, SOCK_STREAM);
+            std::string error;
+            ret = network_loopback_client(port, SOCK_STREAM, &error);
             if (ret >= 0)
                 disable_tcp_nagle(ret);
         } else {
 #if ADB_HOST
-            ret = socket_network_client(name + 1, port, SOCK_STREAM);
+            std::string error;
+            ret = network_connect(name + 1, port, SOCK_STREAM, 0, &error);
 #else
             return -1;
 #endif
         }
-#ifndef HAVE_WINSOCK   /* winsock doesn't implement unix domain sockets */
+#if !defined(_WIN32)   /* winsock doesn't implement unix domain sockets */
     } else if(!strncmp(name, "local:", 6)) {
         ret = socket_local_client(name + 6,
                 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
@@ -461,10 +305,11 @@ int service_to_fd(const char *name)
         ret = create_service_thread(framebuffer_service, 0);
     } else if (!strncmp(name, "jdwp:", 5)) {
         ret = create_jdwp_connection_fd(atoi(name+5));
-    } else if(!HOST && !strncmp(name, "shell:", 6)) {
-        ret = create_subproc_thread(name + 6, SUBPROC_PTY);
-    } else if(!HOST && !strncmp(name, "exec:", 5)) {
-        ret = create_subproc_thread(name + 5, SUBPROC_RAW);
+    } else if(!strncmp(name, "shell", 5)) {
+        ret = ShellService(name + 5, transport);
+    } else if(!strncmp(name, "exec:", 5)) {
+        ret = StartSubprocess(name + 5, SubprocessType::kRaw,
+                              SubprocessProtocol::kNone);
     } else if(!strncmp(name, "sync:", 5)) {
         ret = create_service_thread(file_sync_service, NULL);
     } else if(!strncmp(name, "remount:", 8)) {
@@ -478,28 +323,22 @@ int service_to_fd(const char *name)
     } else if(!strncmp(name, "unroot:", 7)) {
         ret = create_service_thread(restart_unroot_service, NULL);
     } else if(!strncmp(name, "backup:", 7)) {
-        ret = create_subproc_thread(android::base::StringPrintf("/system/bin/bu backup %s",
-                                                                (name + 7)).c_str(), SUBPROC_RAW);
+        ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
+                                                          (name + 7)).c_str(),
+                              SubprocessType::kRaw, SubprocessProtocol::kNone);
     } else if(!strncmp(name, "restore:", 8)) {
-        ret = create_subproc_thread("/system/bin/bu restore", SUBPROC_RAW);
+        ret = StartSubprocess("/system/bin/bu restore", SubprocessType::kRaw,
+                              SubprocessProtocol::kNone);
     } else if(!strncmp(name, "tcpip:", 6)) {
         int port;
         if (sscanf(name + 6, "%d", &port) != 1) {
-            port = 0;
+            return -1;
         }
         ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port);
     } else if(!strncmp(name, "usb:", 4)) {
         ret = create_service_thread(restart_usb_service, NULL);
     } else if (!strncmp(name, "reverse:", 8)) {
-        char* cookie = strdup(name + 8);
-        if (cookie == NULL) {
-            ret = -1;
-        } else {
-            ret = create_service_thread(reverse_service, cookie);
-            if (ret < 0) {
-                free(cookie);
-            }
-        }
+        ret = reverse_service(name + 8);
     } else if(!strncmp(name, "disable-verity:", 15)) {
         ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
     } else if(!strncmp(name, "enable-verity:", 15)) {
@@ -514,61 +353,63 @@ int service_to_fd(const char *name)
 
 #if ADB_HOST
 struct state_info {
-    transport_type transport;
+    TransportType transport_type;
     char* serial;
-    int state;
+    ConnectionState state;
 };
 
-static void wait_for_state(int fd, void* cookie)
-{
+static void wait_for_state(int fd, void* cookie) {
     state_info* sinfo = reinterpret_cast<state_info*>(cookie);
 
-    D("wait_for_state %d\n", sinfo->state);
+    D("wait_for_state %d", sinfo->state);
 
-    std::string error_msg = "unknown error";
-    atransport* t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &error_msg);
-    if (t != 0) {
-        SendOkay(fd);
-    } else {
-        SendFail(fd, error_msg);
+    while (true) {
+        bool is_ambiguous = false;
+        std::string error = "unknown error";
+        atransport* t = acquire_one_transport(sinfo->transport_type, sinfo->serial,
+                                              &is_ambiguous, &error);
+        if (t != nullptr && t->connection_state == sinfo->state) {
+            SendOkay(fd);
+            break;
+        } else if (!is_ambiguous) {
+            adb_sleep_ms(1000);
+            // Try again...
+        } else {
+            SendFail(fd, error);
+            break;
+        }
     }
 
-    if (sinfo->serial)
+    if (sinfo->serial) {
         free(sinfo->serial);
+    }
     free(sinfo);
     adb_close(fd);
-    D("wait_for_state is done\n");
+    D("wait_for_state is done");
 }
 
-static void connect_device(const std::string& host, std::string* response) {
-    if (host.empty()) {
-        *response = "empty host name";
+static void connect_device(const std::string& address, std::string* response) {
+    if (address.empty()) {
+        *response = "empty address";
         return;
     }
 
-    std::vector<std::string> pieces = android::base::Split(host, ":");
-    const std::string& hostname = pieces[0];
-
+    std::string serial;
+    std::string host;
     int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    if (pieces.size() > 1) {
-        if (sscanf(pieces[1].c_str(), "%d", &port) != 1) {
-            *response = android::base::StringPrintf("bad port number %s", pieces[1].c_str());
-            return;
-        }
+    if (!parse_host_and_port(address, &serial, &host, &port, response)) {
+        return;
     }
 
-    // This may look like we're putting 'host' back together,
-    // but we're actually inserting the default port if necessary.
-    std::string serial = android::base::StringPrintf("%s:%d", hostname.c_str(), port);
-
-    int fd = socket_network_client_timeout(hostname.c_str(), port, SOCK_STREAM, 10);
-    if (fd < 0) {
-        *response = android::base::StringPrintf("unable to connect to %s:%d",
-                                                hostname.c_str(), port);
+    std::string error;
+    int fd = network_connect(host.c_str(), port, SOCK_STREAM, 10, &error);
+    if (fd == -1) {
+        *response = android::base::StringPrintf("unable to connect to %s: %s",
+                                                serial.c_str(), error.c_str());
         return;
     }
 
-    D("client: connected on remote on fd %d\n", fd);
+    D("client: connected %s remote on fd %d", serial.c_str(), fd);
     close_on_exec(fd);
     disable_tcp_nagle(fd);
 
@@ -618,25 +459,25 @@ void connect_emulator(const std::string& port_spec, std::string* response) {
     }
 
     // Preconditions met, try to connect to the emulator.
-    if (!local_connect_arbitrary_ports(console_port, adb_port)) {
+    std::string error;
+    if (!local_connect_arbitrary_ports(console_port, adb_port, &error)) {
         *response = android::base::StringPrintf("Connected to emulator on ports %d,%d",
                                                 console_port, adb_port);
     } else {
-        *response = android::base::StringPrintf("Could not connect to emulator on ports %d,%d",
-                                                console_port, adb_port);
+        *response = android::base::StringPrintf("Could not connect to emulator on ports %d,%d: %s",
+                                                console_port, adb_port, error.c_str());
     }
 }
 
-static void connect_service(int fd, void* cookie)
-{
-    char *host = reinterpret_cast<char*>(cookie);
-
+static void connect_service(int fd, void* data) {
+    char* host = reinterpret_cast<char*>(data);
     std::string response;
     if (!strncmp(host, "emu:", 4)) {
         connect_emulator(host + 4, &response);
     } else {
         connect_device(host, &response);
     }
+    free(host);
 
     // Send response for emulator and device
     SendProtocolString(fd, response);
@@ -645,8 +486,7 @@ static void connect_service(int fd, void* cookie)
 #endif
 
 #if ADB_HOST
-asocket*  host_service_to_socket(const char*  name, const char *serial)
-{
+asocket* host_service_to_socket(const char* name, const char* serial) {
     if (!strcmp(name,"track-devices")) {
         return create_device_tracker();
     } else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
@@ -664,15 +504,18 @@ asocket*  host_service_to_socket(const char*  name, const char *serial)
         name += strlen("wait-for-");
 
         if (!strncmp(name, "local", strlen("local"))) {
-            sinfo->transport = kTransportLocal;
-            sinfo->state = CS_DEVICE;
+            sinfo->transport_type = kTransportLocal;
+            sinfo->state = kCsDevice;
         } else if (!strncmp(name, "usb", strlen("usb"))) {
-            sinfo->transport = kTransportUsb;
-            sinfo->state = CS_DEVICE;
+            sinfo->transport_type = kTransportUsb;
+            sinfo->state = kCsDevice;
         } else if (!strncmp(name, "any", strlen("any"))) {
-            sinfo->transport = kTransportAny;
-            sinfo->state = CS_DEVICE;
+            sinfo->transport_type = kTransportAny;
+            sinfo->state = kCsDevice;
         } else {
+            if (sinfo->serial) {
+                free(sinfo->serial);
+            }
             free(sinfo);
             return NULL;
         }
@@ -680,8 +523,8 @@ asocket*  host_service_to_socket(const char*  name, const char *serial)
         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);
+        char* host = strdup(name + 8);
+        int fd = create_service_thread(connect_service, host);
         return create_local_socket(fd);
     }
     return NULL;
diff --git a/adb/services.h b/adb/services.h
new file mode 100644 (file)
index 0000000..0428ca4
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SERVICES_H_
+#define SERVICES_H_
+
+constexpr char kShellServiceArgRaw[] = "raw";
+constexpr char kShellServiceArgPty[] = "pty";
+constexpr char kShellServiceArgShellProtocol[] = "v2";
+
+#endif  // SERVICES_H_
index bae38cf..f5188e9 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_ADB
+#define TRACE_TAG ADB
 
 #include "sysdeps.h"
 
 
 #include "adb.h"
 #include "adb_io.h"
-#include "ext4_sb.h"
 #include "fs_mgr.h"
 #include "remount_service.h"
 
+#include "fec/io.h"
+
 #define FSTAB_PREFIX "/fstab."
 struct fstab *fstab;
 
@@ -41,115 +42,50 @@ static const bool kAllowDisableVerity = true;
 static const bool kAllowDisableVerity = false;
 #endif
 
-static int get_target_device_size(int fd, const char *blk_device,
-                                  uint64_t *device_size)
-{
-    int data_device;
-    struct ext4_super_block sb;
-    struct fs_info info;
-
-    info.len = 0;  /* Only len is set to 0 to ask the device for real size. */
-
-    data_device = adb_open(blk_device, O_RDONLY | O_CLOEXEC);
-    if (data_device < 0) {
-        WriteFdFmt(fd, "Error opening block device (%s)\n", strerror(errno));
-        return -1;
-    }
-
-    if (lseek64(data_device, 1024, SEEK_SET) < 0) {
-        WriteFdFmt(fd, "Error seeking to superblock\n");
-        adb_close(data_device);
-        return -1;
-    }
-
-    if (adb_read(data_device, &sb, sizeof(sb)) != sizeof(sb)) {
-        WriteFdFmt(fd, "Error reading superblock\n");
-        adb_close(data_device);
-        return -1;
-    }
-
-    ext4_parse_sb(&sb, &info);
-    *device_size = info.len;
-
-    adb_close(data_device);
-    return 0;
-}
-
 /* Turn verity on/off */
 static int set_verity_enabled_state(int fd, const char *block_device,
                                     const char* mount_point, bool enable)
 {
-    uint32_t magic_number;
-    const uint32_t new_magic = enable ? VERITY_METADATA_MAGIC_NUMBER
-                                      : VERITY_METADATA_MAGIC_DISABLE;
-    uint64_t device_length = 0;
-    int device = -1;
-    int retval = -1;
-
     if (!make_block_device_writable(block_device)) {
         WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
                    block_device, strerror(errno));
-        goto errout;
+        return -1;
     }
 
-    device = adb_open(block_device, O_RDWR | O_CLOEXEC);
-    if (device == -1) {
-        WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
-        WriteFdFmt(fd, "Maybe run adb remount?\n");
-        goto errout;
-    }
+    fec::io fh(block_device, O_RDWR);
 
-    // find the start of the verity metadata
-    if (get_target_device_size(fd, (char*)block_device, &device_length) < 0) {
-        WriteFdFmt(fd, "Could not get target device size.\n");
-        goto errout;
+    if (!fh) {
+        WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
+        WriteFdFmt(fd, "Maybe run adb root?\n");
+        return -1;
     }
 
-    if (lseek64(device, device_length, SEEK_SET) < 0) {
-        WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
-        goto errout;
-    }
+    fec_verity_metadata metadata;
 
-    // check the magic number
-    if (adb_read(device, &magic_number, sizeof(magic_number)) != sizeof(magic_number)) {
-        WriteFdFmt(fd, "Couldn't read magic number!\n");
-        goto errout;
+    if (!fh.get_verity_metadata(metadata)) {
+        WriteFdFmt(fd, "Couldn't find verity metadata!\n");
+        return -1;
     }
 
-    if (!enable && magic_number == VERITY_METADATA_MAGIC_DISABLE) {
+    if (!enable && metadata.disabled) {
         WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
-        goto errout;
+        return -1;
     }
 
-    if (enable && magic_number == VERITY_METADATA_MAGIC_NUMBER) {
+    if (enable && !metadata.disabled) {
         WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
-        goto errout;
-    }
-
-    if (magic_number != VERITY_METADATA_MAGIC_NUMBER
-            && magic_number != VERITY_METADATA_MAGIC_DISABLE) {
-        WriteFdFmt(fd, "Couldn't find verity metadata at offset %" PRIu64 "!\n", device_length);
-        goto errout;
-    }
-
-    if (lseek64(device, device_length, SEEK_SET) < 0) {
-        WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
-        goto errout;
+        return -1;
     }
 
-    if (adb_write(device, &new_magic, sizeof(new_magic)) != sizeof(new_magic)) {
+    if (!fh.set_verity_status(enable)) {
         WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
                    enable ? "enabled" : "disabled",
                    block_device, strerror(errno));
-        goto errout;
+        return -1;
     }
 
     WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
-    retval = 0;
-errout:
-    if (device != -1)
-        adb_close(device);
-    return retval;
+    return 0;
 }
 
 void set_verity_enabled_state_service(int fd, void* cookie)
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
new file mode 100644 (file)
index 0000000..e3fde26
--- /dev/null
@@ -0,0 +1,668 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Functionality for launching and managing shell subprocesses.
+//
+// There are two types of subprocesses, PTY or raw. PTY is typically used for
+// an interactive session, raw for non-interactive. There are also two methods
+// of communication with the subprocess, passing raw data or using a simple
+// protocol to wrap packets. The protocol allows separating stdout/stderr and
+// passing the exit code back, but is not backwards compatible.
+//   ----------------+--------------------------------------
+//   Type  Protocol  |   Exit code?  Separate stdout/stderr?
+//   ----------------+--------------------------------------
+//   PTY   No        |   No          No
+//   Raw   No        |   No          No
+//   PTY   Yes       |   Yes         No
+//   Raw   Yes       |   Yes         Yes
+//   ----------------+--------------------------------------
+//
+// Non-protocol subprocesses work by passing subprocess stdin/out/err through
+// a single pipe which is registered with a local socket in adbd. The local
+// socket uses the fdevent loop to pass raw data between this pipe and the
+// transport, which then passes data back to the adb client. Cleanup is done by
+// waiting in a separate thread for the subprocesses to exit and then signaling
+// a separate fdevent to close out the local socket from the main loop.
+//
+// ------------------+-------------------------+------------------------------
+//   Subprocess      |  adbd subprocess thread |   adbd main fdevent loop
+// ------------------+-------------------------+------------------------------
+//                   |                         |
+//   stdin/out/err <----------------------------->       LocalSocket
+//      |            |                         |
+//      |            |      Block on exit      |
+//      |            |           *             |
+//      v            |           *             |
+//     Exit         --->      Unblock          |
+//                   |           |             |
+//                   |           v             |
+//                   |   Notify shell exit FD --->    Close LocalSocket
+// ------------------+-------------------------+------------------------------
+//
+// The protocol requires the thread to intercept stdin/out/err in order to
+// wrap/unwrap data with shell protocol packets.
+//
+// ------------------+-------------------------+------------------------------
+//   Subprocess      |  adbd subprocess thread |   adbd main fdevent loop
+// ------------------+-------------------------+------------------------------
+//                   |                         |
+//     stdin/out   <--->      Protocol       <--->       LocalSocket
+//     stderr       --->      Protocol        --->       LocalSocket
+//       |           |                         |
+//       v           |                         |
+//      Exit        --->  Exit code protocol  --->       LocalSocket
+//                   |           |             |
+//                   |           v             |
+//                   |   Notify shell exit FD --->    Close LocalSocket
+// ------------------+-------------------------+------------------------------
+//
+// An alternate approach is to put the protocol wrapping/unwrapping in the main
+// fdevent loop, which has the advantage of being able to re-use the existing
+// select() code for handling data streams. However, implementation turned out
+// to be more complex due to partial reads and non-blocking I/O so this model
+// was chosen instead.
+
+#define TRACE_TAG SHELL
+
+#include "sysdeps.h"
+
+#include "shell_service.h"
+
+#include <errno.h>
+#include <pty.h>
+#include <pwd.h>
+#include <sys/select.h>
+#include <termios.h>
+
+#include <memory>
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+#include <paths.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "adb_trace.h"
+#include "adb_utils.h"
+
+namespace {
+
+void init_subproc_child()
+{
+    setsid();
+
+    // Set OOM score adjustment to prevent killing
+    int fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC);
+    if (fd >= 0) {
+        adb_write(fd, "0", 1);
+        adb_close(fd);
+    } else {
+       D("adb: unable to update oom_score_adj");
+    }
+}
+
+// Reads from |fd| until close or failure.
+std::string ReadAll(int fd) {
+    char buffer[512];
+    std::string received;
+
+    while (1) {
+        int bytes = adb_read(fd, buffer, sizeof(buffer));
+        if (bytes <= 0) {
+            break;
+        }
+        received.append(buffer, bytes);
+    }
+
+    return received;
+}
+
+// Helper to automatically close an FD when it goes out of scope.
+class ScopedFd {
+  public:
+    ScopedFd() {}
+    ~ScopedFd() { Reset(); }
+
+    void Reset(int fd=-1) {
+        if (fd != fd_) {
+            if (valid()) {
+                adb_close(fd_);
+            }
+            fd_ = fd;
+        }
+    }
+
+    int Release() {
+        int temp = fd_;
+        fd_ = -1;
+        return temp;
+    }
+
+    bool valid() const { return fd_ >= 0; }
+
+    int fd() const { return fd_; }
+
+  private:
+    int fd_ = -1;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedFd);
+};
+
+// Creates a socketpair and saves the endpoints to |fd1| and |fd2|.
+bool CreateSocketpair(ScopedFd* fd1, ScopedFd* fd2) {
+    int sockets[2];
+    if (adb_socketpair(sockets) < 0) {
+        PLOG(ERROR) << "cannot create socket pair";
+        return false;
+    }
+    fd1->Reset(sockets[0]);
+    fd2->Reset(sockets[1]);
+    return true;
+}
+
+class Subprocess {
+  public:
+    Subprocess(const std::string& command, SubprocessType type,
+               SubprocessProtocol protocol);
+    ~Subprocess();
+
+    const std::string& command() const { return command_; }
+    bool is_interactive() const { return command_.empty(); }
+
+    int local_socket_fd() const { return local_socket_sfd_.fd(); }
+
+    pid_t pid() const { return pid_; }
+
+    // Sets up FDs, forks a subprocess, starts the subprocess manager thread,
+    // and exec's the child. Returns false on failure.
+    bool ForkAndExec();
+
+  private:
+    // Opens the file at |pts_name|.
+    int OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd);
+
+    static void* ThreadHandler(void* userdata);
+    void PassDataStreams();
+    void WaitForExit();
+
+    ScopedFd* SelectLoop(fd_set* master_read_set_ptr,
+                         fd_set* master_write_set_ptr);
+
+    // Input/output stream handlers. Success returns nullptr, failure returns
+    // a pointer to the failed FD.
+    ScopedFd* PassInput();
+    ScopedFd* PassOutput(ScopedFd* sfd, ShellProtocol::Id id);
+
+    const std::string command_;
+    SubprocessType type_;
+    SubprocessProtocol protocol_;
+    pid_t pid_ = -1;
+    ScopedFd local_socket_sfd_;
+
+    // Shell protocol variables.
+    ScopedFd stdinout_sfd_, stderr_sfd_, protocol_sfd_;
+    std::unique_ptr<ShellProtocol> input_, output_;
+    size_t input_bytes_left_ = 0;
+
+    DISALLOW_COPY_AND_ASSIGN(Subprocess);
+};
+
+Subprocess::Subprocess(const std::string& command, SubprocessType type,
+                       SubprocessProtocol protocol)
+        : command_(command), type_(type), protocol_(protocol) {
+}
+
+Subprocess::~Subprocess() {
+}
+
+bool Subprocess::ForkAndExec() {
+    ScopedFd child_stdinout_sfd, child_stderr_sfd;
+    ScopedFd parent_error_sfd, child_error_sfd;
+    char pts_name[PATH_MAX];
+
+    // Create a socketpair for the fork() child to report any errors back to
+    // the parent. Since we use threads, logging directly from the child could
+    // create a race condition.
+    if (!CreateSocketpair(&parent_error_sfd, &child_error_sfd)) {
+        LOG(ERROR) << "failed to create pipe for subprocess error reporting";
+    }
+
+    if (type_ == SubprocessType::kPty) {
+        int fd;
+        pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
+        stdinout_sfd_.Reset(fd);
+    } else {
+        if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
+            return false;
+        }
+        // Raw subprocess + shell protocol allows for splitting stderr.
+        if (protocol_ == SubprocessProtocol::kShell &&
+                !CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) {
+            return false;
+        }
+        pid_ = fork();
+    }
+
+    if (pid_ == -1) {
+        PLOG(ERROR) << "fork failed";
+        return false;
+    }
+
+    if (pid_ == 0) {
+        // Subprocess child.
+        init_subproc_child();
+
+        if (type_ == SubprocessType::kPty) {
+            child_stdinout_sfd.Reset(OpenPtyChildFd(pts_name, &child_error_sfd));
+        }
+
+        dup2(child_stdinout_sfd.fd(), STDIN_FILENO);
+        dup2(child_stdinout_sfd.fd(), STDOUT_FILENO);
+        dup2(child_stderr_sfd.valid() ? child_stderr_sfd.fd() : child_stdinout_sfd.fd(),
+             STDERR_FILENO);
+
+        // exec doesn't trigger destructors, close the FDs manually.
+        stdinout_sfd_.Reset();
+        stderr_sfd_.Reset();
+        child_stdinout_sfd.Reset();
+        child_stderr_sfd.Reset();
+        parent_error_sfd.Reset();
+        close_on_exec(child_error_sfd.fd());
+
+        // TODO: $HOSTNAME? Normally bash automatically sets that, but mksh doesn't.
+        passwd* pw = getpwuid(getuid());
+        if (pw != nullptr) {
+            setenv("HOME", pw->pw_dir, 1);
+            setenv("LOGNAME", pw->pw_name, 1);
+            setenv("SHELL", pw->pw_shell, 1);
+            setenv("USER", pw->pw_name, 1);
+        }
+
+        if (is_interactive()) {
+            execl(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr);
+        } else {
+            execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr);
+        }
+        WriteFdExactly(child_error_sfd.fd(), "exec '" _PATH_BSHELL "' failed");
+        child_error_sfd.Reset();
+        exit(-1);
+    }
+
+    // Subprocess parent.
+    D("subprocess parent: stdin/stdout FD = %d, stderr FD = %d",
+      stdinout_sfd_.fd(), stderr_sfd_.fd());
+
+    // Wait to make sure the subprocess exec'd without error.
+    child_error_sfd.Reset();
+    std::string error_message = ReadAll(parent_error_sfd.fd());
+    if (!error_message.empty()) {
+        LOG(ERROR) << error_message;
+        return false;
+    }
+
+    if (protocol_ == SubprocessProtocol::kNone) {
+        // No protocol: all streams pass through the stdinout FD and hook
+        // directly into the local socket for raw data transfer.
+        local_socket_sfd_.Reset(stdinout_sfd_.Release());
+    } else {
+        // Shell protocol: create another socketpair to intercept data.
+        if (!CreateSocketpair(&protocol_sfd_, &local_socket_sfd_)) {
+            return false;
+        }
+        D("protocol FD = %d", protocol_sfd_.fd());
+
+        input_.reset(new ShellProtocol(protocol_sfd_.fd()));
+        output_.reset(new ShellProtocol(protocol_sfd_.fd()));
+        if (!input_ || !output_) {
+            LOG(ERROR) << "failed to allocate shell protocol objects";
+            return false;
+        }
+
+        // Don't let reads/writes to the subprocess block our thread. This isn't
+        // likely but could happen under unusual circumstances, such as if we
+        // write a ton of data to stdin but the subprocess never reads it and
+        // the pipe fills up.
+        for (int fd : {stdinout_sfd_.fd(), stderr_sfd_.fd()}) {
+            if (fd >= 0) {
+                if (!set_file_block_mode(fd, false)) {
+                    LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
+                    return false;
+                }
+            }
+        }
+    }
+
+    if (!adb_thread_create(ThreadHandler, this)) {
+        PLOG(ERROR) << "failed to create subprocess thread";
+        return false;
+    }
+
+    return true;
+}
+
+int Subprocess::OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd) {
+    int child_fd = adb_open(pts_name, O_RDWR | O_CLOEXEC);
+    if (child_fd == -1) {
+        // Don't use WriteFdFmt; since we're in the fork() child we don't want
+        // to allocate any heap memory to avoid race conditions.
+        const char* messages[] = {"child failed to open pseudo-term slave ",
+                                  pts_name, ": ", strerror(errno)};
+        for (const char* message : messages) {
+            WriteFdExactly(error_sfd->fd(), message);
+        }
+        exit(-1);
+    }
+
+    if (!is_interactive()) {
+        termios tattr;
+        if (tcgetattr(child_fd, &tattr) == -1) {
+            WriteFdExactly(error_sfd->fd(), "tcgetattr failed");
+            exit(-1);
+        }
+
+        cfmakeraw(&tattr);
+        if (tcsetattr(child_fd, TCSADRAIN, &tattr) == -1) {
+            WriteFdExactly(error_sfd->fd(), "tcsetattr failed");
+            exit(-1);
+        }
+    }
+
+    return child_fd;
+}
+
+void* Subprocess::ThreadHandler(void* userdata) {
+    Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata);
+
+    adb_thread_setname(android::base::StringPrintf(
+            "shell srvc %d", subprocess->local_socket_fd()));
+
+    subprocess->PassDataStreams();
+    subprocess->WaitForExit();
+
+    D("deleting Subprocess for PID %d", subprocess->pid());
+    delete subprocess;
+
+    return nullptr;
+}
+
+void Subprocess::PassDataStreams() {
+    if (!protocol_sfd_.valid()) {
+        return;
+    }
+
+    // Start by trying to read from the protocol FD, stdout, and stderr.
+    fd_set master_read_set, master_write_set;
+    FD_ZERO(&master_read_set);
+    FD_ZERO(&master_write_set);
+    for (ScopedFd* sfd : {&protocol_sfd_, &stdinout_sfd_, &stderr_sfd_}) {
+        if (sfd->valid()) {
+            FD_SET(sfd->fd(), &master_read_set);
+        }
+    }
+
+    // Pass data until the protocol FD or both the subprocess pipes die, at
+    // which point we can't pass any more data.
+    while (protocol_sfd_.valid() &&
+            (stdinout_sfd_.valid() || stderr_sfd_.valid())) {
+        ScopedFd* dead_sfd = SelectLoop(&master_read_set, &master_write_set);
+        if (dead_sfd) {
+            D("closing FD %d", dead_sfd->fd());
+            FD_CLR(dead_sfd->fd(), &master_read_set);
+            FD_CLR(dead_sfd->fd(), &master_write_set);
+            if (dead_sfd == &protocol_sfd_) {
+                // Using SIGHUP is a decent general way to indicate that the
+                // controlling process is going away. If specific signals are
+                // needed (e.g. SIGINT), pass those through the shell protocol
+                // and only fall back on this for unexpected closures.
+                D("protocol FD died, sending SIGHUP to pid %d", pid_);
+                kill(pid_, SIGHUP);
+            }
+            dead_sfd->Reset();
+        }
+    }
+}
+
+namespace {
+
+inline bool ValidAndInSet(const ScopedFd& sfd, fd_set* set) {
+    return sfd.valid() && FD_ISSET(sfd.fd(), set);
+}
+
+}   // namespace
+
+ScopedFd* Subprocess::SelectLoop(fd_set* master_read_set_ptr,
+                                 fd_set* master_write_set_ptr) {
+    fd_set read_set, write_set;
+    int select_n = std::max(std::max(protocol_sfd_.fd(), stdinout_sfd_.fd()),
+                            stderr_sfd_.fd()) + 1;
+    ScopedFd* dead_sfd = nullptr;
+
+    // Keep calling select() and passing data until an FD closes/errors.
+    while (!dead_sfd) {
+        memcpy(&read_set, master_read_set_ptr, sizeof(read_set));
+        memcpy(&write_set, master_write_set_ptr, sizeof(write_set));
+        if (select(select_n, &read_set, &write_set, nullptr, nullptr) < 0) {
+            if (errno == EINTR) {
+                continue;
+            } else {
+                PLOG(ERROR) << "select failed, closing subprocess pipes";
+                stdinout_sfd_.Reset();
+                stderr_sfd_.Reset();
+                return nullptr;
+            }
+        }
+
+        // Read stdout, write to protocol FD.
+        if (ValidAndInSet(stdinout_sfd_, &read_set)) {
+            dead_sfd = PassOutput(&stdinout_sfd_, ShellProtocol::kIdStdout);
+        }
+
+        // Read stderr, write to protocol FD.
+        if (!dead_sfd && ValidAndInSet(stderr_sfd_, &read_set)) {
+            dead_sfd = PassOutput(&stderr_sfd_, ShellProtocol::kIdStderr);
+        }
+
+        // Read protocol FD, write to stdin.
+        if (!dead_sfd && ValidAndInSet(protocol_sfd_, &read_set)) {
+            dead_sfd = PassInput();
+            // If we didn't finish writing, block on stdin write.
+            if (input_bytes_left_) {
+                FD_CLR(protocol_sfd_.fd(), master_read_set_ptr);
+                FD_SET(stdinout_sfd_.fd(), master_write_set_ptr);
+            }
+        }
+
+        // Continue writing to stdin; only happens if a previous write blocked.
+        if (!dead_sfd && ValidAndInSet(stdinout_sfd_, &write_set)) {
+            dead_sfd = PassInput();
+            // If we finished writing, go back to blocking on protocol read.
+            if (!input_bytes_left_) {
+                FD_SET(protocol_sfd_.fd(), master_read_set_ptr);
+                FD_CLR(stdinout_sfd_.fd(), master_write_set_ptr);
+            }
+        }
+    }  // while (!dead_sfd)
+
+    return dead_sfd;
+}
+
+ScopedFd* Subprocess::PassInput() {
+    // Only read a new packet if we've finished writing the last one.
+    if (!input_bytes_left_) {
+        if (!input_->Read()) {
+            // Read() uses ReadFdExactly() which sets errno to 0 on EOF.
+            if (errno != 0) {
+                PLOG(ERROR) << "error reading protocol FD "
+                            << protocol_sfd_.fd();
+            }
+            return &protocol_sfd_;
+        }
+
+        if (stdinout_sfd_.valid()) {
+            switch (input_->id()) {
+                case ShellProtocol::kIdWindowSizeChange:
+                    int rows, cols, x_pixels, y_pixels;
+                    if (sscanf(input_->data(), "%dx%d,%dx%d",
+                               &rows, &cols, &x_pixels, &y_pixels) == 4) {
+                        winsize ws;
+                        ws.ws_row = rows;
+                        ws.ws_col = cols;
+                        ws.ws_xpixel = x_pixels;
+                        ws.ws_ypixel = y_pixels;
+                        ioctl(stdinout_sfd_.fd(), TIOCSWINSZ, &ws);
+                    }
+                    break;
+                case ShellProtocol::kIdStdin:
+                    input_bytes_left_ = input_->data_length();
+                    break;
+                case ShellProtocol::kIdCloseStdin:
+                    if (type_ == SubprocessType::kRaw) {
+                        if (adb_shutdown(stdinout_sfd_.fd(), SHUT_WR) == 0) {
+                            return nullptr;
+                        }
+                        PLOG(ERROR) << "failed to shutdown writes to FD "
+                                    << stdinout_sfd_.fd();
+                        return &stdinout_sfd_;
+                    } else {
+                        // PTYs can't close just input, so rather than close the
+                        // FD and risk losing subprocess output, leave it open.
+                        // This only happens if the client starts a PTY shell
+                        // non-interactively which is rare and unsupported.
+                        // If necessary, the client can manually close the shell
+                        // with `exit` or by killing the adb client process.
+                        D("can't close input for PTY FD %d", stdinout_sfd_.fd());
+                    }
+                    break;
+            }
+        }
+    }
+
+    if (input_bytes_left_ > 0) {
+        int index = input_->data_length() - input_bytes_left_;
+        int bytes = adb_write(stdinout_sfd_.fd(), input_->data() + index,
+                              input_bytes_left_);
+        if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
+            if (bytes < 0) {
+                PLOG(ERROR) << "error reading stdin FD " << stdinout_sfd_.fd();
+            }
+            // stdin is done, mark this packet as finished and we'll just start
+            // dumping any further data received from the protocol FD.
+            input_bytes_left_ = 0;
+            return &stdinout_sfd_;
+        } else if (bytes > 0) {
+            input_bytes_left_ -= bytes;
+        }
+    }
+
+    return nullptr;
+}
+
+ScopedFd* Subprocess::PassOutput(ScopedFd* sfd, ShellProtocol::Id id) {
+    int bytes = adb_read(sfd->fd(), output_->data(), output_->data_capacity());
+    if (bytes == 0 || (bytes < 0 && errno != EAGAIN)) {
+        // read() returns EIO if a PTY closes; don't report this as an error,
+        // it just means the subprocess completed.
+        if (bytes < 0 && !(type_ == SubprocessType::kPty && errno == EIO)) {
+            PLOG(ERROR) << "error reading output FD " << sfd->fd();
+        }
+        return sfd;
+    }
+
+    if (bytes > 0 && !output_->Write(id, bytes)) {
+        if (errno != 0) {
+            PLOG(ERROR) << "error reading protocol FD " << protocol_sfd_.fd();
+        }
+        return &protocol_sfd_;
+    }
+
+    return nullptr;
+}
+
+void Subprocess::WaitForExit() {
+    int exit_code = 1;
+
+    D("waiting for pid %d", pid_);
+    while (true) {
+        int status;
+        if (pid_ == waitpid(pid_, &status, 0)) {
+            D("post waitpid (pid=%d) status=%04x", pid_, status);
+            if (WIFSIGNALED(status)) {
+                exit_code = 0x80 | WTERMSIG(status);
+                D("subprocess killed by signal %d", WTERMSIG(status));
+                break;
+            } else if (!WIFEXITED(status)) {
+                D("subprocess didn't exit");
+                break;
+            } else if (WEXITSTATUS(status) >= 0) {
+                exit_code = WEXITSTATUS(status);
+                D("subprocess exit code = %d", WEXITSTATUS(status));
+                break;
+            }
+        }
+    }
+
+    // If we have an open protocol FD send an exit packet.
+    if (protocol_sfd_.valid()) {
+        output_->data()[0] = exit_code;
+        if (output_->Write(ShellProtocol::kIdExit, 1)) {
+            D("wrote the exit code packet: %d", exit_code);
+        } else {
+            PLOG(ERROR) << "failed to write the exit code packet";
+        }
+        protocol_sfd_.Reset();
+    }
+
+    // Pass the local socket FD to the shell cleanup fdevent.
+    if (SHELL_EXIT_NOTIFY_FD >= 0) {
+        int fd = local_socket_sfd_.fd();
+        if (WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd))) {
+            D("passed fd %d to SHELL_EXIT_NOTIFY_FD (%d) for pid %d",
+              fd, SHELL_EXIT_NOTIFY_FD, pid_);
+            // The shell exit fdevent now owns the FD and will close it once
+            // the last bit of data flushes through.
+            local_socket_sfd_.Release();
+        } else {
+            PLOG(ERROR) << "failed to write fd " << fd
+                        << " to SHELL_EXIT_NOTIFY_FD (" << SHELL_EXIT_NOTIFY_FD
+                        << ") for pid " << pid_;
+        }
+    }
+}
+
+}  // namespace
+
+int StartSubprocess(const char *name, SubprocessType type,
+                    SubprocessProtocol protocol) {
+    D("starting %s subprocess (protocol=%s): '%s'",
+      type == SubprocessType::kRaw ? "raw" : "PTY",
+      protocol == SubprocessProtocol::kNone ? "none" : "shell", name);
+
+    Subprocess* subprocess = new Subprocess(name, type, protocol);
+    if (!subprocess) {
+        LOG(ERROR) << "failed to allocate new subprocess";
+        return -1;
+    }
+
+    if (!subprocess->ForkAndExec()) {
+        LOG(ERROR) << "failed to start subprocess";
+        delete subprocess;
+        return -1;
+    }
+
+    D("subprocess creation successful: local_socket_fd=%d, pid=%d",
+      subprocess->local_socket_fd(), subprocess->pid());
+    return subprocess->local_socket_fd();
+}
diff --git a/adb/shell_service.h b/adb/shell_service.h
new file mode 100644 (file)
index 0000000..63c00da
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This file contains classes and functionality to launch shell subprocesses
+// in adbd and communicate between those subprocesses and the adb client.
+//
+// The main features exposed here are:
+//   1. A ShellPacket class to wrap data in a simple protocol. Both adbd and
+//      the adb client use this class to transmit data between them.
+//   2. Functions to launch a subprocess on the adbd side.
+
+#ifndef SHELL_SERVICE_H_
+#define SHELL_SERVICE_H_
+
+#include <stdint.h>
+
+#include <base/macros.h>
+
+#include "adb.h"
+
+// Class to send and receive shell protocol packets.
+//
+// To keep things simple and predictable, reads and writes block until an entire
+// packet is complete.
+//
+// Example: read raw data from |fd| and send it in a packet.
+//   ShellProtocol* p = new ShellProtocol(protocol_fd);
+//   int len = adb_read(stdout_fd, p->data(), p->data_capacity());
+//   packet->WritePacket(ShellProtocol::kIdStdout, len);
+//
+// Example: read a packet and print it to |stdout|.
+//   ShellProtocol* p = new ShellProtocol(protocol_fd);
+//   if (p->ReadPacket() && p->id() == kIdStdout) {
+//       fwrite(p->data(), 1, p->data_length(), stdout);
+//   }
+class ShellProtocol {
+  public:
+    // This is an unscoped enum to make it easier to compare against raw bytes.
+    enum Id : uint8_t {
+        kIdStdin = 0,
+        kIdStdout = 1,
+        kIdStderr = 2,
+        kIdExit = 3,
+
+        // Close subprocess stdin if possible.
+        kIdCloseStdin = 4,
+
+        // Window size change (an ASCII version of struct winsize).
+        kIdWindowSizeChange = 5,
+
+        // Indicates an invalid or unknown packet.
+        kIdInvalid = 255,
+    };
+
+    // ShellPackets will probably be too large to allocate on the stack so they
+    // should be dynamically allocated on the heap instead.
+    //
+    // |fd| is an open file descriptor to be used to send or receive packets.
+    explicit ShellProtocol(int fd);
+    virtual ~ShellProtocol();
+
+    // Returns a pointer to the data buffer.
+    const char* data() const { return buffer_ + kHeaderSize; }
+    char* data() { return buffer_ + kHeaderSize; }
+
+    // Returns the total capacity of the data buffer.
+    size_t data_capacity() const { return buffer_end_ - data(); }
+
+    // Reads a packet from the FD.
+    //
+    // If a packet is too big to fit in the buffer then Read() will split the
+    // packet across multiple calls. For example, reading a 50-byte packet into
+    // a 20-byte buffer would read 20 bytes, 20 bytes, then 10 bytes.
+    //
+    // Returns false if the FD closed or errored.
+    bool Read();
+
+    // Returns the ID of the packet in the buffer.
+    int id() const { return buffer_[0]; }
+
+    // Returns the number of bytes that have been read into the data buffer.
+    size_t data_length() const { return data_length_; }
+
+    // Writes the packet currently in the buffer to the FD.
+    //
+    // Returns false if the FD closed or errored.
+    bool Write(Id id, size_t length);
+
+  private:
+    // Packets support 4-byte lengths.
+    typedef uint32_t length_t;
+
+    enum {
+        // It's OK if MAX_PAYLOAD doesn't match on the sending and receiving
+        // end, reading will split larger packets into multiple smaller ones.
+        kBufferSize = MAX_PAYLOAD,
+
+        // Header is 1 byte ID + 4 bytes length.
+        kHeaderSize = sizeof(Id) + sizeof(length_t)
+    };
+
+    int fd_;
+    char buffer_[kBufferSize];
+    size_t data_length_ = 0, bytes_left_ = 0;
+
+    // We need to be able to modify this value for testing purposes, but it
+    // will stay constant during actual program use.
+    char* buffer_end_ = buffer_ + sizeof(buffer_);
+
+    friend class ShellProtocolTest;
+
+    DISALLOW_COPY_AND_ASSIGN(ShellProtocol);
+};
+
+#if !ADB_HOST
+
+enum class SubprocessType {
+    kPty,
+    kRaw,
+};
+
+enum class SubprocessProtocol {
+    kNone,
+    kShell,
+};
+
+// Forks and starts a new shell subprocess. If |name| is empty an interactive
+// shell is started, otherwise |name| is executed non-interactively.
+//
+// Returns an open FD connected to the subprocess or -1 on failure.
+int StartSubprocess(const char* name, SubprocessType type,
+                    SubprocessProtocol protocol);
+
+#endif  // !ADB_HOST
+
+#endif  // SHELL_SERVICE_H_
diff --git a/adb/shell_service_protocol.cpp b/adb/shell_service_protocol.cpp
new file mode 100644 (file)
index 0000000..623629c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "shell_service.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "adb_io.h"
+
+ShellProtocol::ShellProtocol(int fd) : fd_(fd) {
+    buffer_[0] = kIdInvalid;
+}
+
+ShellProtocol::~ShellProtocol() {
+}
+
+bool ShellProtocol::Read() {
+    // Only read a new header if we've finished the last packet.
+    if (!bytes_left_) {
+        if (!ReadFdExactly(fd_, buffer_, kHeaderSize)) {
+            return false;
+        }
+
+        length_t packet_length;
+        memcpy(&packet_length, &buffer_[1], sizeof(packet_length));
+        bytes_left_ = packet_length;
+        data_length_ = 0;
+    }
+
+    size_t read_length = std::min(bytes_left_, data_capacity());
+    if (read_length && !ReadFdExactly(fd_, data(), read_length)) {
+        return false;
+    }
+
+    bytes_left_ -= read_length;
+    data_length_ = read_length;
+
+    return true;
+}
+
+bool ShellProtocol::Write(Id id, size_t length) {
+    buffer_[0] = id;
+    length_t typed_length = length;
+    memcpy(&buffer_[1], &typed_length, sizeof(typed_length));
+
+    return WriteFdExactly(fd_, buffer_, kHeaderSize + length);
+}
diff --git a/adb/shell_service_protocol_test.cpp b/adb/shell_service_protocol_test.cpp
new file mode 100644 (file)
index 0000000..a826035
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "shell_service.h"
+
+#include <gtest/gtest.h>
+
+#include <signal.h>
+#include <string.h>
+
+#include "sysdeps.h"
+
+class ShellProtocolTest : public ::testing::Test {
+  public:
+    static void SetUpTestCase() {
+#if !defined(_WIN32)
+        // This is normally done in main.cpp.
+        saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
+#endif
+    }
+
+    static void TearDownTestCase() {
+#if !defined(_WIN32)
+        signal(SIGPIPE, saved_sigpipe_handler_);
+#endif
+    }
+
+    // Initializes the socketpair and ShellProtocols needed for testing.
+    void SetUp() {
+        int fds[2];
+        ASSERT_EQ(0, adb_socketpair(fds));
+        read_fd_ = fds[0];
+        write_fd_ = fds[1];
+
+        write_protocol_ = new ShellProtocol(write_fd_);
+        ASSERT_TRUE(write_protocol_ != nullptr);
+
+        read_protocol_ = new ShellProtocol(read_fd_);
+        ASSERT_TRUE(read_protocol_ != nullptr);
+    }
+
+    // Cleans up FDs and ShellProtocols. If an FD is closed manually during a
+    // test, set it to -1 to prevent TearDown() trying to close it again.
+    void TearDown() {
+        for (int fd : {read_fd_, write_fd_}) {
+            if (fd >= 0) {
+                adb_close(fd);
+            }
+        }
+        for (ShellProtocol* protocol : {read_protocol_, write_protocol_}) {
+            if (protocol) {
+                delete protocol;
+            }
+        }
+    }
+
+    // Fakes the buffer size so we can test filling buffers.
+    void SetReadDataCapacity(size_t size) {
+        read_protocol_->buffer_end_ = read_protocol_->data() + size;
+    }
+
+#if !defined(_WIN32)
+    static sig_t saved_sigpipe_handler_;
+#endif
+
+    int read_fd_ = -1, write_fd_ = -1;
+    ShellProtocol *read_protocol_ = nullptr, *write_protocol_ = nullptr;
+};
+
+#if !defined(_WIN32)
+sig_t ShellProtocolTest::saved_sigpipe_handler_ = nullptr;
+#endif
+
+namespace {
+
+// Returns true if the packet contains the given values.
+bool PacketEquals(const ShellProtocol* protocol, ShellProtocol::Id id,
+                    const void* data, size_t data_length) {
+    return (protocol->id() == id &&
+            protocol->data_length() == data_length &&
+            !memcmp(data, protocol->data(), data_length));
+}
+
+}  // namespace
+
+// Tests data that can fit in a single packet.
+TEST_F(ShellProtocolTest, FullPacket) {
+    ShellProtocol::Id id = ShellProtocol::kIdStdout;
+    char data[] = "abc 123 \0\r\n";
+
+    memcpy(write_protocol_->data(), data, sizeof(data));
+    ASSERT_TRUE(write_protocol_->Write(id, sizeof(data)));
+
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, data, sizeof(data)));
+}
+
+// Tests data that has to be read multiple times due to smaller read buffer.
+TEST_F(ShellProtocolTest, ReadBufferOverflow) {
+    ShellProtocol::Id id = ShellProtocol::kIdStdin;
+
+    memcpy(write_protocol_->data(), "1234567890", 10);
+    ASSERT_TRUE(write_protocol_->Write(id, 10));
+
+    SetReadDataCapacity(4);
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, "1234", 4));
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, "5678", 4));
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, "90", 2));
+}
+
+// Tests a zero length packet.
+TEST_F(ShellProtocolTest, ZeroLengthPacket) {
+    ShellProtocol::Id id = ShellProtocol::kIdStderr;
+
+    ASSERT_TRUE(write_protocol_->Write(id, 0));
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, nullptr, 0));
+}
+
+// Tests exit code packets.
+TEST_F(ShellProtocolTest, ExitCodePacket) {
+    write_protocol_->data()[0] = 20;
+    ASSERT_TRUE(write_protocol_->Write(ShellProtocol::kIdExit, 1));
+
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_EQ(ShellProtocol::kIdExit, read_protocol_->id());
+    ASSERT_EQ(20, read_protocol_->data()[0]);
+}
+
+// Tests writing to a closed pipe.
+TEST_F(ShellProtocolTest, WriteToClosedPipeFail) {
+    adb_close(read_fd_);
+    read_fd_ = -1;
+
+    ASSERT_FALSE(write_protocol_->Write(ShellProtocol::kIdStdout, 0));
+}
+
+// Tests writing to a closed FD.
+TEST_F(ShellProtocolTest, WriteToClosedFdFail) {
+    adb_close(write_fd_);
+    write_fd_ = -1;
+
+    ASSERT_FALSE(write_protocol_->Write(ShellProtocol::kIdStdout, 0));
+}
+
+// Tests reading from a closed pipe.
+TEST_F(ShellProtocolTest, ReadFromClosedPipeFail) {
+    adb_close(write_fd_);
+    write_fd_ = -1;
+
+    ASSERT_FALSE(read_protocol_->Read());
+}
+
+// Tests reading from a closed FD.
+TEST_F(ShellProtocolTest, ReadFromClosedFdFail) {
+    adb_close(read_fd_);
+    read_fd_ = -1;
+
+    ASSERT_FALSE(read_protocol_->Read());
+}
+
+// Tests reading from a closed pipe that has a packet waiting. This checks that
+// even if the pipe closes before we can fully read its contents we will still
+// be able to access the last packets.
+TEST_F(ShellProtocolTest, ReadPacketFromClosedPipe) {
+    ShellProtocol::Id id = ShellProtocol::kIdStdout;
+    char data[] = "foo bar";
+
+    memcpy(write_protocol_->data(), data, sizeof(data));
+    ASSERT_TRUE(write_protocol_->Write(id, sizeof(data)));
+    adb_close(write_fd_);
+    write_fd_ = -1;
+
+    // First read should grab the packet.
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, data, sizeof(data)));
+
+    // Second read should fail.
+    ASSERT_FALSE(read_protocol_->Read());
+}
diff --git a/adb/shell_service_test.cpp b/adb/shell_service_test.cpp
new file mode 100644 (file)
index 0000000..e18f905
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "shell_service.h"
+
+#include <gtest/gtest.h>
+
+#include <signal.h>
+
+#include <string>
+#include <vector>
+
+#include <base/strings.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "sysdeps.h"
+
+class ShellServiceTest : public ::testing::Test {
+  public:
+    static void SetUpTestCase() {
+        // This is normally done in main.cpp.
+        saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
+
+    }
+
+    static void TearDownTestCase() {
+        signal(SIGPIPE, saved_sigpipe_handler_);
+    }
+
+    // Helpers to start and cleanup a subprocess. Cleanup normally does not
+    // need to be called manually unless multiple subprocesses are run from
+    // a single test.
+    void StartTestSubprocess(const char* command, SubprocessType type,
+                             SubprocessProtocol protocol);
+    void CleanupTestSubprocess();
+
+    virtual void TearDown() override {
+        void CleanupTestSubprocess();
+    }
+
+    static sighandler_t saved_sigpipe_handler_;
+
+    int subprocess_fd_ = -1;
+    int shell_exit_receiver_fd_ = -1, saved_shell_exit_fd_;
+};
+
+sighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr;
+
+void ShellServiceTest::StartTestSubprocess(
+        const char* command, SubprocessType type, SubprocessProtocol protocol) {
+    // We want to intercept the shell exit message to make sure it's sent.
+    saved_shell_exit_fd_ = SHELL_EXIT_NOTIFY_FD;
+    int fd[2];
+    ASSERT_TRUE(adb_socketpair(fd) >= 0);
+    SHELL_EXIT_NOTIFY_FD = fd[0];
+    shell_exit_receiver_fd_ = fd[1];
+
+    subprocess_fd_ = StartSubprocess(command, type, protocol);
+    ASSERT_TRUE(subprocess_fd_ >= 0);
+}
+
+void ShellServiceTest::CleanupTestSubprocess() {
+    if (subprocess_fd_ >= 0) {
+        // Subprocess should send its FD to SHELL_EXIT_NOTIFY_FD for cleanup.
+        int notified_fd = -1;
+        ASSERT_TRUE(ReadFdExactly(shell_exit_receiver_fd_, &notified_fd,
+                                  sizeof(notified_fd)));
+        ASSERT_EQ(notified_fd, subprocess_fd_);
+
+        adb_close(subprocess_fd_);
+        subprocess_fd_ = -1;
+
+        // Restore SHELL_EXIT_NOTIFY_FD.
+        adb_close(SHELL_EXIT_NOTIFY_FD);
+        adb_close(shell_exit_receiver_fd_);
+        shell_exit_receiver_fd_ = -1;
+        SHELL_EXIT_NOTIFY_FD = saved_shell_exit_fd_;
+    }
+}
+
+namespace {
+
+// Reads raw data from |fd| until it closes or errors.
+std::string ReadRaw(int fd) {
+    char buffer[1024];
+    char *cur_ptr = buffer, *end_ptr = buffer + sizeof(buffer);
+
+    while (1) {
+        int bytes = adb_read(fd, cur_ptr, end_ptr - cur_ptr);
+        if (bytes <= 0) {
+            return std::string(buffer, cur_ptr);
+        }
+        cur_ptr += bytes;
+    }
+}
+
+// Reads shell protocol data from |fd| until it closes or errors. Fills
+// |stdout| and |stderr| with their respective data, and returns the exit code
+// read from the protocol or -1 if an exit code packet was not received.
+int ReadShellProtocol(int fd, std::string* stdout, std::string* stderr) {
+    int exit_code = -1;
+    stdout->clear();
+    stderr->clear();
+
+    ShellProtocol* protocol = new ShellProtocol(fd);
+    while (protocol->Read()) {
+        switch (protocol->id()) {
+            case ShellProtocol::kIdStdout:
+                stdout->append(protocol->data(), protocol->data_length());
+                break;
+            case ShellProtocol::kIdStderr:
+                stderr->append(protocol->data(), protocol->data_length());
+                break;
+            case ShellProtocol::kIdExit:
+                EXPECT_EQ(-1, exit_code) << "Multiple exit packets received";
+                EXPECT_EQ(1u, protocol->data_length());
+                exit_code = protocol->data()[0];
+                break;
+            default:
+                ADD_FAILURE() << "Unidentified packet ID: " << protocol->id();
+        }
+    }
+    delete protocol;
+
+    return exit_code;
+}
+
+// Checks if each line in |lines| exists in the same order in |output|. Blank
+// lines in |output| are ignored for simplicity.
+bool ExpectLinesEqual(const std::string& output,
+                      const std::vector<std::string>& lines) {
+    auto output_lines = android::base::Split(output, "\r\n");
+    size_t i = 0;
+
+    for (const std::string& line : lines) {
+        // Skip empty lines in output.
+        while (i < output_lines.size() && output_lines[i].empty()) {
+            ++i;
+        }
+        if (i >= output_lines.size()) {
+            ADD_FAILURE() << "Ran out of output lines";
+            return false;
+        }
+        EXPECT_EQ(line, output_lines[i]);
+        ++i;
+    }
+
+    while (i < output_lines.size() && output_lines[i].empty()) {
+        ++i;
+    }
+    EXPECT_EQ(i, output_lines.size()) << "Found unmatched output lines";
+    return true;
+}
+
+}  // namespace
+
+// Tests a raw subprocess with no protocol.
+TEST_F(ShellServiceTest, RawNoProtocolSubprocess) {
+    // [ -t 0 ] checks if stdin is connected to a terminal.
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
+            SubprocessType::kRaw, SubprocessProtocol::kNone));
+
+    // [ -t 0 ] == 1 means no terminal (raw).
+    ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "1"});
+}
+
+// Tests a PTY subprocess with no protocol.
+TEST_F(ShellServiceTest, PtyNoProtocolSubprocess) {
+    // [ -t 0 ] checks if stdin is connected to a terminal.
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "echo foo; echo bar >&2; [ -t 0 ]; echo $?",
+            SubprocessType::kPty, SubprocessProtocol::kNone));
+
+    // [ -t 0 ] == 0 means we have a terminal (PTY).
+    ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"});
+}
+
+// Tests a raw subprocess with the shell protocol.
+TEST_F(ShellServiceTest, RawShellProtocolSubprocess) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "echo foo; echo bar >&2; echo baz; exit 24",
+            SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+    std::string stdout, stderr;
+    EXPECT_EQ(24, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {"foo", "baz"});
+    ExpectLinesEqual(stderr, {"bar"});
+}
+
+// Tests a PTY subprocess with the shell protocol.
+TEST_F(ShellServiceTest, PtyShellProtocolSubprocess) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "echo foo; echo bar >&2; echo baz; exit 50",
+            SubprocessType::kPty, SubprocessProtocol::kShell));
+
+    // PTY always combines stdout and stderr but the shell protocol should
+    // still give us an exit code.
+    std::string stdout, stderr;
+    EXPECT_EQ(50, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {"foo", "bar", "baz"});
+    ExpectLinesEqual(stderr, {});
+}
+
+// Tests an interactive PTY session.
+TEST_F(ShellServiceTest, InteractivePtySubprocess) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "", SubprocessType::kPty, SubprocessProtocol::kShell));
+
+    // Use variable substitution so echoed input is different from output.
+    const char* commands[] = {"TEST_STR=abc123",
+                              "echo --${TEST_STR}--",
+                              "exit"};
+
+    ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+    for (std::string command : commands) {
+        // Interactive shell requires a newline to complete each command.
+        command.push_back('\n');
+        memcpy(protocol->data(), command.data(), command.length());
+        ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, command.length()));
+    }
+    delete protocol;
+
+    std::string stdout, stderr;
+    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    // An unpredictable command prompt makes parsing exact output difficult but
+    // it should at least contain echoed input and the expected output.
+    for (const char* command : commands) {
+        EXPECT_FALSE(stdout.find(command) == std::string::npos);
+    }
+    EXPECT_FALSE(stdout.find("--abc123--") == std::string::npos);
+}
+
+// Tests closing raw subprocess stdin.
+TEST_F(ShellServiceTest, CloseClientStdin) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "cat; echo TEST_DONE",
+            SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+    std::string input = "foo\nbar";
+    ShellProtocol* protocol = new ShellProtocol(subprocess_fd_);
+    memcpy(protocol->data(), input.data(), input.length());
+    ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length()));
+    ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0));
+    delete protocol;
+
+    std::string stdout, stderr;
+    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"});
+    ExpectLinesEqual(stderr, {});
+}
+
+// Tests that nothing breaks when the stdin/stdout pipe closes.
+TEST_F(ShellServiceTest, CloseStdinStdoutSubprocess) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "exec 0<&-; exec 1>&-; echo bar >&2",
+            SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+    std::string stdout, stderr;
+    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {});
+    ExpectLinesEqual(stderr, {"bar"});
+}
+
+// Tests that nothing breaks when the stderr pipe closes.
+TEST_F(ShellServiceTest, CloseStderrSubprocess) {
+    ASSERT_NO_FATAL_FAILURE(StartTestSubprocess(
+            "exec 2>&-; echo foo",
+            SubprocessType::kRaw, SubprocessProtocol::kShell));
+
+    std::string stdout, stderr;
+    EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr));
+    ExpectLinesEqual(stdout, {"foo"});
+    ExpectLinesEqual(stderr, {});
+}
diff --git a/adb/socket.h b/adb/socket.h
new file mode 100644 (file)
index 0000000..4083036
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __ADB_SOCKET_H
+#define __ADB_SOCKET_H
+
+#include <stddef.h>
+
+#include "fdevent.h"
+
+struct apacket;
+class atransport;
+
+/* An asocket represents one half of a connection between a local and
+** remote entity.  A local asocket is bound to a file descriptor.  A
+** remote asocket is bound to the protocol engine.
+*/
+struct asocket {
+        /* chain pointers for the local/remote list of
+        ** asockets that this asocket lives in
+        */
+    asocket *next;
+    asocket *prev;
+
+        /* the unique identifier for this asocket
+        */
+    unsigned id;
+
+        /* flag: set when the socket's peer has closed
+        ** but packets are still queued for delivery
+        */
+    int    closing;
+
+    // flag: set when the socket failed to write, so the socket will not wait to
+    // write packets and close directly.
+    bool has_write_error;
+
+        /* flag: quit adbd when both ends close the
+        ** local service socket
+        */
+    int    exit_on_close;
+
+        /* the asocket we are connected to
+        */
+
+    asocket *peer;
+
+        /* For local asockets, the fde is used to bind
+        ** us to our fd event system.  For remote asockets
+        ** these fields are not used.
+        */
+    fdevent fde;
+    int fd;
+
+        /* queue of apackets waiting to be written
+        */
+    apacket *pkt_first;
+    apacket *pkt_last;
+
+        /* enqueue is called by our peer when it has data
+        ** for us.  It should return 0 if we can accept more
+        ** data or 1 if not.  If we return 1, we must call
+        ** peer->ready() when we once again are ready to
+        ** receive data.
+        */
+    int (*enqueue)(asocket *s, apacket *pkt);
+
+        /* ready is called by the peer when it is ready for
+        ** us to send data via enqueue again
+        */
+    void (*ready)(asocket *s);
+
+        /* shutdown is called by the peer before it goes away.
+        ** the socket should not do any further calls on its peer.
+        ** Always followed by a call to close. Optional, i.e. can be NULL.
+        */
+    void (*shutdown)(asocket *s);
+
+        /* close is called by the peer when it has gone away.
+        ** we are not allowed to make any further calls on the
+        ** peer once our close method is called.
+        */
+    void (*close)(asocket *s);
+
+        /* A socket is bound to atransport */
+    atransport *transport;
+
+    size_t get_max_payload() const;
+};
+
+asocket *find_local_socket(unsigned local_id, unsigned remote_id);
+void install_local_socket(asocket *s);
+void remove_socket(asocket *s);
+void close_all_sockets(atransport *t);
+
+asocket *create_local_socket(int fd);
+asocket *create_local_service_socket(const char* destination,
+                                     const atransport* transport);
+
+asocket *create_remote_socket(unsigned id, atransport *t);
+void connect_to_remote(asocket *s, const char *destination);
+void connect_to_smartsocket(asocket *s);
+
+#endif  // __ADB_SOCKET_H
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
new file mode 100644 (file)
index 0000000..03cab64
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fdevent.h"
+
+#include <gtest/gtest.h>
+
+#include <limits>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include <pthread.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "adb.h"
+#include "adb_io.h"
+#include "socket.h"
+#include "sysdeps.h"
+
+static void signal_handler(int) {
+    ASSERT_EQ(1u, fdevent_installed_count());
+    pthread_exit(nullptr);
+}
+
+// On host, register a dummy socket, so fdevet_loop() will not abort when previously
+// registered local sockets are all closed. On device, fdevent_subproc_setup() installs
+// one fdevent which can be considered as dummy socket.
+static void InstallDummySocket() {
+#if ADB_HOST
+    int dummy_fds[2];
+    ASSERT_EQ(0, pipe(dummy_fds));
+    asocket* dummy_socket = create_local_socket(dummy_fds[0]);
+    ASSERT_TRUE(dummy_socket != nullptr);
+    dummy_socket->ready(dummy_socket);
+#endif
+}
+
+struct ThreadArg {
+    int first_read_fd;
+    int last_write_fd;
+    size_t middle_pipe_count;
+};
+
+static void FdEventThreadFunc(ThreadArg* arg) {
+    std::vector<int> read_fds;
+    std::vector<int> write_fds;
+
+    read_fds.push_back(arg->first_read_fd);
+    for (size_t i = 0; i < arg->middle_pipe_count; ++i) {
+        int fds[2];
+        ASSERT_EQ(0, adb_socketpair(fds));
+        read_fds.push_back(fds[0]);
+        write_fds.push_back(fds[1]);
+    }
+    write_fds.push_back(arg->last_write_fd);
+
+    for (size_t i = 0; i < read_fds.size(); ++i) {
+        asocket* reader = create_local_socket(read_fds[i]);
+        ASSERT_TRUE(reader != nullptr);
+        asocket* writer = create_local_socket(write_fds[i]);
+        ASSERT_TRUE(writer != nullptr);
+        reader->peer = writer;
+        writer->peer = reader;
+        reader->ready(reader);
+    }
+
+    InstallDummySocket();
+    fdevent_loop();
+}
+
+class LocalSocketTest : public ::testing::Test {
+  protected:
+    static void SetUpTestCase() {
+        ASSERT_NE(SIG_ERR, signal(SIGUSR1, signal_handler));
+        ASSERT_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN));
+    }
+
+    virtual void SetUp() {
+        fdevent_reset();
+        ASSERT_EQ(0u, fdevent_installed_count());
+    }
+};
+
+TEST_F(LocalSocketTest, smoke) {
+    const size_t PIPE_COUNT = 100;
+    const size_t MESSAGE_LOOP_COUNT = 100;
+    const std::string MESSAGE = "socket_test";
+    int fd_pair1[2];
+    int fd_pair2[2];
+    ASSERT_EQ(0, adb_socketpair(fd_pair1));
+    ASSERT_EQ(0, adb_socketpair(fd_pair2));
+    pthread_t thread;
+    ThreadArg thread_arg;
+    thread_arg.first_read_fd = fd_pair1[0];
+    thread_arg.last_write_fd = fd_pair2[1];
+    thread_arg.middle_pipe_count = PIPE_COUNT;
+    int writer = fd_pair1[1];
+    int reader = fd_pair2[0];
+
+    ASSERT_EQ(0, pthread_create(&thread, nullptr,
+                                reinterpret_cast<void* (*)(void*)>(FdEventThreadFunc),
+                                &thread_arg));
+
+    usleep(1000);
+    for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
+        std::string read_buffer = MESSAGE;
+        std::string write_buffer(MESSAGE.size(), 'a');
+        ASSERT_TRUE(WriteFdExactly(writer, read_buffer.c_str(), read_buffer.size()));
+        ASSERT_TRUE(ReadFdExactly(reader, &write_buffer[0], write_buffer.size()));
+        ASSERT_EQ(read_buffer, write_buffer);
+    }
+    ASSERT_EQ(0, adb_close(writer));
+    ASSERT_EQ(0, adb_close(reader));
+    // Wait until the local sockets are closed.
+    sleep(1);
+
+    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
+    ASSERT_EQ(0, pthread_join(thread, nullptr));
+}
+
+struct CloseWithPacketArg {
+    int socket_fd;
+    size_t bytes_written;
+    int cause_close_fd;
+};
+
+static void CloseWithPacketThreadFunc(CloseWithPacketArg* arg) {
+    asocket* s = create_local_socket(arg->socket_fd);
+    ASSERT_TRUE(s != nullptr);
+    arg->bytes_written = 0;
+    while (true) {
+        apacket* p = get_apacket();
+        p->len = sizeof(p->data);
+        arg->bytes_written += p->len;
+        int ret = s->enqueue(s, p);
+        if (ret == 1) {
+            // The writer has one packet waiting to send.
+            break;
+        }
+    }
+
+    asocket* cause_close_s = create_local_socket(arg->cause_close_fd);
+    ASSERT_TRUE(cause_close_s != nullptr);
+    cause_close_s->peer = s;
+    s->peer = cause_close_s;
+    cause_close_s->ready(cause_close_s);
+
+    InstallDummySocket();
+    fdevent_loop();
+}
+
+// This test checks if we can close local socket in the following situation:
+// The socket is closing but having some packets, so it is not closed. Then
+// some write error happens in the socket's file handler, e.g., the file
+// handler is closed.
+TEST_F(LocalSocketTest, close_socket_with_packet) {
+    int socket_fd[2];
+    ASSERT_EQ(0, adb_socketpair(socket_fd));
+    int cause_close_fd[2];
+    ASSERT_EQ(0, adb_socketpair(cause_close_fd));
+    CloseWithPacketArg arg;
+    arg.socket_fd = socket_fd[1];
+    arg.cause_close_fd = cause_close_fd[1];
+    pthread_t thread;
+    ASSERT_EQ(0, pthread_create(&thread, nullptr,
+                                reinterpret_cast<void* (*)(void*)>(CloseWithPacketThreadFunc),
+                                &arg));
+    // Wait until the fdevent_loop() starts.
+    sleep(1);
+    ASSERT_EQ(0, adb_close(cause_close_fd[0]));
+    sleep(1);
+    ASSERT_EQ(2u, fdevent_installed_count());
+    ASSERT_EQ(0, adb_close(socket_fd[0]));
+    // Wait until the socket is closed.
+    sleep(1);
+
+    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
+    ASSERT_EQ(0, pthread_join(thread, nullptr));
+}
+
+// This test checks if we can read packets from a closing local socket.
+TEST_F(LocalSocketTest, read_from_closing_socket) {
+    int socket_fd[2];
+    ASSERT_EQ(0, adb_socketpair(socket_fd));
+    int cause_close_fd[2];
+    ASSERT_EQ(0, adb_socketpair(cause_close_fd));
+    CloseWithPacketArg arg;
+    arg.socket_fd = socket_fd[1];
+    arg.cause_close_fd = cause_close_fd[1];
+
+    pthread_t thread;
+    ASSERT_EQ(0, pthread_create(&thread, nullptr,
+                                reinterpret_cast<void* (*)(void*)>(CloseWithPacketThreadFunc),
+                                &arg));
+    // Wait until the fdevent_loop() starts.
+    sleep(1);
+    ASSERT_EQ(0, adb_close(cause_close_fd[0]));
+    sleep(1);
+    ASSERT_EQ(2u, fdevent_installed_count());
+
+    // Verify if we can read successfully.
+    std::vector<char> buf(arg.bytes_written);
+    ASSERT_EQ(true, ReadFdExactly(socket_fd[0], buf.data(), buf.size()));
+    ASSERT_EQ(0, adb_close(socket_fd[0]));
+
+    // Wait until the socket is closed.
+    sleep(1);
+
+    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
+    ASSERT_EQ(0, pthread_join(thread, nullptr));
+}
+
+// This test checks if we can close local socket in the following situation:
+// The socket is not closed and has some packets. When it fails to write to
+// the socket's file handler because the other end is closed, we check if the
+// socket is closed.
+TEST_F(LocalSocketTest, write_error_when_having_packets) {
+    int socket_fd[2];
+    ASSERT_EQ(0, adb_socketpair(socket_fd));
+    int cause_close_fd[2];
+    ASSERT_EQ(0, adb_socketpair(cause_close_fd));
+    CloseWithPacketArg arg;
+    arg.socket_fd = socket_fd[1];
+    arg.cause_close_fd = cause_close_fd[1];
+
+    pthread_t thread;
+    ASSERT_EQ(0, pthread_create(&thread, nullptr,
+                                reinterpret_cast<void* (*)(void*)>(CloseWithPacketThreadFunc),
+                                &arg));
+    // Wait until the fdevent_loop() starts.
+    sleep(1);
+    ASSERT_EQ(3u, fdevent_installed_count());
+    ASSERT_EQ(0, adb_close(socket_fd[0]));
+
+    // Wait until the socket is closed.
+    sleep(1);
+
+    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
+    ASSERT_EQ(0, pthread_join(thread, nullptr));
+}
+
+#if defined(__linux__)
+
+static void ClientThreadFunc() {
+    std::string error;
+    int fd = network_loopback_client(5038, SOCK_STREAM, &error);
+    ASSERT_GE(fd, 0) << error;
+    sleep(2);
+    ASSERT_EQ(0, adb_close(fd));
+}
+
+struct CloseRdHupSocketArg {
+  int socket_fd;
+};
+
+static void CloseRdHupSocketThreadFunc(CloseRdHupSocketArg* arg) {
+  asocket* s = create_local_socket(arg->socket_fd);
+  ASSERT_TRUE(s != nullptr);
+
+  InstallDummySocket();
+  fdevent_loop();
+}
+
+// This test checks if we can close sockets in CLOSE_WAIT state.
+TEST_F(LocalSocketTest, close_socket_in_CLOSE_WAIT_state) {
+  std::string error;
+  int listen_fd = network_inaddr_any_server(5038, SOCK_STREAM, &error);
+  ASSERT_GE(listen_fd, 0);
+  pthread_t client_thread;
+  ASSERT_EQ(0, pthread_create(&client_thread, nullptr,
+                              reinterpret_cast<void* (*)(void*)>(ClientThreadFunc), nullptr));
+
+  struct sockaddr addr;
+  socklen_t alen;
+  alen = sizeof(addr);
+  int accept_fd = adb_socket_accept(listen_fd, &addr, &alen);
+  ASSERT_GE(accept_fd, 0);
+  CloseRdHupSocketArg arg;
+  arg.socket_fd = accept_fd;
+  pthread_t thread;
+  ASSERT_EQ(0, pthread_create(&thread, nullptr,
+                              reinterpret_cast<void* (*)(void*)>(CloseRdHupSocketThreadFunc),
+                              &arg));
+  // Wait until the fdevent_loop() starts.
+  sleep(1);
+  ASSERT_EQ(2u, fdevent_installed_count());
+  // Wait until the client closes its socket.
+  ASSERT_EQ(0, pthread_join(client_thread, nullptr));
+  sleep(2);
+  ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
+  ASSERT_EQ(0, pthread_join(thread, nullptr));
+}
+
+#endif  // defined(__linux__)
index 32ca17d..eb0ce85 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_SOCKETS
+#define TRACE_TAG SOCKETS
 
 #include "sysdeps.h"
 
@@ -25,6 +25,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <algorithm>
+
 #if !ADB_HOST
 #include "cutils/properties.h"
 #endif
@@ -133,7 +135,7 @@ restart:
 
 static int local_socket_enqueue(asocket *s, apacket *p)
 {
-    D("LS(%d): enqueue %d\n", s->id, p->len);
+    D("LS(%d): enqueue %d", s->id, p->len);
 
     p->ptr = p->data;
 
@@ -156,7 +158,9 @@ static int local_socket_enqueue(asocket *s, apacket *p)
             continue;
         }
         if((r == 0) || (errno != EAGAIN)) {
-            D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) );
+            D( "LS(%d): not ready, errno=%d: %s", s->id, errno, strerror(errno) );
+            put_apacket(p);
+            s->has_write_error = true;
             s->close(s);
             return 1; /* not ready (error) */
         } else {
@@ -204,7 +208,7 @@ static void local_socket_destroy(asocket  *s)
     apacket *p, *n;
     int exit_on_close = s->exit_on_close;
 
-    D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd);
+    D("LS(%d): destroying fde.fd=%d", s->id, s->fde.fd);
 
         /* IMPORTANT: the remove closes the fd
         ** that belongs to this socket
@@ -213,7 +217,7 @@ static void local_socket_destroy(asocket  *s)
 
         /* dispose of any unwritten data */
     for(p = s->pkt_first; p; p = n) {
-        D("LS(%d): discarding %d bytes\n", s->id, p->len);
+        D("LS(%d): discarding %d bytes", s->id, p->len);
         n = p->next;
         put_apacket(p);
     }
@@ -221,7 +225,7 @@ static void local_socket_destroy(asocket  *s)
     free(s);
 
     if (exit_on_close) {
-        D("local_socket_destroy: exiting\n");
+        D("local_socket_destroy: exiting");
         exit(1);
     }
 }
@@ -229,9 +233,9 @@ static void local_socket_destroy(asocket  *s)
 
 static void local_socket_close_locked(asocket *s)
 {
-    D("entered local_socket_close_locked. LS(%d) fd=%d\n", s->id, s->fd);
+    D("entered local_socket_close_locked. LS(%d) fd=%d", s->id, s->fd);
     if(s->peer) {
-        D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n",
+        D("LS(%d): closing peer. peer->id=%d peer->fd=%d",
           s->id, s->peer->id, s->peer->fd);
         /* Note: it's important to call shutdown before disconnecting from
          * the peer, this ensures that remote sockets can still get the id
@@ -252,27 +256,28 @@ static void local_socket_close_locked(asocket *s)
         /* If we are already closing, or if there are no
         ** pending packets, destroy immediately
         */
-    if (s->closing || s->pkt_first == NULL) {
+    if (s->closing || s->has_write_error || s->pkt_first == NULL) {
         int   id = s->id;
         local_socket_destroy(s);
-        D("LS(%d): closed\n", id);
+        D("LS(%d): closed", id);
         return;
     }
 
         /* otherwise, put on the closing list
         */
-    D("LS(%d): closing\n", s->id);
+    D("LS(%d): closing", s->id);
     s->closing = 1;
     fdevent_del(&s->fde, FDE_READ);
     remove_socket(s);
-    D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd);
+    D("LS(%d): put on socket_closing_list fd=%d", s->id, s->fd);
     insert_local_socket(s, &local_socket_closing_list);
+    CHECK_EQ(FDE_WRITE, s->fde.state & FDE_WRITE);
 }
 
 static void local_socket_event_func(int fd, unsigned ev, void* _s)
 {
     asocket* s = reinterpret_cast<asocket*>(_s);
-    D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev);
+    D("LS(%d): event_func(fd=%d(==%d), ev=%04x)", s->id, s->fd, fd, ev);
 
     /* put the FDE_WRITE processing before the FDE_READ
     ** in order to simplify the code.
@@ -295,7 +300,8 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s)
                     continue;
                 }
 
-                D(" closing after write because r=%d and errno is %d\n", r, errno);
+                D(" closing after write because r=%d and errno is %d", r, errno);
+                s->has_write_error = true;
                 s->close(s);
                 return;
             }
@@ -313,7 +319,7 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s)
         ** we can now destroy it.
         */
         if (s->closing) {
-            D(" closing because 'closing' is set after write\n");
+            D(" closing because 'closing' is set after write");
             s->close(s);
             return;
         }
@@ -330,13 +336,14 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s)
     if (ev & FDE_READ) {
         apacket *p = get_apacket();
         unsigned char *x = p->data;
-        size_t avail = MAX_PAYLOAD;
-        int r;
+        const size_t max_payload = s->get_max_payload();
+        size_t avail = max_payload;
+        int r = 0;
         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=%zu\n",
+            D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu",
               s->id, s->fd, r, r < 0 ? errno : 0, avail);
             if (r == -1) {
                 if (errno == EAGAIN) {
@@ -352,16 +359,19 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s)
             is_eof = 1;
             break;
         }
-        D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
+        D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d",
           s->id, s->fd, r, is_eof, s->fde.force_eof);
-        if ((avail == MAX_PAYLOAD) || (s->peer == 0)) {
+        if ((avail == max_payload) || (s->peer == 0)) {
             put_apacket(p);
         } else {
-            p->len = MAX_PAYLOAD - avail;
+            p->len = max_payload - avail;
 
+            // s->peer->enqueue() may call s->close() and free s,
+            // so save variables for debug printing below.
+            unsigned saved_id = s->id;
+            int saved_fd = s->fd;
             r = s->peer->enqueue(s->peer, p);
-            D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd,
-              r);
+            D("LS(%u): fd=%d post peer->enqueue(). r=%d", saved_id, saved_fd, r);
 
             if (r < 0) {
                     /* error return means they closed us as a side-effect
@@ -385,9 +395,10 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s)
         }
         /* 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",
+            D(" closing because is_eof=%d r=%d s->fde.force_eof=%d",
               is_eof, r, s->fde.force_eof);
             s->close(s);
+            return;
         }
     }
 
@@ -396,8 +407,7 @@ static void local_socket_event_func(int fd, unsigned ev, void* _s)
             ** catching it here means we may skip the last few
             ** bytes of readable data.
             */
-        D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd);
-
+        D("LS(%d): FDE_ERROR (fd=%d)", s->id, s->fd);
         return;
     }
 }
@@ -414,11 +424,12 @@ asocket *create_local_socket(int fd)
     install_local_socket(s);
 
     fdevent_install(&s->fde, fd, local_socket_event_func, s);
-    D("LS(%d): created (fd=%d)\n", s->id, s->fd);
+    D("LS(%d): created (fd=%d)", s->id, s->fd);
     return s;
 }
 
-asocket *create_local_service_socket(const char *name)
+asocket *create_local_service_socket(const char *name,
+                                     const atransport* transport)
 {
 #if !ADB_HOST
     if (!strcmp(name,"jdwp")) {
@@ -428,11 +439,11 @@ asocket *create_local_service_socket(const char *name)
         return create_jdwp_tracker_service_socket();
     }
 #endif
-    int fd = service_to_fd(name);
+    int fd = service_to_fd(name, transport);
     if(fd < 0) return 0;
 
     asocket* s = create_local_socket(fd);
-    D("LS(%d): bound to '%s' via %d\n", s->id, name, fd);
+    D("LS(%d): bound to '%s' via %d", s->id, name, fd);
 
 #if !ADB_HOST
     char debug[PROPERTY_VALUE_MAX];
@@ -443,7 +454,7 @@ asocket *create_local_service_socket(const char *name)
         || (!strncmp(name, "unroot:", 7) && getuid() == 0)
         || !strncmp(name, "usb:", 4)
         || !strncmp(name, "tcpip:", 6)) {
-        D("LS(%d): enabling exit_on_close\n", s->id);
+        D("LS(%d): enabling exit_on_close", s->id);
         s->exit_on_close = 1;
     }
 #endif
@@ -459,7 +470,7 @@ static asocket *create_host_service_socket(const char *name, const char* serial)
     s = host_service_to_socket(name, serial);
 
     if (s != NULL) {
-        D("LS(%d) bound to '%s'\n", s->id, name);
+        D("LS(%d) bound to '%s'", s->id, name);
         return s;
     }
 
@@ -467,17 +478,9 @@ static asocket *create_host_service_socket(const char *name, const char* serial)
 }
 #endif /* ADB_HOST */
 
-/* a Remote socket is used to send/receive data to/from a given transport object
-** it needs to be closed when the transport is forcibly destroyed by the user
-*/
-struct aremotesocket {
-    asocket      socket;
-    adisconnect  disconnect;
-};
-
 static int remote_socket_enqueue(asocket *s, apacket *p)
 {
-    D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
+    D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d",
       s->id, s->fd, s->peer->fd);
     p->msg.command = A_WRTE;
     p->msg.arg0 = s->peer->id;
@@ -489,7 +492,7 @@ static int remote_socket_enqueue(asocket *s, apacket *p)
 
 static void remote_socket_ready(asocket *s)
 {
-    D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n",
+    D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d",
       s->id, s->fd, s->peer->fd);
     apacket *p = get_apacket();
     p->msg.command = A_OKAY;
@@ -500,7 +503,7 @@ static void remote_socket_ready(asocket *s)
 
 static void remote_socket_shutdown(asocket *s)
 {
-    D("entered remote_socket_shutdown RS(%d) CLOSE fd=%d peer->fd=%d\n",
+    D("entered remote_socket_shutdown RS(%d) CLOSE fd=%d peer->fd=%d",
       s->id, s->fd, s->peer?s->peer->fd:-1);
     apacket *p = get_apacket();
     p->msg.command = A_CLSE;
@@ -515,40 +518,24 @@ static void remote_socket_close(asocket *s)
 {
     if (s->peer) {
         s->peer->peer = 0;
-        D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n",
+        D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d",
           s->id, s->peer->id, s->peer->fd);
         s->peer->close(s->peer);
     }
-    D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
+    D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d",
       s->id, s->fd, s->peer?s->peer->fd:-1);
-    D("RS(%d): closed\n", s->id);
-    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
+    D("RS(%d): closed", s->id);
     free(s);
 }
 
-static void remote_socket_disconnect(void*  _s, atransport*  t)
-{
-    asocket* s = reinterpret_cast<asocket*>(_s);
-    asocket* peer = s->peer;
-
-    D("remote_socket_disconnect RS(%d)\n", s->id);
-    if (peer) {
-        peer->peer = NULL;
-        peer->close(peer);
-    }
-    remove_transport_disconnect( s->transport, &((aremotesocket*)s)->disconnect );
-    free(s);
-}
-
-/* Create an asocket to exchange packets with a remote service through transport
-  |t|. Where |id| is the socket id of the corresponding service on the other
-   side of the transport (it is allocated by the remote side and _cannot_ be 0).
-   Returns a new non-NULL asocket handle. */
+// Create a remote socket to exchange packets with a remote service through transport
+// |t|. Where |id| is the socket id of the corresponding service on the other
+//  side of the transport (it is allocated by the remote side and _cannot_ be 0).
+// Returns a new non-NULL asocket handle.
 asocket *create_remote_socket(unsigned id, atransport *t)
 {
     if (id == 0) fatal("invalid remote socket id (0)");
-    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(aremotesocket)));
-    adisconnect* dis = &reinterpret_cast<aremotesocket*>(s)->disconnect;
+    asocket* s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
 
     if (s == NULL) fatal("cannot allocate socket");
     s->id = id;
@@ -558,24 +545,21 @@ asocket *create_remote_socket(unsigned id, atransport *t)
     s->close = remote_socket_close;
     s->transport = t;
 
-    dis->func   = remote_socket_disconnect;
-    dis->opaque = s;
-    add_transport_disconnect( t, dis );
-    D("RS(%d): created\n", s->id);
+    D("RS(%d): created", s->id);
     return s;
 }
 
 void connect_to_remote(asocket *s, const char *destination)
 {
-    D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd);
+    D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
     apacket *p = get_apacket();
-    int len = strlen(destination) + 1;
+    size_t len = strlen(destination) + 1;
 
-    if(len > (MAX_PAYLOAD-1)) {
+    if(len > (s->get_max_payload()-1)) {
         fatal("destination oversized");
     }
 
-    D("LS(%d): connect('%s')\n", s->id, destination);
+    D("LS(%d): connect('%s')", s->id, destination);
     p->msg.command = A_OPEN;
     p->msg.arg0 = s->id;
     p->msg.data_length = len;
@@ -689,19 +673,19 @@ static int smart_socket_enqueue(asocket *s, apacket *p)
 {
     unsigned len;
 #if ADB_HOST
-    char *service = NULL;
-    char* serial = NULL;
-    transport_type ttype = kTransportAny;
+    char *service = nullptr;
+    char* serial = nullptr;
+    TransportType type = kTransportAny;
 #endif
 
-    D("SS(%d): enqueue %d\n", s->id, p->len);
+    D("SS(%d): enqueue %d", 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);
+        if((s->pkt_first->len + p->len) > s->get_max_payload()) {
+            D("SS(%d): overflow", s->id);
             put_apacket(p);
             goto fail;
         }
@@ -719,20 +703,20 @@ static int smart_socket_enqueue(asocket *s, apacket *p)
 
     len = unhex(p->data, 4);
     if((len < 1) ||  (len > 1024)) {
-        D("SS(%d): bad size (%d)\n", s->id, len);
+        D("SS(%d): bad size (%d)", s->id, len);
         goto fail;
     }
 
-    D("SS(%d): len is %d\n", s->id, len );
+    D("SS(%d): len is %d", s->id, len );
         /* can't do anything until we have the full header */
     if((len + 4) > p->len) {
-        D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len);
+        D("SS(%d): waiting for %d more bytes", s->id, len+4 - p->len);
         return 0;
     }
 
     p->data[len + 4] = 0;
 
-    D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4));
+    D("SS(%d): '%s'", s->id, (char*) (p->data + 4));
 
 #if ADB_HOST
     service = (char *)p->data + 4;
@@ -748,16 +732,16 @@ static int smart_socket_enqueue(asocket *s, apacket *p)
             service = serial_end + 1;
         }
     } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
-        ttype = kTransportUsb;
+        type = kTransportUsb;
         service += strlen("host-usb:");
     } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
-        ttype = kTransportLocal;
+        type = kTransportLocal;
         service += strlen("host-local:");
     } else if (!strncmp(service, "host:", strlen("host:"))) {
-        ttype = kTransportAny;
+        type = kTransportAny;
         service += strlen("host:");
     } else {
-        service = NULL;
+        service = nullptr;
     }
 
     if (service) {
@@ -768,13 +752,13 @@ static int smart_socket_enqueue(asocket *s, apacket *p)
             ** 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) {
+        if(handle_host_request(service, type, serial, s->peer->fd, s) == 0) {
                 /* XXX fail message? */
-            D( "SS(%d): handled host service '%s'\n", s->id, service );
+            D( "SS(%d): handled host service '%s'", s->id, service );
             goto fail;
         }
         if (!strncmp(service, "transport", strlen("transport"))) {
-            D( "SS(%d): okay transport\n", s->id );
+            D( "SS(%d): okay transport", s->id );
             p->len = 0;
             return 0;
         }
@@ -785,7 +769,7 @@ static int smart_socket_enqueue(asocket *s, apacket *p)
             */
         s2 = create_host_service_socket(service, serial);
         if(s2 == 0) {
-            D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
+            D( "SS(%d): couldn't create host service '%s'", s->id, service );
             SendFail(s->peer->fd, "unknown host service");
             goto fail;
         }
@@ -800,12 +784,12 @@ static int smart_socket_enqueue(asocket *s, apacket *p)
         SendOkay(s->peer->fd);
 
         s->peer->ready = local_socket_ready;
-        s->peer->shutdown = NULL;
+        s->peer->shutdown = nullptr;
         s->peer->close = local_socket_close;
         s->peer->peer = s2;
         s2->peer = s->peer;
         s->peer = 0;
-        D( "SS(%d): okay\n", s->id );
+        D( "SS(%d): okay", s->id );
         s->close(s);
 
             /* initial state is "ready" */
@@ -813,18 +797,17 @@ static int smart_socket_enqueue(asocket *s, apacket *p)
         return 0;
     }
 #else /* !ADB_HOST */
-    if (s->transport == NULL) {
+    if (s->transport == nullptr) {
         std::string error_msg = "unknown failure";
-        s->transport = acquire_one_transport(CS_ANY, kTransportAny, NULL, &error_msg);
-
-        if (s->transport == NULL) {
+        s->transport = acquire_one_transport(kTransportAny, nullptr, nullptr, &error_msg);
+        if (s->transport == nullptr) {
             SendFail(s->peer->fd, error_msg);
             goto fail;
         }
     }
 #endif
 
-    if(!(s->transport) || (s->transport->connection_state == CS_OFFLINE)) {
+    if(!(s->transport) || (s->transport->connection_state == kCsOffline)) {
            /* if there's no remote we fail the connection
             ** right here and terminate it
             */
@@ -839,7 +822,7 @@ static int smart_socket_enqueue(asocket *s, apacket *p)
         ** tear down
         */
     s->peer->ready = local_socket_ready_notify;
-    s->peer->shutdown = NULL;
+    s->peer->shutdown = nullptr;
     s->peer->close = local_socket_close_notify;
     s->peer->peer = 0;
         /* give him our transport and upref it */
@@ -861,12 +844,12 @@ fail:
 
 static void smart_socket_ready(asocket *s)
 {
-    D("SS(%d): ready\n", s->id);
+    D("SS(%d): ready", s->id);
 }
 
 static void smart_socket_close(asocket *s)
 {
-    D("SS(%d): closed\n", s->id);
+    D("SS(%d): closed", s->id);
     if(s->pkt_first){
         put_apacket(s->pkt_first);
     }
@@ -880,7 +863,7 @@ static void smart_socket_close(asocket *s)
 
 static asocket *create_smart_socket(void)
 {
-    D("Creating smart socket \n");
+    D("Creating smart socket");
     asocket *s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
     if (s == NULL) fatal("cannot allocate socket");
     s->enqueue = smart_socket_enqueue;
@@ -888,15 +871,26 @@ static asocket *create_smart_socket(void)
     s->shutdown = NULL;
     s->close = smart_socket_close;
 
-    D("SS(%d)\n", s->id);
+    D("SS(%d)", s->id);
     return s;
 }
 
 void connect_to_smartsocket(asocket *s)
 {
-    D("Connecting to smart socket \n");
+    D("Connecting to smart socket");
     asocket *ss = create_smart_socket();
     s->peer = ss;
     ss->peer = s;
     s->ready(s);
 }
+
+size_t asocket::get_max_payload() const {
+    size_t max_payload = MAX_PAYLOAD;
+    if (transport) {
+        max_payload = std::min(max_payload, transport->get_max_payload());
+    }
+    if (peer && peer->transport) {
+        max_payload = std::min(max_payload, peer->transport->get_max_payload());
+    }
+    return max_payload;
+}
index 59e5b0b..cba66fc 100644 (file)
 #  undef _WIN32
 #endif
 
+#include <errno.h>
+
+#include <string>
+
+// Include this before open/unlink are defined as macros below.
+#include <base/utf8.h>
+
 /*
  * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
  * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
     _rc; })
 #endif
 
+// Some printf-like functions are implemented in terms of
+// android::base::StringAppendV, so they should use the same attribute for
+// compile-time format string checking. On Windows, if the mingw version of
+// vsnprintf is used in StringAppendV, use `gnu_printf' which allows z in %zd
+// and PRIu64 (and related) to be recognized by the compile-time checking.
+#define ADB_FORMAT_ARCHETYPE __printf__
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+#undef ADB_FORMAT_ARCHETYPE
+#define ADB_FORMAT_ARCHETYPE gnu_printf
+#endif
+#endif
+
 #ifdef _WIN32
 
 #include <ctype.h>
 #include <direct.h>
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <io.h>
 #include <process.h>
 #include <sys/stat.h>
+#include <utime.h>
 #include <winsock2.h>
 #include <windows.h>
 #include <ws2tcpip.h>
 
+#include <memory>   // unique_ptr
+#include <string>
+
 #include "fdevent.h"
 
+#define OS_PATH_SEPARATORS "\\/"
 #define OS_PATH_SEPARATOR '\\'
 #define OS_PATH_SEPARATOR_STR "\\"
 #define ENV_PATH_SEPARATOR_STR ";"
 
+static __inline__ bool adb_is_separator(char c) {
+    return c == '\\' || c == '/';
+}
+
 typedef CRITICAL_SECTION          adb_mutex_t;
 
 #define  ADB_MUTEX_DEFINE(x)     adb_mutex_t   x
@@ -79,18 +109,19 @@ 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;
-    }
+static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg) {
+    uintptr_t tid = _beginthread((win_thread_func_t)func, 0, arg);
+    return (tid != static_cast<uintptr_t>(-1L));
+}
+
+static __inline__ int adb_thread_setname(const std::string& name) {
+    // TODO: See https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for how to set
+    // the thread name in Windows. Unfortunately, it only works during debugging, but
+    // our build process doesn't generate PDB files needed for debugging.
     return 0;
 }
 
@@ -108,29 +139,15 @@ static __inline__ void  close_on_exec(int  fd)
 
 #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;
-}
+extern int  adb_unlink(const char*  path);
 #undef  unlink
 #define unlink  ___xxx_unlink
 
-static __inline__ int  adb_mkdir(const char*  path, int mode)
-{
-       return _mkdir(path);
-}
+extern int adb_mkdir(const std::string& path, int mode);
 #undef   mkdir
 #define  mkdir  ___xxx_mkdir
 
+// See the comments for the !defined(_WIN32) versions of adb_*().
 extern int  adb_open(const char*  path, int  options);
 extern int  adb_creat(const char*  path, int  mode);
 extern int  adb_read(int  fd, void* buf, int len);
@@ -139,6 +156,7 @@ extern int  adb_lseek(int  fd, int  pos, int  where);
 extern int  adb_shutdown(int  fd);
 extern int  adb_close(int  fd);
 
+// See the comments for the !defined(_WIN32) version of unix_close().
 static __inline__ int  unix_close(int fd)
 {
     return close(fd);
@@ -146,11 +164,13 @@ static __inline__ int  unix_close(int fd)
 #undef   close
 #define  close   ____xxx_close
 
+// See the comments for the !defined(_WIN32) version of unix_read().
 extern int  unix_read(int  fd, void*  buf, size_t  len);
 
 #undef   read
 #define  read  ___xxx_read
 
+// See the comments for the !defined(_WIN32) version of unix_write().
 static __inline__  int  unix_write(int  fd, const void*  buf, size_t  len)
 {
     return write(fd, buf, len);
@@ -158,41 +178,31 @@ static __inline__  int  unix_write(int  fd, const void*  buf, size_t  len)
 #undef   write
 #define  write  ___xxx_write
 
+// See the comments for the !defined(_WIN32) version of adb_open_mode().
 static __inline__ int  adb_open_mode(const char* path, int options, int mode)
 {
     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);
-    }
-}
+// See the comments for the !defined(_WIN32) version of unix_open().
+extern int unix_open(const char* path, int options, ...);
 #define  open    ___xxx_unix_open
 
+// Checks if |fd| corresponds to a console.
+// Standard Windows isatty() returns 1 for both console FDs and character
+// devices like NUL. unix_isatty() performs some extra checking to only match
+// console FDs.
+// |fd| must be a real file descriptor, meaning STDxx_FILENO or unix_open() FDs
+// will work but adb_open() FDs will not. Additionally the OS handle associated
+// with |fd| must have GENERIC_READ access (which console FDs have by default).
+// Returns 1 if |fd| is a console FD, 0 otherwise. The value of errno after
+// calling this function is unreliable and should not be used.
+int unix_isatty(int fd);
+#define  isatty  ___xxx_isatty
 
 /* normally provided by <cutils/misc.h> */
 extern void*  load_file(const char*  pathname, unsigned*  psize);
 
-/* normally provided by <cutils/sockets.h> */
-extern int socket_loopback_client(int port, int type);
-extern int socket_network_client(const char *host, int port, int type);
-extern int socket_network_client_timeout(const char *host, int port, int type,
-                                         int timeout);
-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
@@ -216,6 +226,12 @@ static __inline__ void  adb_sleep_ms( int  mseconds )
     Sleep( mseconds );
 }
 
+int network_loopback_client(int port, int type, std::string* error);
+int network_loopback_server(int port, int type, std::string* error);
+int network_inaddr_any_server(int port, int type, std::string* error);
+int network_connect(const std::string& host, int port, int type, int timeout,
+                    std::string* error);
+
 extern int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen);
 
 #undef   accept
@@ -240,42 +256,148 @@ static __inline__ void  disable_tcp_nagle( int  fd )
 
 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__ int adb_is_absolute_host_path(const char* path) {
+    return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
 }
 
-static __inline__  char*  adb_dirstop( const char*  path )
-{
-    char*  p  = strrchr(path, '/');
-    char*  p2 = strrchr(path, '\\');
+// Like strerror(), but for Win32 error codes.
+std::string SystemErrorCodeToString(DWORD error_code);
+
+// We later define a macro mapping 'stat' to 'adb_stat'. This causes:
+//   struct stat s;
+//   stat(filename, &s);
+// To turn into the following:
+//   struct adb_stat s;
+//   adb_stat(filename, &s);
+// To get this to work, we need to make 'struct adb_stat' the same as
+// 'struct stat'. Note that this definition of 'struct adb_stat' uses the
+// *current* macro definition of stat, so it may actually be inheriting from
+// struct _stat32i64 (or some other remapping).
+struct adb_stat : public stat {};
+
+static_assert(sizeof(struct adb_stat) == sizeof(struct stat),
+    "structures should be the same");
+
+extern int adb_stat(const char* f, struct adb_stat* s);
+
+// stat is already a macro, undefine it so we can redefine it.
+#undef stat
+#define stat adb_stat
+
+// UTF-8 versions of POSIX APIs.
+extern DIR* adb_opendir(const char* dirname);
+extern struct dirent* adb_readdir(DIR* dir);
+extern int adb_closedir(DIR* dir);
+
+extern int adb_utime(const char *, struct utimbuf *);
+extern int adb_chmod(const char *, int);
+
+extern int adb_vfprintf(FILE *stream, const char *format, va_list ap)
+    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 0)));
+extern int adb_fprintf(FILE *stream, const char *format, ...)
+    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3)));
+extern int adb_printf(const char *format, ...)
+    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 1, 2)));
+
+extern int adb_fputs(const char* buf, FILE* stream);
+extern int adb_fputc(int ch, FILE* stream);
+extern size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb,
+                         FILE* stream);
+
+extern FILE* adb_fopen(const char* f, const char* m);
+
+extern char* adb_getenv(const char* name);
+
+extern char* adb_getcwd(char* buf, int size);
+
+// Remap calls to POSIX APIs to our UTF-8 versions.
+#define opendir adb_opendir
+#define readdir adb_readdir
+#define closedir adb_closedir
+#define rewinddir rewinddir_utf8_not_yet_implemented
+#define telldir telldir_utf8_not_yet_implemented
+// Some compiler's C++ headers have members named seekdir, so we can't do the
+// macro technique and instead cause a link error if seekdir is called.
+inline void seekdir(DIR*, long) {
+    extern int seekdir_utf8_not_yet_implemented;
+    seekdir_utf8_not_yet_implemented = 1;
+}
+
+#define utime adb_utime
+#define chmod adb_chmod
+
+#define vfprintf adb_vfprintf
+#define fprintf adb_fprintf
+#define printf adb_printf
+#define fputs adb_fputs
+#define fputc adb_fputc
+#define fwrite adb_fwrite
+
+#define fopen adb_fopen
+
+#define getenv adb_getenv
+#define putenv putenv_utf8_not_yet_implemented
+#define setenv setenv_utf8_not_yet_implemented
+#define unsetenv unsetenv_utf8_not_yet_implemented
+
+#define getcwd adb_getcwd
+
+char* adb_strerror(int err);
+#define strerror adb_strerror
+
+// Helper class to convert UTF-16 argv from wmain() to UTF-8 args that can be
+// passed to main().
+class NarrowArgs {
+public:
+    NarrowArgs(int argc, wchar_t** argv);
+    ~NarrowArgs();
+
+    inline char** data() {
+        return narrow_args;
+    }
 
-    if ( !p )
-        p = p2;
-    else if ( p2 && p2 > p )
-        p = p2;
+private:
+    char** narrow_args;
+};
 
-    return p;
+// Windows HANDLE values only use 32-bits of the type, even on 64-bit machines,
+// so they can fit in an int. To convert back, we just need to sign-extend.
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
+// Note that this does not make a HANDLE value work with APIs like open(), nor
+// does this make a value from open() passable to APIs taking a HANDLE. This
+// just lets you take a HANDLE, pass it around as an int, and then use it again
+// as a HANDLE.
+inline int cast_handle_to_int(const HANDLE h) {
+    // truncate
+    return static_cast<int>(reinterpret_cast<INT_PTR>(h));
 }
 
-static __inline__  int  adb_is_absolute_host_path( const char*  path )
-{
-    return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
+inline HANDLE cast_int_to_handle(const int fd) {
+    // sign-extend
+    return reinterpret_cast<HANDLE>(static_cast<INT_PTR>(fd));
 }
 
+// Deleter for unique_handle. Adapted from many sources, including:
+// http://stackoverflow.com/questions/14841396/stdunique-ptr-deleters-and-the-win32-api
+// https://visualstudiomagazine.com/articles/2013/09/01/get-a-handle-on-the-windows-api.aspx
+class handle_deleter {
+public:
+    typedef HANDLE pointer;
+
+    void operator()(HANDLE h);
+};
+
+// Like std::unique_ptr, but for Windows HANDLE objects that should be
+// CloseHandle()'d. Operator bool() only checks if the handle != nullptr,
+// but does not check if the handle != INVALID_HANDLE_VALUE.
+typedef std::unique_ptr<HANDLE, handle_deleter> unique_handle;
+
 #else /* !_WIN32 a.k.a. Unix */
 
 #include "fdevent.h"
-#include <cutils/sockets.h>
 #include <cutils/misc.h>
+#include <cutils/sockets.h>
+#include <cutils/threads.h>
 #include <signal.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
@@ -285,15 +407,23 @@ static __inline__  int  adb_is_absolute_host_path( const char*  path )
 #include <unistd.h>
 #include <fcntl.h>
 #include <stdarg.h>
+#include <netdb.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <string.h>
 #include <unistd.h>
 
+#include <string>
+
+#define OS_PATH_SEPARATORS "/"
 #define OS_PATH_SEPARATOR '/'
 #define OS_PATH_SEPARATOR_STR "/"
 #define ENV_PATH_SEPARATOR_STR ":"
 
+static __inline__ bool adb_is_separator(char c) {
+    return c == '/';
+}
+
 typedef  pthread_mutex_t          adb_mutex_t;
 
 #define  ADB_MUTEX_INITIALIZER    PTHREAD_MUTEX_INITIALIZER
@@ -320,6 +450,15 @@ static __inline__ void  close_on_exec(int  fd)
     fcntl( fd, F_SETFD, FD_CLOEXEC );
 }
 
+// Open a file and return a file descriptor that may be used with unix_read(),
+// unix_write(), unix_close(), but not adb_read(), adb_write(), adb_close().
+//
+// On Unix, this is based on open(), so the file descriptor is a real OS file
+// descriptor, but the Windows implementation (in sysdeps_win32.cpp) returns a
+// file descriptor that can only be used with C Runtime APIs (which are wrapped
+// by unix_read(), unix_write(), unix_close()). Also, the C Runtime has
+// configurable CR/LF translation which defaults to text mode, but is settable
+// with _setmode().
 static __inline__ int  unix_open(const char*  path, int options,...)
 {
     if ((options & O_CREAT) == 0)
@@ -337,12 +476,21 @@ static __inline__ int  unix_open(const char*  path, int options,...)
     }
 }
 
+// Similar to the two-argument adb_open(), but takes a mode parameter for file
+// creation. See adb_open() for more info.
 static __inline__ int  adb_open_mode( const char*  pathname, int  options, int  mode )
 {
     return TEMP_FAILURE_RETRY( open( pathname, options, mode ) );
 }
 
 
+// Open a file and return a file descriptor that may be used with adb_read(),
+// adb_write(), adb_close(), but not unix_read(), unix_write(), unix_close().
+//
+// On Unix, this is based on open(), but the Windows implementation (in
+// sysdeps_win32.cpp) uses Windows native file I/O and bypasses the C Runtime
+// and its CR/LF translation. The returned file descriptor should be used with
+// adb_read(), adb_write(), adb_close(), etc.
 static __inline__ int  adb_open( const char*  pathname, int  options )
 {
     int  fd = TEMP_FAILURE_RETRY( open( pathname, options ) );
@@ -358,9 +506,16 @@ static __inline__ int  adb_shutdown(int fd)
 {
     return shutdown(fd, SHUT_RDWR);
 }
+static __inline__ int  adb_shutdown(int fd, int direction)
+{
+    return shutdown(fd, direction);
+}
 #undef   shutdown
 #define  shutdown   ____xxx_shutdown
 
+// Closes a file descriptor that came from adb_open() or adb_open_mode(), but
+// not designed to take a file descriptor from unix_open(). See the comments
+// for adb_open() for more info.
 static __inline__ int  adb_close(int fd)
 {
     return close(fd);
@@ -411,6 +566,53 @@ static __inline__  int  adb_creat(const char*  path, int  mode)
 #undef   creat
 #define  creat  ___xxx_creat
 
+static __inline__ int unix_isatty(int fd) {
+    return isatty(fd);
+}
+#define  isatty  ___xxx_isatty
+
+// Helper for network_* functions.
+inline int _fd_set_error_str(int fd, std::string* error) {
+  if (fd == -1) {
+    *error = strerror(errno);
+  }
+  return fd;
+}
+
+inline int network_loopback_client(int port, int type, std::string* error) {
+  return _fd_set_error_str(socket_loopback_client(port, type), error);
+}
+
+inline int network_loopback_server(int port, int type, std::string* error) {
+  return _fd_set_error_str(socket_loopback_server(port, type), error);
+}
+
+inline int network_inaddr_any_server(int port, int type, std::string* error) {
+  return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
+}
+
+inline int network_local_server(const char *name, int namespace_id, int type,
+                                std::string* error) {
+  return _fd_set_error_str(socket_local_server(name, namespace_id, type),
+                           error);
+}
+
+inline int network_connect(const std::string& host, int port, int type,
+                           int timeout, std::string* error) {
+  int getaddrinfo_error = 0;
+  int fd = socket_network_client_timeout(host.c_str(), port, type, timeout,
+                                         &getaddrinfo_error);
+  if (fd != -1) {
+    return fd;
+  }
+  if (getaddrinfo_error != 0) {
+    *error = gai_strerror(getaddrinfo_error);
+  } else {
+    *error = strerror(errno);
+  }
+  return -1;
+}
+
 static __inline__ int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
 {
     int fd;
@@ -425,25 +627,50 @@ static __inline__ int  adb_socket_accept(int  serverfd, struct sockaddr*  addr,
 #undef   accept
 #define  accept  ___xxx_accept
 
+// Operate on a file descriptor returned from unix_open() or a well-known file
+// descriptor such as STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO.
+//
+// On Unix, unix_read(), unix_write(), unix_close() map to adb_read(),
+// adb_write(), adb_close() (which all map to Unix system calls), but the
+// Windows implementations (in the ifdef above and in sysdeps_win32.cpp) call
+// into the C Runtime and its configurable CR/LF translation (which is settable
+// via _setmode()).
 #define  unix_read   adb_read
 #define  unix_write  adb_write
 #define  unix_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);
+static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg) {
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+    pthread_t thread;
+    errno = pthread_create(&thread, &attr, start, arg);
+    return (errno == 0);
+}
+
+static __inline__ int adb_thread_setname(const std::string& name) {
+#ifdef __APPLE__
+    return pthread_setname_np(name.c_str());
+#else
+    const char *s = name.c_str();
+
+    // pthread_setname_np fails rather than truncating long strings.
+    const int max_task_comm_len = 16; // including the null terminator
+    if (name.length() > (max_task_comm_len - 1)) {
+        char buf[max_task_comm_len];
+        strncpy(buf, name.c_str(), sizeof(buf) - 1);
+        buf[sizeof(buf) - 1] = '\0';
+        s = buf;
+    }
 
-    return pthread_create( pthread, &attr, start, arg );
+    return pthread_setname_np(pthread_self(), s) ;
+#endif
 }
 
-static __inline__  int  adb_socket_setbufsize( int   fd, int  bufsize )
+static __inline__  int  adb_socket_setbufsize(int fd, int  bufsize )
 {
     int opt = bufsize;
     return setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
@@ -489,10 +716,11 @@ static __inline__ void  adb_sleep_ms( int  mseconds )
     usleep( mseconds*1000 );
 }
 
-static __inline__ int  adb_mkdir(const char*  path, int mode)
+static __inline__ int  adb_mkdir(const std::string& path, int mode)
 {
-    return mkdir(path, mode);
+    return mkdir(path.c_str(), mode);
 }
+
 #undef   mkdir
 #define  mkdir  ___xxx_mkdir
 
@@ -500,24 +728,13 @@ 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 )
-{
+static __inline__ int adb_is_absolute_host_path(const char* path) {
     return path[0] == '/';
 }
 
 static __inline__ unsigned long adb_thread_id()
 {
-    return (unsigned long)pthread_self();
+    return (unsigned long)gettid();
 }
 
 #endif /* !_WIN32 */
index a21272f..81dcb41 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_SYSDEPS
+#define TRACE_TAG SYSDEPS
 
 #include "sysdeps.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include <cutils/sockets.h>
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+#include <base/strings.h>
+#include <base/utf8.h>
+
 #include "adb.h"
 
 extern void fatal(const char *fmt, ...);
@@ -78,6 +90,52 @@ static const FHClassRec _fh_socket_class = {
 
 #define assert(cond)  do { if (!(cond)) fatal( "assertion failed '%s' on %s:%ld\n", #cond, __FILE__, __LINE__ ); } while (0)
 
+std::string SystemErrorCodeToString(const DWORD error_code) {
+  const int kErrorMessageBufferSize = 256;
+  WCHAR msgbuf[kErrorMessageBufferSize];
+  DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+  DWORD len = FormatMessageW(flags, nullptr, error_code, 0, msgbuf,
+                             arraysize(msgbuf), nullptr);
+  if (len == 0) {
+    return android::base::StringPrintf(
+        "Error (%lu) while retrieving error. (%lu)", GetLastError(),
+        error_code);
+  }
+
+  // Convert UTF-16 to UTF-8.
+  std::string msg;
+  if (!android::base::WideToUTF8(msgbuf, &msg)) {
+      return android::base::StringPrintf(
+          "Error (%d) converting from UTF-16 to UTF-8 while retrieving error. (%lu)", errno,
+          error_code);
+  }
+
+  // Messages returned by the system end with line breaks.
+  msg = android::base::Trim(msg);
+  // There are many Windows error messages compared to POSIX, so include the
+  // numeric error code for easier, quicker, accurate identification. Use
+  // decimal instead of hex because there are decimal ranges like 10000-11999
+  // for Winsock.
+  android::base::StringAppendF(&msg, " (%lu)", error_code);
+  return msg;
+}
+
+void handle_deleter::operator()(HANDLE h) {
+    // CreateFile() is documented to return INVALID_HANDLE_FILE on error,
+    // implying that NULL is a valid handle, but this is probably impossible.
+    // Other APIs like CreateEvent() are documented to return NULL on error,
+    // implying that INVALID_HANDLE_VALUE is a valid handle, but this is also
+    // probably impossible. Thus, consider both NULL and INVALID_HANDLE_VALUE
+    // as invalid handles. std::unique_ptr won't call a deleter with NULL, so we
+    // only need to check for INVALID_HANDLE_VALUE.
+    if (h != INVALID_HANDLE_VALUE) {
+        if (!CloseHandle(h)) {
+            D("CloseHandle(%p) failed: %s", h,
+              SystemErrorCodeToString(GetLastError()).c_str());
+        }
+    }
+}
+
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -92,13 +150,17 @@ void *load_file(const char *fn, unsigned *_sz)
     char     *data;
     DWORD     file_size;
 
-    file = CreateFile( fn,
-                       GENERIC_READ,
-                       FILE_SHARE_READ,
-                       NULL,
-                       OPEN_EXISTING,
-                       0,
-                       NULL );
+    std::wstring fn_wide;
+    if (!android::base::UTF8ToWide(fn, &fn_wide))
+        return NULL;
+
+    file = CreateFileW( fn_wide.c_str(),
+                        GENERIC_READ,
+                        FILE_SHARE_READ,
+                        NULL,
+                        OPEN_EXISTING,
+                        0,
+                        NULL );
 
     if (file == INVALID_HANDLE_VALUE)
         return NULL;
@@ -109,7 +171,7 @@ void *load_file(const char *fn, unsigned *_sz)
     if (file_size > 0) {
         data = (char*) malloc( file_size + 1 );
         if (data == NULL) {
-            D("load_file: could not allocate %ld bytes\n", file_size );
+            D("load_file: could not allocate %ld bytes", file_size );
             file_size = 0;
         } else {
             DWORD  out_bytes;
@@ -117,7 +179,7 @@ void *load_file(const char *fn, unsigned *_sz)
             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);
+                D("load_file: could not read %ld bytes from '%s'", file_size, fn);
                 free(data);
                 data      = NULL;
                 file_size = 0;
@@ -169,17 +231,18 @@ typedef struct FHRec_
 
 static adb_mutex_t   _win32_lock;
 static  FHRec        _win32_fhs[ WIN32_MAX_FHS ];
-static  int          _win32_fh_count;
+static  int          _win32_fh_next;  // where to start search for free FHRec
 
 static FH
-_fh_from_int( int   fd )
+_fh_from_int( int   fd, const char*   func )
 {
     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 );
+    if (fd < 0 || fd >= WIN32_MAX_FHS) {
+        D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
+           func );
         errno = EBADF;
         return NULL;
     }
@@ -187,7 +250,8 @@ _fh_from_int( int   fd )
     f = &_win32_fhs[fd];
 
     if (f->used == 0) {
-        D( "_fh_from_int: invalid fd %d\n", fd + WIN32_FH_BASE );
+        D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
+           func );
         errno = EBADF;
         return NULL;
     }
@@ -208,28 +272,32 @@ _fh_to_int( FH  f )
 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];
+    // Search entire array, starting from _win32_fh_next.
+    for (int nn = 0; nn < WIN32_MAX_FHS; nn++) {
+        // Keep incrementing _win32_fh_next to avoid giving out an index that
+        // was recently closed, to try to avoid use-after-free.
+        const int index = _win32_fh_next++;
+        // Handle wrap-around of _win32_fh_next.
+        if (_win32_fh_next == WIN32_MAX_FHS) {
+            _win32_fh_next = 0;
+        }
+        if (_win32_fhs[index].clazz == NULL) {
+            f = &_win32_fhs[index];
             goto Exit;
         }
     }
-    D( "_fh_alloc: no more free file descriptors\n" );
+    D( "_fh_alloc: no more free file descriptors" );
+    errno = EMFILE;   // Too many open files
 Exit:
     if (f) {
-        f->clazz = clazz;
-        f->used  = 1;
-        f->eof   = 0;
+        f->clazz   = clazz;
+        f->used    = 1;
+        f->eof     = 0;
+        f->name[0] = '\0';
         clazz->_fh_init(f);
     }
     adb_mutex_unlock( &_win32_lock );
@@ -240,15 +308,37 @@ Exit:
 static int
 _fh_close( FH   f )
 {
-    if ( f->used ) {
+    // Use lock so that closing only happens once and so that _fh_alloc can't
+    // allocate a FH that we're in the middle of closing.
+    adb_mutex_lock(&_win32_lock);
+    if (f->used) {
         f->clazz->_fh_close( f );
-        f->used = 0;
-        f->eof  = 0;
-        f->clazz = NULL;
+        f->name[0] = '\0';
+        f->eof     = 0;
+        f->used    = 0;
+        f->clazz   = NULL;
     }
+    adb_mutex_unlock(&_win32_lock);
     return 0;
 }
 
+// Deleter for unique_fh.
+class fh_deleter {
+ public:
+  void operator()(struct FHRec_* fh) {
+    // We're called from a destructor and destructors should not overwrite
+    // errno because callers may do:
+    //   errno = EBLAH;
+    //   return -1; // calls destructor, which should not overwrite errno
+    const int saved_errno = errno;
+    _fh_close(fh);
+    errno = saved_errno;
+  }
+};
+
+// Like std::unique_ptr, but calls _fh_close() instead of operator delete().
+typedef std::unique_ptr<struct FHRec_, fh_deleter> unique_fh;
+
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -271,7 +361,7 @@ 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 );
+        D( "adb_read: could not read %d bytes from %s", len, f->name );
         errno = EIO;
         return -1;
     } else if (read_bytes < (DWORD)len) {
@@ -284,7 +374,7 @@ 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 );
+        D( "adb_file_write: could not write %d bytes from %s", len, f->name );
         errno = EIO;
         return -1;
     } else if (wrote_bytes < (DWORD)len) {
@@ -344,43 +434,48 @@ int  adb_open(const char*  path, int  options)
             desiredAccess = GENERIC_READ | GENERIC_WRITE;
             break;
         default:
-            D("adb_open: invalid options (0x%0x)\n", options);
+            D("adb_open: invalid options (0x%0x)", options);
             errno = EINVAL;
             return -1;
     }
 
     f = _fh_alloc( &_fh_file_class );
     if ( !f ) {
-        errno = ENOMEM;
         return -1;
     }
 
-    f->fh_handle = CreateFile( path, desiredAccess, shareMode, NULL, OPEN_EXISTING,
-                               0, NULL );
+    std::wstring path_wide;
+    if (!android::base::UTF8ToWide(path, &path_wide)) {
+        return -1;
+    }
+    f->fh_handle = CreateFileW( path_wide.c_str(), desiredAccess, shareMode,
+                                NULL, OPEN_EXISTING, 0, NULL );
 
     if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+        const DWORD err = GetLastError();
         _fh_close(f);
-        D( "adb_open: could not open '%s':", path );
-        switch (GetLastError()) {
+        D( "adb_open: could not open '%s': ", path );
+        switch (err) {
             case ERROR_FILE_NOT_FOUND:
-                D( "file not found\n" );
+                D( "file not found" );
                 errno = ENOENT;
                 return -1;
 
             case ERROR_PATH_NOT_FOUND:
-                D( "path not found\n" );
+                D( "path not found" );
                 errno = ENOTDIR;
                 return -1;
 
             default:
-                D( "unknown error\n" );
+                D( "unknown error: %s",
+                   SystemErrorCodeToString( err ).c_str() );
                 errno = ENOENT;
                 return -1;
         }
     }
 
     snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
-    D( "adb_open: '%s' => fd %d\n", path, _fh_to_int(f) );
+    D( "adb_open: '%s' => fd %d", path, _fh_to_int(f) );
     return _fh_to_int(f);
 }
 
@@ -391,43 +486,49 @@ int  adb_creat(const char*  path, int  mode)
 
     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 );
+    std::wstring path_wide;
+    if (!android::base::UTF8ToWide(path, &path_wide)) {
+        return -1;
+    }
+    f->fh_handle = CreateFileW( path_wide.c_str(), GENERIC_WRITE,
+                                FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+                                NULL );
 
     if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+        const DWORD err = GetLastError();
         _fh_close(f);
-        D( "adb_creat: could not open '%s':", path );
-        switch (GetLastError()) {
+        D( "adb_creat: could not open '%s': ", path );
+        switch (err) {
             case ERROR_FILE_NOT_FOUND:
-                D( "file not found\n" );
+                D( "file not found" );
                 errno = ENOENT;
                 return -1;
 
             case ERROR_PATH_NOT_FOUND:
-                D( "path not found\n" );
+                D( "path not found" );
                 errno = ENOTDIR;
                 return -1;
 
             default:
-                D( "unknown error\n" );
+                D( "unknown error: %s",
+                   SystemErrorCodeToString( err ).c_str() );
                 errno = ENOENT;
                 return -1;
         }
     }
     snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
-    D( "adb_creat: '%s' => fd %d\n", path, _fh_to_int(f) );
+    D( "adb_creat: '%s' => fd %d", path, _fh_to_int(f) );
     return _fh_to_int(f);
 }
 
 
 int  adb_read(int  fd, void* buf, int len)
 {
-    FH     f = _fh_from_int(fd);
+    FH     f = _fh_from_int(fd, __func__);
 
     if (f == NULL) {
         return -1;
@@ -439,7 +540,7 @@ int  adb_read(int  fd, void* buf, int len)
 
 int  adb_write(int  fd, const void*  buf, int  len)
 {
-    FH     f = _fh_from_int(fd);
+    FH     f = _fh_from_int(fd, __func__);
 
     if (f == NULL) {
         return -1;
@@ -451,7 +552,7 @@ int  adb_write(int  fd, const void*  buf, int  len)
 
 int  adb_lseek(int  fd, int  pos, int  where)
 {
-    FH     f = _fh_from_int(fd);
+    FH     f = _fh_from_int(fd, __func__);
 
     if (!f) {
         return -1;
@@ -461,32 +562,92 @@ int  adb_lseek(int  fd, int  pos, int  where)
 }
 
 
-int  adb_shutdown(int  fd)
+int  adb_close(int  fd)
 {
-    FH   f = _fh_from_int(fd);
+    FH   f = _fh_from_int(fd, __func__);
 
-    if (!f || f->clazz != &_fh_socket_class) {
-        D("adb_shutdown: invalid fd %d\n", fd);
+    if (!f) {
         return -1;
     }
 
-    D( "adb_shutdown: %s\n", f->name);
-    shutdown( f->fh_socket, SD_BOTH );
+    D( "adb_close: %s", f->name);
+    _fh_close(f);
     return 0;
 }
 
+// Overrides strerror() to handle error codes not supported by the Windows C
+// Runtime (MSVCRT.DLL).
+char* adb_strerror(int err) {
+    // sysdeps.h defines strerror to adb_strerror, but in this function, we
+    // want to call the real C Runtime strerror().
+#pragma push_macro("strerror")
+#undef strerror
+    const int saved_err = errno;      // Save because we overwrite it later.
+
+    // Lookup the string for an unknown error.
+    char* errmsg = strerror(-1);
+    const std::string unknown_error = (errmsg == nullptr) ? "" : errmsg;
+
+    // Lookup the string for this error to see if the C Runtime has it.
+    errmsg = strerror(err);
+    if (errmsg != nullptr && unknown_error != errmsg) {
+        // The CRT returned an error message and it is different than the error
+        // message for an unknown error, so it is probably valid, so use it.
+    } else {
+        // Check if we have a string for this error code.
+        const char* custom_msg = nullptr;
+        switch (err) {
+#pragma push_macro("ERR")
+#undef ERR
+#define ERR(errnum, desc) case errnum: custom_msg = desc; break
+            // These error strings are from AOSP bionic/libc/include/sys/_errdefs.h.
+            // Note that these cannot be longer than 94 characters because we
+            // pass this to _strerror() which has that requirement.
+            ERR(ECONNRESET,    "Connection reset by peer");
+            ERR(EHOSTUNREACH,  "No route to host");
+            ERR(ENETDOWN,      "Network is down");
+            ERR(ENETRESET,     "Network dropped connection because of reset");
+            ERR(ENOBUFS,       "No buffer space available");
+            ERR(ENOPROTOOPT,   "Protocol not available");
+            ERR(ENOTCONN,      "Transport endpoint is not connected");
+            ERR(ENOTSOCK,      "Socket operation on non-socket");
+            ERR(EOPNOTSUPP,    "Operation not supported on transport endpoint");
+#pragma pop_macro("ERR")
+        }
 
-int  adb_close(int  fd)
-{
-    FH   f = _fh_from_int(fd);
-
-    if (!f) {
-        return -1;
+        if (custom_msg != nullptr) {
+            // Use _strerror() to write our string into the writable per-thread
+            // buffer used by strerror()/_strerror(). _strerror() appends the
+            // msg for the current value of errno, so set errno to a consistent
+            // value for every call so that our code-path is always the same.
+            errno = 0;
+            errmsg = _strerror(custom_msg);
+            const size_t custom_msg_len = strlen(custom_msg);
+            // Just in case _strerror() returned a read-only string, check if
+            // the returned string starts with our custom message because that
+            // implies that the string is not read-only.
+            if ((errmsg != nullptr) &&
+                !strncmp(custom_msg, errmsg, custom_msg_len)) {
+                // _strerror() puts other text after our custom message, so
+                // remove that by terminating after our message.
+                errmsg[custom_msg_len] = '\0';
+            } else {
+                // For some reason nullptr was returned or a pointer to a
+                // read-only string was returned, so fallback to whatever
+                // strerror() can muster (probably "Unknown error" or some
+                // generic CRT error string).
+                errmsg = strerror(err);
+            }
+        } else {
+            // We don't have a custom message, so use whatever strerror(err)
+            // returned earlier.
+        }
     }
 
-    D( "adb_close: %s\n", f->name);
-    _fh_close(f);
-    return 0;
+    errno = saved_err;  // restore
+
+    return errmsg;
+#pragma pop_macro("strerror")
 }
 
 /**************************************************************************/
@@ -499,29 +660,86 @@ int  adb_close(int  fd)
 
 #undef setsockopt
 
-static void _socket_set_errno( void ) {
-    switch (WSAGetLastError()) {
+static void _socket_set_errno( const DWORD err ) {
+    // Because the Windows C Runtime (MSVCRT.DLL) strerror() does not support a
+    // lot of POSIX and socket error codes, some of the resulting error codes
+    // are mapped to strings by adb_strerror() above.
+    switch ( err ) {
     case 0:              errno = 0; break;
+    // Don't map WSAEINTR since that is only for Winsock 1.1 which we don't use.
+    // case WSAEINTR:    errno = EINTR; break;
+    case WSAEFAULT:      errno = EFAULT; break;
+    case WSAEINVAL:      errno = EINVAL; break;
+    case WSAEMFILE:      errno = EMFILE; break;
+    // Mapping WSAEWOULDBLOCK to EAGAIN is absolutely critical because
+    // non-blocking sockets can cause an error code of WSAEWOULDBLOCK and
+    // callers check specifically for EAGAIN.
     case WSAEWOULDBLOCK: errno = EAGAIN; break;
-    case WSAEINTR:       errno = EINTR; break;
+    case WSAENOTSOCK:    errno = ENOTSOCK; break;
+    case WSAENOPROTOOPT: errno = ENOPROTOOPT; break;
+    case WSAEOPNOTSUPP:  errno = EOPNOTSUPP; break;
+    case WSAENETDOWN:    errno = ENETDOWN; break;
+    case WSAENETRESET:   errno = ENETRESET; break;
+    // Map WSAECONNABORTED to EPIPE instead of ECONNABORTED because POSIX seems
+    // to use EPIPE for these situations and there are some callers that look
+    // for EPIPE.
+    case WSAECONNABORTED: errno = EPIPE; break;
+    case WSAECONNRESET:  errno = ECONNRESET; break;
+    case WSAENOBUFS:     errno = ENOBUFS; break;
+    case WSAENOTCONN:    errno = ENOTCONN; break;
+    // Don't map WSAETIMEDOUT because we don't currently use SO_RCVTIMEO or
+    // SO_SNDTIMEO which would cause WSAETIMEDOUT to be returned. Future
+    // considerations: Reportedly send() can return zero on timeout, and POSIX
+    // code may expect EAGAIN instead of ETIMEDOUT on timeout.
+    // case WSAETIMEDOUT: errno = ETIMEDOUT; break;
+    case WSAEHOSTUNREACH: errno = EHOSTUNREACH; break;
     default:
-        D( "_socket_set_errno: unhandled value %d\n", WSAGetLastError() );
         errno = EINVAL;
+        D( "_socket_set_errno: mapping Windows error code %lu to errno %d",
+           err, errno );
     }
 }
 
 static void _fh_socket_init( FH  f ) {
     f->fh_socket = INVALID_SOCKET;
     f->event     = WSACreateEvent();
+    if (f->event == WSA_INVALID_EVENT) {
+        D("WSACreateEvent failed: %s",
+          SystemErrorCodeToString(WSAGetLastError()).c_str());
+
+        // _event_socket_start assumes that this field is INVALID_HANDLE_VALUE
+        // on failure, instead of NULL which is what Windows really returns on
+        // error. It might be better to change all the other code to look for
+        // NULL, but that is a much riskier change.
+        f->event = INVALID_HANDLE_VALUE;
+    }
     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 );
+    if (f->fh_socket != INVALID_SOCKET) {
+        /* gently tell any peer that we're closing the socket */
+        if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
+            // If the socket is not connected, this returns an error. We want to
+            // minimize logging spam, so don't log these errors for now.
+#if 0
+            D("socket shutdown failed: %s",
+              SystemErrorCodeToString(WSAGetLastError()).c_str());
+#endif
+        }
+        if (closesocket(f->fh_socket) == SOCKET_ERROR) {
+            D("closesocket failed: %s",
+              SystemErrorCodeToString(WSAGetLastError()).c_str());
+        }
+        f->fh_socket = INVALID_SOCKET;
+    }
+    if (f->event != NULL) {
+        if (!CloseHandle(f->event)) {
+            D("CloseHandle failed: %s",
+              SystemErrorCodeToString(GetLastError()).c_str());
+        }
+        f->event = NULL;
+    }
     f->mask = 0;
     return 0;
 }
@@ -534,7 +752,14 @@ static int _fh_socket_lseek( FH  f, int pos, int origin ) {
 static int _fh_socket_read(FH f, void* buf, int len) {
     int  result = recv(f->fh_socket, reinterpret_cast<char*>(buf), len, 0);
     if (result == SOCKET_ERROR) {
-        _socket_set_errno();
+        const DWORD err = WSAGetLastError();
+        // WSAEWOULDBLOCK is normal with a non-blocking socket, so don't trace
+        // that to reduce spam and confusion.
+        if (err != WSAEWOULDBLOCK) {
+            D("recv fd %d failed: %s", _fh_to_int(f),
+              SystemErrorCodeToString(err).c_str());
+        }
+        _socket_set_errno(err);
         result = -1;
     }
     return  result;
@@ -543,8 +768,21 @@ static int _fh_socket_read(FH f, void* buf, int len) {
 static int _fh_socket_write(FH f, const void* buf, int len) {
     int  result = send(f->fh_socket, reinterpret_cast<const char*>(buf), len, 0);
     if (result == SOCKET_ERROR) {
-        _socket_set_errno();
+        const DWORD err = WSAGetLastError();
+        // WSAEWOULDBLOCK is normal with a non-blocking socket, so don't trace
+        // that to reduce spam and confusion.
+        if (err != WSAEWOULDBLOCK) {
+            D("send fd %d failed: %s", _fh_to_int(f),
+              SystemErrorCodeToString(err).c_str());
+        }
+        _socket_set_errno(err);
         result = -1;
+    } else {
+        // According to https://code.google.com/p/chromium/issues/detail?id=27870
+        // Winsock Layered Service Providers may cause this.
+        CHECK_LE(result, len) << "Tried to write " << len << " bytes to "
+                              << f->name << ", but " << result
+                              << " bytes reportedly written";
     }
     return result;
 }
@@ -562,33 +800,61 @@ static int _fh_socket_write(FH f, const void* buf, int len) {
 static int  _winsock_init;
 
 static void
-_cleanup_winsock( void )
-{
-    WSACleanup();
-}
-
-static void
 _init_winsock( void )
 {
+    // TODO: Multiple threads calling this may potentially cause multiple calls
+    // to WSAStartup() which offers no real benefit.
     if (!_winsock_init) {
         WSADATA  wsaData;
         int      rc = WSAStartup( MAKEWORD(2,2), &wsaData);
         if (rc != 0) {
-            fatal( "adb: could not initialize Winsock\n" );
+            fatal( "adb: could not initialize Winsock: %s",
+                   SystemErrorCodeToString( rc ).c_str());
         }
-        atexit( _cleanup_winsock );
         _winsock_init = 1;
+
+        // Note that we do not call atexit() to register WSACleanup to be called
+        // at normal process termination because:
+        // 1) When exit() is called, there are still threads actively using
+        //    Winsock because we don't cleanly shutdown all threads, so it
+        //    doesn't make sense to call WSACleanup() and may cause problems
+        //    with those threads.
+        // 2) A deadlock can occur when exit() holds a C Runtime lock, then it
+        //    calls WSACleanup() which tries to unload a DLL, which tries to
+        //    grab the LoaderLock. This conflicts with the device_poll_thread
+        //    which holds the LoaderLock because AdbWinApi.dll calls
+        //    setupapi.dll which tries to load wintrust.dll which tries to load
+        //    crypt32.dll which calls atexit() which tries to acquire the C
+        //    Runtime lock that the other thread holds.
+    }
+}
+
+// Map a socket type to an explicit socket protocol instead of using the socket
+// protocol of 0. Explicit socket protocols are used by most apps and we should
+// do the same to reduce the chance of exercising uncommon code-paths that might
+// have problems or that might load different Winsock service providers that
+// have problems.
+static int GetSocketProtocolFromSocketType(int type) {
+    switch (type) {
+        case SOCK_STREAM:
+            return IPPROTO_TCP;
+        case SOCK_DGRAM:
+            return IPPROTO_UDP;
+        default:
+            LOG(FATAL) << "Unknown socket type: " << type;
+            return 0;
     }
 }
 
-int socket_loopback_client(int port, int type)
-{
-    FH  f = _fh_alloc( &_fh_socket_class );
+int network_loopback_client(int port, int type, std::string* error) {
     struct sockaddr_in addr;
     SOCKET  s;
 
-    if (!f)
+    unique_fh  f(_fh_alloc(&_fh_socket_class));
+    if (!f) {
+        *error = strerror(errno);
         return -1;
+    }
 
     if (!_winsock_init)
         _init_winsock();
@@ -598,34 +864,47 @@ int socket_loopback_client(int port, int type)
     addr.sin_port = htons(port);
     addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 
-    s = socket(AF_INET, type, 0);
+    s = socket(AF_INET, type, GetSocketProtocolFromSocketType(type));
     if(s == INVALID_SOCKET) {
-        D("socket_loopback_client: could not create socket\n" );
-        _fh_close(f);
+        *error = android::base::StringPrintf("cannot create socket: %s",
+                SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("%s", error->c_str());
         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);
+
+    if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+        // Save err just in case inet_ntoa() or ntohs() changes the last error.
+        const DWORD err = WSAGetLastError();
+        *error = android::base::StringPrintf("cannot connect to %s:%u: %s",
+                inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+                SystemErrorCodeToString(err).c_str());
+        D("could not connect to %s:%d: %s",
+          type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
         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);
+
+    const int fd = _fh_to_int(f.get());
+    snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", fd,
+              type != SOCK_STREAM ? "udp:" : "", port );
+    D( "port %d type %s => fd %d", port, type != SOCK_STREAM ? "udp" : "tcp",
+       fd );
+    f.release();
+    return fd;
 }
 
 #define LISTEN_BACKLOG 4
 
-int socket_loopback_server(int port, int type)
-{
-    FH   f = _fh_alloc( &_fh_socket_class );
+// interface_address is INADDR_LOOPBACK or INADDR_ANY.
+static int _network_server(int port, int type, u_long interface_address,
+                           std::string* error) {
     struct sockaddr_in addr;
     SOCKET  s;
     int  n;
 
+    unique_fh   f(_fh_alloc(&_fh_socket_class));
     if (!f) {
+        *error = strerror(errno);
         return -1;
     }
 
@@ -635,171 +914,224 @@ int socket_loopback_server(int port, int type)
     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;
+    addr.sin_addr.s_addr = htonl(interface_address);
+
+    // TODO: Consider using dual-stack socket that can simultaneously listen on
+    // IPv4 and IPv6.
+    s = socket(AF_INET, type, GetSocketProtocolFromSocketType(type));
+    if (s == INVALID_SOCKET) {
+        *error = android::base::StringPrintf("cannot create socket: %s",
+                SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("%s", error->c_str());
+        return -1;
+    }
 
     f->fh_socket = s;
 
+    // Note: SO_REUSEADDR on Windows allows multiple processes to bind to the
+    // same port, so instead use SO_EXCLUSIVEADDRUSE.
     n = 1;
-    setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n));
+    if (setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n,
+                   sizeof(n)) == SOCKET_ERROR) {
+        *error = android::base::StringPrintf(
+                "cannot set socket option SO_EXCLUSIVEADDRUSE: %s",
+                SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("%s", error->c_str());
+        return -1;
+    }
 
-    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        _fh_close(f);
+    if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+        // Save err just in case inet_ntoa() or ntohs() changes the last error.
+        const DWORD err = WSAGetLastError();
+        *error = android::base::StringPrintf("cannot bind to %s:%u: %s",
+                inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+                SystemErrorCodeToString(err).c_str());
+        D("could not bind to %s:%d: %s",
+          type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
         return -1;
     }
     if (type == SOCK_STREAM) {
-        int ret;
-
-        ret = listen(s, LISTEN_BACKLOG);
-        if (ret < 0) {
-            _fh_close(f);
+        if (listen(s, LISTEN_BACKLOG) == SOCKET_ERROR) {
+            *error = android::base::StringPrintf("cannot listen on socket: %s",
+                    SystemErrorCodeToString(WSAGetLastError()).c_str());
+            D("could not listen on %s:%d: %s",
+              type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
             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);
+    const int fd = _fh_to_int(f.get());
+    snprintf( f->name, sizeof(f->name), "%d(%s-server:%s%d)", fd,
+              interface_address == INADDR_LOOPBACK ? "lo" : "any",
+              type != SOCK_STREAM ? "udp:" : "", port );
+    D( "port %d type %s => fd %d", port, type != SOCK_STREAM ? "udp" : "tcp",
+       fd );
+    f.release();
+    return fd;
 }
 
+int network_loopback_server(int port, int type, std::string* error) {
+    return _network_server(port, type, INADDR_LOOPBACK, error);
+}
 
-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;
+int network_inaddr_any_server(int port, int type, std::string* error) {
+    return _network_server(port, type, INADDR_ANY, error);
+}
 
-    if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        _fh_close(f);
+int network_connect(const std::string& host, int port, int type, int timeout, std::string* error) {
+    unique_fh f(_fh_alloc(&_fh_socket_class));
+    if (!f) {
+        *error = strerror(errno);
         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);
-}
+    if (!_winsock_init) _init_winsock();
 
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = type;
+    hints.ai_protocol = GetSocketProtocolFromSocketType(type);
 
-int socket_network_client_timeout(const char *host, int port, int type, int timeout)
-{
-    // TODO: implement timeouts for Windows.
-    return socket_network_client(host, port, type);
-}
-
+    char port_str[16];
+    snprintf(port_str, sizeof(port_str), "%d", port);
 
-int socket_inaddr_any_server(int port, int type)
-{
-    FH  f = _fh_alloc( &_fh_socket_class );
-    struct sockaddr_in addr;
-    SOCKET  s;
-    int n;
+    struct addrinfo* addrinfo_ptr = nullptr;
 
-    if (!f)
+#if (NTDDI_VERSION >= NTDDI_WINXPSP2) || (_WIN32_WINNT >= _WIN32_WINNT_WS03)
+    // TODO: When the Android SDK tools increases the Windows system
+    // requirements >= WinXP SP2, switch to android::base::UTF8ToWide() + GetAddrInfoW().
+#else
+    // Otherwise, keep using getaddrinfo(), or do runtime API detection
+    // with GetProcAddress("GetAddrInfoW").
+#endif
+    if (getaddrinfo(host.c_str(), port_str, &hints, &addrinfo_ptr) != 0) {
+        *error = android::base::StringPrintf(
+                "cannot resolve host '%s' and port %s: %s", host.c_str(),
+                port_str, SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("%s", error->c_str());
         return -1;
+    }
+    std::unique_ptr<struct addrinfo, decltype(freeaddrinfo)*>
+        addrinfo(addrinfo_ptr, freeaddrinfo);
+    addrinfo_ptr = nullptr;
 
-    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);
+    // TODO: Try all the addresses if there's more than one? This just uses
+    // the first. Or, could call WSAConnectByName() (Windows Vista and newer)
+    // which tries all addresses, takes a timeout and more.
+    SOCKET s = socket(addrinfo->ai_family, addrinfo->ai_socktype,
+                      addrinfo->ai_protocol);
     if(s == INVALID_SOCKET) {
-        _fh_close(f);
+        *error = android::base::StringPrintf("cannot create socket: %s",
+                SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("%s", error->c_str());
         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);
+    // TODO: Implement timeouts for Windows. Seems like the default in theory
+    // (according to http://serverfault.com/a/671453) and in practice is 21 sec.
+    if(connect(s, addrinfo->ai_addr, addrinfo->ai_addrlen) == SOCKET_ERROR) {
+        // TODO: Use WSAAddressToString or inet_ntop on address.
+        *error = android::base::StringPrintf("cannot connect to %s:%s: %s",
+                host.c_str(), port_str,
+                SystemErrorCodeToString(WSAGetLastError()).c_str());
+        D("could not connect to %s:%s:%s: %s",
+          type != SOCK_STREAM ? "udp" : "tcp", host.c_str(), port_str,
+          error->c_str());
         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);
+    const int fd = _fh_to_int(f.get());
+    snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", fd,
+              type != SOCK_STREAM ? "udp:" : "", port );
+    D( "host '%s' port %d type %s => fd %d", host.c_str(), port,
+       type != SOCK_STREAM ? "udp" : "tcp", fd );
+    f.release();
+    return fd;
 }
 
 #undef accept
 int  adb_socket_accept(int  serverfd, struct sockaddr*  addr, socklen_t  *addrlen)
 {
-    FH   serverfh = _fh_from_int(serverfd);
-    FH   fh;
+    FH   serverfh = _fh_from_int(serverfd, __func__);
 
     if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
-        D( "adb_socket_accept: invalid fd %d\n", serverfd );
+        D("adb_socket_accept: invalid fd %d", serverfd);
+        errno = EBADF;
         return -1;
     }
 
-    fh = _fh_alloc( &_fh_socket_class );
+    unique_fh fh(_fh_alloc( &_fh_socket_class ));
     if (!fh) {
-        D( "adb_socket_accept: not enough memory to allocate accepted socket descriptor\n" );
+        PLOG(ERROR) << "adb_socket_accept: failed to allocate accepted socket "
+                       "descriptor";
         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() );
+        const DWORD err = WSAGetLastError();
+        LOG(ERROR) << "adb_socket_accept: accept on fd " << serverfd <<
+                      " failed: " + SystemErrorCodeToString(err);
+        _socket_set_errno( err );
         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);
+    const int fd = _fh_to_int(fh.get());
+    snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", fd, serverfh->name );
+    D( "adb_socket_accept on fd %d returns fd %d", serverfd, fd );
+    fh.release();
+    return  fd;
 }
 
 
 int  adb_setsockopt( int  fd, int  level, int  optname, const void*  optval, socklen_t  optlen )
 {
-    FH   fh = _fh_from_int(fd);
+    FH   fh = _fh_from_int(fd, __func__);
 
     if ( !fh || fh->clazz != &_fh_socket_class ) {
-        D("adb_setsockopt: invalid fd %d\n", fd);
+        D("adb_setsockopt: invalid fd %d", fd);
+        errno = EBADF;
         return -1;
     }
 
-    return setsockopt( fh->fh_socket, level, optname, reinterpret_cast<const char*>(optval), optlen );
+    // TODO: Once we can assume Windows Vista or later, if the caller is trying
+    // to set SOL_SOCKET, SO_SNDBUF/SO_RCVBUF, ignore it since the OS has
+    // auto-tuning.
+
+    int result = setsockopt( fh->fh_socket, level, optname,
+                             reinterpret_cast<const char*>(optval), optlen );
+    if ( result == SOCKET_ERROR ) {
+        const DWORD err = WSAGetLastError();
+        D( "adb_setsockopt: setsockopt on fd %d level %d optname %d "
+           "failed: %s\n", fd, level, optname,
+           SystemErrorCodeToString(err).c_str() );
+        _socket_set_errno( err );
+        result = -1;
+    }
+    return result;
+}
+
+
+int  adb_shutdown(int  fd)
+{
+    FH   f = _fh_from_int(fd, __func__);
+
+    if (!f || f->clazz != &_fh_socket_class) {
+        D("adb_shutdown: invalid fd %d", fd);
+        errno = EBADF;
+        return -1;
+    }
+
+    D( "adb_shutdown: %s", f->name);
+    if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
+        const DWORD err = WSAGetLastError();
+        D("socket shutdown fd %d failed: %s", fd,
+          SystemErrorCodeToString(err).c_str());
+        _socket_set_errno(err);
+        return -1;
+    }
+    return 0;
 }
 
 /**************************************************************************/
@@ -889,7 +1221,7 @@ typedef struct BipBufferRec_
 static void
 bip_buffer_init( BipBuffer  buffer )
 {
-    D( "bit_buffer_init %p\n", buffer );
+    D( "bit_buffer_init %p", buffer );
     buffer->a_start   = 0;
     buffer->a_end     = 0;
     buffer->b_end     = 0;
@@ -919,7 +1251,7 @@ bip_buffer_close( BipBuffer  bip )
 static void
 bip_buffer_done( BipBuffer  bip )
 {
-    BIPD(( "bip_buffer_done: %d->%d\n", bip->fdin, bip->fdout ));
+    BIPD(( "bip_buffer_done: %d->%d", bip->fdin, bip->fdout ));
     CloseHandle( bip->evt_read );
     CloseHandle( bip->evt_write );
     DeleteCriticalSection( &bip->lock );
@@ -933,9 +1265,14 @@ bip_buffer_write( BipBuffer  bip, const void* src, int  len )
     if (len <= 0)
         return 0;
 
-    BIPD(( "bip_buffer_write: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+    BIPD(( "bip_buffer_write: enter %d->%d len %d", bip->fdin, bip->fdout, len ));
     BIPDUMP( src, len );
 
+    if (bip->closed) {
+        errno = EPIPE;
+        return -1;
+    }
+
     EnterCriticalSection( &bip->lock );
 
     while (!bip->can_write) {
@@ -949,7 +1286,7 @@ bip_buffer_write( BipBuffer  bip, const void* src, int  len )
         /* 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() );
+            D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld", bip->fdin, bip->fdout, ret, GetLastError() );
             return 0;
         }
         if (bip->closed) {
@@ -959,7 +1296,7 @@ bip_buffer_write( BipBuffer  bip, const void* src, int  len )
         EnterCriticalSection( &bip->lock );
     }
 
-    BIPD(( "bip_buffer_write: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+    BIPD(( "bip_buffer_write: exec %d->%d len %d", bip->fdin, bip->fdout, len ));
 
     avail = BIP_BUFFER_SIZE - bip->a_end;
     if (avail > 0)
@@ -1007,7 +1344,7 @@ Exit:
         SetEvent( bip->evt_read );
     }
 
-    BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
+    BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d",
             bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
     LeaveCriticalSection( &bip->lock );
 
@@ -1022,7 +1359,7 @@ bip_buffer_read( BipBuffer  bip, void*  dst, int  len )
     if (len <= 0)
         return 0;
 
-    BIPD(( "bip_buffer_read: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+    BIPD(( "bip_buffer_read: enter %d->%d len %d", bip->fdin, bip->fdout, len ));
 
     EnterCriticalSection( &bip->lock );
     while ( !bip->can_read )
@@ -1042,7 +1379,7 @@ bip_buffer_read( BipBuffer  bip, void*  dst, int  len )
 
         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());
+            D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld", bip->fdin, bip->fdout, ret, GetLastError());
             return 0;
         }
         if (bip->closed) {
@@ -1053,7 +1390,7 @@ bip_buffer_read( BipBuffer  bip, void*  dst, int  len )
 #endif
     }
 
-    BIPD(( "bip_buffer_read: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+    BIPD(( "bip_buffer_read: exec %d->%d len %d", bip->fdin, bip->fdout, len ));
 
     avail = bip->a_end - bip->a_start;
     assert( avail > 0 );  /* since can_read is TRUE */
@@ -1100,7 +1437,7 @@ Exit:
     }
 
     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",
+    BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d",
             bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
     LeaveCriticalSection( &bip->lock );
 
@@ -1202,16 +1539,19 @@ static const FHClassRec  _fh_socketpair_class =
 int  adb_socketpair(int sv[2]) {
     SocketPair pair;
 
-    FH fa = _fh_alloc(&_fh_socketpair_class);
-    FH fb = _fh_alloc(&_fh_socketpair_class);
-
-    if (!fa || !fb)
-        goto Fail;
+    unique_fh fa(_fh_alloc(&_fh_socketpair_class));
+    if (!fa) {
+        return -1;
+    }
+    unique_fh fb(_fh_alloc(&_fh_socketpair_class));
+    if (!fb) {
+        return -1;
+    }
 
     pair = reinterpret_cast<SocketPair>(malloc(sizeof(*pair)));
     if (pair == NULL) {
-        D("adb_socketpair: not enough memory to allocate pipes\n" );
-        goto Fail;
+        D("adb_socketpair: not enough memory to allocate pipes" );
+        return -1;
     }
 
     bip_buffer_init( &pair->a2b_bip );
@@ -1220,10 +1560,10 @@ int  adb_socketpair(int sv[2]) {
     fa->fh_pair = pair;
     fb->fh_pair = pair;
     pair->used  = 2;
-    pair->a_fd  = fa;
+    pair->a_fd  = fa.get();
 
-    sv[0] = _fh_to_int(fa);
-    sv[1] = _fh_to_int(fb);
+    sv[0] = _fh_to_int(fa.get());
+    sv[1] = _fh_to_int(fb.get());
 
     pair->a2b_bip.fdin  = sv[0];
     pair->a2b_bip.fdout = sv[1];
@@ -1232,13 +1572,10 @@ int  adb_socketpair(int sv[2]) {
 
     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] );
+    D( "adb_socketpair: returns (%d, %d)", sv[0], sv[1] );
+    fa.release();
+    fb.release();
     return 0;
-
-Fail:
-    _fh_close(fb);
-    _fh_close(fa);
-    return -1;
 }
 
 /**************************************************************************/
@@ -1386,12 +1723,12 @@ event_looper_find_p( EventLooper  looper, FH  fh )
 static void
 event_looper_hook( EventLooper  looper, int  fd, int  events )
 {
-    FH          f = _fh_from_int(fd);
+    FH          f = _fh_from_int(fd, __func__);
     EventHook  *pnode;
     EventHook   node;
 
     if (f == NULL)  /* invalid arg */ {
-        D("event_looper_hook: invalid fd=%d\n", fd);
+        D("event_looper_hook: invalid fd=%d", fd);
         return;
     }
 
@@ -1405,12 +1742,12 @@ event_looper_hook( EventLooper  looper, int  fd, int  events )
 
     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",
+        D("event_looper_hook: call hook for %d (new=%x, old=%x)",
            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",
+        D("event_looper_hook: ignoring events %x for %d wanted=%x)",
            events, fd, node->wanted);
     }
 }
@@ -1418,14 +1755,14 @@ event_looper_hook( EventLooper  looper, int  fd, int  events )
 static void
 event_looper_unhook( EventLooper  looper, int  fd, int  events )
 {
-    FH          fh    = _fh_from_int(fd);
+    FH          fh    = _fh_from_int(fd, __func__);
     EventHook  *pnode = event_looper_find_p( looper, fh );
     EventHook   node  = *pnode;
 
     if (node != NULL) {
         int  events2 = events & node->wanted;
         if ( events2 == 0 ) {
-            D( "event_looper_unhook: events %x not registered for fd %d\n", events, fd );
+            D( "event_looper_unhook: events %x not registered for fd %d", events, fd );
             return;
         }
         node->wanted &= ~events2;
@@ -1552,7 +1889,7 @@ _wait_for_all(HANDLE* handles, int handles_count)
      * 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", (int)GetLastError());
+        D("Unable to create main event. Error: %ld", GetLastError());
         free(threads);
         return (int)WAIT_FAILED;
     }
@@ -1645,11 +1982,11 @@ static void fdevent_update(fdevent *fde, unsigned events)
         int  removes = events0 & ~events;
         int  adds    = events  & ~events0;
         if (removes) {
-            D("fdevent_update: remove %x from %d\n", removes, fde->fd);
+            D("fdevent_update: remove %x from %d", removes, fde->fd);
             event_looper_unhook( looper, fde->fd, removes );
         }
         if (adds) {
-            D("fdevent_update: add %x to %d\n", adds, fde->fd);
+            D("fdevent_update: add %x to %d", adds, fde->fd);
             event_looper_hook  ( looper, fde->fd, adds );
         }
     }
@@ -1681,7 +2018,7 @@ static void fdevent_process()
         for (hook = looper->hooks; hook; hook = hook->next)
         {
             if (hook->start && !hook->start(hook)) {
-                D( "fdevent_process: error when starting a hook\n" );
+                D( "fdevent_process: error when starting a hook" );
                 return;
             }
             if (hook->h != INVALID_HANDLE_VALUE) {
@@ -1699,7 +2036,7 @@ static void fdevent_process()
         }
 
         if (looper->htab_count == 0) {
-            D( "fdevent_process: nothing to wait for !!\n" );
+            D( "fdevent_process: nothing to wait for !!" );
             return;
         }
 
@@ -1707,17 +2044,17 @@ static void fdevent_process()
         {
             int   wait_ret;
 
-            D( "adb_win32: waiting for %d events\n", looper->htab_count );
+            D( "adb_win32: waiting for %d events", looper->htab_count );
             if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) {
-                D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.\n", looper->htab_count);
+                D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.", 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() );
+                D( "adb_win32: wait failed, error %ld", GetLastError() );
             } else {
-                D( "adb_win32: got one (index %d)\n", wait_ret );
+                D( "adb_win32: got one (index %d)", 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
@@ -1729,7 +2066,7 @@ static void fdevent_process()
                         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 );
+                            D( "adb_win32: signaling %s for %x", hook->fh->name, hook->ready );
                             event_hook_signal( hook );
                             gotone = 1;
                             break;
@@ -2021,14 +2358,14 @@ static int _event_socket_start( EventHook  hook )
 
     hook->h = fh->event;
     if (hook->h == INVALID_HANDLE_VALUE) {
-        D( "_event_socket_start: no event for %s\n", fh->name );
+        D( "_event_socket_start: no event for %s", 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 );
+        D( "_event_socket_start: hooking %s for %x (flags %ld)", 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() );
+            D( "_event_socket_start: WSAEventSelect() for %s failed, error %d", hook->fh->name, WSAGetLastError() );
             CloseHandle( hook->h );
             hook->h = INVALID_HANDLE_VALUE;
             exit(1);
@@ -2057,7 +2394,7 @@ static int  _event_socket_check( EventHook  hook )
             ResetEvent( hook->h );
         }
     }
-    D( "_event_socket_check %s returns %d\n", fh->name, result );
+    D( "_event_socket_check %s returns %d", fh->name, result );
     return  result;
 }
 
@@ -2086,6 +2423,7 @@ static void  _fh_socket_hook( FH  f, int  events, EventHook  hook )
     hook->check   = _event_socket_check;
     hook->peek    = _event_socket_peek;
 
+    // TODO: check return value?
     _event_socket_start( hook );
 }
 
@@ -2120,10 +2458,10 @@ static void  _event_socketpair_prepare( EventHook  hook )
         hook->h = wbip->evt_write;
 
     else {
-        D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" );
+        D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE" );
         return 0;
     }
-    D( "_event_socketpair_start: hook %s for %x wanted=%x\n",
+    D( "_event_socketpair_start: hook %s for %x wanted=%x",
        hook->fh->name, _fh_to_int(fh), hook->wanted);
     return 1;
 }
@@ -2180,20 +2518,61 @@ adb_sysdeps_init( void )
 //
 // Code organization:
 //
+// * _get_console_handle() and unix_isatty() provide console information.
 // * stdin_raw_init() and stdin_raw_restore() reconfigure the console.
 // * unix_read() detects console windows (as opposed to pipes, files, etc.).
 // * _console_read() is the main code of the emulation.
 
+// Returns a console HANDLE if |fd| is a console, otherwise returns nullptr.
+// If a valid HANDLE is returned and |mode| is not null, |mode| is also filled
+// with the console mode. Requires GENERIC_READ access to the underlying HANDLE.
+static HANDLE _get_console_handle(int fd, DWORD* mode=nullptr) {
+    // First check isatty(); this is very fast and eliminates most non-console
+    // FDs, but returns 1 for both consoles and character devices like NUL.
+#pragma push_macro("isatty")
+#undef isatty
+    if (!isatty(fd)) {
+        return nullptr;
+    }
+#pragma pop_macro("isatty")
+
+    // To differentiate between character devices and consoles we need to get
+    // the underlying HANDLE and use GetConsoleMode(), which is what requires
+    // GENERIC_READ permissions.
+    const intptr_t intptr_handle = _get_osfhandle(fd);
+    if (intptr_handle == -1) {
+        return nullptr;
+    }
+    const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
+    DWORD temp_mode = 0;
+    if (!GetConsoleMode(handle, mode ? mode : &temp_mode)) {
+        return nullptr;
+    }
+
+    return handle;
+}
 
-// Read an input record from the console; one that should be processed.
-static bool _get_interesting_input_record_uncached(const HANDLE console,
-    INPUT_RECORD* const input_record) {
+// Returns a console handle if |stream| is a console, otherwise returns nullptr.
+static HANDLE _get_console_handle(FILE* const stream) {
+    const int fd = fileno(stream);
+    if (fd < 0) {
+        return nullptr;
+    }
+    return _get_console_handle(fd);
+}
+
+int unix_isatty(int fd) {
+    return _get_console_handle(fd) ? 1 : 0;
+}
+
+// Get the next KEY_EVENT_RECORD that should be processed.
+static bool _get_key_event_record(const HANDLE console, INPUT_RECORD* const input_record) {
     for (;;) {
         DWORD read_count = 0;
         memset(input_record, 0, sizeof(*input_record));
         if (!ReadConsoleInputA(console, input_record, 1, &read_count)) {
-            D("_get_interesting_input_record_uncached: ReadConsoleInputA() "
-              "failure, error %ld\n", GetLastError());
+            D("_get_key_event_record: ReadConsoleInputA() failed: %s\n",
+              SystemErrorCodeToString(GetLastError()).c_str());
             errno = EIO;
             return false;
         }
@@ -2219,28 +2598,6 @@ static bool _get_interesting_input_record_uncached(const HANDLE console,
     }
 }
 
-// Cached input record (in case _console_read() is passed a buffer that doesn't
-// have enough space to fit wRepeatCount number of key sequences). A non-zero
-// wRepeatCount indicates that a record is cached.
-static INPUT_RECORD _win32_input_record;
-
-// Get the next KEY_EVENT_RECORD that should be processed.
-static KEY_EVENT_RECORD* _get_key_event_record(const HANDLE console) {
-    // If nothing cached, read directly from the console until we get an
-    // interesting record.
-    if (_win32_input_record.Event.KeyEvent.wRepeatCount == 0) {
-        if (!_get_interesting_input_record_uncached(console,
-            &_win32_input_record)) {
-            // There was an error, so make sure wRepeatCount is zero because
-            // that signifies no cached input record.
-            _win32_input_record.Event.KeyEvent.wRepeatCount = 0;
-            return NULL;
-        }
-    }
-
-    return &_win32_input_record.Event.KeyEvent;
-}
-
 static __inline__ bool _is_shift_pressed(const DWORD control_key_state) {
     return (control_key_state & SHIFT_PRESSED) != 0;
 }
@@ -2585,16 +2942,34 @@ size_t _escape_prefix(char* const buf, const size_t len) {
     return len + 1;
 }
 
-// Writes to buffer buf (of length len), returning number of bytes written or
-// -1 on error. Never returns zero because Win32 consoles are never 'closed'
-// (as far as I can tell).
+// Internal buffer to satisfy future _console_read() calls.
+static auto& g_console_input_buffer = *new std::vector<char>();
+
+// Writes to buffer buf (of length len), returning number of bytes written or -1 on error. Never
+// returns zero on console closure because Win32 consoles are never 'closed' (as far as I can tell).
 static int _console_read(const HANDLE console, void* buf, size_t len) {
     for (;;) {
-        KEY_EVENT_RECORD* const key_event = _get_key_event_record(console);
-        if (key_event == NULL) {
+        // Read of zero bytes should not block waiting for something from the console.
+        if (len == 0) {
+            return 0;
+        }
+
+        // Flush as much as possible from input buffer.
+        if (!g_console_input_buffer.empty()) {
+            const int bytes_read = std::min(len, g_console_input_buffer.size());
+            memcpy(buf, g_console_input_buffer.data(), bytes_read);
+            const auto begin = g_console_input_buffer.begin();
+            g_console_input_buffer.erase(begin, begin + bytes_read);
+            return bytes_read;
+        }
+
+        // Read from the actual console. This may block until input.
+        INPUT_RECORD input_record;
+        if (!_get_key_event_record(console, &input_record)) {
             return -1;
         }
 
+        KEY_EVENT_RECORD* const key_event = &input_record.Event.KeyEvent;
         const WORD vk = key_event->wVirtualKeyCode;
         const CHAR ch = key_event->uChar.AsciiChar;
         const DWORD control_key_state = _normalize_altgr_control_key_state(
@@ -2772,7 +3147,12 @@ static int _console_read(const HANDLE console, void* buf, size_t len) {
                 break;
 
                 case 0x32:          // 2
+                case 0x33:          // 3
+                case 0x34:          // 4
+                case 0x35:          // 5
                 case 0x36:          // 6
+                case 0x37:          // 7
+                case 0x38:          // 8
                 case VK_OEM_MINUS:  // -_
                 {
                     seqbuflen = _get_control_character(seqbuf, key_event,
@@ -2788,25 +3168,6 @@ static int _console_read(const HANDLE console, void* buf, size_t len) {
                 }
                 break;
 
-                case 0x33:  // 3
-                case 0x34:  // 4
-                case 0x35:  // 5
-                case 0x37:  // 7
-                case 0x38:  // 8
-                {
-                    seqbuflen = _get_control_character(seqbuf, key_event,
-                        control_key_state);
-
-                    // If Alt is pressed and it isn't Ctrl-Alt-ShiftUp, then
-                    // prefix with escape.
-                    if (_is_alt_pressed(control_key_state) &&
-                        !(_is_ctrl_pressed(control_key_state) &&
-                        !_is_shift_pressed(control_key_state))) {
-                        seqbuflen = _escape_prefix(seqbuf, seqbuflen);
-                    }
-                }
-                break;
-
                 case 0x41:  // a
                 case 0x42:  // b
                 case 0x43:  // c
@@ -2933,110 +3294,63 @@ static int _console_read(const HANDLE console, void* buf, size_t len) {
             //
             // Consume the input and 'continue' to cause us to get a new key
             // event.
-            D("_console_read: unknown virtual key code: %d, enhanced: %s\n",
+            D("_console_read: unknown virtual key code: %d, enhanced: %s",
                 vk, _is_enhanced_key(control_key_state) ? "true" : "false");
-            key_event->wRepeatCount = 0;
             continue;
         }
 
-        int bytesRead = 0;
-
-        // put output wRepeatCount times into buf/len
-        while (key_event->wRepeatCount > 0) {
-            if (len >= outlen) {
-                // Write to buf/len
-                memcpy(buf, out, outlen);
-                buf = (void*)((char*)buf + outlen);
-                len -= outlen;
-                bytesRead += outlen;
-
-                // consume the input
-                --key_event->wRepeatCount;
-            } else {
-                // Not enough space, so just leave it in _win32_input_record
-                // for a subsequent retrieval.
-                if (bytesRead == 0) {
-                    // We didn't write anything because there wasn't enough
-                    // space to even write one sequence. This should never
-                    // happen if the caller uses sensible buffer sizes
-                    // (i.e. >= maximum sequence length which is probably a
-                    // few bytes long).
-                    D("_console_read: no buffer space to write one sequence; "
-                        "buffer: %ld, sequence: %ld\n", (long)len,
-                        (long)outlen);
-                    errno = ENOMEM;
-                    return -1;
-                } else {
-                    // Stop trying to write to buf/len, just return whatever
-                    // we wrote so far.
-                    break;
-                }
-            }
+        // put output wRepeatCount times into g_console_input_buffer
+        while (key_event->wRepeatCount-- > 0) {
+            g_console_input_buffer.insert(g_console_input_buffer.end(), out, out + outlen);
         }
 
-        return bytesRead;
+        // Loop around and try to flush g_console_input_buffer
     }
 }
 
 static DWORD _old_console_mode; // previous GetConsoleMode() result
 static HANDLE _console_handle;  // when set, console mode should be restored
 
-void stdin_raw_init(const int fd) {
-    if (STDIN_FILENO == fd) {
-        const HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
-        if ((in == INVALID_HANDLE_VALUE) || (in == NULL)) {
-            return;
-        }
+void stdin_raw_init() {
+    const HANDLE in = _get_console_handle(STDIN_FILENO, &_old_console_mode);
 
-        if (GetFileType(in) != FILE_TYPE_CHAR) {
-            // stdin might be a file or pipe.
-            return;
-        }
-
-        if (!GetConsoleMode(in, &_old_console_mode)) {
-            // If GetConsoleMode() fails, stdin is probably is not a console.
-            return;
-        }
-
-        // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
-        // calling the process Ctrl-C routine (configured by
-        // SetConsoleCtrlHandler()).
-        // Disable ENABLE_LINE_INPUT so that input is immediately sent.
-        // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this
-        // flag also seems necessary to have proper line-ending processing.
-        if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
-            ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))) {
-            // This really should not fail.
-            D("stdin_raw_init: SetConsoleMode() failure, error %ld\n",
-                GetLastError());
-        }
+    // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
+    // calling the process Ctrl-C routine (configured by
+    // SetConsoleCtrlHandler()).
+    // Disable ENABLE_LINE_INPUT so that input is immediately sent.
+    // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this
+    // flag also seems necessary to have proper line-ending processing.
+    if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
+                                                  ENABLE_LINE_INPUT |
+                                                  ENABLE_ECHO_INPUT))) {
+        // This really should not fail.
+        D("stdin_raw_init: SetConsoleMode() failed: %s",
+          SystemErrorCodeToString(GetLastError()).c_str());
+    }
 
-        // Once this is set, it means that stdin has been configured for
-        // reading from and that the old console mode should be restored later.
-        _console_handle = in;
+    // Once this is set, it means that stdin has been configured for
+    // reading from and that the old console mode should be restored later.
+    _console_handle = in;
 
-        // Note that we don't need to configure C Runtime line-ending
-        // translation because _console_read() does not call the C Runtime to
-        // read from the console.
-    }
+    // Note that we don't need to configure C Runtime line-ending
+    // translation because _console_read() does not call the C Runtime to
+    // read from the console.
 }
 
-void stdin_raw_restore(const int fd) {
-    if (STDIN_FILENO == fd) {
-        if (_console_handle != NULL) {
-            const HANDLE in = _console_handle;
-            _console_handle = NULL;  // clear state
+void stdin_raw_restore() {
+    if (_console_handle != NULL) {
+        const HANDLE in = _console_handle;
+        _console_handle = NULL;  // clear state
 
-            if (!SetConsoleMode(in, _old_console_mode)) {
-                // This really should not fail.
-                D("stdin_raw_restore: SetConsoleMode() failure, error %ld\n",
-                    GetLastError());
-            }
+        if (!SetConsoleMode(in, _old_console_mode)) {
+            // This really should not fail.
+            D("stdin_raw_restore: SetConsoleMode() failed: %s",
+              SystemErrorCodeToString(GetLastError()).c_str());
         }
     }
 }
 
-// Called by 'adb shell' command to read from stdin.
+// Called by 'adb shell' and 'adb exec-in' to read from stdin.
 int unix_read(int fd, void* buf, size_t len) {
     if ((fd == STDIN_FILENO) && (_console_handle != NULL)) {
         // If it is a request to read from stdin, and stdin_raw_init() has been
@@ -3045,9 +3359,584 @@ int unix_read(int fd, void* buf, size_t len) {
         // terminal.
         return _console_read(_console_handle, buf, len);
     } else {
+        // On older versions of Windows (definitely 7, definitely not 10),
+        // ReadConsole() with a size >= 31367 fails, so if |fd| is a console
+        // we need to limit the read size.
+        if (len > 4096 && unix_isatty(fd)) {
+            len = 4096;
+        }
         // Just call into C Runtime which can read from pipes/files and which
-        // can do LF/CR translation.
+        // can do LF/CR translation (which is overridable with _setmode()).
+        // Undefine the macro that is set in sysdeps.h which bans calls to
+        // plain read() in favor of unix_read() or adb_read().
+#pragma push_macro("read")
 #undef read
         return read(fd, buf, len);
+#pragma pop_macro("read")
+    }
+}
+
+/**************************************************************************/
+/**************************************************************************/
+/*****                                                                *****/
+/*****      Unicode support                                           *****/
+/*****                                                                *****/
+/**************************************************************************/
+/**************************************************************************/
+
+// This implements support for using files with Unicode filenames and for
+// outputting Unicode text to a Win32 console window. This is inspired from
+// http://utf8everywhere.org/.
+//
+// Background
+// ----------
+//
+// On POSIX systems, to deal with files with Unicode filenames, just pass UTF-8
+// filenames to APIs such as open(). This works because filenames are largely
+// opaque 'cookies' (perhaps excluding path separators).
+//
+// On Windows, the native file APIs such as CreateFileW() take 2-byte wchar_t
+// UTF-16 strings. There is an API, CreateFileA() that takes 1-byte char
+// strings, but the strings are in the ANSI codepage and not UTF-8. (The
+// CreateFile() API is really just a macro that adds the W/A based on whether
+// the UNICODE preprocessor symbol is defined).
+//
+// Options
+// -------
+//
+// Thus, to write a portable program, there are a few options:
+//
+// 1. Write the program with wchar_t filenames (wchar_t path[256];).
+//    For Windows, just call CreateFileW(). For POSIX, write a wrapper openW()
+//    that takes a wchar_t string, converts it to UTF-8 and then calls the real
+//    open() API.
+//
+// 2. Write the program with a TCHAR typedef that is 2 bytes on Windows and
+//    1 byte on POSIX. Make T-* wrappers for various OS APIs and call those,
+//    potentially touching a lot of code.
+//
+// 3. Write the program with a 1-byte char filenames (char path[256];) that are
+//    UTF-8. For POSIX, just call open(). For Windows, write a wrapper that
+//    takes a UTF-8 string, converts it to UTF-16 and then calls the real OS
+//    or C Runtime API.
+//
+// The Choice
+// ----------
+//
+// The code below chooses option 3, the UTF-8 everywhere strategy. It uses
+// android::base::WideToUTF8() which converts UTF-16 to UTF-8. This is used by the
+// NarrowArgs helper class that is used to convert wmain() args into UTF-8
+// args that are passed to main() at the beginning of program startup. We also use
+// android::base::UTF8ToWide() which converts from UTF-8 to UTF-16. This is used to
+// implement wrappers below that call UTF-16 OS and C Runtime APIs.
+//
+// Unicode console output
+// ----------------------
+//
+// The way to output Unicode to a Win32 console window is to call
+// WriteConsoleW() with UTF-16 text. (The user must also choose a proper font
+// such as Lucida Console or Consolas, and in the case of East Asian languages
+// (such as Chinese, Japanese, Korean), the user must go to the Control Panel
+// and change the "system locale" to Chinese, etc., which allows a Chinese, etc.
+// font to be used in console windows.)
+//
+// The problem is getting the C Runtime to make fprintf and related APIs call
+// WriteConsoleW() under the covers. The C Runtime API, _setmode() sounds
+// promising, but the various modes have issues:
+//
+// 1. _setmode(_O_TEXT) (the default) does not use WriteConsoleW() so UTF-8 and
+//    UTF-16 do not display properly.
+// 2. _setmode(_O_BINARY) does not use WriteConsoleW() and the text comes out
+//    totally wrong.
+// 3. _setmode(_O_U8TEXT) seems to cause the C Runtime _invalid_parameter
+//    handler to be called (upon a later I/O call), aborting the process.
+// 4. _setmode(_O_U16TEXT) and _setmode(_O_WTEXT) cause non-wide printf/fprintf
+//    to output nothing.
+//
+// So the only solution is to write our own adb_fprintf() that converts UTF-8
+// to UTF-16 and then calls WriteConsoleW().
+
+
+// Constructor for helper class to convert wmain() UTF-16 args to UTF-8 to
+// be passed to main().
+NarrowArgs::NarrowArgs(const int argc, wchar_t** const argv) {
+    narrow_args = new char*[argc + 1];
+
+    for (int i = 0; i < argc; ++i) {
+        std::string arg_narrow;
+        if (!android::base::WideToUTF8(argv[i], &arg_narrow)) {
+            fatal_errno("cannot convert argument from UTF-16 to UTF-8");
+        }
+        narrow_args[i] = strdup(arg_narrow.c_str());
+    }
+    narrow_args[argc] = nullptr;   // terminate
+}
+
+NarrowArgs::~NarrowArgs() {
+    if (narrow_args != nullptr) {
+        for (char** argp = narrow_args; *argp != nullptr; ++argp) {
+            free(*argp);
+        }
+        delete[] narrow_args;
+        narrow_args = nullptr;
+    }
+}
+
+int unix_open(const char* path, int options, ...) {
+    std::wstring path_wide;
+    if (!android::base::UTF8ToWide(path, &path_wide)) {
+        return -1;
+    }
+    if ((options & O_CREAT) == 0) {
+        return _wopen(path_wide.c_str(), options);
+    } else {
+        int      mode;
+        va_list  args;
+        va_start(args, options);
+        mode = va_arg(args, int);
+        va_end(args);
+        return _wopen(path_wide.c_str(), options, mode);
+    }
+}
+
+// Version of stat() that takes a UTF-8 path.
+int adb_stat(const char* path, struct adb_stat* s) {
+#pragma push_macro("wstat")
+// This definition of wstat seems to be missing from <sys/stat.h>.
+#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+#ifdef _USE_32BIT_TIME_T
+#define wstat _wstat32i64
+#else
+#define wstat _wstat64
+#endif
+#else
+// <sys/stat.h> has a function prototype for wstat() that should be available.
+#endif
+
+    std::wstring path_wide;
+    if (!android::base::UTF8ToWide(path, &path_wide)) {
+        return -1;
+    }
+
+    return wstat(path_wide.c_str(), s);
+
+#pragma pop_macro("wstat")
+}
+
+// Version of opendir() that takes a UTF-8 path.
+DIR* adb_opendir(const char* path) {
+    std::wstring path_wide;
+    if (!android::base::UTF8ToWide(path, &path_wide)) {
+        return nullptr;
+    }
+
+    // Just cast _WDIR* to DIR*. This doesn't work if the caller reads any of
+    // the fields, but right now all the callers treat the structure as
+    // opaque.
+    return reinterpret_cast<DIR*>(_wopendir(path_wide.c_str()));
+}
+
+// Version of readdir() that returns UTF-8 paths.
+struct dirent* adb_readdir(DIR* dir) {
+    _WDIR* const wdir = reinterpret_cast<_WDIR*>(dir);
+    struct _wdirent* const went = _wreaddir(wdir);
+    if (went == nullptr) {
+        return nullptr;
+    }
+
+    // Convert from UTF-16 to UTF-8.
+    std::string name_utf8;
+    if (!android::base::WideToUTF8(went->d_name, &name_utf8)) {
+        return nullptr;
+    }
+
+    // Cast the _wdirent* to dirent* and overwrite the d_name field (which has
+    // space for UTF-16 wchar_t's) with UTF-8 char's.
+    struct dirent* ent = reinterpret_cast<struct dirent*>(went);
+
+    if (name_utf8.length() + 1 > sizeof(went->d_name)) {
+        // Name too big to fit in existing buffer.
+        errno = ENOMEM;
+        return nullptr;
+    }
+
+    // Note that sizeof(_wdirent::d_name) is bigger than sizeof(dirent::d_name)
+    // because _wdirent contains wchar_t instead of char. So even if name_utf8
+    // can fit in _wdirent::d_name, the resulting dirent::d_name field may be
+    // bigger than the caller expects because they expect a dirent structure
+    // which has a smaller d_name field. Ignore this since the caller should be
+    // resilient.
+
+    // Rewrite the UTF-16 d_name field to UTF-8.
+    strcpy(ent->d_name, name_utf8.c_str());
+
+    return ent;
+}
+
+// Version of closedir() to go with our version of adb_opendir().
+int adb_closedir(DIR* dir) {
+    return _wclosedir(reinterpret_cast<_WDIR*>(dir));
+}
+
+// Version of unlink() that takes a UTF-8 path.
+int adb_unlink(const char* path) {
+    std::wstring wpath;
+    if (!android::base::UTF8ToWide(path, &wpath)) {
+        return -1;
+    }
+
+    int  rc = _wunlink(wpath.c_str());
+
+    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 = _wchmod(wpath.c_str(), _S_IREAD | _S_IWRITE);
+        if (rc == 0)
+            rc = _wunlink(wpath.c_str());
+    }
+    return rc;
+}
+
+// Version of mkdir() that takes a UTF-8 path.
+int adb_mkdir(const std::string& path, int mode) {
+    std::wstring path_wide;
+    if (!android::base::UTF8ToWide(path, &path_wide)) {
+        return -1;
     }
+
+    return _wmkdir(path_wide.c_str());
+}
+
+// Version of utime() that takes a UTF-8 path.
+int adb_utime(const char* path, struct utimbuf* u) {
+    std::wstring path_wide;
+    if (!android::base::UTF8ToWide(path, &path_wide)) {
+        return -1;
+    }
+
+    static_assert(sizeof(struct utimbuf) == sizeof(struct _utimbuf),
+        "utimbuf and _utimbuf should be the same size because they both "
+        "contain the same types, namely time_t");
+    return _wutime(path_wide.c_str(), reinterpret_cast<struct _utimbuf*>(u));
+}
+
+// Version of chmod() that takes a UTF-8 path.
+int adb_chmod(const char* path, int mode) {
+    std::wstring path_wide;
+    if (!android::base::UTF8ToWide(path, &path_wide)) {
+        return -1;
+    }
+
+    return _wchmod(path_wide.c_str(), mode);
+}
+
+// Internal helper function to write UTF-8 bytes to a console. Returns -1
+// on error.
+static int _console_write_utf8(const char* buf, size_t size, FILE* stream,
+                               HANDLE console) {
+    std::wstring output;
+
+    // Try to convert from data that might be UTF-8 to UTF-16, ignoring errors.
+    // Data might not be UTF-8 if the user cat's random data, runs dmesg, etc.
+    // This could throw std::bad_alloc.
+    (void)android::base::UTF8ToWide(buf, size, &output);
+
+    // Note that this does not do \n => \r\n translation because that
+    // doesn't seem necessary for the Windows console. For the Windows
+    // console \r moves to the beginning of the line and \n moves to a new
+    // line.
+
+    // Flush any stream buffering so that our output is afterwards which
+    // makes sense because our call is afterwards.
+    (void)fflush(stream);
+
+    // Write UTF-16 to the console.
+    DWORD written = 0;
+    if (!WriteConsoleW(console, output.c_str(), output.length(), &written,
+                       NULL)) {
+        errno = EIO;
+        return -1;
+    }
+
+    // This is the number of UTF-16 chars written, which might be different
+    // than the number of UTF-8 chars passed in. It doesn't seem practical to
+    // get this count correct.
+    return written;
+}
+
+// Function prototype because attributes cannot be placed on func definitions.
+static int _console_vfprintf(const HANDLE console, FILE* stream,
+                             const char *format, va_list ap)
+    __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 3, 0)));
+
+// Internal function to format a UTF-8 string and write it to a Win32 console.
+// Returns -1 on error.
+static int _console_vfprintf(const HANDLE console, FILE* stream,
+                             const char *format, va_list ap) {
+    std::string output_utf8;
+
+    // Format the string.
+    // This could throw std::bad_alloc.
+    android::base::StringAppendV(&output_utf8, format, ap);
+
+    return _console_write_utf8(output_utf8.c_str(), output_utf8.length(),
+                               stream, console);
+}
+
+// Version of vfprintf() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_vfprintf(FILE *stream, const char *format, va_list ap) {
+    const HANDLE console = _get_console_handle(stream);
+
+    // If there is an associated Win32 console, write to it specially,
+    // otherwise defer to the regular C Runtime, passing it UTF-8.
+    if (console != NULL) {
+        return _console_vfprintf(console, stream, format, ap);
+    } else {
+        // If vfprintf is a macro, undefine it, so we can call the real
+        // C Runtime API.
+#pragma push_macro("vfprintf")
+#undef vfprintf
+        return vfprintf(stream, format, ap);
+#pragma pop_macro("vfprintf")
+    }
+}
+
+// Version of fprintf() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_fprintf(FILE *stream, const char *format, ...) {
+    va_list ap;
+    va_start(ap, format);
+    const int result = adb_vfprintf(stream, format, ap);
+    va_end(ap);
+
+    return result;
+}
+
+// Version of printf() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_printf(const char *format, ...) {
+    va_list ap;
+    va_start(ap, format);
+    const int result = adb_vfprintf(stdout, format, ap);
+    va_end(ap);
+
+    return result;
+}
+
+// Version of fputs() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_fputs(const char* buf, FILE* stream) {
+    // adb_fprintf returns -1 on error, which is conveniently the same as EOF
+    // which fputs (and hence adb_fputs) should return on error.
+    return adb_fprintf(stream, "%s", buf);
+}
+
+// Version of fputc() that takes UTF-8 and can write Unicode to a
+// Windows console.
+int adb_fputc(int ch, FILE* stream) {
+    const int result = adb_fprintf(stream, "%c", ch);
+    if (result <= 0) {
+        // If there was an error, or if nothing was printed (which should be an
+        // error), return an error, which fprintf signifies with EOF.
+        return EOF;
+    }
+    // For success, fputc returns the char, cast to unsigned char, then to int.
+    return static_cast<unsigned char>(ch);
+}
+
+// Internal function to write UTF-8 to a Win32 console. Returns the number of
+// items (of length size) written. On error, returns a short item count or 0.
+static size_t _console_fwrite(const void* ptr, size_t size, size_t nmemb,
+                              FILE* stream, HANDLE console) {
+    // TODO: Note that a Unicode character could be several UTF-8 bytes. But
+    // if we're passed only some of the bytes of a character (for example, from
+    // the network socket for adb shell), we won't be able to convert the char
+    // to a complete UTF-16 char (or surrogate pair), so the output won't look
+    // right.
+    //
+    // To fix this, see libutils/Unicode.cpp for hints on decoding UTF-8.
+    //
+    // For now we ignore this problem because the alternative is that we'd have
+    // to parse UTF-8 and buffer things up (doable). At least this is better
+    // than what we had before -- always incorrect multi-byte UTF-8 output.
+    int result = _console_write_utf8(reinterpret_cast<const char*>(ptr),
+                                     size * nmemb, stream, console);
+    if (result == -1) {
+        return 0;
+    }
+    return result / size;
+}
+
+// Version of fwrite() that takes UTF-8 and can write Unicode to a
+// Windows console.
+size_t adb_fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream) {
+    const HANDLE console = _get_console_handle(stream);
+
+    // If there is an associated Win32 console, write to it specially,
+    // otherwise defer to the regular C Runtime, passing it UTF-8.
+    if (console != NULL) {
+        return _console_fwrite(ptr, size, nmemb, stream, console);
+    } else {
+        // If fwrite is a macro, undefine it, so we can call the real
+        // C Runtime API.
+#pragma push_macro("fwrite")
+#undef fwrite
+        return fwrite(ptr, size, nmemb, stream);
+#pragma pop_macro("fwrite")
+    }
+}
+
+// Version of fopen() that takes a UTF-8 filename and can access a file with
+// a Unicode filename.
+FILE* adb_fopen(const char* path, const char* mode) {
+    std::wstring path_wide;
+    if (!android::base::UTF8ToWide(path, &path_wide)) {
+        return nullptr;
+    }
+
+    std::wstring mode_wide;
+    if (!android::base::UTF8ToWide(mode, &mode_wide)) {
+        return nullptr;
+    }
+
+    return _wfopen(path_wide.c_str(), mode_wide.c_str());
+}
+
+// Return a lowercase version of the argument. Uses C Runtime tolower() on
+// each byte which is not UTF-8 aware, and theoretically uses the current C
+// Runtime locale (which in practice is not changed, so this becomes a ASCII
+// conversion).
+static std::string ToLower(const std::string& anycase) {
+    // copy string
+    std::string str(anycase);
+    // transform the copy
+    std::transform(str.begin(), str.end(), str.begin(), tolower);
+    return str;
+}
+
+extern "C" int main(int argc, char** argv);
+
+// Link with -municode to cause this wmain() to be used as the program
+// entrypoint. It will convert the args from UTF-16 to UTF-8 and call the
+// regular main() with UTF-8 args.
+extern "C" int wmain(int argc, wchar_t **argv) {
+    // Convert args from UTF-16 to UTF-8 and pass that to main().
+    NarrowArgs narrow_args(argc, argv);
+    return main(argc, narrow_args.data());
+}
+
+// Shadow UTF-8 environment variable name/value pairs that are created from
+// _wenviron the first time that adb_getenv() is called. Note that this is not
+// currently updated if putenv, setenv, unsetenv are called. Note that no
+// thread synchronization is done, but we're called early enough in
+// single-threaded startup that things work ok.
+static auto& g_environ_utf8 = *new std::unordered_map<std::string, char*>();
+
+// Make sure that shadow UTF-8 environment variables are setup.
+static void _ensure_env_setup() {
+    // If some name/value pairs exist, then we've already done the setup below.
+    if (g_environ_utf8.size() != 0) {
+        return;
+    }
+
+    if (_wenviron == nullptr) {
+        // If _wenviron is null, then -municode probably wasn't used. That
+        // linker flag will cause the entry point to setup _wenviron. It will
+        // also require an implementation of wmain() (which we provide above).
+        fatal("_wenviron is not set, did you link with -municode?");
+    }
+
+    // Read name/value pairs from UTF-16 _wenviron and write new name/value
+    // pairs to UTF-8 g_environ_utf8. Note that it probably does not make sense
+    // to use the D() macro here because that tracing only works if the
+    // ADB_TRACE environment variable is setup, but that env var can't be read
+    // until this code completes.
+    for (wchar_t** env = _wenviron; *env != nullptr; ++env) {
+        wchar_t* const equal = wcschr(*env, L'=');
+        if (equal == nullptr) {
+            // Malformed environment variable with no equal sign. Shouldn't
+            // really happen, but we should be resilient to this.
+            continue;
+        }
+
+        // If we encounter an error converting UTF-16, don't error-out on account of a single env
+        // var because the program might never even read this particular variable.
+        std::string name_utf8;
+        if (!android::base::WideToUTF8(*env, equal - *env, &name_utf8)) {
+            continue;
+        }
+
+        // Store lowercase name so that we can do case-insensitive searches.
+        name_utf8 = ToLower(name_utf8);
+
+        std::string value_utf8;
+        if (!android::base::WideToUTF8(equal + 1, &value_utf8)) {
+            continue;
+        }
+
+        char* const value_dup = strdup(value_utf8.c_str());
+
+        // Don't overwrite a previus env var with the same name. In reality,
+        // the system probably won't let two env vars with the same name exist
+        // in _wenviron.
+        g_environ_utf8.insert({name_utf8, value_dup});
+    }
+}
+
+// Version of getenv() that takes a UTF-8 environment variable name and
+// retrieves a UTF-8 value. Case-insensitive to match getenv() on Windows.
+char* adb_getenv(const char* name) {
+    _ensure_env_setup();
+
+    // Case-insensitive search by searching for lowercase name in a map of
+    // lowercase names.
+    const auto it = g_environ_utf8.find(ToLower(std::string(name)));
+    if (it == g_environ_utf8.end()) {
+        return nullptr;
+    }
+
+    return it->second;
+}
+
+// Version of getcwd() that returns the current working directory in UTF-8.
+char* adb_getcwd(char* buf, int size) {
+    wchar_t* wbuf = _wgetcwd(nullptr, 0);
+    if (wbuf == nullptr) {
+        return nullptr;
+    }
+
+    std::string buf_utf8;
+    const bool narrow_result = android::base::WideToUTF8(wbuf, &buf_utf8);
+    free(wbuf);
+    wbuf = nullptr;
+
+    if (!narrow_result) {
+        return nullptr;
+    }
+
+    // If size was specified, make sure all the chars will fit.
+    if (size != 0) {
+        if (size < static_cast<int>(buf_utf8.length() + 1)) {
+            errno = ERANGE;
+            return nullptr;
+        }
+    }
+
+    // If buf was not specified, allocate storage.
+    if (buf == nullptr) {
+        if (size == 0) {
+            size = buf_utf8.length() + 1;
+        }
+        buf = reinterpret_cast<char*>(malloc(size));
+        if (buf == nullptr) {
+            return nullptr;
+        }
+    }
+
+    // Destination buffer was allocated with enough space, or we've already
+    // checked an existing buffer size for enough space.
+    strcpy(buf, buf_utf8.c_str());
+
+    return buf;
 }
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
new file mode 100755 (executable)
index 0000000..55b5eb4
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "sysdeps.h"
+
+#include "base/test_utils.h"
+
+TEST(sysdeps_win32, adb_getenv) {
+    // Insert all test env vars before first call to adb_getenv() which will
+    // read the env var block only once.
+    ASSERT_EQ(0, _putenv("SYSDEPS_WIN32_TEST_UPPERCASE=1"));
+    ASSERT_EQ(0, _putenv("sysdeps_win32_test_lowercase=2"));
+    ASSERT_EQ(0, _putenv("Sysdeps_Win32_Test_MixedCase=3"));
+
+    // UTF-16 value
+    ASSERT_EQ(0, _wputenv(L"SYSDEPS_WIN32_TEST_UNICODE=\u00a1\u0048\u006f\u006c"
+                          L"\u0061\u0021\u03b1\u03b2\u03b3\u0061\u006d\u0062"
+                          L"\u0075\u006c\u014d\u043f\u0440\u0438\u0432\u0435"
+                          L"\u0442"));
+
+    // Search for non-existant env vars.
+    EXPECT_STREQ(nullptr, adb_getenv("SYSDEPS_WIN32_TEST_NONEXISTANT"));
+
+    // Search for existing env vars.
+
+    // There is no test for an env var with a value of a zero-length string
+    // because _putenv() does not support inserting such an env var.
+
+    // Search for env var that is uppercase.
+    EXPECT_STREQ("1", adb_getenv("SYSDEPS_WIN32_TEST_UPPERCASE"));
+    EXPECT_STREQ("1", adb_getenv("sysdeps_win32_test_uppercase"));
+    EXPECT_STREQ("1", adb_getenv("Sysdeps_Win32_Test_Uppercase"));
+
+    // Search for env var that is lowercase.
+    EXPECT_STREQ("2", adb_getenv("SYSDEPS_WIN32_TEST_LOWERCASE"));
+    EXPECT_STREQ("2", adb_getenv("sysdeps_win32_test_lowercase"));
+    EXPECT_STREQ("2", adb_getenv("Sysdeps_Win32_Test_Lowercase"));
+
+    // Search for env var that is mixed-case.
+    EXPECT_STREQ("3", adb_getenv("SYSDEPS_WIN32_TEST_MIXEDCASE"));
+    EXPECT_STREQ("3", adb_getenv("sysdeps_win32_test_mixedcase"));
+    EXPECT_STREQ("3", adb_getenv("Sysdeps_Win32_Test_MixedCase"));
+
+    // Check that UTF-16 was converted to UTF-8.
+    EXPECT_STREQ("\xc2\xa1\x48\x6f\x6c\x61\x21\xce\xb1\xce\xb2\xce\xb3\x61\x6d"
+                 "\x62\x75\x6c\xc5\x8d\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5"
+                 "\xd1\x82",
+                 adb_getenv("SYSDEPS_WIN32_TEST_UNICODE"));
+
+    // Check an env var that should always be set.
+    const char* path_val = adb_getenv("PATH");
+    EXPECT_NE(nullptr, path_val);
+    if (path_val != nullptr) {
+        EXPECT_GT(strlen(path_val), 0);
+    }
+}
+
+void TestAdbStrError(int err, const char* expected) {
+    errno = 12345;
+    const char* result = adb_strerror(err);
+    // Check that errno is not overwritten.
+    EXPECT_EQ(12345, errno);
+    EXPECT_STREQ(expected, result);
+}
+
+TEST(sysdeps_win32, adb_strerror) {
+    // Test an error code that should not have a mapped string. Use an error
+    // code that is not used by the internal implementation of adb_strerror().
+    TestAdbStrError(-2, "Unknown error");
+    // adb_strerror() uses -1 internally, so test that it can still be passed
+    // as a parameter.
+    TestAdbStrError(-1, "Unknown error");
+    // Test very big, positive unknown error.
+    TestAdbStrError(1000000, "Unknown error");
+    // Test success case.
+    TestAdbStrError(0, "No error");
+    // Test error that regular strerror() should have a string for.
+    TestAdbStrError(EPERM, "Operation not permitted");
+    // Test error that regular strerror() doesn't have a string for, but that
+    // adb_strerror() returns.
+    TestAdbStrError(ECONNRESET, "Connection reset by peer");
+}
+
+TEST(sysdeps_win32, unix_isatty) {
+    // stdin and stdout should be consoles. Use CONIN$ and CONOUT$ special files
+    // so that we can test this even if stdin/stdout have been redirected. Read
+    // permissions are required for unix_isatty().
+    int conin_fd = unix_open("CONIN$", O_RDONLY);
+    int conout_fd = unix_open("CONOUT$", O_RDWR);
+    for (const int fd : {conin_fd, conout_fd}) {
+        EXPECT_TRUE(fd >= 0);
+        EXPECT_EQ(1, unix_isatty(fd));
+        EXPECT_EQ(0, unix_close(fd));
+    }
+
+    // nul returns 1 from isatty(), make sure unix_isatty() corrects that.
+    for (auto flags : {O_RDONLY, O_RDWR}) {
+        int nul_fd = unix_open("nul", flags);
+        EXPECT_TRUE(nul_fd >= 0);
+        EXPECT_EQ(0, unix_isatty(nul_fd));
+        EXPECT_EQ(0, unix_close(nul_fd));
+    }
+
+    // Check a real file, both read-write and read-only.
+    TemporaryFile temp_file;
+    EXPECT_TRUE(temp_file.fd >= 0);
+    EXPECT_EQ(0, unix_isatty(temp_file.fd));
+
+    int temp_file_ro_fd = unix_open(temp_file.path, O_RDONLY);
+    EXPECT_TRUE(temp_file_ro_fd >= 0);
+    EXPECT_EQ(0, unix_isatty(temp_file_ro_fd));
+    EXPECT_EQ(0, unix_close(temp_file_ro_fd));
+
+    // Check a real OS pipe.
+    int pipe_fds[2];
+    EXPECT_EQ(0, _pipe(pipe_fds, 64, _O_BINARY));
+    EXPECT_EQ(0, unix_isatty(pipe_fds[0]));
+    EXPECT_EQ(0, unix_isatty(pipe_fds[1]));
+    EXPECT_EQ(0, _close(pipe_fds[0]));
+    EXPECT_EQ(0, _close(pipe_fds[1]));
+
+    // Make sure an invalid FD is handled correctly.
+    EXPECT_EQ(0, unix_isatty(-1));
+}
diff --git a/adb/test_adb.py b/adb/test_adb.py
new file mode 100644 (file)
index 0000000..0f1b034
--- /dev/null
@@ -0,0 +1,221 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Tests for the adb program itself.
+
+This differs from things in test_device.py in that there is no API for these
+things. Most of these tests involve specific error messages or the help text.
+"""
+from __future__ import print_function
+
+import contextlib
+import os
+import random
+import socket
+import struct
+import subprocess
+import threading
+import unittest
+
+import adb
+
+
+class NonApiTest(unittest.TestCase):
+    """Tests for ADB that aren't a part of the AndroidDevice API."""
+
+    def test_help(self):
+        """Make sure we get _something_ out of help."""
+        out = subprocess.check_output(
+            ['adb', 'help'], stderr=subprocess.STDOUT)
+        self.assertGreater(len(out), 0)
+
+    def test_version(self):
+        """Get a version number out of the output of adb."""
+        lines = subprocess.check_output(['adb', 'version']).splitlines()
+        version_line = lines[0]
+        self.assertRegexpMatches(
+            version_line, r'^Android Debug Bridge version \d+\.\d+\.\d+$')
+        if len(lines) == 2:
+            # Newer versions of ADB have a second line of output for the
+            # version that includes a specific revision (git SHA).
+            revision_line = lines[1]
+            self.assertRegexpMatches(
+                revision_line, r'^Revision [0-9a-f]{12}-android$')
+
+    def test_tcpip_error_messages(self):
+        p = subprocess.Popen(['adb', 'tcpip'], stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+        out, _ = p.communicate()
+        self.assertEqual(1, p.returncode)
+        self.assertIn('help message', out)
+
+        p = subprocess.Popen(['adb', 'tcpip', 'foo'], stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+        out, _ = p.communicate()
+        self.assertEqual(1, p.returncode)
+        self.assertIn('error', out)
+
+    # Helper method that reads a pipe until it is closed, then sets the event.
+    def _read_pipe_and_set_event(self, pipe, event):
+        x = pipe.read()
+        event.set()
+
+    # Test that launch_server() does not let the adb server inherit
+    # stdin/stdout/stderr handles which can cause callers of adb.exe to hang.
+    # This test also runs fine on unix even though the impetus is an issue
+    # unique to Windows.
+    def test_handle_inheritance(self):
+        # This test takes 5 seconds to run on Windows: if there is no adb server
+        # running on the the port used below, adb kill-server tries to make a
+        # TCP connection to a closed port and that takes 1 second on Windows;
+        # adb start-server does the same TCP connection which takes another
+        # second, and it waits 3 seconds after starting the server.
+
+        # Start adb client with redirected stdin/stdout/stderr to check if it
+        # passes those redirections to the adb server that it starts. To do
+        # this, run an instance of the adb server on a non-default port so we
+        # don't conflict with a pre-existing adb server that may already be
+        # setup with adb TCP/emulator connections. If there is a pre-existing
+        # adb server, this also tests whether multiple instances of the adb
+        # server conflict on adb.log.
+
+        port = 5038
+        # Kill any existing server on this non-default port.
+        subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+                                stderr=subprocess.STDOUT)
+
+        try:
+            # Run the adb client and have it start the adb server.
+            p = subprocess.Popen(['adb', '-P', str(port), 'start-server'],
+                                 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE)
+
+            # Start threads that set events when stdout/stderr are closed.
+            stdout_event = threading.Event()
+            stdout_thread = threading.Thread(
+                    target=self._read_pipe_and_set_event,
+                    args=(p.stdout, stdout_event))
+            stdout_thread.daemon = True
+            stdout_thread.start()
+
+            stderr_event = threading.Event()
+            stderr_thread = threading.Thread(
+                    target=self._read_pipe_and_set_event,
+                    args=(p.stderr, stderr_event))
+            stderr_thread.daemon = True
+            stderr_thread.start()
+
+            # Wait for the adb client to finish. Once that has occurred, if
+            # stdin/stderr/stdout are still open, it must be open in the adb
+            # server.
+            p.wait()
+
+            # Try to write to stdin which we expect is closed. If it isn't
+            # closed, we should get an IOError. If we don't get an IOError,
+            # stdin must still be open in the adb server. The adb client is
+            # probably letting the adb server inherit stdin which would be
+            # wrong.
+            with self.assertRaises(IOError):
+                p.stdin.write('x')
+
+            # Wait a few seconds for stdout/stderr to be closed (in the success
+            # case, this won't wait at all). If there is a timeout, that means
+            # stdout/stderr were not closed and and they must be open in the adb
+            # server, suggesting that the adb client is letting the adb server
+            # inherit stdout/stderr which would be wrong.
+            self.assertTrue(stdout_event.wait(5), "adb stdout not closed")
+            self.assertTrue(stderr_event.wait(5), "adb stderr not closed")
+        finally:
+            # If we started a server, kill it.
+            subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
+                                    stderr=subprocess.STDOUT)
+
+    # Use SO_LINGER to cause TCP RST segment to be sent on socket close.
+    def _reset_socket_on_close(self, sock):
+        # The linger structure is two shorts on Windows, but two ints on Unix.
+        linger_format = 'hh' if os.name == 'nt' else 'ii'
+        l_onoff = 1
+        l_linger = 0
+
+        sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+                        struct.pack(linger_format, l_onoff, l_linger))
+        # Verify that we set the linger structure properly by retrieving it.
+        linger = sock.getsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 16)
+        self.assertEqual((l_onoff, l_linger),
+                         struct.unpack_from(linger_format, linger))
+
+    def test_emu_kill(self):
+        """Ensure that adb emu kill works.
+
+        Bug: https://code.google.com/p/android/issues/detail?id=21021
+        """
+        port = 12345
+
+        with contextlib.closing(
+                socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
+            # Use SO_REUSEADDR so subsequent runs of the test can grab the port
+            # even if it is in TIME_WAIT.
+            listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+            listener.bind(('127.0.0.1', port))
+            listener.listen(4)
+
+            # Now that listening has started, start adb emu kill, telling it to
+            # connect to our mock emulator.
+            p = subprocess.Popen(
+                ['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'],
+                stderr=subprocess.STDOUT)
+
+            accepted_connection, addr = listener.accept()
+            with contextlib.closing(accepted_connection) as conn:
+                # If WSAECONNABORTED (10053) is raised by any socket calls,
+                # then adb probably isn't reading the data that we sent it.
+                conn.sendall('Android Console: type \'help\' for a list ' +
+                                'of commands\r\n')
+                conn.sendall('OK\r\n')
+
+                with contextlib.closing(conn.makefile()) as f:
+                    self.assertEqual('kill\n', f.readline())
+                    self.assertEqual('quit\n', f.readline())
+
+                conn.sendall('OK: killing emulator, bye bye\r\n')
+
+                # Use SO_LINGER to send TCP RST segment to test whether adb
+                # ignores WSAECONNRESET on Windows. This happens with the
+                # real emulator because it just calls exit() without closing
+                # the socket or calling shutdown(SD_SEND). At process
+                # termination, Windows sends a TCP RST segment for every
+                # open socket that shutdown(SD_SEND) wasn't used on.
+                self._reset_socket_on_close(conn)
+
+            # Wait for adb to finish, so we can check return code.
+            p.communicate()
+
+            # If this fails, adb probably isn't ignoring WSAECONNRESET when
+            # reading the response from the adb emu kill command (on Windows).
+            self.assertEqual(0, p.returncode)
+
+
+def main():
+    random.seed(0)
+    if len(adb.get_devices()) > 0:
+        suite = unittest.TestLoader().loadTestsFromName(__name__)
+        unittest.TextTestRunner(verbosity=3).run(suite)
+    else:
+        print('Test suite must be run with attached devices')
+
+
+if __name__ == '__main__':
+    main()
index 3e823e9..6f658f6 100644 (file)
@@ -1,12 +1,13 @@
 // TODO: replace this with a shell/python script.
 
 /* a simple test program, connects to ADB server, and opens a track-devices session */
+#include <errno.h>
+#include <memory.h>
 #include <netdb.h>
-#include <sys/socket.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <errno.h>
-#include <memory.h>
+#include <sys/socket.h>
+#include <unistd.h>
 
 #include <base/file.h>
 
@@ -62,7 +63,7 @@ int main(int argc, char* argv[]) {
         if (!android::base::ReadFully(s, buffer, len))
             panic("could not read data");
 
-        printf( "received header %.*s (%d bytes):\n%.*s", 4, head, len, len, buffer );
+        printf( "received header %.*s (%d bytes):\n%.*s----\n", 4, head, len, len, buffer );
     }
     close(s);
 }
diff --git a/adb/tests/test_adb.py b/adb/tests/test_adb.py
deleted file mode 100755 (executable)
index 0ff87d2..0000000
+++ /dev/null
@@ -1,439 +0,0 @@
-#!/usr/bin/env python2
-"""Simple conformance test for adb.
-
-This script will use the available adb in path and run simple
-tests that attempt to touch all accessible attached devices.
-"""
-import hashlib
-import os
-import pipes
-import random
-import re
-import shlex
-import subprocess
-import sys
-import tempfile
-import unittest
-
-
-def trace(cmd):
-    """Print debug message if tracing enabled."""
-    if False:
-        print >> sys.stderr, cmd
-
-
-def call(cmd_str):
-    """Run process and return output tuple (stdout, stderr, ret code)."""
-    trace(cmd_str)
-    process = subprocess.Popen(shlex.split(cmd_str),
-                               stdout=subprocess.PIPE,
-                               stderr=subprocess.PIPE)
-    stdout, stderr = process.communicate()
-    return stdout, stderr, process.returncode
-
-
-def call_combined(cmd_str):
-    """Run process and return output tuple (stdout+stderr, ret code)."""
-    trace(cmd_str)
-    process = subprocess.Popen(shlex.split(cmd_str),
-                               stdout=subprocess.PIPE,
-                               stderr=subprocess.STDOUT)
-    stdout, _ = process.communicate()
-    return stdout, process.returncode
-
-
-def call_checked(cmd_str):
-    """Run process and get stdout+stderr, raise an exception on trouble."""
-    trace(cmd_str)
-    return subprocess.check_output(shlex.split(cmd_str),
-                                   stderr=subprocess.STDOUT)
-
-
-def call_checked_list(cmd_str):
-    return call_checked(cmd_str).split('\n')
-
-
-def call_checked_list_skip(cmd_str):
-    out_list = call_checked_list(cmd_str)
-
-    def is_init_line(line):
-        if (len(line) >= 3) and (line[0] == "*") and (line[-2] == "*"):
-            return True
-        else:
-            return False
-
-    return [line for line in out_list if not is_init_line(line)]
-
-
-def get_device_list():
-    output = call_checked_list_skip("adb devices")
-    dev_list = []
-    for line in output[1:]:
-        if line.strip() == "":
-            continue
-        device, _ = line.split()
-        dev_list.append(device)
-    return dev_list
-
-
-def get_attached_device_count():
-    return len(get_device_list())
-
-
-def compute_md5(string):
-    hsh = hashlib.md5()
-    hsh.update(string)
-    return hsh.hexdigest()
-
-
-class HostFile(object):
-    def __init__(self, handle, md5):
-        self.handle = handle
-        self.md5 = md5
-        self.full_path = handle.name
-        self.base_name = os.path.basename(self.full_path)
-
-
-class DeviceFile(object):
-    def __init__(self, md5, full_path):
-        self.md5 = md5
-        self.full_path = full_path
-        self.base_name = os.path.basename(self.full_path)
-
-
-def make_random_host_files(in_dir, num_files, rand_size=True):
-    files = {}
-    min_size = 1 * (1 << 10)
-    max_size = 16 * (1 << 10)
-    fixed_size = min_size
-
-    for _ in range(num_files):
-        file_handle = tempfile.NamedTemporaryFile(dir=in_dir)
-
-        if rand_size:
-            size = random.randrange(min_size, max_size, 1024)
-        else:
-            size = fixed_size
-        rand_str = os.urandom(size)
-        file_handle.write(rand_str)
-        file_handle.flush()
-
-        md5 = compute_md5(rand_str)
-        files[file_handle.name] = HostFile(file_handle, md5)
-    return files
-
-
-def make_random_device_files(adb, in_dir, num_files, rand_size=True):
-    files = {}
-    min_size = 1 * (1 << 10)
-    max_size = 16 * (1 << 10)
-    fixed_size = min_size
-
-    for i in range(num_files):
-        if rand_size:
-            size = random.randrange(min_size, max_size, 1024)
-        else:
-            size = fixed_size
-
-        base_name = "device_tmpfile" + str(i)
-        full_path = in_dir + "/" + base_name
-
-        adb.shell("dd if=/dev/urandom of={} bs={} count=1".format(full_path,
-                                                                  size))
-        dev_md5, _ = adb.shell("md5sum {}".format(full_path)).split()
-
-        files[full_path] = DeviceFile(dev_md5, full_path)
-    return files
-
-
-class AdbWrapper(object):
-    """Convenience wrapper object for the adb command."""
-    def __init__(self, device=None, out_dir=None):
-        self.device = device
-        self.out_dir = out_dir
-        self.adb_cmd = "adb "
-        if self.device:
-            self.adb_cmd += "-s {} ".format(device)
-        if self.out_dir:
-            self.adb_cmd += "-p {} ".format(out_dir)
-
-    def shell(self, cmd):
-        return call_checked(self.adb_cmd + "shell " + cmd)
-
-    def shell_nocheck(self, cmd):
-        return call_combined(self.adb_cmd + "shell " + cmd)
-
-    def install(self, filename):
-        return call_checked(self.adb_cmd + "install {}".format(pipes.quote(filename)))
-
-    def push(self, local, remote):
-        return call_checked(self.adb_cmd + "push {} {}".format(local, remote))
-
-    def pull(self, remote, local):
-        return call_checked(self.adb_cmd + "pull {} {}".format(remote, local))
-
-    def sync(self, directory=""):
-        return call_checked(self.adb_cmd + "sync {}".format(directory))
-
-    def forward(self, local, remote):
-        return call_checked(self.adb_cmd + "forward {} {}".format(local,
-                                                                  remote))
-
-    def tcpip(self, port):
-        return call_checked(self.adb_cmd + "tcpip {}".format(port))
-
-    def usb(self):
-        return call_checked(self.adb_cmd + "usb")
-
-    def root(self):
-        return call_checked(self.adb_cmd + "root")
-
-    def unroot(self):
-        return call_checked(self.adb_cmd + "unroot")
-
-    def forward_remove(self, local):
-        return call_checked(self.adb_cmd + "forward --remove {}".format(local))
-
-    def forward_remove_all(self):
-        return call_checked(self.adb_cmd + "forward --remove-all")
-
-    def connect(self, host):
-        return call_checked(self.adb_cmd + "connect {}".format(host))
-
-    def disconnect(self, host):
-        return call_checked(self.adb_cmd + "disconnect {}".format(host))
-
-    def reverse(self, remote, local):
-        return call_checked(self.adb_cmd + "reverse {} {}".format(remote,
-                                                                  local))
-
-    def reverse_remove_all(self):
-        return call_checked(self.adb_cmd + "reverse --remove-all")
-
-    def reverse_remove(self, remote):
-        return call_checked(
-            self.adb_cmd + "reverse --remove {}".format(remote))
-
-    def wait(self):
-        return call_checked(self.adb_cmd + "wait-for-device")
-
-
-class AdbBasic(unittest.TestCase):
-    def test_shell(self):
-        """Check that we can at least cat a file."""
-        adb = AdbWrapper()
-        out = adb.shell("cat /proc/uptime")
-        self.assertEqual(len(out.split()), 2)
-        self.assertGreater(float(out.split()[0]), 0.0)
-        self.assertGreater(float(out.split()[1]), 0.0)
-
-    def test_help(self):
-        """Make sure we get _something_ out of help."""
-        out = call_checked("adb help")
-        self.assertTrue(len(out) > 0)
-
-    def test_version(self):
-        """Get a version number out of the output of adb."""
-        out = call_checked("adb version").split()
-        version_num = False
-        for item in out:
-            if re.match(r"[\d+\.]*\d", item):
-                version_num = True
-        self.assertTrue(version_num)
-
-    def _test_root(self):
-        adb = AdbWrapper()
-        adb.root()
-        adb.wait()
-        self.assertEqual("root", adb.shell("id -un").strip())
-
-    def _test_unroot(self):
-        adb = AdbWrapper()
-        adb.unroot()
-        adb.wait()
-        self.assertEqual("shell", adb.shell("id -un").strip())
-
-    def test_root_unroot(self):
-        """Make sure that adb root and adb unroot work, using id(1)."""
-        adb = AdbWrapper()
-        original_user = adb.shell("id -un").strip()
-        try:
-            if original_user == "root":
-                self._test_unroot()
-                self._test_root()
-            elif original_user == "shell":
-                self._test_root()
-                self._test_unroot()
-        finally:
-            if original_user == "root":
-                adb.root()
-            else:
-                adb.unroot()
-            adb.wait()
-
-    def test_argument_escaping(self):
-        """Make sure that argument escaping is somewhat sane."""
-        adb = AdbWrapper()
-
-        # http://b/19734868
-        # Note that this actually matches ssh(1)'s behavior --- it's
-        # converted to "sh -c echo hello; echo world" which sh interprets
-        # as "sh -c echo" (with an argument to that shell of "hello"),
-        # and then "echo world" back in the first shell.
-        result = adb.shell("sh -c 'echo hello; echo world'").splitlines()
-        self.assertEqual(["", "world"], result)
-        # If you really wanted "hello" and "world", here's what you'd do:
-        result = adb.shell("echo hello\;echo world").splitlines()
-        self.assertEqual(["hello", "world"], result)
-
-        # http://b/15479704
-        self.assertEqual('t', adb.shell("'true && echo t'").strip())
-        self.assertEqual('t', adb.shell("sh -c 'true && echo t'").strip())
-
-        # http://b/20564385
-        self.assertEqual('t', adb.shell("FOO=a BAR=b echo t").strip())
-        self.assertEqual('123Linux', adb.shell("echo -n 123\;uname").strip())
-
-    def test_install_argument_escaping(self):
-        """Make sure that install argument escaping works."""
-        adb = AdbWrapper()
-
-        # http://b/20323053
-        tf = tempfile.NamedTemporaryFile("w", suffix="-text;ls;1.apk")
-        self.assertIn("-text;ls;1.apk", adb.install(tf.name))
-
-        # http://b/3090932
-        tf = tempfile.NamedTemporaryFile("w", suffix="-Live Hold'em.apk")
-        self.assertIn("-Live Hold'em.apk", adb.install(tf.name))
-
-
-class AdbFile(unittest.TestCase):
-    SCRATCH_DIR = "/data/local/tmp"
-    DEVICE_TEMP_FILE = SCRATCH_DIR + "/adb_test_file"
-    DEVICE_TEMP_DIR = SCRATCH_DIR + "/adb_test_dir"
-
-    def test_push(self):
-        """Push a randomly generated file to specified device."""
-        kbytes = 512
-        adb = AdbWrapper()
-        with tempfile.NamedTemporaryFile(mode="w") as tmp:
-            rand_str = os.urandom(1024 * kbytes)
-            tmp.write(rand_str)
-            tmp.flush()
-
-            host_md5 = compute_md5(rand_str)
-            adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_FILE))
-            try:
-                adb.push(local=tmp.name, remote=AdbFile.DEVICE_TEMP_FILE)
-                dev_md5, _ = adb.shell(
-                    "md5sum {}".format(AdbFile.DEVICE_TEMP_FILE)).split()
-                self.assertEqual(host_md5, dev_md5)
-            finally:
-                adb.shell_nocheck("rm {}".format(AdbFile.DEVICE_TEMP_FILE))
-
-    # TODO: write push directory test.
-
-    def test_pull(self):
-        """Pull a randomly generated file from specified device."""
-        kbytes = 512
-        adb = AdbWrapper()
-        adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_FILE))
-        try:
-            adb.shell("dd if=/dev/urandom of={} bs=1024 count={}".format(
-                AdbFile.DEVICE_TEMP_FILE, kbytes))
-            dev_md5, _ = adb.shell(
-                "md5sum {}".format(AdbFile.DEVICE_TEMP_FILE)).split()
-
-            with tempfile.NamedTemporaryFile(mode="w") as tmp_write:
-                adb.pull(remote=AdbFile.DEVICE_TEMP_FILE, local=tmp_write.name)
-                with open(tmp_write.name) as tmp_read:
-                    host_contents = tmp_read.read()
-                    host_md5 = compute_md5(host_contents)
-                self.assertEqual(dev_md5, host_md5)
-        finally:
-            adb.shell_nocheck("rm {}".format(AdbFile.DEVICE_TEMP_FILE))
-
-    def test_pull_dir(self):
-        """Pull a randomly generated directory of files from the device."""
-        adb = AdbWrapper()
-        temp_files = {}
-        host_dir = None
-        try:
-            # create temporary host directory
-            host_dir = tempfile.mkdtemp()
-
-            # create temporary dir on device
-            adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
-            adb.shell("mkdir -p {}".format(AdbFile.DEVICE_TEMP_DIR))
-
-            # populate device dir with random files
-            temp_files = make_random_device_files(
-                adb, in_dir=AdbFile.DEVICE_TEMP_DIR, num_files=32)
-
-            adb.pull(remote=AdbFile.DEVICE_TEMP_DIR, local=host_dir)
-
-            for device_full_path in temp_files:
-                host_path = os.path.join(
-                    host_dir, temp_files[device_full_path].base_name)
-                with open(host_path) as host_file:
-                    host_md5 = compute_md5(host_file.read())
-                    self.assertEqual(host_md5,
-                                     temp_files[device_full_path].md5)
-        finally:
-            for dev_file in temp_files.values():
-                host_path = os.path.join(host_dir, dev_file.base_name)
-                os.remove(host_path)
-            adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
-            if host_dir:
-                os.removedirs(host_dir)
-
-    def test_sync(self):
-        """Sync a randomly generated directory of files to specified device."""
-        try:
-            adb = AdbWrapper()
-            temp_files = {}
-
-            # create temporary host directory
-            base_dir = tempfile.mkdtemp()
-
-            # create mirror device directory hierarchy within base_dir
-            full_dir_path = base_dir + AdbFile.DEVICE_TEMP_DIR
-            os.makedirs(full_dir_path)
-
-            # create 32 random files within the host mirror
-            temp_files = make_random_host_files(in_dir=full_dir_path,
-                                                num_files=32)
-
-            # clean up any trash on the device
-            adb = AdbWrapper(out_dir=base_dir)
-            adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
-
-            # issue the sync
-            adb.sync("data")
-
-            # confirm that every file on the device mirrors that on the host
-            for host_full_path in temp_files.keys():
-                device_full_path = os.path.join(
-                    AdbFile.DEVICE_TEMP_DIR,
-                    temp_files[host_full_path].base_name)
-                dev_md5, _ = adb.shell(
-                    "md5sum {}".format(device_full_path)).split()
-                self.assertEqual(temp_files[host_full_path].md5, dev_md5)
-
-        finally:
-            adb.shell_nocheck("rm -r {}".format(AdbFile.DEVICE_TEMP_DIR))
-            if temp_files:
-                for tf in temp_files.values():
-                    tf.handle.close()
-            if base_dir:
-                os.removedirs(base_dir + AdbFile.DEVICE_TEMP_DIR)
-
-
-if __name__ == '__main__':
-    random.seed(0)
-    dev_count = get_attached_device_count()
-    if dev_count:
-        suite = unittest.TestLoader().loadTestsFromName(__name__)
-        unittest.TextTestRunner(verbosity=3).run(suite)
-    else:
-        print "Test suite must be run with attached devices"
index 2cd6ac2..8b0cb38 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_TRANSPORT
+#define TRACE_TAG TRANSPORT
 
 #include "sysdeps.h"
 #include "transport.h"
 #include <string.h>
 #include <unistd.h>
 
+#include <algorithm>
+#include <list>
+
+#include <base/logging.h>
 #include <base/stringprintf.h>
+#include <base/strings.h>
 
 #include "adb.h"
 #include "adb_utils.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,
-};
+static auto& transport_list = *new std::list<atransport*>();
+static auto& pending_list = *new std::list<atransport*>();
 
 ADB_MUTEX_DEFINE( transport_lock );
 
-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);
-    }
-}
-
-// Each atransport contains a list of adisconnects (t->disconnects).
-// An adisconnect contains a link to the next/prev adisconnect, a function
-// pointer to a disconnect callback which takes a void* piece of user data and
-// the atransport, and some user data for the callback (helpfully named
-// "opaque").
-//
-// The list is circular. New items are added to the entry member of the list
-// (t->disconnects) by add_transport_disconnect.
-//
-// run_transport_disconnects invokes each function in the list.
-//
-// Gotchas:
-//   * run_transport_disconnects assumes that t->disconnects is non-null, so
-//     this can't be run on a zeroed atransport.
-//   * The callbacks in this list are not removed when called, and this function
-//     is not guarded against running more than once. As such, ensure that this
-//     function is not called multiple times on the same atransport.
-//     TODO(danalbert): Just fix this so that it is guarded once you have tests.
-void run_transport_disconnects(atransport* t)
-{
-    adisconnect*  dis = t->disconnects.next;
+const char* const kFeatureShell2 = "shell_v2";
+const char* const kFeatureCmd = "cmd";
 
-    D("%s: run_transport_disconnects\n", t->serial);
-    while (dis != &t->disconnects) {
-        adisconnect*  next = dis->next;
-        dis->func( dis->opaque, t );
-        dis = next;
-    }
-}
-
-static void dump_packet(const char* name, const char* func, apacket* p) {
+static std::string dump_packet(const char* name, const char* func, apacket* p) {
     unsigned  command = p->msg.command;
     int       len     = p->msg.data_length;
     char      cmd[9];
@@ -123,63 +77,55 @@ static void dump_packet(const char* name, const char* func, apacket* p) {
     else
         snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
 
-    D("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ",
-        name, func, cmd, arg0, arg1, len);
-    dump_hex(p->data, len);
+    std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ",
+                                                     name, func, cmd, arg0, arg1, len);
+    result += dump_hex(p->data, len);
+    return result;
 }
 
 static int
 read_packet(int  fd, const char* name, apacket** ppacket)
 {
-    char *p = (char*)ppacket;  /* really read a packet address */
-    int   r;
-    int   len = sizeof(*ppacket);
-    char  buff[8];
+    char buff[8];
     if (!name) {
         snprintf(buff, sizeof buff, "fd=%d", fd);
         name = buff;
     }
+    char* p = reinterpret_cast<char*>(ppacket);  /* really read a packet address */
+    int len = sizeof(apacket*);
     while(len > 0) {
-        r = adb_read(fd, p, len);
+        int r = adb_read(fd, p, len);
         if(r > 0) {
             len -= r;
-            p   += r;
+            p += r;
         } else {
-            D("%s: read_packet (fd=%d), error ret=%d errno=%d: %s\n", name, fd, r, errno, strerror(errno));
-            if((r < 0) && (errno == EINTR)) continue;
+            D("%s: read_packet (fd=%d), error ret=%d: %s", name, fd, r, strerror(errno));
             return -1;
         }
     }
 
-    if (ADB_TRACING) {
-        dump_packet(name, "from remote", *ppacket);
-    }
+    VLOG(TRANSPORT) << dump_packet(name, "from remote", *ppacket);
     return 0;
 }
 
 static int
 write_packet(int  fd, const char* name, apacket** ppacket)
 {
-    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_TRACING) {
-        dump_packet(name, "to remote", *ppacket);
-    }
-    len = sizeof(ppacket);
+    VLOG(TRANSPORT) << dump_packet(name, "to remote", *ppacket);
+    char* p = reinterpret_cast<char*>(ppacket);  /* we really write the packet address */
+    int len = sizeof(apacket*);
     while(len > 0) {
-        r = adb_write(fd, p, len);
+        int 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;
+            D("%s: write_packet (fd=%d) error ret=%d: %s", name, fd, r, strerror(errno));
             return -1;
         }
     }
@@ -189,11 +135,11 @@ write_packet(int  fd, const char* name, apacket** ppacket)
 static void transport_socket_events(int fd, unsigned events, void *_t)
 {
     atransport *t = reinterpret_cast<atransport*>(_t);
-    D("transport_socket_events(fd=%d, events=%04x,...)\n", fd, events);
+    D("transport_socket_events(fd=%d, events=%04x,...)", fd, events);
     if(events & FDE_READ){
         apacket *p = 0;
         if(read_packet(fd, t->serial, &p)){
-            D("%s: failed to read packet from transport socket on fd %d\n", t->serial, fd);
+            D("%s: failed to read packet from transport socket on fd %d", t->serial, fd);
         } else {
             handle_packet(p, (atransport *) _t);
         }
@@ -219,7 +165,7 @@ void send_packet(apacket *p, atransport *t)
     print_packet("send", p);
 
     if (t == NULL) {
-        D("Transport is null \n");
+        D("Transport is null");
         // Zap errno because print_packet() and other stuff have errno effect.
         errno = 0;
         fatal_errno("Transport is null");
@@ -230,25 +176,27 @@ void send_packet(apacket *p, atransport *t)
     }
 }
 
-/* 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)
+// The transport is opened by transport_register_func before
+// the read_transport and write_transport threads are started.
+//
+// The read_transport thread issues a SYNC(1, token) message to let
+// the write_transport thread know to start things up.  In the event
+// of transport IO failure, the read_transport thread will post a
+// SYNC(0,0) message to ensure shutdown.
+//
+// The transport will not actually be closed until both threads exit, but the threads
+// will kick the transport on their way out to disconnect the underlying device.
+//
+// read_transport thread reads data from a transport (representing a usb/tcp connection),
+// and makes the main thread call handle_packet().
+static void *read_transport_thread(void *_t)
 {
     atransport *t = reinterpret_cast<atransport*>(_t);
     apacket *p;
 
-    D("%s: starting transport output thread on fd %d, SYNC online (%d)\n",
+    adb_thread_setname(android::base::StringPrintf("<-%s",
+                                                   (t->serial != nullptr ? t->serial : "transport")));
+    D("%s: starting read_transport thread on fd %d, SYNC online (%d)",
        t->serial, t->fd, t->sync_token + 1);
     p = get_apacket();
     p->msg.command = A_SYNC;
@@ -257,30 +205,30 @@ static void *output_thread(void *_t)
     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);
+        D("%s: failed to write SYNC packet", t->serial);
         goto oops;
     }
 
-    D("%s: data pump started\n", t->serial);
+    D("%s: data pump started", t->serial);
     for(;;) {
         p = get_apacket();
 
         if(t->read_from_remote(p, t) == 0){
-            D("%s: received remote packet, sending to transport\n",
+            D("%s: received remote packet, sending to transport",
               t->serial);
             if(write_packet(t->fd, t->serial, &p)){
                 put_apacket(p);
-                D("%s: failed to write apacket to transport\n", t->serial);
+                D("%s: failed to write apacket to transport", t->serial);
                 goto oops;
             }
         } else {
-            D("%s: remote read failed for transport\n", t->serial);
+            D("%s: remote read failed for transport", t->serial);
             put_apacket(p);
             break;
         }
     }
 
-    D("%s: SYNC offline for transport\n", t->serial);
+    D("%s: SYNC offline for transport", t->serial);
     p = get_apacket();
     p->msg.command = A_SYNC;
     p->msg.arg0 = 0;
@@ -292,63 +240,76 @@ static void *output_thread(void *_t)
     }
 
 oops:
-    D("%s: transport output thread is exiting\n", t->serial);
+    D("%s: read_transport thread is exiting", t->serial);
     kick_transport(t);
     transport_unref(t);
     return 0;
 }
 
-static void *input_thread(void *_t)
+// write_transport thread gets packets sent by the main thread (through send_packet()),
+// and writes to a transport (representing a usb/tcp connection).
+static void *write_transport_thread(void *_t)
 {
     atransport *t = reinterpret_cast<atransport*>(_t);
     apacket *p;
     int active = 0;
 
-    D("%s: starting transport input thread, reading from fd %d\n",
+    adb_thread_setname(android::base::StringPrintf("->%s",
+                                                   (t->serial != nullptr ? t->serial : "transport")));
+    D("%s: starting write_transport thread, reading from fd %d",
        t->serial, t->fd);
 
     for(;;){
         if(read_packet(t->fd, t->serial, &p)) {
-            D("%s: failed to read apacket from transport on fd %d\n",
+            D("%s: failed to read apacket from transport on fd %d",
                t->serial, t->fd );
             break;
         }
         if(p->msg.command == A_SYNC){
             if(p->msg.arg0 == 0) {
-                D("%s: transport SYNC offline\n", t->serial);
+                D("%s: transport SYNC offline", t->serial);
                 put_apacket(p);
                 break;
             } else {
                 if(p->msg.arg1 == t->sync_token) {
-                    D("%s: transport SYNC online\n", t->serial);
+                    D("%s: transport SYNC online", t->serial);
                     active = 1;
                 } else {
-                    D("%s: transport ignoring SYNC %d != %d\n",
+                    D("%s: transport ignoring SYNC %d != %d",
                       t->serial, p->msg.arg1, t->sync_token);
                 }
             }
         } else {
             if(active) {
-                D("%s: transport got packet, sending to remote\n", t->serial);
+                D("%s: transport got packet, sending to remote", t->serial);
                 t->write_to_remote(p, t);
             } else {
-                D("%s: transport ignoring packet while offline\n", t->serial);
+                D("%s: transport ignoring packet while offline", t->serial);
             }
         }
 
         put_apacket(p);
     }
 
-    // this is necessary to avoid a race condition that occured when a transport closes
-    // while a client socket is still active.
-    close_all_sockets(t);
-
-    D("%s: transport input thread is exiting, fd %d\n", t->serial, t->fd);
+    D("%s: write_transport thread is exiting, fd %d", t->serial, t->fd);
     kick_transport(t);
     transport_unref(t);
     return 0;
 }
 
+static void kick_transport_locked(atransport* t) {
+    CHECK(t != nullptr);
+    if (!t->kicked) {
+        t->kicked = true;
+        t->kick(t);
+    }
+}
+
+void kick_transport(atransport* t) {
+    adb_mutex_lock(&transport_lock);
+    kick_transport_locked(t);
+    adb_mutex_unlock(&transport_lock);
+}
 
 static int transport_registration_send = -1;
 static int transport_registration_recv = -1;
@@ -395,7 +356,7 @@ device_tracker_close( asocket*  socket )
     device_tracker*  tracker = (device_tracker*) socket;
     asocket*         peer    = socket->peer;
 
-    D( "device tracker %p removed\n", tracker);
+    D( "device tracker %p removed", tracker);
     if (peer) {
         peer->peer = NULL;
         peer->close(peer);
@@ -442,7 +403,7 @@ create_device_tracker(void)
     device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
     if (tracker == nullptr) fatal("cannot allocate device tracker");
 
-    D( "device tracker %p created\n", tracker);
+    D( "device tracker %p created", tracker);
 
     tracker->socket.enqueue = device_tracker_enqueue;
     tracker->socket.ready   = device_tracker_ready;
@@ -496,9 +457,7 @@ transport_read_action(int  fd, struct tmsg*  m)
             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));
+            D("transport_read_action: on fd %d: %s", fd, strerror(errno));
             return -1;
         }
     }
@@ -518,9 +477,7 @@ transport_write_action(int  fd, struct tmsg*  m)
             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));
+            D("transport_write_action: on fd %d: %s", fd, strerror(errno));
             return -1;
         }
     }
@@ -530,8 +487,6 @@ transport_write_action(int  fd, struct tmsg*  m)
 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;
 
@@ -545,8 +500,8 @@ static void transport_registration_func(int _fd, unsigned ev, void *data)
 
     t = m.transport;
 
-    if(m.action == 0){
-        D("transport: %s removing and free'ing %d\n", t->serial, t->transport_socket);
+    if (m.action == 0) {
+        D("transport: %s removing and free'ing %d", t->serial, t->transport_socket);
 
             /* IMPORTANT: the remove closes one half of the
             ** socket pair.  The close closes the other half.
@@ -555,12 +510,9 @@ static void transport_registration_func(int _fd, unsigned ev, void *data)
         adb_close(t->fd);
 
         adb_mutex_lock(&transport_lock);
-        t->next->prev = t->prev;
-        t->prev->next = t->next;
+        transport_list.remove(t);
         adb_mutex_unlock(&transport_lock);
 
-        run_transport_disconnects(t);
-
         if (t->product)
             free(t->product);
         if (t->serial)
@@ -572,19 +524,18 @@ static void transport_registration_func(int _fd, unsigned ev, void *data)
         if (t->devpath)
             free(t->devpath);
 
-        memset(t,0xee,sizeof(atransport));
-        free(t);
+        delete t;
 
         update_transports();
         return;
     }
 
     /* don't create transport threads for inaccessible devices */
-    if (t->connection_state != CS_NOPERM) {
+    if (t->connection_state != kCsNoPerm) {
         /* initial references are the two threads */
         t->ref_count = 2;
 
-        if(adb_socketpair(s)) {
+        if (adb_socketpair(s)) {
             fatal_errno("cannot open transport socketpair");
         }
 
@@ -600,28 +551,20 @@ static void transport_registration_func(int _fd, unsigned ev, void *data)
 
         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(write_transport_thread, t)) {
+            fatal_errno("cannot create write_transport thread");
         }
 
-        if(adb_thread_create(&output_thread_ptr, output_thread, t)){
-            fatal_errno("cannot create output thread");
+        if (!adb_thread_create(read_transport_thread, t)) {
+            fatal_errno("cannot create read_transport thread");
         }
     }
 
     adb_mutex_lock(&transport_lock);
-    /* 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;
+    pending_list.remove(t);
+    transport_list.push_front(t);
     adb_mutex_unlock(&transport_lock);
 
-    t->disconnects.next = t->disconnects.prev = &t->disconnects;
-
     update_transports();
 }
 
@@ -651,7 +594,7 @@ static void register_transport(atransport *transport)
     tmsg m;
     m.transport = transport;
     m.action = 1;
-    D("transport: %s registered\n", transport->serial);
+    D("transport: %s registered", transport->serial);
     if(transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
@@ -662,55 +605,29 @@ static void remove_transport(atransport *transport)
     tmsg m;
     m.transport = transport;
     m.action = 0;
-    D("transport: %s removed\n", transport->serial);
+    D("transport: %s removed", transport->serial);
     if(transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
 }
 
 
-static void transport_unref_locked(atransport *t)
-{
+static void transport_unref(atransport* t) {
+    CHECK(t != nullptr);
+    adb_mutex_lock(&transport_lock);
+    CHECK_GT(t->ref_count, 0u);
     t->ref_count--;
     if (t->ref_count == 0) {
-        D("transport: %s unref (kicking and closing)\n", t->serial);
-        if (!t->kicked) {
-            t->kicked = 1;
-            t->kick(t);
-        }
+        D("transport: %s unref (kicking and closing)", t->serial);
+        kick_transport_locked(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);
+        D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
     }
-}
-
-void add_transport_disconnect(atransport*  t, adisconnect*  dis)
-{
-    adb_mutex_lock(&transport_lock);
-    dis->next       = &t->disconnects;
-    dis->prev       = dis->next->prev;
-    dis->prev->next = dis;
-    dis->next->prev = dis;
     adb_mutex_unlock(&transport_lock);
 }
 
-void remove_transport_disconnect(atransport*  t, adisconnect*  dis)
-{
-    dis->prev->next = dis->next;
-    dis->next->prev = dis->prev;
-    dis->next = dis->prev = dis;
-}
-
 static int qual_match(const char *to_test,
                       const char *prefix, const char *qual, bool sanitize_qual)
 {
@@ -740,24 +657,28 @@ static int qual_match(const char *to_test,
     return !*to_test;
 }
 
-atransport* acquire_one_transport(int state, transport_type ttype,
-                                  const char* serial, std::string* error_out)
-{
-    atransport *t;
-    atransport *result = NULL;
-    int ambiguous = 0;
+atransport* acquire_one_transport(TransportType type, const char* serial,
+                                  bool* is_ambiguous, std::string* error_out) {
+    atransport* result = nullptr;
 
-retry:
-    if (error_out) *error_out = android::base::StringPrintf("device '%s' not found", serial);
+    if (serial) {
+        *error_out = android::base::StringPrintf("device '%s' not found", serial);
+    } else if (type == kTransportLocal) {
+        *error_out = "no emulators found";
+    } else if (type == kTransportAny) {
+        *error_out = "no devices/emulators found";
+    } else {
+        *error_out = "no devices found";
+    }
 
     adb_mutex_lock(&transport_lock);
-    for (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";
+    for (const auto& t : transport_list) {
+        if (t->connection_state == kCsNoPerm) {
+            *error_out = "insufficient permissions for device";
             continue;
         }
 
-        /* check for matching serial number */
+        // Check for matching serial number.
         if (serial) {
             if ((t->serial && !strcmp(serial, t->serial)) ||
                 (t->devpath && !strcmp(serial, t->devpath)) ||
@@ -765,35 +686,35 @@ retry:
                 qual_match(serial, "model:", t->model, true) ||
                 qual_match(serial, "device:", t->device, false)) {
                 if (result) {
-                    if (error_out) *error_out = "more than one device";
-                    ambiguous = 1;
-                    result = NULL;
+                    *error_out = "more than one device";
+                    if (is_ambiguous) *is_ambiguous = true;
+                    result = nullptr;
                     break;
                 }
                 result = t;
             }
         } else {
-            if (ttype == kTransportUsb && t->type == kTransportUsb) {
+            if (type == kTransportUsb && t->type == kTransportUsb) {
                 if (result) {
-                    if (error_out) *error_out = "more than one device";
-                    ambiguous = 1;
-                    result = NULL;
+                    *error_out = "more than one device";
+                    if (is_ambiguous) *is_ambiguous = true;
+                    result = nullptr;
                     break;
                 }
                 result = t;
-            } else if (ttype == kTransportLocal && t->type == kTransportLocal) {
+            } else if (type == kTransportLocal && t->type == kTransportLocal) {
                 if (result) {
-                    if (error_out) *error_out = "more than one emulator";
-                    ambiguous = 1;
-                    result = NULL;
+                    *error_out = "more than one emulator";
+                    if (is_ambiguous) *is_ambiguous = true;
+                    result = nullptr;
                     break;
                 }
                 result = t;
-            } else if (ttype == kTransportAny) {
+            } else if (type == kTransportAny) {
                 if (result) {
-                    if (error_out) *error_out = "more than one device/emulator";
-                    ambiguous = 1;
-                    result = NULL;
+                    *error_out = "more than one device/emulator";
+                    if (is_ambiguous) *is_ambiguous = true;
+                    result = nullptr;
                     break;
                 }
                 result = t;
@@ -802,38 +723,26 @@ retry:
     }
     adb_mutex_unlock(&transport_lock);
 
-    if (result) {
-        if (result->connection_state == CS_UNAUTHORIZED) {
-            if (error_out) {
-                *error_out = "device unauthorized.\n";
-                char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
-                *error_out += "This adbd's $ADB_VENDOR_KEYS is ";
-                *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
-                *error_out += "; try 'adb kill-server' if that seems wrong.\n";
-                *error_out += "Otherwise check for a 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;
-        }
+    // Don't return unauthorized devices; the caller can't do anything with them.
+    if (result && result->connection_state == kCsUnauthorized) {
+        *error_out = "device unauthorized.\n";
+        char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
+        *error_out += "This adb server's $ADB_VENDOR_KEYS is ";
+        *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+        *error_out += "\n";
+        *error_out += "Try 'adb kill-server' if that seems wrong.\n";
+        *error_out += "Otherwise check for a confirmation dialog on your device.";
+        result = nullptr;
+    }
 
-        /* check for required connection state */
-        if (result && state != CS_ANY && result->connection_state != state) {
-            if (error_out) *error_out = "invalid device state";
-            result = NULL;
-        }
+    // Don't return offline devices; the caller can't do anything with them.
+    if (result && result->connection_state == kCsOffline) {
+        *error_out = "device offline";
+        result = nullptr;
     }
 
     if (result) {
-        /* found one that we can take */
-        if (error_out) *error_out = "success";
-    } else if (state != CS_ANY && (serial || !ambiguous)) {
-        adb_sleep_ms(1000);
-        goto retry;
+        *error_out = "success";
     }
 
     return result;
@@ -841,18 +750,92 @@ retry:
 
 const char* atransport::connection_state_name() const {
     switch (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_NOPERM: return "no permissions";
-    case CS_SIDELOAD: return "sideload";
-    case CS_UNAUTHORIZED: return "unauthorized";
+    case kCsOffline: return "offline";
+    case kCsBootloader: return "bootloader";
+    case kCsDevice: return "device";
+    case kCsHost: return "host";
+    case kCsRecovery: return "recovery";
+    case kCsNoPerm: return "no permissions";
+    case kCsSideload: return "sideload";
+    case kCsUnauthorized: return "unauthorized";
     default: return "unknown";
     }
 }
 
+void atransport::update_version(int version, size_t payload) {
+    protocol_version = std::min(version, A_VERSION);
+    max_payload = std::min(payload, MAX_PAYLOAD);
+}
+
+int atransport::get_protocol_version() const {
+    return protocol_version;
+}
+
+size_t atransport::get_max_payload() const {
+    return max_payload;
+}
+
+namespace {
+
+constexpr char kFeatureStringDelimiter = ',';
+
+}  // namespace
+
+const FeatureSet& supported_features() {
+    // Local static allocation to avoid global non-POD variables.
+    static const FeatureSet* features = new FeatureSet{
+        kFeatureShell2,
+        kFeatureCmd
+        // Increment ADB_SERVER_VERSION whenever the feature list changes to
+        // make sure that the adb client and server features stay in sync
+        // (http://b/24370690).
+    };
+
+    return *features;
+}
+
+std::string FeatureSetToString(const FeatureSet& features) {
+    return android::base::Join(features, kFeatureStringDelimiter);
+}
+
+FeatureSet StringToFeatureSet(const std::string& features_string) {
+    if (features_string.empty()) {
+        return FeatureSet();
+    }
+
+    auto names = android::base::Split(features_string,
+                                      {kFeatureStringDelimiter});
+    return FeatureSet(names.begin(), names.end());
+}
+
+bool CanUseFeature(const FeatureSet& feature_set, const std::string& feature) {
+    return feature_set.count(feature) > 0 &&
+            supported_features().count(feature) > 0;
+}
+
+bool atransport::has_feature(const std::string& feature) const {
+    return features_.count(feature) > 0;
+}
+
+void atransport::SetFeatures(const std::string& features_string) {
+    features_ = StringToFeatureSet(features_string);
+}
+
+void atransport::AddDisconnect(adisconnect* disconnect) {
+    disconnects_.push_back(disconnect);
+}
+
+void atransport::RemoveDisconnect(adisconnect* disconnect) {
+    disconnects_.remove(disconnect);
+}
+
+void atransport::RunDisconnects() {
+    for (const auto& disconnect : disconnects_) {
+        disconnect->func(disconnect->opaque, this);
+    }
+    disconnects_.clear();
+}
+
 #if ADB_HOST
 
 static void append_transport_info(std::string* result, const char* key,
@@ -869,7 +852,8 @@ static void append_transport_info(std::string* result, const char* key,
     }
 }
 
-static void append_transport(atransport* t, std::string* result, bool long_listing) {
+static void append_transport(const atransport* t, std::string* result,
+                             bool long_listing) {
     const char* serial = t->serial;
     if (!serial || !serial[0]) {
         serial = "(no serial number)";
@@ -893,7 +877,7 @@ static void append_transport(atransport* t, std::string* result, bool long_listi
 std::string list_transports(bool long_listing) {
     std::string result;
     adb_mutex_lock(&transport_lock);
-    for (atransport* t = transport_list.next; t != &transport_list; t = t->next) {
+    for (const auto& t : transport_list) {
         append_transport(t, &result, long_listing);
     }
     adb_mutex_unlock(&transport_lock);
@@ -901,11 +885,10 @@ std::string list_transports(bool long_listing) {
 }
 
 /* hack for osx */
-void close_usb_devices()
-{
+void close_usb_devices() {
     adb_mutex_lock(&transport_lock);
-    for (atransport* t = transport_list.next; t != &transport_list; t = t->next) {
-        if ( !t->kicked ) {
+    for (const auto& t : transport_list) {
+        if (!t->kicked) {
             t->kicked = 1;
             t->kick(t);
         }
@@ -914,47 +897,39 @@ void close_usb_devices()
 }
 #endif // ADB_HOST
 
-int register_socket_transport(int s, const char *serial, int port, int local)
-{
-    atransport *t = reinterpret_cast<atransport*>(calloc(1, sizeof(atransport)));
-    if (t == nullptr) {
-        return -1;
-    }
-
-    atransport *n;
-    char buff[32];
+int register_socket_transport(int s, const char *serial, int port, int local) {
+    atransport* t = new atransport();
 
     if (!serial) {
-        snprintf(buff, sizeof buff, "T-%p", t);
-        serial = buff;
+        char buf[32];
+        snprintf(buf, sizeof(buf), "T-%p", t);
+        serial = buf;
     }
-    D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port);
+
+    D("transport: %s init'ing for socket %d, on port %d", serial, s, port);
     if (init_socket_transport(t, s, port, local) < 0) {
-        free(t);
+        delete 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)) {
+    for (const auto& transport : pending_list) {
+        if (transport->serial && strcmp(serial, transport->serial) == 0) {
             adb_mutex_unlock(&transport_lock);
-            free(t);
+            delete t;
             return -1;
         }
     }
 
-    for (n = transport_list.next; n != &transport_list; n = n->next) {
-        if (n->serial && !strcmp(serial, n->serial)) {
+    for (const auto& transport : transport_list) {
+        if (transport->serial && strcmp(serial, transport->serial) == 0) {
             adb_mutex_unlock(&transport_lock);
-            free(t);
+            delete t;
             return -1;
         }
     }
 
-    t->next = &pending_list;
-    t->prev = pending_list.prev;
-    t->next->prev = t;
-    t->prev->next = t;
+    pending_list.push_front(t);
     t->serial = strdup(serial);
     adb_mutex_unlock(&transport_lock);
 
@@ -963,111 +938,79 @@ int register_socket_transport(int s, const char *serial, int port, int local)
 }
 
 #if ADB_HOST
-atransport *find_transport(const char *serial)
-{
-    atransport *t;
+atransport *find_transport(const char *serial) {
+    atransport* result = nullptr;
 
     adb_mutex_lock(&transport_lock);
-    for(t = transport_list.next; t != &transport_list; t = t->next) {
-        if (t->serial && !strcmp(serial, t->serial)) {
+    for (auto& t : transport_list) {
+        if (t->serial && strcmp(serial, t->serial) == 0) {
+            result = t;
             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);
+    return result;
 }
 
-// unregisters all non-emulator TCP transports
-void unregister_all_tcp_transports()
-{
-    atransport *t, *next;
+void kick_all_tcp_devices() {
     adb_mutex_lock(&transport_lock);
-    for (t = transport_list.next; t != &transport_list; t = next) {
-        next = t->next;
+    for (auto& t : transport_list) {
+        // TCP/IP devices have adb_port == 0.
         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);
+            // Kicking breaks the read_transport thread of this transport out of any read, then
+            // the read_transport thread will notify the main thread to make this transport
+            // offline. Then the main thread will notify the write_transport thread to exit.
+            // Finally, this transport will be closed and freed in the main thread.
+            kick_transport_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 = reinterpret_cast<atransport*>(calloc(1, sizeof(atransport)));
-    if (t == nullptr) fatal("cannot allocate USB atransport");
-    D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
+void register_usb_transport(usb_handle* usb, const char* serial,
+                            const char* devpath, unsigned writeable) {
+    atransport* t = new atransport();
+
+    D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb,
       serial ? serial : "");
-    init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM));
+    init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
     if(serial) {
         t->serial = strdup(serial);
     }
-    if(devpath) {
+
+    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;
+    pending_list.push_front(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;
+// This should only be used for transports with connection_state == kCsNoPerm.
+void unregister_usb_transport(usb_handle *usb) {
     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;
-        }
-     }
+    transport_list.remove_if([usb](atransport* t) {
+        return t->usb == usb && t->connection_state == kCsNoPerm;
+    });
     adb_mutex_unlock(&transport_lock);
 }
 
-#undef TRACE_TAG
-#define TRACE_TAG  TRACE_RWX
-
-int check_header(apacket *p)
+int check_header(apacket *p, atransport *t)
 {
     if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
-        D("check_header(): invalid magic\n");
+        VLOG(RWX) << "check_header(): invalid magic";
         return -1;
     }
 
-    if(p->msg.data_length > MAX_PAYLOAD) {
-        D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length);
+    if(p->msg.data_length > t->get_max_payload()) {
+        VLOG(RWX) << "check_header(): " << p->msg.data_length << " atransport::max_payload = "
+                  << t->get_max_payload();
         return -1;
     }
 
index 5b6fdac..d9845b6 100644 (file)
 
 #include <sys/types.h>
 
+#include <list>
 #include <string>
+#include <unordered_set>
 
 #include "adb.h"
 
+typedef std::unordered_set<std::string> FeatureSet;
+
+const FeatureSet& supported_features();
+
+// Encodes and decodes FeatureSet objects into human-readable strings.
+std::string FeatureSetToString(const FeatureSet& features);
+FeatureSet StringToFeatureSet(const std::string& features_string);
+
+// Returns true if both local features and |feature_set| support |feature|.
+bool CanUseFeature(const FeatureSet& feature_set, const std::string& feature);
+
+// Do not use any of [:;=,] in feature strings, they have special meaning
+// in the connection banner.
+extern const char* const kFeatureShell2;
+// The 'cmd' command is available
+extern const char* const kFeatureCmd;
+
+class atransport {
+public:
+    // TODO(danalbert): We expose waaaaaaay too much stuff because this was
+    // historically just a struct, but making the whole thing a more idiomatic
+    // class in one go is a very large change. Given how bad our testing is,
+    // it's better to do this piece by piece.
+
+    atransport() {
+        auth_fde = {};
+        transport_fde = {};
+        protocol_version = A_VERSION;
+        max_payload = MAX_PAYLOAD;
+    }
+
+    virtual ~atransport() {}
+
+    int (*read_from_remote)(apacket* p, atransport* t) = nullptr;
+    int (*write_to_remote)(apacket* p, atransport* t) = nullptr;
+    void (*close)(atransport* t) = nullptr;
+    void (*kick)(atransport* t) = nullptr;
+
+    int fd = -1;
+    int transport_socket = -1;
+    fdevent transport_fde;
+    size_t ref_count = 0;
+    uint32_t sync_token = 0;
+    ConnectionState connection_state = kCsOffline;
+    bool online = false;
+    TransportType type = kTransportAny;
+
+    // USB handle or socket fd as needed.
+    usb_handle* usb = nullptr;
+    int sfd = -1;
+
+    // Used to identify transports for clients.
+    char* serial = nullptr;
+    char* product = nullptr;
+    char* model = nullptr;
+    char* device = nullptr;
+    char* devpath = nullptr;
+    int adb_port = -1;  // Use for emulators (local transport)
+    bool kicked = false;
+
+    void* key = nullptr;
+    unsigned char token[TOKEN_SIZE] = {};
+    fdevent auth_fde;
+    size_t failed_auth_attempts = 0;
+
+    const char* connection_state_name() const;
+
+    void update_version(int version, size_t payload);
+    int get_protocol_version() const;
+    size_t get_max_payload() const;
+
+    const FeatureSet& features() const {
+        return features_;
+    }
+
+    bool has_feature(const std::string& feature) const;
+
+    // Loads the transport's feature set from the given string.
+    void SetFeatures(const std::string& features_string);
+
+    void AddDisconnect(adisconnect* disconnect);
+    void RemoveDisconnect(adisconnect* disconnect);
+    void RunDisconnects();
+
+private:
+    // A set of features transmitted in the banner with the initial connection.
+    // This is stored in the banner as 'features=feature0,feature1,etc'.
+    FeatureSet features_;
+    int protocol_version;
+    size_t max_payload;
+
+    // A list of adisconnect callbacks called when the transport is kicked.
+    std::list<adisconnect*> disconnects_;
+
+    DISALLOW_COPY_AND_ASSIGN(atransport);
+};
+
 /*
  * 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.
+ * If serial is non-null then only the device with that serial will be chosen.
+ * If multiple devices/emulators would match, *is_ambiguous (if non-null)
+ * is set to true and nullptr returned.
+ * If no suitable transport is found, error is set and nullptr returned.
  */
-atransport* acquire_one_transport(int state, transport_type ttype,
-                                  const char* serial, std::string* error_out);
-void add_transport_disconnect(atransport* t, adisconnect* dis);
-void remove_transport_disconnect(atransport* t, adisconnect* dis);
+atransport* acquire_one_transport(TransportType type, const char* serial,
+                                  bool* is_ambiguous, std::string* error_out);
 void kick_transport(atransport* t);
-void run_transport_disconnects(atransport* t);
 void update_transports(void);
 
-/* transports are ref-counted
-** get_device_transport does an acquire on your behalf before returning
-*/
 void init_transport_registration(void);
 std::string list_transports(bool long_listing);
 atransport* find_transport(const char* serial);
+void kick_all_tcp_devices();
 
 void register_usb_transport(usb_handle* h, const char* serial,
                             const char* devpath, unsigned writeable);
@@ -50,14 +145,10 @@ void register_usb_transport(usb_handle* h, const char* serial,
 /* 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);
 
-/* this should only be used for transports with connection_state == CS_NOPERM */
+// This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb);
 
-/* these should only be used for the "adb disconnect" command */
-void unregister_transport(atransport* t);
-void unregister_all_tcp_transports();
-
-int check_header(apacket* p);
+int check_header(apacket* p, atransport* t);
 int check_data(apacket* p);
 
 /* for MacOS X cleanup */
index b1deffd..bf0cc3c 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_TRANSPORT
+#define TRACE_TAG TRANSPORT
 
 #include "sysdeps.h"
 #include "transport.h"
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 
 #include <base/stringprintf.h>
+#include <cutils/sockets.h>
 
 #if !ADB_HOST
 #include "cutils/properties.h"
@@ -33,6 +34,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 
 #if ADB_HOST
 /* we keep a list of opened transports. The atransport struct knows to which
@@ -49,22 +51,22 @@ static atransport*  local_transports[ ADB_LOCAL_TRANSPORT_MAX ];
 static int remote_read(apacket *p, atransport *t)
 {
     if(!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))){
-        D("remote local: read terminated (message)\n");
+        D("remote local: read terminated (message)");
         return -1;
     }
 
-    if(check_header(p)) {
-        D("bad header: terminated (data)\n");
+    if(check_header(p, t)) {
+        D("bad header: terminated (data)");
         return -1;
     }
 
     if(!ReadFdExactly(t->sfd, p->data, p->msg.data_length)){
-        D("remote local: terminated (data)\n");
+        D("remote local: terminated (data)");
         return -1;
     }
 
     if(check_data(p)) {
-        D("bad data: terminated (data)\n");
+        D("bad data: terminated (data)");
         return -1;
     }
 
@@ -76,62 +78,68 @@ static int remote_write(apacket *p, atransport *t)
     int   length = p->msg.data_length;
 
     if(!WriteFdExactly(t->sfd, &p->msg, sizeof(amessage) + length)) {
-        D("remote local: write terminated\n");
+        D("remote local: write terminated");
         return -1;
     }
 
     return 0;
 }
 
-
-int local_connect(int port) {
-    return local_connect_arbitrary_ports(port-1, port);
+void local_connect(int port) {
+    std::string dummy;
+    local_connect_arbitrary_ports(port-1, port, &dummy);
 }
 
-int local_connect_arbitrary_ports(int console_port, int adb_port)
-{
-    int  fd = -1;
+int local_connect_arbitrary_ports(int console_port, int adb_port, std::string* error) {
+    int fd = -1;
 
 #if ADB_HOST
+    if (find_emulator_transport_by_adb_port(adb_port) != nullptr) {
+        return -1;
+    }
+
     const char *host = getenv("ADBHOST");
     if (host) {
-        fd = socket_network_client(host, adb_port, SOCK_STREAM);
+        fd = network_connect(host, adb_port, SOCK_STREAM, 0, error);
     }
 #endif
     if (fd < 0) {
-        fd = socket_loopback_client(adb_port, SOCK_STREAM);
+        fd = network_loopback_client(adb_port, SOCK_STREAM, error);
     }
 
     if (fd >= 0) {
-        D("client: connected on remote on fd %d\n", fd);
+        D("client: connected on remote on fd %d", fd);
         close_on_exec(fd);
         disable_tcp_nagle(fd);
         std::string serial = android::base::StringPrintf("emulator-%d", console_port);
-        register_socket_transport(fd, serial.c_str(), adb_port, 1);
-        return 0;
+        if (register_socket_transport(fd, serial.c_str(), adb_port, 1) == 0) {
+            return 0;
+        }
+        adb_close(fd);
     }
     return -1;
 }
 
-
+#if ADB_HOST
 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);
+    adb_thread_setname("client_socket_thread");
+    D("transport: client_socket_thread() starting");
+    while (true) {
+        int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+        int count = ADB_LOCAL_TRANSPORT_MAX;
+
+        // Try to connect to any number of running emulator instances.
+        for ( ; count > 0; count--, port += 2 ) {
+            local_connect(port);
+        }
+        sleep(1);
     }
-#endif
     return 0;
 }
 
+#else // ADB_HOST
+
 static void *server_socket_thread(void * arg)
 {
     int serverfd, fd;
@@ -139,13 +147,15 @@ static void *server_socket_thread(void * arg)
     socklen_t alen;
     int port = (int) (uintptr_t) arg;
 
-    D("transport: server_socket_thread() starting\n");
+    adb_thread_setname("server socket");
+    D("transport: server_socket_thread() starting");
     serverfd = -1;
     for(;;) {
         if(serverfd == -1) {
-            serverfd = socket_inaddr_any_server(port, SOCK_STREAM);
+            std::string error;
+            serverfd = network_inaddr_any_server(port, SOCK_STREAM, &error);
             if(serverfd < 0) {
-                D("server: cannot bind socket yet: %s\n", strerror(errno));
+                D("server: cannot bind socket yet: %s", error.c_str());
                 adb_sleep_ms(1000);
                 continue;
             }
@@ -153,21 +163,20 @@ static void *server_socket_thread(void * arg)
         }
 
         alen = sizeof(addr);
-        D("server: trying to get new connection from %d\n", port);
+        D("server: trying to get new connection from %d", port);
         fd = adb_socket_accept(serverfd, &addr, &alen);
         if(fd >= 0) {
-            D("server: new connection on fd %d\n", fd);
+            D("server: new connection on fd %d", fd);
             close_on_exec(fd);
             disable_tcp_nagle(fd);
             register_socket_transport(fd, "host", port, 1);
         }
     }
-    D("transport: server_socket_thread() exiting\n");
+    D("transport: server_socket_thread() exiting");
     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.
@@ -224,7 +233,8 @@ static const char _ok_resp[]    = "ok";
     char tmp[256];
     char con_name[32];
 
-    D("transport: qemu_socket_thread() starting\n");
+    adb_thread_setname("qemu socket");
+    D("transport: qemu_socket_thread() starting");
 
     /* adb QEMUD service connection request. */
     snprintf(con_name, sizeof(con_name), "qemud:adb:%d", port);
@@ -234,9 +244,8 @@ static const char _ok_resp[]    = "ok";
     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);
+        D("adb service is not available. Falling back to TCP socket.");
+        adb_thread_create(server_socket_thread, arg);
         return 0;
     }
 
@@ -252,7 +261,7 @@ static const char _ok_resp[]    = "ok";
              * 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");
+                D("Accepting ADB host connection has failed.");
                 adb_close(fd);
             } else {
                 /* Host is connected. Register the transport, and start the
@@ -264,49 +273,45 @@ static const char _ok_resp[]    = "ok";
             /* Prepare for accepting of the next ADB host connection. */
             fd = qemu_pipe_open(con_name);
             if (fd < 0) {
-                D("adb service become unavailable.\n");
+                D("adb service become unavailable.");
                 return 0;
             }
         } else {
-            D("Unable to send the '%s' request to ADB service.\n", _accept_req);
+            D("Unable to send the '%s' request to ADB service.", _accept_req);
             return 0;
         }
     }
-    D("transport: qemu_socket_thread() exiting\n");
+    D("transport: qemu_socket_thread() exiting");
     return 0;
 }
 #endif  // !ADB_HOST
 
 void local_init(int port)
 {
-    adb_thread_t thr;
     void* (*func)(void *);
+    const char* debug_name = "";
 
-    if(HOST) {
-        func = client_socket_thread;
-    } else {
 #if ADB_HOST
-        func = server_socket_thread;
+    func = client_socket_thread;
+    debug_name = "client";
 #else
-        /* For the adbd daemon in the system image we need to distinguish
-         * between the device, and the emulator. */
-        char is_qemu[PROPERTY_VALUE_MAX];
-        property_get("ro.kernel.qemu", is_qemu, "");
-        if (!strcmp(is_qemu, "1")) {
-            /* Running inside the emulator: use QEMUD pipe as the transport. */
-            func = qemu_socket_thread;
-        } else {
-            /* Running inside the device: use TCP socket as the transport. */
-            func = server_socket_thread;
-        }
-#endif // !ADB_HOST
+    /* 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;
     }
+    debug_name = "server";
+#endif // !ADB_HOST
 
-    D("transport: local %s init\n", HOST ? "client" : "server");
-
-    if(adb_thread_create(&thr, func, (void *) (uintptr_t) port)) {
-        fatal_errno("cannot create local socket %s thread",
-                    HOST ? "client" : "server");
+    D("transport: local %s init", debug_name);
+    if (!adb_thread_create(func, (void *) (uintptr_t) port)) {
+        fatal_errno("cannot create local socket %s thread", debug_name);
     }
 }
 
@@ -318,23 +323,25 @@ static void remote_kick(atransport *t)
     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;
-            }
+    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 );
     }
+    adb_mutex_unlock( &local_transports_lock );
 #endif
 }
 
 static void remote_close(atransport *t)
 {
-    adb_close(t->fd);
+    int fd = t->sfd;
+    if (fd != -1) {
+        t->sfd = -1;
+        adb_close(fd);
+    }
 }
 
 
@@ -390,12 +397,12 @@ int init_socket_transport(atransport *t, int s, int adb_port, int local)
     t->write_to_remote = remote_write;
     t->sfd = s;
     t->sync_token = 1;
-    t->connection_state = CS_OFFLINE;
+    t->connection_state = kCsOffline;
     t->type = kTransportLocal;
     t->adb_port = 0;
 
 #if ADB_HOST
-    if (HOST && local) {
+    if (local) {
         adb_mutex_lock( &local_transports_lock );
         {
             t->adb_port = adb_port;
@@ -403,12 +410,12 @@ int init_socket_transport(atransport *t, int s, int adb_port, int local)
                     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",
+                D("local transport for port %d already registered (%p)?",
                 adb_port, existing_transport);
                 fail = -1;
             } else if (index < 0) {
                 // Too many emulators.
-                D("cannot register more emulators. Maximum is %d\n",
+                D("cannot register more emulators. Maximum is %d",
                         ADB_LOCAL_TRANSPORT_MAX);
                 fail = -1;
             } else {
index 2b3fe3c..97fc069 100644 (file)
 
 #include "adb.h"
 
+class TestTransport : public atransport {
+public:
+    bool operator==(const atransport& rhs) const {
+        EXPECT_EQ(read_from_remote, rhs.read_from_remote);
+        EXPECT_EQ(write_to_remote, rhs.write_to_remote);
+        EXPECT_EQ(close, rhs.close);
+        EXPECT_EQ(kick, rhs.kick);
+
+        EXPECT_EQ(fd, rhs.fd);
+        EXPECT_EQ(transport_socket, rhs.transport_socket);
+
+        EXPECT_EQ(
+            0, memcmp(&transport_fde, &rhs.transport_fde, sizeof(fdevent)));
+
+        EXPECT_EQ(ref_count, rhs.ref_count);
+        EXPECT_EQ(sync_token, rhs.sync_token);
+        EXPECT_EQ(connection_state, rhs.connection_state);
+        EXPECT_EQ(online, rhs.online);
+        EXPECT_EQ(type, rhs.type);
+
+        EXPECT_EQ(usb, rhs.usb);
+        EXPECT_EQ(sfd, rhs.sfd);
+
+        EXPECT_EQ(serial, rhs.serial);
+        EXPECT_EQ(product, rhs.product);
+        EXPECT_EQ(model, rhs.model);
+        EXPECT_EQ(device, rhs.device);
+        EXPECT_EQ(devpath, rhs.devpath);
+        EXPECT_EQ(adb_port, rhs.adb_port);
+        EXPECT_EQ(kicked, rhs.kicked);
+
+        EXPECT_EQ(key, rhs.key);
+        EXPECT_EQ(0, memcmp(token, rhs.token, TOKEN_SIZE));
+        EXPECT_EQ(0, memcmp(&auth_fde, &rhs.auth_fde, sizeof(fdevent)));
+        EXPECT_EQ(failed_auth_attempts, rhs.failed_auth_attempts);
+
+        EXPECT_EQ(features(), rhs.features());
+
+        return true;
+    }
+};
+
+class TransportSetup {
+public:
+  TransportSetup() {
+#ifdef _WIN32
+    // Use extern instead of including sysdeps.h which brings in various macros
+    // that conflict with APIs used in this file.
+    extern void adb_sysdeps_init(void);
+    adb_sysdeps_init();
+#else
+    // adb_sysdeps_init() is an inline function that we cannot link against.
+#endif
+  }
+};
+
+// Static initializer will call adb_sysdeps_init() before main() to initialize
+// the transport mutex before it is used in the tests. Alternatives would be to
+// use __attribute__((constructor)) here or to use that or a static initializer
+// for adb_sysdeps_init() itself in sysdeps_win32.cpp (caveats of unclear
+// init order), or to use a test fixture whose SetUp() could do the init once.
+static TransportSetup g_TransportSetup;
+
 TEST(transport, kick_transport) {
-  atransport t = {};
+  TestTransport t;
+
   // Mutate some member so we can test that the function is run.
   t.kick = [](atransport* trans) { trans->fd = 42; };
-  atransport expected = t;
+
+  TestTransport expected;
+  expected.kick = t.kick;
   expected.fd = 42;
   expected.kicked = 1;
+
   kick_transport(&t);
   ASSERT_EQ(42, t.fd);
   ASSERT_EQ(1, t.kicked);
-  ASSERT_EQ(0, memcmp(&expected, &t, sizeof(atransport)));
+  ASSERT_EQ(expected, t);
 }
 
 TEST(transport, kick_transport_already_kicked) {
   // Ensure that the transport is not modified if the transport has already been
   // kicked.
-  atransport t = {};
+  TestTransport t;
   t.kicked = 1;
   t.kick = [](atransport*) { FAIL() << "Kick should not have been called"; };
-  atransport expected = t;
+
+  TestTransport expected;
+  expected.kicked = 1;
+  expected.kick = t.kick;
+
   kick_transport(&t);
-  ASSERT_EQ(0, memcmp(&expected, &t, sizeof(atransport)));
+  ASSERT_EQ(expected, t);
+}
+
+static void DisconnectFunc(void* arg, atransport*) {
+    int* count = reinterpret_cast<int*>(arg);
+    ++*count;
+}
+
+TEST(transport, RunDisconnects) {
+    atransport t;
+    // RunDisconnects() can be called with an empty atransport.
+    t.RunDisconnects();
+
+    int count = 0;
+    adisconnect disconnect;
+    disconnect.func = DisconnectFunc;
+    disconnect.opaque = &count;
+    t.AddDisconnect(&disconnect);
+    t.RunDisconnects();
+    ASSERT_EQ(1, count);
+
+    // disconnect should have been removed automatically.
+    t.RunDisconnects();
+    ASSERT_EQ(1, count);
+
+    count = 0;
+    t.AddDisconnect(&disconnect);
+    t.RemoveDisconnect(&disconnect);
+    t.RunDisconnects();
+    ASSERT_EQ(0, count);
+}
+
+TEST(transport, SetFeatures) {
+    atransport t;
+    ASSERT_EQ(0U, t.features().size());
+
+    t.SetFeatures(FeatureSetToString(FeatureSet{"foo"}));
+    ASSERT_EQ(1U, t.features().size());
+    ASSERT_TRUE(t.has_feature("foo"));
+
+    t.SetFeatures(FeatureSetToString(FeatureSet{"foo", "bar"}));
+    ASSERT_EQ(2U, t.features().size());
+    ASSERT_TRUE(t.has_feature("foo"));
+    ASSERT_TRUE(t.has_feature("bar"));
+
+    t.SetFeatures(FeatureSetToString(FeatureSet{"foo", "bar", "foo"}));
+    ASSERT_EQ(2U, t.features().size());
+    ASSERT_TRUE(t.has_feature("foo"));
+    ASSERT_TRUE(t.has_feature("bar"));
+
+    t.SetFeatures(FeatureSetToString(FeatureSet{"bar", "baz"}));
+    ASSERT_EQ(2U, t.features().size());
+    ASSERT_FALSE(t.has_feature("foo"));
+    ASSERT_TRUE(t.has_feature("bar"));
+    ASSERT_TRUE(t.has_feature("baz"));
+
+    t.SetFeatures("");
+    ASSERT_EQ(0U, t.features().size());
 }
 
-// Disabled because the function currently segfaults for a zeroed atransport. I
-// want to make sure I understand how this is working at all before I try fixing
-// that.
-TEST(transport, DISABLED_run_transport_disconnects_zeroed_atransport) {
-  atransport t = {};
-  run_transport_disconnects(&t);
+TEST(transport, parse_banner_no_features) {
+    atransport t;
+
+    parse_banner("host::", &t);
+
+    ASSERT_EQ(0U, t.features().size());
+    ASSERT_EQ(kCsHost, t.connection_state);
+
+    ASSERT_EQ(nullptr, t.product);
+    ASSERT_EQ(nullptr, t.model);
+    ASSERT_EQ(nullptr, t.device);
+}
+
+TEST(transport, parse_banner_product_features) {
+    atransport t;
+
+    const char banner[] =
+        "host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;";
+    parse_banner(banner, &t);
+
+    ASSERT_EQ(kCsHost, t.connection_state);
+
+    ASSERT_EQ(0U, t.features().size());
+
+    ASSERT_EQ(std::string("foo"), t.product);
+    ASSERT_EQ(std::string("bar"), t.model);
+    ASSERT_EQ(std::string("baz"), t.device);
+}
+
+TEST(transport, parse_banner_features) {
+    atransport t;
+
+    const char banner[] =
+        "host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;"
+        "features=woodly,doodly";
+    parse_banner(banner, &t);
+
+    ASSERT_EQ(kCsHost, t.connection_state);
+
+    ASSERT_EQ(2U, t.features().size());
+    ASSERT_TRUE(t.has_feature("woodly"));
+    ASSERT_TRUE(t.has_feature("doodly"));
+
+    ASSERT_EQ(std::string("foo"), t.product);
+    ASSERT_EQ(std::string("bar"), t.model);
+    ASSERT_EQ(std::string("baz"), t.device);
 }
index cdabffe..263f9e7 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_TRANSPORT
+#define TRACE_TAG TRANSPORT
 
 #include "sysdeps.h"
 #include "transport.h"
 static int remote_read(apacket *p, atransport *t)
 {
     if(usb_read(t->usb, &p->msg, sizeof(amessage))){
-        D("remote usb: read terminated (message)\n");
+        D("remote usb: read terminated (message)");
         return -1;
     }
 
-    if(check_header(p)) {
-        D("remote usb: check_header failed\n");
+    if(check_header(p, t)) {
+        D("remote usb: check_header failed");
         return -1;
     }
 
     if(p->msg.data_length) {
         if(usb_read(t->usb, p->data, p->msg.data_length)){
-            D("remote usb: terminated (data)\n");
+            D("remote usb: terminated (data)");
             return -1;
         }
     }
 
     if(check_data(p)) {
-        D("remote usb: check_data failed\n");
+        D("remote usb: check_data failed");
         return -1;
     }
 
@@ -57,12 +57,12 @@ static int remote_write(apacket *p, atransport *t)
     unsigned size = p->msg.data_length;
 
     if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
-        D("remote usb: 1 - write terminated\n");
+        D("remote usb: 1 - write terminated");
         return -1;
     }
     if(p->msg.data_length == 0) return 0;
     if(usb_write(t->usb, &p->data, size)) {
-        D("remote usb: 2 - write terminated\n");
+        D("remote usb: 2 - write terminated");
         return -1;
     }
 
@@ -80,9 +80,9 @@ static void remote_kick(atransport *t)
     usb_kick(t->usb);
 }
 
-void init_usb_transport(atransport *t, usb_handle *h, int state)
+void init_usb_transport(atransport *t, usb_handle *h, ConnectionState state)
 {
-    D("transport: usb\n");
+    D("transport: usb");
     t->close = remote_close;
     t->kick = remote_kick;
     t->read_from_remote = remote_read;
@@ -91,12 +91,6 @@ void init_usb_transport(atransport *t, usb_handle *h, int state)
     t->connection_state = state;
     t->type = kTransportUsb;
     t->usb = h;
-
-#if ADB_HOST
-    HOST = 1;
-#else
-    HOST = 0;
-#endif
 }
 
 #if ADB_HOST
index 71baaee..0358b62 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_USB
+#define TRACE_TAG USB
 
 #include "sysdeps.h"
 
@@ -22,6 +22,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/usb/ch9.h>
 #include <linux/usbdevice_fs.h>
 #include <linux/version.h>
 #include <stdio.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <linux/usb/ch9.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <list>
+#include <mutex>
+#include <string>
 
 #include <base/file.h>
 #include <base/stringprintf.h>
 #include "adb.h"
 #include "transport.h"
 
+using namespace std::literals;
+
 /* usb scan debugging is waaaay too verbose */
 #define DBGX(x...)
 
-ADB_MUTEX_DEFINE( usb_lock );
-
-struct usb_handle
-{
-    usb_handle *prev;
-    usb_handle *next;
+struct usb_handle {
+    ~usb_handle() {
+      if (fd != -1) unix_close(fd);
+    }
 
-    char fname[64];
-    int desc;
+    std::string path;
+    int fd = -1;
     unsigned char ep_in;
     unsigned char ep_out;
 
     unsigned zero_mask;
-    unsigned writeable;
+    unsigned writeable = 1;
 
-    struct usbdevfs_urb urb_in;
-    struct usbdevfs_urb urb_out;
+    usbdevfs_urb urb_in;
+    usbdevfs_urb urb_out;
 
-    int urb_in_busy;
-    int urb_out_busy;
-    int dead;
+    bool urb_in_busy = false;
+    bool urb_out_busy = false;
+    bool dead = false;
 
-    adb_cond_t notify;
-    adb_mutex_t lock;
+    std::condition_variable cv;
+    std::mutex mutex;
 
     // for garbage collecting disconnected devices
-    int mark;
+    bool mark;
 
     // ID of thread currently in REAPURB
-    pthread_t reaper_thread;
+    pthread_t reaper_thread = 0;
 };
 
-static usb_handle handle_list = {
-    .prev = &handle_list,
-    .next = &handle_list,
-};
+static auto& g_usb_handles_mutex = *new std::mutex();
+static auto& g_usb_handles = *new std::list<usb_handle*>();
 
-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)) {
+static int is_known_device(const char* dev_name) {
+    std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+    for (usb_handle* usb : g_usb_handles) {
+        if (usb->path == dev_name) {
             // set mark flag to indicate this device is still alive
-            usb->mark = 1;
-            adb_mutex_unlock(&usb_lock);
+            usb->mark = true;
             return 1;
         }
     }
-    adb_mutex_unlock(&usb_lock);
     return 0;
 }
 
-static void kick_disconnected_devices()
-{
-    usb_handle *usb;
-
-    adb_mutex_lock(&usb_lock);
+static void kick_disconnected_devices() {
+    std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
     // kick any devices in the device list that were not found in the device scan
-    for(usb = handle_list.next; usb != &handle_list; usb = usb->next){
-        if (usb->mark == 0) {
+    for (usb_handle* usb : g_usb_handles) {
+        if (!usb->mark) {
             usb_kick(usb);
         } else {
-            usb->mark = 0;
+            usb->mark = false;
         }
     }
-    adb_mutex_unlock(&usb_lock);
-
 }
 
-static inline int badname(const char *name)
-{
-    while(*name) {
-        if(!isdigit(*name++)) return 1;
+static inline bool contains_non_digit(const char* name) {
+    while (*name) {
+        if (!isdigit(*name++)) return true;
     }
-    return 0;
+    return false;
 }
 
-static void find_usb_device(const char *base,
+static void find_usb_device(const std::string& base,
         void (*register_device_callback)
-                (const char *, const char *, unsigned char, unsigned char, int, int, unsigned))
+                (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 ;
+    std::unique_ptr<DIR, int(*)(DIR*)> bus_dir(opendir(base.c_str()), closedir);
+    if (!bus_dir) return;
 
-    busdir = opendir(base);
-    if(busdir == 0) return;
+    dirent* de;
+    while ((de = readdir(bus_dir.get())) != 0) {
+        if (contains_non_digit(de->d_name)) continue;
 
-    while((de = readdir(busdir)) != 0) {
-        if(badname(de->d_name)) continue;
+        std::string bus_name = base + "/" + de->d_name;
 
-        snprintf(busname, sizeof busname, "%s/%s", base, de->d_name);
-        devdir = opendir(busname);
-        if(devdir == 0) continue;
+        std::unique_ptr<DIR, int(*)(DIR*)> dev_dir(opendir(bus_name.c_str()), closedir);
+        if (!dev_dir) continue;
 
-//        DBGX("[ scanning %s ]\n", busname);
-        while((de = readdir(devdir))) {
+        while ((de = readdir(dev_dir.get()))) {
             unsigned char devdesc[4096];
             unsigned char* bufptr = devdesc;
             unsigned char* bufend;
@@ -153,28 +141,26 @@ static void find_usb_device(const char *base,
             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 (contains_non_digit(de->d_name)) continue;
 
-            if(known_device(devname)) {
-                DBGX("skipping %s\n", devname);
+            std::string dev_name = bus_name + "/" + de->d_name;
+            if (is_known_device(dev_name.c_str())) {
                 continue;
             }
 
-//            DBGX("[ scanning %s ]\n", devname);
-            if((fd = unix_open(devname, O_RDONLY | O_CLOEXEC)) < 0) {
+            int fd = unix_open(dev_name.c_str(), O_RDONLY | O_CLOEXEC);
+            if (fd == -1) {
                 continue;
             }
 
-            desclength = adb_read(fd, devdesc, sizeof(devdesc));
+            size_t desclength = unix_read(fd, devdesc, sizeof(devdesc));
             bufend = bufptr + desclength;
 
                 // should have device and configuration descriptors, and atleast two endpoints
             if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
-                D("desclength %zu is too small\n", desclength);
-                adb_close(fd);
+                D("desclength %zu is too small", desclength);
+                unix_close(fd);
                 continue;
             }
 
@@ -182,20 +168,20 @@ static void find_usb_device(const char *base,
             bufptr += USB_DT_DEVICE_SIZE;
 
             if((device->bLength != USB_DT_DEVICE_SIZE) || (device->bDescriptorType != USB_DT_DEVICE)) {
-                adb_close(fd);
+                unix_close(fd);
                 continue;
             }
 
             vid = device->idVendor;
             pid = device->idProduct;
-            DBGX("[ %s is V:%04x P:%04x ]\n", devname, vid, pid);
+            DBGX("[ %s is V:%04x P:%04x ]\n", dev_name.c_str(), vid, pid);
 
                 // should have config descriptor next
             config = (struct usb_config_descriptor *)bufptr;
             bufptr += USB_DT_CONFIG_SIZE;
             if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
-                D("usb_config_descriptor not found\n");
-                adb_close(fd);
+                D("usb_config_descriptor not found");
+                unix_close(fd);
                 continue;
             }
 
@@ -209,7 +195,7 @@ static void find_usb_device(const char *base,
                     bufptr += length;
 
                     if (length != USB_DT_INTERFACE_SIZE) {
-                        D("interface descriptor has wrong size\n");
+                        D("interface descriptor has wrong size");
                         break;
                     }
 
@@ -225,7 +211,7 @@ static void find_usb_device(const char *base,
                         struct stat st;
                         char pathbuf[128];
                         char link[256];
-                        char *devpath = NULL;
+                        char *devpath = nullptr;
 
                         DBGX("looking for bulk endpoints\n");
                             // looks like ADB...
@@ -251,14 +237,14 @@ static void find_usb_device(const char *base,
                             ep1->bDescriptorType != USB_DT_ENDPOINT ||
                             ep2->bLength != USB_DT_ENDPOINT_SIZE ||
                             ep2->bDescriptorType != USB_DT_ENDPOINT) {
-                            D("endpoints not found\n");
+                            D("endpoints not found");
                             break;
                         }
 
                             // both endpoints should be bulk
                         if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
                             ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
-                            D("bulk endpoints not found\n");
+                            D("bulk endpoints not found");
                             continue;
                         }
                             /* aproto 01 needs 0 termination */
@@ -267,6 +253,7 @@ static void find_usb_device(const char *base,
                         }
 
                             // we have a match.  now we just need to figure out which is in and which is out.
+                        unsigned char local_ep_in, local_ep_out;
                         if (ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
                             local_ep_in = ep1->bEndpointAddress;
                             local_ep_out = ep2->bEndpointAddress;
@@ -277,14 +264,12 @@ static void find_usb_device(const char *base,
 
                             // 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);
+                            ssize_t link_len = readlink(pathbuf, link, sizeof(link) - 1);
                             if (link_len > 0) {
                                 link[link_len] = '\0';
-                                slash = strrchr(link, '/');
+                                const char* slash = strrchr(link, '/');
                                 if (slash) {
                                     snprintf(pathbuf, sizeof(pathbuf),
                                              "usb:%s", slash + 1);
@@ -293,7 +278,7 @@ static void find_usb_device(const char *base,
                             }
                         }
 
-                        register_device_callback(devname, devpath,
+                        register_device_callback(dev_name.c_str(), devpath,
                                 local_ep_in, local_ep_out,
                                 interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
                         break;
@@ -303,77 +288,55 @@ static void find_usb_device(const char *base,
                 }
             } // end of while
 
-            adb_close(fd);
-        } // end of devdir while
-        closedir(devdir);
-    } //end of busdir while
-    closedir(busdir);
-}
-
-void usb_cleanup()
-{
+            unix_close(fd);
+        }
+    }
 }
 
-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;
+static int usb_bulk_write(usb_handle* h, const void* data, int len) {
+    std::unique_lock<std::mutex> lock(h->mutex);
+    D("++ usb_bulk_write ++");
 
+    usbdevfs_urb* urb = &h->urb_out;
     memset(urb, 0, sizeof(*urb));
     urb->type = USBDEVFS_URB_TYPE_BULK;
     urb->endpoint = h->ep_out;
     urb->status = -1;
-    urb->buffer = (void*) data;
+    urb->buffer = const_cast<void*>(data);
     urb->buffer_length = len;
 
-    D("++ write ++\n");
-
-    adb_mutex_lock(&h->lock);
-    if(h->dead) {
-        res = -1;
-        goto fail;
+    if (h->dead) {
+        errno = EINVAL;
+        return -1;
     }
-    do {
-        res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
-    } while((res < 0) && (errno == EINTR));
 
-    if(res < 0) {
-        goto fail;
+    if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
+        return -1;
     }
 
-    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;
+    h->urb_out_busy = true;
+    while (true) {
+        auto now = std::chrono::system_clock::now();
+        if (h->cv.wait_until(lock, now + 5s) == std::cv_status::timeout || h->dead) {
+            // TODO: call USBDEVFS_DISCARDURB?
+            errno = ETIMEDOUT;
+            return -1;
         }
-        if(h->urb_out_busy == 0) {
-            if(urb->status == 0) {
-                res = urb->actual_length;
+        if (!h->urb_out_busy) {
+            if (urb->status != 0) {
+                errno = -urb->status;
+                return -1;
             }
-            break;
+            return urb->actual_length;
         }
     }
-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;
+static int usb_bulk_read(usb_handle* h, void* data, int len) {
+    std::unique_lock<std::mutex> lock(h->mutex);
+    D("++ usb_bulk_read ++");
 
-    D("++ usb_bulk_read ++\n");
+    usbdevfs_urb* urb = &h->urb_in;
     memset(urb, 0, sizeof(*urb));
     urb->type = USBDEVFS_URB_TYPE_BULK;
     urb->endpoint = h->ep_in;
@@ -381,103 +344,79 @@ static int usb_bulk_read(usb_handle *h, void *data, int len)
     urb->buffer = data;
     urb->buffer_length = len;
 
-
-    adb_mutex_lock(&h->lock);
-    if(h->dead) {
-        res = -1;
-        goto fail;
+    if (h->dead) {
+        errno = EINVAL;
+        return -1;
     }
-    do {
-        res = ioctl(h->desc, USBDEVFS_SUBMITURB, urb);
-    } while((res < 0) && (errno == EINTR));
 
-    if(res < 0) {
-        goto fail;
+    if (TEMP_FAILURE_RETRY(ioctl(h->fd, USBDEVFS_SUBMITURB, urb)) == -1) {
+        return -1;
     }
 
-    h->urb_in_busy = 1;
-    for(;;) {
-        D("[ reap urb - wait ]\n");
+    h->urb_in_busy = true;
+    while (true) {
+        D("[ reap urb - wait ]");
         h->reaper_thread = pthread_self();
-        adb_mutex_unlock(&h->lock);
-        res = ioctl(h->desc, USBDEVFS_REAPURB, &out);
+        int fd = h->fd;
+        lock.unlock();
+
+        // This ioctl must not have TEMP_FAILURE_RETRY because we send SIGALRM to break out.
+        usbdevfs_urb* out = nullptr;
+        int res = ioctl(fd, USBDEVFS_REAPURB, &out);
         int saved_errno = errno;
-        adb_mutex_lock(&h->lock);
+
+        lock.lock();
         h->reaper_thread = 0;
-        if(h->dead) {
-            res = -1;
-            break;
+        if (h->dead) {
+            errno = EINVAL;
+            return -1;
         }
-        if(res < 0) {
-            if(saved_errno == EINTR) {
+        if (res < 0) {
+            if (saved_errno == EINTR) {
                 continue;
             }
-            D("[ reap urb - error ]\n");
-            break;
+            D("[ reap urb - error ]");
+            errno = saved_errno;
+            return -1;
         }
-        D("[ urb @%p status = %d, actual = %d ]\n",
-            out, out->status, out->actual_length);
-
-        if(out == &h->urb_in) {
-            D("[ reap urb - IN complete ]\n");
-            h->urb_in_busy = 0;
-            if(urb->status == 0) {
-                res = urb->actual_length;
-            } else {
-                res = -1;
+        D("[ urb @%p status = %d, actual = %d ]", out, out->status, out->actual_length);
+
+        if (out == &h->urb_in) {
+            D("[ reap urb - IN complete ]");
+            h->urb_in_busy = false;
+            if (urb->status != 0) {
+                errno = -urb->status;
+                return -1;
             }
-            break;
+            return urb->actual_length;
         }
-        if(out == &h->urb_out) {
-            D("[ reap urb - OUT compelete ]\n");
-            h->urb_out_busy = 0;
-            adb_cond_broadcast(&h->notify);
+        if (out == &h->urb_out) {
+            D("[ reap urb - OUT compelete ]");
+            h->urb_out_busy = false;
+            h->cv.notify_all();
         }
     }
-fail:
-    adb_mutex_unlock(&h->lock);
-    D("-- usb_bulk_read --\n");
-    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;
-
-    D("++ usb_write ++\n");
-    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;
-        }
-    }
+    D("++ usb_write ++");
 
-    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;
+    unsigned char *data = (unsigned char*) _data;
+    int n = usb_bulk_write(h, data, len);
+    if (n != len) {
+        D("ERROR: n = %d, errno = %d (%s)", n, errno, strerror(errno));
+        return -1;
     }
 
-    if(need_zero){
-        n = usb_bulk_write(h, _data, 0);
-        return n;
+    if (h->zero_mask && !(len & h->zero_mask)) {
+        // If we need 0-markers and our transfer is an even multiple of the packet size,
+        // then send a zero marker.
+        return usb_bulk_write(h, _data, 0);
     }
 
-    D("-- usb_write --\n");
+    D("-- usb_write --");
     return 0;
 }
 
@@ -486,23 +425,23 @@ int usb_read(usb_handle *h, void *_data, int len)
     unsigned char *data = (unsigned char*) _data;
     int n;
 
-    D("++ usb_read ++\n");
+    D("++ usb_read ++");
     while(len > 0) {
-        int xfer = (len > 4096) ? 4096 : len;
+        int xfer = len;
 
-        D("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
+        D("[ usb read %d fd = %d], path=%s", xfer, h->fd, h->path.c_str());
         n = usb_bulk_read(h, data, xfer);
-        D("[ usb read %d ] = %d, fname=%s\n", xfer, n, h->fname);
+        D("[ usb read %d ] = %d, path=%s", xfer, n, h->path.c_str());
         if(n != xfer) {
-            if((errno == ETIMEDOUT) && (h->desc != -1)) {
-                D("[ timeout ]\n");
+            if((errno == ETIMEDOUT) && (h->fd != -1)) {
+                D("[ timeout ]");
                 if(n > 0){
                     data += n;
                     len -= n;
                 }
                 continue;
             }
-            D("ERROR: n = %d, errno = %d (%s)\n",
+            D("ERROR: n = %d, errno = %d (%s)",
                 n, errno, strerror(errno));
             return -1;
         }
@@ -511,16 +450,15 @@ int usb_read(usb_handle *h, void *_data, int len)
         data += xfer;
     }
 
-    D("-- usb_read --\n");
+    D("-- usb_read --");
     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;
+void usb_kick(usb_handle* h) {
+    std::lock_guard<std::mutex> lock(h->mutex);
+    D("[ kicking %p (fd = %d) ]", h, h->fd);
+    if (!h->dead) {
+        h->dead = true;
 
         if (h->writeable) {
             /* HACK ALERT!
@@ -536,34 +474,27 @@ void usb_kick(usb_handle *h)
             ** 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);
+            ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_in);
+            ioctl(h->fd, USBDEVFS_DISCARDURB, &h->urb_out);
             h->urb_in.status = -ENODEV;
             h->urb_out.status = -ENODEV;
-            h->urb_in_busy = 0;
-            h->urb_out_busy = 0;
-            adb_cond_broadcast(&h->notify);
+            h->urb_in_busy = false;
+            h->urb_out_busy = false;
+            h->cv.notify_all();
         } 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);
+int usb_close(usb_handle* h) {
+    std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+    g_usb_handles.remove(h);
+
+    D("-- usb close %p (fd = %d) --", h, h->fd);
+
+    delete h;
+
     return 0;
 }
 
@@ -576,54 +507,44 @@ static void register_device(const char* dev_name, const char* dev_path,
     // 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
+    // If we have a usb_handle on the list of handles with a matching name, we
     // have no further work to do.
-    adb_mutex_lock(&usb_lock);
-    for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
-        if (!strcmp(usb->fname, dev_name)) {
-            adb_mutex_unlock(&usb_lock);
-            return;
+    {
+        std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+        for (usb_handle* usb: g_usb_handles) {
+            if (usb->path == dev_name) {
+                return;
+            }
         }
     }
-    adb_mutex_unlock(&usb_lock);
 
-    D("[ usb located new device %s (%d/%d/%d) ]\n", dev_name, ep_in, ep_out, interface);
-    usb_handle* usb = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
-    if (usb == nullptr) fatal("couldn't allocate usb_handle");
-    strcpy(usb->fname, dev_name);
+    D("[ usb located new device %s (%d/%d/%d) ]", dev_name, ep_in, ep_out, interface);
+    std::unique_ptr<usb_handle> usb(new usb_handle);
+    usb->path = dev_name;
     usb->ep_in = ep_in;
     usb->ep_out = ep_out;
     usb->zero_mask = zero_mask;
-    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;
+    // Initialize mark so we don't get garbage collected after the device scan.
+    usb->mark = true;
 
-    usb->desc = unix_open(usb->fname, O_RDWR | O_CLOEXEC);
-    if (usb->desc == -1) {
+    usb->fd = unix_open(usb->path.c_str(), O_RDWR | O_CLOEXEC);
+    if (usb->fd == -1) {
         // Opening RW failed, so see if we have RO access.
-        usb->desc = unix_open(usb->fname, O_RDONLY | O_CLOEXEC);
-        if (usb->desc == -1) {
-            D("[ usb open %s failed: %s]\n", usb->fname, strerror(errno));
-            free(usb);
+        usb->fd = unix_open(usb->path.c_str(), O_RDONLY | O_CLOEXEC);
+        if (usb->fd == -1) {
+            D("[ usb open %s failed: %s]", usb->path.c_str(), strerror(errno));
             return;
         }
         usb->writeable = 0;
     }
 
-    D("[ usb opened %s%s, fd=%d]\n", usb->fname,
-      (usb->writeable ? "" : " (read-only)"), usb->desc);
+    D("[ usb opened %s%s, fd=%d]",
+      usb->path.c_str(), (usb->writeable ? "" : " (read-only)"), usb->fd);
 
     if (usb->writeable) {
-        if (ioctl(usb->desc, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
-            D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]\n",
-              usb->desc, strerror(errno));
-            adb_close(usb->desc);
-            free(usb);
+        if (ioctl(usb->fd, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
+            D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]", usb->fd, strerror(errno));
             return;
         }
     }
@@ -633,7 +554,7 @@ static void register_device(const char* dev_name, const char* dev_path,
         "/sys/bus/usb/devices/%s/serial", dev_path + 4);
     std::string serial;
     if (!android::base::ReadFileToString(serial_path, &serial)) {
-        D("[ usb read %s failed: %s ]\n", serial_path.c_str(), strerror(errno));
+        D("[ usb read %s failed: %s ]", serial_path.c_str(), strerror(errno));
         // We don't actually want to treat an unknown serial as an error because
         // devices aren't able to communicate a serial number in early bringup.
         // http://b/20883914
@@ -642,18 +563,17 @@ static void register_device(const char* dev_name, const char* dev_path,
     serial = android::base::Trim(serial);
 
     // 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.c_str(), dev_path, usb->writeable);
+    usb_handle* done_usb = usb.release();
+    {
+        std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
+        g_usb_handles.push_back(done_usb);
+    }
+    register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
 }
 
 static void* device_poll_thread(void* unused) {
-    D("Created device thread\n");
+    adb_thread_setname("device poll");
+    D("Created device thread");
     while (true) {
         // TODO: Use inotify.
         find_usb_device("/dev/bus/usb", register_device);
@@ -663,22 +583,15 @@ static void* device_poll_thread(void* unused) {
     return nullptr;
 }
 
-static void sigalrm_handler(int signo) {
-    // don't need to do anything here
-}
-
-void usb_init()
-{
-    adb_thread_t tid;
-    struct sigaction    actions;
-
+void usb_init() {
+    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);
+    actions.sa_handler = [](int) {};
+    sigaction(SIGALRM, &actions, nullptr);
 
-    if(adb_thread_create(&tid, device_poll_thread, NULL)){
-        fatal_errno("cannot create input thread");
+    if (!adb_thread_create(device_poll_thread, nullptr)) {
+        fatal_errno("cannot create device_poll thread");
     }
 }
index f3db346..3495a71 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_USB
+#define TRACE_TAG USB
 
 #include "sysdeps.h"
 
@@ -88,11 +88,14 @@ struct desc_v2 {
     __le32 fs_count;
     __le32 hs_count;
     __le32 ss_count;
+    __le32 os_count;
     struct func_desc fs_descs, hs_descs;
     struct ss_func_desc ss_descs;
+    struct usb_os_desc_header os_header;
+    struct usb_ext_compat_desc os_desc;
 } __attribute__((packed));
 
-struct func_desc fs_descriptors = {
+static struct func_desc fs_descriptors = {
     .intf = {
         .bLength = sizeof(fs_descriptors.intf),
         .bDescriptorType = USB_DT_INTERFACE,
@@ -119,7 +122,7 @@ struct func_desc fs_descriptors = {
     },
 };
 
-struct func_desc hs_descriptors = {
+static struct func_desc hs_descriptors = {
     .intf = {
         .bLength = sizeof(hs_descriptors.intf),
         .bDescriptorType = USB_DT_INTERFACE,
@@ -181,6 +184,24 @@ static struct ss_func_desc ss_descriptors = {
     },
 };
 
+struct usb_ext_compat_desc os_desc_compat = {
+    .bFirstInterfaceNumber = 0,
+    .Reserved1 = cpu_to_le32(1),
+    .CompatibleID = {0},
+    .SubCompatibleID = {0},
+    .Reserved2 = {0},
+};
+
+static struct usb_os_desc_header os_desc_header = {
+    .interface = cpu_to_le32(1),
+    .dwLength = cpu_to_le32(sizeof(os_desc_header) + sizeof(os_desc_compat)),
+    .bcdVersion = cpu_to_le32(1),
+    .wIndex = cpu_to_le32(4),
+    .bCount = cpu_to_le32(1),
+    .Reserved = cpu_to_le32(0),
+};
+
+
 #define STR_INTERFACE_ "ADB Interface"
 
 static const struct {
@@ -209,6 +230,8 @@ static void *usb_adb_open_thread(void *x)
     struct usb_handle *usb = (struct usb_handle *)x;
     int fd;
 
+    adb_thread_setname("usb open");
+
     while (true) {
         // wait until the USB device needs opening
         adb_mutex_lock(&usb->lock);
@@ -216,7 +239,7 @@ static void *usb_adb_open_thread(void *x)
             adb_cond_wait(&usb->notify, &usb->lock);
         adb_mutex_unlock(&usb->lock);
 
-        D("[ usb_thread - opening device ]\n");
+        D("[ usb_thread - opening device ]");
         do {
             /* XXX use inotify? */
             fd = unix_open("/dev/android_adb", O_RDWR);
@@ -228,12 +251,12 @@ static void *usb_adb_open_thread(void *x)
                 adb_sleep_ms(1000);
             }
         } while (fd < 0);
-        D("[ opening device succeeded ]\n");
+        D("[ opening device succeeded ]");
 
         close_on_exec(fd);
         usb->fd = fd;
 
-        D("[ usb_thread - registering device ]\n");
+        D("[ usb_thread - registering device ]");
         register_usb_transport(usb, 0, 0, 1);
     }
 
@@ -245,37 +268,43 @@ 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);
+    D("about to write (fd=%d, len=%d)", h->fd, len);
+    n = unix_write(h->fd, data, len);
     if(n != len) {
-        D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+        D("ERROR: fd = %d, n = %d, errno = %d (%s)",
             h->fd, n, errno, strerror(errno));
         return -1;
     }
-    D("[ done fd=%d ]\n", h->fd);
+    D("[ done fd=%d ]", 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("about to read (fd=%d, len=%d)", h->fd, len);
+    while (len > 0) {
+        // The kernel implementation of adb_read in f_adb.c doesn't support
+        // reads larger then 4096 bytes. Read the data in 4096 byte chunks to
+        // avoid the issue. (The ffs implementation doesn't have this limit.)
+        int bytes_to_read = len < 4096 ? len : 4096;
+        int n = unix_read(h->fd, data, bytes_to_read);
+        if (n != bytes_to_read) {
+            D("ERROR: fd = %d, n = %d, errno = %d (%s)",
+                h->fd, n, errno, strerror(errno));
+            return -1;
+        }
+        len -= n;
+        data = ((char*)data) + n;
     }
-    D("[ done fd=%d ]\n", h->fd);
+    D("[ done fd=%d ]", h->fd);
     return 0;
 }
 
 static void usb_adb_kick(usb_handle *h)
 {
-    D("usb_kick\n");
+    D("usb_kick");
     adb_mutex_lock(&h->lock);
-    adb_close(h->fd);
+    unix_close(h->fd);
     h->fd = -1;
 
     // notify usb_adb_open_thread that we are disconnected
@@ -303,14 +332,13 @@ static void usb_adb_init()
     // and when we are not.
     int fd = unix_open("/dev/android_adb_enable", O_RDWR);
     if (fd < 0) {
-       D("failed to open /dev/android_adb_enable\n");
+       D("failed to open /dev/android_adb_enable");
     } else {
         close_on_exec(fd);
     }
 
-    D("[ usb_init - starting thread ]\n");
-    adb_thread_t tid;
-    if(adb_thread_create(&tid, usb_adb_open_thread, h)){
+    D("[ usb_init - starting thread ]");
+    if (!adb_thread_create(usb_adb_open_thread, h)) {
         fatal_errno("cannot create usb thread");
     }
 }
@@ -325,19 +353,22 @@ static void init_functionfs(struct usb_handle *h)
     v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
     v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
     v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
-                                 FUNCTIONFS_HAS_SS_DESC;
+                                 FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC;
     v2_descriptor.fs_count = 3;
     v2_descriptor.hs_count = 3;
     v2_descriptor.ss_count = 5;
+    v2_descriptor.os_count = 1;
     v2_descriptor.fs_descs = fs_descriptors;
     v2_descriptor.hs_descs = hs_descriptors;
     v2_descriptor.ss_descs = ss_descriptors;
+    v2_descriptor.os_header = os_desc_header;
+    v2_descriptor.os_desc = os_desc_compat;
 
     if (h->control < 0) { // might have already done this before
-        D("OPENING %s\n", USB_FFS_ADB_EP0);
+        D("OPENING %s", USB_FFS_ADB_EP0);
         h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
         if (h->control < 0) {
-            D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno);
+            D("[ %s: cannot open control endpoint: errno=%d]", USB_FFS_ADB_EP0, errno);
             goto err;
         }
 
@@ -349,30 +380,30 @@ static void init_functionfs(struct usb_handle *h)
             v1_descriptor.header.hs_count = 3;
             v1_descriptor.fs_descs = fs_descriptors;
             v1_descriptor.hs_descs = hs_descriptors;
-            D("[ %s: Switching to V1_descriptor format errno=%d ]\n", USB_FFS_ADB_EP0, errno);
+            D("[ %s: Switching to V1_descriptor format errno=%d ]", USB_FFS_ADB_EP0, errno);
             ret = adb_write(h->control, &v1_descriptor, sizeof(v1_descriptor));
             if (ret < 0) {
-                D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno);
+                D("[ %s: write descriptors failed: errno=%d ]", USB_FFS_ADB_EP0, errno);
                 goto err;
             }
         }
 
         ret = adb_write(h->control, &strings, sizeof(strings));
         if (ret < 0) {
-            D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno);
+            D("[ %s: writing strings failed: errno=%d]", USB_FFS_ADB_EP0, errno);
             goto err;
         }
     }
 
     h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
     if (h->bulk_out < 0) {
-        D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno);
+        D("[ %s: cannot open bulk-out ep: errno=%d ]", USB_FFS_ADB_OUT, errno);
         goto err;
     }
 
     h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
     if (h->bulk_in < 0) {
-        D("[ %s: cannot open bulk-in ep: errno=%d ]\n", USB_FFS_ADB_IN, errno);
+        D("[ %s: cannot open bulk-in ep: errno=%d ]", USB_FFS_ADB_IN, errno);
         goto err;
     }
 
@@ -398,6 +429,8 @@ static void *usb_ffs_open_thread(void *x)
 {
     struct usb_handle *usb = (struct usb_handle *)x;
 
+    adb_thread_setname("usb ffs open");
+
     while (true) {
         // wait until the USB device needs opening
         adb_mutex_lock(&usb->lock);
@@ -415,7 +448,7 @@ static void *usb_ffs_open_thread(void *x)
         }
         property_set("sys.usb.ffs.ready", "1");
 
-        D("[ usb_thread - registering device ]\n");
+        D("[ usb_thread - registering device ]");
         register_usb_transport(usb, 0, 0, 1);
     }
 
@@ -426,64 +459,54 @@ static void *usb_ffs_open_thread(void *x)
 static int bulk_write(int bulk_in, const uint8_t* buf, size_t length)
 {
     size_t count = 0;
-    int ret;
 
-    do {
-        ret = adb_write(bulk_in, buf + count, length - count);
-        if (ret < 0) {
-            if (errno != EINTR)
-                return ret;
-        } else {
-            count += ret;
-        }
-    } while (count < length);
+    while (count < length) {
+        int ret = adb_write(bulk_in, buf + count, length - count);
+        if (ret < 0) return -1;
+        count += ret;
+    }
 
-    D("[ bulk_write done fd=%d ]\n", bulk_in);
+    D("[ bulk_write done fd=%d ]", bulk_in);
     return count;
 }
 
 static int usb_ffs_write(usb_handle* h, const void* data, int len)
 {
-    D("about to write (fd=%d, len=%d)\n", h->bulk_in, len);
+    D("about to write (fd=%d, len=%d)", h->bulk_in, len);
     int n = bulk_write(h->bulk_in, reinterpret_cast<const uint8_t*>(data), len);
     if (n != len) {
-        D("ERROR: fd = %d, n = %d: %s\n", h->bulk_in, n, strerror(errno));
+        D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
         return -1;
     }
-    D("[ done fd=%d ]\n", h->bulk_in);
+    D("[ done fd=%d ]", h->bulk_in);
     return 0;
 }
 
 static int bulk_read(int bulk_out, uint8_t* buf, size_t length)
 {
     size_t count = 0;
-    int ret;
 
-    do {
-        ret = adb_read(bulk_out, buf + count, length - count);
+    while (count < length) {
+        int ret = adb_read(bulk_out, buf + count, length - count);
         if (ret < 0) {
-            if (errno != EINTR) {
-                D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n",
-                                           bulk_out, length, count);
-                return ret;
-            }
-        } else {
-            count += ret;
+            D("[ bulk_read failed fd=%d length=%zu count=%zu ]", bulk_out, length, count);
+            return -1;
         }
-    } while (count < length);
+        count += ret;
+    }
 
     return count;
 }
 
 static int usb_ffs_read(usb_handle* h, void* data, int len)
 {
-    D("about to read (fd=%d, len=%d)\n", h->bulk_out, len);
+    D("about to read (fd=%d, len=%d)", h->bulk_out, len);
     int n = bulk_read(h->bulk_out, reinterpret_cast<uint8_t*>(data), len);
     if (n != len) {
-        D("ERROR: fd = %d, n = %d: %s\n", h->bulk_out, n, strerror(errno));
+        D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
         return -1;
     }
-    D("[ done fd=%d ]\n", h->bulk_out);
+    D("[ done fd=%d ]", h->bulk_out);
     return 0;
 }
 
@@ -492,12 +515,14 @@ static void usb_ffs_kick(usb_handle *h)
     int err;
 
     err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
-    if (err < 0)
+    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)
+    if (err < 0) {
         D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno);
+    }
 
     adb_mutex_lock(&h->lock);
 
@@ -515,7 +540,7 @@ static void usb_ffs_kick(usb_handle *h)
 
 static void usb_ffs_init()
 {
-    D("[ usb_init - using FunctionFS ]\n");
+    D("[ usb_init - using FunctionFS ]");
 
     usb_handle* h = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
     if (h == nullptr) fatal("couldn't allocate usb_handle");
@@ -530,9 +555,8 @@ static void usb_ffs_init()
     adb_cond_init(&h->notify, 0);
     adb_mutex_init(&h->lock, 0);
 
-    D("[ usb_init - starting thread ]\n");
-    adb_thread_t tid;
-    if (adb_thread_create(&tid, usb_ffs_open_thread, h)){
+    D("[ usb_init - starting thread ]");
+    if (!adb_thread_create(usb_ffs_open_thread, h)) {
         fatal_errno("[ cannot create usb thread ]\n");
     }
 }
@@ -545,10 +569,6 @@ void usb_init()
         usb_adb_init();
 }
 
-void usb_cleanup()
-{
-}
-
 int usb_write(usb_handle *h, const void *data, int len)
 {
     return h->write(h, data, len);
index a795ce3..e0dcc75 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_USB
+#define TRACE_TAG USB
 
 #include "sysdeps.h"
 
 #include <inttypes.h>
 #include <stdio.h>
 
+#include <base/logging.h>
+#include <base/stringprintf.h>
+
 #include "adb.h"
 #include "transport.h"
 
 #define  DBG   D
 
+// There's no strerror equivalent for the errors returned by IOKit.
+// https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Handling_Errors/AH_Handling_Errors.html
+// Search the web for "IOReturn.h" to find a complete up to date list.
+
 static IONotificationPortRef    notificationPort = 0;
 static io_iterator_t            notificationIterator;
 
 struct usb_handle
 {
-    UInt8                     bulkIn;
-    UInt8                     bulkOut;
-    IOUSBInterfaceInterface   **interface;
-    io_object_t               usbNotification;
-    unsigned int              zero_mask;
+    UInt8 bulkIn;
+    UInt8 bulkOut;
+    IOUSBInterfaceInterface190** interface;
+    io_object_t usbNotification;
+    unsigned int zero_mask;
 };
 
 static CFRunLoopRef currentRunLoop = 0;
@@ -55,7 +62,7 @@ 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,
+static usb_handle* CheckInterface(IOUSBInterfaceInterface190 **iface,
                                   UInt16 vendor, UInt16 product);
 
 static int
@@ -77,7 +84,7 @@ InitUSB()
     matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
 
     if (!matchingDict) {
-        DBG("ERR: Couldn't create USB matching dictionary.\n");
+        LOG(ERROR) << "Couldn't create USB matching dictionary.";
         return -1;
     }
 
@@ -111,7 +118,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
     IOUSBDeviceInterface197  **dev = NULL;
     HRESULT                  result;
     SInt32                   score;
-    UInt32                   locationId;
+    uint32_t                 locationId;
     UInt8                    if_class, subclass, protocol;
     UInt16                   vendor;
     UInt16                   product;
@@ -128,7 +135,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
                                                &plugInInterface, &score);
         IOObjectRelease(usbInterface);
         if ((kIOReturnSuccess != kr) || (!plugInInterface)) {
-            DBG("ERR: Unable to create an interface plug-in (%08x)\n", kr);
+            LOG(ERROR) << "Unable to create an interface plug-in (" << std::hex << kr << ")";
             continue;
         }
 
@@ -139,7 +146,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
         //* 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);
+            LOG(ERROR) << "Couldn't query the interface (" << std::hex << result << ")";
             continue;
         }
 
@@ -148,7 +155,8 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
         kr = (*iface)->GetInterfaceProtocol(iface, &protocol);
         if(if_class != ADB_CLASS || subclass != ADB_SUBCLASS || protocol != ADB_PROTOCOL) {
             // Ignore non-ADB devices.
-            DBG("Ignoring interface with incorrect class/subclass/protocol - %d, %d, %d\n", if_class, subclass, protocol);
+            LOG(DEBUG) << "Ignoring interface with incorrect class/subclass/protocol - " << if_class
+                       << ", " << subclass << ", " << protocol;
             (*iface)->Release(iface);
             continue;
         }
@@ -159,7 +167,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
         //* Gotta love OS X
         kr = (*iface)->GetDevice(iface, &usbDevice);
         if (kIOReturnSuccess != kr || !usbDevice) {
-            DBG("ERR: Couldn't grab device from interface (%08x)\n", kr);
+            LOG(ERROR) << "Couldn't grab device from interface (" << std::hex << kr << ")";
             continue;
         }
 
@@ -173,7 +181,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
         //* 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);
+            LOG(ERROR) << "Unable to create a device plug-in (" << std::hex << kr << ")";
             continue;
         }
 
@@ -182,8 +190,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
         //* 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);
+            LOG(ERROR) << "Couldn't create a device interface (" << std::hex << result << ")";
             continue;
         }
 
@@ -193,74 +200,74 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
         kr = (*dev)->GetDeviceProduct(dev, &product);
         kr = (*dev)->GetLocationID(dev, &locationId);
         if (kr == 0) {
-            snprintf(devpathBuf, sizeof(devpathBuf), "usb:%" PRIu32 "X",
-                    (unsigned int)locationId);
+            snprintf(devpathBuf, sizeof(devpathBuf), "usb:%" PRIu32 "X", 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;
-                               }
-                       }
-               }
-       }
+        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);
+        LOG(INFO) << android::base::StringPrintf("Found vid=%04x pid=%04x serial=%s\n",
+                        vendor, product, serial);
 
-        usb_handle* handle = CheckInterface((IOUSBInterfaceInterface**)iface,
+        usb_handle* handle = CheckInterface((IOUSBInterfaceInterface190**)iface,
                                             vendor, product);
         if (handle == NULL) {
-            DBG("ERR: Could not find device interface: %08x\n", kr);
+            LOG(ERROR) << "Could not find device interface";
             (*iface)->Release(iface);
             continue;
         }
 
-        DBG("AndroidDeviceAdded calling register_usb_transport\n");
+        LOG(DEBUG) << "AndroidDeviceAdded calling register_usb_transport";
         register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 1);
 
         // Register for an interest notification of this device being removed.
@@ -274,7 +281,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
                 &handle->usbNotification);
 
         if (kIOReturnSuccess != kr) {
-            DBG("ERR: Unable to create interest notification (%08x)\n", kr);
+            LOG(ERROR) << "Unable to create interest notification (" << std::hex << kr << ")";
         }
     }
 }
@@ -286,38 +293,49 @@ AndroidInterfaceNotify(void *refCon, io_service_t service, natural_t messageType
 
     if (messageType == kIOMessageServiceIsTerminated) {
         if (!handle) {
-            DBG("ERR: NULL handle\n");
+            LOG(ERROR) << "NULL handle";
             return;
         }
-        DBG("AndroidInterfaceNotify\n");
+        LOG(DEBUG) << "AndroidInterfaceNotify";
         IOObjectRelease(handle->usbNotification);
         usb_kick(handle);
     }
 }
 
+// Used to clear both the endpoints before starting.
+// When adb quits, we might clear the host endpoint but not the device.
+// So we make sure both sides are clear before starting up.
+static bool ClearPipeStallBothEnds(IOUSBInterfaceInterface190** interface, UInt8 bulkEp) {
+    IOReturn rc = (*interface)->ClearPipeStallBothEnds(interface, bulkEp);
+    if (rc != kIOReturnSuccess) {
+        LOG(ERROR) << "Could not clear pipe stall both ends: " << std::hex << rc;
+        return false;
+    }
+    return true;
+}
+
 //* TODO: simplify this further since we only register to get ADB interface
 //* subclass+protocol events
 static usb_handle*
-CheckInterface(IOUSBInterfaceInterface **interface, UInt16 vendor, UInt16 product)
+CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 product)
 {
-    usb_handle*                 handle = NULL;
-    IOReturn                    kr;
-    UInt8  interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
-    UInt8  endpoint;
-
+    usb_handle* handle;
+    IOReturn kr;
+    UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
+    UInt8 endpoint;
 
     //* Now open the interface.  This will cause the pipes associated with
     //* the endpoints in the interface descriptor to be instantiated
     kr = (*interface)->USBInterfaceOpen(interface);
     if (kr != kIOReturnSuccess) {
-        DBG("ERR: Could not open interface: (%08x)\n", kr);
+        LOG(ERROR) << "Could not open interface: " << std::hex << kr;
         return NULL;
     }
 
     //* Get the number of endpoints associated with this interface
     kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);
     if (kr != kIOReturnSuccess) {
-        DBG("ERR: Unable to get number of endpoints: (%08x)\n", kr);
+        LOG(ERROR) << "Unable to get number of endpoints: " << std::hex << kr;
         goto err_get_num_ep;
     }
 
@@ -325,22 +343,22 @@ CheckInterface(IOUSBInterfaceInterface **interface, UInt16 vendor, UInt16 produc
     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");
+            LOG(ERROR) << "Unable to get interface class, subclass and protocol";
             goto err_get_interface_class;
     }
 
     //* check to make sure interface class, subclass and protocol match ADB
     //* avoid opening mass storage endpoints
-    if (!is_adb_interface(vendor, product, interfaceClass,
-                interfaceSubClass, interfaceProtocol))
+    if (!is_adb_interface(vendor, product, interfaceClass, interfaceSubClass, interfaceProtocol)) {
         goto err_bad_adb_interface;
+    }
 
     handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
     if (handle == nullptr) goto err_bad_adb_interface;
 
     //* Iterate over the endpoints for this interface and find the first
     //* bulk in/out pipes available.  These will be our read/write pipes.
-    for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
+    for (endpoint = 1; endpoint <= interfaceNumEndpoints; endpoint++) {
         UInt8   transferType;
         UInt16  maxPacketSize;
         UInt8   interval;
@@ -349,22 +367,25 @@ CheckInterface(IOUSBInterfaceInterface **interface, UInt16 vendor, UInt16 produc
 
         kr = (*interface)->GetPipeProperties(interface, endpoint, &direction,
                 &number, &transferType, &maxPacketSize, &interval);
+        if (kr != kIOReturnSuccess) {
+            LOG(ERROR) << "FindDeviceInterface - could not get pipe properties: "
+                       << std::hex << kr;
+            goto err_get_pipe_props;
+        }
 
-        if (kIOReturnSuccess == kr) {
-            if (kUSBBulk != transferType)
-                continue;
-
-            if (kUSBIn == direction)
-                handle->bulkIn = endpoint;
+        if (kUSBBulk != transferType) continue;
 
-            if (kUSBOut == direction)
-                handle->bulkOut = endpoint;
+        if (kUSBIn == direction) {
+            handle->bulkIn = endpoint;
+            if (!ClearPipeStallBothEnds(interface, handle->bulkIn)) goto err_get_pipe_props;
+        }
 
-            handle->zero_mask = maxPacketSize - 1;
-        } else {
-            DBG("ERR: FindDeviceInterface - could not get pipe properties\n");
-            goto err_get_pipe_props;
+        if (kUSBOut == direction) {
+            handle->bulkOut = endpoint;
+            if (!ClearPipeStallBothEnds(interface, handle->bulkOut)) goto err_get_pipe_props;
         }
+
+        handle->zero_mask = maxPacketSize - 1;
     }
 
     handle->interface = interface;
@@ -382,6 +403,7 @@ err_get_num_ep:
 
 void* RunLoopThread(void* unused)
 {
+    adb_thread_setname("RunLoop");
     InitUSB();
 
     currentRunLoop = CFRunLoopGetCurrent();
@@ -397,23 +419,28 @@ void* RunLoopThread(void* unused)
     IOObjectRelease(notificationIterator);
     IONotificationPortDestroy(notificationPort);
 
-    DBG("RunLoopThread done\n");
-    return NULL;    
+    LOG(DEBUG) << "RunLoopThread done";
+    return NULL;
 }
 
+static void usb_cleanup() {
+    LOG(DEBUG) << "usb_cleanup";
+    close_usb_devices();
+    if (currentRunLoop)
+        CFRunLoopStop(currentRunLoop);
+}
 
-static int initialized = 0;
-void usb_init()
-{
-    if (!initialized)
-    {
-        adb_thread_t    tid;
+void usb_init() {
+    static bool initialized = false;
+    if (!initialized) {
+        atexit(usb_cleanup);
 
         adb_mutex_init(&start_lock, NULL);
         adb_cond_init(&start_cond, NULL);
 
-        if(adb_thread_create(&tid, RunLoopThread, NULL))
-            fatal_errno("cannot create input thread");
+        if (!adb_thread_create(RunLoopThread, nullptr)) {
+            fatal_errno("cannot create RunLoop thread");
+        }
 
         // Wait for initialization to finish
         adb_mutex_lock(&start_lock);
@@ -423,18 +450,10 @@ void usb_init()
         adb_mutex_destroy(&start_lock);
         adb_cond_destroy(&start_cond);
 
-        initialized = 1;
+        initialized = true;
     }
 }
 
-void usb_cleanup()
-{
-    DBG("usb_cleanup\n");
-    close_usb_devices();
-    if (currentRunLoop)
-        CFRunLoopStop(currentRunLoop);
-}
-
 int usb_write(usb_handle *handle, const void *buf, int len)
 {
     IOReturn    result;
@@ -446,18 +465,17 @@ int usb_write(usb_handle *handle, const void *buf, int len)
         return -1;
 
     if (NULL == handle->interface) {
-        DBG("ERR: usb_write interface was null\n");
+        LOG(ERROR) << "usb_write interface was null";
         return -1;
     }
 
     if (0 == handle->bulkOut) {
-        DBG("ERR: bulkOut endpoint not assigned\n");
+        LOG(ERROR) << "bulkOut endpoint not assigned";
         return -1;
     }
 
     result =
-        (*handle->interface)->WritePipe(
-                              handle->interface, handle->bulkOut, (void *)buf, len);
+        (*handle->interface)->WritePipe(handle->interface, handle->bulkOut, (void *)buf, len);
 
     if ((result == 0) && (handle->zero_mask)) {
         /* we need 0-markers and our transfer */
@@ -471,7 +489,7 @@ int usb_write(usb_handle *handle, const void *buf, int len)
     if (0 == result)
         return 0;
 
-    DBG("ERR: usb_write failed with status %d\n", result);
+    LOG(ERROR) << "usb_write failed with status: " << std::hex << result;
     return -1;
 }
 
@@ -489,19 +507,19 @@ int usb_read(usb_handle *handle, void *buf, int len)
     }
 
     if (NULL == handle->interface) {
-        DBG("ERR: usb_read interface was null\n");
+        LOG(ERROR) << "usb_read interface was null";
         return -1;
     }
 
     if (0 == handle->bulkIn) {
-        DBG("ERR: bulkIn endpoint not assigned\n");
+        LOG(ERROR) << "bulkIn endpoint not assigned";
         return -1;
     }
 
     result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
 
     if (kIOUSBPipeStalled == result) {
-        DBG(" Pipe stalled, clearing stall.\n");
+        LOG(ERROR) << "Pipe stalled, clearing stall.\n";
         (*handle->interface)->ClearPipeStall(handle->interface, handle->bulkIn);
         result = (*handle->interface)->ReadPipe(handle->interface, handle->bulkIn, buf, &numBytes);
     }
@@ -509,7 +527,7 @@ int usb_read(usb_handle *handle, void *buf, int len)
     if (kIOReturnSuccess == result)
         return 0;
     else {
-        DBG("ERR: usb_read failed with status %x\n", result);
+        LOG(ERROR) << "usb_read failed with status: " << std::hex << result;
     }
 
     return -1;
@@ -522,6 +540,7 @@ int usb_close(usb_handle *handle)
 
 void usb_kick(usb_handle *handle)
 {
+    LOG(INFO) << "Kicking handle";
     /* release the interface */
     if (!handle)
         return;
index d2bd58c..8d3501e 100644 (file)
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define TRACE_TAG TRACE_USB
+#define TRACE_TAG USB
 
 #include "sysdeps.h"
 
 /** Structure usb_handle describes our connection to the usb device via
   AdbWinApi.dll. This structure is returned from usb_open() routine and
   is expected in each subsequent call that is accessing the device.
+
+  Most members are protected by usb_lock, except for adb_{read,write}_pipe which
+  rely on AdbWinApi.dll's handle validation and AdbCloseHandle(endpoint)'s
+  ability to break a thread out of pipe IO.
 */
 struct usb_handle {
   /// Previous entry in the list of opened usb handles
@@ -51,7 +55,7 @@ struct usb_handle {
   ADBAPIHANDLE  adb_write_pipe;
 
   /// Interface name
-  char*         interface_name;
+  wchar_t*      interface_name;
 
   /// Mask for determining when to use zero length packets
   unsigned zero_mask;
@@ -70,11 +74,11 @@ static usb_handle handle_list = {
 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);
+int known_device(const wchar_t* dev_name);
 
 /// Checks if there is opened usb handle in handle_list for this device.
 /// usb_lock mutex must be held before calling this routine.
-int known_device_locked(const char* dev_name);
+int known_device_locked(const wchar_t* dev_name);
 
 /// Registers opened usb handle (adds it to handle_list).
 int register_new_device(usb_handle* handle);
@@ -86,6 +90,9 @@ int recognized_device(usb_handle* handle);
 /// registers usb transport for them.
 void find_devices();
 
+/// Kicks all USB devices
+static void kick_devices();
+
 /// Entry point for thread that polls (every second) for new usb interfaces.
 /// This routine calls find_devices in infinite loop.
 void* device_poll_thread(void* unused);
@@ -93,9 +100,6 @@ 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);
 
@@ -114,10 +118,7 @@ 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) {
+int known_device_locked(const wchar_t* dev_name) {
   usb_handle* usb;
 
   if (NULL != dev_name) {
@@ -125,7 +126,7 @@ int known_device_locked(const char* dev_name) {
     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))) {
+         (0 == wcsicmp(usb->interface_name, dev_name))) {
         return 1;
       }
     }
@@ -134,7 +135,7 @@ int known_device_locked(const char* dev_name) {
   return 0;
 }
 
-int known_device(const char* dev_name) {
+int known_device(const wchar_t* dev_name) {
   int ret = 0;
 
   if (NULL != dev_name) {
@@ -170,7 +171,8 @@ int register_new_device(usb_handle* handle) {
 }
 
 void* device_poll_thread(void* unused) {
-  D("Created device thread\n");
+  adb_thread_setname("Device Poll");
+  D("Created device thread");
 
   while(1) {
     find_devices();
@@ -180,22 +182,100 @@ void* device_poll_thread(void* unused) {
   return NULL;
 }
 
-void usb_init() {
-  adb_thread_t tid;
+static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam,
+                                           LPARAM lParam) {
+  switch (uMsg) {
+  case WM_POWERBROADCAST:
+    switch (wParam) {
+    case PBT_APMRESUMEAUTOMATIC:
+      // Resuming from sleep or hibernation, so kick all existing USB devices
+      // and then allow the device_poll_thread to redetect USB devices from
+      // scratch. If we don't do this, existing USB devices will never respond
+      // to us because they'll be waiting for the connect/auth handshake.
+      D("Received (WM_POWERBROADCAST, PBT_APMRESUMEAUTOMATIC) notification, "
+        "so kicking all USB devices\n");
+      kick_devices();
+      return TRUE;
+    }
+  }
+  return DefWindowProcW(hwnd, uMsg, wParam, lParam);
+}
+
+static void* _power_notification_thread(void* unused) {
+  // This uses a thread with its own window message pump to get power
+  // notifications. If adb runs from a non-interactive service account, this
+  // might not work (not sure). If that happens to not work, we could use
+  // heavyweight WMI APIs to get power notifications. But for the common case
+  // of a developer's interactive session, a window message pump is more
+  // appropriate.
+  D("Created power notification thread");
+  adb_thread_setname("Power Notifier");
+
+  // Window class names are process specific.
+  static const WCHAR kPowerNotificationWindowClassName[] =
+    L"PowerNotificationWindow";
+
+  // Get the HINSTANCE corresponding to the module that _power_window_proc
+  // is in (the main module).
+  const HINSTANCE instance = GetModuleHandleW(NULL);
+  if (!instance) {
+    // This is such a common API call that this should never fail.
+    fatal("GetModuleHandleW failed: %s",
+          SystemErrorCodeToString(GetLastError()).c_str());
+  }
+
+  WNDCLASSEXW wndclass;
+  memset(&wndclass, 0, sizeof(wndclass));
+  wndclass.cbSize = sizeof(wndclass);
+  wndclass.lpfnWndProc = _power_window_proc;
+  wndclass.hInstance = instance;
+  wndclass.lpszClassName = kPowerNotificationWindowClassName;
+  if (!RegisterClassExW(&wndclass)) {
+    fatal("RegisterClassExW failed: %s",
+          SystemErrorCodeToString(GetLastError()).c_str());
+  }
 
-  if(adb_thread_create(&tid, device_poll_thread, NULL)) {
-    fatal_errno("cannot create input thread");
+  if (!CreateWindowExW(WS_EX_NOACTIVATE, kPowerNotificationWindowClassName,
+                       L"ADB Power Notification Window", WS_POPUP, 0, 0, 0, 0,
+                       NULL, NULL, instance, NULL)) {
+    fatal("CreateWindowExW failed: %s",
+          SystemErrorCodeToString(GetLastError()).c_str());
   }
+
+  MSG msg;
+  while (GetMessageW(&msg, NULL, 0, 0)) {
+    TranslateMessage(&msg);
+    DispatchMessageW(&msg);
+  }
+
+  // GetMessageW() will return false if a quit message is posted. We don't
+  // do that, but it might be possible for that to occur when logging off or
+  // shutting down. Not a big deal since the whole process will be going away
+  // soon anyway.
+  D("Power notification thread exiting");
+
+  return NULL;
 }
 
-void usb_cleanup() {
+void usb_init() {
+  if (!adb_thread_create(device_poll_thread, nullptr)) {
+    fatal_errno("cannot create device poll thread");
+  }
+  if (!adb_thread_create(_power_notification_thread, nullptr)) {
+    fatal_errno("cannot create power notification thread");
+  }
 }
 
 usb_handle* do_usb_open(const wchar_t* interface_name) {
+  unsigned long name_len = 0;
+
   // Allocate our handle
-  usb_handle* ret = (usb_handle*)malloc(sizeof(usb_handle));
-  if (NULL == ret)
-    return NULL;
+  usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
+  if (NULL == ret) {
+    D("Could not allocate %u bytes for usb_handle: %s", sizeof(usb_handle),
+      strerror(errno));
+    goto fail;
+  }
 
   // Set linkers back to the handle
   ret->next = ret;
@@ -203,11 +283,10 @@ usb_handle* do_usb_open(const wchar_t* interface_name) {
 
   // Create interface.
   ret->adb_interface = AdbCreateInterfaceByName(interface_name);
-
   if (NULL == ret->adb_interface) {
-    free(ret);
-    errno = GetLastError();
-    return NULL;
+    D("AdbCreateInterfaceByName failed: %s",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    goto fail;
   }
 
   // Open read pipe (endpoint)
@@ -215,45 +294,59 @@ usb_handle* do_usb_open(const wchar_t* interface_name) {
     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);
-        }
-      }
-    }
+  if (NULL == ret->adb_read_pipe) {
+    D("AdbOpenDefaultBulkReadEndpoint failed: %s",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    goto fail;
+  }
+
+  // Open write pipe (endpoint)
+  ret->adb_write_pipe =
+    AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,
+                                    AdbOpenAccessTypeReadWrite,
+                                    AdbOpenSharingModeReadWrite);
+  if (NULL == ret->adb_write_pipe) {
+    D("AdbOpenDefaultBulkWriteEndpoint failed: %s",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    goto fail;
+  }
+
+  // Save interface name
+  // First get expected name length
+  AdbGetInterfaceName(ret->adb_interface,
+                      NULL,
+                      &name_len,
+                      false);
+  if (0 == name_len) {
+    D("AdbGetInterfaceName returned name length of zero: %s",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    goto fail;
+  }
+
+  ret->interface_name = (wchar_t*)malloc(name_len * sizeof(ret->interface_name[0]));
+  if (NULL == ret->interface_name) {
+    D("Could not allocate %lu characters for interface_name: %s", name_len, strerror(errno));
+    goto fail;
+  }
+
+  // Now save the name
+  if (!AdbGetInterfaceName(ret->adb_interface,
+                           ret->interface_name,
+                           &name_len,
+                           false)) {
+    D("AdbGetInterfaceName failed: %s",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    goto fail;
   }
 
-  // Something went wrong.
-  int saved_errno = GetLastError();
-  usb_cleanup_handle(ret);
-  free(ret);
-  SetLastError(saved_errno);
+  // We're done at this point
+  return ret;
+
+fail:
+  if (NULL != ret) {
+    usb_cleanup_handle(ret);
+    free(ret);
+  }
 
   return NULL;
 }
@@ -261,99 +354,130 @@ usb_handle* do_usb_open(const wchar_t* interface_name) {
 int usb_write(usb_handle* handle, const void* data, int len) {
   unsigned long time_out = 5000;
   unsigned long written = 0;
-  int ret;
+  int err = 0;
 
-  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);
+  D("usb_write %d", len);
+  if (NULL == handle) {
+    D("usb_write was passed NULL handle");
+    err = EINVAL;
+    goto fail;
+  }
+
+  // Perform write
+  if (!AdbWriteEndpointSync(handle->adb_write_pipe,
+                            (void*)data,
+                            (unsigned long)len,
+                            &written,
+                            time_out)) {
+    D("AdbWriteEndpointSync failed: %s",
+      SystemErrorCodeToString(GetLastError()).c_str());
+    err = EIO;
+    goto fail;
+  }
+
+  // Make sure that we've written what we were asked to write
+  D("usb_write got: %ld, expected: %d", written, len);
+  if (written != (unsigned long)len) {
+    // If this occurs, this code should be changed to repeatedly call
+    // AdbWriteEndpointSync() until all bytes are written.
+    D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld",
+      len, written);
+    err = EIO;
+    goto fail;
+  }
+
+  if (handle->zero_mask && (len & handle->zero_mask) == 0) {
+    // Send a zero length packet
+    if (!AdbWriteEndpointSync(handle->adb_write_pipe,
+                              (void*)data,
+                              0,
+                              &written,
+                              time_out)) {
+      D("AdbWriteEndpointSync of zero length packet failed: %s",
+        SystemErrorCodeToString(GetLastError()).c_str());
+      err = EIO;
+      goto fail;
     }
-    errno = saved_errno;
-  } else {
-    D("usb_write NULL handle\n");
-    SetLastError(ERROR_INVALID_HANDLE);
   }
 
-  D("usb_write failed: %d\n", errno);
+  return 0;
+
+fail:
+  // Any failure should cause us to kick the device instead of leaving it a
+  // zombie state with potential to hang.
+  if (NULL != handle) {
+    D("Kicking device due to error in usb_write");
+    usb_kick(handle);
+  }
 
+  D("usb_write failed");
+  errno = err;
   return -1;
 }
 
 int usb_read(usb_handle *handle, void* data, int len) {
   unsigned long time_out = 0;
   unsigned long read = 0;
-  int ret;
+  int err = 0;
 
-  D("usb_read %d\n", len);
-  if (NULL != handle) {
-    while (len > 0) {
-      int xfer = (len > 4096) ? 4096 : len;
-
-      ret = AdbReadEndpointSync(handle->adb_read_pipe,
-                                  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 = (char *)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;
+  D("usb_read %d", len);
+  if (NULL == handle) {
+    D("usb_read was passed NULL handle");
+    err = EINVAL;
+    goto fail;
+  }
+
+  while (len > 0) {
+    if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read,
+                             time_out)) {
+      D("AdbReadEndpointSync failed: %s",
+        SystemErrorCodeToString(GetLastError()).c_str());
+      err = EIO;
+      goto fail;
     }
-  } else {
-    D("usb_read NULL handle\n");
-    SetLastError(ERROR_INVALID_HANDLE);
+    D("usb_read got: %ld, expected: %d", read, len);
+
+    data = (char *)data + read;
+    len -= read;
   }
 
-  D("usb_read failed: %d\n", errno);
+  return 0;
+
+fail:
+  // Any failure should cause us to kick the device instead of leaving it a
+  // zombie state with potential to hang.
+  if (NULL != handle) {
+    D("Kicking device due to error in usb_read");
+    usb_kick(handle);
+  }
 
+  D("usb_read failed");
+  errno = err;
   return -1;
 }
 
+// Wrapper around AdbCloseHandle() that logs diagnostics.
+static void _adb_close_handle(ADBAPIHANDLE adb_handle) {
+  if (!AdbCloseHandle(adb_handle)) {
+    D("AdbCloseHandle(%p) failed: %s", adb_handle,
+      SystemErrorCodeToString(GetLastError()).c_str());
+  }
+}
+
 void usb_cleanup_handle(usb_handle* handle) {
+  D("usb_cleanup_handle");
   if (NULL != handle) {
     if (NULL != handle->interface_name)
       free(handle->interface_name);
+    // AdbCloseHandle(pipe) will break any threads out of pending IO calls and
+    // wait until the pipe no longer uses the interface. Then we can
+    // AdbCloseHandle() the interface.
     if (NULL != handle->adb_write_pipe)
-      AdbCloseHandle(handle->adb_write_pipe);
+      _adb_close_handle(handle->adb_write_pipe);
     if (NULL != handle->adb_read_pipe)
-      AdbCloseHandle(handle->adb_read_pipe);
+      _adb_close_handle(handle->adb_read_pipe);
     if (NULL != handle->adb_interface)
-      AdbCloseHandle(handle->adb_interface);
+      _adb_close_handle(handle->adb_interface);
 
     handle->interface_name = NULL;
     handle->adb_write_pipe = NULL;
@@ -362,21 +486,27 @@ void usb_cleanup_handle(usb_handle* handle) {
   }
 }
 
+static void usb_kick_locked(usb_handle* handle) {
+  // The reason the lock must be acquired before calling this function is in
+  // case multiple threads are trying to kick the same device at the same time.
+  usb_cleanup_handle(handle);
+}
+
 void usb_kick(usb_handle* handle) {
+  D("usb_kick");
   if (NULL != handle) {
     adb_mutex_lock(&usb_lock);
 
-    usb_cleanup_handle(handle);
+    usb_kick_locked(handle);
 
     adb_mutex_unlock(&usb_lock);
   } else {
-    SetLastError(ERROR_INVALID_HANDLE);
-    errno = ERROR_INVALID_HANDLE;
+    errno = EINVAL;
   }
 }
 
 int usb_close(usb_handle* handle) {
-  D("usb_close\n");
+  D("usb_close");
 
   if (NULL != handle) {
     // Remove handle from the list
@@ -399,16 +529,6 @@ int usb_close(usb_handle* 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;
@@ -418,6 +538,8 @@ int recognized_device(usb_handle* handle) {
 
   if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
                                  &device_desc)) {
+    D("AdbGetUsbDeviceDescriptor failed: %s",
+      SystemErrorCodeToString(GetLastError()).c_str());
     return 0;
   }
 
@@ -426,6 +548,8 @@ int recognized_device(usb_handle* handle) {
 
   if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
                                     &interf_desc)) {
+    D("AdbGetUsbInterfaceDescriptor failed: %s",
+      SystemErrorCodeToString(GetLastError()).c_str());
     return 0;
   }
 
@@ -442,6 +566,10 @@ int recognized_device(usb_handle* handle) {
       // assuming zero is a valid bulk endpoint ID
       if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
         handle->zero_mask = endpoint_info.max_packet_size - 1;
+        D("device zero_mask: 0x%x", handle->zero_mask);
+      } else {
+        D("AdbGetEndpointInformation failed: %s",
+          SystemErrorCodeToString(GetLastError()).c_str());
       }
     }
 
@@ -452,40 +580,37 @@ int recognized_device(usb_handle* handle) {
 }
 
 void find_devices() {
-        usb_handle* handle = NULL;
+  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)
+  if (NULL == enum_handle) {
+    D("AdbEnumInterfaces failed: %s",
+      SystemErrorCodeToString(GetLastError()).c_str());
     return;
+  }
 
   while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {
-    // TODO: FIXME - temp hack converting wchar_t into char.
-    // It would be better to change AdbNextInterface so it will return
-    // interface name as single char string.
-    const wchar_t* wchar_name = next_interface->device_name;
-    for(copy_name = interf_name;
-        L'\0' != *wchar_name;
-        wchar_name++, copy_name++) {
-      *copy_name = (char)(*wchar_name);
-    }
-    *copy_name = '\0';
-
     // Lets see if we already have this device in the list
-    if (!known_device(interf_name)) {
+    if (!known_device(next_interface->device_name)) {
       // This seems to be a new device. Open it!
-        handle = do_usb_open(next_interface->device_name);
-        if (NULL != handle) {
+      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);
+          D("adding a new device %ls", next_interface->device_name);
+
+          // We don't request a wchar_t string from AdbGetSerialNumber() because of a bug in
+          // adb_winusb_interface.cpp:CopyMemory(buffer, ser_num->bString, bytes_written) where the
+          // last parameter should be (str_len * sizeof(wchar_t)). The bug reads 2 bytes past the
+          // end of a stack buffer in the best case, and in the unlikely case of a long serial
+          // number, it will read 2 bytes past the end of a heap allocation. This doesn't affect the
+          // resulting string, but we should avoid the bad reads in the first place.
           char serial_number[512];
           unsigned long serial_number_len = sizeof(serial_number);
           if (AdbGetSerialNumber(handle->adb_interface,
@@ -496,12 +621,13 @@ void find_devices() {
             if (register_new_device(handle)) {
               register_usb_transport(handle, serial_number, NULL, 1);
             } else {
-              D("register_new_device failed for %s\n", interf_name);
+              D("register_new_device failed for %ls", next_interface->device_name);
               usb_cleanup_handle(handle);
               free(handle);
             }
           } else {
-            D("cannot get serial number\n");
+            D("cannot get serial number: %s",
+              SystemErrorCodeToString(GetLastError()).c_str());
             usb_cleanup_handle(handle);
             free(handle);
           }
@@ -515,5 +641,21 @@ void find_devices() {
     entry_buffer_size = sizeof(entry_buffer);
   }
 
-  AdbCloseHandle(enum_handle);
+  if (GetLastError() != ERROR_NO_MORE_ITEMS) {
+    // Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
+    D("AdbNextInterface failed: %s",
+      SystemErrorCodeToString(GetLastError()).c_str());
+  }
+
+  _adb_close_handle(enum_handle);
+}
+
+static void kick_devices() {
+  // Need to acquire lock to safely walk the list which might be modified
+  // by another thread.
+  adb_mutex_lock(&usb_lock);
+  for (usb_handle* usb = handle_list.next; usb != &handle_list; usb = usb->next) {
+    usb_kick_locked(usb);
+  }
+  adb_mutex_unlock(&usb_lock);
 }
index 66c329c..c4d6681 100644 (file)
@@ -87,7 +87,6 @@ ssize_t adf_devices(adf_id_t **ids)
 int adf_device_open(adf_id_t id, int flags, struct adf_device *dev)
 {
     char filename[64];
-    int err;
 
     dev->id = id;
 
index ad85c6b..cba70d4 100644 (file)
@@ -18,29 +18,47 @@ LOCAL_PATH := $(call my-dir)
 
 libbase_src_files := \
     file.cpp \
+    logging.cpp \
     stringprintf.cpp \
     strings.cpp \
+    test_utils.cpp \
+
+libbase_windows_src_files := \
+    utf8.cpp \
 
 libbase_test_src_files := \
     file_test.cpp \
+    logging_test.cpp \
+    parseint_test.cpp \
     stringprintf_test.cpp \
     strings_test.cpp \
     test_main.cpp \
-    test_utils.cpp \
+
+libbase_test_windows_src_files := \
+    utf8_test.cpp \
 
 libbase_cppflags := \
     -Wall \
     -Wextra \
     -Werror \
 
+libbase_linux_cppflags := \
+    -Wexit-time-destructors \
+
+libbase_darwin_cppflags := \
+    -Wexit-time-destructors \
+
 # Device
 # ------------------------------------------------------------------------------
 include $(CLEAR_VARS)
 LOCAL_MODULE := libbase
 LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(libbase_src_files) logging.cpp
+LOCAL_SRC_FILES := $(libbase_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_windows_src_files)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CPPFLAGS := $(libbase_cppflags)
+LOCAL_CPPFLAGS := $(libbase_cppflags) $(libbase_linux_cppflags)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_STATIC_LIBRARIES := libcutils
 LOCAL_MULTILIB := both
@@ -61,14 +79,17 @@ include $(BUILD_SHARED_LIBRARY)
 include $(CLEAR_VARS)
 LOCAL_MODULE := libbase
 LOCAL_SRC_FILES := $(libbase_src_files)
-ifneq ($(HOST_OS),windows)
-    LOCAL_SRC_FILES += logging.cpp
-endif
+LOCAL_SRC_FILES_darwin := $(libbase_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_windows_src_files)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_CPPFLAGS := $(libbase_cppflags)
+LOCAL_CPPFLAGS_darwin := $(libbase_darwin_cppflags)
+LOCAL_CPPFLAGS_linux := $(libbase_linux_cppflags)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_STATIC_LIBRARIES := libcutils
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -78,6 +99,7 @@ LOCAL_SHARED_LIBRARIES := liblog
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_STATIC_LIBRARIES := libcutils
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 # Tests
@@ -85,7 +107,10 @@ include $(BUILD_HOST_SHARED_LIBRARY)
 include $(CLEAR_VARS)
 LOCAL_MODULE := libbase_test
 LOCAL_CLANG := true
-LOCAL_SRC_FILES := $(libbase_test_src_files) logging_test.cpp
+LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_SHARED_LIBRARIES := libbase
@@ -96,10 +121,11 @@ include $(BUILD_NATIVE_TEST)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := libbase_test
+LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_SRC_FILES := $(libbase_test_src_files)
-ifneq ($(HOST_OS),windows)
-    LOCAL_SRC_FILES += logging_test.cpp
-endif
+LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_SHARED_LIBRARIES := libbase
index 9a340b7..7b5e7b1 100644 (file)
 #include <string>
 
 #include "base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
+#include "base/utf8.h"
 #define LOG_TAG "base.file"
 #include "cutils/log.h"
 #include "utils/Compat.h"
 
+#if !defined(_WIN32)
+#define O_BINARY 0
+#endif
+
 namespace android {
 namespace base {
 
+// Versions of standard library APIs that support UTF-8 strings.
+using namespace android::base::utf8;
+
 bool ReadFdToString(int fd, std::string* content) {
   content->clear();
 
@@ -45,8 +53,7 @@ bool ReadFdToString(int fd, std::string* content) {
 bool ReadFileToString(const std::string& path, std::string* content) {
   content->clear();
 
-  int fd =
-      TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_BINARY));
   if (fd == -1) {
     return false;
   }
@@ -80,9 +87,8 @@ static bool CleanUpAfterFailedWrite(const std::string& path) {
 #if !defined(_WIN32)
 bool WriteStringToFile(const std::string& content, const std::string& path,
                        mode_t mode, uid_t owner, gid_t group) {
-  int fd = TEMP_FAILURE_RETRY(
-      open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
-           mode));
+  int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
   if (fd == -1) {
     ALOGE("android::WriteStringToFile open failed: %s", strerror(errno));
     return false;
@@ -108,9 +114,8 @@ bool WriteStringToFile(const std::string& content, const std::string& path,
 #endif
 
 bool WriteStringToFile(const std::string& content, const std::string& path) {
-  int fd = TEMP_FAILURE_RETRY(
-      open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
-           DEFFILEMODE));
+  int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
   if (fd == -1) {
     return false;
   }
index 5445a0d..77b9268 100644 (file)
@@ -24,7 +24,7 @@
 
 #include <string>
 
-#include "test_utils.h"
+#include "base/test_utils.h"
 
 TEST(file, ReadFileToString_ENOENT) {
   std::string s("hello");
@@ -34,73 +34,65 @@ TEST(file, ReadFileToString_ENOENT) {
   EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(file, ReadFileToString_success) {
-  std::string s("hello");
-  ASSERT_TRUE(android::base::ReadFileToString("/proc/version", &s)) << errno;
-  EXPECT_GT(s.length(), 6U);
-  EXPECT_EQ('\n', s[s.length() - 1]);
-  s[5] = 0;
-  EXPECT_STREQ("Linux", s.c_str());
-}
-
-TEST(file, WriteStringToFile) {
+TEST(file, ReadFileToString_WriteStringToFile) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
-  ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename)) << errno;
+  ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
+    << strerror(errno);
   std::string s;
-  ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
+    << strerror(errno);
   EXPECT_EQ("abc", s);
 }
 
+// WriteStringToFile2 is explicitly for setting Unix permissions, which make no
+// sense on Windows.
+#if !defined(_WIN32)
 TEST(file, WriteStringToFile2) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
-  ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.filename, 0660,
+  ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
                                                getuid(), getgid()))
-      << errno;
+      << strerror(errno);
   struct stat sb;
-  ASSERT_EQ(0, stat(tf.filename, &sb));
+  ASSERT_EQ(0, stat(tf.path, &sb));
   ASSERT_EQ(0660U, static_cast<unsigned int>(sb.st_mode & ~S_IFMT));
   ASSERT_EQ(getuid(), sb.st_uid);
   ASSERT_EQ(getgid(), sb.st_gid);
   std::string s;
-  ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno;
+  ASSERT_TRUE(android::base::ReadFileToString(tf.path, &s))
+    << strerror(errno);
   EXPECT_EQ("abc", s);
 }
+#endif
 
 TEST(file, WriteStringToFd) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
   ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
 
-  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << errno;
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
 
   std::string s;
-  ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << errno;
+  ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
   EXPECT_EQ("abc", s);
 }
 
-TEST(file, ReadFully) {
-  int fd = open("/proc/version", O_RDONLY);
-  ASSERT_NE(-1, fd) << strerror(errno);
-
-  char buf[1024];
-  memset(buf, 0, sizeof(buf));
-  ASSERT_TRUE(android::base::ReadFully(fd, buf, 5));
-  ASSERT_STREQ("Linux", buf);
-
-  ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
-
-  ASSERT_FALSE(android::base::ReadFully(fd, buf, sizeof(buf)));
-
-  close(fd);
-}
-
 TEST(file, WriteFully) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
   ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
+
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
   std::string s;
-  ASSERT_TRUE(android::base::ReadFileToString(tf.filename, &s)) << errno;
+  s.resize(3);
+  ASSERT_TRUE(android::base::ReadFully(tf.fd, &s[0], s.size()))
+    << strerror(errno);
   EXPECT_EQ("abc", s);
+
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+  s.resize(1024);
+  ASSERT_FALSE(android::base::ReadFully(tf.fd, &s[0], s.size()));
 }
index 230adb8..30f6906 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #ifndef BASE_LOGGING_H
 #define BASE_LOGGING_H
 
+// NOTE: For Windows, you must include logging.h after windows.h to allow the
+// following code to suppress the evil ERROR macro:
+#ifdef _WIN32
+// windows.h includes wingdi.h which defines an evil macro ERROR.
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
 #include <functional>
 #include <memory>
 #include <ostream>
@@ -79,30 +87,64 @@ extern void InitLogging(char* argv[]);
 // Replace the current logger.
 extern void SetLogger(LogFunction&& logger);
 
+// Get the minimum severity level for logging.
+extern LogSeverity GetMinimumLogSeverity();
+
+class ErrnoRestorer {
+ public:
+  ErrnoRestorer()
+      : saved_errno_(errno) {
+  }
+
+  ~ErrnoRestorer() {
+    errno = saved_errno_;
+  }
+
+  // Allow this object to evaluate to false which is useful in macros.
+  operator bool() const {
+    return false;
+  }
+
+ private:
+  const int saved_errno_;
+
+  DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
+};
+
 // Logs a message to logcat on Android otherwise to stderr. If the severity is
 // FATAL it also causes an abort. For example:
 //
 //     LOG(FATAL) << "We didn't expect to reach here";
-#define LOG(severity)                                                       \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
-                              ::android::base::severity, -1).stream()
+#define LOG(severity) LOG_TO(DEFAULT, severity)
 
 // Logs a message to logcat with the specified log ID on Android otherwise to
 // stderr. If the severity is FATAL it also causes an abort.
-#define LOG_TO(dest, severity)                                           \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
-                              ::android::base::severity, -1).stream()
+// Use an if-else statement instead of just an if statement here. So if there is a
+// else statement after LOG() macro, it won't bind to the if statement in the macro.
+// do-while(0) statement doesn't work here. Because we need to support << operator
+// following the macro, like "LOG(DEBUG) << xxx;".
+#define LOG_TO(dest, severity)                                                      \
+  if (LIKELY(::android::base::severity < ::android::base::GetMinimumLogSeverity())) \
+    ;                                                                               \
+  else                                                                              \
+    ::android::base::ErrnoRestorer() ? *(std::ostream*)nullptr :                    \
+      ::android::base::LogMessage(__FILE__, __LINE__,                               \
+          ::android::base::dest,                                                    \
+          ::android::base::severity, -1).stream()
 
 // A variant of LOG that also logs the current errno value. To be used when
 // library calls fail.
-#define PLOG(severity)                                                      \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
-                              ::android::base::severity, errno).stream()
+#define PLOG(severity) PLOG_TO(DEFAULT, severity)
 
 // Behaves like PLOG, but logs to the specified log ID.
-#define PLOG_TO(dest, severity)                                          \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::dest, \
-                              ::android::base::severity, errno).stream()
+#define PLOG_TO(dest, severity)                                                     \
+  if (LIKELY(::android::base::severity < ::android::base::GetMinimumLogSeverity())) \
+    ;                                                                               \
+  else                                                                              \
+    ::android::base::ErrnoRestorer() ? *(std::ostream*)nullptr :                    \
+      ::android::base::LogMessage(__FILE__, __LINE__,                               \
+          ::android::base::dest,                                                    \
+          ::android::base::severity, errno).stream()
 
 // Marker that code is yet to be implemented.
 #define UNIMPLEMENTED(level) \
@@ -114,11 +156,13 @@ extern void SetLogger(LogFunction&& logger);
 //
 //     CHECK(false == true) results in a log message of
 //       "Check failed: false == true".
-#define CHECK(x)                                                            \
-  if (UNLIKELY(!(x)))                                                       \
-  ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
-                              ::android::base::FATAL, -1).stream()          \
-      << "Check failed: " #x << " "
+#define CHECK(x)                                                              \
+  if (LIKELY((x)))                                                            \
+    ;                                                                         \
+  else                                                                        \
+    ::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
+                                ::android::base::FATAL, -1).stream()          \
+        << "Check failed: " #x << " "
 
 // Helper for CHECK_xx(x,y) macros.
 #define CHECK_OP(LHS, RHS, OP)                                              \
@@ -145,7 +189,9 @@ extern void SetLogger(LogFunction&& logger);
 
 // Helper for CHECK_STRxx(s1,s2) macros.
 #define CHECK_STROP(s1, s2, sense)                                         \
-  if (UNLIKELY((strcmp(s1, s2) == 0) != sense))                            \
+  if (LIKELY((strcmp(s1, s2) == 0) == sense))                              \
+    ;                                                                      \
+  else                                                                     \
     LOG(FATAL) << "Check failed: "                                         \
                << "\"" << s1 << "\""                                       \
                << (sense ? " == " : " != ") << "\"" << s2 << "\""
diff --git a/base/include/base/parseint.h b/base/include/base/parseint.h
new file mode 100644 (file)
index 0000000..0543795
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_PARSEINT_H
+#define BASE_PARSEINT_H
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include <limits>
+
+namespace android {
+namespace base {
+
+// Parses the unsigned decimal integer in the string 's' and sets 'out' to
+// that value. Optionally allows the caller to define a 'max' beyond which
+// otherwise valid values will be rejected. Returns boolean success.
+template <typename T>
+bool ParseUint(const char* s, T* out,
+               T max = std::numeric_limits<T>::max()) {
+  int base = (s[0] == '0' && s[1] == 'x') ? 16 : 10;
+  errno = 0;
+  char* end;
+  unsigned long long int result = strtoull(s, &end, base);
+  if (errno != 0 || s == end || *end != '\0') {
+    return false;
+  }
+  if (max < result) {
+    return false;
+  }
+  *out = static_cast<T>(result);
+  return true;
+}
+
+// Parses the signed decimal integer in the string 's' and sets 'out' to
+// that value. Optionally allows the caller to define a 'min' and 'max
+// beyond which otherwise valid values will be rejected. Returns boolean
+// success.
+template <typename T>
+bool ParseInt(const char* s, T* out,
+              T min = std::numeric_limits<T>::min(),
+              T max = std::numeric_limits<T>::max()) {
+  int base = (s[0] == '0' && s[1] == 'x') ? 16 : 10;
+  errno = 0;
+  char* end;
+  long long int result = strtoll(s, &end, base);
+  if (errno != 0 || s == end || *end != '\0') {
+    return false;
+  }
+  if (result < min || max < result) {
+    return false;
+  }
+  *out = static_cast<T>(result);
+  return true;
+}
+
+}  // namespace base
+}  // namespace android
+
+#endif  // BASE_PARSEINT_H
index 195c1de..d68af87 100644 (file)
 namespace android {
 namespace base {
 
+// These printf-like functions are implemented in terms of vsnprintf, so they
+// use the same attribute for compile-time format string checking. On Windows,
+// if the mingw version of vsnprintf is used, use `gnu_printf' which allows z
+// in %zd and PRIu64 (and related) to be recognized by the compile-time
+// checking.
+#define FORMAT_ARCHETYPE __printf__
+#ifdef __USE_MINGW_ANSI_STDIO
+#if __USE_MINGW_ANSI_STDIO
+#undef FORMAT_ARCHETYPE
+#define FORMAT_ARCHETYPE gnu_printf
+#endif
+#endif
+
 // Returns a string corresponding to printf-like formatting of the arguments.
 std::string StringPrintf(const char* fmt, ...)
-    __attribute__((__format__(__printf__, 1, 2)));
+    __attribute__((__format__(FORMAT_ARCHETYPE, 1, 2)));
 
 // Appends a printf-like formatting of the arguments to 'dst'.
 void StringAppendF(std::string* dst, const char* fmt, ...)
-    __attribute__((__format__(__printf__, 2, 3)));
+    __attribute__((__format__(FORMAT_ARCHETYPE, 2, 3)));
 
 // Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendV(std::string* dst, const char* format, va_list ap);
+void StringAppendV(std::string* dst, const char* format, va_list ap)
+    __attribute__((__format__(FORMAT_ARCHETYPE, 2, 0)));
+
+#undef FORMAT_ARCHETYPE
 
 }  // namespace base
 }  // namespace android
index 5dbc5fb..20da144 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef BASE_STRINGS_H
 #define BASE_STRINGS_H
 
+#include <sstream>
 #include <string>
 #include <vector>
 
@@ -34,9 +35,26 @@ std::vector<std::string> Split(const std::string& s,
 // Trims whitespace off both ends of the given string.
 std::string Trim(const std::string& s);
 
-// Joins a vector of strings into a single string, using the given separator.
-template <typename StringT>
-std::string Join(const std::vector<StringT>& strings, char separator);
+// Joins a container of things into a single string, using the given separator.
+template <typename ContainerT, typename SeparatorT>
+std::string Join(const ContainerT& things, SeparatorT separator) {
+  if (things.empty()) {
+    return "";
+  }
+
+  std::ostringstream result;
+  result << *things.begin();
+  for (auto it = std::next(things.begin()); it != things.end(); ++it) {
+    result << separator << *it;
+  }
+  return result.str();
+}
+
+// We instantiate the common cases in strings.cpp.
+extern template std::string Join(const std::vector<std::string>&, char);
+extern template std::string Join(const std::vector<const char*>&, char);
+extern template std::string Join(const std::vector<std::string>&, const std::string&);
+extern template std::string Join(const std::vector<const char*>&, const std::string&);
 
 // Tests whether 's' starts with 'prefix'.
 bool StartsWith(const std::string& s, const char* prefix);
diff --git a/base/include/base/test_utils.h b/base/include/base/test_utils.h
new file mode 100644 (file)
index 0000000..402e0a5
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TEST_UTILS_H
+#define TEST_UTILS_H
+
+#include <string>
+
+#include <base/macros.h>
+
+class TemporaryFile {
+ public:
+  TemporaryFile();
+  ~TemporaryFile();
+
+  int fd;
+  char path[1024];
+
+ private:
+  void init(const std::string& tmp_dir);
+
+  DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
+};
+
+class TemporaryDir {
+ public:
+  TemporaryDir();
+  ~TemporaryDir();
+
+  char path[1024];
+
+ private:
+  bool init(const std::string& tmp_dir);
+
+  DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
+};
+
+#endif // TEST_UTILS_H
diff --git a/base/include/base/utf8.h b/base/include/base/utf8.h
new file mode 100755 (executable)
index 0000000..3b0ed0a
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BASE_UTF8_H
+#define BASE_UTF8_H
+
+#ifdef _WIN32
+#include <string>
+#else
+// Bring in prototypes for standard APIs so that we can import them into the utf8 namespace.
+#include <fcntl.h>      // open
+#include <unistd.h>     // unlink
+#endif
+
+namespace android {
+namespace base {
+
+// Only available on Windows because this is only needed on Windows.
+#ifdef _WIN32
+// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the
+// conversion was done successfully.
+bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8);
+
+// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns
+// whether the conversion was done successfully.
+bool WideToUTF8(const wchar_t* utf16, std::string* utf8);
+
+// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
+// UTF-8. Returns whether the conversion was done successfully.
+bool WideToUTF8(const std::wstring& utf16, std::string* utf8);
+
+// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion
+// was done successfully.
+bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16);
+
+// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns
+// whether the conversion was done successfully.
+bool UTF8ToWide(const char* utf8, std::wstring* utf16);
+
+// Convert a UTF-8 std::string (including any embedded NULL characters) to
+// UTF-16. Returns whether the conversion was done successfully.
+bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
+#endif
+
+// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
+// are wrappers, for non-Windows these just expose existing APIs. To call these
+// functions, use:
+//
+// // anonymous namespace to avoid conflict with existing open(), unlink(), etc.
+// namespace {
+//   // Import functions into anonymous namespace.
+//   using namespace android::base::utf8;
+//
+//   void SomeFunction(const char* name) {
+//     int fd = open(name, ...);  // Calls android::base::utf8::open().
+//     ...
+//     unlink(name);              // Calls android::base::utf8::unlink().
+//   }
+// }
+namespace utf8 {
+
+#ifdef _WIN32
+int open(const char* name, int flags, ...);
+int unlink(const char* name);
+#else
+using ::open;
+using ::unlink;
+#endif
+
+}  // namespace utf8
+}  // namespace base
+}  // namespace android
+
+#endif  // BASE_UTF8_H
index 0142b70..85f8b3f 100644 (file)
  * limitations under the License.
  */
 
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
 #include "base/logging.h"
 
 #include <libgen.h>
 
 #include <iostream>
 #include <limits>
-#include <mutex>
 #include <sstream>
 #include <string>
 #include <utility>
 #include <vector>
 
+#ifndef _WIN32
+#include <mutex>
+#endif
+
+#include "base/macros.h"
 #include "base/strings.h"
 #include "cutils/threads.h"
 
 #include <unistd.h>
 #endif
 
+// For gettid.
+#if defined(__APPLE__)
+#include "AvailabilityMacros.h"  // For MAC_OS_X_VERSION_MAX_ALLOWED
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#include <unistd.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+static pid_t GetThreadId() {
+#if defined(__BIONIC__)
+  return gettid();
+#elif defined(__APPLE__)
+  return syscall(SYS_thread_selfid);
+#elif defined(__linux__)
+  return syscall(__NR_gettid);
+#elif defined(_WIN32)
+  return GetCurrentThreadId();
+#endif
+}
+
+namespace {
+#ifndef _WIN32
+using std::mutex;
+using std::lock_guard;
+
+#if defined(__GLIBC__)
+const char* getprogname() {
+  return program_invocation_short_name;
+}
+#endif
+
+#else
+const char* getprogname() {
+  static bool first = true;
+  static char progname[MAX_PATH] = {};
+
+  if (first) {
+    CHAR longname[MAX_PATH];
+    DWORD nchars = GetModuleFileNameA(nullptr, longname, arraysize(longname));
+    if ((nchars >= arraysize(longname)) || (nchars == 0)) {
+      // String truncation or some other error.
+      strcpy(progname, "<unknown>");
+    } else {
+      strcpy(progname, basename(longname));
+    }
+    first = false;
+  }
+
+  return progname;
+}
+
+class mutex {
+ public:
+  mutex() {
+    InitializeCriticalSection(&critical_section_);
+  }
+  ~mutex() {
+    DeleteCriticalSection(&critical_section_);
+  }
+
+  void lock() {
+    EnterCriticalSection(&critical_section_);
+  }
+
+  void unlock() {
+    LeaveCriticalSection(&critical_section_);
+  }
+
+ private:
+  CRITICAL_SECTION critical_section_;
+};
+
+template <typename LockT>
+class lock_guard {
+ public:
+  explicit lock_guard(LockT& lock) : lock_(lock) {
+    lock_.lock();
+  }
+
+  ~lock_guard() {
+    lock_.unlock();
+  }
+
+ private:
+  LockT& lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(lock_guard);
+};
+#endif
+} // namespace
+
 namespace android {
 namespace base {
 
-static std::mutex logging_lock;
+static auto& logging_lock = *new mutex();
 
 #ifdef __ANDROID__
-static LogFunction gLogger = LogdLogger();
+static auto& gLogger = *new LogFunction(LogdLogger());
 #else
-static LogFunction gLogger = StderrLogger;
+static auto& gLogger = *new LogFunction(StderrLogger);
 #endif
 
 static bool gInitialized = false;
 static LogSeverity gMinimumLogSeverity = INFO;
-static std::unique_ptr<std::string> gProgramInvocationName;
+static auto& gProgramInvocationName = *new std::unique_ptr<std::string>();
 
-#if defined(__GLIBC__)
-static const char* getprogname() {
-  return program_invocation_short_name;
+LogSeverity GetMinimumLogSeverity() {
+  return gMinimumLogSeverity;
 }
-#endif
 
 static const char* ProgramInvocationName() {
   if (gProgramInvocationName == nullptr) {
@@ -76,11 +180,12 @@ static const char* ProgramInvocationName() {
 
 void StderrLogger(LogId, LogSeverity severity, const char*, const char* file,
                   unsigned int line, const char* message) {
-  static const char* log_characters = "VDIWEF";
-  CHECK_EQ(strlen(log_characters), FATAL + 1U);
+  static const char log_characters[] = "VDIWEF";
+  static_assert(arraysize(log_characters) - 1 == FATAL + 1,
+                "Mismatch in size of log_characters and values in LogSeverity");
   char severity_char = log_characters[severity];
   fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationName(),
-          severity_char, getpid(), gettid(), file, line, message);
+          severity_char, getpid(), GetThreadId(), file, line, message);
 }
 
 
@@ -134,8 +239,8 @@ void InitLogging(char* argv[]) {
   gInitialized = true;
 
   // Stash the command line for later use. We can use /proc/self/cmdline on
-  // Linux to recover this, but we don't have that luxury on the Mac, and there
-  // are a couple of argv[0] variants that are commonly used.
+  // Linux to recover this, but we don't have that luxury on the Mac/Windows,
+  // and there are a couple of argv[0] variants that are commonly used.
   if (argv != nullptr) {
     gProgramInvocationName.reset(new std::string(basename(argv[0])));
   }
@@ -182,23 +287,37 @@ void InitLogging(char* argv[]) {
 }
 
 void SetLogger(LogFunction&& logger) {
-  std::lock_guard<std::mutex> lock(logging_lock);
+  lock_guard<mutex> lock(logging_lock);
   gLogger = std::move(logger);
 }
 
+static const char* GetFileBasename(const char* file) {
+  // We can't use basename(3) even on Unix because the Mac doesn't
+  // have a non-modifying basename.
+  const char* last_slash = strrchr(file, '/');
+  if (last_slash != nullptr) {
+    return last_slash + 1;
+  }
+#if defined(_WIN32)
+  const char* last_backslash = strrchr(file, '\\');
+  if (last_backslash != nullptr) {
+    return last_backslash + 1;
+  }
+#endif
+  return file;
+}
+
 // This indirection greatly reduces the stack impact of having lots of
 // checks/logging in a function.
 class LogMessageData {
  public:
   LogMessageData(const char* file, unsigned int line, LogId id,
                  LogSeverity severity, int error)
-      : file_(file),
+      : file_(GetFileBasename(file)),
         line_number_(line),
         id_(id),
         severity_(severity),
         error_(error) {
-    const char* last_slash = strrchr(file, '/');
-    file = (last_slash == nullptr) ? file : last_slash + 1;
   }
 
   const char* GetFile() const {
@@ -246,28 +365,28 @@ LogMessage::LogMessage(const char* file, unsigned int line, LogId id,
 }
 
 LogMessage::~LogMessage() {
-  if (data_->GetSeverity() < gMinimumLogSeverity) {
-    return;  // No need to format something we're not going to output.
-  }
-
   // Finish constructing the message.
   if (data_->GetError() != -1) {
     data_->GetBuffer() << ": " << strerror(data_->GetError());
   }
   std::string msg(data_->ToString());
 
-  if (msg.find('\n') == std::string::npos) {
-    LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
-            data_->GetSeverity(), msg.c_str());
-  } else {
-    msg += '\n';
-    size_t i = 0;
-    while (i < msg.size()) {
-      size_t nl = msg.find('\n', i);
-      msg[nl] = '\0';
+  {
+    // Do the actual logging with the lock held.
+    lock_guard<mutex> lock(logging_lock);
+    if (msg.find('\n') == std::string::npos) {
       LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
-              data_->GetSeverity(), &msg[i]);
-      i = nl + 1;
+              data_->GetSeverity(), msg.c_str());
+    } else {
+      msg += '\n';
+      size_t i = 0;
+      while (i < msg.size()) {
+        size_t nl = msg.find('\n', i);
+        msg[nl] = '\0';
+        LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetId(),
+                data_->GetSeverity(), &msg[i]);
+        i = nl + 1;
+      }
     }
   }
 
@@ -287,7 +406,6 @@ std::ostream& LogMessage::stream() {
 void LogMessage::LogLine(const char* file, unsigned int line, LogId id,
                          LogSeverity severity, const char* message) {
   const char* tag = ProgramInvocationName();
-  std::lock_guard<std::mutex> lock(logging_lock);
   gLogger(id, severity, tag, file, line, message);
 }
 
index d947c1d..70f9952 100644 (file)
 
 #include "base/logging.h"
 
+#include <libgen.h>
+
+#if defined(_WIN32)
+#include <signal.h>
+#endif
+
 #include <regex>
 #include <string>
 
 #include "base/file.h"
 #include "base/stringprintf.h"
-#include "test_utils.h"
+#include "base/test_utils.h"
 
 #include <gtest/gtest.h>
 
@@ -47,6 +53,11 @@ class CapturedStderr {
 
  private:
   void init() {
+#if defined(_WIN32)
+    // On Windows, stderr is often buffered, so make sure it is unbuffered so
+    // that we can immediately read back what was written to stderr.
+    ASSERT_EQ(0, setvbuf(stderr, NULL, _IONBF, 0));
+#endif
     old_stderr_ = dup(STDERR_FILENO);
     ASSERT_NE(-1, old_stderr_);
     ASSERT_NE(-1, dup2(fd(), STDERR_FILENO));
@@ -55,66 +66,112 @@ class CapturedStderr {
   void reset() {
     ASSERT_NE(-1, dup2(old_stderr_, STDERR_FILENO));
     ASSERT_EQ(0, close(old_stderr_));
+    // Note: cannot restore prior setvbuf() setting.
   }
 
   TemporaryFile temp_file_;
   int old_stderr_;
 };
 
+#if defined(_WIN32)
+static void ExitSignalAbortHandler(int) {
+  _exit(3);
+}
+#endif
+
+static void SuppressAbortUI() {
+#if defined(_WIN32)
+  // We really just want to call _set_abort_behavior(0, _CALL_REPORTFAULT) to
+  // suppress the Windows Error Reporting dialog box, but that API is not
+  // available in the OS-supplied C Runtime, msvcrt.dll, that we currently
+  // use (it is available in the Visual Studio C runtime).
+  //
+  // Instead, we setup a SIGABRT handler, which is called in abort() right
+  // before calling Windows Error Reporting. In the handler, we exit the
+  // process just like abort() does.
+  ASSERT_NE(SIG_ERR, signal(SIGABRT, ExitSignalAbortHandler));
+#endif
+}
+
 TEST(logging, CHECK) {
-  ASSERT_DEATH(CHECK(false), "Check failed: false ");
+  ASSERT_DEATH({SuppressAbortUI(); CHECK(false);}, "Check failed: false ");
   CHECK(true);
 
-  ASSERT_DEATH(CHECK_EQ(0, 1), "Check failed: 0 == 1 ");
+  ASSERT_DEATH({SuppressAbortUI(); CHECK_EQ(0, 1);}, "Check failed: 0 == 1 ");
   CHECK_EQ(0, 0);
 
-  ASSERT_DEATH(CHECK_STREQ("foo", "bar"), R"(Check failed: "foo" == "bar")");
+  ASSERT_DEATH({SuppressAbortUI(); CHECK_STREQ("foo", "bar");},
+               R"(Check failed: "foo" == "bar")");
   CHECK_STREQ("foo", "foo");
+
+  // Test whether CHECK() and CHECK_STREQ() have a dangling if with no else.
+  bool flag = false;
+  if (true)
+    CHECK(true);
+  else
+    flag = true;
+  EXPECT_FALSE(flag) << "CHECK macro probably has a dangling if with no else";
+
+  flag = false;
+  if (true)
+    CHECK_STREQ("foo", "foo");
+  else
+    flag = true;
+  EXPECT_FALSE(flag) << "CHECK_STREQ probably has a dangling if with no else";
 }
 
 std::string make_log_pattern(android::base::LogSeverity severity,
                              const char* message) {
   static const char* log_characters = "VDIWEF";
   char log_char = log_characters[severity];
+  std::string holder(__FILE__);
   return android::base::StringPrintf(
-      "%c[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+ " __FILE__
-      ":[[:digit:]]+] %s",
-      log_char, message);
+      "%c[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+ %s:[[:digit:]]+] %s",
+      log_char, basename(&holder[0]), message);
 }
 
 TEST(logging, LOG) {
-  ASSERT_DEATH(LOG(FATAL) << "foobar", "foobar");
+  ASSERT_DEATH({SuppressAbortUI(); LOG(FATAL) << "foobar";}, "foobar");
 
+  // We can't usefully check the output of any of these on Windows because we
+  // don't have std::regex, but we can at least make sure we printed at least as
+  // many characters are in the log message.
   {
     CapturedStderr cap;
     LOG(WARNING) << "foobar";
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_GT(output.length(), strlen("foobar"));
 
+#if !defined(_WIN32)
     std::regex message_regex(
         make_log_pattern(android::base::WARNING, "foobar"));
-    ASSERT_TRUE(std::regex_search(output, message_regex));
+    ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
   }
 
   {
     CapturedStderr cap;
     LOG(INFO) << "foobar";
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_GT(output.length(), strlen("foobar"));
 
+#if !defined(_WIN32)
     std::regex message_regex(
         make_log_pattern(android::base::INFO, "foobar"));
-    ASSERT_TRUE(std::regex_search(output, message_regex));
+    ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
   }
 
   {
     CapturedStderr cap;
     LOG(DEBUG) << "foobar";
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
@@ -125,14 +182,61 @@ TEST(logging, LOG) {
     android::base::ScopedLogSeverity severity(android::base::DEBUG);
     CapturedStderr cap;
     LOG(DEBUG) << "foobar";
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_GT(output.length(), strlen("foobar"));
 
+#if !defined(_WIN32)
     std::regex message_regex(
         make_log_pattern(android::base::DEBUG, "foobar"));
-    ASSERT_TRUE(std::regex_search(output, message_regex));
+    ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
+  }
+
+  // Test whether LOG() saves and restores errno.
+  {
+    CapturedStderr cap;
+    errno = 12345;
+    LOG(INFO) << (errno = 67890);
+    EXPECT_EQ(12345, errno) << "errno was not restored";
+
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
+
+    std::string output;
+    android::base::ReadFdToString(cap.fd(), &output);
+    EXPECT_NE(nullptr, strstr(output.c_str(), "67890")) << output;
+
+#if !defined(_WIN32)
+    std::regex message_regex(
+        make_log_pattern(android::base::INFO, "67890"));
+    ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
+  }
+
+  // Test whether LOG() has a dangling if with no else.
+  {
+    CapturedStderr cap;
+
+    // Do the test two ways: once where we hypothesize that LOG()'s if
+    // will evaluate to true (when severity is high enough) and once when we
+    // expect it to evaluate to false (when severity is not high enough).
+    bool flag = false;
+    if (true)
+      LOG(INFO) << "foobar";
+    else
+      flag = true;
+
+    EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
+
+    flag = false;
+    if (true)
+      LOG(VERBOSE) << "foobar";
+    else
+      flag = true;
+
+    EXPECT_FALSE(flag) << "LOG macro probably has a dangling if with no else";
   }
 }
 
@@ -141,14 +245,17 @@ TEST(logging, PLOG) {
     CapturedStderr cap;
     errno = ENOENT;
     PLOG(INFO) << "foobar";
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_GT(output.length(), strlen("foobar"));
 
+#if !defined(_WIN32)
     std::regex message_regex(make_log_pattern(
         android::base::INFO, "foobar: No such file or directory"));
-    ASSERT_TRUE(std::regex_search(output, message_regex));
+    ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
   }
 }
 
@@ -157,15 +264,18 @@ TEST(logging, UNIMPLEMENTED) {
     CapturedStderr cap;
     errno = ENOENT;
     UNIMPLEMENTED(ERROR);
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
+    ASSERT_GT(output.length(), strlen("unimplemented"));
 
+#if !defined(_WIN32)
     std::string expected_message =
         android::base::StringPrintf("%s unimplemented ", __PRETTY_FUNCTION__);
     std::regex message_regex(
         make_log_pattern(android::base::ERROR, expected_message.c_str()));
-    ASSERT_TRUE(std::regex_search(output, message_regex));
+    ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
+#endif
   }
 }
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
new file mode 100644 (file)
index 0000000..8a11d29
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base/parseint.h"
+
+#include <gtest/gtest.h>
+
+TEST(parseint, signed_smoke) {
+  int i;
+  ASSERT_FALSE(android::base::ParseInt("x", &i));
+  ASSERT_FALSE(android::base::ParseInt("123x", &i));
+
+  ASSERT_TRUE(android::base::ParseInt("123", &i));
+  ASSERT_EQ(123, i);
+  ASSERT_TRUE(android::base::ParseInt("-123", &i));
+  ASSERT_EQ(-123, i);
+
+  short s;
+  ASSERT_TRUE(android::base::ParseInt("1234", &s));
+  ASSERT_EQ(1234, s);
+
+  ASSERT_TRUE(android::base::ParseInt("12", &i, 0, 15));
+  ASSERT_EQ(12, i);
+  ASSERT_FALSE(android::base::ParseInt("-12", &i, 0, 15));
+  ASSERT_FALSE(android::base::ParseInt("16", &i, 0, 15));
+}
+
+TEST(parseint, unsigned_smoke) {
+  unsigned int i;
+  ASSERT_FALSE(android::base::ParseUint("x", &i));
+  ASSERT_FALSE(android::base::ParseUint("123x", &i));
+
+  ASSERT_TRUE(android::base::ParseUint("123", &i));
+  ASSERT_EQ(123u, i);
+  ASSERT_FALSE(android::base::ParseUint("-123", &i));
+
+  unsigned short s;
+  ASSERT_TRUE(android::base::ParseUint("1234", &s));
+  ASSERT_EQ(1234u, s);
+
+  ASSERT_TRUE(android::base::ParseUint("12", &i, 15u));
+  ASSERT_EQ(12u, i);
+  ASSERT_FALSE(android::base::ParseUint("-12", &i, 15u));
+  ASSERT_FALSE(android::base::ParseUint("16", &i, 15u));
+}
+
+TEST(parseint, no_implicit_octal) {
+  int i;
+  ASSERT_TRUE(android::base::ParseInt("0123", &i));
+  ASSERT_EQ(123, i);
+
+  unsigned int u;
+  ASSERT_TRUE(android::base::ParseUint("0123", &u));
+  ASSERT_EQ(123u, u);
+}
+
+TEST(parseint, explicit_hex) {
+  int i;
+  ASSERT_TRUE(android::base::ParseInt("0x123", &i));
+  ASSERT_EQ(0x123, i);
+
+  unsigned int u;
+  ASSERT_TRUE(android::base::ParseUint("0x123", &u));
+  ASSERT_EQ(0x123u, u);
+}
index d3375d9..d687e3c 100644 (file)
@@ -79,25 +79,12 @@ std::string Trim(const std::string& s) {
   return s.substr(start_index, end_index - start_index + 1);
 }
 
-template <typename StringT>
-std::string Join(const std::vector<StringT>& strings, char separator) {
-  if (strings.empty()) {
-    return "";
-  }
-
-  std::string result(strings[0]);
-  for (size_t i = 1; i < strings.size(); ++i) {
-    result += separator;
-    result += strings[i];
-  }
-  return result;
-}
-
-// Explicit instantiations.
-template std::string Join<std::string>(const std::vector<std::string>& strings,
-                                       char separator);
-template std::string Join<const char*>(const std::vector<const char*>& strings,
-                                       char separator);
+// These cases are probably the norm, so we mark them extern in the header to
+// aid compile time and binary size.
+template std::string Join(const std::vector<std::string>&, char);
+template std::string Join(const std::vector<const char*>&, char);
+template std::string Join(const std::vector<std::string>&, const std::string&);
+template std::string Join(const std::vector<const char*>&, const std::string&);
 
 bool StartsWith(const std::string& s, const char* prefix) {
   return s.compare(0, strlen(prefix), prefix) == 0;
index 46a1ab5..5f67575 100644 (file)
@@ -20,6 +20,8 @@
 
 #include <string>
 #include <vector>
+#include <set>
+#include <unordered_set>
 
 TEST(strings, split_empty) {
   std::vector<std::string> parts = android::base::Split("", ",");
@@ -121,6 +123,17 @@ TEST(strings, join_separator_in_vector) {
   ASSERT_EQ(",,,", android::base::Join(list, ','));
 }
 
+TEST(strings, join_simple_ints) {
+  std::set<int> list = {1, 2, 3};
+  ASSERT_EQ("1,2,3", android::base::Join(list, ','));
+}
+
+TEST(strings, join_unordered_set) {
+  std::unordered_set<int> list = {1, 2};
+  ASSERT_TRUE("1,2" == android::base::Join(list, ',') ||
+              "2,1" == android::base::Join(list, ','));
+}
+
 TEST(strings, startswith_empty) {
   ASSERT_FALSE(android::base::StartsWith("", "foo"));
   ASSERT_TRUE(android::base::StartsWith("", ""));
index 1f6d3cf..22641e7 100644 (file)
  * limitations under the License.
  */
 
-#include "test_utils.h"
+#include "base/logging.h"
+#include "base/test_utils.h"
+#include "utils/Compat.h" // For OS_PATH_SEPARATOR.
 
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
-TemporaryFile::TemporaryFile() {
-  init("/data/local/tmp");
-  if (fd == -1) {
-    init("/tmp");
+#if defined(_WIN32)
+#include <windows.h>
+#include <direct.h>
+#endif
+
+#include <string>
+
+#ifdef _WIN32
+int mkstemp(char* template_name) {
+  if (_mktemp(template_name) == nullptr) {
+    return -1;
+  }
+  // Use open() to match the close() that TemporaryFile's destructor does.
+  // Use O_BINARY to match base file APIs.
+  return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
+              S_IRUSR | S_IWUSR);
+}
+
+char* mkdtemp(char* template_name) {
+  if (_mktemp(template_name) == nullptr) {
+    return nullptr;
+  }
+  if (_mkdir(template_name) == -1) {
+    return nullptr;
   }
+  return template_name;
+}
+#endif
+
+static std::string GetSystemTempDir() {
+#if defined(__ANDROID__)
+  return "/data/local/tmp";
+#elif defined(_WIN32)
+  char tmp_dir[MAX_PATH];
+  DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);
+  CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
+  CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
+
+  // GetTempPath() returns a path with a trailing slash, but init()
+  // does not expect that, so remove it.
+  CHECK_EQ(tmp_dir[result - 1], '\\');
+  tmp_dir[result - 1] = '\0';
+  return tmp_dir;
+#else
+  return "/tmp";
+#endif
+}
+
+TemporaryFile::TemporaryFile() {
+  init(GetSystemTempDir());
 }
 
 TemporaryFile::~TemporaryFile() {
   close(fd);
-  unlink(filename);
+  unlink(path);
+}
+
+void TemporaryFile::init(const std::string& tmp_dir) {
+  snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
+           OS_PATH_SEPARATOR);
+  fd = mkstemp(path);
+}
+
+TemporaryDir::TemporaryDir() {
+  init(GetSystemTempDir());
+}
+
+TemporaryDir::~TemporaryDir() {
+  rmdir(path);
 }
 
-void TemporaryFile::init(const char* tmp_dir) {
-  snprintf(filename, sizeof(filename), "%s/TemporaryFile-XXXXXX", tmp_dir);
-  fd = mkstemp(filename);
+bool TemporaryDir::init(const std::string& tmp_dir) {
+  snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(),
+           OS_PATH_SEPARATOR);
+  return (mkdtemp(path) != nullptr);
 }
diff --git a/base/utf8.cpp b/base/utf8.cpp
new file mode 100755 (executable)
index 0000000..99f0f54
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <windows.h>
+
+#include "base/utf8.h"
+
+#include <fcntl.h>
+
+#include <string>
+
+#include "base/logging.h"
+
+namespace android {
+namespace base {
+
+// Helper to set errno based on GetLastError() after WideCharToMultiByte()/MultiByteToWideChar().
+static void SetErrnoFromLastError() {
+  switch (GetLastError()) {
+    case ERROR_NO_UNICODE_TRANSLATION:
+      errno = EILSEQ;
+      break;
+    default:
+      errno = EINVAL;
+      break;
+  }
+}
+
+bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) {
+  utf8->clear();
+
+  if (size == 0) {
+    return true;
+  }
+
+  // TODO: Consider using std::wstring_convert once libcxx is supported on
+  // Windows.
+
+  // Only Vista or later has this flag that causes WideCharToMultiByte() to
+  // return an error on invalid characters.
+  const DWORD flags =
+#if (WINVER >= 0x0600)
+    WC_ERR_INVALID_CHARS;
+#else
+    0;
+#endif
+
+  const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
+                                                 NULL, 0, NULL, NULL);
+  if (chars_required <= 0) {
+    SetErrnoFromLastError();
+    return false;
+  }
+
+  // This could potentially throw a std::bad_alloc exception.
+  utf8->resize(chars_required);
+
+  const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
+                                         &(*utf8)[0], chars_required, NULL,
+                                         NULL);
+  if (result != chars_required) {
+    SetErrnoFromLastError();
+    CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result
+        << " chars to buffer of " << chars_required << " chars";
+    utf8->clear();
+    return false;
+  }
+
+  return true;
+}
+
+bool WideToUTF8(const wchar_t* utf16, std::string* utf8) {
+  // Compute string length of NULL-terminated string with wcslen().
+  return WideToUTF8(utf16, wcslen(utf16), utf8);
+}
+
+bool WideToUTF8(const std::wstring& utf16, std::string* utf8) {
+  // Use the stored length of the string which allows embedded NULL characters
+  // to be converted.
+  return WideToUTF8(utf16.c_str(), utf16.length(), utf8);
+}
+
+// Internal helper function that takes MultiByteToWideChar() flags.
+static bool UTF8ToWideWithFlags(const char* utf8, const size_t size, std::wstring* utf16,
+                                const DWORD flags) {
+  utf16->clear();
+
+  if (size == 0) {
+    return true;
+  }
+
+  // TODO: Consider using std::wstring_convert once libcxx is supported on
+  // Windows.
+  const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
+                                                 NULL, 0);
+  if (chars_required <= 0) {
+    SetErrnoFromLastError();
+    return false;
+  }
+
+  // This could potentially throw a std::bad_alloc exception.
+  utf16->resize(chars_required);
+
+  const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
+                                         &(*utf16)[0], chars_required);
+  if (result != chars_required) {
+    SetErrnoFromLastError();
+    CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result
+        << " chars to buffer of " << chars_required << " chars";
+    utf16->clear();
+    return false;
+  }
+
+  return true;
+}
+
+bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) {
+  // If strictly interpreting as UTF-8 succeeds, return success.
+  if (UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) {
+    return true;
+  }
+
+  const int saved_errno = errno;
+
+  // Fallback to non-strict interpretation, allowing invalid characters and
+  // converting as best as possible, and return false to signify a problem.
+  (void)UTF8ToWideWithFlags(utf8, size, utf16, 0);
+  errno = saved_errno;
+  return false;
+}
+
+bool UTF8ToWide(const char* utf8, std::wstring* utf16) {
+  // Compute string length of NULL-terminated string with strlen().
+  return UTF8ToWide(utf8, strlen(utf8), utf16);
+}
+
+bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) {
+  // Use the stored length of the string which allows embedded NULL characters
+  // to be converted.
+  return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
+}
+
+// Versions of standard library APIs that support UTF-8 strings.
+namespace utf8 {
+
+int open(const char* name, int flags, ...) {
+  std::wstring name_utf16;
+  if (!UTF8ToWide(name, &name_utf16)) {
+    return -1;
+  }
+
+  int mode = 0;
+  if ((flags & O_CREAT) != 0) {
+    va_list args;
+    va_start(args, flags);
+    mode = va_arg(args, int);
+    va_end(args);
+  }
+
+  return _wopen(name_utf16.c_str(), flags, mode);
+}
+
+int unlink(const char* name) {
+  std::wstring name_utf16;
+  if (!UTF8ToWide(name, &name_utf16)) {
+    return -1;
+  }
+
+  return _wunlink(name_utf16.c_str());
+}
+
+}  // namespace utf8
+}  // namespace base
+}  // namespace android
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
new file mode 100755 (executable)
index 0000000..13f6431
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "base/utf8.h"
+
+#include <gtest/gtest.h>
+
+#include "base/macros.h"
+
+namespace android {
+namespace base {
+
+TEST(UTFStringConversionsTest, ConvertInvalidUTF8) {
+  std::wstring wide;
+
+  errno = 0;
+
+  // Standalone \xa2 is an invalid UTF-8 sequence, so this should return an
+  // error. Concatenate two C/C++ literal string constants to prevent the
+  // compiler from giving an error about "\xa2af" containing a "hex escape
+  // sequence out of range".
+  EXPECT_FALSE(android::base::UTF8ToWide("before\xa2" "after", &wide));
+
+  EXPECT_EQ(EILSEQ, errno);
+
+  // Even if an invalid character is encountered, UTF8ToWide() should still do
+  // its best to convert the rest of the string. sysdeps_win32.cpp:
+  // _console_write_utf8() depends on this behavior.
+  //
+  // Thus, we verify that the valid characters are converted, but we ignore the
+  // specific replacement character that UTF8ToWide() may replace the invalid
+  // UTF-8 characters with because we want to allow that to change if the
+  // implementation changes.
+  EXPECT_EQ(0, wide.find(L"before"));
+  const wchar_t after_wide[] = L"after";
+  EXPECT_EQ(wide.length() - (arraysize(after_wide) - 1), wide.find(after_wide));
+}
+
+// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/utf_string_conversions_unittest.cc
+
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The tests below from utf_string_conversions_unittest.cc check for this
+// preprocessor symbol, so define it, as it is appropriate for Windows.
+#define WCHAR_T_IS_UTF16
+static_assert(sizeof(wchar_t) == 2, "wchar_t is not 2 bytes");
+
+// The tests below from utf_string_conversions_unittest.cc call versions of
+// UTF8ToWide() and WideToUTF8() that don't return success/failure, so these are
+// stub implementations with that signature. These are just for testing and
+// should not be moved to base because they assert/expect no errors which is
+// probably not a good idea (or at least it is something that should be left
+// up to the caller, not a base library).
+
+static std::wstring UTF8ToWide(const std::string& utf8) {
+  std::wstring utf16;
+  EXPECT_TRUE(UTF8ToWide(utf8, &utf16));
+  return utf16;
+}
+
+static std::string WideToUTF8(const std::wstring& utf16) {
+  std::string utf8;
+  EXPECT_TRUE(WideToUTF8(utf16, &utf8));
+  return utf8;
+}
+
+namespace {
+
+const wchar_t* const kConvertRoundtripCases[] = {
+  L"Google Video",
+  // "网页 图片 资讯更多 »"
+  L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
+  //  "Παγκόσμιος Ιστός"
+  L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+  L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
+  // "Поиск страниц на русском"
+  L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
+  L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
+  L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
+  // "전체서비스"
+  L"\xc804\xccb4\xc11c\xbe44\xc2a4",
+
+  // Test characters that take more than 16 bits. This will depend on whether
+  // wchar_t is 16 or 32 bits.
+#if defined(WCHAR_T_IS_UTF16)
+  L"\xd800\xdf00",
+  // ?????  (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+  L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
+#elif defined(WCHAR_T_IS_UTF32)
+  L"\x10300",
+  // ?????  (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+  L"\x11d40\x11d41\x11d42\x11d43\x11d44",
+#endif
+};
+
+}  // namespace
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWide) {
+  // we round-trip all the wide strings through UTF-8 to make sure everything
+  // agrees on the conversion. This uses the stream operators to test them
+  // simultaneously.
+  for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+    std::ostringstream utf8;
+    utf8 << WideToUTF8(kConvertRoundtripCases[i]);
+    std::wostringstream wide;
+    wide << UTF8ToWide(utf8.str());
+
+    EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
+  }
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWideEmptyString) {
+  // An empty std::wstring should be converted to an empty std::string,
+  // and vice versa.
+  std::wstring wempty;
+  std::string empty;
+  EXPECT_EQ(empty, WideToUTF8(wempty));
+  EXPECT_EQ(wempty, UTF8ToWide(empty));
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8ToWide) {
+  struct UTF8ToWideCase {
+    const char* utf8;
+    const wchar_t* wide;
+    bool success;
+  } convert_cases[] = {
+    // Regular UTF-8 input.
+    {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true},
+    // Non-character is passed through.
+    {"\xef\xbf\xbfHello", L"\xffffHello", true},
+    // Truncated UTF-8 sequence.
+    {"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+    // Truncated off the end.
+    {"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false},
+    // Non-shortest-form UTF-8.
+    {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+    // This UTF-8 character decodes to a UTF-16 surrogate, which is illegal.
+    // Note that for whatever reason, this test fails on Windows XP.
+    {"\xed\xb0\x80", L"\xfffd", false},
+    // Non-BMP characters. The second is a non-character regarded as valid.
+    // The result will either be in UTF-16 or UTF-32.
+#if defined(WCHAR_T_IS_UTF16)
+    {"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true},
+    {"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true},
+#elif defined(WCHAR_T_IS_UTF32)
+    {"A\xF0\x90\x8C\x80z", L"A\x10300z", true},
+    {"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true},
+#endif
+  };
+
+  for (size_t i = 0; i < arraysize(convert_cases); i++) {
+    std::wstring converted;
+    errno = 0;
+    const bool success = UTF8ToWide(convert_cases[i].utf8,
+                                    strlen(convert_cases[i].utf8),
+                                    &converted);
+    EXPECT_EQ(convert_cases[i].success, success);
+    // The original test always compared expected and converted, but don't do
+    // that because our implementation of UTF8ToWide() does not guarantee to
+    // produce the same output in error situations.
+    if (success) {
+      std::wstring expected(convert_cases[i].wide);
+      EXPECT_EQ(expected, converted);
+    } else {
+      EXPECT_EQ(EILSEQ, errno);
+    }
+  }
+
+  // Manually test an embedded NULL.
+  std::wstring converted;
+  EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted));
+  ASSERT_EQ(3U, converted.length());
+  EXPECT_EQ(static_cast<wchar_t>(0), converted[0]);
+  EXPECT_EQ('Z', converted[1]);
+  EXPECT_EQ('\t', converted[2]);
+
+  // Make sure that conversion replaces, not appends.
+  EXPECT_TRUE(UTF8ToWide("B", 1, &converted));
+  ASSERT_EQ(1U, converted.length());
+  EXPECT_EQ('B', converted[0]);
+}
+
+#if defined(WCHAR_T_IS_UTF16)
+// This test is only valid when wchar_t == UTF-16.
+TEST(UTFStringConversionsTest, ConvertUTF16ToUTF8) {
+  struct WideToUTF8Case {
+    const wchar_t* utf16;
+    const char* utf8;
+    bool success;
+  } convert_cases[] = {
+    // Regular UTF-16 input.
+    {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+    // Test a non-BMP character.
+    {L"\xd800\xdf00", "\xF0\x90\x8C\x80", true},
+    // Non-characters are passed through.
+    {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+    {L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true},
+    // The first character is a truncated UTF-16 character.
+    // Note that for whatever reason, this test fails on Windows XP.
+    {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd",
+#if (WINVER >= 0x0600)
+    // Only Vista and later has a new API/flag that correctly returns false.
+    false
+#else
+    true
+#endif
+    },
+    // Truncated at the end.
+    // Note that for whatever reason, this test fails on Windows XP.
+    {L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd",
+#if (WINVER >= 0x0600)
+    // Only Vista and later has a new API/flag that correctly returns false.
+    false
+#else
+    true
+#endif
+    },
+  };
+
+  for (size_t i = 0; i < arraysize(convert_cases); i++) {
+    std::string converted;
+    errno = 0;
+    const bool success = WideToUTF8(convert_cases[i].utf16,
+                                    wcslen(convert_cases[i].utf16),
+                                    &converted);
+    EXPECT_EQ(convert_cases[i].success, success);
+    // The original test always compared expected and converted, but don't do
+    // that because our implementation of WideToUTF8() does not guarantee to
+    // produce the same output in error situations.
+    if (success) {
+      std::string expected(convert_cases[i].utf8);
+      EXPECT_EQ(expected, converted);
+    } else {
+      EXPECT_EQ(EILSEQ, errno);
+    }
+  }
+}
+
+#elif defined(WCHAR_T_IS_UTF32)
+// This test is only valid when wchar_t == UTF-32.
+TEST(UTFStringConversionsTest, ConvertUTF32ToUTF8) {
+  struct WideToUTF8Case {
+    const wchar_t* utf32;
+    const char* utf8;
+    bool success;
+  } convert_cases[] = {
+    // Regular 16-bit input.
+    {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+    // Test a non-BMP character.
+    {L"A\x10300z", "A\xF0\x90\x8C\x80z", true},
+    // Non-characters are passed through.
+    {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+    {L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true},
+    // Invalid Unicode code points.
+    {L"\xfffffffHello", "\xEF\xBF\xBDHello", false},
+    // The first character is a truncated UTF-16 character.
+    {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
+    {L"\xdc01Hello", "\xef\xbf\xbdHello", false},
+  };
+
+  for (size_t i = 0; i < arraysize(convert_cases); i++) {
+    std::string converted;
+    EXPECT_EQ(convert_cases[i].success,
+              WideToUTF8(convert_cases[i].utf32,
+                         wcslen(convert_cases[i].utf32),
+                         &converted));
+    std::string expected(convert_cases[i].utf8);
+    EXPECT_EQ(expected, converted);
+  }
+}
+#endif  // defined(WCHAR_T_IS_UTF32)
+
+// The test below uses these types and functions, so just do enough to get the
+// test running.
+typedef wchar_t char16;
+typedef std::wstring string16;
+
+template<typename T>
+static void* WriteInto(T* t, size_t size) {
+  // std::(w)string::resize() already includes space for a NULL terminator.
+  t->resize(size - 1);
+  return &(*t)[0];
+}
+
+// A stub implementation that calls a helper from above, just to get the test
+// below working. This is just for testing and should not be moved to base
+// because this ignores errors which is probably not a good idea, plus it takes
+// a string16 type which we don't really have.
+static std::string UTF16ToUTF8(const string16& utf16) {
+  return WideToUTF8(utf16);
+}
+
+TEST(UTFStringConversionsTest, ConvertMultiString) {
+  static char16 multi16[] = {
+    'f', 'o', 'o', '\0',
+    'b', 'a', 'r', '\0',
+    'b', 'a', 'z', '\0',
+    '\0'
+  };
+  static char multi[] = {
+    'f', 'o', 'o', '\0',
+    'b', 'a', 'r', '\0',
+    'b', 'a', 'z', '\0',
+    '\0'
+  };
+  string16 multistring16;
+  memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16,
+                   sizeof(multi16));
+  EXPECT_EQ(arraysize(multi16) - 1, multistring16.length());
+  std::string expected;
+  memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
+  EXPECT_EQ(arraysize(multi) - 1, expected.length());
+  const std::string& converted = UTF16ToUTF8(multistring16);
+  EXPECT_EQ(arraysize(multi) - 1, converted.length());
+  EXPECT_EQ(expected, converted);
+}
+
+// The tests below from sys_string_conversions_unittest.cc call SysWideToUTF8()
+// and SysUTF8ToWide(), so these are stub implementations that call the helpers
+// above. These are just for testing and should not be moved to base because
+// they ignore errors which is probably not a good idea.
+
+static std::string SysWideToUTF8(const std::wstring& utf16) {
+  return WideToUTF8(utf16);
+}
+
+static std::wstring SysUTF8ToWide(const std::string& utf8) {
+  return UTF8ToWide(utf8);
+}
+
+// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/sys_string_conversions_unittest.cc
+
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifdef WCHAR_T_IS_UTF32
+static const std::wstring kSysWideOldItalicLetterA = L"\x10300";
+#else
+static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00";
+#endif
+
+TEST(SysStrings, SysWideToUTF8) {
+  EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world"));
+  EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d"));
+
+  // >16 bits
+  EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA));
+
+  // Error case. When Windows finds a UTF-16 character going off the end of
+  // a string, it just converts that literal value to UTF-8, even though this
+  // is invalid.
+  //
+  // This is what XP does, but Vista has different behavior, so we don't bother
+  // verifying it:
+  // EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw",
+  //           SysWideToUTF8(L"\x4f60\xd800zyxw"));
+
+  // Test embedded NULLs.
+  std::wstring wide_null(L"a");
+  wide_null.push_back(0);
+  wide_null.push_back('b');
+
+  std::string expected_null("a");
+  expected_null.push_back(0);
+  expected_null.push_back('b');
+
+  EXPECT_EQ(expected_null, SysWideToUTF8(wide_null));
+}
+
+TEST(SysStrings, SysUTF8ToWide) {
+  EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world"));
+  EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
+  // >16 bits
+  EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80"));
+
+  // Error case. When Windows finds an invalid UTF-8 character, it just skips
+  // it. This seems weird because it's inconsistent with the reverse conversion.
+  //
+  // This is what XP does, but Vista has different behavior, so we don't bother
+  // verifying it:
+  // EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw"));
+
+  // Test embedded NULLs.
+  std::string utf8_null("a");
+  utf8_null.push_back(0);
+  utf8_null.push_back('b');
+
+  std::wstring expected_null(L"a");
+  expected_null.push_back(0);
+  expected_null.push_back('b');
+
+  EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/crash_reporter/.project_alias b/crash_reporter/.project_alias
new file mode 100644 (file)
index 0000000..0bc3798
--- /dev/null
@@ -0,0 +1 @@
+crash
diff --git a/crash_reporter/99-crash-reporter.rules b/crash_reporter/99-crash-reporter.rules
new file mode 100644 (file)
index 0000000..aea5b1c
--- /dev/null
@@ -0,0 +1,6 @@
+ACTION=="change", SUBSYSTEM=="drm", KERNEL=="card0", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=KERNEL=card0:SUBSYSTEM=drm:ACTION=change"
+# For detecting cypress trackpad issue. Passing into crash_reporter SUBSYSTEM=i2c-cyapa since crash_reporter does not handle DRIVER string.
+ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="cyapa", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-cyapa:ACTION=change"
+# For detecting Atmel trackpad/touchscreen issue. Passing into crash_reporter SUBSYSTEM=i2c-atmel_mxt_ts since crash_reporter does not handle DRIVER string.
+ACTION=="change", SUBSYSTEM=="i2c", DRIVER=="atmel_mxt_ts", ENV{ERROR}=="1", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=i2c-atmel_mxt_ts:ACTION=change"
+ACTION=="add", SUBSYSTEM=="devcoredump", RUN+="/sbin/crash_reporter --udev=SUBSYSTEM=devcoredump:ACTION=add:KERNEL_NUMBER=%n"
diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk
new file mode 100644 (file)
index 0000000..81cb458
--- /dev/null
@@ -0,0 +1,147 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+crash_reporter_cpp_extension := .cc
+
+crash_reporter_src := crash_collector.cc \
+    kernel_collector.cc \
+    kernel_warning_collector.cc \
+    udev_collector.cc \
+    unclean_shutdown_collector.cc \
+    user_collector.cc
+
+crash_reporter_includes := external/gtest/include
+
+crash_reporter_test_src := crash_collector_test.cc \
+    crash_reporter_logs_test.cc \
+    kernel_collector_test.cc \
+    testrunner.cc \
+    udev_collector_test.cc \
+    unclean_shutdown_collector_test.cc \
+    user_collector_test.cc
+
+warn_collector_src := warn_collector.l
+
+# Crash reporter static library.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libcrash
+LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
+LOCAL_C_INCLUDES := $(crash_reporter_includes)
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := libchrome \
+    libbrillo \
+    libcutils \
+    libdbus \
+    libmetrics \
+    libpcrecpp
+LOCAL_SRC_FILES := $(crash_reporter_src)
+include $(BUILD_STATIC_LIBRARY)
+
+# Crash reporter client.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_reporter
+LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
+LOCAL_C_INCLUDES := $(crash_reporter_includes)
+LOCAL_REQUIRED_MODULES := core2md \
+    crash_reporter_logs.conf \
+    crash_sender \
+    crash_server \
+    dbus-send
+LOCAL_INIT_RC := crash_reporter.rc
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := libchrome \
+    libbrillo \
+    libcutils \
+    libdbus \
+    libmetrics \
+    libpcrecpp
+LOCAL_SRC_FILES := crash_reporter.cc
+LOCAL_STATIC_LIBRARIES := libcrash
+include $(BUILD_EXECUTABLE)
+
+# Crash sender script.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_sender
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)
+LOCAL_REQUIRED_MODULES := curl grep periodic_scheduler
+LOCAL_SRC_FILES := crash_sender
+include $(BUILD_PREBUILT)
+
+# Warn collector client.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := warn_collector
+LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
+LOCAL_SHARED_LIBRARIES := libmetrics
+LOCAL_SRC_FILES := $(warn_collector_src)
+include $(BUILD_EXECUTABLE)
+
+# /etc/os-release.d/crash_server configuration file.
+# ========================================================
+ifdef OSRELEASED_DIRECTORY
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_server
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)
+include $(BUILD_SYSTEM)/base_rules.mk
+
+# Optionally populate the BRILLO_CRASH_SERVER variable from a product
+# configuration file: brillo/crash_server.
+LOADED_BRILLO_CRASH_SERVER := $(call cfgtree-get-if-exists,brillo/crash_server)
+
+# If the crash server isn't set, use a blank value.  crash_sender
+# will log it as a configuration error.
+$(LOCAL_BUILT_MODULE): BRILLO_CRASH_SERVER ?= "$(LOADED_BRILLO_CRASH_SERVER)"
+$(LOCAL_BUILT_MODULE):
+       $(hide)mkdir -p $(dir $@)
+       echo $(BRILLO_CRASH_SERVER) > $@
+endif
+
+# Crash reporter logs conf file.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_reporter_logs.conf
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/etc
+LOCAL_SRC_FILES := crash_reporter_logs.conf
+include $(BUILD_PREBUILT)
+
+# Periodic Scheduler.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := periodic_scheduler
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)
+LOCAL_SRC_FILES := periodic_scheduler
+include $(BUILD_PREBUILT)
+
+# Crash reporter tests.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := crash_reporter_tests
+LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
+LOCAL_SHARED_LIBRARIES := libchrome \
+    libbrillo \
+    libcutils \
+    libdbus \
+    libpcrecpp
+LOCAL_SRC_FILES := $(crash_reporter_test_src)
+LOCAL_STATIC_LIBRARIES := libcrash libgmock
+include $(BUILD_NATIVE_TEST)
diff --git a/crash_reporter/OWNERS b/crash_reporter/OWNERS
new file mode 100644 (file)
index 0000000..96ea5b2
--- /dev/null
@@ -0,0 +1,2 @@
+set noparent
+vapier@chromium.org
diff --git a/crash_reporter/TEST_WARNING b/crash_reporter/TEST_WARNING
new file mode 100644 (file)
index 0000000..64ad2e9
--- /dev/null
@@ -0,0 +1,31 @@
+Apr 31 25:25:25 localhost kernel: [117959.226729]  [<ffffffff810e16bf>] do_vfs_ioctl+0x469/0x4b3
+Apr 31 25:25:25 localhost kernel: [117959.226738]  [<ffffffff810d3117>] ? fsnotify_access+0x58/0x60
+Apr 31 25:25:25 localhost kernel: [117959.226747]  [<ffffffff810d3791>] ? vfs_read+0xad/0xd7
+Apr 31 25:25:25 localhost kernel: [117959.226756]  [<ffffffff810e175f>] sys_ioctl+0x56/0x7b
+Apr 31 25:25:25 localhost kernel: [117959.226765]  [<ffffffff810d37fe>] ? sys_read+0x43/0x73
+Apr 31 25:25:25 localhost kernel: [117959.226774]  [<ffffffff8146b7d2>] system_call_fastpath+0x16/0x1b
+Apr 31 25:25:25 localhost kernel: [117959.226782] ---[ end trace f16822cad7406cec ]---
+Apr 31 25:25:25 localhost kernel: [117959.231085] ------------[ cut here ]------------
+Apr 31 25:25:25 localhost kernel: [117959.231100] WARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9()
+Apr 31 25:25:25 localhost kernel: [117959.231113] Hardware name: Link
+Apr 31 25:25:25 localhost kernel: [117959.231117] eDP powered off while attempting aux channel communication.
+Apr 31 25:25:25 localhost kernel: [117959.231240] Pid: 10508, comm: X Tainted: G        WC   3.4.0 #1
+Apr 31 25:25:25 localhost kernel: [117959.231247] Call Trace:
+Apr 31 25:25:25 localhost kernel: [117959.231393]  [<ffffffff810d3117>] ? fsnotify_access+0x58/0x60
+Apr 31 25:25:25 localhost kernel: [117959.231402]  [<ffffffff810d3791>] ? vfs_read+0xad/0xd7
+Apr 31 25:25:25 localhost kernel: [117959.231411]  [<ffffffff810e175f>] sys_ioctl+0x56/0x7b
+Apr 31 25:25:25 localhost kernel: [117959.231420]  [<ffffffff810d37fe>] ? sys_read+0x43/0x73
+Apr 31 25:25:25 localhost kernel: [117959.231431]  [<ffffffff8146b7d2>] system_call_fastpath+0x16/0x1b
+Apr 31 25:25:25 localhost kernel: [117959.231439] ---[ end trace f16822cad7406ced ]---
+Apr 31 25:25:25 localhost kernel: [117959.231450] ------------[ cut here ]------------
+Apr 31 25:25:25 localhost kernel: [117959.231458] BARNING: at /mnt/host/source/src/third_party/kernel/files/drivers/gpu/drm/i915/intel_dp.c:351 intel_dp_check_edp+0x6b/0xb9()
+Apr 31 25:25:25 localhost kernel: [117959.231458] ("BARNING" above is intentional)
+Apr 31 25:25:25 localhost kernel: [117959.231471] Hardware name: Link
+Apr 31 25:25:25 localhost kernel: [117959.231475] eDP powered off while attempting aux channel communication.
+Apr 31 25:25:25 localhost kernel: [117959.231482] Modules linked in: nls_iso8859_1 nls_cp437 vfat fat rfcomm i2c_dev ath9k_btcoex snd_hda_codec_hdmi snd_hda_codec_ca0132 mac80211 snd_hda_intel ath9k_common_btcoex snd_hda_codec ath9k_hw_btcoex aesni_intel cryptd snd_hwdep ath snd_pcm aes_x86_64 isl29018(C) memconsole snd_timer snd_page_alloc industrialio(C) cfg80211 rtc_cmos nm10_gpio zram(C) zsmalloc(C) lzo_decompress lzo_compress fuse nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables xt_mark option usb_wwan cdc_ether usbnet ath3k btusb bluetooth uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops joydev
+Apr 31 25:25:25 localhost kernel: [117959.231588] Pid: 10508, comm: X Tainted: G        WC   3.4.0 #1
+Apr 31 25:25:25 localhost kernel: [117959.231595] Call Trace:
+Apr 31 25:25:25 localhost kernel: [117959.231601]  [<ffffffff8102a931>] warn_slowpath_common+0x83/0x9c
+Apr 31 25:25:25 localhost kernel: [117959.231610]  [<ffffffff8102a9ed>] warn_slowpath_fmt+0x46/0x48
+Apr 31 25:25:25 localhost kernel: [117959.231620]  [<ffffffff812af495>] intel_dp_check_edp+0x6b/0xb9
+Apr 31 25:25:25 localhost kernel: [117959.231629]  [<ffffffff8102a9ed>] ? warn_slowpath_fmt+
diff --git a/crash_reporter/crash_collector.cc b/crash_reporter/crash_collector.cc
new file mode 100644 (file)
index 0000000..2a9d1d3
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * 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 "crash_collector.h"
+
+#include <dirent.h>
+#include <fcntl.h>  // For file creation modes.
+#include <inttypes.h>
+#include <linux/limits.h>  // PATH_MAX
+#include <pwd.h>  // For struct passwd.
+#include <sys/types.h>  // for mode_t.
+#include <sys/wait.h>  // For waitpid.
+#include <unistd.h>  // For execv and fork.
+
+#include <set>
+#include <utility>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/key_value_store.h>
+#include <brillo/process.h>
+
+namespace {
+
+const char kCollectChromeFile[] =
+    "/mnt/stateful_partition/etc/collect_chrome_crashes";
+const char kCrashTestInProgressPath[] =
+    "/data/misc/crash_reporter/tmp/crash-test-in-progress";
+const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
+const char kDefaultUserName[] = "chronos";
+const char kLeaveCoreFile[] = "/data/misc/crash_reporter/.leave_core";
+const char kLsbRelease[] = "/etc/lsb-release";
+const char kShellPath[] = "/system/bin/sh";
+const char kSystemCrashPath[] = "/data/misc/crash_reporter/crash";
+const char kUploadVarPrefix[] = "upload_var_";
+const char kUploadFilePrefix[] = "upload_file_";
+
+// Normally this path is not used.  Unfortunately, there are a few edge cases
+// where we need this.  Any process that runs as kDefaultUserName that crashes
+// is consider a "user crash".  That includes the initial Chrome browser that
+// runs the login screen.  If that blows up, there is no logged in user yet,
+// so there is no per-user dir for us to stash things in.  Instead we fallback
+// to this path as it is at least encrypted on a per-system basis.
+//
+// This also comes up when running autotests.  The GUI is sitting at the login
+// screen while tests are sshing in, changing users, and triggering crashes as
+// the user (purposefully).
+const char kFallbackUserCrashPath[] = "/home/chronos/crash";
+
+// Directory mode of the user crash spool directory.
+const mode_t kUserCrashPathMode = 0755;
+
+// Directory mode of the system crash spool directory.
+const mode_t kSystemCrashPathMode = 01755;
+
+const uid_t kRootOwner = 0;
+const uid_t kRootGroup = 0;
+
+}  // namespace
+
+// Maximum crash reports per crash spool directory.  Note that this is
+// a separate maximum from the maximum rate at which we upload these
+// diagnostics.  The higher this rate is, the more space we allow for
+// core files, minidumps, and kcrash logs, and equivalently the more
+// processor and I/O bandwidth we dedicate to handling these crashes when
+// many occur at once.  Also note that if core files are configured to
+// be left on the file system, we stop adding crashes when either the
+// number of core files or minidumps reaches this number.
+const int CrashCollector::kMaxCrashDirectorySize = 32;
+
+using base::FilePath;
+using base::StringPrintf;
+
+CrashCollector::CrashCollector()
+    : lsb_release_(kLsbRelease),
+      log_config_path_(kDefaultLogConfig) {
+}
+
+CrashCollector::~CrashCollector() {
+}
+
+void CrashCollector::Initialize(
+    CrashCollector::CountCrashFunction count_crash_function,
+    CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) {
+  CHECK(count_crash_function);
+  CHECK(is_feedback_allowed_function);
+
+  count_crash_function_ = count_crash_function;
+  is_feedback_allowed_function_ = is_feedback_allowed_function;
+}
+
+int CrashCollector::WriteNewFile(const FilePath &filename,
+                                 const char *data,
+                                 int size) {
+  int fd = HANDLE_EINTR(open(filename.value().c_str(),
+                             O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666));
+  if (fd < 0) {
+    return -1;
+  }
+
+  int rv = base::WriteFileDescriptor(fd, data, size) ? size : -1;
+  IGNORE_EINTR(close(fd));
+  return rv;
+}
+
+std::string CrashCollector::Sanitize(const std::string &name) {
+  // Make sure the sanitized name does not include any periods.
+  // The logic in crash_sender relies on this.
+  std::string result = name;
+  for (size_t i = 0; i < name.size(); ++i) {
+    if (!isalnum(result[i]) && result[i] != '_')
+      result[i] = '_';
+  }
+  return result;
+}
+
+std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
+                                               time_t timestamp,
+                                               pid_t pid) {
+  struct tm tm;
+  localtime_r(&timestamp, &tm);
+  std::string sanitized_exec_name = Sanitize(exec_name);
+  return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
+                      sanitized_exec_name.c_str(),
+                      tm.tm_year + 1900,
+                      tm.tm_mon + 1,
+                      tm.tm_mday,
+                      tm.tm_hour,
+                      tm.tm_min,
+                      tm.tm_sec,
+                      pid);
+}
+
+FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory,
+                                      const std::string &basename,
+                                      const std::string &extension) {
+  return crash_directory.Append(StringPrintf("%s.%s",
+                                             basename.c_str(),
+                                             extension.c_str()));
+}
+
+FilePath CrashCollector::GetCrashDirectoryInfo(
+    mode_t *mode,
+    uid_t *directory_owner,
+    gid_t *directory_group) {
+  *mode = kSystemCrashPathMode;
+  *directory_owner = kRootOwner;
+  *directory_group = kRootGroup;
+  return FilePath(kSystemCrashPath);
+}
+
+bool CrashCollector::GetUserInfoFromName(const std::string &name,
+                                         uid_t *uid,
+                                         gid_t *gid) {
+  char storage[256];
+  struct passwd passwd_storage;
+  struct passwd *passwd_result = nullptr;
+
+  if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
+                 &passwd_result) != 0 || passwd_result == nullptr) {
+    LOG(ERROR) << "Cannot find user named " << name;
+    return false;
+  }
+
+  *uid = passwd_result->pw_uid;
+  *gid = passwd_result->pw_gid;
+  return true;
+}
+
+bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
+                                                    FilePath *crash_directory,
+                                                    bool *out_of_capacity) {
+  if (out_of_capacity) *out_of_capacity = false;
+
+  // For testing.
+  if (!forced_crash_directory_.empty()) {
+    *crash_directory = forced_crash_directory_;
+    return true;
+  }
+
+  mode_t directory_mode;
+  uid_t directory_owner;
+  gid_t directory_group;
+  *crash_directory =
+      GetCrashDirectoryInfo(&directory_mode,
+                            &directory_owner,
+                            &directory_group);
+
+  if (!base::PathExists(*crash_directory)) {
+    // Create the spool directory with the appropriate mode (regardless of
+    // umask) and ownership.
+    mode_t old_mask = umask(0);
+    if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
+        chown(crash_directory->value().c_str(),
+              directory_owner,
+              directory_group) < 0) {
+      LOG(ERROR) << "Unable to create appropriate crash directory";
+      return false;
+    }
+    umask(old_mask);
+  }
+
+  if (!base::PathExists(*crash_directory)) {
+    LOG(ERROR) << "Unable to create crash directory "
+               << crash_directory->value().c_str();
+    return false;
+  }
+
+  if (!CheckHasCapacity(*crash_directory)) {
+    if (out_of_capacity) *out_of_capacity = true;
+    LOG(ERROR) << "Directory " << crash_directory->value()
+               << " is out of capacity.";
+    return false;
+  }
+
+  return true;
+}
+
+FilePath CrashCollector::GetProcessPath(pid_t pid) {
+  return FilePath(StringPrintf("/proc/%d", pid));
+}
+
+bool CrashCollector::GetSymlinkTarget(const FilePath &symlink,
+                                      FilePath *target) {
+  ssize_t max_size = 64;
+  std::vector<char> buffer;
+
+  while (true) {
+    buffer.resize(max_size + 1);
+    ssize_t size = readlink(symlink.value().c_str(), buffer.data(), max_size);
+    if (size < 0) {
+      int saved_errno = errno;
+      LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
+                 << saved_errno;
+      return false;
+    }
+
+    buffer[size] = 0;
+    if (size == max_size) {
+      max_size *= 2;
+      if (max_size > PATH_MAX) {
+        return false;
+      }
+      continue;
+    }
+    break;
+  }
+
+  *target = FilePath(buffer.data());
+  return true;
+}
+
+bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid,
+                                                 std::string *base_name) {
+  FilePath target;
+  FilePath process_path = GetProcessPath(pid);
+  FilePath exe_path = process_path.Append("exe");
+  if (!GetSymlinkTarget(exe_path, &target)) {
+    LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
+              << " DirectoryExists: "
+              << base::DirectoryExists(process_path);
+    // Try to further diagnose exe readlink failure cause.
+    struct stat buf;
+    int stat_result = stat(exe_path.value().c_str(), &buf);
+    int saved_errno = errno;
+    if (stat_result < 0) {
+      LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
+                << " " << saved_errno;
+    } else {
+      LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
+                << buf.st_mode;
+    }
+    return false;
+  }
+  *base_name = target.BaseName().value();
+  return true;
+}
+
+// Return true if the given crash directory has not already reached
+// maximum capacity.
+bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
+  DIR* dir = opendir(crash_directory.value().c_str());
+  if (!dir) {
+    LOG(WARNING) << "Unable to open crash directory "
+                 << crash_directory.value();
+    return false;
+  }
+  struct dirent ent_buf;
+  struct dirent* ent;
+  bool full = false;
+  std::set<std::string> basenames;
+  while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
+    if ((strcmp(ent->d_name, ".") == 0) ||
+        (strcmp(ent->d_name, "..") == 0))
+      continue;
+
+    std::string filename(ent->d_name);
+    size_t last_dot = filename.rfind(".");
+    std::string basename;
+    // If there is a valid looking extension, use the base part of the
+    // name.  If the only dot is the first byte (aka a dot file), treat
+    // it as unique to avoid allowing a directory full of dot files
+    // from accumulating.
+    if (last_dot != std::string::npos && last_dot != 0)
+      basename = filename.substr(0, last_dot);
+    else
+      basename = filename;
+    basenames.insert(basename);
+
+    if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
+      LOG(WARNING) << "Crash directory " << crash_directory.value()
+                   << " already full with " << kMaxCrashDirectorySize
+                   << " pending reports";
+      full = true;
+      break;
+    }
+  }
+  closedir(dir);
+  return !full;
+}
+
+bool CrashCollector::GetLogContents(const FilePath &config_path,
+                                    const std::string &exec_name,
+                                    const FilePath &output_file) {
+  brillo::KeyValueStore store;
+  if (!store.Load(config_path)) {
+    LOG(INFO) << "Unable to read log configuration file "
+              << config_path.value();
+    return false;
+  }
+
+  std::string command;
+  if (!store.GetString(exec_name, &command))
+    return false;
+
+  brillo::ProcessImpl diag_process;
+  diag_process.AddArg(kShellPath);
+  diag_process.AddStringOption("-c", command);
+  diag_process.RedirectOutput(output_file.value());
+
+  const int result = diag_process.Run();
+  if (result != 0) {
+    LOG(INFO) << "Log command \"" << command << "\" exited with " << result;
+    return false;
+  }
+  return true;
+}
+
+void CrashCollector::AddCrashMetaData(const std::string &key,
+                                      const std::string &value) {
+  extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
+}
+
+void CrashCollector::AddCrashMetaUploadFile(const std::string &key,
+                                            const std::string &path) {
+  if (!path.empty())
+    AddCrashMetaData(kUploadFilePrefix + key, path);
+}
+
+void CrashCollector::AddCrashMetaUploadData(const std::string &key,
+                                            const std::string &value) {
+  if (!value.empty())
+    AddCrashMetaData(kUploadVarPrefix + key, value);
+}
+
+void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
+                                        const std::string &exec_name,
+                                        const std::string &payload_path) {
+  int64_t payload_size = -1;
+  base::GetFileSize(FilePath(payload_path), &payload_size);
+  std::string meta_data = StringPrintf("%sexec_name=%s\n"
+                                       "payload=%s\n"
+                                       "payload_size=%" PRId64 "\n"
+                                       "done=1\n",
+                                       extra_metadata_.c_str(),
+                                       exec_name.c_str(),
+                                       payload_path.c_str(),
+                                       payload_size);
+  // We must use WriteNewFile instead of base::WriteFile as we
+  // do not want to write with root access to a symlink that an attacker
+  // might have created.
+  if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) {
+    LOG(ERROR) << "Unable to write " << meta_path.value();
+  }
+}
+
+bool CrashCollector::IsCrashTestInProgress() {
+  return base::PathExists(FilePath(kCrashTestInProgressPath));
+}
+
+bool CrashCollector::IsDeveloperImage() {
+  // If we're testing crash reporter itself, we don't want to special-case
+  // for developer images.
+  if (IsCrashTestInProgress())
+    return false;
+  return base::PathExists(FilePath(kLeaveCoreFile));
+}
diff --git a/crash_reporter/crash_collector.h b/crash_reporter/crash_collector.h
new file mode 100644 (file)
index 0000000..cfd76fd
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * 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 CRASH_REPORTER_CRASH_COLLECTOR_H_
+#define CRASH_REPORTER_CRASH_COLLECTOR_H_
+
+#include <sys/stat.h>
+
+#include <map>
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+// User crash collector.
+class CrashCollector {
+ public:
+  typedef void (*CountCrashFunction)();
+  typedef bool (*IsFeedbackAllowedFunction)();
+
+  CrashCollector();
+
+  virtual ~CrashCollector();
+
+  // Initialize the crash collector for detection of crashes, given a
+  // crash counting function, and metrics collection enabled oracle.
+  void Initialize(CountCrashFunction count_crash,
+                  IsFeedbackAllowedFunction is_metrics_allowed);
+
+ protected:
+  friend class CrashCollectorTest;
+  FRIEND_TEST(ChromeCollectorTest, HandleCrash);
+  FRIEND_TEST(CrashCollectorTest, CheckHasCapacityCorrectBasename);
+  FRIEND_TEST(CrashCollectorTest, CheckHasCapacityStrangeNames);
+  FRIEND_TEST(CrashCollectorTest, CheckHasCapacityUsual);
+  FRIEND_TEST(CrashCollectorTest, GetCrashDirectoryInfo);
+  FRIEND_TEST(CrashCollectorTest, GetCrashPath);
+  FRIEND_TEST(CrashCollectorTest, GetLogContents);
+  FRIEND_TEST(CrashCollectorTest, ForkExecAndPipe);
+  FRIEND_TEST(CrashCollectorTest, FormatDumpBasename);
+  FRIEND_TEST(CrashCollectorTest, Initialize);
+  FRIEND_TEST(CrashCollectorTest, MetaData);
+  FRIEND_TEST(CrashCollectorTest, Sanitize);
+  FRIEND_TEST(CrashCollectorTest, WriteNewFile);
+  FRIEND_TEST(ForkExecAndPipeTest, Basic);
+  FRIEND_TEST(ForkExecAndPipeTest, NonZeroReturnValue);
+  FRIEND_TEST(ForkExecAndPipeTest, BadOutputFile);
+  FRIEND_TEST(ForkExecAndPipeTest, ExistingOutputFile);
+  FRIEND_TEST(ForkExecAndPipeTest, BadExecutable);
+  FRIEND_TEST(ForkExecAndPipeTest, StderrCaptured);
+  FRIEND_TEST(ForkExecAndPipeTest, NULLParam);
+  FRIEND_TEST(ForkExecAndPipeTest, NoParams);
+  FRIEND_TEST(ForkExecAndPipeTest, SegFaultHandling);
+
+  // Set maximum enqueued crashes in a crash directory.
+  static const int kMaxCrashDirectorySize;
+
+  // Writes |data| of |size| to |filename|, which must be a new file.
+  // If the file already exists or writing fails, return a negative value.
+  // Otherwise returns the number of bytes written.
+  int WriteNewFile(const base::FilePath &filename, const char *data, int size);
+
+  // Return a filename that has only [a-z0-1_] characters by mapping
+  // all others into '_'.
+  std::string Sanitize(const std::string &name);
+
+  // For testing, set the directory always returned by
+  // GetCreatedCrashDirectoryByEuid.
+  void ForceCrashDirectory(const base::FilePath &forced_directory) {
+    forced_crash_directory_ = forced_directory;
+  }
+
+  base::FilePath GetCrashDirectoryInfo(mode_t *mode,
+                                       uid_t *directory_owner,
+                                       gid_t *directory_group);
+  bool GetUserInfoFromName(const std::string &name,
+                           uid_t *uid,
+                           gid_t *gid);
+
+  // Determines the crash directory for given euid, and creates the
+  // directory if necessary with appropriate permissions.  If
+  // |out_of_capacity| is not nullptr, it is set to indicate if the call
+  // failed due to not having capacity in the crash directory. Returns
+  // true whether or not directory needed to be created, false on any
+  // failure.  If the crash directory is at capacity, returns false.
+  bool GetCreatedCrashDirectoryByEuid(uid_t euid,
+                                      base::FilePath *crash_file_path,
+                                      bool *out_of_capacity);
+
+  // Format crash name based on components.
+  std::string FormatDumpBasename(const std::string &exec_name,
+                                 time_t timestamp,
+                                 pid_t pid);
+
+  // Create a file path to a file in |crash_directory| with the given
+  // |basename| and |extension|.
+  base::FilePath GetCrashPath(const base::FilePath &crash_directory,
+                              const std::string &basename,
+                              const std::string &extension);
+
+  base::FilePath GetProcessPath(pid_t pid);
+  bool GetSymlinkTarget(const base::FilePath &symlink,
+                        base::FilePath *target);
+  bool GetExecutableBaseNameFromPid(pid_t pid,
+                                    std::string *base_name);
+
+  // Check given crash directory still has remaining capacity for another
+  // crash.
+  bool CheckHasCapacity(const base::FilePath &crash_directory);
+
+  // Write a log applicable to |exec_name| to |output_file| based on the
+  // log configuration file at |config_path|.
+  bool GetLogContents(const base::FilePath &config_path,
+                      const std::string &exec_name,
+                      const base::FilePath &output_file);
+
+  // Add non-standard meta data to the crash metadata file.  Call
+  // before calling WriteCrashMetaData.  Key must not contain "=" or
+  // "\n" characters.  Value must not contain "\n" characters.
+  void AddCrashMetaData(const std::string &key, const std::string &value);
+
+  // Add a file to be uploaded to the crash reporter server. The file must
+  // persist until the crash report is sent; ideally it should live in the same
+  // place as the .meta file, so it can be cleaned up automatically.
+  void AddCrashMetaUploadFile(const std::string &key, const std::string &path);
+
+  // Add non-standard meta data to the crash metadata file.
+  // Data added though this call will be uploaded to the crash reporter server,
+  // appearing as a form field.
+  void AddCrashMetaUploadData(const std::string &key, const std::string &value);
+
+  // Write a file of metadata about crash.
+  void WriteCrashMetaData(const base::FilePath &meta_path,
+                          const std::string &exec_name,
+                          const std::string &payload_path);
+
+  // Returns true if the a crash test is currently running.
+  bool IsCrashTestInProgress();
+  // Returns true if we should consider ourselves to be running on a
+  // developer image.
+  bool IsDeveloperImage();
+
+  CountCrashFunction count_crash_function_;
+  IsFeedbackAllowedFunction is_feedback_allowed_function_;
+  std::string extra_metadata_;
+  base::FilePath forced_crash_directory_;
+  std::string lsb_release_;
+  base::FilePath log_config_path_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CrashCollector);
+};
+
+#endif  // CRASH_REPORTER_CRASH_COLLECTOR_H_
diff --git a/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
new file mode 100644 (file)
index 0000000..d00a5b5
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * 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 "crash_collector_test.h"
+
+#include <unistd.h>
+#include <utility>
+
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/syslog_logging.h>
+#include <gtest/gtest.h>
+
+#include "crash_collector.h"
+
+using base::FilePath;
+using base::StringPrintf;
+using brillo::FindLog;
+using ::testing::Invoke;
+using ::testing::Return;
+
+namespace {
+
+void CountCrash() {
+  ADD_FAILURE();
+}
+
+bool IsMetrics() {
+  ADD_FAILURE();
+  return false;
+}
+
+}  // namespace
+
+class CrashCollectorTest : public ::testing::Test {
+ public:
+  void SetUp() {
+    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(Return());
+
+    collector_.Initialize(CountCrash, IsMetrics);
+    test_dir_ = FilePath("test");
+    base::CreateDirectory(test_dir_);
+    brillo::ClearLog();
+  }
+
+  void TearDown() {
+    base::DeleteFile(test_dir_, true);
+  }
+
+  bool CheckHasCapacity();
+
+ protected:
+  CrashCollectorMock collector_;
+  FilePath test_dir_;
+};
+
+TEST_F(CrashCollectorTest, Initialize) {
+  ASSERT_TRUE(CountCrash == collector_.count_crash_function_);
+  ASSERT_TRUE(IsMetrics == collector_.is_feedback_allowed_function_);
+}
+
+TEST_F(CrashCollectorTest, WriteNewFile) {
+  FilePath test_file = test_dir_.Append("test_new");
+  const char kBuffer[] = "buffer";
+  EXPECT_EQ(strlen(kBuffer),
+            collector_.WriteNewFile(test_file,
+                                    kBuffer,
+                                    strlen(kBuffer)));
+  EXPECT_LT(collector_.WriteNewFile(test_file,
+                                    kBuffer,
+                                    strlen(kBuffer)), 0);
+}
+
+TEST_F(CrashCollectorTest, Sanitize) {
+  EXPECT_EQ("chrome", collector_.Sanitize("chrome"));
+  EXPECT_EQ("CHROME", collector_.Sanitize("CHROME"));
+  EXPECT_EQ("1chrome2", collector_.Sanitize("1chrome2"));
+  EXPECT_EQ("chrome__deleted_", collector_.Sanitize("chrome (deleted)"));
+  EXPECT_EQ("foo_bar", collector_.Sanitize("foo.bar"));
+  EXPECT_EQ("", collector_.Sanitize(""));
+  EXPECT_EQ("_", collector_.Sanitize(" "));
+}
+
+TEST_F(CrashCollectorTest, FormatDumpBasename) {
+  struct tm tm = {0};
+  tm.tm_sec = 15;
+  tm.tm_min = 50;
+  tm.tm_hour = 13;
+  tm.tm_mday = 23;
+  tm.tm_mon = 4;
+  tm.tm_year = 110;
+  tm.tm_isdst = -1;
+  std::string basename =
+      collector_.FormatDumpBasename("foo", mktime(&tm), 100);
+  ASSERT_EQ("foo.20100523.135015.100", basename);
+}
+
+TEST_F(CrashCollectorTest, GetCrashPath) {
+  EXPECT_EQ("/var/spool/crash/myprog.20100101.1200.1234.core",
+            collector_.GetCrashPath(FilePath("/var/spool/crash"),
+                                    "myprog.20100101.1200.1234",
+                                    "core").value());
+  EXPECT_EQ("/home/chronos/user/crash/chrome.20100101.1200.1234.dmp",
+            collector_.GetCrashPath(FilePath("/home/chronos/user/crash"),
+                                    "chrome.20100101.1200.1234",
+                                    "dmp").value());
+}
+
+
+bool CrashCollectorTest::CheckHasCapacity() {
+  static const char kFullMessage[] = "Crash directory test already full";
+  bool has_capacity = collector_.CheckHasCapacity(test_dir_);
+  bool has_message = FindLog(kFullMessage);
+  EXPECT_EQ(has_message, !has_capacity);
+  return has_capacity;
+}
+
+TEST_F(CrashCollectorTest, CheckHasCapacityUsual) {
+  // Test kMaxCrashDirectorySize - 1 non-meta files can be added.
+  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), "", 0);
+    EXPECT_TRUE(CheckHasCapacity());
+  }
+
+  // Test an additional kMaxCrashDirectorySize - 1 meta files fit.
+  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf("file%d.meta", i)), "", 0);
+    EXPECT_TRUE(CheckHasCapacity());
+  }
+
+  // Test an additional kMaxCrashDirectorySize meta files don't fit.
+  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf("overage%d.meta", i)), "", 0);
+    EXPECT_FALSE(CheckHasCapacity());
+  }
+}
+
+TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) {
+  // Test kMaxCrashDirectorySize - 1 files can be added.
+  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf("file.%d.core", i)), "", 0);
+    EXPECT_TRUE(CheckHasCapacity());
+  }
+  base::WriteFile(test_dir_.Append("file.last.core"), "", 0);
+  EXPECT_FALSE(CheckHasCapacity());
+}
+
+TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) {
+  // Test many files with different extensions and same base fit.
+  for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf("a.%d", i)), "", 0);
+    EXPECT_TRUE(CheckHasCapacity());
+  }
+  // Test dot files are treated as individual files.
+  for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) {
+    base::WriteFile(test_dir_.Append(StringPrintf(".file%d", i)), "", 0);
+    EXPECT_TRUE(CheckHasCapacity());
+  }
+  base::WriteFile(test_dir_.Append("normal.meta"), "", 0);
+  EXPECT_FALSE(CheckHasCapacity());
+}
+
+TEST_F(CrashCollectorTest, MetaData) {
+  const char kMetaFileBasename[] = "generated.meta";
+  FilePath meta_file = test_dir_.Append(kMetaFileBasename);
+  FilePath lsb_release = test_dir_.Append("lsb-release");
+  FilePath payload_file = test_dir_.Append("payload-file");
+  std::string contents;
+  collector_.lsb_release_ = lsb_release.value();
+  const char kLsbContents[] =
+      "CHROMEOS_RELEASE_BOARD=lumpy\n"
+      "CHROMEOS_RELEASE_VERSION=6727.0.2015_01_26_0853\n"
+      "CHROMEOS_RELEASE_NAME=Chromium OS\n";
+  ASSERT_TRUE(base::WriteFile(lsb_release, kLsbContents, strlen(kLsbContents)));
+  const char kPayload[] = "foo";
+  ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
+  collector_.AddCrashMetaData("foo", "bar");
+  collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value());
+  EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
+  const char kExpectedMeta[] =
+      "foo=bar\n"
+      "exec_name=kernel\n"
+      "ver=6727.0.2015_01_26_0853\n"
+      "payload=test/payload-file\n"
+      "payload_size=3\n"
+      "done=1\n";
+  EXPECT_EQ(kExpectedMeta, contents);
+
+  // Test target of symlink is not overwritten.
+  payload_file = test_dir_.Append("payload2-file");
+  ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
+  FilePath meta_symlink_path = test_dir_.Append("symlink.meta");
+  ASSERT_EQ(0,
+            symlink(kMetaFileBasename,
+                    meta_symlink_path.value().c_str()));
+  ASSERT_TRUE(base::PathExists(meta_symlink_path));
+  brillo::ClearLog();
+  collector_.WriteCrashMetaData(meta_symlink_path,
+                                "kernel",
+                                payload_file.value());
+  // Target metadata contents should have stayed the same.
+  contents.clear();
+  EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
+  EXPECT_EQ(kExpectedMeta, contents);
+  EXPECT_TRUE(FindLog("Unable to write"));
+
+  // Test target of dangling symlink is not created.
+  base::DeleteFile(meta_file, false);
+  ASSERT_FALSE(base::PathExists(meta_file));
+  brillo::ClearLog();
+  collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
+                                payload_file.value());
+  EXPECT_FALSE(base::PathExists(meta_file));
+  EXPECT_TRUE(FindLog("Unable to write"));
+}
+
+TEST_F(CrashCollectorTest, GetLogContents) {
+  FilePath config_file = test_dir_.Append("crash_config");
+  FilePath output_file = test_dir_.Append("crash_log");
+  const char kConfigContents[] =
+      "foobar=echo hello there | \\\n  sed -e \"s/there/world/\"";
+  ASSERT_TRUE(
+      base::WriteFile(config_file, kConfigContents, strlen(kConfigContents)));
+  base::DeleteFile(FilePath(output_file), false);
+  EXPECT_FALSE(collector_.GetLogContents(config_file,
+                                         "barfoo",
+                                         output_file));
+  EXPECT_FALSE(base::PathExists(output_file));
+  base::DeleteFile(FilePath(output_file), false);
+  EXPECT_TRUE(collector_.GetLogContents(config_file,
+                                        "foobar",
+                                        output_file));
+  ASSERT_TRUE(base::PathExists(output_file));
+  std::string contents;
+  EXPECT_TRUE(base::ReadFileToString(output_file, &contents));
+  EXPECT_EQ("hello world\n", contents);
+}
diff --git a/crash_reporter/crash_collector_test.h b/crash_reporter/crash_collector_test.h
new file mode 100644 (file)
index 0000000..cfbb97b
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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 CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
+#define CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
+
+#include "crash_collector.h"
+
+#include <map>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+class CrashCollectorMock : public CrashCollector {
+ public:
+  MOCK_METHOD0(SetUpDBus, void());
+  MOCK_METHOD1(GetActiveUserSessions,
+               bool(std::map<std::string, std::string> *sessions));
+};
+
+#endif  // CRASH_REPORTER_CRASH_COLLECTOR_TEST_H_
diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc
new file mode 100644 (file)
index 0000000..3955fe5
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * 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 <fcntl.h>  // for open
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/guid.h>
+#include <base/logging.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/flag_helper.h>
+#include <brillo/process.h>
+#include <brillo/syslog_logging.h>
+#include <metrics/metrics_library.h>
+
+#include "kernel_collector.h"
+#include "kernel_warning_collector.h"
+#include "udev_collector.h"
+#include "unclean_shutdown_collector.h"
+#include "user_collector.h"
+
+static const char kCrashCounterHistogram[] = "Logging.CrashCounter";
+static const char kUserCrashSignal[] =
+    "org.chromium.CrashReporter.UserCrash";
+static const char kKernelCrashDetected[] = "/var/run/kernel-crash-detected";
+static const char kUncleanShutdownDetected[] =
+    "/var/run/unclean-shutdown-detected";
+static const char kGUIDFileName[] = "/data/misc/crash_reporter/guid";
+
+// Enumeration of kinds of crashes to be used in the CrashCounter histogram.
+enum CrashKinds {
+  kCrashKindUncleanShutdown = 1,
+  kCrashKindUser = 2,
+  kCrashKindKernel = 3,
+  kCrashKindUdev = 4,
+  kCrashKindKernelWarning = 5,
+  kCrashKindMax
+};
+
+static MetricsLibrary s_metrics_lib;
+
+using base::FilePath;
+using base::StringPrintf;
+
+static bool IsFeedbackAllowed() {
+  return s_metrics_lib.AreMetricsEnabled();
+}
+
+static bool TouchFile(const FilePath &file_path) {
+  return base::WriteFile(file_path, "", 0) == 0;
+}
+
+static void SendCrashMetrics(CrashKinds type, const char* name) {
+  // TODO(kmixter): We can remove this histogram as part of
+  // crosbug.com/11163.
+  s_metrics_lib.SendEnumToUMA(kCrashCounterHistogram, type, kCrashKindMax);
+  s_metrics_lib.SendCrashToUMA(name);
+}
+
+static void CountKernelCrash() {
+  SendCrashMetrics(kCrashKindKernel, "kernel");
+}
+
+static void CountUdevCrash() {
+  SendCrashMetrics(kCrashKindUdev, "udevcrash");
+}
+
+static void CountUncleanShutdown() {
+  SendCrashMetrics(kCrashKindUncleanShutdown, "uncleanshutdown");
+}
+
+static void CountUserCrash() {
+  SendCrashMetrics(kCrashKindUser, "user");
+  // Announce through D-Bus whenever a user crash happens. This is
+  // used by the metrics daemon to log active use time between
+  // crashes.
+  //
+  // We run in the background in case dbus-daemon itself is crashed
+  // and not responding.  This allows us to not block and potentially
+  // deadlock on a dbus-daemon crash.  If dbus-daemon crashes without
+  // restarting, each crash will fork off a lot of dbus-send
+  // processes.  Such a system is in a unusable state and will need
+  // to be restarted anyway.
+  //
+  // Note: This will mean that the dbus-send process will become a zombie and
+  // reparent to init for reaping, but that's OK -- see above.
+
+  brillo::ProcessImpl dbus_send;
+  dbus_send.AddArg("/system/bin/dbus-send");
+  dbus_send.AddArg("--type=signal");
+  dbus_send.AddArg("--system");
+  dbus_send.AddArg("/");
+  dbus_send.AddArg(kUserCrashSignal);
+  bool status = dbus_send.Start();
+  if (status) {
+    dbus_send.Release();
+  } else {
+    PLOG(WARNING) << "Sending UserCrash DBus signal failed";
+  }
+}
+
+
+static int Initialize(KernelCollector *kernel_collector,
+                      UserCollector *user_collector,
+                      UncleanShutdownCollector *unclean_shutdown_collector,
+                      const bool unclean_check,
+                      const bool clean_shutdown) {
+  CHECK(!clean_shutdown) << "Incompatible options";
+
+  // Try to read the GUID from kGUIDFileName.  If the file doesn't exist, is
+  // blank, or the read fails, generate a new GUID and write it to the file.
+  std::string guid;
+  base::FilePath filepath(kGUIDFileName);
+  if (!base::ReadFileToString(filepath, &guid) || guid.empty()) {
+    guid = base::GenerateGUID();
+    // If we can't read or write the file, log an error.  However it is not
+    // a fatal error, as the crash server will assign a random GUID based
+    // on a hash of the IP address if one is not provided in the report.
+    if (base::WriteFile(filepath, guid.c_str(), guid.size()) <= 0) {
+      LOG(ERROR) << "Could not write guid " << guid << " to file "
+                 << filepath.value();
+    }
+  }
+
+  bool was_kernel_crash = false;
+  bool was_unclean_shutdown = false;
+  kernel_collector->Enable();
+  if (kernel_collector->is_enabled()) {
+    was_kernel_crash = kernel_collector->Collect();
+  }
+
+  if (unclean_check) {
+    was_unclean_shutdown = unclean_shutdown_collector->Collect();
+  }
+
+  // Touch a file to notify the metrics daemon that a kernel
+  // crash has been detected so that it can log the time since
+  // the last kernel crash.
+  if (IsFeedbackAllowed()) {
+    if (was_kernel_crash) {
+      TouchFile(FilePath(kKernelCrashDetected));
+    } else if (was_unclean_shutdown) {
+      // We only count an unclean shutdown if it did not come with
+      // an associated kernel crash.
+      TouchFile(FilePath(kUncleanShutdownDetected));
+    }
+  }
+
+  // Must enable the unclean shutdown collector *after* collecting.
+  unclean_shutdown_collector->Enable();
+  user_collector->Enable();
+
+  return 0;
+}
+
+static int HandleUserCrash(UserCollector *user_collector,
+                           const std::string& user, const bool crash_test) {
+  // Handle a specific user space crash.
+  CHECK(!user.empty()) << "--user= must be set";
+
+  // Make it possible to test what happens when we crash while
+  // handling a crash.
+  if (crash_test) {
+    *(volatile char *)0 = 0;
+    return 0;
+  }
+
+  // Accumulate logs to help in diagnosing failures during user collection.
+  brillo::LogToString(true);
+  // Handle the crash, get the name of the process from procfs.
+  bool handled = user_collector->HandleCrash(user, nullptr);
+  brillo::LogToString(false);
+  if (!handled)
+    return 1;
+  return 0;
+}
+
+static int HandleUdevCrash(UdevCollector *udev_collector,
+                           const std::string& udev_event) {
+  // Handle a crash indicated by a udev event.
+  CHECK(!udev_event.empty()) << "--udev= must be set";
+
+  // Accumulate logs to help in diagnosing failures during user collection.
+  brillo::LogToString(true);
+  bool handled = udev_collector->HandleCrash(udev_event);
+  brillo::LogToString(false);
+  if (!handled)
+    return 1;
+  return 0;
+}
+
+static int HandleKernelWarning(KernelWarningCollector
+                               *kernel_warning_collector) {
+  // Accumulate logs to help in diagnosing failures during collection.
+  brillo::LogToString(true);
+  bool handled = kernel_warning_collector->Collect();
+  brillo::LogToString(false);
+  if (!handled)
+    return 1;
+  return 0;
+}
+
+// Interactive/diagnostics mode for generating kernel crash signatures.
+static int GenerateKernelSignature(KernelCollector *kernel_collector,
+                                   const std::string& kernel_signature_file) {
+  std::string kcrash_contents;
+  std::string signature;
+  if (!base::ReadFileToString(FilePath(kernel_signature_file),
+                              &kcrash_contents)) {
+    fprintf(stderr, "Could not read file.\n");
+    return 1;
+  }
+  if (!kernel_collector->ComputeKernelStackSignature(
+          kcrash_contents,
+          &signature,
+          true)) {
+    fprintf(stderr, "Signature could not be generated.\n");
+    return 1;
+  }
+  printf("Kernel crash signature is \"%s\".\n", signature.c_str());
+  return 0;
+}
+
+// Ensure stdout, stdin, and stderr are open file descriptors.  If
+// they are not, any code which writes to stderr/stdout may write out
+// to files opened during execution.  In particular, when
+// crash_reporter is run by the kernel coredump pipe handler (via
+// kthread_create/kernel_execve), it will not have file table entries
+// 1 and 2 (stdout and stderr) populated.  We populate them here.
+static void OpenStandardFileDescriptors() {
+  int new_fd = -1;
+  // We open /dev/null to fill in any of the standard [0, 2] file
+  // descriptors.  We leave these open for the duration of the
+  // process.  This works because open returns the lowest numbered
+  // invalid fd.
+  do {
+    new_fd = open("/dev/null", 0);
+    CHECK_GE(new_fd, 0) << "Unable to open /dev/null";
+  } while (new_fd >= 0 && new_fd <= 2);
+  close(new_fd);
+}
+
+int main(int argc, char *argv[]) {
+  DEFINE_bool(init, false, "Initialize crash logging");
+  DEFINE_bool(clean_shutdown, false, "Signal clean shutdown");
+  DEFINE_string(generate_kernel_signature, "",
+                "Generate signature from given kcrash file");
+  DEFINE_bool(crash_test, false, "Crash test");
+  DEFINE_string(user, "", "User crash info (pid:signal:exec_name)");
+  DEFINE_bool(unclean_check, true, "Check for unclean shutdown");
+  DEFINE_string(udev, "", "Udev event description (type:device:subsystem)");
+  DEFINE_bool(kernel_warning, false, "Report collected kernel warning");
+  DEFINE_string(pid, "", "PID of crashing process");
+  DEFINE_string(uid, "", "UID of crashing process");
+  DEFINE_string(exe, "", "Executable name of crashing process");
+  DEFINE_bool(core2md_failure, false, "Core2md failure test");
+  DEFINE_bool(directory_failure, false, "Spool directory failure test");
+  DEFINE_string(filter_in, "",
+                "Ignore all crashes but this for testing");
+
+  OpenStandardFileDescriptors();
+  FilePath my_path = base::MakeAbsoluteFilePath(FilePath(argv[0]));
+  s_metrics_lib.Init();
+  brillo::FlagHelper::Init(argc, argv, "Chromium OS Crash Reporter");
+  brillo::OpenLog(my_path.BaseName().value().c_str(), true);
+  brillo::InitLog(brillo::kLogToSyslog);
+
+  KernelCollector kernel_collector;
+  kernel_collector.Initialize(CountKernelCrash, IsFeedbackAllowed);
+  UserCollector user_collector;
+  user_collector.Initialize(CountUserCrash,
+                            my_path.value(),
+                            IsFeedbackAllowed,
+                            true,  // generate_diagnostics
+                            FLAGS_core2md_failure,
+                            FLAGS_directory_failure,
+                            FLAGS_filter_in);
+  UncleanShutdownCollector unclean_shutdown_collector;
+  unclean_shutdown_collector.Initialize(CountUncleanShutdown,
+                                        IsFeedbackAllowed);
+  UdevCollector udev_collector;
+  udev_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
+
+  KernelWarningCollector kernel_warning_collector;
+  kernel_warning_collector.Initialize(CountUdevCrash, IsFeedbackAllowed);
+
+  if (FLAGS_init) {
+    return Initialize(&kernel_collector,
+                      &user_collector,
+                      &unclean_shutdown_collector,
+                      FLAGS_unclean_check,
+                      FLAGS_clean_shutdown);
+  }
+
+  if (FLAGS_clean_shutdown) {
+    unclean_shutdown_collector.Disable();
+    user_collector.Disable();
+    return 0;
+  }
+
+  if (!FLAGS_generate_kernel_signature.empty()) {
+    return GenerateKernelSignature(&kernel_collector,
+                                   FLAGS_generate_kernel_signature);
+  }
+
+  if (!FLAGS_udev.empty()) {
+    return HandleUdevCrash(&udev_collector, FLAGS_udev);
+  }
+
+  if (FLAGS_kernel_warning) {
+    return HandleKernelWarning(&kernel_warning_collector);
+  }
+
+  return HandleUserCrash(&user_collector, FLAGS_user, FLAGS_crash_test);
+}
diff --git a/crash_reporter/crash_reporter.rc b/crash_reporter/crash_reporter.rc
new file mode 100644 (file)
index 0000000..57c1d40
--- /dev/null
@@ -0,0 +1,33 @@
+on property:crash_reporter.coredump.enabled=1
+    write /proc/sys/kernel/core_pattern \
+          "|/system/bin/crash_reporter --user=%P:%s:%u:%g:%e"
+
+on property:crash_reporter.coredump.enabled=0
+    write /proc/sys/kernel/core_pattern "core"
+
+on post-fs-data
+    # Allow catching multiple unrelated concurrent crashes, but use a finite
+    # number to prevent infinitely recursing on crash handling.
+    write /proc/sys/kernel/core_pipe_limit 4
+
+    # Remove any previous orphaned locks.
+    rmdir /data/misc/crash_reporter/lock/crash_sender
+
+    # Create crash directories.
+    # These directories are group-writable by root so that crash_reporter can
+    # still access them when it switches users.
+    mkdir /data/misc/crash_reporter 0770 root root
+    mkdir /data/misc/crash_reporter/crash 0770 root root
+    mkdir /data/misc/crash_reporter/lock 0700 root root
+    mkdir /data/misc/crash_reporter/log 0700 root root
+    mkdir /data/misc/crash_reporter/run 0700 root root
+    mkdir /data/misc/crash_reporter/tmp 0770 root root
+
+service crash_reporter /system/bin/crash_reporter --init
+    class late_start
+    oneshot
+
+service crash_sender /system/bin/periodic_scheduler 3600 14400 crash_sender \
+    /system/bin/crash_sender
+    class late_start
+    group system
diff --git a/crash_reporter/crash_reporter_logs.conf b/crash_reporter/crash_reporter_logs.conf
new file mode 100644 (file)
index 0000000..7db308c
--- /dev/null
@@ -0,0 +1,122 @@
+# 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.
+
+# This file is parsed by chromeos::KeyValueStore. It has the format:
+#
+# <basename>=<shell command>\n
+#
+# Commands may be split across multiple lines using trailing backslashes.
+#
+# When an executable named <basename> crashes, the corresponding command is
+# executed and its standard output and standard error are attached to the crash
+# report.
+#
+# Use caution in modifying this file. Only run common Unix commands here, as
+# these commands will be run when a crash has recently occurred and we should
+# avoid running anything that might cause another crash. Similarly, these
+# commands block notification of the crash to parent processes, so commands
+# should execute quickly.
+
+update_engine=cat $(ls -1tr /var/log/update_engine | tail -5 | \
+  sed s.^./var/log/update_engine/.) | tail -c 50000
+
+# The cros_installer output is logged into the update engine log file,
+# so it is handled in the same way as update_engine.
+cros_installer=cat $(ls -1tr /var/log/update_engine | tail -5 | \
+  sed s.^./var/log/update_engine/.) | tail -c 50000
+
+# Dump the last 20 lines of the last two files in Chrome's system and user log
+# directories, along with the last 20 messages from the session manager.
+chrome=\
+  for f in $(ls -1rt /var/log/chrome/chrome_[0-9]* | tail -2) \
+    $(ls -1rt /home/chronos/u-*/log/chrome_[0-9]* 2>/dev/null | tail -2); do \
+    echo "===$f (tail)==="; \
+    tail -20 $f; \
+    echo EOF; \
+    echo; \
+  done; \
+  echo "===session_manager (tail)==="; \
+  awk '$3 ~ "^session_manager\[" { print }' /var/log/messages | tail -20; \
+  echo EOF
+
+# The following rule is used for generating additional diagnostics when
+# collection of user crashes fails.  This output should not be too large
+# as it is stored in memory.  The output format specified for 'ps' is the
+# same as with the "u" ("user-oriented") option, except it doesn't show
+# the commands' arguments (i.e. "comm" instead of "command").
+crash_reporter-user-collection=\
+  echo "===ps output==="; \
+  ps axw -o user,pid,%cpu,%mem,vsz,rss,tname,stat,start_time,bsdtime,comm | \
+    tail -c 25000; \
+  echo "===meminfo==="; \
+  cat /proc/meminfo
+
+# This rule is similar to the crash_reporter-user-collection rule, except it is
+# run for kernel errors reported through udev events.
+crash_reporter-udev-collection-change-card0-drm=\
+  for dri in /sys/kernel/debug/dri/*; do \
+    echo "===$dri/i915_error_state==="; \
+    cat $dri/i915_error_state; \
+  done
+
+# When trackpad driver cyapa detects some abnormal behavior, we collect
+# additional logs from kernel messages.
+crash_reporter-udev-collection-change--i2c-cyapa=\
+  /usr/sbin/kernel_log_collector.sh cyapa 30
+# When trackpad/touchscreen driver atmel_mxt_ts detects some abnormal behavior,
+# we collect additional logs from kernel messages.
+crash_reporter-udev-collection-change--i2c-atmel_mxt_ts=\
+  /usr/sbin/kernel_log_collector.sh atmel 30
+# When touch device noise are detected, we collect relevant logs.
+# (crosbug.com/p/16788)
+crash_reporter-udev-collection---TouchNoise=cat /var/log/touch_noise.log
+# Periodically collect touch event log for debugging (crosbug.com/p/17244)
+crash_reporter-udev-collection---TouchEvent=cat /var/log/touch_event.log
+
+# Collect the last 50 lines of /var/log/messages and /var/log/net.log for
+# intel wifi driver (iwlwifi) for debugging purpose.
+crash_reporter-udev-collection-devcoredump-iwlwifi=\
+  echo "===/var/log/messages==="; \
+  tail -n 50 /var/log/messages; \
+  echo "===/var/log/net.log==="; \
+  tail -n 50 /var/log/net.log; \
+  echo EOF
+
+# Dump the last 50 lines of the last two powerd log files -- if the job has
+# already restarted, we want to see the end of the previous instance's logs.
+powerd=\
+  for f in $(ls -1tr /var/log/power_manager/powerd.[0-9]* | tail -2); do \
+    echo "===$(basename $f) (tail)==="; \
+    tail -50 $f; \
+    echo EOF; \
+  done
+# If power_supply_info aborts (due to e.g. a bad battery), its failure message
+# could end up in various places depending on which process was running it.
+# Attach the end of powerd's log since it might've also logged the underlying
+# problem.
+power_supply_info=\
+  echo "===powerd.LATEST (tail)==="; \
+  tail -50 /var/log/power_manager/powerd.LATEST; \
+  echo EOF
+# powerd_setuid_helper gets run by powerd, so its stdout/stderr will be mixed in
+# with powerd's stdout/stderr.
+powerd_setuid_helper=\
+  echo "===powerd.OUT (tail)==="; \
+  tail -50 /var/log/powerd.out; \
+  echo EOF
+
+# The following rules are only for testing purposes.
+crash_log_test=echo hello world
+crash_log_recursion_test=sleep 1 && \
+  /usr/local/autotest/tests/crash_log_recursion_test
diff --git a/crash_reporter/crash_reporter_logs_test.cc b/crash_reporter/crash_reporter_logs_test.cc
new file mode 100644 (file)
index 0000000..e778002
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <brillo/key_value_store.h>
+#include <gtest/gtest.h>
+
+namespace {
+
+// Name of the checked-in configuration file containing log-collection commands.
+const char kConfigFile[] = "crash_reporter_logs.conf";
+
+// Executable name for Chrome. kConfigFile is expected to contain this entry.
+const char kChromeExecName[] = "chrome";
+
+}  // namespace
+
+// Tests that the config file is parsable and that Chrome is listed.
+TEST(CrashReporterLogsTest, ReadConfig) {
+  brillo::KeyValueStore store;
+  ASSERT_TRUE(store.Load(base::FilePath(kConfigFile)));
+  std::string command;
+  EXPECT_TRUE(store.GetString(kChromeExecName, &command));
+  EXPECT_FALSE(command.empty());
+}
diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender
new file mode 100755 (executable)
index 0000000..95204a4
--- /dev/null
@@ -0,0 +1,706 @@
+#!/system/bin/sh
+
+# 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.
+
+set -e
+
+# Default product ID in crash report (used if GOOGLE_CRASH_* is undefined).
+BRILLO_PRODUCT=Brillo
+
+# Base directory that contains any crash reporter state files.
+CRASH_STATE_DIR="/data/misc/crash_reporter"
+
+# File containing crash_reporter's anonymized guid.
+GUID_FILE="${CRASH_STATE_DIR}/guid"
+
+# Crash sender lock in case the sender is already running.
+CRASH_SENDER_LOCK="${CRASH_STATE_DIR}/lock/crash_sender"
+
+# Path to file that indicates a crash test is currently running.
+CRASH_TEST_IN_PROGRESS_FILE="${CRASH_STATE_DIR}/tmp/crash-test-in-progress"
+
+# Set this to 1 in the environment to allow uploading crash reports
+# for unofficial versions.
+FORCE_OFFICIAL=${FORCE_OFFICIAL:-0}
+
+# Path to hardware class description.
+HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID"
+
+# Path to file that indicates this is a developer image.
+LEAVE_CORE_FILE="${CRASH_STATE_DIR}/.leave_core"
+
+# Path to list_proxies.
+LIST_PROXIES="list_proxies"
+
+# Maximum crashes to send per day.
+MAX_CRASH_RATE=${MAX_CRASH_RATE:-32}
+
+# File whose existence mocks crash sending.  If empty we pretend the
+# crash sending was successful, otherwise unsuccessful.
+MOCK_CRASH_SENDING="${CRASH_STATE_DIR}/tmp/mock-crash-sending"
+
+# Set this to 1 in the environment to pretend to have booted in developer
+# mode.  This is used by autotests.
+MOCK_DEVELOPER_MODE=${MOCK_DEVELOPER_MODE:-0}
+
+# Ignore PAUSE_CRASH_SENDING file if set.
+OVERRIDE_PAUSE_SENDING=${OVERRIDE_PAUSE_SENDING:-0}
+
+# File whose existence causes crash sending to be delayed (for testing).
+# Must be stateful to enable testing kernel crashes.
+PAUSE_CRASH_SENDING="${CRASH_STATE_DIR}/lock/crash_sender_paused"
+
+# Path to a directory of restricted certificates which includes
+# a certificate for the crash server.
+RESTRICTED_CERTIFICATES_PATH="/system/etc/security/cacerts"
+
+# File whose existence implies we're running and not to start again.
+RUN_FILE="${CRASH_STATE_DIR}/run/crash_sender.pid"
+
+# Maximum time to sleep between sends.
+SECONDS_SEND_SPREAD=${SECONDS_SEND_SPREAD:-600}
+
+# Set this to 1 to allow uploading of device coredumps.
+DEVCOREDUMP_UPLOAD_FLAG_FILE="${CRASH_STATE_DIR}/device_coredump_upload_allowed"
+
+# The weave configuration file.
+WEAVE_CONF_FILE="/etc/weaved/weaved.conf"
+
+# The os-release.d folder.
+OSRELEASED_FOLDER="/etc/os-release.d"
+
+# The syslog tag for all logging we emit.
+TAG="$(basename $0)[$$]"
+
+# Directory to store timestamp files indicating the uploads in the past 24
+# hours.
+TIMESTAMPS_DIR="${CRASH_STATE_DIR}/crash_sender"
+
+# Temp directory for this process.
+TMP_DIR=""
+
+# Crash report log file.
+CRASH_LOG="${CRASH_STATE_DIR}/log/uploads.log"
+
+lecho() {
+  log -t "${TAG}" "$@"
+}
+
+lwarn() {
+  lecho -psyslog.warn "$@"
+}
+
+# Returns true if mock is enabled.
+is_mock() {
+  [ -f "${MOCK_CRASH_SENDING}" ] && return 0
+  return 1
+}
+
+is_mock_successful() {
+  local mock_in=$(cat "${MOCK_CRASH_SENDING}")
+  [ "${mock_in}" = "" ] && return 0  # empty file means success
+  return 1
+}
+
+cleanup() {
+  if [ -n "${TMP_DIR}" ]; then
+    rm -rf "${TMP_DIR}"
+  fi
+  rm -f "${RUN_FILE}"
+  if [ -n "${CRASH_SENDER_LOCK}" ]; then
+    rm -rf "${CRASH_SENDER_LOCK}"
+  fi
+  crash_done
+}
+
+crash_done() {
+  if is_mock; then
+    # For testing purposes, emit a message to log so that we
+    # know when the test has received all the messages from this run.
+    lecho "crash_sender done."
+  fi
+}
+
+is_official_image() {
+  [ ${FORCE_OFFICIAL} -ne 0 ] && return 0
+  if [ "$(getprop ro.secure)" = "1" ]; then
+    return 0
+  else
+    return 1
+  fi
+}
+
+# Returns 0 if the a crash test is currently running.  NOTE: Mirrors
+# crash_collector.cc:CrashCollector::IsCrashTestInProgress().
+is_crash_test_in_progress() {
+  [ -f "${CRASH_TEST_IN_PROGRESS_FILE}" ] && return 0
+  return 1
+}
+
+# Returns 0 if we should consider ourselves to be running on a developer
+# image.  NOTE: Mirrors crash_collector.cc:CrashCollector::IsDeveloperImage().
+is_developer_image() {
+  # If we're testing crash reporter itself, we don't want to special-case
+  # for developer images.
+  is_crash_test_in_progress && return 1
+  [ -f "${LEAVE_CORE_FILE}" ] && return 0
+  return 1
+}
+
+# Returns 0 if we should consider ourselves to be running on a test image.
+is_test_image() {
+  # If we're testing crash reporter itself, we don't want to special-case
+  # for test images.
+  is_crash_test_in_progress && return 1
+  case $(get_channel) in
+  test*) return 0;;
+  esac
+  return 1
+}
+
+# Returns 0 if the machine booted up in developer mode.
+is_developer_mode() {
+  [ ${MOCK_DEVELOPER_MODE} -ne 0 ] && return 0
+  # If we're testing crash reporter itself, we don't want to special-case
+  # for developer mode.
+  is_crash_test_in_progress && return 1
+  if [ "$(getprop ro.debuggable)" = "1" ]; then
+    return 0
+  else
+    return 1
+  fi
+}
+
+# Return 0 if the uploading of device coredumps is allowed.
+is_device_coredump_upload_allowed() {
+  [ -f "${DEVCOREDUMP_UPLOAD_FLAG_FILE}" ] && return 0
+  return 1
+}
+
+# Generate a uniform random number in 0..max-1.
+# POSIX arithmetic expansion requires support of at least signed long integers.
+# On 32-bit systems, that may mean 32-bit signed integers, in which case the
+# 32-bit random number read from /dev/urandom may be interpreted as negative
+# when used inside an arithmetic expansion (since the high bit might be set).
+# mksh at least is known to behave this way.
+# For this case, simply take the absolute value, which will still give a
+# roughly uniform random distribution for the modulo (as we are merely ignoring
+# the high/sign bit).
+# See corresponding Arithmetic Expansion and Arithmetic Expression sections:
+# POSIX: http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_04
+# mksh: http://linux.die.net/man/1/mksh
+generate_uniform_random() {
+  local max=$1
+  local random="$(od -An -N4 -tu /dev/urandom)"
+  echo $(((random < 0 ? -random : random) % max))
+}
+
+# Check if sending a crash now does not exceed the maximum 24hr rate and
+# commit to doing so, if not.
+check_rate() {
+  mkdir -p ${TIMESTAMPS_DIR}
+  # Only consider minidumps written in the past 24 hours by removing all older.
+  find "${TIMESTAMPS_DIR}" -mindepth 1 -mtime +1 \
+      -exec rm -- '{}' ';'
+  local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w)
+  lecho "Current send rate: ${sends_in_24hrs}sends/24hrs"
+  if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then
+    lecho "Cannot send more crashes:"
+    lecho "  current ${sends_in_24hrs}send/24hrs >= " \
+          "max ${MAX_CRASH_RATE}send/24hrs"
+    return 1
+  fi
+  mktemp "${TIMESTAMPS_DIR}"/XXXXXX > /dev/null
+  return 0
+}
+
+# Gets the base part of a crash report file, such as name.01234.5678.9012 from
+# name.01234.5678.9012.meta or name.01234.5678.9012.log.tar.xz.  We make sure
+# "name" is sanitized in CrashCollector::Sanitize to not include any periods.
+get_base() {
+  echo "$1" | cut -d. -f-4
+}
+
+get_extension() {
+  local extension="${1##*.}"
+  local filename="${1%.*}"
+  # For gzipped file, we ignore .gz and get the real extension
+  if [ "${extension}" = "gz" ]; then
+    echo "${filename##*.}"
+  else
+    echo "${extension}"
+  fi
+}
+
+# Return which kind of report the given metadata file relates to
+get_kind() {
+  local payload="$(get_key_value "$1" "payload")"
+  if [ ! -r "${payload}" ]; then
+    lecho "Missing payload: ${payload}"
+    echo "undefined"
+    return
+  fi
+  local kind="$(get_extension "${payload}")"
+  if [ "${kind}" = "dmp" ]; then
+    echo "minidump"
+    return
+  fi
+  echo "${kind}"
+}
+
+get_key_value() {
+  local file="$1" key="$2" value
+
+  if [ -f "${file}/${key}" ]; then
+    # Get the value from a folder where each key is its own file.  The key
+    # file's entire contents is the value.
+    value=$(cat "${file}/${key}")
+  elif [ -f "${file}" ]; then
+    # Get the value from a file that has multiple key=value combinations.
+    # Return the first entry.  There shouldn't be more than one anyways.
+    # Substr at length($1) + 2 skips past the key and following = sign (awk
+    # uses 1-based indexes), but preserves embedded = characters.
+    value=$(sed -n "/^${key}[[:space:]]*=/{s:^[^=]*=::p;q}" "${file}")
+  fi
+
+  echo "${value:-undefined}"
+}
+
+get_keys() {
+  local file="$1" regex="$2"
+
+  cut -d '=' -f1 "${file}" | grep --color=never "${regex}"
+}
+
+# Return the channel name (sans "-channel" suffix).
+get_channel() {
+  getprop ro.product.channel | sed 's:-channel$::'
+}
+
+# Return the hardware class or "undefined".
+get_hardware_class() {
+  if [ -r "${HWCLASS_PATH}" ]; then
+    cat "${HWCLASS_PATH}"
+  else
+    echo "undefined"
+  fi
+}
+
+# Return the log string filtered with only JSON-safe white-listed characters.
+filter_log_string() {
+  echo "$1" | tr -cd '[:alnum:]_.\-:;'
+}
+
+send_crash() {
+  local meta_path="$1"
+  local report_payload="$(get_key_value "${meta_path}" "payload")"
+  local kind="$(get_kind "${meta_path}")"
+  local exec_name="$(get_key_value "${meta_path}" "exec_name")"
+  local url="$(get_key_value "${OSRELEASED_FOLDER}" "crash_server")"
+  local bdk_version="$(get_key_value "${meta_path}" "bdk_version")"
+  local hwclass="$(get_hardware_class)"
+  local write_payload_size="$(get_key_value "${meta_path}" "payload_size")"
+  local log="$(get_key_value "${meta_path}" "log")"
+  local sig="$(get_key_value "${meta_path}" "sig")"
+  local send_payload_size="$(stat -c "%s" "${report_payload}" 2>/dev/null)"
+  local product="$(get_key_value "${meta_path}" "product_id")"
+  local version="$(get_key_value "${meta_path}" "product_version")"
+  local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")"
+  local guid
+  local model_manifest_id="$(get_key_value "${WEAVE_CONF_FILE}" "model_id")"
+
+  # If crash_reporter.server is not set return with an error.
+  if [ -z "${url}" ]; then
+    lecho "Configuration error: crash_reporter.server not set."
+    return 1
+  fi
+
+  set -- \
+    -F "write_payload_size=${write_payload_size}" \
+    -F "send_payload_size=${send_payload_size}"
+  if [ "${sig}" != "undefined" ]; then
+    set -- "$@" \
+      -F "sig=${sig}" \
+      -F "sig2=${sig}"
+  fi
+  if [ -r "${report_payload}" ]; then
+    set -- "$@" \
+      -F "upload_file_${kind}=@${report_payload}"
+  fi
+  if [ "${log}" != "undefined" -a -r "${log}" ]; then
+    set -- "$@" \
+      -F "log=@${log}"
+  fi
+
+  if [ "${upload_prefix}" = "undefined" ]; then
+    upload_prefix=""
+  fi
+
+  # Grab any variable that begins with upload_.
+  local v
+  for k in $(get_keys "${meta_path}" "^upload_"); do
+    v="$(get_key_value "${meta_path}" "${k}")"
+    case ${k} in
+      # Product & version are handled separately.
+      upload_var_prod) ;;
+      upload_var_ver) ;;
+      upload_var_*)
+        set -- "$@" -F "${upload_prefix}${k#upload_var_}=${v}"
+        ;;
+      upload_file_*)
+        if [ -r "${v}" ]; then
+          set -- "$@" -F "${upload_prefix}${k#upload_file_}=@${v}"
+        fi
+        ;;
+    esac
+  done
+
+  # If ID or VERSION_ID is undefined, we use the default product name
+  # and bdk_version from /etc/os-release.d.
+  if [ "${product}" = "undefined" ]; then
+    product="${BRILLO_PRODUCT}"
+  fi
+  if [ "${version}" = "undefined" ]; then
+    version="${bdk_version}"
+  fi
+
+  local image_type
+  if is_test_image; then
+    image_type="test"
+  elif is_developer_image; then
+    image_type="dev"
+  elif [ ${FORCE_OFFICIAL} -ne 0 ]; then
+    image_type="force-official"
+  elif is_mock && ! is_mock_successful; then
+    image_type="mock-fail"
+  fi
+
+  local boot_mode
+  if is_developer_mode; then
+    boot_mode="dev"
+  fi
+
+  # Need to strip dashes ourselves as Chrome preserves it in the file
+  # nowadays.  This is also what the Chrome breakpad client does.
+  guid=$(tr -d '-' < "${GUID_FILE}")
+
+  local error_type="$(get_key_value "${meta_path}" "error_type")"
+  [ "${error_type}" = "undefined" ] && error_type=
+
+  lecho "Sending crash:"
+  if [ "${product}" != "${BRILLO_PRODUCT}" ]; then
+    lecho "  Sending crash report on behalf of ${product}"
+  fi
+  lecho "  Metadata: ${meta_path} (${kind})"
+  lecho "  Payload: ${report_payload}"
+  lecho "  Version: ${version}"
+  lecho "  Bdk Version: ${bdk_version}"
+  [ -n "${image_type}" ] && lecho "  Image type: ${image_type}"
+  [ -n "${boot_mode}" ] && lecho "  Boot mode: ${boot_mode}"
+  if is_mock; then
+    lecho "  Product: ${product}"
+    lecho "  URL: ${url}"
+    lecho "  HWClass: ${hwclass}"
+    lecho "  write_payload_size: ${write_payload_size}"
+    lecho "  send_payload_size: ${send_payload_size}"
+    if [ "${log}" != "undefined" ]; then
+      lecho "  log: @${log}"
+    fi
+    if [ "${sig}" != "undefined" ]; then
+      lecho "  sig: ${sig}"
+    fi
+  fi
+  lecho "  Exec name: ${exec_name}"
+  [ -n "${error_type}" ] && lecho "  Error type: ${error_type}"
+  if is_mock; then
+    if ! is_mock_successful; then
+      lecho "Mocking unsuccessful send"
+      return 1
+    fi
+    lecho "Mocking successful send"
+    return 0
+  fi
+
+  # Read in the first proxy, if any, for a given URL.  NOTE: The
+  # double-quotes are necessary due to a bug in dash with the "local"
+  # builtin command and values that have spaces in them (see
+  # "https://bugs.launchpad.net/ubuntu/+source/dash/+bug/139097").
+  if [ -f "${LIST_PROXIES}" ]; then
+    local proxy ret
+    proxy=$("${LIST_PROXIES}" --quiet "${url}")
+    ret=$?
+    if [ ${ret} -ne 0 ]; then
+      proxy=''
+      lwarn "Listing proxies failed with exit code ${ret}"
+    else
+      proxy=$(echo "${proxy}" | head -1)
+    fi
+  fi
+  # if a direct connection should be used, unset the proxy variable.
+  [ "${proxy}" = "direct://" ] && proxy=
+  local report_id="${TMP_DIR}/report_id"
+  local curl_stderr="${TMP_DIR}/curl_stderr"
+
+  set +e
+  curl "${url}" -f -v ${proxy:+--proxy "$proxy"} \
+    --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \
+    -F "prod=${product}" \
+    -F "ver=${version}" \
+    -F "bdk_version=${bdk_version}" \
+    -F "hwclass=${hwclass}" \
+    -F "exec_name=${exec_name}" \
+    -F "model_manifest_id=${model_manifest_id}" \
+    ${image_type:+-F "image_type=${image_type}"} \
+    ${boot_mode:+-F "boot_mode=${boot_mode}"} \
+    ${error_type:+-F "error_type=${error_type}"} \
+    -F "guid=${guid}" \
+    -o "${report_id}" \
+    "$@" \
+    2>"${curl_stderr}"
+  curl_result=$?
+  set -e
+
+  if [ ${curl_result} -eq 0 ]; then
+    local id="$(cat "${report_id}")"
+    local timestamp="$(date +%s)"
+    local filter_prod="$(filter_log_string "${product}")"
+    local filter_exec="$(filter_log_string "${exec_name}")"
+    if [ "${filter_prod}" != "${product}" ]; then
+      lwarn "Product name filtered to: ${filter_prod}."
+    fi
+    if [ "${filter_exec}" != "${exec_name}" ]; then
+      lwarn "Exec name filtered to: ${filter_exec}."
+    fi
+    printf "{'time':%s,'id':'%s','product':'%s','exec_name':'%s'}\n" \
+      "${timestamp}" "${id}" "${filter_prod}" "${filter_exec}" >> "${CRASH_LOG}"
+    lecho "Crash report receipt ID ${id}"
+  else
+    lecho "Crash sending failed with exit code ${curl_result}: " \
+      "$(cat "${curl_stderr}")"
+  fi
+
+  rm -f "${report_id}"
+
+  return ${curl_result}
+}
+
+# *.meta files always end with done=1 so we can tell if they are complete.
+is_complete_metadata() {
+  grep -q "done=1" "$1"
+}
+
+# Remove the given report path.
+remove_report() {
+  local base="${1%.*}"
+  rm -f -- "${base}".*
+}
+
+# Send all crashes from the given directory.  This applies even when we're on a
+# 3G connection (see crosbug.com/3304 for discussion).
+send_crashes() {
+  local dir="$1"
+  lecho "Sending crashes for ${dir}"
+
+  if [ ! -d "${dir}" ]; then
+    return
+  fi
+
+  # Consider any old files which still have no corresponding meta file
+  # as orphaned, and remove them.
+  for old_file in $(find "${dir}" -mindepth 1 \
+                    -mtime +1 -type f); do
+    if [ ! -e "$(get_base "${old_file}").meta" ]; then
+      lecho "Removing old orphaned file: ${old_file}."
+      rm -f -- "${old_file}"
+    fi
+  done
+
+  # Look through all metadata (*.meta) files, oldest first.  That way, the rate
+  # limit does not stall old crashes if there's a high amount of new crashes
+  # coming in.
+  # For each crash report, first evaluate conditions that might lead to its
+  # removal to honor user choice and to free disk space as soon as possible,
+  # then decide whether it should be sent right now or kept for later sending.
+  for meta_path in $(ls -1tr "${dir}"/*.meta 2>/dev/null); do
+    lecho "Considering metadata ${meta_path}."
+
+    local kind=$(get_kind "${meta_path}")
+    if [ "${kind}" != "minidump" ] && \
+       [ "${kind}" != "kcrash" ] && \
+       [ "${kind}" != "log" ] &&
+       [ "${kind}" != "devcore" ]; then
+      lecho "Unknown report kind ${kind}.  Removing report."
+      remove_report "${meta_path}"
+      continue
+    fi
+
+    if ! is_complete_metadata "${meta_path}"; then
+      # This report is incomplete, so if it's old, just remove it.
+      local old_meta=$(find "${dir}" -mindepth 1 -name \
+        $(basename "${meta_path}") -mtime +1 -type f)
+      if [ -n "${old_meta}" ]; then
+        lecho "Removing old incomplete metadata."
+        remove_report "${meta_path}"
+      else
+        lecho "Ignoring recent incomplete metadata."
+      fi
+      continue
+    fi
+
+    # Ignore device coredump if device coredump uploading is not allowed.
+    if [ "${kind}" = "devcore" ] && ! is_device_coredump_upload_allowed; then
+      lecho "Ignoring device coredump. Device coredump upload not allowed."
+      continue
+    fi
+
+    if ! is_mock && ! is_official_image; then
+      lecho "Not an official OS version.  Removing crash."
+      remove_report "${meta_path}"
+      continue
+    fi
+
+    # Remove existing crashes in case user consent has not (yet) been given or
+    # has been revoked.  This must come after the guest mode check because
+    # metrics_client always returns "not consented" in guest mode.
+    if ! metrics_client -c; then
+      lecho "Crash reporting is disabled.  Removing crash."
+      remove_report "${meta_path}"
+      continue
+    fi
+
+    # Skip report if the upload rate is exceeded.  (Don't exit right now because
+    # subsequent reports may be candidates for deletion.)
+    if ! check_rate; then
+      lecho "Sending ${meta_path} would exceed rate.  Leaving for later."
+      continue
+    fi
+
+    # The .meta file should be written *after* all to-be-uploaded files that it
+    # references.  Nevertheless, as a safeguard, a hold-off time of thirty
+    # seconds after writing the .meta file is ensured.  Also, sending of crash
+    # reports is spread out randomly by up to SECONDS_SEND_SPREAD.  Thus, for
+    # the sleep call the greater of the two delays is used.
+    local now=$(date +%s)
+    local holdoff_time=$(($(stat -c "%Y" "${meta_path}") + 30 - ${now}))
+    local spread_time=$(generate_uniform_random "${SECONDS_SEND_SPREAD}")
+    local sleep_time
+    if [ ${spread_time} -gt ${holdoff_time} ]; then
+      sleep_time="${spread_time}"
+    else
+      sleep_time="${holdoff_time}"
+    fi
+    lecho "Scheduled to send in ${sleep_time}s."
+    if ! is_mock; then
+      if ! sleep "${sleep_time}"; then
+          lecho "Sleep failed"
+          return 1
+      fi
+    fi
+
+    # Try to upload.
+    if ! send_crash "${meta_path}"; then
+      lecho "Problem sending ${meta_path}, not removing."
+      continue
+    fi
+
+    # Send was successful, now remove.
+    lecho "Successfully sent crash ${meta_path} and removing."
+    remove_report "${meta_path}"
+  done
+}
+
+usage() {
+  cat <<EOF
+Usage: crash_sender [options]
+
+Options:
+ -e <var>=<val>     Set env |var| to |val| (only some vars)
+EOF
+  exit ${1:-1}
+}
+
+parseargs() {
+  # Parse the command line arguments.
+  while [ $# -gt 0 ]; do
+    case $1 in
+    -e)
+      shift
+      case $1 in
+      FORCE_OFFICIAL=*|\
+      MAX_CRASH_RATE=*|\
+      MOCK_DEVELOPER_MODE=*|\
+      OVERRIDE_PAUSE_SENDING=*|\
+      SECONDS_SEND_SPREAD=*)
+        export "$1"
+        ;;
+      *)
+        lecho "Unknown var passed to -e: $1"
+        exit 1
+        ;;
+      esac
+      ;;
+    -h)
+      usage 0
+      ;;
+    *)
+      lecho "Unknown options: $*"
+      exit 1
+      ;;
+    esac
+    shift
+  done
+}
+
+main() {
+  parseargs "$@"
+
+  if [ -e "${PAUSE_CRASH_SENDING}" ] && \
+     [ ${OVERRIDE_PAUSE_SENDING} -eq 0 ]; then
+    lecho "Exiting early due to ${PAUSE_CRASH_SENDING}."
+    exit 1
+  fi
+
+  if is_test_image; then
+    lecho "Exiting early due to test image."
+    exit 1
+  fi
+
+  # We don't perform checks on this because we have a master lock with the
+  # CRASH_SENDER_LOCK file.  This pid file is for the system to keep track
+  # (like with autotests) that we're still running.
+  echo $$ > "${RUN_FILE}"
+
+  for dependency in "${RESTRICTED_CERTIFICATES_PATH}"; do
+    if [ ! -x "${dependency}" ]; then
+      lecho "Fatal: Crash sending disabled: ${dependency} not found."
+      exit 1
+    fi
+  done
+
+  TMP_DIR="$(mktemp -d "${CRASH_STATE_DIR}/tmp/crash_sender.XXXXXX")"
+
+  # Send system-wide crashes
+  send_crashes "${CRASH_STATE_DIR}/crash"
+}
+
+trap cleanup EXIT INT TERM
+
+#TODO(http://b/23937249): Change the locking logic back to using flock.
+if ! mkdir "${CRASH_SENDER_LOCK}" 2>/dev/null; then
+  lecho "Already running; quitting."
+  crash_done
+  exit 1
+fi
+main "$@"
diff --git a/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml b/crash_reporter/dbus_bindings/org.chromium.LibCrosService.xml
new file mode 100644 (file)
index 0000000..64b8b84
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/org/chromium/LibCrosService"
+      xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.LibCrosServiceInterface">
+    <method name="ResolveNetworkProxy">
+      <arg name="source_url" type="s" direction="in"/>
+      <arg name="signal_interface" type="s" direction="in"/>
+      <arg name="signal_name" type="s" direction="in"/>
+      <annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
+    </method>
+  </interface>
+  <interface name="org.chromium.CrashReporterLibcrosProxyResolvedInterface">
+    <signal name="ProxyResolved">
+      <arg name="source_url" type="s" direction="out"/>
+      <arg name="proxy_info" type="s" direction="out"/>
+      <arg name="error_message" type="s" direction="out"/>
+    </signal>
+  </interface>
+</node>
diff --git a/crash_reporter/init/crash-reporter.conf b/crash_reporter/init/crash-reporter.conf
new file mode 100644 (file)
index 0000000..19f2cdb
--- /dev/null
@@ -0,0 +1,27 @@
+# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description     "Initialize crash reporting services"
+author          "chromium-os-dev@chromium.org"
+
+# This job merely initializes its service and then terminates; the
+# actual checking and reporting of crash dumps is triggered by an
+# hourly cron job.
+start on starting system-services
+
+pre-start script
+  mkdir -p /var/spool
+
+  # Only allow device coredumps on a "developer system".
+  if ! is_developer_end_user; then
+    # consumer end-user - disable device coredumps, if driver exists.
+    echo 1 > /sys/class/devcoredump/disabled || true
+  fi
+end script
+
+# crash_reporter uses argv[0] as part of the command line for
+# /proc/sys/kernel/core_pattern.  That command line is invoked by
+# the kernel, and can't rely on PATH, so argv[0] must be a full
+# path; we invoke it as such here.
+exec /sbin/crash_reporter --init
diff --git a/crash_reporter/init/crash-sender.conf b/crash_reporter/init/crash-sender.conf
new file mode 100644 (file)
index 0000000..892186f
--- /dev/null
@@ -0,0 +1,11 @@
+# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description     "Run the crash sender periodically"
+author          "chromium-os-dev@chromium.org"
+
+start on starting system-services
+stop on stopping system-services
+
+exec periodic_scheduler 3600 14400 crash_sender /sbin/crash_sender
diff --git a/crash_reporter/init/warn-collector.conf b/crash_reporter/init/warn-collector.conf
new file mode 100644 (file)
index 0000000..3be80da
--- /dev/null
@@ -0,0 +1,12 @@
+# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+description "Runs a daemon which collects and reports kernel warnings"
+author      "chromium-os-dev@chromium.org"
+
+start on started system-services
+stop on stopping system-services
+respawn
+
+exec warn_collector
diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc
new file mode 100644 (file)
index 0000000..12b00b9
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+ * 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 "kernel_collector.h"
+
+#include <map>
+#include <sys/stat.h>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+using base::FilePath;
+using base::StringPrintf;
+
+namespace {
+
+const char kDefaultKernelStackSignature[] = "kernel-UnspecifiedStackSignature";
+const char kDumpParentPath[] = "/dev";
+const char kDumpPath[] = "/dev/pstore";
+const char kDumpFormat[] = "dmesg-ramoops-%zu";
+const char kKernelExecName[] = "kernel";
+// Maximum number of records to examine in the kDumpPath.
+const size_t kMaxDumpRecords = 100;
+const pid_t kKernelPid = 0;
+const char kKernelSignatureKey[] = "sig";
+// Byte length of maximum human readable portion of a kernel crash signature.
+const int kMaxHumanStringLength = 40;
+const uid_t kRootUid = 0;
+// Time in seconds from the final kernel log message for a call stack
+// to count towards the signature of the kcrash.
+const int kSignatureTimestampWindow = 2;
+// Kernel log timestamp regular expression.
+const char kTimestampRegex[] = "^<.*>\\[\\s*(\\d+\\.\\d+)\\]";
+
+//
+// These regular expressions enable to us capture the PC in a backtrace.
+// The backtrace is obtained through dmesg or the kernel's preserved/kcrashmem
+// feature.
+//
+// For ARM we see:
+//   "<5>[   39.458982] PC is at write_breakme+0xd0/0x1b4"
+// For MIPS we see:
+//   "<5>[ 3378.552000] epc   : 804010f0 lkdtm_do_action+0x68/0x3f8"
+// For x86:
+//   "<0>[   37.474699] EIP: [<790ed488>] write_breakme+0x80/0x108
+//    SS:ESP 0068:e9dd3efc"
+//
+const char* const kPCRegex[] = {
+  0,
+  " PC is at ([^\\+ ]+).*",
+  " epc\\s+:\\s+\\S+\\s+([^\\+ ]+).*",  // MIPS has an exception program counter
+  " EIP: \\[<.*>\\] ([^\\+ ]+).*",  // X86 uses EIP for the program counter
+  " RIP  \\[<.*>\\] ([^\\+ ]+).*",  // X86_64 uses RIP for the program counter
+};
+
+COMPILE_ASSERT(arraysize(kPCRegex) == KernelCollector::kArchCount,
+               missing_arch_pc_regexp);
+
+}  // namespace
+
+KernelCollector::KernelCollector()
+    : is_enabled_(false),
+      ramoops_dump_path_(kDumpPath),
+      records_(0),
+      // We expect crash dumps in the format of architecture we are built for.
+      arch_(GetCompilerArch()) {
+}
+
+KernelCollector::~KernelCollector() {
+}
+
+void KernelCollector::OverridePreservedDumpPath(const FilePath &file_path) {
+  ramoops_dump_path_ = file_path;
+}
+
+bool KernelCollector::ReadRecordToString(std::string *contents,
+                                         size_t current_record,
+                                         bool *record_found) {
+  // A record is a ramoops dump. It has an associated size of "record_size".
+  std::string record;
+  std::string captured;
+
+  // Ramoops appends a header to a crash which contains ==== followed by a
+  // timestamp. Ignore the header.
+  pcrecpp::RE record_re(
+      "====\\d+\\.\\d+\n(.*)",
+      pcrecpp::RE_Options().set_multiline(true).set_dotall(true));
+
+  pcrecpp::RE sanity_check_re("\n<\\d+>\\[\\s*(\\d+\\.\\d+)\\]");
+
+  FilePath ramoops_record;
+  GetRamoopsRecordPath(&ramoops_record, current_record);
+  if (!base::ReadFileToString(ramoops_record, &record)) {
+    LOG(ERROR) << "Unable to open " << ramoops_record.value();
+    return false;
+  }
+
+  *record_found = false;
+  if (record_re.FullMatch(record, &captured)) {
+    // Found a ramoops header, so strip the header and append the rest.
+    contents->append(captured);
+    *record_found = true;
+  } else if (sanity_check_re.PartialMatch(record.substr(0, 1024))) {
+    // pstore compression has been added since kernel 3.12. In order to
+    // decompress dmesg correctly, ramoops driver has to strip the header
+    // before handing over the record to the pstore driver, so we don't
+    // need to do it here anymore. However, the sanity check is needed because
+    // sometimes a pstore record is just a chunk of uninitialized memory which
+    // is not the result of a kernel crash. See crbug.com/443764
+    contents->append(record);
+    *record_found = true;
+  } else {
+    LOG(WARNING) << "Found invalid record at " << ramoops_record.value();
+  }
+
+  // Remove the record from pstore after it's found.
+  if (*record_found)
+    base::DeleteFile(ramoops_record, false);
+
+  return true;
+}
+
+void KernelCollector::GetRamoopsRecordPath(FilePath *path,
+                                           size_t record) {
+  // Disable error "format not a string literal, argument types not checked"
+  // because this is valid, but GNU apparently doesn't bother checking a const
+  // format string.
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wformat-nonliteral"
+  *path = ramoops_dump_path_.Append(StringPrintf(kDumpFormat, record));
+  #pragma GCC diagnostic pop
+}
+
+bool KernelCollector::LoadParameters() {
+  // Discover how many ramoops records are being exported by the driver.
+  size_t count;
+
+  for (count = 0; count < kMaxDumpRecords; ++count) {
+    FilePath ramoops_record;
+    GetRamoopsRecordPath(&ramoops_record, count);
+
+    if (!base::PathExists(ramoops_record))
+      break;
+  }
+
+  records_ = count;
+  return (records_ > 0);
+}
+
+bool KernelCollector::LoadPreservedDump(std::string *contents) {
+  // Load dumps from the preserved memory and save them in contents.
+  // Since the system is set to restart on oops we won't actually ever have
+  // multiple records (only 0 or 1), but check in case we don't restart on
+  // oops in the future.
+  bool any_records_found = false;
+  bool record_found = false;
+  // clear contents since ReadFileToString actually appends to the string.
+  contents->clear();
+
+  for (size_t i = 0; i < records_; ++i) {
+    if (!ReadRecordToString(contents, i, &record_found)) {
+      break;
+    }
+    if (record_found) {
+      any_records_found = true;
+    }
+  }
+
+  if (!any_records_found) {
+    LOG(ERROR) << "No valid records found in " << ramoops_dump_path_.value();
+    return false;
+  }
+
+  return true;
+}
+
+void KernelCollector::StripSensitiveData(std::string *kernel_dump) {
+  // Strip any data that the user might not want sent up to the crash servers.
+  // We'll read in from kernel_dump and also place our output there.
+  //
+  // At the moment, the only sensitive data we strip is MAC addresses.
+
+  // Get rid of things that look like MAC addresses, since they could possibly
+  // give information about where someone has been.  This is strings that look
+  // like this: 11:22:33:44:55:66
+  // Complications:
+  // - Within a given kernel_dump, want to be able to tell when the same MAC
+  //   was used more than once.  Thus, we'll consistently replace the first
+  //   MAC found with 00:00:00:00:00:01, the second with ...:02, etc.
+  // - ACPI commands look like MAC addresses.  We'll specifically avoid getting
+  //   rid of those.
+  std::ostringstream result;
+  std::string pre_mac_str;
+  std::string mac_str;
+  std::map<std::string, std::string> mac_map;
+  pcrecpp::StringPiece input(*kernel_dump);
+
+  // This RE will find the next MAC address and can return us the data preceding
+  // the MAC and the MAC itself.
+  pcrecpp::RE mac_re("(.*?)("
+                     "[0-9a-fA-F][0-9a-fA-F]:"
+                     "[0-9a-fA-F][0-9a-fA-F]:"
+                     "[0-9a-fA-F][0-9a-fA-F]:"
+                     "[0-9a-fA-F][0-9a-fA-F]:"
+                     "[0-9a-fA-F][0-9a-fA-F]:"
+                     "[0-9a-fA-F][0-9a-fA-F])",
+                     pcrecpp::RE_Options()
+                       .set_multiline(true)
+                       .set_dotall(true));
+
+  // This RE will identify when the 'pre_mac_str' shows that the MAC address
+  // was really an ACPI cmd.  The full string looks like this:
+  //   ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES) filtered out
+  pcrecpp::RE acpi_re("ACPI cmd ef/$",
+                      pcrecpp::RE_Options()
+                        .set_multiline(true)
+                        .set_dotall(true));
+
+  // Keep consuming, building up a result string as we go.
+  while (mac_re.Consume(&input, &pre_mac_str, &mac_str)) {
+    if (acpi_re.PartialMatch(pre_mac_str)) {
+      // We really saw an ACPI command; add to result w/ no stripping.
+      result << pre_mac_str << mac_str;
+    } else {
+      // Found a MAC address; look up in our hash for the mapping.
+      std::string replacement_mac = mac_map[mac_str];
+      if (replacement_mac == "") {
+        // It wasn't present, so build up a replacement string.
+        int mac_id = mac_map.size();
+
+        // Handle up to 2^32 unique MAC address; overkill, but doesn't hurt.
+        replacement_mac = StringPrintf("00:00:%02x:%02x:%02x:%02x",
+                                       (mac_id & 0xff000000) >> 24,
+                                       (mac_id & 0x00ff0000) >> 16,
+                                       (mac_id & 0x0000ff00) >> 8,
+                                       (mac_id & 0x000000ff));
+        mac_map[mac_str] = replacement_mac;
+      }
+
+      // Dump the string before the MAC and the fake MAC address into result.
+      result << pre_mac_str << replacement_mac;
+    }
+  }
+
+  // One last bit of data might still be in the input.
+  result << input;
+
+  // We'll just assign right back to kernel_dump.
+  *kernel_dump = result.str();
+}
+
+bool KernelCollector::DumpDirMounted() {
+  struct stat st_parent;
+  if (stat(kDumpParentPath, &st_parent)) {
+    PLOG(WARNING) << "Could not stat " << kDumpParentPath;
+    return false;
+  }
+
+  struct stat st_dump;
+  if (stat(kDumpPath, &st_dump)) {
+    PLOG(WARNING) << "Could not stat " << kDumpPath;
+    return false;
+  }
+
+  if (st_parent.st_dev == st_dump.st_dev) {
+    LOG(WARNING) << "Dump dir " << kDumpPath << " not mounted";
+    return false;
+  }
+
+  return true;
+}
+
+bool KernelCollector::Enable() {
+  if (arch_ == kArchUnknown || arch_ >= kArchCount ||
+      kPCRegex[arch_] == nullptr) {
+    LOG(WARNING) << "KernelCollector does not understand this architecture";
+    return false;
+  }
+
+  if (!DumpDirMounted()) {
+    LOG(WARNING) << "Kernel does not support crash dumping";
+    return false;
+  }
+
+  // To enable crashes, we will eventually need to set
+  // the chnv bit in BIOS, but it does not yet work.
+  LOG(INFO) << "Enabling kernel crash handling";
+  is_enabled_ = true;
+  return true;
+}
+
+// Hash a string to a number.  We define our own hash function to not
+// be dependent on a C++ library that might change.  This function
+// uses basically the same approach as tr1/functional_hash.h but with
+// a larger prime number (16127 vs 131).
+static unsigned HashString(const std::string &input) {
+  unsigned hash = 0;
+  for (size_t i = 0; i < input.length(); ++i)
+    hash = hash * 16127 + input[i];
+  return hash;
+}
+
+void KernelCollector::ProcessStackTrace(
+    pcrecpp::StringPiece kernel_dump,
+    bool print_diagnostics,
+    unsigned *hash,
+    float *last_stack_timestamp,
+    bool *is_watchdog_crash) {
+  pcrecpp::RE line_re("(.+)", pcrecpp::MULTILINE());
+  pcrecpp::RE stack_trace_start_re(std::string(kTimestampRegex) +
+        " (Call Trace|Backtrace):$");
+
+  // Match lines such as the following and grab out "function_name".
+  // The ? may or may not be present.
+  //
+  // For ARM:
+  // <4>[ 3498.731164] [<c0057220>] ? (function_name+0x20/0x2c) from
+  // [<c018062c>] (foo_bar+0xdc/0x1bc)
+  //
+  // For MIPS:
+  // <5>[ 3378.656000] [<804010f0>] lkdtm_do_action+0x68/0x3f8
+  //
+  // For X86:
+  // <4>[ 6066.849504]  [<7937bcee>] ? function_name+0x66/0x6c
+  //
+  pcrecpp::RE stack_entry_re(std::string(kTimestampRegex) +
+    "\\s+\\[<[[:xdigit:]]+>\\]"      // Matches "  [<7937bcee>]"
+    "([\\s\\?(]+)"                   // Matches " ? (" (ARM) or " ? " (X86)
+    "([^\\+ )]+)");                  // Matches until delimiter reached
+  std::string line;
+  std::string hashable;
+  std::string previous_hashable;
+  bool is_watchdog = false;
+
+  *hash = 0;
+  *last_stack_timestamp = 0;
+
+  // Find the last and second-to-last stack traces.  The latter is used when
+  // the panic is from a watchdog timeout.
+  while (line_re.FindAndConsume(&kernel_dump, &line)) {
+    std::string certainty;
+    std::string function_name;
+    if (stack_trace_start_re.PartialMatch(line, last_stack_timestamp)) {
+      if (print_diagnostics) {
+        printf("Stack trace starting.%s\n",
+               hashable.empty() ? "" : "  Saving prior trace.");
+      }
+      previous_hashable = hashable;
+      hashable.clear();
+      is_watchdog = false;
+    } else if (stack_entry_re.PartialMatch(line,
+                                           last_stack_timestamp,
+                                           &certainty,
+                                           &function_name)) {
+      bool is_certain = certainty.find('?') == std::string::npos;
+      if (print_diagnostics) {
+        printf("@%f: stack entry for %s (%s)\n",
+               *last_stack_timestamp,
+               function_name.c_str(),
+               is_certain ? "certain" : "uncertain");
+      }
+      // Do not include any uncertain (prefixed by '?') frames in our hash.
+      if (!is_certain)
+        continue;
+      if (!hashable.empty())
+        hashable.append("|");
+      if (function_name == "watchdog_timer_fn" ||
+          function_name == "watchdog") {
+        is_watchdog = true;
+      }
+      hashable.append(function_name);
+    }
+  }
+
+  // If the last stack trace contains a watchdog function we assume the panic
+  // is from the watchdog timer, and we hash the previous stack trace rather
+  // than the last one, assuming that the previous stack is that of the hung
+  // thread.
+  //
+  // In addition, if the hashable is empty (meaning all frames are uncertain,
+  // for whatever reason) also use the previous frame, as it cannot be any
+  // worse.
+  if (is_watchdog || hashable.empty()) {
+    hashable = previous_hashable;
+  }
+
+  *hash = HashString(hashable);
+  *is_watchdog_crash = is_watchdog;
+
+  if (print_diagnostics) {
+    printf("Hash based on stack trace: \"%s\" at %f.\n",
+           hashable.c_str(), *last_stack_timestamp);
+  }
+}
+
+// static
+KernelCollector::ArchKind KernelCollector::GetCompilerArch() {
+#if defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY)
+  return kArchArm;
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS_FAMILY)
+  return kArchMips;
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_64)
+  return kArchX86_64;
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
+  return kArchX86;
+#else
+  return kArchUnknown;
+#endif
+}
+
+bool KernelCollector::FindCrashingFunction(
+  pcrecpp::StringPiece kernel_dump,
+  bool print_diagnostics,
+  float stack_trace_timestamp,
+  std::string *crashing_function) {
+  float timestamp = 0;
+
+  // Use the correct regex for this architecture.
+  pcrecpp::RE eip_re(std::string(kTimestampRegex) + kPCRegex[arch_],
+                     pcrecpp::MULTILINE());
+
+  while (eip_re.FindAndConsume(&kernel_dump, &timestamp, crashing_function)) {
+    if (print_diagnostics) {
+      printf("@%f: found crashing function %s\n",
+             timestamp,
+             crashing_function->c_str());
+    }
+  }
+  if (timestamp == 0) {
+    if (print_diagnostics) {
+      printf("Found no crashing function.\n");
+    }
+    return false;
+  }
+  if (stack_trace_timestamp != 0 &&
+      abs(static_cast<int>(stack_trace_timestamp - timestamp))
+        > kSignatureTimestampWindow) {
+    if (print_diagnostics) {
+      printf("Found crashing function but not within window.\n");
+    }
+    return false;
+  }
+  if (print_diagnostics) {
+    printf("Found crashing function %s\n", crashing_function->c_str());
+  }
+  return true;
+}
+
+bool KernelCollector::FindPanicMessage(pcrecpp::StringPiece kernel_dump,
+                                       bool print_diagnostics,
+                                       std::string *panic_message) {
+  // Match lines such as the following and grab out "Fatal exception"
+  // <0>[  342.841135] Kernel panic - not syncing: Fatal exception
+  pcrecpp::RE kernel_panic_re(std::string(kTimestampRegex) +
+                              " Kernel panic[^\\:]*\\:\\s*(.*)",
+                              pcrecpp::MULTILINE());
+  float timestamp = 0;
+  while (kernel_panic_re.FindAndConsume(&kernel_dump,
+                                        &timestamp,
+                                        panic_message)) {
+    if (print_diagnostics) {
+      printf("@%f: panic message %s\n",
+             timestamp,
+             panic_message->c_str());
+    }
+  }
+  if (timestamp == 0) {
+    if (print_diagnostics) {
+      printf("Found no panic message.\n");
+    }
+    return false;
+  }
+  return true;
+}
+
+bool KernelCollector::ComputeKernelStackSignature(
+    const std::string &kernel_dump,
+    std::string *kernel_signature,
+    bool print_diagnostics) {
+  unsigned stack_hash = 0;
+  float last_stack_timestamp = 0;
+  std::string human_string;
+  bool is_watchdog_crash;
+
+  ProcessStackTrace(kernel_dump,
+                    print_diagnostics,
+                    &stack_hash,
+                    &last_stack_timestamp,
+                    &is_watchdog_crash);
+
+  if (!FindCrashingFunction(kernel_dump,
+                            print_diagnostics,
+                            last_stack_timestamp,
+                            &human_string)) {
+    if (!FindPanicMessage(kernel_dump, print_diagnostics, &human_string)) {
+      if (print_diagnostics) {
+        printf("Found no human readable string, using empty string.\n");
+      }
+      human_string.clear();
+    }
+  }
+
+  if (human_string.empty() && stack_hash == 0) {
+    if (print_diagnostics) {
+      printf("Found neither a stack nor a human readable string, failing.\n");
+    }
+    return false;
+  }
+
+  human_string = human_string.substr(0, kMaxHumanStringLength);
+  *kernel_signature = StringPrintf("%s-%s%s-%08X",
+                                   kKernelExecName,
+                                   (is_watchdog_crash ? "(HANG)-" : ""),
+                                   human_string.c_str(),
+                                   stack_hash);
+  return true;
+}
+
+bool KernelCollector::Collect() {
+  std::string kernel_dump;
+  FilePath root_crash_directory;
+
+  if (!LoadParameters()) {
+    return false;
+  }
+  if (!LoadPreservedDump(&kernel_dump)) {
+    return false;
+  }
+  StripSensitiveData(&kernel_dump);
+  if (kernel_dump.empty()) {
+    return false;
+  }
+  std::string signature;
+  if (!ComputeKernelStackSignature(kernel_dump, &signature, false)) {
+    signature = kDefaultKernelStackSignature;
+  }
+
+  std::string reason = "handling";
+  bool feedback = true;
+  if (IsDeveloperImage()) {
+    reason = "developer build - always dumping";
+    feedback = true;
+  } else if (!is_feedback_allowed_function_()) {
+    reason = "ignoring - no consent";
+    feedback = false;
+  }
+
+  LOG(INFO) << "Received prior crash notification from "
+            << "kernel (signature " << signature << ") (" << reason << ")";
+
+  if (feedback) {
+    count_crash_function_();
+
+    if (!GetCreatedCrashDirectoryByEuid(kRootUid,
+                                        &root_crash_directory,
+                                        nullptr)) {
+      return true;
+    }
+
+    std::string dump_basename =
+        FormatDumpBasename(kKernelExecName, time(nullptr), kKernelPid);
+    FilePath kernel_crash_path = root_crash_directory.Append(
+        StringPrintf("%s.kcrash", dump_basename.c_str()));
+
+    // We must use WriteNewFile instead of base::WriteFile as we
+    // do not want to write with root access to a symlink that an attacker
+    // might have created.
+    if (WriteNewFile(kernel_crash_path,
+                     kernel_dump.data(),
+                     kernel_dump.length()) !=
+        static_cast<int>(kernel_dump.length())) {
+      LOG(INFO) << "Failed to write kernel dump to "
+                << kernel_crash_path.value().c_str();
+      return true;
+    }
+
+    AddCrashMetaData(kKernelSignatureKey, signature);
+    WriteCrashMetaData(
+        root_crash_directory.Append(
+            StringPrintf("%s.meta", dump_basename.c_str())),
+        kKernelExecName,
+        kernel_crash_path.value());
+
+    LOG(INFO) << "Stored kcrash to " << kernel_crash_path.value();
+  }
+
+  return true;
+}
diff --git a/crash_reporter/kernel_collector.h b/crash_reporter/kernel_collector.h
new file mode 100644 (file)
index 0000000..206ee26
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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 CRASH_REPORTER_KERNEL_COLLECTOR_H_
+#define CRASH_REPORTER_KERNEL_COLLECTOR_H_
+
+#include <pcrecpp.h>
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "crash_collector.h"
+
+// Kernel crash collector.
+class KernelCollector : public CrashCollector {
+ public:
+  // Enumeration to specify architecture type.
+  enum ArchKind {
+    kArchUnknown,
+    kArchArm,
+    kArchMips,
+    kArchX86,
+    kArchX86_64,
+
+    kArchCount  // Number of architectures.
+  };
+
+  KernelCollector();
+
+  ~KernelCollector() override;
+
+  void OverridePreservedDumpPath(const base::FilePath &file_path);
+
+  // Enable collection.
+  bool Enable();
+
+  // Returns true if the kernel collection currently enabled.
+  bool is_enabled() const { return is_enabled_; }
+
+  // Collect any preserved kernel crash dump. Returns true if there was
+  // a dump (even if there were problems storing the dump), false otherwise.
+  bool Collect();
+
+  // Compute a stack signature string from a kernel dump.
+  bool ComputeKernelStackSignature(const std::string &kernel_dump,
+                                   std::string *kernel_signature,
+                                   bool print_diagnostics);
+
+  // Set the architecture of the crash dumps we are looking at.
+  void set_arch(ArchKind arch) { arch_ = arch; }
+  ArchKind arch() const { return arch_; }
+
+ private:
+  friend class KernelCollectorTest;
+  FRIEND_TEST(KernelCollectorTest, LoadPreservedDump);
+  FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBasic);
+  FRIEND_TEST(KernelCollectorTest, StripSensitiveDataBulk);
+  FRIEND_TEST(KernelCollectorTest, StripSensitiveDataSample);
+  FRIEND_TEST(KernelCollectorTest, CollectOK);
+
+  virtual bool DumpDirMounted();
+
+  bool LoadPreservedDump(std::string *contents);
+  void StripSensitiveData(std::string *kernel_dump);
+
+  void GetRamoopsRecordPath(base::FilePath *path, size_t record);
+  bool LoadParameters();
+  bool HasMoreRecords();
+
+  // Read a record to string, modified from file_utils since that didn't
+  // provide a way to restrict the read length.
+  // Return value indicates (only) error state:
+  //  * false when we get an error (can't read from dump location).
+  //  * true if no error occured.
+  // Not finding a valid record is not an error state and is signaled by the
+  // record_found output parameter.
+  bool ReadRecordToString(std::string *contents,
+                          size_t current_record,
+                          bool *record_found);
+
+  void ProcessStackTrace(pcrecpp::StringPiece kernel_dump,
+                         bool print_diagnostics,
+                         unsigned *hash,
+                         float *last_stack_timestamp,
+                         bool *is_watchdog_crash);
+  bool FindCrashingFunction(pcrecpp::StringPiece kernel_dump,
+                            bool print_diagnostics,
+                            float stack_trace_timestamp,
+                            std::string *crashing_function);
+  bool FindPanicMessage(pcrecpp::StringPiece kernel_dump,
+                        bool print_diagnostics,
+                        std::string *panic_message);
+
+  // Returns the architecture kind for which we are built.
+  static ArchKind GetCompilerArch();
+
+  bool is_enabled_;
+  base::FilePath ramoops_dump_path_;
+  size_t records_;
+
+  // The architecture of kernel dump strings we are working with.
+  ArchKind arch_;
+
+  DISALLOW_COPY_AND_ASSIGN(KernelCollector);
+};
+
+#endif  // CRASH_REPORTER_KERNEL_COLLECTOR_H_
diff --git a/crash_reporter/kernel_collector_test.cc b/crash_reporter/kernel_collector_test.cc
new file mode 100644 (file)
index 0000000..cdb0ae7
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ * 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 "kernel_collector_test.h"
+
+#include <unistd.h>
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/syslog_logging.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+using base::StringPrintf;
+using brillo::FindLog;
+using brillo::GetLog;
+
+namespace {
+
+int s_crashes = 0;
+bool s_metrics = false;
+
+void CountCrash() {
+  ++s_crashes;
+}
+
+bool IsMetrics() {
+  return s_metrics;
+}
+
+}  // namespace
+
+class KernelCollectorTest : public ::testing::Test {
+ protected:
+  void WriteStringToFile(const FilePath &file_path,
+                         const char *data) {
+    ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
+  }
+
+  void SetUpSuccessfulCollect();
+  void ComputeKernelStackSignatureCommon();
+
+  const FilePath &kcrash_file() const { return test_kcrash_; }
+  const FilePath &test_crash_directory() const { return test_crash_directory_; }
+
+  KernelCollectorMock collector_;
+
+ private:
+  void SetUp() override {
+    s_crashes = 0;
+    s_metrics = true;
+
+    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+    collector_.Initialize(CountCrash, IsMetrics);
+    ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
+    test_kcrash_ = scoped_temp_dir_.path().Append("kcrash");
+    ASSERT_TRUE(base::CreateDirectory(test_kcrash_));
+    collector_.OverridePreservedDumpPath(test_kcrash_);
+
+    test_kcrash_ = test_kcrash_.Append("dmesg-ramoops-0");
+    ASSERT_FALSE(base::PathExists(test_kcrash_));
+
+    test_crash_directory_ = scoped_temp_dir_.path().Append("crash_directory");
+    ASSERT_TRUE(base::CreateDirectory(test_crash_directory_));
+    brillo::ClearLog();
+  }
+
+  FilePath test_kcrash_;
+  FilePath test_crash_directory_;
+  base::ScopedTempDir scoped_temp_dir_;
+};
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureBase) {
+  // Make sure the normal build architecture is detected
+  EXPECT_NE(KernelCollector::kArchUnknown, collector_.arch());
+}
+
+TEST_F(KernelCollectorTest, LoadPreservedDump) {
+  ASSERT_FALSE(base::PathExists(kcrash_file()));
+  std::string dump;
+  dump.clear();
+
+  WriteStringToFile(kcrash_file(),
+      "CrashRecordWithoutRamoopsHeader\n<6>[    0.078852]");
+  ASSERT_TRUE(collector_.LoadParameters());
+  ASSERT_TRUE(collector_.LoadPreservedDump(&dump));
+  ASSERT_EQ("CrashRecordWithoutRamoopsHeader\n<6>[    0.078852]", dump);
+
+  WriteStringToFile(kcrash_file(), "====1.1\nsomething");
+  ASSERT_TRUE(collector_.LoadParameters());
+  ASSERT_TRUE(collector_.LoadPreservedDump(&dump));
+  ASSERT_EQ("something", dump);
+
+  WriteStringToFile(kcrash_file(), "\x01\x02\xfe\xff random blob");
+  ASSERT_TRUE(collector_.LoadParameters());
+  ASSERT_FALSE(collector_.LoadPreservedDump(&dump));
+  ASSERT_EQ("", dump);
+}
+
+TEST_F(KernelCollectorTest, EnableMissingKernel) {
+  ASSERT_FALSE(collector_.Enable());
+  ASSERT_FALSE(collector_.is_enabled());
+  ASSERT_TRUE(FindLog(
+      "Kernel does not support crash dumping"));
+  ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(KernelCollectorTest, EnableOK) {
+  WriteStringToFile(kcrash_file(), "");
+  EXPECT_CALL(collector_, DumpDirMounted()).WillOnce(::testing::Return(true));
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(collector_.is_enabled());
+  ASSERT_TRUE(FindLog("Enabling kernel crash handling"));
+  ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(KernelCollectorTest, StripSensitiveDataBasic) {
+  // Basic tests of StripSensitiveData...
+
+  // Make sure we work OK with a string w/ no MAC addresses.
+  const std::string kCrashWithNoMacsOrig =
+      "<7>[111566.131728] PM: Entering mem sleep\n";
+  std::string crash_with_no_macs(kCrashWithNoMacsOrig);
+  collector_.StripSensitiveData(&crash_with_no_macs);
+  EXPECT_EQ(kCrashWithNoMacsOrig, crash_with_no_macs);
+
+  // Make sure that we handle the case where there's nothing before/after the
+  // MAC address.
+  const std::string kJustAMacOrig =
+      "11:22:33:44:55:66";
+  const std::string kJustAMacStripped =
+      "00:00:00:00:00:01";
+  std::string just_a_mac(kJustAMacOrig);
+  collector_.StripSensitiveData(&just_a_mac);
+  EXPECT_EQ(kJustAMacStripped, just_a_mac);
+
+  // Test MAC addresses crammed together to make sure it gets both of them.
+  //
+  // I'm not sure that the code does ideal on these two test cases (they don't
+  // look like two MAC addresses to me), but since we don't see them I think
+  // it's OK to behave as shown here.
+  const std::string kCrammedMacs1Orig =
+      "11:22:33:44:55:66:11:22:33:44:55:66";
+  const std::string kCrammedMacs1Stripped =
+      "00:00:00:00:00:01:00:00:00:00:00:01";
+  std::string crammed_macs_1(kCrammedMacs1Orig);
+  collector_.StripSensitiveData(&crammed_macs_1);
+  EXPECT_EQ(kCrammedMacs1Stripped, crammed_macs_1);
+
+  const std::string kCrammedMacs2Orig =
+      "11:22:33:44:55:6611:22:33:44:55:66";
+  const std::string kCrammedMacs2Stripped =
+      "00:00:00:00:00:0100:00:00:00:00:01";
+  std::string crammed_macs_2(kCrammedMacs2Orig);
+  collector_.StripSensitiveData(&crammed_macs_2);
+  EXPECT_EQ(kCrammedMacs2Stripped, crammed_macs_2);
+
+  // Test case-sensitiveness (we shouldn't be case-senstive).
+  const std::string kCapsMacOrig =
+      "AA:BB:CC:DD:EE:FF";
+  const std::string kCapsMacStripped =
+      "00:00:00:00:00:01";
+  std::string caps_mac(kCapsMacOrig);
+  collector_.StripSensitiveData(&caps_mac);
+  EXPECT_EQ(kCapsMacStripped, caps_mac);
+
+  const std::string kLowerMacOrig =
+      "aa:bb:cc:dd:ee:ff";
+  const std::string kLowerMacStripped =
+      "00:00:00:00:00:01";
+  std::string lower_mac(kLowerMacOrig);
+  collector_.StripSensitiveData(&lower_mac);
+  EXPECT_EQ(kLowerMacStripped, lower_mac);
+}
+
+TEST_F(KernelCollectorTest, StripSensitiveDataBulk) {
+  // Test calling StripSensitiveData w/ lots of MAC addresses in the "log".
+
+  // Test that stripping code handles more than 256 unique MAC addresses, since
+  // that overflows past the last byte...
+  // We'll write up some code that generates 258 unique MAC addresses.  Sorta
+  // cheating since the code is very similar to the current code in
+  // StripSensitiveData(), but would catch if someone changed that later.
+  std::string lotsa_macs_orig;
+  std::string lotsa_macs_stripped;
+  int i;
+  for (i = 0; i < 258; i++) {
+    lotsa_macs_orig += StringPrintf(" 11:11:11:11:%02X:%02x",
+                                  (i & 0xff00) >> 8, i & 0x00ff);
+    lotsa_macs_stripped += StringPrintf(" 00:00:00:00:%02X:%02x",
+                                     ((i+1) & 0xff00) >> 8, (i+1) & 0x00ff);
+  }
+  std::string lotsa_macs(lotsa_macs_orig);
+  collector_.StripSensitiveData(&lotsa_macs);
+  EXPECT_EQ(lotsa_macs_stripped, lotsa_macs);
+}
+
+TEST_F(KernelCollectorTest, StripSensitiveDataSample) {
+  // Test calling StripSensitiveData w/ some actual lines from a real crash;
+  // included two MAC addresses (though replaced them with some bogusness).
+  const std::string kCrashWithMacsOrig =
+      "<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)"
+        " filtered out\n"
+      "<7>[108539.540144] wlan0: authenticate with 11:22:33:44:55:66 (try 1)\n"
+      "<7>[108539.554973] wlan0: associate with 11:22:33:44:55:66 (try 1)\n"
+      "<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+        " QCUSBNet Ethernet Device, 99:88:77:66:55:44\n"
+      "<7>[110964.314648] wlan0: deauthenticated from 11:22:33:44:55:66"
+        " (Reason: 6)\n"
+      "<7>[110964.325057] phy0: Removed STA 11:22:33:44:55:66\n"
+      "<7>[110964.325115] phy0: Destroyed STA 11:22:33:44:55:66\n"
+      "<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+        " QCUSBNet Ethernet Device, 99:88:77:66:55:44\n"
+      "<7>[111566.131728] PM: Entering mem sleep\n";
+  const std::string kCrashWithMacsStripped =
+      "<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)"
+        " filtered out\n"
+      "<7>[108539.540144] wlan0: authenticate with 00:00:00:00:00:01 (try 1)\n"
+      "<7>[108539.554973] wlan0: associate with 00:00:00:00:00:01 (try 1)\n"
+      "<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+        " QCUSBNet Ethernet Device, 00:00:00:00:00:02\n"
+      "<7>[110964.314648] wlan0: deauthenticated from 00:00:00:00:00:01"
+        " (Reason: 6)\n"
+      "<7>[110964.325057] phy0: Removed STA 00:00:00:00:00:01\n"
+      "<7>[110964.325115] phy0: Destroyed STA 00:00:00:00:00:01\n"
+      "<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
+        " QCUSBNet Ethernet Device, 00:00:00:00:00:02\n"
+      "<7>[111566.131728] PM: Entering mem sleep\n";
+  std::string crash_with_macs(kCrashWithMacsOrig);
+  collector_.StripSensitiveData(&crash_with_macs);
+  EXPECT_EQ(kCrashWithMacsStripped, crash_with_macs);
+}
+
+TEST_F(KernelCollectorTest, CollectPreservedFileMissing) {
+  ASSERT_FALSE(collector_.Collect());
+  ASSERT_FALSE(FindLog("Stored kcrash to "));
+  ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(KernelCollectorTest, CollectBadDirectory) {
+  WriteStringToFile(kcrash_file(), "====1.1\nsomething");
+  ASSERT_TRUE(collector_.Collect());
+  ASSERT_TRUE(FindLog("Unable to create appropriate crash directory"))
+      << "Did not find expected error string in log: {\n"
+      << GetLog() << "}";
+  ASSERT_EQ(1, s_crashes);
+}
+
+void KernelCollectorTest::SetUpSuccessfulCollect() {
+  collector_.ForceCrashDirectory(test_crash_directory());
+  WriteStringToFile(kcrash_file(), "====1.1\nsomething");
+  ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(KernelCollectorTest, CollectOptedOut) {
+  SetUpSuccessfulCollect();
+  s_metrics = false;
+  ASSERT_TRUE(collector_.Collect());
+  ASSERT_TRUE(FindLog("(ignoring - no consent)"));
+  ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(KernelCollectorTest, CollectOK) {
+  SetUpSuccessfulCollect();
+  ASSERT_TRUE(collector_.Collect());
+  ASSERT_EQ(1, s_crashes);
+  ASSERT_TRUE(FindLog("(handling)"));
+  static const char kNamePrefix[] = "Stored kcrash to ";
+  std::string log = brillo::GetLog();
+  size_t pos = log.find(kNamePrefix);
+  ASSERT_NE(std::string::npos, pos)
+      << "Did not find string \"" << kNamePrefix << "\" in log: {\n"
+      << log << "}";
+  pos += strlen(kNamePrefix);
+  std::string filename = log.substr(pos, std::string::npos);
+  // Take the name up until \n
+  size_t end_pos = filename.find_first_of("\n");
+  ASSERT_NE(std::string::npos, end_pos);
+  filename = filename.substr(0, end_pos);
+  ASSERT_EQ(0, filename.find(test_crash_directory().value()));
+  ASSERT_TRUE(base::PathExists(FilePath(filename)));
+  std::string contents;
+  ASSERT_TRUE(base::ReadFileToString(FilePath(filename), &contents));
+  ASSERT_EQ("something", contents);
+}
+
+// Perform tests which are common across architectures
+void KernelCollectorTest::ComputeKernelStackSignatureCommon() {
+  std::string signature;
+
+  const char kStackButNoPC[] =
+      "<4>[ 6066.829029]  [<790340af>] __do_softirq+0xa6/0x143\n";
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kStackButNoPC, &signature, false));
+  EXPECT_EQ("kernel--83615F0A", signature);
+
+  const char kMissingEverything[] =
+      "<4>[ 6066.829029]  [<790340af>] ? __do_softirq+0xa6/0x143\n";
+  EXPECT_FALSE(
+      collector_.ComputeKernelStackSignature(kMissingEverything,
+                                             &signature,
+                                             false));
+
+  // Long message.
+  const char kTruncatedMessage[] =
+      "<0>[   87.485611] Kernel panic - not syncing: 01234567890123456789"
+          "01234567890123456789X\n";
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kTruncatedMessage,
+                                             &signature,
+                                             false));
+  EXPECT_EQ("kernel-0123456789012345678901234567890123456789-00000000",
+            signature);
+}
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureARM) {
+  const char kBugToPanic[] =
+      "<5>[  123.412524] Modules linked in:\n"
+      "<5>[  123.412534] CPU: 0    Tainted: G        W    "
+          "(2.6.37-01030-g51cee64 #153)\n"
+      "<5>[  123.412552] PC is at write_breakme+0xd0/0x1b4\n"
+      "<5>[  123.412560] LR is at write_breakme+0xc8/0x1b4\n"
+      "<5>[  123.412569] pc : [<c0058220>]    lr : [<c005821c>]    "
+          "psr: 60000013\n"
+      "<5>[  123.412574] sp : f4e0ded8  ip : c04d104c  fp : 000e45e0\n"
+      "<5>[  123.412581] r10: 400ff000  r9 : f4e0c000  r8 : 00000004\n"
+      "<5>[  123.412589] r7 : f4e0df80  r6 : f4820c80  r5 : 00000004  "
+          "r4 : f4e0dee8\n"
+      "<5>[  123.412598] r3 : 00000000  r2 : f4e0decc  r1 : c05f88a9  "
+          "r0 : 00000039\n"
+      "<5>[  123.412608] Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA "
+          "ARM  Segment user\n"
+      "<5>[  123.412617] Control: 10c53c7d  Table: 34dcc04a  DAC: 00000015\n"
+      "<0>[  123.412626] Process bash (pid: 1014, stack limit = 0xf4e0c2f8)\n"
+      "<0>[  123.412634] Stack: (0xf4e0ded8 to 0xf4e0e000)\n"
+      "<0>[  123.412641] dec0:                                              "
+          "         f4e0dee8 c0183678\n"
+      "<0>[  123.412654] dee0: 00000000 00000000 00677562 0000081f c06a6a78 "
+          "400ff000 f4e0dfb0 00000000\n"
+      "<0>[  123.412666] df00: bec7ab44 000b1719 bec7ab0c c004f498 bec7a314 "
+          "c024acc8 00000001 c018359c\n"
+      "<0>[  123.412679] df20: f4e0df34 c04d10fc f5803c80 271beb39 000e45e0 "
+          "f5803c80 c018359c c017bfe0\n"
+      "<0>[  123.412691] df40: 00000004 f4820c80 400ff000 f4e0df80 00000004 "
+          "f4e0c000 00000000 c01383e4\n"
+      "<0>[  123.412703] df60: f4820c80 400ff000 f4820c80 400ff000 00000000 "
+          "00000000 00000004 c0138578\n"
+      "<0>[  123.412715] df80: 00000000 00000000 00000004 00000000 00000004 "
+          "402f95d0 00000004 00000004\n"
+      "<0>[  123.412727] dfa0: c0054984 c00547c0 00000004 402f95d0 00000001 "
+          "400ff000 00000004 00000000\n"
+      "<0>[  123.412739] dfc0: 00000004 402f95d0 00000004 00000004 400ff000 "
+          "000c194c bec7ab58 000e45e0\n"
+      "<0>[  123.412751] dfe0: 00000000 bec7aad8 40232520 40284e9c 60000010 "
+          "00000001 00000000 00000000\n"
+      "<5>[   39.496577] Backtrace:\n"
+      "<5>[  123.412782] [<c0058220>] (__bug+0x20/0x2c) from [<c0183678>] "
+          "(write_breakme+0xdc/0x1bc)\n"
+      "<5>[  123.412798] [<c0183678>] (write_breakme+0xdc/0x1bc) from "
+          "[<c017bfe0>] (proc_reg_write+0x88/0x9c)\n";
+  std::string signature;
+
+  collector_.set_arch(KernelCollector::kArchArm);
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
+  EXPECT_EQ("kernel-write_breakme-97D3E92F", signature);
+
+  ComputeKernelStackSignatureCommon();
+}
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureMIPS) {
+  const char kBugToPanic[] =
+      "<5>[ 3378.472000] lkdtm: Performing direct entry BUG\n"
+      "<5>[ 3378.476000] Kernel bug detected[#1]:\n"
+      "<5>[ 3378.484000] CPU: 0 PID: 185 Comm: dash Not tainted 3.14.0 #1\n"
+      "<5>[ 3378.488000] task: 8fed5220 ti: 8ec4a000 task.ti: 8ec4a000\n"
+      "<5>[ 3378.496000] $ 0   : 00000000 804018b8 804010f0 7785b507\n"
+      "<5>[ 3378.500000] $ 4   : 8061ab64 81204478 81205b20 00000000\n"
+      "<5>[ 3378.508000] $ 8   : 80830000 20746365 72746e65 55422079\n"
+      "<5>[ 3378.512000] $12   : 8ec4be94 000000fc 00000000 00000048\n"
+      "<5>[ 3378.520000] $16   : 00000004 8ef54000 80710000 00000002\n"
+      "<5>[ 3378.528000] $20   : 7765b6d4 00000004 7fffffff 00000002\n"
+      "<5>[ 3378.532000] $24   : 00000001 803dc0dc                  \n"
+      "<5>[ 3378.540000] $28   : 8ec4a000 8ec4be20 7775438d 804018b8\n"
+      "<5>[ 3378.544000] Hi    : 00000000\n"
+      "<5>[ 3378.548000] Lo    : 49bf8080\n"
+      "<5>[ 3378.552000] epc   : 804010f0 lkdtm_do_action+0x68/0x3f8\n"
+      "<5>[ 3378.560000]     Not tainted\n"
+      "<5>[ 3378.564000] ra    : 804018b8 direct_entry+0x110/0x154\n"
+      "<5>[ 3378.568000] Status: 3100dc03 KERNEL EXL IE \n"
+      "<5>[ 3378.572000] Cause : 10800024\n"
+      "<5>[ 3378.576000] PrId  : 0001a120 (MIPS interAptiv (multi))\n"
+      "<5>[ 3378.580000] Modules linked in: uinput cfg80211 nf_conntrack_ipv6 "
+          "nf_defrag_ipv6 ip6table_filter ip6_tables pcnet32 mii fuse "
+          "ppp_async ppp_generic slhc tun\n"
+      "<5>[ 3378.600000] Process dash (pid: 185, threadinfo=8ec4a000, "
+          "task=8fed5220, tls=77632490)\n"
+      "<5>[ 3378.608000] Stack : 00000006 ffffff9c 00000000 00000000 00000000 "
+          "00000000 8083454a 00000022\n"
+      "<5>          7765baa1 00001fee 80710000 8ef54000 8ec4bf08 00000002 "
+          "7765b6d4 00000004\n"
+      "<5>          7fffffff 00000002 7775438d 805e5158 7fffffff 00000002 "
+          "00000000 7785b507\n"
+      "<5>          806a96bc 00000004 8ef54000 8ec4bf08 00000002 804018b8 "
+          "80710000 806a98bc\n"
+      "<5>          00000002 00000020 00000004 8d515600 77756450 00000004 "
+          "8ec4bf08 802377e4\n"
+      "<5>          ...\n"
+      "<5>[ 3378.652000] Call Trace:\n"
+      "<5>[ 3378.656000] [<804010f0>] lkdtm_do_action+0x68/0x3f8\n"
+      "<5>[ 3378.660000] [<804018b8>] direct_entry+0x110/0x154\n"
+      "<5>[ 3378.664000] [<802377e4>] vfs_write+0xe0/0x1bc\n"
+      "<5>[ 3378.672000] [<80237f90>] SyS_write+0x78/0xf8\n"
+      "<5>[ 3378.676000] [<80111888>] handle_sys+0x128/0x14c\n"
+      "<5>[ 3378.680000] \n"
+      "<5>[ 3378.684000] \n"
+      "<5>Code: 3c04806b  0c1793aa  248494f0 <000c000d> 3c04806b  248494fc  "
+          "0c04cc7f  2405017a  08100514 \n"
+      "<5>[ 3378.696000] ---[ end trace 75067432f24bbc93 ]---\n";
+  std::string signature;
+
+  collector_.set_arch(KernelCollector::kArchMips);
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
+  EXPECT_EQ("kernel-lkdtm_do_action-5E600A6B", signature);
+
+  ComputeKernelStackSignatureCommon();
+}
+
+TEST_F(KernelCollectorTest, ComputeKernelStackSignatureX86) {
+  const char kBugToPanic[] =
+      "<4>[ 6066.829029]  [<79039d16>] ? run_timer_softirq+0x165/0x1e6\n"
+      "<4>[ 6066.829029]  [<790340af>] ignore_old_stack+0xa6/0x143\n"
+      "<0>[ 6066.829029] EIP: [<b82d7c15>] ieee80211_stop_tx_ba_session+"
+          "0xa3/0xb5 [mac80211] SS:ESP 0068:7951febc\n"
+      "<0>[ 6066.829029] CR2: 00000000323038a7\n"
+      "<4>[ 6066.845422] ---[ end trace 12b058bb46c43500 ]---\n"
+      "<0>[ 6066.845747] Kernel panic - not syncing: Fatal exception "
+          "in interrupt\n"
+      "<0>[ 6066.846902] Call Trace:\n"
+      "<4>[ 6066.846902]  [<7937a07b>] ? printk+0x14/0x19\n"
+      "<4>[ 6066.949779]  [<79379fc1>] panic+0x3e/0xe4\n"
+      "<4>[ 6066.949971]  [<7937c5c5>] oops_end+0x73/0x81\n"
+      "<4>[ 6066.950208]  [<7901b260>] no_context+0x10d/0x117\n";
+  std::string signature;
+
+  collector_.set_arch(KernelCollector::kArchX86);
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kBugToPanic, &signature, false));
+  EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-DE253569", signature);
+
+  const char kPCButNoStack[] =
+      "<0>[ 6066.829029] EIP: [<b82d7c15>] ieee80211_stop_tx_ba_session+";
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kPCButNoStack, &signature, false));
+  EXPECT_EQ("kernel-ieee80211_stop_tx_ba_session-00000000", signature);
+
+  const char kBreakmeBug[] =
+      "<4>[  180.492137]  [<790970c6>] ? handle_mm_fault+0x67f/0x96d\n"
+      "<4>[  180.492137]  [<790dcdfe>] ? proc_reg_write+0x5f/0x73\n"
+      "<4>[  180.492137]  [<790e2224>] ? write_breakme+0x0/0x108\n"
+      "<4>[  180.492137]  [<790dcd9f>] ? proc_reg_write+0x0/0x73\n"
+      "<4>[  180.492137]  [<790ac0aa>] vfs_write+0x85/0xe4\n"
+      "<0>[  180.492137] Code: c6 44 05 b2 00 89 d8 e8 0c ef 09 00 85 c0 75 "
+      "0b c7 00 00 00 00 00 e9 8e 00 00 00 ba e6 75 4b 79 89 d8 e8 f1 ee 09 "
+      "00 85 c0 75 04 <0f> 0b eb fe ba 58 47 49 79 89 d8 e8 dd ee 09 00 85 "
+      "c0 75 0a 68\n"
+      "<0>[  180.492137] EIP: [<790e22a4>] write_breakme+0x80/0x108 SS:ESP "
+          "0068:aa3e9efc\n"
+      "<4>[  180.501800] ---[ end trace 2a6b72965e1b1523 ]---\n"
+      "<0>[  180.502026] Kernel panic - not syncing: Fatal exception\n"
+      "<4>[  180.502026] Call Trace:\n"
+      "<4>[  180.502806]  [<79379aba>] ? printk+0x14/0x1a\n"
+      "<4>[  180.503033]  [<79379a00>] panic+0x3e/0xe4\n"
+      "<4>[  180.503287]  [<7937c005>] oops_end+0x73/0x81\n"
+      "<4>[  180.503520]  [<790055dd>] die+0x58/0x5e\n"
+      "<4>[  180.503538]  [<7937b96c>] do_trap+0x8e/0xa7\n"
+      "<4>[  180.503555]  [<79003d70>] ? do_invalid_op+0x0/0x80\n";
+
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kBreakmeBug, &signature, false));
+  EXPECT_EQ("kernel-write_breakme-122AB3CD", signature);
+
+  const char kPCLineTooOld[] =
+      "<4>[  174.492137]  [<790970c6>] ignored_function+0x67f/0x96d\n"
+      "<4>[  175.492137]  [<790970c6>] ignored_function2+0x67f/0x96d\n"
+      "<0>[  174.492137] EIP: [<790e22a4>] write_breakme+0x80/0x108 SS:ESP "
+          "0068:aa3e9efc\n"
+      "<4>[  180.501800] ---[ end trace 2a6b72965e1b1523 ]---\n"
+      "<4>[  180.502026] Call Trace:\n"
+      "<0>[  180.502026] Kernel panic - not syncing: Fatal exception\n"
+      "<4>[  180.502806]  [<79379aba>] printk+0x14/0x1a\n";
+
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kPCLineTooOld, &signature, false));
+  EXPECT_EQ("kernel-Fatal exception-ED4C84FE", signature);
+
+  // Panic without EIP line.
+  const char kExamplePanicOnly[] =
+      "<0>[   87.485611] Kernel panic - not syncing: Testing panic\n"
+      "<4>[   87.485630] Pid: 2825, comm: bash Tainted: G         "
+          "C 2.6.32.23+drm33.10 #1\n"
+      "<4>[   87.485639] Call Trace:\n"
+      "<4>[   87.485660]  [<8133f71d>] ? printk+0x14/0x17\n"
+      "<4>[   87.485674]  [<8133f663>] panic+0x3e/0xe4\n"
+      "<4>[   87.485689]  [<810d062e>] write_breakme+0xaa/0x124\n";
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kExamplePanicOnly,
+                                             &signature,
+                                             false));
+  EXPECT_EQ("kernel-Testing panic-E0FC3552", signature);
+
+  // Panic from hung task.
+  const char kHungTaskBreakMe[] =
+      "<3>[  720.459157] INFO: task bash:2287 blocked blah blah\n"
+      "<5>[  720.459282] Call Trace:\n"
+      "<5>[  720.459307]  [<810a457b>] ? __dentry_open+0x186/0x23e\n"
+      "<5>[  720.459323]  [<810b9c71>] ? mntput_no_expire+0x29/0xe2\n"
+      "<5>[  720.459336]  [<810b9d48>] ? mntput+0x1e/0x20\n"
+      "<5>[  720.459350]  [<810ad135>] ? path_put+0x1a/0x1d\n"
+      "<5>[  720.459366]  [<8137cacc>] schedule+0x4d/0x4f\n"
+      "<5>[  720.459379]  [<8137ccfb>] schedule_timeout+0x26/0xaf\n"
+      "<5>[  720.459394]  [<8102127e>] ? should_resched+0xd/0x27\n"
+      "<5>[  720.459409]  [<81174d1f>] ? _copy_from_user+0x3c/0x50\n"
+      "<5>[  720.459423]  [<8137cd9e>] "
+      "schedule_timeout_uninterruptible+0x1a/0x1c\n"
+      "<5>[  720.459438]  [<810dee63>] write_breakme+0xb3/0x178\n"
+      "<5>[  720.459453]  [<810dedb0>] ? meminfo_proc_show+0x2f2/0x2f2\n"
+      "<5>[  720.459467]  [<810d94ae>] proc_reg_write+0x6d/0x87\n"
+      "<5>[  720.459481]  [<810d9441>] ? proc_reg_poll+0x76/0x76\n"
+      "<5>[  720.459493]  [<810a5e9e>] vfs_write+0x79/0xa5\n"
+      "<5>[  720.459505]  [<810a6011>] sys_write+0x40/0x65\n"
+      "<5>[  720.459519]  [<8137e677>] sysenter_do_call+0x12/0x26\n"
+      "<0>[  720.459530] Kernel panic - not syncing: hung_task: blocked tasks\n"
+      "<5>[  720.459768] Pid: 31, comm: khungtaskd Tainted: "
+      "G         C  3.0.8 #1\n"
+      "<5>[  720.459998] Call Trace:\n"
+      "<5>[  720.460140]  [<81378a35>] panic+0x53/0x14a\n"
+      "<5>[  720.460312]  [<8105f875>] watchdog+0x15b/0x1a0\n"
+      "<5>[  720.460495]  [<8105f71a>] ? hung_task_panic+0x16/0x16\n"
+      "<5>[  720.460693]  [<81043af3>] kthread+0x67/0x6c\n"
+      "<5>[  720.460862]  [<81043a8c>] ? __init_kthread_worker+0x2d/0x2d\n"
+      "<5>[  720.461106]  [<8137eb9e>] kernel_thread_helper+0x6/0x10\n";
+
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kHungTaskBreakMe,
+                                             &signature,
+                                             false));
+
+  EXPECT_EQ("kernel-(HANG)-hung_task: blocked tasks-600B37EA", signature);
+
+  // Panic with all question marks in the last stack trace.
+  const char kUncertainStackTrace[] =
+      "<0>[56279.689669] ------------[ cut here ]------------\n"
+      "<2>[56279.689677] kernel BUG at /build/x86-alex/tmp/portage/"
+      "sys-kernel/chromeos-kernel-0.0.1-r516/work/chromeos-kernel-0.0.1/"
+      "kernel/timer.c:844!\n"
+      "<0>[56279.689683] invalid opcode: 0000 [#1] SMP \n"
+      "<0>[56279.689688] last sysfs file: /sys/power/state\n"
+      "<5>[56279.689692] Modules linked in: nls_iso8859_1 nls_cp437 vfat fat "
+      "gobi usbnet tsl2583(C) industrialio(C) snd_hda_codec_realtek "
+      "snd_hda_intel i2c_dev snd_hda_codec snd_hwdep qcserial snd_pcm usb_wwan "
+      "i2c_i801 snd_timer nm10_gpio snd_page_alloc rtc_cmos fuse "
+      "nf_conntrack_ipv6 nf_defrag_ipv6 uvcvideo videodev ip6table_filter "
+      "ath9k ip6_tables ipv6 mac80211 ath9k_common ath9k_hw ath cfg80211 "
+      "xt_mark\n"
+      "<5>[56279.689731] \n"
+      "<5>[56279.689738] Pid: 24607, comm: powerd_suspend Tainted: G        "
+      "WC  2.6.38.3+ #1 SAMSUNG ELECTRONICS CO., LTD. Alex/G100          \n"
+      "<5>[56279.689748] EIP: 0060:[<8103e3ea>] EFLAGS: 00210286 CPU: 3\n"
+      "<5>[56279.689758] EIP is at add_timer+0xd/0x1b\n"
+      "<5>[56279.689762] EAX: f5e00684 EBX: f5e003c0 ECX: 00000002 EDX: "
+      "00200246\n"
+      "<5>[56279.689767] ESI: f5e003c0 EDI: d28bc03c EBP: d2be5e40 ESP: "
+      "d2be5e40\n"
+      "<5>[56279.689772]  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068\n"
+      "<0>[56279.689778] Process powerd_suspend (pid: 24607, ti=d2be4000 "
+      "task=f5dc9b60 task.ti=d2be4000)\n"
+      "<0>[56279.689782] Stack:\n"
+      "<5>[56279.689785]  d2be5e4c f8dccced f4ac02c0 d2be5e70 f8ddc752 "
+      "f5e003c0 f4ac0458 f4ac092c\n"
+      "<5>[56279.689797]  f4ac043c f4ac02c0 f4ac0000 f4ac007c d2be5e7c "
+      "f8dd4a33 f4ac0164 d2be5e94\n"
+      "<5>[56279.689809]  f87e0304 f69ff0cc f4ac0164 f87e02a4 f4ac0164 "
+      "d2be5eb0 81248968 00000000\n"
+      "<0>[56279.689821] Call Trace:\n"
+      "<5>[56279.689840]  [<f8dccced>] ieee80211_sta_restart+0x25/0x8c "
+      "[mac80211]\n"
+      "<5>[56279.689854]  [<f8ddc752>] ieee80211_reconfig+0x2e9/0x339 "
+      "[mac80211]\n"
+      "<5>[56279.689869]  [<f8dd4a33>] ieee80211_aes_cmac+0x182d/0x184e "
+      "[mac80211]\n"
+      "<5>[56279.689883]  [<f87e0304>] cfg80211_get_dev_from_info+0x29b/0x2c0 "
+      "[cfg80211]\n"
+      "<5>[56279.689895]  [<f87e02a4>] ? "
+      "cfg80211_get_dev_from_info+0x23b/0x2c0 [cfg80211]\n"
+      "<5>[56279.689904]  [<81248968>] legacy_resume+0x25/0x5d\n"
+      "<5>[56279.689910]  [<812490ae>] device_resume+0xdd/0x110\n"
+      "<5>[56279.689917]  [<812491c2>] dpm_resume_end+0xe1/0x271\n"
+      "<5>[56279.689925]  [<81060481>] suspend_devices_and_enter+0x18b/0x1de\n"
+      "<5>[56279.689932]  [<810605ba>] enter_state+0xe6/0x132\n"
+      "<5>[56279.689939]  [<8105fd4b>] state_store+0x91/0x9d\n"
+      "<5>[56279.689945]  [<8105fcba>] ? state_store+0x0/0x9d\n"
+      "<5>[56279.689953]  [<81178fb1>] kobj_attr_store+0x16/0x22\n"
+      "<5>[56279.689961]  [<810eea5e>] sysfs_write_file+0xc1/0xec\n"
+      "<5>[56279.689969]  [<810af443>] vfs_write+0x8f/0x101\n"
+      "<5>[56279.689975]  [<810ee99d>] ? sysfs_write_file+0x0/0xec\n"
+      "<5>[56279.689982]  [<810af556>] sys_write+0x40/0x65\n"
+      "<5>[56279.689989]  [<81002d57>] sysenter_do_call+0x12/0x26\n"
+      "<0>[56279.689993] Code: c1 d3 e2 4a 89 55 f4 f7 d2 21 f2 6a 00 31 c9 89 "
+      "d8 e8 6e fd ff ff 5a 8d 65 f8 5b 5e 5d c3 55 89 e5 3e 8d 74 26 00 83 38 "
+      "00 74 04 <0f> 0b eb fe 8b 50 08 e8 6f ff ff ff 5d c3 55 89 e5 3e 8d 74 "
+      "26 \n"
+      "<0>[56279.690009] EIP: [<8103e3ea>] add_timer+0xd/0x1b SS:ESP "
+      "0068:d2be5e40\n"
+      "<4>[56279.690113] ---[ end trace b71141bb67c6032a ]---\n"
+      "<7>[56279.694069] wlan0: deauthenticated from 00:00:00:00:00:01 "
+      "(Reason: 6)\n"
+      "<0>[56279.703465] Kernel panic - not syncing: Fatal exception\n"
+      "<5>[56279.703471] Pid: 24607, comm: powerd_suspend Tainted: G      D "
+      "WC  2.6.38.3+ #1\n"
+      "<5>[56279.703475] Call Trace:\n"
+      "<5>[56279.703483]  [<8136648c>] ? panic+0x55/0x152\n"
+      "<5>[56279.703491]  [<810057fa>] ? oops_end+0x73/0x81\n"
+      "<5>[56279.703497]  [<81005a44>] ? die+0xed/0xf5\n"
+      "<5>[56279.703503]  [<810033cb>] ? do_trap+0x7a/0x80\n"
+      "<5>[56279.703509]  [<8100369b>] ? do_invalid_op+0x0/0x80\n"
+      "<5>[56279.703515]  [<81003711>] ? do_invalid_op+0x76/0x80\n"
+      "<5>[56279.703522]  [<8103e3ea>] ? add_timer+0xd/0x1b\n"
+      "<5>[56279.703529]  [<81025e23>] ? check_preempt_curr+0x2e/0x69\n"
+      "<5>[56279.703536]  [<8102ef28>] ? ttwu_post_activation+0x5a/0x11b\n"
+      "<5>[56279.703543]  [<8102fa8d>] ? try_to_wake_up+0x213/0x21d\n"
+      "<5>[56279.703550]  [<81368b7f>] ? error_code+0x67/0x6c\n"
+      "<5>[56279.703557]  [<8103e3ea>] ? add_timer+0xd/0x1b\n"
+      "<5>[56279.703577]  [<f8dccced>] ? ieee80211_sta_restart+0x25/0x8c "
+      "[mac80211]\n"
+      "<5>[56279.703591]  [<f8ddc752>] ? ieee80211_reconfig+0x2e9/0x339 "
+      "[mac80211]\n"
+      "<5>[56279.703605]  [<f8dd4a33>] ? ieee80211_aes_cmac+0x182d/0x184e "
+      "[mac80211]\n"
+      "<5>[56279.703618]  [<f87e0304>] ? "
+      "cfg80211_get_dev_from_info+0x29b/0x2c0 [cfg80211]\n"
+      "<5>[56279.703630]  [<f87e02a4>] ? "
+      "cfg80211_get_dev_from_info+0x23b/0x2c0 [cfg80211]\n"
+      "<5>[56279.703637]  [<81248968>] ? legacy_resume+0x25/0x5d\n"
+      "<5>[56279.703643]  [<812490ae>] ? device_resume+0xdd/0x110\n"
+      "<5>[56279.703649]  [<812491c2>] ? dpm_resume_end+0xe1/0x271\n"
+      "<5>[56279.703657]  [<81060481>] ? "
+      "suspend_devices_and_enter+0x18b/0x1de\n"
+      "<5>[56279.703663]  [<810605ba>] ? enter_state+0xe6/0x132\n"
+      "<5>[56279.703670]  [<8105fd4b>] ? state_store+0x91/0x9d\n"
+      "<5>[56279.703676]  [<8105fcba>] ? state_store+0x0/0x9d\n"
+      "<5>[56279.703683]  [<81178fb1>] ? kobj_attr_store+0x16/0x22\n"
+      "<5>[56279.703690]  [<810eea5e>] ? sysfs_write_file+0xc1/0xec\n"
+      "<5>[56279.703697]  [<810af443>] ? vfs_write+0x8f/0x101\n"
+      "<5>[56279.703703]  [<810ee99d>] ? sysfs_write_file+0x0/0xec\n"
+      "<5>[56279.703709]  [<810af556>] ? sys_write+0x40/0x65\n"
+      "<5>[56279.703716]  [<81002d57>] ? sysenter_do_call+0x12/0x26\n";
+
+  EXPECT_TRUE(
+      collector_.ComputeKernelStackSignature(kUncertainStackTrace,
+                                             &signature,
+                                             false));
+  // The first trace contains only uncertain entries and its hash is 00000000,
+  // so, if we used that, the signature would be kernel-add_timer-00000000.
+  // Instead we use the second-to-last trace for the hash.
+  EXPECT_EQ("kernel-add_timer-B5178878", signature);
+
+  ComputeKernelStackSignatureCommon();
+}
diff --git a/crash_reporter/kernel_collector_test.h b/crash_reporter/kernel_collector_test.h
new file mode 100644 (file)
index 0000000..f689e7d
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
+#define CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
+
+#include "kernel_collector.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+class KernelCollectorMock : public KernelCollector {
+ public:
+  MOCK_METHOD0(DumpDirMounted, bool());
+  MOCK_METHOD0(SetUpDBus, void());
+};
+
+#endif  // CRASH_REPORTER_KERNEL_COLLECTOR_TEST_H_
diff --git a/crash_reporter/kernel_log_collector.sh b/crash_reporter/kernel_log_collector.sh
new file mode 100644 (file)
index 0000000..82512c2
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+# 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.
+
+# Usage example: "kernel_log_collector.sh XXX YYY"
+# This script searches logs in the /var/log/messages which have the keyword XXX.
+# And only those logs which are within the last YYY seconds of the latest log
+# that has the keyword XXX are printed.
+
+# Kernel log has the possible formats:
+# 2013-06-14T16:31:40.514513-07:00 localhost kernel: [    2.682472] MSG MSG ...
+# 2013-06-19T20:38:58.661826+00:00 localhost kernel: [    1.668092] MSG MSG ...
+
+search_key=$1
+time_duration=$2
+msg_pattern="^[0-9-]*T[0-9:.+-]* localhost kernel"
+
+die() {
+  echo "kernel_log_collector: $*" >&2
+  exit 1
+}
+
+get_timestamp() {
+  timestamp="$(echo $1 | cut -d " " -f 1)"
+  timestamp="$(date -d "${timestamp}" +%s)" || exit $?
+  echo "${timestamp}"
+}
+
+last_line=$(grep "${msg_pattern}" /var/log/messages | grep -- "${search_key}" | tail -n 1)
+
+if [ -n "${last_line}" ]; then
+  if ! allowed_timestamp=$(get_timestamp "${last_line}"); then
+    die "coule not get timestamp from: ${last_line}"
+  fi
+  : $(( allowed_timestamp -= ${time_duration} ))
+  grep "${msg_pattern}" /var/log/messages | grep -- "${search_key}" | while read line; do
+    if ! timestamp=$(get_timestamp "${line}"); then
+      die "could not get timestamp from: ${line}"
+    fi
+    if [ ${timestamp} -gt ${allowed_timestamp} ]; then
+      echo "${line}"
+    fi
+  done
+fi
+
+echo "END-OF-LOG"
+
diff --git a/crash_reporter/kernel_warning_collector.cc b/crash_reporter/kernel_warning_collector.cc
new file mode 100644 (file)
index 0000000..e28e8fd
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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 "kernel_warning_collector.h"
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+
+namespace {
+const char kExecName[] = "kernel-warning";
+const char kKernelWarningSignatureKey[] = "sig";
+const char kKernelWarningPath[] = "/var/run/kwarn/warning";
+const pid_t kKernelPid = 0;
+const uid_t kRootUid = 0;
+}  // namespace
+
+using base::FilePath;
+using base::StringPrintf;
+
+KernelWarningCollector::KernelWarningCollector() {
+}
+
+KernelWarningCollector::~KernelWarningCollector() {
+}
+
+bool KernelWarningCollector::LoadKernelWarning(std::string *content,
+                                               std::string *signature) {
+  FilePath kernel_warning_path(kKernelWarningPath);
+  if (!base::ReadFileToString(kernel_warning_path, content)) {
+    LOG(ERROR) << "Could not open " << kKernelWarningPath;
+    return false;
+  }
+  // The signature is in the first line.
+  std::string::size_type end_position = content->find('\n');
+  if (end_position == std::string::npos) {
+    LOG(ERROR) << "unexpected kernel warning format";
+    return false;
+  }
+  *signature = content->substr(0, end_position);
+  return true;
+}
+
+bool KernelWarningCollector::Collect() {
+  std::string reason = "normal collection";
+  bool feedback = true;
+  if (IsDeveloperImage()) {
+    reason = "always collect from developer builds";
+    feedback = true;
+  } else if (!is_feedback_allowed_function_()) {
+    reason = "no user consent";
+    feedback = false;
+  }
+
+  LOG(INFO) << "Processing kernel warning: " << reason;
+
+  if (!feedback) {
+    return true;
+  }
+
+  std::string kernel_warning;
+  std::string warning_signature;
+  if (!LoadKernelWarning(&kernel_warning, &warning_signature)) {
+    return true;
+  }
+
+  FilePath root_crash_directory;
+  if (!GetCreatedCrashDirectoryByEuid(kRootUid, &root_crash_directory,
+                                      nullptr)) {
+    return true;
+  }
+
+  std::string dump_basename =
+      FormatDumpBasename(kExecName, time(nullptr), kKernelPid);
+  FilePath kernel_crash_path = root_crash_directory.Append(
+      StringPrintf("%s.kcrash", dump_basename.c_str()));
+
+  // We must use WriteNewFile instead of base::WriteFile as we
+  // do not want to write with root access to a symlink that an attacker
+  // might have created.
+  if (WriteNewFile(kernel_crash_path,
+                   kernel_warning.data(),
+                   kernel_warning.length()) !=
+      static_cast<int>(kernel_warning.length())) {
+    LOG(INFO) << "Failed to write kernel warning to "
+              << kernel_crash_path.value().c_str();
+    return true;
+  }
+
+  AddCrashMetaData(kKernelWarningSignatureKey, warning_signature);
+  WriteCrashMetaData(
+      root_crash_directory.Append(
+          StringPrintf("%s.meta", dump_basename.c_str())),
+    kExecName, kernel_crash_path.value());
+
+  LOG(INFO) << "Stored kernel warning into " << kernel_crash_path.value();
+  return true;
+}
diff --git a/crash_reporter/kernel_warning_collector.h b/crash_reporter/kernel_warning_collector.h
new file mode 100644 (file)
index 0000000..5ccb780
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * 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 CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
+#define CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "crash_collector.h"
+
+// Kernel warning collector.
+class KernelWarningCollector : public CrashCollector {
+ public:
+  KernelWarningCollector();
+
+  ~KernelWarningCollector() override;
+
+  // Collects warning.
+  bool Collect();
+
+ private:
+  friend class KernelWarningCollectorTest;
+  FRIEND_TEST(KernelWarningCollectorTest, CollectOK);
+
+  // Reads the full content of the kernel warn dump and its signature.
+  bool LoadKernelWarning(std::string *content, std::string *signature);
+
+  DISALLOW_COPY_AND_ASSIGN(KernelWarningCollector);
+};
+
+#endif  // CRASH_REPORTER_KERNEL_WARNING_COLLECTOR_H_
diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc
new file mode 100644 (file)
index 0000000..d445557
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * 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 <sysexits.h>
+#include <unistd.h>  // for isatty()
+
+#include <string>
+#include <vector>
+
+#include <base/cancelable_callback.h>
+#include <base/command_line.h>
+#include <base/files/file_util.h>
+#include <base/memory/weak_ptr.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_tokenizer.h>
+#include <base/strings/string_util.h>
+#include <base/values.h>
+#include <brillo/daemons/dbus_daemon.h>
+#include <brillo/syslog_logging.h>
+
+#include "libcrosservice/dbus-proxies.h"
+
+using std::unique_ptr;
+
+namespace {
+
+const char kLibCrosProxyResolvedSignalInterface[] =
+    "org.chromium.CrashReporterLibcrosProxyResolvedInterface";
+const char kLibCrosProxyResolvedName[] = "ProxyResolved";
+const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
+const char kNoProxy[] = "direct://";
+
+const int kTimeoutDefaultSeconds = 5;
+
+const char kHelp[] = "help";
+const char kQuiet[] = "quiet";
+const char kTimeout[] = "timeout";
+const char kVerbose[] = "verbose";
+// Help message to show when the --help command line switch is specified.
+const char kHelpMessage[] =
+    "Chromium OS Crash helper: proxy lister\n"
+    "\n"
+    "Available Switches:\n"
+    "  --quiet      Only print the proxies\n"
+    "  --verbose    Print additional messages even when not run from a TTY\n"
+    "  --timeout=N  Set timeout for browser resolving proxies (default is 5)\n"
+    "  --help       Show this help.\n";
+
+// Copied from src/update_engine/chrome_browser_proxy_resolver.cc
+// Parses the browser's answer for resolved proxies.  It returns a
+// list of strings, each of which is a resolved proxy.
+std::vector<std::string> ParseProxyString(const std::string& input) {
+  std::vector<std::string> ret;
+  // Some of this code taken from
+  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
+  // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
+  base::StringTokenizer entry_tok(input, ";");
+  while (entry_tok.GetNext()) {
+    std::string token = entry_tok.token();
+    base::TrimWhitespaceASCII(token, base::TRIM_ALL, &token);
+
+    // Start by finding the first space (if any).
+    std::string::iterator space;
+    for (space = token.begin(); space != token.end(); ++space) {
+      if (IsAsciiWhitespace(*space)) {
+        break;
+      }
+    }
+
+    std::string scheme = std::string(token.begin(), space);
+    base::StringToLowerASCII(&scheme);
+    // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
+    if (scheme == "socks")
+      scheme += "4";
+    else if (scheme == "proxy")
+      scheme = "http";
+    else if (scheme != "https" &&
+             scheme != "socks4" &&
+             scheme != "socks5" &&
+             scheme != "direct")
+      continue;  // Invalid proxy scheme
+
+    std::string host_and_port = std::string(space, token.end());
+    base::TrimWhitespaceASCII(host_and_port, base::TRIM_ALL, &host_and_port);
+    if (scheme != "direct" && host_and_port.empty())
+      continue;  // Must supply host/port when non-direct proxy used.
+    ret.push_back(scheme + "://" + host_and_port);
+  }
+  if (ret.empty() || *ret.rbegin() != kNoProxy)
+    ret.push_back(kNoProxy);
+  return ret;
+}
+
+// A class for interfacing with Chrome to resolve proxies for a given source
+// url.  The class is initialized with the given source url to check, the
+// signal interface and name that Chrome will reply to, and how long to wait
+// for the resolve request to timeout.  Once initialized, the Run() function
+// must be called, which blocks on the D-Bus call to Chrome.  The call returns
+// after either the timeout or the proxy has been resolved.  The resolved
+// proxies can then be accessed through the proxies() function.
+class ProxyResolver : public brillo::DBusDaemon {
+ public:
+  ProxyResolver(const std::string& source_url,
+                const std::string& signal_interface,
+                const std::string& signal_name,
+                base::TimeDelta timeout)
+      : source_url_(source_url),
+        signal_interface_(signal_interface),
+        signal_name_(signal_name),
+        timeout_(timeout),
+        weak_ptr_factory_(this),
+        timeout_callback_(base::Bind(&ProxyResolver::HandleBrowserTimeout,
+                                     weak_ptr_factory_.GetWeakPtr())) {}
+
+  ~ProxyResolver() override {}
+
+  const std::vector<std::string>& proxies() {
+    return proxies_;
+  }
+
+  int Run() override {
+    // Add task for if the browser proxy call times out.
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        timeout_callback_.callback(),
+        timeout_);
+
+    return brillo::DBusDaemon::Run();
+  }
+
+ protected:
+  // If the browser times out, quit the run loop.
+  void HandleBrowserTimeout() {
+    LOG(ERROR) << "Timeout while waiting for browser to resolve proxy";
+    Quit();
+  }
+
+  // If the signal handler connects successfully, call the browser's
+  // ResolveNetworkProxy D-Bus method.  Otherwise, don't do anything and let
+  // the timeout task quit the run loop.
+  void HandleDBusSignalConnected(const std::string& interface,
+                                 const std::string& signal,
+                                 bool success) {
+    if (!success) {
+      LOG(ERROR) << "Could not connect to signal " << interface << "."
+                 << signal;
+      timeout_callback_.Cancel();
+      Quit();
+      return;
+    }
+
+    brillo::ErrorPtr error;
+    call_proxy_->ResolveNetworkProxy(source_url_,
+                                     signal_interface_,
+                                     signal_name_,
+                                     &error);
+
+    if (error) {
+      LOG(ERROR) << "Call to ResolveNetworkProxy failed: "
+                 << error->GetMessage();
+      timeout_callback_.Cancel();
+      Quit();
+    }
+  }
+
+  // Handle incoming ProxyResolved signal.
+  void HandleProxyResolvedSignal(const std::string& source_url,
+                                 const std::string& proxy_info,
+                                 const std::string& error_message) {
+    timeout_callback_.Cancel();
+    proxies_ = ParseProxyString(proxy_info);
+    LOG(INFO) << "Found proxies via browser signal: "
+              << JoinString(proxies_, 'x');
+
+    Quit();
+  }
+
+  int OnInit() override {
+    int return_code = brillo::DBusDaemon::OnInit();
+    if (return_code != EX_OK)
+      return return_code;
+
+    // Initialize D-Bus proxies.
+    call_proxy_.reset(
+        new org::chromium::LibCrosServiceInterfaceProxy(bus_,
+                                                        kLibCrosServiceName));
+    signal_proxy_.reset(
+        new org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy(
+            bus_,
+            kLibCrosServiceName));
+
+    // Set up the D-Bus signal handler.
+    // TODO(crbug.com/446115): Update ResolveNetworkProxy call to use an
+    //     asynchronous return value rather than a return signal.
+    signal_proxy_->RegisterProxyResolvedSignalHandler(
+        base::Bind(&ProxyResolver::HandleProxyResolvedSignal,
+                   weak_ptr_factory_.GetWeakPtr()),
+        base::Bind(&ProxyResolver::HandleDBusSignalConnected,
+                   weak_ptr_factory_.GetWeakPtr()));
+
+    return EX_OK;
+  }
+
+ private:
+  unique_ptr<org::chromium::LibCrosServiceInterfaceProxy> call_proxy_;
+  unique_ptr<org::chromium::CrashReporterLibcrosProxyResolvedInterfaceProxy>
+      signal_proxy_;
+
+  const std::string source_url_;
+  const std::string signal_interface_;
+  const std::string signal_name_;
+  base::TimeDelta timeout_;
+
+  std::vector<std::string> proxies_;
+  base::WeakPtrFactory<ProxyResolver> weak_ptr_factory_;
+
+  base::CancelableClosure timeout_callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(ProxyResolver);
+};
+
+static bool ShowBrowserProxies(std::string url, base::TimeDelta timeout) {
+  // Initialize and run the proxy resolver to watch for signals.
+  ProxyResolver resolver(url,
+                         kLibCrosProxyResolvedSignalInterface,
+                         kLibCrosProxyResolvedName,
+                         timeout);
+  resolver.Run();
+
+  std::vector<std::string> proxies = resolver.proxies();
+
+  // If proxies is empty, then the timeout was reached waiting for the proxy
+  // resolved signal.  If no proxies are defined, proxies will be populated
+  // with "direct://".
+  if (proxies.empty())
+    return false;
+
+  for (const auto& proxy : proxies) {
+    printf("%s\n", proxy.c_str());
+  }
+  return true;
+}
+
+}  // namespace
+
+int main(int argc, char *argv[]) {
+  base::CommandLine::Init(argc, argv);
+  base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
+
+  if (cl->HasSwitch(kHelp)) {
+    LOG(INFO) << kHelpMessage;
+    return 0;
+  }
+
+  bool quiet = cl->HasSwitch(kQuiet);
+  bool verbose = cl->HasSwitch(kVerbose);
+
+  int timeout = kTimeoutDefaultSeconds;
+  std::string str_timeout = cl->GetSwitchValueASCII(kTimeout);
+  if (!str_timeout.empty() && !base::StringToInt(str_timeout, &timeout)) {
+    LOG(ERROR) << "Invalid timeout value: " << str_timeout;
+    return 1;
+  }
+
+  // Default to logging to syslog.
+  int init_flags = brillo::kLogToSyslog;
+  // Log to stderr if a TTY (and "-quiet" wasn't passed), or if "-verbose"
+  // was passed.
+
+  if ((!quiet && isatty(STDERR_FILENO)) || verbose)
+    init_flags |= brillo::kLogToStderr;
+  brillo::InitLog(init_flags);
+
+  std::string url;
+  base::CommandLine::StringVector urls = cl->GetArgs();
+  if (!urls.empty()) {
+    url = urls[0];
+    LOG(INFO) << "Resolving proxies for URL: " << url;
+  } else {
+    LOG(INFO) << "Resolving proxies without URL";
+  }
+
+  if (!ShowBrowserProxies(url, base::TimeDelta::FromSeconds(timeout))) {
+    LOG(ERROR) << "Error resolving proxies via the browser";
+    LOG(INFO) << "Assuming direct proxy";
+    printf("%s\n", kNoProxy);
+  }
+
+  return 0;
+}
diff --git a/crash_reporter/periodic_scheduler b/crash_reporter/periodic_scheduler
new file mode 100755 (executable)
index 0000000..5408da7
--- /dev/null
@@ -0,0 +1,80 @@
+#!/system/bin/sh
+
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Run tasks periodically.
+# Usage: $0 <delay_seconds> <timeout_seconds> <task_name> <task_binary>
+#
+# Executes task <task_name> by running <task_binary> every <delay_seconds>.
+
+set -e -u
+
+SCRIPT_NAME="$(basename "$0")"
+CHECK_DELAY=300  # Check every 5 minutes.
+KILL_DELAY=10    # How long to let the job clean up after a timeout.
+# Let the unittests override.
+: ${SPOOL_DIR:=/data/misc/crash_reporter/spool/cron-lite}
+
+loginfo() {
+  log -p i -t "${SCRIPT_NAME}" "$@"
+}
+
+trap "loginfo 'exiting'" EXIT
+
+check_and_fix_spool_paths() {
+  # Avoid weird spool paths if possible.
+  rm -f "$(dirname "${SPOOL_DIR}")" "${SPOOL_DIR}" 2>/dev/null || :
+  mkdir -p "${SPOOL_DIR}"
+  if [ ! -O "${SPOOL_DIR}" -o ! -d "${SPOOL_DIR}" ]; then
+    loginfo "Spool directory is damaged. Aborting!"
+    exit 1
+  fi
+}
+
+main() {
+  local delay="$1"
+  local timeout="$2"
+  local name="$3"
+  local spool_file="${SPOOL_DIR}/${name}"
+  shift 3
+
+  [ -z "${delay}" ] && exit 1
+  [ -z "${timeout}" ] && exit 1
+  [ -z "${name}" ] && exit 1
+  [ $# -eq 0 ] && exit 1
+  check_and_fix_spool_paths
+
+  while true; do
+    # Allow the sleep to be killed manually without terminating the handler.
+    # Send stderr to /dev/null to suppress the shell's "Terminated" message.
+    sleep $(( CHECK_DELAY + KILL_DELAY )) 2>/dev/null || true
+
+    [ ! -e "${spool_file}" ] && touch "${spool_file}"
+
+    local last_rotation="$(stat -c "%Y" "${spool_file}" 2>/dev/null || echo 0)"
+    local now="$(date +%s)"
+    local time_diff=$((now - last_rotation))
+
+    if [ ${time_diff} -gt ${delay} ]; then
+      rm "${spool_file}" || true
+      touch "${spool_file}"
+      loginfo "${name}: running $*"
+      timeout -k ${KILL_DELAY} ${timeout} "$@" || true
+      loginfo "${name}: job completed"
+    fi
+  done
+}
+
+main "$@"
diff --git a/crash_reporter/testrunner.cc b/crash_reporter/testrunner.cc
new file mode 100644 (file)
index 0000000..744cf10
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <brillo/test_helpers.h>
+#include <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+  SetUpTests(&argc, argv, true);
+  return RUN_ALL_TESTS();
+}
diff --git a/crash_reporter/udev_collector.cc b/crash_reporter/udev_collector.cc
new file mode 100644 (file)
index 0000000..1e018db
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * 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 "udev_collector.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include <base/files/file_enumerator.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/process.h>
+
+using base::FilePath;
+
+namespace {
+
+const char kCollectUdevSignature[] = "crash_reporter-udev-collection";
+const char kGzipPath[] = "/bin/gzip";
+const char kUdevExecName[] = "udev";
+const char kUdevSignatureKey[] = "sig";
+const char kUdevSubsystemDevCoredump[] = "devcoredump";
+const char kDefaultDevCoredumpDirectory[] = "/sys/class/devcoredump";
+const char kDevCoredumpFilePrefixFormat[] = "devcoredump_%s";
+
+}  // namespace
+
+UdevCollector::UdevCollector()
+    : dev_coredump_directory_(kDefaultDevCoredumpDirectory) {}
+
+UdevCollector::~UdevCollector() {}
+
+bool UdevCollector::HandleCrash(const std::string &udev_event) {
+  if (IsDeveloperImage()) {
+    LOG(INFO) << "developer image - collect udev crash info.";
+  } else if (is_feedback_allowed_function_()) {
+    LOG(INFO) << "Consent given - collect udev crash info.";
+  } else {
+    LOG(INFO) << "Ignoring - Non-developer image and no consent given.";
+    return false;
+  }
+
+  // Process the udev event string.
+  // First get all the key-value pairs.
+  std::vector<std::pair<std::string, std::string>> udev_event_keyval;
+  base::SplitStringIntoKeyValuePairs(udev_event, '=', ':', &udev_event_keyval);
+  std::vector<std::pair<std::string, std::string>>::const_iterator iter;
+  std::map<std::string, std::string> udev_event_map;
+  for (iter = udev_event_keyval.begin();
+       iter != udev_event_keyval.end();
+       ++iter) {
+    udev_event_map[iter->first] = iter->second;
+  }
+
+  // Make sure the crash directory exists, or create it if it doesn't.
+  FilePath crash_directory;
+  if (!GetCreatedCrashDirectoryByEuid(0, &crash_directory, nullptr)) {
+    LOG(ERROR) << "Could not get crash directory.";
+    return false;
+  }
+
+  if (udev_event_map["SUBSYSTEM"] == kUdevSubsystemDevCoredump) {
+    int instance_number;
+    if (!base::StringToInt(udev_event_map["KERNEL_NUMBER"], &instance_number)) {
+      LOG(ERROR) << "Invalid kernel number: "
+                 << udev_event_map["KERNEL_NUMBER"];
+      return false;
+    }
+    return ProcessDevCoredump(crash_directory, instance_number);
+  }
+
+  return ProcessUdevCrashLogs(crash_directory,
+                              udev_event_map["ACTION"],
+                              udev_event_map["KERNEL"],
+                              udev_event_map["SUBSYSTEM"]);
+}
+
+bool UdevCollector::ProcessUdevCrashLogs(const FilePath& crash_directory,
+                                         const std::string& action,
+                                         const std::string& kernel,
+                                         const std::string& subsystem) {
+  // Construct the basename string for crash_reporter_logs.conf:
+  //   "crash_reporter-udev-collection-[action]-[name]-[subsystem]"
+  // If a udev field is not provided, "" is used in its place, e.g.:
+  //   "crash_reporter-udev-collection-[action]--[subsystem]"
+  // Hence, "" is used as a wildcard name string.
+  // TODO(sque, crosbug.com/32238): Implement wildcard checking.
+  std::string basename = action + "-" + kernel + "-" + subsystem;
+  std::string udev_log_name = std::string(kCollectUdevSignature) + '-' +
+                              basename;
+
+  // Create the destination path.
+  std::string log_file_name =
+      FormatDumpBasename(basename, time(nullptr), 0);
+  FilePath crash_path = GetCrashPath(crash_directory, log_file_name, "log");
+
+  // Handle the crash.
+  bool result = GetLogContents(log_config_path_, udev_log_name, crash_path);
+  if (!result) {
+    LOG(ERROR) << "Error reading udev log info " << udev_log_name;
+    return false;
+  }
+
+  // Compress the output using gzip.
+  brillo::ProcessImpl gzip_process;
+  gzip_process.AddArg(kGzipPath);
+  gzip_process.AddArg(crash_path.value());
+  int process_result = gzip_process.Run();
+  FilePath crash_path_zipped = FilePath(crash_path.value() + ".gz");
+  // If the zip file was not created, use the uncompressed file.
+  if (process_result != 0 || !base::PathExists(crash_path_zipped))
+    LOG(ERROR) << "Could not create zip file " << crash_path_zipped.value();
+  else
+    crash_path = crash_path_zipped;
+
+  std::string exec_name = std::string(kUdevExecName) + "-" + subsystem;
+  AddCrashMetaData(kUdevSignatureKey, udev_log_name);
+  WriteCrashMetaData(GetCrashPath(crash_directory, log_file_name, "meta"),
+                     exec_name, crash_path.value());
+  return true;
+}
+
+bool UdevCollector::ProcessDevCoredump(const FilePath& crash_directory,
+                                       int instance_number) {
+  FilePath coredump_path =
+      FilePath(base::StringPrintf("%s/devcd%d/data",
+                                  dev_coredump_directory_.c_str(),
+                                  instance_number));
+  if (!base::PathExists(coredump_path)) {
+    LOG(ERROR) << "Device coredump file " << coredump_path.value()
+               << " does not exist";
+    return false;
+  }
+
+  // Add coredump file to the crash directory.
+  if (!AppendDevCoredump(crash_directory, coredump_path, instance_number)) {
+    ClearDevCoredump(coredump_path);
+    return false;
+  }
+
+  // Clear the coredump data to allow generation of future device coredumps
+  // without having to wait for the 5-minutes timeout.
+  return ClearDevCoredump(coredump_path);
+}
+
+bool UdevCollector::AppendDevCoredump(const FilePath& crash_directory,
+                                      const FilePath& coredump_path,
+                                      int instance_number) {
+  // Retrieve the driver name of the failing device.
+  std::string driver_name = GetFailingDeviceDriverName(instance_number);
+  if (driver_name.empty()) {
+    LOG(ERROR) << "Failed to obtain driver name for instance: "
+               << instance_number;
+    return false;
+  }
+
+  std::string coredump_prefix =
+      base::StringPrintf(kDevCoredumpFilePrefixFormat, driver_name.c_str());
+
+  std::string dump_basename = FormatDumpBasename(coredump_prefix,
+                                                 time(nullptr),
+                                                 instance_number);
+  FilePath core_path = GetCrashPath(crash_directory, dump_basename, "devcore");
+  FilePath log_path = GetCrashPath(crash_directory, dump_basename, "log");
+  FilePath meta_path = GetCrashPath(crash_directory, dump_basename, "meta");
+
+  // Collect coredump data.
+  if (!base::CopyFile(coredump_path, core_path)) {
+    LOG(ERROR) << "Failed to copy device coredumpm file from "
+               << coredump_path.value() << " to " << core_path.value();
+    return false;
+  }
+
+  // Collect additional logs if one is specified in the config file.
+  std::string udev_log_name = std::string(kCollectUdevSignature) + '-' +
+      kUdevSubsystemDevCoredump + '-' + driver_name;
+  bool result = GetLogContents(log_config_path_, udev_log_name, log_path);
+  if (result) {
+    AddCrashMetaUploadFile("logs", log_path.value());
+  }
+
+  WriteCrashMetaData(meta_path, coredump_prefix, core_path.value());
+
+  return true;
+}
+
+bool UdevCollector::ClearDevCoredump(const FilePath& coredump_path) {
+  if (!base::WriteFile(coredump_path, "0", 1)) {
+    LOG(ERROR) << "Failed to delete the coredump data file "
+               << coredump_path.value();
+    return false;
+  }
+  return true;
+}
+
+std::string UdevCollector::GetFailingDeviceDriverName(int instance_number) {
+  FilePath failing_uevent_path =
+      FilePath(base::StringPrintf("%s/devcd%d/failing_device/uevent",
+                                  dev_coredump_directory_.c_str(),
+                                  instance_number));
+  if (!base::PathExists(failing_uevent_path)) {
+    LOG(ERROR) << "Failing uevent path " << failing_uevent_path.value()
+               << " does not exist";
+    return "";
+  }
+
+  std::string uevent_content;
+  if (!base::ReadFileToString(failing_uevent_path, &uevent_content)) {
+    LOG(ERROR) << "Failed to read uevent file " << failing_uevent_path.value();
+    return "";
+  }
+
+  // Parse uevent file contents as key-value pairs.
+  std::vector<std::pair<std::string, std::string>> uevent_keyval;
+  base::SplitStringIntoKeyValuePairs(uevent_content, '=', '\n', &uevent_keyval);
+  std::vector<std::pair<std::string, std::string>>::const_iterator iter;
+  for (iter = uevent_keyval.begin();
+       iter != uevent_keyval.end();
+       ++iter) {
+    if (iter->first == "DRIVER") {
+      return iter->second;
+    }
+  }
+
+  return "";
+}
diff --git a/crash_reporter/udev_collector.h b/crash_reporter/udev_collector.h
new file mode 100644 (file)
index 0000000..e267b75
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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 CRASH_REPORTER_UDEV_COLLECTOR_H_
+#define CRASH_REPORTER_UDEV_COLLECTOR_H_
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "crash_collector.h"
+
+// Udev crash collector.
+class UdevCollector : public CrashCollector {
+ public:
+  UdevCollector();
+
+  ~UdevCollector() override;
+
+  // The udev event string should be formatted as follows:
+  //   "ACTION=[action]:KERNEL=[name]:SUBSYSTEM=[subsystem]"
+  // The values don't have to be in any particular order. One or more of them
+  // could be omitted, in which case it would be treated as a wildcard (*).
+  bool HandleCrash(const std::string& udev_event);
+
+ protected:
+  std::string dev_coredump_directory_;
+
+ private:
+  friend class UdevCollectorTest;
+
+  // Process udev crash logs, collecting log files according to the config
+  // file (crash_reporter_logs.conf).
+  bool ProcessUdevCrashLogs(const base::FilePath& crash_directory,
+                            const std::string& action,
+                            const std::string& kernel,
+                            const std::string& subsystem);
+  // Process device coredump, collecting device coredump file.
+  // |instance_number| is the kernel number of the virtual device for the device
+  // coredump instance.
+  bool ProcessDevCoredump(const base::FilePath& crash_directory,
+                          int instance_number);
+  // Copy device coredump file to crash directory, and perform necessary
+  // coredump file management.
+  bool AppendDevCoredump(const base::FilePath& crash_directory,
+                         const base::FilePath& coredump_path,
+                         int instance_number);
+  // Clear the device coredump file by performing a dummy write to it.
+  bool ClearDevCoredump(const base::FilePath& coredump_path);
+  // Return the driver name of the device that generates the coredump.
+  std::string GetFailingDeviceDriverName(int instance_number);
+
+  // Mutator for unit testing.
+  void set_log_config_path(const std::string& path) {
+    log_config_path_ = base::FilePath(path);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(UdevCollector);
+};
+
+#endif  // CRASH_REPORTER_UDEV_COLLECTOR_H_
diff --git a/crash_reporter/udev_collector_test.cc b/crash_reporter/udev_collector_test.cc
new file mode 100644 (file)
index 0000000..5474f48
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * 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 <base/files/file_enumerator.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/syslog_logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "udev_collector.h"
+
+using base::FilePath;
+
+namespace {
+
+// Dummy log config file name.
+const char kLogConfigFileName[] = "log_config_file";
+
+// Dummy directory for storing device coredumps.
+const char kDevCoredumpDirectory[] = "devcoredump";
+
+// A bunch of random rules to put into the dummy log config file.
+const char kLogConfigFileContents[] =
+    "crash_reporter-udev-collection-change-card0-drm=echo change card0 drm\n"
+    "crash_reporter-udev-collection-add-state0-cpu=echo change state0 cpu\n"
+    "crash_reporter-udev-collection-devcoredump-iwlwifi=echo devcoredump\n"
+    "cros_installer=echo not for udev";
+
+const char kCrashLogFilePattern[] = "*.log.gz";
+const char kDevCoredumpFilePattern[] = "*.devcore";
+
+// Dummy content for device coredump data file.
+const char kDevCoredumpDataContents[] = "coredump";
+
+// Content for failing device's uevent file.
+const char kFailingDeviceUeventContents[] = "DRIVER=iwlwifi\n";
+
+void CountCrash() {}
+
+bool s_consent_given = true;
+
+bool IsMetrics() {
+  return s_consent_given;
+}
+
+// Returns the number of files found in the given path that matches the
+// specified file name pattern.
+int GetNumFiles(const FilePath& path, const std::string& file_pattern) {
+  base::FileEnumerator enumerator(path, false, base::FileEnumerator::FILES,
+                                  file_pattern);
+  int num_files = 0;
+  for (FilePath file_path = enumerator.Next();
+       !file_path.value().empty();
+       file_path = enumerator.Next()) {
+    num_files++;
+  }
+  return num_files;
+}
+
+}  // namespace
+
+class UdevCollectorMock : public UdevCollector {
+ public:
+  MOCK_METHOD0(SetUpDBus, void());
+};
+
+class UdevCollectorTest : public ::testing::Test {
+ protected:
+  base::ScopedTempDir temp_dir_generator_;
+
+  void HandleCrash(const std::string &udev_event) {
+    collector_.HandleCrash(udev_event);
+  }
+
+  void GenerateDevCoredump(const std::string& device_name) {
+    // Generate coredump data file.
+    ASSERT_TRUE(CreateDirectory(
+        FilePath(base::StringPrintf("%s/%s",
+                                    collector_.dev_coredump_directory_.c_str(),
+                                    device_name.c_str()))));
+    FilePath data_path =
+        FilePath(base::StringPrintf("%s/%s/data",
+                                    collector_.dev_coredump_directory_.c_str(),
+                                    device_name.c_str()));
+    ASSERT_EQ(strlen(kDevCoredumpDataContents),
+              base::WriteFile(data_path,
+                              kDevCoredumpDataContents,
+                              strlen(kDevCoredumpDataContents)));
+    // Generate uevent file for failing device.
+    ASSERT_TRUE(CreateDirectory(
+        FilePath(base::StringPrintf("%s/%s/failing_device",
+                                    collector_.dev_coredump_directory_.c_str(),
+                                    device_name.c_str()))));
+    FilePath uevent_path =
+        FilePath(base::StringPrintf("%s/%s/failing_device/uevent",
+                                    collector_.dev_coredump_directory_.c_str(),
+                                    device_name.c_str()));
+    ASSERT_EQ(strlen(kFailingDeviceUeventContents),
+              base::WriteFile(uevent_path,
+                              kFailingDeviceUeventContents,
+                              strlen(kFailingDeviceUeventContents)));
+  }
+
+ private:
+  void SetUp() override {
+    s_consent_given = true;
+
+    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+    collector_.Initialize(CountCrash, IsMetrics);
+
+    ASSERT_TRUE(temp_dir_generator_.CreateUniqueTempDir());
+
+    FilePath log_config_path =
+        temp_dir_generator_.path().Append(kLogConfigFileName);
+    collector_.log_config_path_ = log_config_path;
+    collector_.ForceCrashDirectory(temp_dir_generator_.path());
+
+    FilePath dev_coredump_path =
+        temp_dir_generator_.path().Append(kDevCoredumpDirectory);
+    collector_.dev_coredump_directory_ = dev_coredump_path.value();
+
+    // Write to a dummy log config file.
+    ASSERT_EQ(strlen(kLogConfigFileContents),
+              base::WriteFile(log_config_path,
+                              kLogConfigFileContents,
+                              strlen(kLogConfigFileContents)));
+
+    brillo::ClearLog();
+  }
+
+  UdevCollectorMock collector_;
+};
+
+TEST_F(UdevCollectorTest, TestNoConsent) {
+  s_consent_given = false;
+  HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
+  EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+}
+
+TEST_F(UdevCollectorTest, TestNoMatch) {
+  // No rule should match this.
+  HandleCrash("ACTION=change:KERNEL=foo:SUBSYSTEM=bar");
+  EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+}
+
+TEST_F(UdevCollectorTest, TestMatches) {
+  // Try multiple udev events in sequence.  The number of log files generated
+  // should increase.
+  HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
+  EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+  HandleCrash("ACTION=add:KERNEL=state0:SUBSYSTEM=cpu");
+  EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
+}
+
+TEST_F(UdevCollectorTest, TestDevCoredump) {
+  GenerateDevCoredump("devcd0");
+  HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
+  EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(),
+                           kDevCoredumpFilePattern));
+  GenerateDevCoredump("devcd1");
+  HandleCrash("ACTION=add:KERNEL_NUMBER=1:SUBSYSTEM=devcoredump");
+  EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(),
+                           kDevCoredumpFilePattern));
+}
+
+// TODO(sque, crosbug.com/32238) - test wildcard cases, multiple identical udev
+// events.
diff --git a/crash_reporter/unclean_shutdown_collector.cc b/crash_reporter/unclean_shutdown_collector.cc
new file mode 100644 (file)
index 0000000..8a092ec
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unclean_shutdown_collector.h"
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+
+static const char kUncleanShutdownFile[] =
+    "/var/lib/crash_reporter/pending_clean_shutdown";
+
+// Files created by power manager used for crash reporting.
+static const char kPowerdTracePath[] = "/var/lib/power_manager";
+// Presence of this file indicates that the system was suspended
+static const char kPowerdSuspended[] = "powerd_suspended";
+
+using base::FilePath;
+
+UncleanShutdownCollector::UncleanShutdownCollector()
+    : unclean_shutdown_file_(kUncleanShutdownFile),
+      powerd_trace_path_(kPowerdTracePath),
+      powerd_suspended_file_(powerd_trace_path_.Append(kPowerdSuspended)) {
+}
+
+UncleanShutdownCollector::~UncleanShutdownCollector() {
+}
+
+bool UncleanShutdownCollector::Enable() {
+  FilePath file_path(unclean_shutdown_file_);
+  base::CreateDirectory(file_path.DirName());
+  if (base::WriteFile(file_path, "", 0) != 0) {
+    LOG(ERROR) << "Unable to create shutdown check file";
+    return false;
+  }
+  return true;
+}
+
+bool UncleanShutdownCollector::DeleteUncleanShutdownFiles() {
+  if (!base::DeleteFile(FilePath(unclean_shutdown_file_), false)) {
+    LOG(ERROR) << "Failed to delete unclean shutdown file "
+               << unclean_shutdown_file_;
+    return false;
+  }
+  // Delete power manager state file if it exists.
+  base::DeleteFile(powerd_suspended_file_, false);
+  return true;
+}
+
+bool UncleanShutdownCollector::Collect() {
+  FilePath unclean_file_path(unclean_shutdown_file_);
+  if (!base::PathExists(unclean_file_path)) {
+    return false;
+  }
+  LOG(WARNING) << "Last shutdown was not clean";
+  if (DeadBatteryCausedUncleanShutdown()) {
+    DeleteUncleanShutdownFiles();
+    return false;
+  }
+  DeleteUncleanShutdownFiles();
+
+  if (is_feedback_allowed_function_()) {
+    count_crash_function_();
+  }
+  return true;
+}
+
+bool UncleanShutdownCollector::Disable() {
+  LOG(INFO) << "Clean shutdown signalled";
+  return DeleteUncleanShutdownFiles();
+}
+
+bool UncleanShutdownCollector::DeadBatteryCausedUncleanShutdown() {
+  // Check for case of battery running out while suspended.
+  if (base::PathExists(powerd_suspended_file_)) {
+    LOG(INFO) << "Unclean shutdown occurred while suspended. Not counting "
+              << "toward unclean shutdown statistic.";
+    return true;
+  }
+  return false;
+}
diff --git a/crash_reporter/unclean_shutdown_collector.h b/crash_reporter/unclean_shutdown_collector.h
new file mode 100644 (file)
index 0000000..5bc9968
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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 CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
+#define CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "crash_collector.h"
+
+// Unclean shutdown collector.
+class UncleanShutdownCollector : public CrashCollector {
+ public:
+  UncleanShutdownCollector();
+  ~UncleanShutdownCollector() override;
+
+  // Enable collection - signal that a boot has started.
+  bool Enable();
+
+  // Collect if there is was an unclean shutdown. Returns true if
+  // there was, false otherwise.
+  bool Collect();
+
+  // Disable collection - signal that the system has been shutdown cleanly.
+  bool Disable();
+
+ private:
+  friend class UncleanShutdownCollectorTest;
+  FRIEND_TEST(UncleanShutdownCollectorTest, EnableCannotWrite);
+  FRIEND_TEST(UncleanShutdownCollectorTest, CollectDeadBatterySuspended);
+
+  bool DeleteUncleanShutdownFiles();
+
+  // Check for unclean shutdown due to battery running out by analyzing powerd
+  // trace files.
+  bool DeadBatteryCausedUncleanShutdown();
+
+  const char *unclean_shutdown_file_;
+  base::FilePath powerd_trace_path_;
+  base::FilePath powerd_suspended_file_;
+
+  DISALLOW_COPY_AND_ASSIGN(UncleanShutdownCollector);
+};
+
+#endif  // CRASH_REPORTER_UNCLEAN_SHUTDOWN_COLLECTOR_H_
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
new file mode 100644 (file)
index 0000000..3bdeca1
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "unclean_shutdown_collector.h"
+
+#include <unistd.h>
+
+#include <base/files/file_util.h>
+#include <base/strings/string_util.h>
+#include <brillo/syslog_logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+using ::brillo::FindLog;
+
+namespace {
+
+int s_crashes = 0;
+bool s_metrics = true;
+
+const char kTestDirectory[] = "test";
+const char kTestSuspended[] = "test/suspended";
+const char kTestUnclean[] = "test/unclean";
+
+void CountCrash() {
+  ++s_crashes;
+}
+
+bool IsMetrics() {
+  return s_metrics;
+}
+
+}  // namespace
+
+class UncleanShutdownCollectorMock : public UncleanShutdownCollector {
+ public:
+  MOCK_METHOD0(SetUpDBus, void());
+};
+
+class UncleanShutdownCollectorTest : public ::testing::Test {
+  void SetUp() {
+    s_crashes = 0;
+
+    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+    collector_.Initialize(CountCrash,
+                          IsMetrics);
+    rmdir(kTestDirectory);
+    test_unclean_ = FilePath(kTestUnclean);
+    collector_.unclean_shutdown_file_ = kTestUnclean;
+    base::DeleteFile(test_unclean_, true);
+    // Set up an alternate power manager state file as well
+    collector_.powerd_suspended_file_ = FilePath(kTestSuspended);
+    brillo::ClearLog();
+  }
+
+ protected:
+  void WriteStringToFile(const FilePath &file_path,
+                         const char *data) {
+    ASSERT_EQ(strlen(data), base::WriteFile(file_path, data, strlen(data)));
+  }
+
+  UncleanShutdownCollectorMock collector_;
+  FilePath test_unclean_;
+};
+
+TEST_F(UncleanShutdownCollectorTest, EnableWithoutParent) {
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(base::PathExists(test_unclean_));
+}
+
+TEST_F(UncleanShutdownCollectorTest, EnableWithParent) {
+  mkdir(kTestDirectory, 0777);
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(base::PathExists(test_unclean_));
+}
+
+TEST_F(UncleanShutdownCollectorTest, EnableCannotWrite) {
+  collector_.unclean_shutdown_file_ = "/bad/path";
+  ASSERT_FALSE(collector_.Enable());
+  ASSERT_TRUE(FindLog("Unable to create shutdown check file"));
+}
+
+TEST_F(UncleanShutdownCollectorTest, CollectTrue) {
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(base::PathExists(test_unclean_));
+  ASSERT_TRUE(collector_.Collect());
+  ASSERT_FALSE(base::PathExists(test_unclean_));
+  ASSERT_EQ(1, s_crashes);
+  ASSERT_TRUE(FindLog("Last shutdown was not clean"));
+}
+
+TEST_F(UncleanShutdownCollectorTest, CollectFalse) {
+  ASSERT_FALSE(collector_.Collect());
+  ASSERT_EQ(0, s_crashes);
+}
+
+TEST_F(UncleanShutdownCollectorTest, CollectDeadBatterySuspended) {
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(base::PathExists(test_unclean_));
+  base::WriteFile(collector_.powerd_suspended_file_, "", 0);
+  ASSERT_FALSE(collector_.Collect());
+  ASSERT_FALSE(base::PathExists(test_unclean_));
+  ASSERT_FALSE(base::PathExists(collector_.powerd_suspended_file_));
+  ASSERT_EQ(0, s_crashes);
+  ASSERT_TRUE(FindLog("Unclean shutdown occurred while suspended."));
+}
+
+TEST_F(UncleanShutdownCollectorTest, Disable) {
+  ASSERT_TRUE(collector_.Enable());
+  ASSERT_TRUE(base::PathExists(test_unclean_));
+  ASSERT_TRUE(collector_.Disable());
+  ASSERT_FALSE(base::PathExists(test_unclean_));
+  ASSERT_FALSE(collector_.Collect());
+}
+
+TEST_F(UncleanShutdownCollectorTest, DisableWhenNotEnabled) {
+  ASSERT_TRUE(collector_.Disable());
+}
+
+TEST_F(UncleanShutdownCollectorTest, CantDisable) {
+  mkdir(kTestDirectory, 0700);
+  if (mkdir(kTestUnclean, 0700)) {
+    ASSERT_EQ(EEXIST, errno)
+        << "Error while creating directory '" << kTestUnclean
+        << "': " << strerror(errno);
+  }
+  ASSERT_EQ(0, base::WriteFile(test_unclean_.Append("foo"), "", 0))
+      << "Error while creating empty file '"
+      << test_unclean_.Append("foo").value() << "': " << strerror(errno);
+  ASSERT_FALSE(collector_.Disable());
+  rmdir(kTestUnclean);
+}
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
new file mode 100644 (file)
index 0000000..6714f52
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ * 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 "user_collector.h"
+
+#include <elf.h>
+#include <fcntl.h>
+#include <grp.h>  // For struct group.
+#include <pcrecpp.h>
+#include <pwd.h>  // For struct passwd.
+#include <stdint.h>
+#include <sys/cdefs.h>  // For __WORDSIZE
+#include <sys/fsuid.h>
+#include <sys/types.h>  // For getpwuid_r, getgrnam_r, WEXITSTATUS.
+#include <unistd.h>  // For setgroups
+
+#include <iostream>  // For std::oct
+#include <string>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/osrelease_reader.h>
+#include <brillo/process.h>
+#include <brillo/syslog_logging.h>
+#include <cutils/properties.h>
+#include <private/android_filesystem_config.h>
+
+static const char kCollectionErrorSignature[] =
+    "crash_reporter-user-collection";
+static const char kCorePatternProperty[] = "crash_reporter.coredump.enabled";
+static const char kCoreToMinidumpConverterPath[] = "/system/bin/core2md";
+
+static const char kStatePrefix[] = "State:\t";
+
+static const char kCoreTempFolder[] = "/data/misc/crash_reporter/tmp";
+
+// Define an otherwise invalid value that represents an unknown UID and GID.
+static const uid_t kUnknownUid = -1;
+static const gid_t kUnknownGid = -1;
+
+const char *UserCollector::kUserId = "Uid:\t";
+const char *UserCollector::kGroupId = "Gid:\t";
+
+// Product information keys in the /etc/os-release.d folder.
+static const char kBdkVersionKey[] = "bdk_version";
+static const char kProductIDKey[] = "product_id";
+static const char kProductVersionKey[] = "product_version";
+
+
+using base::FilePath;
+using base::StringPrintf;
+
+UserCollector::UserCollector()
+    : generate_diagnostics_(false),
+      initialized_(false) {
+}
+
+void UserCollector::Initialize(
+    UserCollector::CountCrashFunction count_crash_function,
+    const std::string &our_path,
+    UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
+    bool generate_diagnostics,
+    bool core2md_failure,
+    bool directory_failure,
+    const std::string &filter_in) {
+  CrashCollector::Initialize(count_crash_function,
+                             is_feedback_allowed_function);
+  our_path_ = our_path;
+  initialized_ = true;
+  generate_diagnostics_ = generate_diagnostics;
+  core2md_failure_ = core2md_failure;
+  directory_failure_ = directory_failure;
+  filter_in_ = filter_in;
+
+  gid_t groups[] = { AID_ROOT, AID_SYSTEM, AID_DBUS, AID_READPROC };
+  if (setgroups(arraysize(groups), groups) != 0) {
+    PLOG(FATAL) << "Unable to set groups to root, system, dbus, and readproc";
+  }
+}
+
+UserCollector::~UserCollector() {
+}
+
+std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const {
+  switch (error_type) {
+    case kErrorSystemIssue:
+      return "system-issue";
+    case kErrorReadCoreData:
+      return "read-core-data";
+    case kErrorUnusableProcFiles:
+      return "unusable-proc-files";
+    case kErrorInvalidCoreFile:
+      return "invalid-core-file";
+    case kErrorUnsupported32BitCoreFile:
+      return "unsupported-32bit-core-file";
+    case kErrorCore2MinidumpConversion:
+      return "core2md-conversion";
+    default:
+      return "";
+  }
+}
+
+bool UserCollector::SetUpInternal(bool enabled) {
+  CHECK(initialized_);
+  LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
+
+  property_set(kCorePatternProperty, enabled ? "1" : "0");
+
+  return true;
+}
+
+bool UserCollector::GetFirstLineWithPrefix(
+    const std::vector<std::string> &lines,
+    const char *prefix, std::string *line) {
+  std::vector<std::string>::const_iterator line_iterator;
+  for (line_iterator = lines.begin(); line_iterator != lines.end();
+       ++line_iterator) {
+    if (line_iterator->find(prefix) == 0) {
+      *line = *line_iterator;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool UserCollector::GetIdFromStatus(
+    const char *prefix, IdKind kind,
+    const std::vector<std::string> &status_lines, int *id) {
+  // From fs/proc/array.c:task_state(), this file contains:
+  // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
+  std::string id_line;
+  if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) {
+    return false;
+  }
+  std::string id_substring = id_line.substr(strlen(prefix), std::string::npos);
+  std::vector<std::string> ids;
+  base::SplitString(id_substring, '\t', &ids);
+  if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
+    return false;
+  }
+  const char *number = ids[kind].c_str();
+  char *end_number = nullptr;
+  *id = strtol(number, &end_number, 10);
+  if (*end_number != '\0') {
+    return false;
+  }
+  return true;
+}
+
+bool UserCollector::GetStateFromStatus(
+    const std::vector<std::string> &status_lines, std::string *state) {
+  std::string state_line;
+  if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) {
+    return false;
+  }
+  *state = state_line.substr(strlen(kStatePrefix), std::string::npos);
+  return true;
+}
+
+void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
+                                              ErrorType error_type,
+                                              const std::string &exec) {
+  FilePath crash_path;
+  LOG(INFO) << "Writing conversion problems as separate crash report.";
+  if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, nullptr)) {
+    LOG(ERROR) << "Could not even get log directory; out of space?";
+    return;
+  }
+  AddCrashMetaData("sig", kCollectionErrorSignature);
+  AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
+  std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
+  std::string error_log = brillo::GetLog();
+  FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
+  if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature,
+                     diag_log_path)) {
+    // We load the contents of diag_log into memory and append it to
+    // the error log.  We cannot just append to files because we need
+    // to always create new files to prevent attack.
+    std::string diag_log_contents;
+    base::ReadFileToString(diag_log_path, &diag_log_contents);
+    error_log.append(diag_log_contents);
+    base::DeleteFile(diag_log_path, false);
+  }
+  FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
+  FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
+  // We must use WriteNewFile instead of base::WriteFile as we do
+  // not want to write with root access to a symlink that an attacker
+  // might have created.
+  if (WriteNewFile(log_path, error_log.data(), error_log.length()) < 0) {
+    LOG(ERROR) << "Error writing new file " << log_path.value();
+    return;
+  }
+  WriteCrashMetaData(meta_path, exec, log_path.value());
+}
+
+bool UserCollector::CopyOffProcFiles(pid_t pid,
+                                     const FilePath &container_dir) {
+  if (!base::CreateDirectory(container_dir)) {
+    PLOG(ERROR) << "Could not create " << container_dir.value();
+    return false;
+  }
+  int dir_mask = base::FILE_PERMISSION_READ_BY_USER
+                 | base::FILE_PERMISSION_WRITE_BY_USER
+                 | base::FILE_PERMISSION_EXECUTE_BY_USER
+                 | base::FILE_PERMISSION_READ_BY_GROUP
+                 | base::FILE_PERMISSION_WRITE_BY_GROUP;
+  if (!base::SetPosixFilePermissions(container_dir,
+                                     base::FILE_PERMISSION_MASK & dir_mask)) {
+    PLOG(ERROR) << "Could not set permissions for " << container_dir.value()
+                << " to " << std::oct
+                << (base::FILE_PERMISSION_MASK & dir_mask);
+    return false;
+  }
+  FilePath process_path = GetProcessPath(pid);
+  if (!base::PathExists(process_path)) {
+    LOG(ERROR) << "Path " << process_path.value() << " does not exist";
+    return false;
+  }
+  static const char *proc_files[] = {
+    "auxv",
+    "cmdline",
+    "environ",
+    "maps",
+    "status"
+  };
+  for (unsigned i = 0; i < arraysize(proc_files); ++i) {
+    if (!base::CopyFile(process_path.Append(proc_files[i]),
+                        container_dir.Append(proc_files[i]))) {
+      LOG(ERROR) << "Could not copy " << proc_files[i] << " file";
+      return false;
+    }
+  }
+  return true;
+}
+
+bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const {
+  // Check if the maps file is empty, which could be due to the crashed
+  // process being reaped by the kernel before finishing a core dump.
+  int64_t file_size = 0;
+  if (!base::GetFileSize(container_dir.Append("maps"), &file_size)) {
+    LOG(ERROR) << "Could not get the size of maps file";
+    return false;
+  }
+  if (file_size == 0) {
+    LOG(ERROR) << "maps file is empty";
+    return false;
+  }
+  return true;
+}
+
+UserCollector::ErrorType UserCollector::ValidateCoreFile(
+    const FilePath &core_path) const {
+  int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY));
+  if (fd < 0) {
+    PLOG(ERROR) << "Could not open core file " << core_path.value();
+    return kErrorInvalidCoreFile;
+  }
+
+  char e_ident[EI_NIDENT];
+  bool read_ok = base::ReadFromFD(fd, e_ident, sizeof(e_ident));
+  IGNORE_EINTR(close(fd));
+  if (!read_ok) {
+    LOG(ERROR) << "Could not read header of core file";
+    return kErrorInvalidCoreFile;
+  }
+
+  if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
+      e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
+    LOG(ERROR) << "Invalid core file";
+    return kErrorInvalidCoreFile;
+  }
+
+#if __WORDSIZE == 64
+  // TODO(benchan, mkrebs): Remove this check once core2md can
+  // handles both 32-bit and 64-bit ELF on a 64-bit platform.
+  if (e_ident[EI_CLASS] == ELFCLASS32) {
+    LOG(ERROR) << "Conversion of 32-bit core file on 64-bit platform is "
+               << "currently not supported";
+    return kErrorUnsupported32BitCoreFile;
+  }
+#endif
+
+  return kErrorNone;
+}
+
+bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid,
+                                             FilePath *crash_file_path,
+                                             bool *out_of_capacity) {
+  FilePath process_path = GetProcessPath(pid);
+  std::string status;
+  if (directory_failure_) {
+    LOG(ERROR) << "Purposefully failing to create spool directory";
+    return false;
+  }
+
+  uid_t uid;
+  if (base::ReadFileToString(process_path.Append("status"), &status)) {
+    std::vector<std::string> status_lines;
+    base::SplitString(status, '\n', &status_lines);
+
+    std::string process_state;
+    if (!GetStateFromStatus(status_lines, &process_state)) {
+      LOG(ERROR) << "Could not find process state in status file";
+      return false;
+    }
+    LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state;
+
+    // Get effective UID of crashing process.
+    int id;
+    if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &id)) {
+      LOG(ERROR) << "Could not find euid in status file";
+      return false;
+    }
+    uid = id;
+  } else if (supplied_ruid != kUnknownUid) {
+    LOG(INFO) << "Using supplied UID " << supplied_ruid
+              << " for crashed process [" << pid
+              << "] due to error reading status file";
+    uid = supplied_ruid;
+  } else {
+    LOG(ERROR) << "Could not read status file and kernel did not supply UID";
+    LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: "
+              << base::DirectoryExists(process_path);
+    return false;
+  }
+
+  if (!GetCreatedCrashDirectoryByEuid(uid, crash_file_path, out_of_capacity)) {
+    LOG(ERROR) << "Could not create crash directory";
+    return false;
+  }
+  return true;
+}
+
+bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
+  // Copy off all stdin to a core file.
+  FilePath stdin_path("/proc/self/fd/0");
+  if (base::CopyFile(stdin_path, core_path)) {
+    return true;
+  }
+
+  PLOG(ERROR) << "Could not write core file";
+  // If the file system was full, make sure we remove any remnants.
+  base::DeleteFile(core_path, false);
+  return false;
+}
+
+bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
+                                      const FilePath &procfs_directory,
+                                      const FilePath &minidump_path,
+                                      const FilePath &temp_directory) {
+  FilePath output_path = temp_directory.Append("output");
+  brillo::ProcessImpl core2md;
+  core2md.RedirectOutput(output_path.value());
+  core2md.AddArg(kCoreToMinidumpConverterPath);
+  core2md.AddArg(core_path.value());
+  core2md.AddArg(procfs_directory.value());
+
+  if (!core2md_failure_) {
+    core2md.AddArg(minidump_path.value());
+  } else {
+    // To test how core2md errors are propagaged, cause an error
+    // by forgetting a required argument.
+  }
+
+  int errorlevel = core2md.Run();
+
+  std::string output;
+  base::ReadFileToString(output_path, &output);
+  if (errorlevel != 0) {
+    LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath
+               << " [result=" << errorlevel << "]: " << output;
+    return false;
+  }
+
+  if (!base::PathExists(minidump_path)) {
+    LOG(ERROR) << "Minidump file " << minidump_path.value()
+               << " was not created";
+    return false;
+  }
+  return true;
+}
+
+UserCollector::ErrorType UserCollector::ConvertCoreToMinidump(
+    pid_t pid,
+    const FilePath &container_dir,
+    const FilePath &core_path,
+    const FilePath &minidump_path) {
+  // If proc files are unuable, we continue to read the core file from stdin,
+  // but only skip the core-to-minidump conversion, so that we may still use
+  // the core file for debugging.
+  bool proc_files_usable =
+      CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
+
+  // Switch back to the original UID/GID.
+  gid_t rgid, egid, sgid;
+  if (getresgid(&rgid, &egid, &sgid) != 0) {
+    PLOG(FATAL) << "Unable to read saved gid";
+  }
+  if (setresgid(sgid, sgid, -1) != 0) {
+    PLOG(FATAL) << "Unable to set real group ID back to saved gid";
+  } else {
+    if (getresgid(&rgid, &egid, &sgid) != 0) {
+      // If the groups cannot be read at this point, the rgid variable will
+      // contain the previously read group ID from before changing it.  This
+      // will cause the chown call below to set the incorrect group for
+      // non-root crashes.  But do not treat this as a fatal error, so that
+      // the rest of the collection will continue for potential manual
+      // collection by a developer.
+      PLOG(ERROR) << "Unable to read real group ID after setting it";
+    }
+  }
+
+  uid_t ruid, euid, suid;
+  if (getresuid(&ruid, &euid, &suid) != 0) {
+    PLOG(FATAL) << "Unable to read saved uid";
+  }
+  if (setresuid(suid, suid, -1) != 0) {
+    PLOG(FATAL) << "Unable to set real user ID back to saved uid";
+  } else {
+    if (getresuid(&ruid, &euid, &suid) != 0) {
+      // If the user ID cannot be read at this point, the ruid variable will
+      // contain the previously read user ID from before changing it.  This
+      // will cause the chown call below to set the incorrect user for
+      // non-root crashes.  But do not treat this as a fatal error, so that
+      // the rest of the collection will continue for potential manual
+      // collection by a developer.
+      PLOG(ERROR) << "Unable to read real user ID after setting it";
+    }
+  }
+
+  if (!CopyStdinToCoreFile(core_path)) {
+    return kErrorReadCoreData;
+  }
+
+  if (!proc_files_usable) {
+    LOG(INFO) << "Skipped converting core file to minidump due to "
+              << "unusable proc files";
+    return kErrorUnusableProcFiles;
+  }
+
+  ErrorType error = ValidateCoreFile(core_path);
+  if (error != kErrorNone) {
+    return error;
+  }
+
+  // Chown the temp container directory back to the original user/group that
+  // crash_reporter is run as, so that additional files can be written to
+  // the temp folder.
+  if (chown(container_dir.value().c_str(), ruid, rgid) < 0) {
+    PLOG(ERROR) << "Could not set owner for " << container_dir.value();
+  }
+
+  if (!RunCoreToMinidump(core_path,
+                         container_dir,  // procfs directory
+                         minidump_path,
+                         container_dir)) {  // temporary directory
+    return kErrorCore2MinidumpConversion;
+  }
+
+  LOG(INFO) << "Stored minidump to " << minidump_path.value();
+  return kErrorNone;
+}
+
+UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash(
+    pid_t pid, const std::string &exec, uid_t supplied_ruid,
+    bool *out_of_capacity) {
+  FilePath crash_path;
+  if (!GetCreatedCrashDirectory(pid, supplied_ruid, &crash_path,
+      out_of_capacity)) {
+    LOG(ERROR) << "Unable to find/create process-specific crash path";
+    return kErrorSystemIssue;
+  }
+
+  // Directory like /tmp/crash_reporter/1234 which contains the
+  // procfs entries and other temporary files used during conversion.
+  FilePath container_dir(StringPrintf("%s/%d", kCoreTempFolder, pid));
+  // Delete a pre-existing directory from crash reporter that may have
+  // been left around for diagnostics from a failed conversion attempt.
+  // If we don't, existing files can cause forking to fail.
+  base::DeleteFile(container_dir, true);
+  std::string dump_basename = FormatDumpBasename(exec, time(nullptr), pid);
+  FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
+  FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
+  FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
+  FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
+
+  if (GetLogContents(FilePath(log_config_path_), exec, log_path))
+    AddCrashMetaData("log", log_path.value());
+
+  brillo::OsReleaseReader reader;
+  reader.Load();
+  std::string value = "undefined";
+  if (!reader.GetString(kBdkVersionKey, &value)) {
+    LOG(ERROR) << "Could not read " << kBdkVersionKey
+               << " from /etc/os-release.d/";
+  }
+  AddCrashMetaData(kBdkVersionKey, value);
+
+  value = "undefined";
+  if (!reader.GetString(kProductIDKey, &value)) {
+    LOG(ERROR) << "Could not read " << kProductIDKey
+               << " from /etc/os-release.d/";
+  }
+  AddCrashMetaData(kProductIDKey, value);
+
+  value = "undefined";
+  if (!reader.GetString(kProductVersionKey, &value)) {
+    LOG(ERROR) << "Could not read " << kProductVersionKey
+               << " from /etc/os-release.d/";
+  }
+  AddCrashMetaData(kProductVersionKey, value);
+
+  ErrorType error_type =
+      ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
+  if (error_type != kErrorNone) {
+    LOG(INFO) << "Leaving core file at " << core_path.value()
+              << " due to conversion error";
+    return error_type;
+  }
+
+  // Here we commit to sending this file.  We must not return false
+  // after this point or we will generate a log report as well as a
+  // crash report.
+  WriteCrashMetaData(meta_path,
+                     exec,
+                     minidump_path.value());
+
+  if (!IsDeveloperImage()) {
+    base::DeleteFile(core_path, false);
+  } else {
+    LOG(INFO) << "Leaving core file at " << core_path.value()
+              << " due to developer image";
+  }
+
+  base::DeleteFile(container_dir, true);
+  return kErrorNone;
+}
+
+bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
+                                         pid_t *pid, int *signal, uid_t *uid,
+                                         gid_t *gid,
+                                         std::string *kernel_supplied_name) {
+  pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(\\d+):(.*)");
+  if (re.FullMatch(crash_attributes, pid, signal, uid, gid,
+                   kernel_supplied_name))
+    return true;
+
+  LOG(INFO) << "Falling back to parsing crash attributes '"
+            << crash_attributes << "' without UID and GID";
+  pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)");
+  *uid = kUnknownUid;
+  *gid = kUnknownGid;
+  return re_without_uid.FullMatch(crash_attributes, pid, signal,
+      kernel_supplied_name);
+}
+
+bool UserCollector::ShouldDump(bool has_owner_consent,
+                               bool is_developer,
+                               std::string *reason) {
+  reason->clear();
+
+  // For developer builds, we always want to keep the crash reports unless
+  // we're testing the crash facilities themselves.  This overrides
+  // feedback.  Crash sending still obeys consent.
+  if (is_developer) {
+    *reason = "developer build - not testing - always dumping";
+    return true;
+  }
+
+  if (!has_owner_consent) {
+    *reason = "ignoring - no consent";
+    return false;
+  }
+
+  *reason = "handling";
+  return true;
+}
+
+bool UserCollector::HandleCrash(const std::string &crash_attributes,
+                                const char *force_exec) {
+  CHECK(initialized_);
+  pid_t pid = 0;
+  int signal = 0;
+  uid_t supplied_ruid = kUnknownUid;
+  gid_t supplied_rgid = kUnknownGid;
+  std::string kernel_supplied_name;
+
+  if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid,
+                            &supplied_rgid, &kernel_supplied_name)) {
+    LOG(ERROR) << "Invalid parameter: --user=" <<  crash_attributes;
+    return false;
+  }
+
+  // Switch to the group and user that ran the crashing binary in order to
+  // access their /proc files.  Do not set suid/sgid, so that we can switch
+  // back after copying the necessary files.
+  if (setresgid(supplied_rgid, supplied_rgid, -1) != 0) {
+    PLOG(FATAL) << "Unable to set real group ID to access process files";
+  }
+  if (setresuid(supplied_ruid, supplied_ruid, -1) != 0) {
+    PLOG(FATAL) << "Unable to set real user ID to access process files";
+  }
+
+  std::string exec;
+  if (force_exec) {
+    exec.assign(force_exec);
+  } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
+    // If we cannot find the exec name, use the kernel supplied name.
+    // We don't always use the kernel's since it truncates the name to
+    // 16 characters.
+    exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
+  }
+
+  // Allow us to test the crash reporting mechanism successfully even if
+  // other parts of the system crash.
+  if (!filter_in_.empty() &&
+      (filter_in_ == "none" ||
+       filter_in_ != exec)) {
+    // We use a different format message to make it more obvious in tests
+    // which crashes are test generated and which are real.
+    LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while "
+                 << "filter_in=" << filter_in_ << ".";
+    return true;
+  }
+
+  std::string reason;
+  bool dump = ShouldDump(is_feedback_allowed_function_(),
+                         IsDeveloperImage(),
+                         &reason);
+
+  LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
+               << "] sig " << signal << ", user " << supplied_ruid
+               << " (" << reason << ")";
+
+  if (dump) {
+    count_crash_function_();
+
+    if (generate_diagnostics_) {
+      bool out_of_capacity = false;
+      ErrorType error_type =
+          ConvertAndEnqueueCrash(pid, exec, supplied_ruid, &out_of_capacity);
+      if (error_type != kErrorNone) {
+        if (!out_of_capacity)
+          EnqueueCollectionErrorLog(pid, error_type, exec);
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h
new file mode 100644 (file)
index 0000000..7261ed4
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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 CRASH_REPORTER_USER_COLLECTOR_H_
+#define CRASH_REPORTER_USER_COLLECTOR_H_
+
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "crash_collector.h"
+
+class SystemLogging;
+
+// User crash collector.
+class UserCollector : public CrashCollector {
+ public:
+  UserCollector();
+
+  // Initialize the user crash collector for detection of crashes,
+  // given a crash counting function, the path to this executable,
+  // metrics collection enabled oracle, and system logger facility.
+  // Crash detection/reporting is not enabled until Enable is called.
+  // |generate_diagnostics| is used to indicate whether or not to try
+  // to generate a minidump from crashes.
+  void Initialize(CountCrashFunction count_crash,
+                  const std::string &our_path,
+                  IsFeedbackAllowedFunction is_metrics_allowed,
+                  bool generate_diagnostics,
+                  bool core2md_failure,
+                  bool directory_failure,
+                  const std::string &filter_in);
+
+  ~UserCollector() override;
+
+  // Enable collection.
+  bool Enable() { return SetUpInternal(true); }
+
+  // Disable collection.
+  bool Disable() { return SetUpInternal(false); }
+
+  // Handle a specific user crash.  Returns true on success.
+  bool HandleCrash(const std::string &crash_attributes,
+                   const char *force_exec);
+
+ private:
+  friend class UserCollectorTest;
+  FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPath);
+  FRIEND_TEST(UserCollectorTest, CopyOffProcFilesBadPid);
+  FRIEND_TEST(UserCollectorTest, CopyOffProcFilesOK);
+  FRIEND_TEST(UserCollectorTest, GetExecutableBaseNameFromPid);
+  FRIEND_TEST(UserCollectorTest, GetFirstLineWithPrefix);
+  FRIEND_TEST(UserCollectorTest, GetIdFromStatus);
+  FRIEND_TEST(UserCollectorTest, GetStateFromStatus);
+  FRIEND_TEST(UserCollectorTest, GetProcessPath);
+  FRIEND_TEST(UserCollectorTest, GetSymlinkTarget);
+  FRIEND_TEST(UserCollectorTest, GetUserInfoFromName);
+  FRIEND_TEST(UserCollectorTest, ParseCrashAttributes);
+  FRIEND_TEST(UserCollectorTest, ShouldDumpChromeOverridesDeveloperImage);
+  FRIEND_TEST(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent);
+  FRIEND_TEST(UserCollectorTest, ShouldDumpUseConsentProductionImage);
+  FRIEND_TEST(UserCollectorTest, ValidateProcFiles);
+  FRIEND_TEST(UserCollectorTest, ValidateCoreFile);
+
+  // Enumeration to pass to GetIdFromStatus.  Must match the order
+  // that the kernel lists IDs in the status file.
+  enum IdKind {
+    kIdReal = 0,  // uid and gid
+    kIdEffective = 1,  // euid and egid
+    kIdSet = 2,  // suid and sgid
+    kIdFileSystem = 3,  // fsuid and fsgid
+    kIdMax
+  };
+
+  enum ErrorType {
+    kErrorNone,
+    kErrorSystemIssue,
+    kErrorReadCoreData,
+    kErrorUnusableProcFiles,
+    kErrorInvalidCoreFile,
+    kErrorUnsupported32BitCoreFile,
+    kErrorCore2MinidumpConversion,
+  };
+
+  static const int kForkProblem = 255;
+
+  // Returns an error type signature for a given |error_type| value,
+  // which is reported to the crash server along with the
+  // crash_reporter-user-collection signature.
+  std::string GetErrorTypeSignature(ErrorType error_type) const;
+
+  bool SetUpInternal(bool enabled);
+
+  // Returns, via |line|, the first line in |lines| that starts with |prefix|.
+  // Returns true if a line is found, or false otherwise.
+  bool GetFirstLineWithPrefix(const std::vector<std::string> &lines,
+                              const char *prefix, std::string *line);
+
+  // Returns the identifier of |kind|, via |id|, found in |status_lines| on
+  // the line starting with |prefix|. |status_lines| contains the lines in
+  // the status file. Returns true if the identifier can be determined.
+  bool GetIdFromStatus(const char *prefix,
+                       IdKind kind,
+                       const std::vector<std::string> &status_lines,
+                       int *id);
+
+  // Returns the process state, via |state|, found in |status_lines|, which
+  // contains the lines in the status file. Returns true if the process state
+  // can be determined.
+  bool GetStateFromStatus(const std::vector<std::string> &status_lines,
+                          std::string *state);
+
+  void LogCollectionError(const std::string &error_message);
+  void EnqueueCollectionErrorLog(pid_t pid, ErrorType error_type,
+                                 const std::string &exec_name);
+
+  bool CopyOffProcFiles(pid_t pid, const base::FilePath &container_dir);
+
+  // Validates the proc files at |container_dir| and returns true if they
+  // are usable for the core-to-minidump conversion later. For instance, if
+  // a process is reaped by the kernel before the copying of its proc files
+  // takes place, some proc files like /proc/<pid>/maps may contain nothing
+  // and thus become unusable.
+  bool ValidateProcFiles(const base::FilePath &container_dir) const;
+
+  // Validates the core file at |core_path| and returns kErrorNone if
+  // the file contains the ELF magic bytes and an ELF class that matches the
+  // platform (i.e. 32-bit ELF on a 32-bit platform or 64-bit ELF on a 64-bit
+  // platform), which is due to the limitation in core2md. It returns an error
+  // type otherwise.
+  ErrorType ValidateCoreFile(const base::FilePath &core_path) const;
+
+  // Determines the crash directory for given pid based on pid's owner,
+  // and creates the directory if necessary with appropriate permissions.
+  // Returns true whether or not directory needed to be created, false on
+  // any failure.
+  bool GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid,
+                                base::FilePath *crash_file_path,
+                                bool *out_of_capacity);
+  bool CopyStdinToCoreFile(const base::FilePath &core_path);
+  bool RunCoreToMinidump(const base::FilePath &core_path,
+                         const base::FilePath &procfs_directory,
+                         const base::FilePath &minidump_path,
+                         const base::FilePath &temp_directory);
+  ErrorType ConvertCoreToMinidump(pid_t pid,
+                                  const base::FilePath &container_dir,
+                                  const base::FilePath &core_path,
+                                  const base::FilePath &minidump_path);
+  ErrorType ConvertAndEnqueueCrash(pid_t pid, const std::string &exec_name,
+                                   uid_t supplied_ruid, bool *out_of_capacity);
+  bool ParseCrashAttributes(const std::string &crash_attributes,
+                            pid_t *pid, int *signal, uid_t *uid, gid_t *gid,
+                            std::string *kernel_supplied_name);
+
+  bool ShouldDump(bool has_owner_consent,
+                  bool is_developer,
+                  std::string *reason);
+
+  bool generate_diagnostics_;
+  std::string our_path_;
+  bool initialized_;
+
+  bool core2md_failure_;
+  bool directory_failure_;
+  std::string filter_in_;
+
+  static const char *kUserId;
+  static const char *kGroupId;
+
+  DISALLOW_COPY_AND_ASSIGN(UserCollector);
+};
+
+#endif  // CRASH_REPORTER_USER_COLLECTOR_H_
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
new file mode 100644 (file)
index 0000000..72e61e6
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+ * 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 "user_collector.h"
+
+#include <elf.h>
+#include <sys/cdefs.h>  // For __WORDSIZE
+#include <unistd.h>
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/string_split.h>
+#include <brillo/syslog_logging.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using base::FilePath;
+using brillo::FindLog;
+
+namespace {
+
+int s_crashes = 0;
+bool s_metrics = false;
+
+const char kFilePath[] = "/my/path";
+
+// Keep in sync with UserCollector::ShouldDump.
+const char kChromeIgnoreMsg[] =
+    "ignoring call by kernel - chrome crash; "
+    "waiting for chrome to call us directly";
+
+void CountCrash() {
+  ++s_crashes;
+}
+
+bool IsMetrics() {
+  return s_metrics;
+}
+
+}  // namespace
+
+class UserCollectorMock : public UserCollector {
+ public:
+  MOCK_METHOD0(SetUpDBus, void());
+};
+
+class UserCollectorTest : public ::testing::Test {
+  void SetUp() {
+    s_crashes = 0;
+
+    EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
+
+    collector_.Initialize(CountCrash,
+                          kFilePath,
+                          IsMetrics,
+                          false,
+                          false,
+                          false,
+                          "");
+    base::DeleteFile(FilePath("test"), true);
+    mkdir("test", 0777);
+    pid_ = getpid();
+    brillo::ClearLog();
+  }
+
+ protected:
+  void ExpectFileEquals(const char *golden,
+                        const FilePath &file_path) {
+    std::string contents;
+    EXPECT_TRUE(base::ReadFileToString(file_path, &contents));
+    EXPECT_EQ(golden, contents);
+  }
+
+  std::vector<std::string> SplitLines(const std::string &lines) const {
+    std::vector<std::string> result;
+    base::SplitString(lines, '\n', &result);
+    return result;
+  }
+
+  UserCollectorMock collector_;
+  pid_t pid_;
+};
+
+TEST_F(UserCollectorTest, ParseCrashAttributes) {
+  pid_t pid;
+  int signal;
+  uid_t uid;
+  gid_t gid;
+  std::string exec_name;
+  EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:2000:foobar",
+      &pid, &signal, &uid, &gid, &exec_name));
+  EXPECT_EQ(123456, pid);
+  EXPECT_EQ(11, signal);
+  EXPECT_EQ(1000, uid);
+  EXPECT_EQ(2000, gid);
+  EXPECT_EQ("foobar", exec_name);
+  EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo",
+      &pid, &signal, &uid, &gid, &exec_name));
+  EXPECT_EQ(4321, pid);
+  EXPECT_EQ(6, signal);
+  EXPECT_EQ(-1, uid);
+  EXPECT_EQ(-1, gid);
+  EXPECT_EQ("barfoo", exec_name);
+
+  EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11",
+      &pid, &signal, &uid, &gid, &exec_name));
+
+  EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:exec:extra",
+      &pid, &signal, &uid, &gid, &exec_name));
+  EXPECT_EQ("exec:extra", exec_name);
+
+  EXPECT_FALSE(collector_.ParseCrashAttributes("12345p:11:foobar",
+      &pid, &signal, &uid, &gid, &exec_name));
+
+  EXPECT_FALSE(collector_.ParseCrashAttributes("123456:1 :foobar",
+      &pid, &signal, &uid, &gid, &exec_name));
+
+  EXPECT_FALSE(collector_.ParseCrashAttributes("123456::foobar",
+      &pid, &signal, &uid, &gid, &exec_name));
+}
+
+TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) {
+  std::string reason;
+  EXPECT_TRUE(collector_.ShouldDump(false, true, &reason));
+  EXPECT_EQ("developer build - not testing - always dumping", reason);
+
+  // When running a crash test, behave as normal.
+  EXPECT_FALSE(collector_.ShouldDump(false, false, &reason));
+  EXPECT_EQ("ignoring - no consent", reason);
+}
+
+TEST_F(UserCollectorTest, ShouldDumpUseConsentProductionImage) {
+  std::string result;
+  EXPECT_FALSE(collector_.ShouldDump(false, false, &result));
+  EXPECT_EQ("ignoring - no consent", result);
+
+  EXPECT_TRUE(collector_.ShouldDump(true, false, &result));
+  EXPECT_EQ("handling", result);
+}
+
+TEST_F(UserCollectorTest, HandleCrashWithoutConsent) {
+  s_metrics = false;
+  collector_.HandleCrash("20:10:ignored", "foobar");
+  EXPECT_TRUE(FindLog(
+      "Received crash notification for foobar[20] sig 10"));
+  ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(UserCollectorTest, HandleNonChromeCrashWithConsent) {
+  s_metrics = true;
+  collector_.HandleCrash("5:2:ignored", "chromeos-wm");
+  EXPECT_TRUE(FindLog(
+      "Received crash notification for chromeos-wm[5] sig 2"));
+  ASSERT_EQ(s_crashes, 1);
+}
+
+TEST_F(UserCollectorTest, HandleChromeCrashWithConsent) {
+  s_metrics = true;
+  collector_.HandleCrash("5:2:ignored", "chrome");
+  EXPECT_TRUE(FindLog(
+      "Received crash notification for chrome[5] sig 2"));
+  EXPECT_TRUE(FindLog(kChromeIgnoreMsg));
+  ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(UserCollectorTest, HandleSuppliedChromeCrashWithConsent) {
+  s_metrics = true;
+  collector_.HandleCrash("0:2:chrome", nullptr);
+  EXPECT_TRUE(FindLog(
+      "Received crash notification for supplied_chrome[0] sig 2"));
+  EXPECT_TRUE(FindLog(kChromeIgnoreMsg));
+  ASSERT_EQ(s_crashes, 0);
+}
+
+TEST_F(UserCollectorTest, GetProcessPath) {
+  FilePath path = collector_.GetProcessPath(100);
+  ASSERT_EQ("/proc/100", path.value());
+}
+
+TEST_F(UserCollectorTest, GetSymlinkTarget) {
+  FilePath result;
+  ASSERT_FALSE(collector_.GetSymlinkTarget(FilePath("/does_not_exist"),
+                                           &result));
+  ASSERT_TRUE(FindLog(
+      "Readlink failed on /does_not_exist with 2"));
+  std::string long_link;
+  for (int i = 0; i < 50; ++i)
+    long_link += "0123456789";
+  long_link += "/gold";
+
+  for (size_t len = 1; len <= long_link.size(); ++len) {
+    std::string this_link;
+    static const char kLink[] = "test/this_link";
+    this_link.assign(long_link.c_str(), len);
+    ASSERT_EQ(len, this_link.size());
+    unlink(kLink);
+    ASSERT_EQ(0, symlink(this_link.c_str(), kLink));
+    ASSERT_TRUE(collector_.GetSymlinkTarget(FilePath(kLink), &result));
+    ASSERT_EQ(this_link, result.value());
+  }
+}
+
+TEST_F(UserCollectorTest, GetExecutableBaseNameFromPid) {
+  std::string base_name;
+  EXPECT_FALSE(collector_.GetExecutableBaseNameFromPid(0, &base_name));
+  EXPECT_TRUE(FindLog(
+      "Readlink failed on /proc/0/exe with 2"));
+  EXPECT_TRUE(FindLog(
+      "GetSymlinkTarget failed - Path /proc/0 DirectoryExists: 0"));
+  EXPECT_TRUE(FindLog("stat /proc/0/exe failed: -1 2"));
+
+  brillo::ClearLog();
+  pid_t my_pid = getpid();
+  EXPECT_TRUE(collector_.GetExecutableBaseNameFromPid(my_pid, &base_name));
+  EXPECT_FALSE(FindLog("Readlink failed"));
+  EXPECT_EQ("crash_reporter_test", base_name);
+}
+
+TEST_F(UserCollectorTest, GetFirstLineWithPrefix) {
+  std::vector<std::string> lines;
+  std::string line;
+
+  EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line));
+  EXPECT_EQ("", line);
+
+  lines.push_back("Name:\tls");
+  lines.push_back("State:\tR (running)");
+  lines.push_back(" Foo:\t1000");
+
+  line.clear();
+  EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "Name:", &line));
+  EXPECT_EQ(lines[0], line);
+
+  line.clear();
+  EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, "State:", &line));
+  EXPECT_EQ(lines[1], line);
+
+  line.clear();
+  EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Foo:", &line));
+  EXPECT_EQ("", line);
+
+  line.clear();
+  EXPECT_TRUE(collector_.GetFirstLineWithPrefix(lines, " Foo:", &line));
+  EXPECT_EQ(lines[2], line);
+
+  line.clear();
+  EXPECT_FALSE(collector_.GetFirstLineWithPrefix(lines, "Bar:", &line));
+  EXPECT_EQ("", line);
+}
+
+TEST_F(UserCollectorTest, GetIdFromStatus) {
+  int id = 1;
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                          UserCollector::kIdEffective,
+                                          SplitLines("nothing here"),
+                                          &id));
+  EXPECT_EQ(id, 1);
+
+  // Not enough parameters.
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                          UserCollector::kIdReal,
+                                          SplitLines("line 1\nUid:\t1\n"),
+                                          &id));
+
+  const std::vector<std::string> valid_contents =
+      SplitLines("\nUid:\t1\t2\t3\t4\nGid:\t5\t6\t7\t8\n");
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                         UserCollector::kIdReal,
+                                         valid_contents,
+                                         &id));
+  EXPECT_EQ(1, id);
+
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                         UserCollector::kIdEffective,
+                                         valid_contents,
+                                         &id));
+  EXPECT_EQ(2, id);
+
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                         UserCollector::kIdFileSystem,
+                                         valid_contents,
+                                         &id));
+  EXPECT_EQ(4, id);
+
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+                                         UserCollector::kIdEffective,
+                                         valid_contents,
+                                         &id));
+  EXPECT_EQ(6, id);
+
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+                                         UserCollector::kIdSet,
+                                         valid_contents,
+                                         &id));
+  EXPECT_EQ(7, id);
+
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+                                          UserCollector::IdKind(5),
+                                          valid_contents,
+                                          &id));
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kGroupId,
+                                          UserCollector::IdKind(-1),
+                                          valid_contents,
+                                          &id));
+
+  // Fail if junk after number
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                          UserCollector::kIdReal,
+                                          SplitLines("Uid:\t1f\t2\t3\t4\n"),
+                                          &id));
+  EXPECT_TRUE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                         UserCollector::kIdReal,
+                                         SplitLines("Uid:\t1\t2\t3\t4\n"),
+                                         &id));
+  EXPECT_EQ(1, id);
+
+  // Fail if more than 4 numbers.
+  EXPECT_FALSE(collector_.GetIdFromStatus(UserCollector::kUserId,
+                                          UserCollector::kIdReal,
+                                          SplitLines("Uid:\t1\t2\t3\t4\t5\n"),
+                                          &id));
+}
+
+TEST_F(UserCollectorTest, GetStateFromStatus) {
+  std::string state;
+  EXPECT_FALSE(collector_.GetStateFromStatus(SplitLines("nothing here"),
+                                             &state));
+  EXPECT_EQ("", state);
+
+  EXPECT_TRUE(collector_.GetStateFromStatus(SplitLines("State:\tR (running)"),
+                                            &state));
+  EXPECT_EQ("R (running)", state);
+
+  EXPECT_TRUE(collector_.GetStateFromStatus(
+      SplitLines("Name:\tls\nState:\tZ (zombie)\n"), &state));
+  EXPECT_EQ("Z (zombie)", state);
+}
+
+TEST_F(UserCollectorTest, GetUserInfoFromName) {
+  gid_t gid = 100;
+  uid_t uid = 100;
+  EXPECT_TRUE(collector_.GetUserInfoFromName("root", &uid, &gid));
+  EXPECT_EQ(0, uid);
+  EXPECT_EQ(0, gid);
+}
+
+TEST_F(UserCollectorTest, CopyOffProcFilesBadPath) {
+  // Try a path that is not writable.
+  ASSERT_FALSE(collector_.CopyOffProcFiles(pid_, FilePath("/bad/path")));
+  EXPECT_TRUE(FindLog("Could not create /bad/path"));
+}
+
+TEST_F(UserCollectorTest, CopyOffProcFilesBadPid) {
+  FilePath container_path("test/container");
+  ASSERT_FALSE(collector_.CopyOffProcFiles(0, container_path));
+  EXPECT_TRUE(FindLog("Path /proc/0 does not exist"));
+}
+
+TEST_F(UserCollectorTest, CopyOffProcFilesOK) {
+  FilePath container_path("test/container");
+  ASSERT_TRUE(collector_.CopyOffProcFiles(pid_, container_path));
+  EXPECT_FALSE(FindLog("Could not copy"));
+  static struct {
+    const char *name;
+    bool exists;
+  } expectations[] = {
+    { "auxv", true },
+    { "cmdline", true },
+    { "environ", true },
+    { "maps", true },
+    { "mem", false },
+    { "mounts", false },
+    { "sched", false },
+    { "status", true }
+  };
+  for (unsigned i = 0; i < sizeof(expectations)/sizeof(expectations[0]); ++i) {
+    EXPECT_EQ(expectations[i].exists,
+              base::PathExists(
+                  container_path.Append(expectations[i].name)));
+  }
+}
+
+TEST_F(UserCollectorTest, ValidateProcFiles) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  FilePath container_dir = temp_dir.path();
+
+  // maps file not exists (i.e. GetFileSize fails)
+  EXPECT_FALSE(collector_.ValidateProcFiles(container_dir));
+
+  // maps file is empty
+  FilePath maps_file = container_dir.Append("maps");
+  ASSERT_EQ(0, base::WriteFile(maps_file, nullptr, 0));
+  ASSERT_TRUE(base::PathExists(maps_file));
+  EXPECT_FALSE(collector_.ValidateProcFiles(container_dir));
+
+  // maps file is not empty
+  const char data[] = "test data";
+  ASSERT_EQ(sizeof(data), base::WriteFile(maps_file, data, sizeof(data)));
+  ASSERT_TRUE(base::PathExists(maps_file));
+  EXPECT_TRUE(collector_.ValidateProcFiles(container_dir));
+}
+
+TEST_F(UserCollectorTest, ValidateCoreFile) {
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  FilePath container_dir = temp_dir.path();
+  FilePath core_file = container_dir.Append("core");
+
+  // Core file does not exist
+  EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
+            collector_.ValidateCoreFile(core_file));
+  char e_ident[EI_NIDENT];
+  e_ident[EI_MAG0] = ELFMAG0;
+  e_ident[EI_MAG1] = ELFMAG1;
+  e_ident[EI_MAG2] = ELFMAG2;
+  e_ident[EI_MAG3] = ELFMAG3;
+#if __WORDSIZE == 32
+  e_ident[EI_CLASS] = ELFCLASS32;
+#elif __WORDSIZE == 64
+  e_ident[EI_CLASS] = ELFCLASS64;
+#else
+#error Unknown/unsupported value of __WORDSIZE.
+#endif
+
+  // Core file has the expected header
+  ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
+  EXPECT_EQ(UserCollector::kErrorNone,
+            collector_.ValidateCoreFile(core_file));
+
+#if __WORDSIZE == 64
+  // 32-bit core file on 64-bit platform
+  e_ident[EI_CLASS] = ELFCLASS32;
+  ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
+  EXPECT_EQ(UserCollector::kErrorUnsupported32BitCoreFile,
+            collector_.ValidateCoreFile(core_file));
+  e_ident[EI_CLASS] = ELFCLASS64;
+#endif
+
+  // Invalid core files
+  ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident) - 1));
+  EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
+            collector_.ValidateCoreFile(core_file));
+
+  e_ident[EI_MAG0] = 0;
+  ASSERT_TRUE(base::WriteFile(core_file, e_ident, sizeof(e_ident)));
+  EXPECT_EQ(UserCollector::kErrorInvalidCoreFile,
+            collector_.ValidateCoreFile(core_file));
+}
diff --git a/crash_reporter/warn_collector.l b/crash_reporter/warn_collector.l
new file mode 100644 (file)
index 0000000..70ab25c
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * 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.
+ *
+ * This flex program reads /var/log/messages as it grows and saves kernel
+ * warnings to files.  It keeps track of warnings it has seen (based on
+ * file/line only, ignoring differences in the stack trace), and reports only
+ * the first warning of each kind, but maintains a count of all warnings by
+ * using their hashes as buckets in a UMA sparse histogram.  It also invokes
+ * the crash collector, which collects the warnings and prepares them for later
+ * shipment to the crash server.
+ */
+
+%option noyywrap
+
+%{
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <sys/inotify.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "metrics/c_metrics_library.h"
+
+int WarnStart(void);
+void WarnEnd(void);
+void WarnInput(char *buf, yy_size_t *result, size_t max_size);
+
+#define YY_INPUT(buf, result, max_size) WarnInput(buf, &result, max_size)
+
+%}
+
+/* Define a few useful regular expressions. */
+
+D               [0-9]
+PREFIX          .*" kernel: [ "*{D}+"."{D}+"]"
+CUT_HERE        {PREFIX}" ------------[ cut here".*
+WARNING         {PREFIX}" WARNING: at "
+END_TRACE       {PREFIX}" ---[ end trace".*
+
+/* Use exclusive start conditions. */
+%x PRE_WARN WARN
+
+%%
+ /* The scanner itself. */
+
+^{CUT_HERE}\n{WARNING}          BEGIN(PRE_WARN);
+.|\n                            /* ignore all other input in state 0 */
+<PRE_WARN>[^ ]+.[^ ]+\n         if (WarnStart()) {
+                                  /* yytext is
+                                     "file:line func+offset/offset()\n" */
+                                  BEGIN(WARN); ECHO;
+                                } else {
+                                  BEGIN(0);
+                                }
+
+ /* Assume the warning ends at the "end trace" line */
+<WARN>^{END_TRACE}\n            ECHO; BEGIN(0); WarnEnd();
+<WARN>^.*\n                     ECHO;
+
+%%
+
+#define HASH_BITMAP_SIZE        (1 << 15)  /* size in bits */
+#define HASH_BITMAP_MASK        (HASH_BITMAP_SIZE - 1)
+
+const char warn_hist_name[] = "Platform.KernelWarningHashes";
+uint32_t hash_bitmap[HASH_BITMAP_SIZE / 32];
+CMetricsLibrary metrics_library;
+
+const char *prog_name;          /* the name of this program */
+int yyin_fd;                    /* instead of FILE *yyin to avoid buffering */
+int i_fd;                       /* for inotify, to detect file changes */
+int testing;                    /* 1 if running test */
+int filter;                     /* 1 when using as filter (for development) */
+int fifo;                       /* 1 when reading from fifo (for devel) */
+int draining;                   /* 1 when draining renamed log file */
+
+const char *msg_path = "/var/log/messages";
+const char warn_dump_dir[]  = "/var/run/kwarn";
+const char *warn_dump_path = "/var/run/kwarn/warning";
+const char *crash_reporter_command;
+
+__attribute__((__format__(__printf__, 1, 2)))
+static void Die(const char *format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  fprintf(stderr, "%s: ", prog_name);
+  vfprintf(stderr, format, ap);
+  exit(1);
+}
+
+static void RunCrashReporter(void) {
+  int status = system(crash_reporter_command);
+  if (status != 0)
+    Die("%s exited with status %d\n", crash_reporter_command, status);
+}
+
+static uint32_t StringHash(const char *string) {
+  uint32_t hash = 0;
+  while (*string != '\0') {
+    hash = (hash << 5) + hash + *string++;
+  }
+  return hash;
+}
+
+/* We expect only a handful of different warnings per boot session, so the
+ * probability of a collision is very low, and statistically it won't matter
+ * (unless warnings with the same hash also happens in tandem, which is even
+ * rarer).
+ */
+static int HashSeen(uint32_t hash) {
+  int word_index = (hash & HASH_BITMAP_MASK) / 32;
+  int bit_index = (hash & HASH_BITMAP_MASK) % 32;
+  return hash_bitmap[word_index] & 1 << bit_index;
+}
+
+static void SetHashSeen(uint32_t hash) {
+  int word_index = (hash & HASH_BITMAP_MASK) / 32;
+  int bit_index = (hash & HASH_BITMAP_MASK) % 32;
+  hash_bitmap[word_index] |= 1 << bit_index;
+}
+
+#pragma GCC diagnostic ignored "-Wwrite-strings"
+int WarnStart(void) {
+  uint32_t hash;
+  char *spacep;
+
+  if (filter)
+    return 1;
+
+  hash = StringHash(yytext);
+  if (!(testing || fifo || filter)) {
+    CMetricsLibrarySendSparseToUMA(metrics_library, warn_hist_name, (int) hash);
+  }
+  if (HashSeen(hash))
+    return 0;
+  SetHashSeen(hash);
+
+  yyout = fopen(warn_dump_path, "w");
+  if (yyout == NULL)
+    Die("fopen %s failed: %s\n", warn_dump_path, strerror(errno));
+  spacep = strchr(yytext, ' ');
+  if (spacep == NULL || spacep[1] == '\0')
+    spacep = "unknown-function";
+  fprintf(yyout, "%08x-%s\n", hash, spacep + 1);
+  return 1;
+}
+
+void WarnEnd(void) {
+  if (filter)
+    return;
+  fclose(yyout);
+  yyout = stdout;               /* for debugging */
+  RunCrashReporter();
+}
+
+static void WarnOpenInput(const char *path) {
+  yyin_fd = open(path, O_RDONLY);
+  if (yyin_fd < 0)
+    Die("could not open %s: %s\n", path, strerror(errno));
+  if (!fifo) {
+    /* Go directly to the end of the file.  We don't want to parse the same
+     * warnings multiple times on reboot/restart.  We might miss some
+     * warnings, but so be it---it's too hard to keep track reliably of the
+     * last parsed position in the syslog.
+     */
+    if (lseek(yyin_fd, 0, SEEK_END) < 0)
+      Die("could not lseek %s: %s\n", path, strerror(errno));
+    /* Set up notification of file growth and rename. */
+    i_fd = inotify_init();
+    if (i_fd < 0)
+      Die("inotify_init: %s\n", strerror(errno));
+    if (inotify_add_watch(i_fd, path, IN_MODIFY | IN_MOVE_SELF) < 0)
+      Die("inotify_add_watch: %s\n", strerror(errno));
+  }
+}
+
+/* We replace the default YY_INPUT() for the following reasons:
+ *
+ * 1.  We want to read data as soon as it becomes available, but the default
+ * YY_INPUT() uses buffered I/O.
+ *
+ * 2.  We want to block on end of input and wait for the file to grow.
+ *
+ * 3.  We want to detect log rotation, and reopen the input file as needed.
+ */
+void WarnInput(char *buf, yy_size_t *result, size_t max_size) {
+  while (1) {
+    ssize_t ret = read(yyin_fd, buf, max_size);
+    if (ret < 0)
+      Die("read: %s", strerror(errno));
+    *result = ret;
+    if (*result > 0 || fifo || filter)
+      return;
+    if (draining) {
+      /* Assume we're done with this log, and move to next
+       * log.  Rsyslogd may keep writing to the old log file
+       * for a while, but we don't care since we don't have
+       * to be exact.
+       */
+      close(yyin_fd);
+      if (YYSTATE == WARN) {
+        /* Be conservative in case we lose the warn
+         * terminator during the switch---or we may
+         * collect personally identifiable information.
+         */
+        WarnEnd();
+      }
+      BEGIN(0);        /* see above comment */
+      sleep(1);        /* avoid race with log rotator */
+      WarnOpenInput(msg_path);
+      draining = 0;
+      continue;
+    }
+    /* Nothing left to read, so we must wait. */
+    struct inotify_event event;
+    while (1) {
+      int n = read(i_fd, &event, sizeof(event));
+      if (n <= 0) {
+        if (errno == EINTR)
+          continue;
+        else
+          Die("inotify: %s\n", strerror(errno));
+      } else
+        break;
+    }
+    if (event.mask & IN_MOVE_SELF) {
+      /* The file has been renamed.  Before switching
+       * to the new one, we process any remaining
+       * content of this file.
+       */
+      draining = 1;
+    }
+  }
+}
+
+int main(int argc, char **argv) {
+  int result;
+  struct passwd *user;
+  prog_name = argv[0];
+
+  if (argc == 2 && strcmp(argv[1], "--test") == 0)
+    testing = 1;
+  else if (argc == 2 && strcmp(argv[1], "--filter") == 0)
+    filter = 1;
+  else if (argc == 2 && strcmp(argv[1], "--fifo") == 0) {
+    fifo = 1;
+  } else if (argc != 1) {
+    fprintf(stderr,
+            "usage: %s [single-flag]\n"
+            "flags (for testing only):\n"
+            "--fifo\tinput is fifo \"fifo\", output is stdout\n"
+            "--filter\tinput is stdin, output is stdout\n"
+            "--test\trun self-test\n",
+            prog_name);
+    exit(1);
+  }
+
+  metrics_library = CMetricsLibraryNew();
+  CMetricsLibraryInit(metrics_library);
+
+  crash_reporter_command = testing ?
+    "./warn_collector_test_reporter.sh" :
+    "/sbin/crash_reporter --kernel_warning";
+
+  /* When filtering with --filter (for development) use stdin for input.
+   * Otherwise read input from a file or a fifo.
+   */
+  yyin_fd = fileno(stdin);
+  if (testing) {
+    msg_path = "messages";
+    warn_dump_path = "warning";
+  }
+  if (fifo) {
+    msg_path = "fifo";
+  }
+  if (!filter) {
+    WarnOpenInput(msg_path);
+  }
+
+  /* Create directory for dump file.  Still need to be root here. */
+  unlink(warn_dump_path);
+  if (!testing && !fifo && !filter) {
+    rmdir(warn_dump_dir);
+    result = mkdir(warn_dump_dir, 0755);
+    if (result < 0)
+      Die("could not create %s: %s\n",
+          warn_dump_dir, strerror(errno));
+  }
+
+  if (0) {
+    /* TODO(semenzato): put this back in once we decide it's safe
+     * to make /var/spool/crash rwxrwxrwx root, or use a different
+     * owner and setuid for the crash reporter as well.
+     */
+
+    /* Get low privilege uid, gid. */
+    user = getpwnam("chronos");
+    if (user == NULL)
+      Die("getpwnam failed\n");
+
+    /* Change dump directory ownership. */
+    if (chown(warn_dump_dir, user->pw_uid, user->pw_gid) < 0)
+      Die("chown: %s\n", strerror(errno));
+
+    /* Drop privileges. */
+    if (setuid(user->pw_uid) < 0) {
+      Die("setuid: %s\n", strerror(errno));
+    }
+  }
+
+  /* Go! */
+  return yylex();
+}
+
+/* Flex should really know not to generate these functions.
+ */
+void UnusedFunctionWarningSuppressor(void) {
+  yyunput(0, 0);
+}
diff --git a/crash_reporter/warn_collector_test.c b/crash_reporter/warn_collector_test.c
new file mode 100644 (file)
index 0000000..7ebe0a8
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+/*
+ * Test driver for the warn_collector daemon.
+ */
+#include <stdlib.h>
+
+int main(int ac, char **av) {
+  int status = system("exec \"${SRC}\"/warn_collector_test.sh");
+  return status < 0 ? EXIT_FAILURE : WEXITSTATUS(status);
+}
diff --git a/crash_reporter/warn_collector_test.sh b/crash_reporter/warn_collector_test.sh
new file mode 100755 (executable)
index 0000000..a5af16c
--- /dev/null
@@ -0,0 +1,90 @@
+#! /bin/bash
+
+# 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.
+
+# Test for warn_collector.  Run the warn collector in the background, emulate
+# the kernel by appending lines to the log file "messages", and observe the log
+# of the (fake) crash reporter each time is run by the warn collector daemon.
+
+set -e
+
+fail() {
+  printf '[ FAIL ] %b\n' "$*"
+  exit 1
+}
+
+if [[ -z ${SYSROOT} ]]; then
+  fail "SYSROOT must be set for this test to work"
+fi
+: ${OUT:=${PWD}}
+cd "${OUT}"
+PATH=${OUT}:${PATH}
+TESTLOG="${OUT}/warn-test-log"
+
+echo "Testing: $(which warn_collector)"
+
+cleanup() {
+  # Kill daemon (if started) on exit
+  kill %
+}
+
+check_log() {
+  local n_expected=$1
+  if [[ ! -f ${TESTLOG} ]]; then
+    fail "${TESTLOG} was not created"
+  fi
+  if [[ $(wc -l < "${TESTLOG}") -ne ${n_expected} ]]; then
+    fail "expected ${n_expected} lines in ${TESTLOG}, found this instead:
+$(<"${TESTLOG}")"
+  fi
+  if egrep -qv '^[0-9a-f]{8}' "${TESTLOG}"; then
+    fail "found bad lines in ${TESTLOG}:
+$(<"${TESTLOG}")"
+  fi
+}
+
+rm -f "${TESTLOG}"
+cp "${SRC}/warn_collector_test_reporter.sh" .
+cp "${SRC}/TEST_WARNING" .
+cp TEST_WARNING messages
+
+# Start the collector daemon.  With the --test option, the daemon reads input
+# from ./messages, writes the warning into ./warning, and invokes
+# ./warn_collector_test_reporter.sh to report the warning.
+warn_collector --test &
+trap cleanup EXIT
+
+# After a while, check that the first warning has been collected.
+sleep 1
+check_log 1
+
+# Add the same warning to messages, verify that it is NOT collected
+cat TEST_WARNING >> messages
+sleep 1
+check_log 1
+
+# Add a slightly different warning to messages, check that it is collected.
+sed s/intel_dp.c/intel_xx.c/ < TEST_WARNING >> messages
+sleep 1
+check_log 2
+
+# Emulate log rotation, add a warning, and check.
+mv messages messages.1
+sed s/intel_dp.c/intel_xy.c/ < TEST_WARNING > messages
+sleep 2
+check_log 3
+
+# Success!
+exit 0
diff --git a/crash_reporter/warn_collector_test_reporter.sh b/crash_reporter/warn_collector_test_reporter.sh
new file mode 100755 (executable)
index 0000000..b6096ed
--- /dev/null
@@ -0,0 +1,27 @@
+#! /bin/sh
+
+# 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.
+
+# Replacement for the crash reporter, for testing.  Log the first line of the
+# "warning" file, which by convention contains the warning hash, and remove the
+# file.
+
+set -e
+
+exec 1>> warn-test-log
+exec 2>> warn-test-log
+
+head -1 warning
+rm warning
index 6cfb541..de0f943 100644 (file)
@@ -27,6 +27,9 @@ LOCAL_SRC_FILES_x86_64 := x86_64/machine.cpp
 
 LOCAL_CPPFLAGS := $(common_cppflags)
 
+LOCAL_INIT_RC_32 := debuggerd.rc
+LOCAL_INIT_RC_64 := debuggerd64.rc
+
 ifeq ($(TARGET_IS_64_BIT),true)
 LOCAL_CPPFLAGS += -DTARGET_IS_64_BIT
 endif
@@ -77,12 +80,12 @@ include $(BUILD_EXECUTABLE)
 
 debuggerd_test_src_files := \
     utility.cpp \
-    test/dump_maps_test.cpp \
     test/dump_memory_test.cpp \
     test/elf_fake.cpp \
     test/log_fake.cpp \
     test/property_fake.cpp \
     test/ptrace_fake.cpp \
+    test/tombstone_test.cpp \
     test/selinux_fake.cpp \
 
 debuggerd_shared_libraries := \
@@ -96,6 +99,7 @@ debuggerd_c_includes := \
 debuggerd_cpp_flags := \
     $(common_cppflags) \
     -Wno-missing-field-initializers \
+    -fno-rtti \
 
 # Only build the host tests on linux.
 ifeq ($(HOST_OS),linux)
index b7d6997..78c2306 100644 (file)
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <stdint.h>
 #include <string.h>
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -28,7 +31,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs regs;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &regs)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -48,7 +51,7 @@ void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
 void dump_registers(log_t* log, pid_t tid) {
   pt_regs r;
   if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -68,7 +71,7 @@ void dump_registers(log_t* log, pid_t tid) {
 
   user_vfp vfp_regs;
   if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
-    _LOG(log, logtype::ERROR, "cannot get FP registers: %s\n", strerror(errno));
+    ALOGE("cannot get FP registers: %s\n", strerror(errno));
     return;
   }
 
index 2e097da..e7bf79a 100644 (file)
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <elf.h>
 #include <errno.h>
 #include <stdint.h>
@@ -23,6 +25,7 @@
 #include <sys/uio.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -34,8 +37,7 @@ void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   io.iov_len = sizeof(regs);
 
   if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) {
-    _LOG(log, logtype::ERROR, "%s: ptrace failed to get registers: %s",
-         __func__, strerror(errno));
+    ALOGE("ptrace failed to get registers: %s", strerror(errno));
     return;
   }
 
@@ -57,7 +59,7 @@ void dump_registers(log_t* log, pid_t tid) {
   io.iov_len = sizeof(r);
 
   if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) {
-    _LOG(log, logtype::ERROR, "ptrace error: %s\n", strerror(errno));
+    ALOGE("ptrace error: %s\n", strerror(errno));
     return;
   }
 
@@ -81,7 +83,7 @@ void dump_registers(log_t* log, pid_t tid) {
   io.iov_len = sizeof(f);
 
   if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) {
-    _LOG(log, logtype::ERROR, "ptrace error: %s\n", strerror(errno));
+    ALOGE("ptrace error: %s\n", strerror(errno));
     return;
   }
 
index b8084c5..b46f8f4 100644 (file)
@@ -105,7 +105,7 @@ static void dump_thread(
   }
 
   if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
-    _LOG(log, logtype::ERROR, "ptrace detach from %d failed: %s\n", tid, strerror(errno));
+    ALOGE("ptrace detach from %d failed: %s\n", tid, strerror(errno));
     *detach_failed = true;
   }
 }
index af86fe9..75f070b 100644 (file)
@@ -51,6 +51,11 @@ __attribute__ ((noinline)) static int smash_stack(volatile int* plen) {
     return 0;
 }
 
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winfinite-recursion"
+#endif
+
 static void* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.
 
 __attribute__((noinline)) static void overflow_stack(void* p) {
@@ -60,6 +65,10 @@ __attribute__((noinline)) static void overflow_stack(void* p) {
     overflow_stack(&buf);
 }
 
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
 static void *noisy(void *x)
 {
     char c = (uintptr_t) x;
@@ -157,12 +166,6 @@ static int do_action(const char* arg)
     } else if (!strcmp(arg, "SIGFPE")) {
         raise(SIGFPE);
         return EXIT_SUCCESS;
-    } else if (!strcmp(arg, "SIGPIPE")) {
-        int pipe_fds[2];
-        pipe(pipe_fds);
-        close(pipe_fds[0]);
-        write(pipe_fds[1], "oops", 4);
-        return EXIT_SUCCESS;
     } else if (!strcmp(arg, "SIGTRAP")) {
         raise(SIGTRAP);
         return EXIT_SUCCESS;
@@ -189,7 +192,6 @@ static int do_action(const char* arg)
     fprintf(stderr, "  LOG_ALWAYS_FATAL      call LOG_ALWAYS_FATAL\n");
     fprintf(stderr, "  LOG_ALWAYS_FATAL_IF   call LOG_ALWAYS_FATAL\n");
     fprintf(stderr, "  SIGFPE                cause a SIGFPE\n");
-    fprintf(stderr, "  SIGPIPE               cause a SIGPIPE\n");
     fprintf(stderr, "  SIGSEGV               cause a SIGSEGV at address 0x0 (synonym: crash)\n");
     fprintf(stderr, "  SIGSEGV-non-null      cause a SIGSEGV at a non-zero address\n");
     fprintf(stderr, "  SIGSEGV-unmapped      mmap/munmap a region of memory and then attempt to access it\n");
index 9c8a41e..1287fb9 100644 (file)
@@ -63,32 +63,18 @@ struct debugger_request_t {
   int32_t original_si_code;
 };
 
-static void wait_for_user_action(const debugger_request_t &request) {
-  // Find out the name of the process that crashed.
-  char path[64];
-  snprintf(path, sizeof(path), "/proc/%d/exe", request.pid);
-
-  char exe[PATH_MAX];
-  int count;
-  if ((count = readlink(path, exe, sizeof(exe) - 1)) == -1) {
-    ALOGE("readlink('%s') failed: %s", path, strerror(errno));
-    strlcpy(exe, "unknown", sizeof(exe));
-  } else {
-    exe[count] = '\0';
-  }
-
+static void wait_for_user_action(const debugger_request_t& request) {
   // Explain how to attach the debugger.
-  ALOGI("********************************************************\n"
+  ALOGI("***********************************************************\n"
         "* Process %d has been suspended while crashing.\n"
-        "* To attach gdbserver for a gdb connection on port 5039\n"
-        "* and start gdbclient:\n"
+        "* To attach gdbserver and start gdb, run this on the host:\n"
         "*\n"
-        "*     gdbclient %s :5039 %d\n"
+        "*     gdbclient %d\n"
         "*\n"
         "* Wait for gdb to start, then press the VOLUME DOWN key\n"
         "* to let the process continue crashing.\n"
-        "********************************************************",
-        request.pid, exe, request.tid);
+        "***********************************************************",
+        request.pid, request.tid);
 
   // Wait for VOLUME DOWN.
   if (init_getevent() == 0) {
@@ -134,8 +120,6 @@ static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* ou
   return fields == 7 ? 0 : -1;
 }
 
-static int selinux_enabled;
-
 /*
  * Corresponds with debugger_action_t enum type in
  * include/cutils/debugger.h.
@@ -146,34 +130,44 @@ static const char *debuggerd_perms[] = {
   "dump_backtrace"
 };
 
-static bool selinux_action_allowed(int s, pid_t tid, debugger_action_t action)
+static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len)
+{
+    struct debugger_request_t* req = reinterpret_cast<debugger_request_t*>(data);
+
+    if (!req) {
+        ALOGE("No debuggerd request audit data");
+        return 0;
+    }
+
+    snprintf(buf, len, "pid=%d uid=%d gid=%d", req->pid, req->uid, req->gid);
+    return 0;
+}
+
+static bool selinux_action_allowed(int s, debugger_request_t* request)
 {
   char *scon = NULL, *tcon = NULL;
   const char *tclass = "debuggerd";
   const char *perm;
   bool allowed = false;
 
-  if (selinux_enabled <= 0)
-    return true;
-
-  if (action <= 0 || action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
-    ALOGE("SELinux:  No permission defined for debugger action %d", action);
+  if (request->action <= 0 || request->action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
+    ALOGE("SELinux:  No permission defined for debugger action %d", request->action);
     return false;
   }
 
-  perm = debuggerd_perms[action];
+  perm = debuggerd_perms[request->action];
 
   if (getpeercon(s, &scon) < 0) {
     ALOGE("Cannot get peer context from socket\n");
     goto out;
   }
 
-  if (getpidcon(tid, &tcon) < 0) {
-    ALOGE("Cannot get context for tid %d\n", tid);
+  if (getpidcon(request->tid, &tcon) < 0) {
+    ALOGE("Cannot get context for tid %d\n", request->tid);
     goto out;
   }
 
-  allowed = (selinux_check_access(scon, tcon, tclass, perm, NULL) == 0);
+  allowed = (selinux_check_access(scon, tcon, tclass, perm, reinterpret_cast<void*>(request)) == 0);
 
 out:
    freecon(scon);
@@ -244,7 +238,7 @@ static int read_request(int fd, debugger_request_t* out_request) {
       return -1;
     }
 
-    if (!selinux_action_allowed(fd, out_request->tid, out_request->action))
+    if (!selinux_action_allowed(fd, out_request))
       return -1;
   } else {
     // No one else is allowed to dump arbitrary processes.
@@ -255,10 +249,7 @@ static int read_request(int fd, debugger_request_t* out_request) {
 
 static bool should_attach_gdb(debugger_request_t* request) {
   if (request->action == DEBUGGER_ACTION_CRASH) {
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.db.uid", value, "-1");
-    int debug_uid = atoi(value);
-    return debug_uid >= 0 && request->uid <= (uid_t)debug_uid;
+    return property_get_bool("debug.debuggerd.wait_for_gdb", false);
   }
   return false;
 }
@@ -430,7 +421,6 @@ static void handle_request(int fd) {
             case SIGBUS:
             case SIGFPE:
             case SIGILL:
-            case SIGPIPE:
             case SIGSEGV:
 #ifdef SIGSTKFLT
             case SIGSTKFLT:
@@ -589,7 +579,8 @@ static void usage() {
 int main(int argc, char** argv) {
   union selinux_callback cb;
   if (argc == 1) {
-    selinux_enabled = is_selinux_enabled();
+    cb.func_audit = audit_callback;
+    selinux_set_callback(SELINUX_CB_AUDIT, cb);
     cb.func_log = selinux_log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
     return do_server();
diff --git a/debuggerd/debuggerd.rc b/debuggerd/debuggerd.rc
new file mode 100644 (file)
index 0000000..e43fe96
--- /dev/null
@@ -0,0 +1,4 @@
+service debuggerd /system/bin/debuggerd
+    class main
+    group root readproc
+    writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd64.rc b/debuggerd/debuggerd64.rc
new file mode 100644 (file)
index 0000000..35b5af3
--- /dev/null
@@ -0,0 +1,4 @@
+service debuggerd64 /system/bin/debuggerd64
+    class main
+    group root readproc
+    writepid /dev/cpuset/system-background/tasks
index f7b8a86..cbf272a 100644 (file)
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <inttypes.h>
 #include <stdint.h>
@@ -21,6 +23,7 @@
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -32,7 +35,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -61,7 +64,7 @@ void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
 void dump_registers(log_t* log, pid_t tid) {
   pt_regs r;
   if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
index 293dcf6..0a8d532 100644 (file)
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <inttypes.h>
 #include <stdint.h>
@@ -21,6 +23,7 @@
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -32,7 +35,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -61,7 +64,7 @@ void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
 void dump_registers(log_t* log, pid_t tid) {
   pt_regs r;
   if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
index 5c252ab..f75534e 100644 (file)
@@ -60,12 +60,14 @@ class BacktraceMock : public Backtrace {
     }
     size_t bytes_available = buffer_.size() - offset;
 
-    if (bytes_partial_read_ > 0) {
+    if (do_partial_read_) {
       // Do a partial read.
       if (bytes > bytes_partial_read_) {
         bytes = bytes_partial_read_;
       }
       bytes_partial_read_ -= bytes;
+      // Only support a single partial read.
+      do_partial_read_ = false;
     } else if (bytes > bytes_available) {
       bytes = bytes_available;
     }
@@ -82,6 +84,7 @@ class BacktraceMock : public Backtrace {
     buffer_.resize(bytes);
     memcpy(buffer_.data(), buffer, bytes);
     bytes_partial_read_ = 0;
+    do_partial_read_ = false;
     last_read_addr_ = 0;
   }
 
@@ -90,12 +93,14 @@ class BacktraceMock : public Backtrace {
       abort();
     }
     bytes_partial_read_ = bytes;
+    do_partial_read_ = true;
   }
 
  private:
   std::vector<uint8_t> buffer_;
   size_t bytes_partial_read_ = 0;
   uintptr_t last_read_addr_ = 0;
+  bool do_partial_read_ = false;
 };
 
 #endif //  _DEBUGGERD_TEST_BACKTRACE_MOCK_H
index fcb0108..75e7028 100644 (file)
@@ -288,9 +288,9 @@ TEST_F(DumpMemoryTest, memory_partially_unreadable_unaligned_return) {
   ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
 
 #if defined(__LP64__)
-  ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
+  ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
 #else
-  ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
+  ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
 #endif
 
   // Verify that the log buf is empty, and no error messages.
@@ -313,12 +313,12 @@ TEST_F(DumpMemoryTest, memory_partially_unreadable_two_unaligned_reads) {
   ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
 
 #if defined(__LP64__)
-  ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 8\n"
-               "DEBUG Bytes after second read 106, is not a multiple of 8\n",
+  ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 8\n"
+               "DEBUG Bytes after second read 106, is not a multiple of 8\n",
                getFakeLogPrint().c_str());
 #else
-  ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 4\n"
-               "DEBUG Bytes after second read 106, is not a multiple of 4\n",
+  ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 4\n"
+               "DEBUG Bytes after second read 106, is not a multiple of 4\n",
                getFakeLogPrint().c_str());
 #endif
 
@@ -502,3 +502,239 @@ TEST_F(DumpMemoryTest, memory_address_nearly_too_high) {
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
+
+TEST_F(DumpMemoryTest, first_read_empty) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  size_t page_size = sysconf(_SC_PAGE_SIZE);
+  uintptr_t addr = 0x10000020 + page_size - 120;
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000f88 ---------------- ----------------  ................\n"
+"    0000000010000f98 ---------------- ----------------  ................\n"
+"    0000000010000fa8 ---------------- ----------------  ................\n"
+"    0000000010000fb8 ---------------- ----------------  ................\n"
+"    0000000010000fc8 ---------------- ----------------  ................\n"
+"    0000000010000fd8 ---------------- ----------------  ................\n"
+"    0000000010000fe8 ---------------- ----------------  ................\n"
+"    0000000010000ff8 ---------------- 7f7e7d7c7b7a7978  ........xyz{|}~.\n"
+"    0000000010001008 8786858483828180 8f8e8d8c8b8a8988  ................\n"
+"    0000000010001018 9796959493929190 9f9e9d9c9b9a9998  ................\n"
+"    0000000010001028 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8  ................\n"
+"    0000000010001038 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8  ................\n"
+"    0000000010001048 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
+"    0000000010001058 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
+"    0000000010001068 e7e6e5e4e3e2e1e0 efeeedecebeae9e8  ................\n"
+"    0000000010001078 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8  ................\n";
+#else
+"    10000f88 -------- -------- -------- --------  ................\n"
+"    10000f98 -------- -------- -------- --------  ................\n"
+"    10000fa8 -------- -------- -------- --------  ................\n"
+"    10000fb8 -------- -------- -------- --------  ................\n"
+"    10000fc8 -------- -------- -------- --------  ................\n"
+"    10000fd8 -------- -------- -------- --------  ................\n"
+"    10000fe8 -------- -------- -------- --------  ................\n"
+"    10000ff8 -------- -------- 7b7a7978 7f7e7d7c  ........xyz{|}~.\n"
+"    10001008 83828180 87868584 8b8a8988 8f8e8d8c  ................\n"
+"    10001018 93929190 97969594 9b9a9998 9f9e9d9c  ................\n"
+"    10001028 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac  ................\n"
+"    10001038 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc  ................\n"
+"    10001048 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
+"    10001058 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n"
+"    10001068 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec  ................\n"
+"    10001078 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_second_read_stops) {
+  uint8_t buffer[224];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  size_t page_size = sysconf(_SC_PAGE_SIZE);
+  uintptr_t addr = 0x10000020 + page_size - 192;
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000f40 ---------------- ----------------  ................\n"
+"    0000000010000f50 ---------------- ----------------  ................\n"
+"    0000000010000f60 ---------------- ----------------  ................\n"
+"    0000000010000f70 ---------------- ----------------  ................\n"
+"    0000000010000f80 ---------------- ----------------  ................\n"
+"    0000000010000f90 ---------------- ----------------  ................\n"
+"    0000000010000fa0 ---------------- ----------------  ................\n"
+"    0000000010000fb0 ---------------- ----------------  ................\n"
+"    0000000010000fc0 ---------------- ----------------  ................\n"
+"    0000000010000fd0 ---------------- ----------------  ................\n"
+"    0000000010000fe0 ---------------- ----------------  ................\n"
+"    0000000010000ff0 ---------------- ----------------  ................\n"
+"    0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
+"    0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
+"    0000000010001020 ---------------- ----------------  ................\n"
+"    0000000010001030 ---------------- ----------------  ................\n";
+#else
+"    10000f40 -------- -------- -------- --------  ................\n"
+"    10000f50 -------- -------- -------- --------  ................\n"
+"    10000f60 -------- -------- -------- --------  ................\n"
+"    10000f70 -------- -------- -------- --------  ................\n"
+"    10000f80 -------- -------- -------- --------  ................\n"
+"    10000f90 -------- -------- -------- --------  ................\n"
+"    10000fa0 -------- -------- -------- --------  ................\n"
+"    10000fb0 -------- -------- -------- --------  ................\n"
+"    10000fc0 -------- -------- -------- --------  ................\n"
+"    10000fd0 -------- -------- -------- --------  ................\n"
+"    10000fe0 -------- -------- -------- --------  ................\n"
+"    10000ff0 -------- -------- -------- --------  ................\n"
+"    10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
+"    10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n"
+"    10001020 -------- -------- -------- --------  ................\n"
+"    10001030 -------- -------- -------- --------  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  uintptr_t addr = 0x10000020;
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000000 ---------------- ----------------  ................\n"
+"    0000000010000010 ---------------- ----------------  ................\n"
+"    0000000010000020 ---------------- ----------------  ................\n"
+"    0000000010000030 ---------------- ----------------  ................\n"
+"    0000000010000040 ---------------- ----------------  ................\n"
+"    0000000010000050 ---------------- ----------------  ................\n"
+"    0000000010000060 ---------------- ----------------  ................\n"
+"    0000000010000070 ---------------- ----------------  ................\n"
+"    0000000010000080 ---------------- ----------------  ................\n"
+"    0000000010000090 ---------------- ----------------  ................\n"
+"    00000000100000a0 ---------------- ----------------  ................\n"
+"    00000000100000b0 ---------------- ----------------  ................\n"
+"    00000000100000c0 ---------------- ----------------  ................\n"
+"    00000000100000d0 ---------------- ----------------  ................\n"
+"    00000000100000e0 ---------------- ----------------  ................\n"
+"    00000000100000f0 ---------------- ----------------  ................\n";
+#else
+"    10000000 -------- -------- -------- --------  ................\n"
+"    10000010 -------- -------- -------- --------  ................\n"
+"    10000020 -------- -------- -------- --------  ................\n"
+"    10000030 -------- -------- -------- --------  ................\n"
+"    10000040 -------- -------- -------- --------  ................\n"
+"    10000050 -------- -------- -------- --------  ................\n"
+"    10000060 -------- -------- -------- --------  ................\n"
+"    10000070 -------- -------- -------- --------  ................\n"
+"    10000080 -------- -------- -------- --------  ................\n"
+"    10000090 -------- -------- -------- --------  ................\n"
+"    100000a0 -------- -------- -------- --------  ................\n"
+"    100000b0 -------- -------- -------- --------  ................\n"
+"    100000c0 -------- -------- -------- --------  ................\n"
+"    100000d0 -------- -------- -------- --------  ................\n"
+"    100000e0 -------- -------- -------- --------  ................\n"
+"    100000f0 -------- -------- -------- --------  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range_fence_post) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  size_t page_size = sysconf(_SC_PAGE_SIZE);
+  uintptr_t addr = 0x10000020 + page_size - 256;
+
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000f00 ---------------- ----------------  ................\n"
+"    0000000010000f10 ---------------- ----------------  ................\n"
+"    0000000010000f20 ---------------- ----------------  ................\n"
+"    0000000010000f30 ---------------- ----------------  ................\n"
+"    0000000010000f40 ---------------- ----------------  ................\n"
+"    0000000010000f50 ---------------- ----------------  ................\n"
+"    0000000010000f60 ---------------- ----------------  ................\n"
+"    0000000010000f70 ---------------- ----------------  ................\n"
+"    0000000010000f80 ---------------- ----------------  ................\n"
+"    0000000010000f90 ---------------- ----------------  ................\n"
+"    0000000010000fa0 ---------------- ----------------  ................\n"
+"    0000000010000fb0 ---------------- ----------------  ................\n"
+"    0000000010000fc0 ---------------- ----------------  ................\n"
+"    0000000010000fd0 ---------------- ----------------  ................\n"
+"    0000000010000fe0 ---------------- ----------------  ................\n"
+"    0000000010000ff0 ---------------- ----------------  ................\n";
+#else
+"    10000f00 -------- -------- -------- --------  ................\n"
+"    10000f10 -------- -------- -------- --------  ................\n"
+"    10000f20 -------- -------- -------- --------  ................\n"
+"    10000f30 -------- -------- -------- --------  ................\n"
+"    10000f40 -------- -------- -------- --------  ................\n"
+"    10000f50 -------- -------- -------- --------  ................\n"
+"    10000f60 -------- -------- -------- --------  ................\n"
+"    10000f70 -------- -------- -------- --------  ................\n"
+"    10000f80 -------- -------- -------- --------  ................\n"
+"    10000f90 -------- -------- -------- --------  ................\n"
+"    10000fa0 -------- -------- -------- --------  ................\n"
+"    10000fb0 -------- -------- -------- --------  ................\n"
+"    10000fc0 -------- -------- -------- --------  ................\n"
+"    10000fd0 -------- -------- -------- --------  ................\n"
+"    10000fe0 -------- -------- -------- --------  ................\n"
+"    10000ff0 -------- -------- -------- --------  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
index 26523ad..d584a5e 100644 (file)
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <errno.h>
 #include <stdarg.h>
 
 #include <string>
@@ -44,14 +45,16 @@ std::string getFakeLogPrint() {
   return g_fake_log_print;
 }
 
-extern "C" int __android_log_buf_write(int, int, const char* tag, const char* msg) {
+extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
+  g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
   g_fake_log_buf += tag;
   g_fake_log_buf += ' ';
   g_fake_log_buf += msg;
   return 1;
 }
 
-extern "C" int __android_log_print(int, const char* tag, const char* fmt, ...) {
+extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+  g_fake_log_print += std::to_string(prio) + ' ';
   g_fake_log_print += tag;
   g_fake_log_print += ' ';
 
@@ -70,6 +73,7 @@ extern "C" log_id_t android_name_to_log_id(const char*) {
 }
 
 extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+  errno = EACCES;
   return nullptr;
 }
 
similarity index 90%
rename from debuggerd/test/dump_maps_test.cpp
rename to debuggerd/test/tombstone_test.cpp
index 230f4f5..d945d27 100644 (file)
@@ -45,7 +45,7 @@ void dump_memory_and_code(log_t*, Backtrace*) {
 void dump_backtrace_to_log(Backtrace*, log_t*, char const*) {
 }
 
-class DumpMapsTest : public ::testing::Test {
+class TombstoneTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
     map_mock_.reset(new BacktraceMapMock());
@@ -76,7 +76,7 @@ class DumpMapsTest : public ::testing::Test {
     resetLogs();
     elf_set_fake_build_id("");
     siginfo_t si;
-    si.si_signo = SIGPIPE;
+    si.si_signo = SIGABRT;
     ptrace_set_fake_getsiginfo(si);
   }
 
@@ -92,7 +92,7 @@ class DumpMapsTest : public ::testing::Test {
   log_t log_;
 };
 
-TEST_F(DumpMapsTest, single_map) {
+TEST_F(TombstoneTest, single_map) {
   backtrace_map_t map;
 #if defined(__LP64__)
   map.start = 0x123456789abcd000UL;
@@ -122,7 +122,7 @@ TEST_F(DumpMapsTest, single_map) {
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, single_map_elf_build_id) {
+TEST_F(TombstoneTest, single_map_elf_build_id) {
   backtrace_map_t map;
 #if defined(__LP64__)
   map.start = 0x123456789abcd000UL;
@@ -157,7 +157,7 @@ TEST_F(DumpMapsTest, single_map_elf_build_id) {
 
 // Even though build id is present, it should not be printed in either of
 // these cases.
-TEST_F(DumpMapsTest, single_map_no_build_id) {
+TEST_F(TombstoneTest, single_map_no_build_id) {
   backtrace_map_t map;
 #if defined(__LP64__)
   map.start = 0x123456789abcd000UL;
@@ -194,7 +194,7 @@ TEST_F(DumpMapsTest, single_map_no_build_id) {
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps) {
+TEST_F(TombstoneTest, multiple_maps) {
   backtrace_map_t map;
 
   map.start = 0xa234000;
@@ -256,7 +256,7 @@ TEST_F(DumpMapsTest, multiple_maps) {
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_before) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -310,7 +310,7 @@ TEST_F(DumpMapsTest, multiple_maps_fault_address_before) {
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_between) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -364,7 +364,7 @@ TEST_F(DumpMapsTest, multiple_maps_fault_address_between) {
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_in_map) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -416,7 +416,7 @@ TEST_F(DumpMapsTest, multiple_maps_fault_address_in_map) {
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_after) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_after) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -474,7 +474,7 @@ TEST_F(DumpMapsTest, multiple_maps_fault_address_after) {
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_getsiginfo_fail) {
+TEST_F(TombstoneTest, multiple_maps_getsiginfo_fail) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -493,7 +493,6 @@ TEST_F(DumpMapsTest, multiple_maps_getsiginfo_fail) {
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"Cannot get siginfo for 100: Bad address\n"
 "\nmemory map:\n"
 #if defined(__LP64__)
 "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n";
@@ -503,12 +502,11 @@ TEST_F(DumpMapsTest, multiple_maps_getsiginfo_fail) {
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
   // Verify that the log buf is empty, and no error messages.
-  ASSERT_STREQ("DEBUG Cannot get siginfo for 100: Bad address\n",
-               getFakeLogBuf().c_str());
-  ASSERT_STREQ("", getFakeLogPrint().c_str());
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_check_signal_has_si_addr) {
+TEST_F(TombstoneTest, multiple_maps_check_signal_has_si_addr) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -569,3 +567,33 @@ TEST_F(DumpMapsTest, multiple_maps_check_signal_has_si_addr) {
     ASSERT_STREQ("", getFakeLogPrint().c_str());
   }
 }
+
+TEST_F(TombstoneTest, dump_signal_info_error) {
+  siginfo_t si;
+  si.si_signo = 0;
+  ptrace_set_fake_getsiginfo(si);
+
+  dump_signal_info(&log_, 123, SIGSEGV, 10);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ("", tombstone_contents.c_str());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, dump_log_file_error) {
+  log_.should_retrieve_logcat = true;
+  dump_log_file(&log_, 123, "/fake/filename", 10);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ("", tombstone_contents.c_str());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n",
+               getFakeLogPrint().c_str());
+}
index b0ad274..e283923 100644 (file)
@@ -81,7 +81,6 @@ static const char* get_signame(int sig) {
     case SIGBUS: return "SIGBUS";
     case SIGFPE: return "SIGFPE";
     case SIGILL: return "SIGILL";
-    case SIGPIPE: return "SIGPIPE";
     case SIGSEGV: return "SIGSEGV";
 #if defined(SIGSTKFLT)
     case SIGSTKFLT: return "SIGSTKFLT";
@@ -180,7 +179,7 @@ static void dump_signal_info(log_t* log, pid_t tid, int signal, int si_code) {
   siginfo_t si;
   memset(&si, 0, sizeof(si));
   if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
-    _LOG(log, logtype::HEADER, "cannot get siginfo: %s\n", strerror(errno));
+    ALOGE("cannot get siginfo: %s\n", strerror(errno));
     return;
   }
 
@@ -338,7 +337,7 @@ static void dump_all_maps(Backtrace* backtrace, BacktraceMap* map, log_t* log, p
     print_fault_address_marker = signal_has_si_addr(si.si_signo);
     addr = reinterpret_cast<uintptr_t>(si.si_addr);
   } else {
-    _LOG(log, logtype::ERROR, "Cannot get siginfo for %d: %s\n", tid, strerror(errno));
+    ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
   }
 
   _LOG(log, logtype::MAPS, "\n");
@@ -448,7 +447,7 @@ static bool dump_sibling_thread_report(
 
     // Skip this thread if cannot ptrace it
     if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
-      _LOG(log, logtype::ERROR, "ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
+      ALOGE("ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
       continue;
     }
 
@@ -471,7 +470,7 @@ static bool dump_sibling_thread_report(
     log->current_tid = log->crashed_tid;
 
     if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
-      _LOG(log, logtype::ERROR, "ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
+      ALOGE("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
       detach_failed = true;
     }
   }
@@ -517,13 +516,11 @@ static void dump_log_file(
         // non-blocking EOF; we're done
         break;
       } else {
-        _LOG(log, logtype::ERROR, "Error while reading log: %s\n",
-          strerror(-actual));
+        ALOGE("Error while reading log: %s\n", strerror(-actual));
         break;
       }
     } else if (actual == 0) {
-      _LOG(log, logtype::ERROR, "Got zero bytes while reading log: %s\n",
-        strerror(errno));
+      ALOGE("Got zero bytes while reading log: %s\n", strerror(errno));
       break;
     }
 
@@ -789,11 +786,11 @@ char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
   log.crashed_tid = tid;
 
   if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) {
-    _LOG(&log, logtype::ERROR, "failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+    ALOGE("failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
   }
 
   if (chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM) == -1) {
-    _LOG(&log, logtype::ERROR, "failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+    ALOGE("failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
   }
 
   int fd = -1;
@@ -801,11 +798,11 @@ char* engrave_tombstone(pid_t pid, pid_t tid, int signal, int original_si_code,
   if (selinux_android_restorecon(TOMBSTONE_DIR, 0) == 0) {
     path = find_and_open_tombstone(&fd);
   } else {
-    _LOG(&log, logtype::ERROR, "Failed to restore security context, not writing tombstone.\n");
+    ALOGE("Failed to restore security context, not writing tombstone.\n");
   }
 
   if (fd < 0) {
-    _LOG(&log, logtype::ERROR, "Skipping tombstone write, nothing to do.\n");
+    ALOGE("Skipping tombstone write, nothing to do.\n");
     *detach_failed = false;
     return NULL;
   }
index 9f340a8..f5d6ec1 100644 (file)
@@ -35,8 +35,7 @@ const int MAX_TOTAL_SLEEP_USEC = 10000000; // 10 seconds
 
 // Whitelist output desired in the logcat output.
 bool is_allowed_in_logcat(enum logtype ltype) {
-  if ((ltype == ERROR)
-   || (ltype == HEADER)
+  if ((ltype == HEADER)
    || (ltype == REGISTERS)
    || (ltype == BACKTRACE)) {
     return true;
@@ -157,13 +156,28 @@ void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* f
     bytes &= ~(sizeof(uintptr_t) - 1);
   }
 
-  if (bytes < MEMORY_BYTES_TO_DUMP && bytes > 0) {
-    // Try to do one more read. This could happen if a read crosses a map, but
-    // the maps do not have any break between them. Only requires one extra
-    // read because a map has to contain at least one page, and the total
-    // number of bytes to dump is smaller than a page.
-    size_t bytes2 = backtrace->Read(addr + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
-                                    sizeof(data) - bytes);
+  uintptr_t start = 0;
+  bool skip_2nd_read = false;
+  if (bytes == 0) {
+    // In this case, we might want to try another read at the beginning of
+    // the next page only if it's within the amount of memory we would have
+    // read.
+    size_t page_size = sysconf(_SC_PAGE_SIZE);
+    start = ((addr + (page_size - 1)) & ~(page_size - 1)) - addr;
+    if (start == 0 || start >= MEMORY_BYTES_TO_DUMP) {
+      skip_2nd_read = true;
+    }
+  }
+
+  if (bytes < MEMORY_BYTES_TO_DUMP && !skip_2nd_read) {
+    // Try to do one more read. This could happen if a read crosses a map,
+    // but the maps do not have any break between them. Or it could happen
+    // if reading from an unreadable map, but the read would cross back
+    // into a readable map. Only requires one extra read because a map has
+    // to contain at least one page, and the total number of bytes to dump
+    // is smaller than a page.
+    size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
+                                    sizeof(data) - bytes - start);
     bytes += bytes2;
     if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
       // This should never happen, but we'll try and continue any way.
@@ -179,15 +193,16 @@ void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* f
   // On 32-bit machines, there are still 16 bytes per line but addresses and
   // words are of course presented differently.
   uintptr_t* data_ptr = data;
+  size_t current = 0;
+  size_t total_bytes = start + bytes;
   for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
     std::string logline;
     android::base::StringAppendF(&logline, "    %" PRIPTR, addr);
 
     addr += MEMORY_BYTES_PER_LINE;
     std::string ascii;
-    for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++, data_ptr++) {
-      if (bytes >= sizeof(uintptr_t)) {
-        bytes -= sizeof(uintptr_t);
+    for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) {
+      if (current >= start && current + sizeof(uintptr_t) <= total_bytes) {
         android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr);
 
         // Fill out the ascii string from the data.
@@ -199,10 +214,12 @@ void dump_memory(log_t* log, Backtrace* backtrace, uintptr_t addr, const char* f
             ascii += '.';
           }
         }
+        data_ptr++;
       } else {
         logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-');
         ascii += std::string(sizeof(uintptr_t), '.');
       }
+      current += sizeof(uintptr_t);
     }
     _LOG(log, logtype::MEMORY, "%s  %s\n", logline.c_str(), ascii.c_str());
   }
index 263374d..8bef192 100644 (file)
@@ -59,7 +59,6 @@ struct log_t{
 
 // List of types of logs to simplify the logging decision in _LOG
 enum logtype {
-  ERROR,
   HEADER,
   THREAD,
   REGISTERS,
index c5f9259..af10817 100644 (file)
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <stdint.h>
 #include <string.h>
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -27,7 +30,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   struct pt_regs r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -44,7 +47,7 @@ void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
 void dump_registers(log_t* log, pid_t tid) {
   struct pt_regs r;
   if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
index 4f09a5d..fc86bc2 100644 (file)
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <stdint.h>
 #include <sys/ptrace.h>
@@ -21,6 +23,7 @@
 #include <sys/user.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -28,7 +31,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   struct user_regs_struct r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -45,7 +48,7 @@ void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
 void dump_registers(log_t* log, pid_t tid) {
   struct user_regs_struct r;
   if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
index 66a470a..7a7366e 100644 (file)
@@ -21,67 +21,48 @@ include $(CLEAR_VARS)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
   $(LOCAL_PATH)/../../extras/ext4_utils \
   $(LOCAL_PATH)/../../extras/f2fs_utils
-LOCAL_SRC_FILES := protocol.c engine.c bootimg_utils.cpp fastboot.cpp util.c fs.c
+LOCAL_SRC_FILES := protocol.cpp engine.cpp bootimg_utils.cpp fastboot.cpp util.cpp fs.cpp
 LOCAL_MODULE := fastboot
 LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CONLYFLAGS += -std=gnu99
 LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
 
 LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
 
-ifeq ($(HOST_OS),linux)
-  LOCAL_SRC_FILES += usb_linux.c util_linux.c
-endif
+LOCAL_SRC_FILES_linux := usb_linux.cpp util_linux.cpp
 
-ifeq ($(HOST_OS),darwin)
-  LOCAL_SRC_FILES += usb_osx.c util_osx.c
-  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-  LOCAL_CFLAGS += -Wno-unused-parameter
-endif
+LOCAL_SRC_FILES_darwin := usb_osx.cpp util_osx.cpp
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+LOCAL_CFLAGS_darwin := -Wno-unused-parameter
 
-ifeq ($(HOST_OS),windows)
-  LOCAL_SRC_FILES += usb_windows.c util_windows.c
-  EXTRA_STATIC_LIBS := AdbWinApi
-  ifneq ($(strip $(USE_CYGWIN)),)
-    # Pure cygwin case
-    LOCAL_LDLIBS += -lpthread
-  endif
-  ifneq ($(strip $(USE_MINGW)),)
-    # MinGW under Linux case
-    LOCAL_LDLIBS += -lws2_32
-    USE_SYSDEPS_WIN32 := 1
-  endif
-  LOCAL_C_INCLUDES += development/host/windows/usb/api
-endif
+LOCAL_SRC_FILES_windows := usb_windows.cpp util_windows.cpp
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi
+LOCAL_LDLIBS_windows := -lws2_32
+LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
 
 LOCAL_STATIC_LIBRARIES := \
-    $(EXTRA_STATIC_LIBS) \
     libziparchive-host \
     libext4_utils_host \
     libsparse_host \
     libutils \
     liblog \
     libz \
-    libbase
+    libbase \
 
-ifneq ($(HOST_OS),windows)
-LOCAL_STATIC_LIBRARIES += libselinux
-endif # HOST_OS != windows
+LOCAL_STATIC_LIBRARIES_darwin := libselinux
+LOCAL_STATIC_LIBRARIES_linux := libselinux
 
-ifeq ($(HOST_OS),linux)
 # libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
-LOCAL_CFLAGS += -DUSE_F2FS
-LOCAL_LDFLAGS += -ldl -rdynamic -Wl,-rpath,.
-LOCAL_REQUIRED_MODULES := libf2fs_fmt_host_dyn
+LOCAL_CFLAGS_linux := -DUSE_F2FS
+LOCAL_LDFLAGS_linux := -ldl -rdynamic -Wl,-rpath,.
+LOCAL_REQUIRED_MODULES_linux := libf2fs_fmt_host_dyn
 # The following libf2fs_* are from system/extras/f2fs_utils,
 # and do not use code in external/f2fs-tools.
-LOCAL_STATIC_LIBRARIES += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
-endif
+LOCAL_STATIC_LIBRARIES_linux += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
 
-# libc++ not available on windows yet
-ifneq ($(HOST_OS),windows)
-    LOCAL_CXX_STL := libc++_static
-endif
+LOCAL_CXX_STL := libc++_static
 
 # Don't add anything here, we don't want additional shared dependencies
 # on the host fastboot tool, and shared libraries that link against libc++
@@ -97,15 +78,10 @@ endif
 $(call dist-for-goals,dist_files sdk,$(my_dist_files))
 my_dist_files :=
 
-
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := usbtest.c usb_linux.c util.c
+LOCAL_SRC_FILES := usbtest.cpp usb_linux.cpp util.cpp
 LOCAL_MODULE := usbtest
 LOCAL_CFLAGS := -Werror
 include $(BUILD_HOST_EXECUTABLE)
 endif
-
-ifeq ($(HOST_OS),windows)
-$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
-endif
index d8905a6..c1028ef 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
-void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline)
+void bootimg_set_cmdline(boot_img_hdr* h, const char* cmdline)
 {
     strcpy((char*) h->cmdline, cmdline);
 }
 
-boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned kernel_offset,
-                        void *ramdisk, unsigned ramdisk_size, unsigned ramdisk_offset,
-                        void *second, unsigned second_size, unsigned second_offset,
-                        unsigned page_size, unsigned base, unsigned tags_offset,
-                        unsigned *bootimg_size)
+boot_img_hdr* mkbootimg(void* kernel, int64_t kernel_size, off_t kernel_offset,
+                        void* ramdisk, int64_t ramdisk_size, off_t ramdisk_offset,
+                        void* second, int64_t second_size, off_t second_offset,
+                        size_t page_size, size_t base, off_t tags_offset,
+                        int64_t* bootimg_size)
 {
-    unsigned kernel_actual;
-    unsigned ramdisk_actual;
-    unsigned second_actual;
-    unsigned page_mask;
+    size_t page_mask = page_size - 1;
 
-    page_mask = page_size - 1;
-
-    kernel_actual = (kernel_size + page_mask) & (~page_mask);
-    ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
-    second_actual = (second_size + page_mask) & (~page_mask);
+    int64_t kernel_actual = (kernel_size + page_mask) & (~page_mask);
+    int64_t ramdisk_actual = (ramdisk_size + page_mask) & (~page_mask);
+    int64_t second_actual = (second_size + page_mask) & (~page_mask);
 
     *bootimg_size = page_size + kernel_actual + ramdisk_actual + second_actual;
 
     boot_img_hdr* hdr = reinterpret_cast<boot_img_hdr*>(calloc(*bootimg_size, 1));
-    if (hdr == 0) {
+    if (hdr == nullptr) {
         return hdr;
     }
 
@@ -74,12 +69,9 @@ boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned kernel_offs
 
     hdr->page_size =    page_size;
 
+    memcpy(hdr->magic + page_size, kernel, kernel_size);
+    memcpy(hdr->magic + page_size + kernel_actual, ramdisk, ramdisk_size);
+    memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual, second, second_size);
 
-    memcpy(hdr->magic + page_size,
-           kernel, kernel_size);
-    memcpy(hdr->magic + page_size + kernel_actual,
-           ramdisk, ramdisk_size);
-    memcpy(hdr->magic + page_size + kernel_actual + ramdisk_actual,
-           second, second_size);
     return hdr;
 }
index b1a86cd..fcc8662 100644 (file)
 #define _FASTBOOT_BOOTIMG_UTILS_H_
 
 #include <bootimg.h>
+#include <inttypes.h>
+#include <sys/types.h>
 
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline);
-boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, unsigned kernel_offset,
-                        void *ramdisk, unsigned ramdisk_size, unsigned ramdisk_offset,
-                        void *second, unsigned second_size, unsigned second_offset,
-                        unsigned page_size, unsigned base, unsigned tags_offset,
-                        unsigned *bootimg_size);
-
-#if defined(__cplusplus)
-}
-#endif
+void bootimg_set_cmdline(boot_img_hdr* h, const char* cmdline);
+boot_img_hdr* mkbootimg(void* kernel, int64_t kernel_size, off_t kernel_offset,
+                        void* ramdisk, int64_t ramdisk_size, off_t ramdisk_offset,
+                        void* second, int64_t second_size, off_t second_offset,
+                        size_t page_size, size_t base, off_t tags_offset,
+                        int64_t* bootimg_size);
 
 #endif
similarity index 78%
rename from fastboot/engine.c
rename to fastboot/engine.cpp
index 2f90e41..d4be63b 100644 (file)
@@ -31,7 +31,6 @@
 
 #include <errno.h>
 #include <stdarg.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
 
-#ifdef USE_MINGW
-#include <fcntl.h>
-#else
-#include <sys/mman.h>
-#endif
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
 #define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
 
 #define OP_DOWNLOAD   1
@@ -62,18 +51,20 @@ typedef struct Action Action;
 
 #define CMD_SIZE 64
 
-struct Action
-{
+struct Action {
     unsigned op;
-    Action *next;
+    Actionnext;
 
     char cmd[CMD_SIZE];
-    const char *prod;
-    void *data;
-    unsigned size;
+    const char* prod;
+    void* data;
+
+    // The protocol only supports 32-bit sizes, so you'll have to break
+    // anything larger into chunks.
+    uint32_t size;
 
     const char *msg;
-    int (*func)(Action *a, int status, char *resp);
+    int (*func)(Action* a, int status, const char* resp);
 
     double start;
 };
@@ -84,45 +75,20 @@ static Action *action_last = 0;
 
 
 
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
-{
-    char cmd[CMD_SIZE] = "getvar:";
-    int getvar_len = strlen(cmd);
-    va_list args;
-
-    response[FB_RESPONSE_SZ] = '\0';
-    va_start(args, fmt);
-    vsnprintf(cmd + getvar_len, sizeof(cmd) - getvar_len, fmt, args);
-    va_end(args);
-    cmd[CMD_SIZE - 1] = '\0';
-    return fb_command_response(usb, cmd, response);
-}
+bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value) {
+    std::string cmd = "getvar:";
+    cmd += key;
 
-
-/* Return true if this partition is supported by the fastboot format command.
- * It is also used to determine if we should first erase a partition before
- * flashing it with an ext4 filesystem.  See needs_erase()
- *
- * Not all devices report the filesystem type, so don't report any errors,
- * just return false.
- */
-int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override)
-{
-    char fs_type[FB_RESPONSE_SZ + 1] = {0,};
-    int status;
-
-    if (type_override) {
-        return !!fs_get_generator(type_override);
+    char buf[FB_RESPONSE_SZ + 1];
+    memset(buf, 0, sizeof(buf));
+    if (fb_command_response(usb, cmd.c_str(), buf)) {
+      return false;
     }
-    status = fb_getvar(usb, fs_type, "partition-type:%s", partition);
-    if (status) {
-        return 0;
-    }
-    return !!fs_get_generator(fs_type);
+    *value = buf;
+    return true;
 }
 
-static int cb_default(Action *a, int status, char *resp)
-{
+static int cb_default(Action* a, int status, const char* resp) {
     if (status) {
         fprintf(stderr,"FAILED (%s)\n", resp);
     } else {
@@ -135,12 +101,11 @@ static int cb_default(Action *a, int status, char *resp)
 
 static Action *queue_action(unsigned op, const char *fmt, ...)
 {
-    Action *a;
     va_list ap;
     size_t cmdsize;
 
-    a = calloc(1, sizeof(Action));
-    if (a == 0) die("out of memory");
+    Action* a = reinterpret_cast<Action*>(calloc(1, sizeof(Action)));
+    if (a == nullptr) die("out of memory");
 
     va_start(ap, fmt);
     cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap);
@@ -165,6 +130,13 @@ static Action *queue_action(unsigned op, const char *fmt, ...)
     return a;
 }
 
+void fb_set_active(const char *slot)
+{
+    Action *a;
+    a = queue_action(OP_COMMAND, "set_active:%s", slot);
+    a->msg = mkmsg("Setting current slot to '%s'", slot);
+}
+
 void fb_queue_erase(const char *ptn)
 {
     Action *a;
@@ -198,8 +170,7 @@ void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz)
     a->msg = mkmsg("writing '%s'", ptn);
 }
 
-static int match(char *str, const char **value, unsigned count)
-{
+static int match(const char* str, const char** value, unsigned count) {
     unsigned n;
 
     for (n = 0; n < count; n++) {
@@ -222,9 +193,9 @@ static int match(char *str, const char **value, unsigned count)
 
 
 
-static int cb_check(Action *a, int status, char *resp, int invert)
+static int cb_check(Action* a, int status, const char* resp, int invert)
 {
-    const char **value = a->data;
+    const char** value = reinterpret_cast<const char**>(a->data);
     unsigned count = a->size;
     unsigned n;
     int yes;
@@ -265,18 +236,16 @@ static int cb_check(Action *a, int status, char *resp, int invert)
     return -1;
 }
 
-static int cb_require(Action *a, int status, char *resp)
-{
+static int cb_require(Action*a, int status, const char* resp) {
     return cb_check(a, status, resp, 0);
 }
 
-static int cb_reject(Action *a, int status, char *resp)
-{
+static int cb_reject(Action* a, int status, const char* resp) {
     return cb_check(a, status, resp, 1);
 }
 
 void fb_queue_require(const char *prod, const char *var,
-               int invert, unsigned nvalues, const char **value)
+                      bool invert, size_t nvalues, const char **value)
 {
     Action *a;
     a = queue_action(OP_QUERY, "getvar:%s", var);
@@ -285,11 +254,10 @@ void fb_queue_require(const char *prod, const char *var,
     a->size = nvalues;
     a->msg = mkmsg("checking %s", var);
     a->func = invert ? cb_reject : cb_require;
-    if (a->data == 0) die("out of memory");
+    if (a->data == nullptr) die("out of memory");
 }
 
-static int cb_display(Action *a, int status, char *resp)
-{
+static int cb_display(Action* a, int status, const char* resp) {
     if (status) {
         fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
         return status;
@@ -303,17 +271,16 @@ void fb_queue_display(const char *var, const char *prettyname)
     Action *a;
     a = queue_action(OP_QUERY, "getvar:%s", var);
     a->data = strdup(prettyname);
-    if (a->data == 0) die("out of memory");
+    if (a->data == nullptr) die("out of memory");
     a->func = cb_display;
 }
 
-static int cb_save(Action *a, int status, char *resp)
-{
+static int cb_save(Action* a, int status, const char* resp) {
     if (status) {
         fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
         return status;
     }
-    strncpy(a->data, resp, a->size);
+    strncpy(reinterpret_cast<char*>(a->data), resp, a->size);
     return 0;
 }
 
@@ -326,8 +293,7 @@ void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
     a->func = cb_save;
 }
 
-static int cb_do_nothing(Action *a __unused, int status __unused, char *resp __unused)
-{
+static int cb_do_nothing(Action*, int , const char*) {
     fprintf(stderr,"\n");
     return 0;
 }
@@ -398,7 +364,7 @@ int fb_execute_queue(usb_handle *usb)
         } else if (a->op == OP_NOTICE) {
             fprintf(stderr,"%s\n",(char*)a->data);
         } else if (a->op == OP_DOWNLOAD_SPARSE) {
-            status = fb_download_data_sparse(usb, a->data);
+            status = fb_download_data_sparse(usb, reinterpret_cast<sparse_file*>(a->data));
             status = a->func(a, status, status ? fb_get_error() : "");
             if (status) break;
         } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
@@ -411,8 +377,3 @@ int fb_execute_queue(usb_handle *usb)
     fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
     return status;
 }
-
-int fb_queue_is_empty(void)
-{
-    return (action_list == NULL);
-}
diff --git a/fastboot/engineering_key.p12 b/fastboot/engineering_key.p12
deleted file mode 100644 (file)
index d8183b0..0000000
Binary files a/fastboot/engineering_key.p12 and /dev/null differ
index de4c0ea..9f72c83 100644 (file)
@@ -34,7 +34,6 @@
 #include <getopt.h>
 #include <inttypes.h>
 #include <limits.h>
-#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <functional>
 
+#include <base/parseint.h>
+#include <base/strings.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
 
+#include <base/strings.h>
+#include <base/parseint.h>
+
 #include "bootimg_utils.h"
 #include "fastboot.h"
 #include "fs.h"
@@ -67,12 +72,12 @@ static int long_listing = 0;
 static int64_t sparse_limit = -1;
 static int64_t target_sparse_limit = -1;
 
-unsigned page_size = 2048;
-unsigned base_addr      = 0x10000000;
-unsigned kernel_offset  = 0x00008000;
-unsigned ramdisk_offset = 0x01000000;
-unsigned second_offset  = 0x00f00000;
-unsigned tags_offset    = 0x00000100;
+static unsigned page_size = 2048;
+static unsigned base_addr      = 0x10000000;
+static unsigned kernel_offset  = 0x00008000;
+static unsigned ramdisk_offset = 0x01000000;
+static unsigned second_offset  = 0x00f00000;
+static unsigned tags_offset    = 0x00000100;
 
 enum fb_buffer_type {
     FB_BUFFER,
@@ -81,8 +86,8 @@ enum fb_buffer_type {
 
 struct fastboot_buffer {
     enum fb_buffer_type type;
-    void *data;
-    unsigned int sz;
+    voiddata;
+    int64_t sz;
 };
 
 static struct {
@@ -97,8 +102,7 @@ static struct {
     {"vendor.img", "vendor.sig", "vendor", true},
 };
 
-char *find_item(const char *item, const char *product)
-{
+static char* find_item(const char* item, const char* product) {
     char *dir;
     const char *fn;
     char path[PATH_MAX + 128];
@@ -139,36 +143,26 @@ char *find_item(const char *item, const char *product)
     return strdup(path);
 }
 
-static int64_t file_size(int fd)
-{
-    struct stat st;
-    int ret;
-
-    ret = fstat(fd, &st);
-
-    return ret ? -1 : st.st_size;
+static int64_t get_file_size(int fd) {
+    struct stat sb;
+    return fstat(fd, &sb) == -1 ? -1 : sb.st_size;
 }
 
-static void *load_fd(int fd, unsigned *_sz)
-{
-    char *data;
-    int sz;
+static void* load_fd(int fd, int64_t* sz) {
     int errno_tmp;
+    char* data = nullptr;
 
-    data = 0;
-
-    sz = file_size(fd);
-    if (sz < 0) {
+    *sz = get_file_size(fd);
+    if (*sz < 0) {
         goto oops;
     }
 
-    data = (char*) malloc(sz);
-    if(data == 0) goto oops;
+    data = (char*) malloc(*sz);
+    if (data == nullptr) goto oops;
 
-    if(read(fd, data, sz) != sz) goto oops;
+    if(read(fd, data, *sz) != *sz) goto oops;
     close(fd);
 
-    if(_sz) *_sz = sz;
     return data;
 
 oops:
@@ -179,35 +173,22 @@ oops:
     return 0;
 }
 
-static void *load_file(const char *fn, unsigned *_sz)
-{
-    int fd;
+static void* load_file(const char* fn, int64_t* sz) {
+    int fd = open(fn, O_RDONLY | O_BINARY);
+    if (fd == -1) return nullptr;
+    return load_fd(fd, sz);
+}
 
-    fd = open(fn, O_RDONLY | O_BINARY);
-    if(fd < 0) return 0;
+static int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {
+    // Require a matching vendor id if the user specified one with -i.
+    if (vendor_id != 0 && info->dev_vendor != vendor_id) {
+        return -1;
+    }
 
-    return load_fd(fd, _sz);
-}
+    if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {
+        return -1;
+    }
 
-int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial)
-{
-    if(!(vendor_id && (info->dev_vendor == vendor_id)) &&
-       (info->dev_vendor != 0x18d1) &&  // Google
-       (info->dev_vendor != 0x8087) &&  // Intel
-       (info->dev_vendor != 0x0451) &&
-       (info->dev_vendor != 0x0502) &&
-       (info->dev_vendor != 0x0fce) &&  // Sony Ericsson
-       (info->dev_vendor != 0x05c6) &&  // Qualcomm
-       (info->dev_vendor != 0x22b8) &&  // Motorola
-       (info->dev_vendor != 0x0955) &&  // Nvidia
-       (info->dev_vendor != 0x413c) &&  // DELL
-       (info->dev_vendor != 0x2314) &&  // INQ Mobile
-       (info->dev_vendor != 0x0b05) &&  // Asus
-       (info->dev_vendor != 0x0bb4))    // HTC
-            return -1;
-    if(info->ifc_class != 0xff) return -1;
-    if(info->ifc_subclass != 0x42) return -1;
-    if(info->ifc_protocol != 0x03) return -1;
     // require matching serial number or device path if requested
     // at the command line with the -s option.
     if (local_serial && (strcmp(local_serial, info->serial_number) != 0 &&
@@ -215,14 +196,12 @@ int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial)
     return 0;
 }
 
-int match_fastboot(usb_ifc_info *info)
-{
+static int match_fastboot(usb_ifc_info* info) {
     return match_fastboot_with_serial(info, serial);
 }
 
-int list_devices_callback(usb_ifc_info *info)
-{
-    if (match_fastboot_with_serial(info, NULL) == 0) {
+static int list_devices_callback(usb_ifc_info* info) {
+    if (match_fastboot_with_serial(info, nullptr) == 0) {
         const char* serial = info->serial_number;
         if (!info->writable) {
             serial = "no permissions"; // like "adb devices"
@@ -243,8 +222,7 @@ int list_devices_callback(usb_ifc_info *info)
     return -1;
 }
 
-usb_handle *open_device(void)
-{
+static usb_handle* open_device() {
     static usb_handle *usb = 0;
     int announce = 1;
 
@@ -255,103 +233,118 @@ usb_handle *open_device(void)
         if(usb) return usb;
         if(announce) {
             announce = 0;
-            fprintf(stderr,"< waiting for device >\n");
+            fprintf(stderr, "< waiting for %s >\n", serial ? serial : "any device");
         }
         usleep(1000);
     }
 }
 
-void list_devices(void) {
+static void list_devices() {
     // We don't actually open a USB device here,
     // just getting our callback called so we can
     // list all the connected devices.
     usb_open(list_devices_callback);
 }
 
-void usage(void)
-{
+static void usage() {
     fprintf(stderr,
 /*           1234567890123456789012345678901234567890123456789012345678901234567890123456 */
             "usage: fastboot [ <option> ] <command>\n"
             "\n"
             "commands:\n"
-            "  update <filename>                        reflash device from update.zip\n"
-            "  flashall                                 flash boot, system, vendor and if found,\n"
-            "                                           recovery\n"
-            "  flash <partition> [ <filename> ]         write a file to a flash partition\n"
-            "  flashing lock                            locks the device. Prevents flashing\n"
-            "                                           partitions\n"
-            "  flashing unlock                          unlocks the device. Allows user to\n"
-            "                                           flash any partition except the ones\n"
-            "                                           that are related to bootloader\n"
-            "  flashing lock_critical                   Prevents flashing bootloader related\n"
-            "                                           partitions\n"
-            "  flashing unlock_critical                 Enables flashing bootloader related\n"
-            "                                           partitions\n"
+            "  update <filename>                        Reflash device from update.zip.\n"
+            "  flashall                                 Flash boot, system, vendor, and --\n"
+            "                                           if found -- recovery.\n"
+            "  flash <partition> [ <filename> ]         Write a file to a flash partition.\n"
+            "  flashing lock                            Locks the device. Prevents flashing.\n"
+            "  flashing unlock                          Unlocks the device. Allows flashing\n"
+            "                                           any partition except\n"
+            "                                           bootloader-related partitions.\n"
+            "  flashing lock_critical                   Prevents flashing bootloader-related\n"
+            "                                           partitions.\n"
+            "  flashing unlock_critical                 Enables flashing bootloader-related\n"
+            "                                           partitions.\n"
             "  flashing get_unlock_ability              Queries bootloader to see if the\n"
-            "                                           device is unlocked\n"
+            "                                           device is unlocked.\n"
             "  flashing get_unlock_bootloader_nonce     Queries the bootloader to get the\n"
-            "                                           unlock nonce\n"
-            "  flashing unlock_bootloader <request>     Issue unlock bootloader using request\n"
+            "                                           unlock nonce.\n"
+            "  flashing unlock_bootloader <request>     Issue unlock bootloader using request.\n"
             "  flashing lock_bootloader                 Locks the bootloader to prevent\n"
-            "                                           bootloader version rollback\n"
-            "  erase <partition>                        erase a flash partition\n"
-            "  format[:[<fs type>][:[<size>]] <partition> format a flash partition.\n"
-            "                                           Can override the fs type and/or\n"
-            "                                           size the bootloader reports.\n"
-            "  getvar <variable>                        display a bootloader variable\n"
-            "  boot <kernel> [ <ramdisk> ]              download and boot kernel\n"
-            "  flash:raw boot <kernel> [ <ramdisk> ]    create bootimage and flash it\n"
-            "  devices                                  list all connected devices\n"
-            "  continue                                 continue with autoboot\n"
-            "  reboot [bootloader]                      reboot device, optionally into bootloader\n"
-            "  reboot-bootloader                        reboot device into bootloader\n"
-            "  help                                     show this help message\n"
+            "                                           bootloader version rollback.\n"
+            "  erase <partition>                        Erase a flash partition.\n"
+            "  format[:[<fs type>][:[<size>]] <partition>\n"
+            "                                           Format a flash partition. Can\n"
+            "                                           override the fs type and/or size\n"
+            "                                           the bootloader reports.\n"
+            "  getvar <variable>                        Display a bootloader variable.\n"
+            "  boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
+            "  flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
+            "                                           Create bootimage and flash it.\n"
+            "  devices [-l]                             List all connected devices [with\n"
+            "                                           device paths].\n"
+            "  continue                                 Continue with autoboot.\n"
+            "  reboot [bootloader]                      Reboot device [into bootloader].\n"
+            "  reboot-bootloader                        Reboot device into bootloader.\n"
+            "  help                                     Show this help message.\n"
             "\n"
             "options:\n"
-            "  -w                                       erase userdata and cache (and format\n"
-            "                                           if supported by partition type)\n"
-            "  -u                                       do not first erase partition before\n"
-            "                                           formatting\n"
-            "  -s <specific device>                     specify device serial number\n"
-            "                                           or path to device port\n"
-            "  -l                                       with \"devices\", lists device paths\n"
-            "  -p <product>                             specify product name\n"
-            "  -c <cmdline>                             override kernel commandline\n"
-            "  -i <vendor id>                           specify a custom USB vendor id\n"
-            "  -b <base_addr>                           specify a custom kernel base address.\n"
-            "                                           default: 0x10000000\n"
-            "  -n <page size>                           specify the nand page size.\n"
-            "                                           default: 2048\n"
-            "  -S <size>[K|M|G]                         automatically sparse files greater\n"
-            "                                           than size.  0 to disable\n"
+            "  -w                                       Erase userdata and cache (and format\n"
+            "                                           if supported by partition type).\n"
+            "  -u                                       Do not erase partition before\n"
+            "                                           formatting.\n"
+            "  -s <specific device>                     Specify device serial number\n"
+            "                                           or path to device port.\n"
+            "  -p <product>                             Specify product name.\n"
+            "  -c <cmdline>                             Override kernel commandline.\n"
+            "  -i <vendor id>                           Specify a custom USB vendor id.\n"
+            "  -b, --base <base_addr>                   Specify a custom kernel base\n"
+            "                                           address (default: 0x10000000).\n"
+            "  --kernel-offset                          Specify a custom kernel offset.\n"
+            "                                           (default: 0x00008000)\n"
+            "  --ramdisk-offset                         Specify a custom ramdisk offset.\n"
+            "                                           (default: 0x01000000)\n"
+            "  --tags-offset                            Specify a custom tags offset.\n"
+            "                                           (default: 0x00000100)\n"
+            "  -n, --page-size <page size>              Specify the nand page size\n"
+            "                                           (default: 2048).\n"
+            "  -S <size>[K|M|G]                         Automatically sparse files greater\n"
+            "                                           than 'size'. 0 to disable.\n"
+            "  --slot <suffix>                          Specify slot suffix to be used if the\n"
+            "                                           device supports slots. This will be\n"
+            "                                           added to all partition names that use\n"
+            "                                           slots. 'all' can be given to refer\n"
+            "                                           to all slots. If this is not given,\n"
+            "                                           slotted partitions will default to\n"
+            "                                           the current active slot.\n"
+            "  -a, --set-active[=<suffix>]              Sets the active slot. If no suffix is\n"
+            "                                           provided, this will default to the value\n"
+            "                                           given by --slot. If slots are not\n"
+            "                                           supported, this does nothing.\n"
+            "  --unbuffered                             Do not buffer input or output.\n"
+            "  --version                                Display version.\n"
+            "  -h, --help                               show this message.\n"
         );
 }
-
-void *load_bootable_image(const char *kernel, const char *ramdisk,
-                          unsigned *sz, const char *cmdline)
-{
-    void *kdata = 0, *rdata = 0;
-    unsigned ksize = 0, rsize = 0;
-    void *bdata;
-    unsigned bsize;
-
-    if(kernel == 0) {
+static void* load_bootable_image(const char* kernel, const char* ramdisk,
+                                 const char* secondstage, int64_t* sz,
+                                 const char* cmdline) {
+    if (kernel == nullptr) {
         fprintf(stderr, "no image specified\n");
         return 0;
     }
 
-    kdata = load_file(kernel, &ksize);
-    if(kdata == 0) {
+    int64_t ksize;
+    void* kdata = load_file(kernel, &ksize);
+    if (kdata == nullptr) {
         fprintf(stderr, "cannot load '%s': %s\n", kernel, strerror(errno));
         return 0;
     }
 
-        /* is this actually a boot image? */
+    // Is this actually a boot image?
     if(!memcmp(kdata, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
-        if(cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
+        if (cmdline) bootimg_set_cmdline((boot_img_hdr*) kdata, cmdline);
 
-        if(ramdisk) {
+        if (ramdisk) {
             fprintf(stderr, "cannot boot a boot.img *and* ramdisk\n");
             return 0;
         }
@@ -360,33 +353,46 @@ void *load_bootable_image(const char *kernel, const char *ramdisk,
         return kdata;
     }
 
-    if(ramdisk) {
+    void* rdata = nullptr;
+    int64_t rsize = 0;
+    if (ramdisk) {
         rdata = load_file(ramdisk, &rsize);
-        if(rdata == 0) {
+        if (rdata == nullptr) {
             fprintf(stderr,"cannot load '%s': %s\n", ramdisk, strerror(errno));
             return  0;
         }
     }
 
+    void* sdata = nullptr;
+    int64_t ssize = 0;
+    if (secondstage) {
+        sdata = load_file(secondstage, &ssize);
+        if (sdata == nullptr) {
+            fprintf(stderr,"cannot load '%s': %s\n", secondstage, strerror(errno));
+            return  0;
+        }
+    }
+
     fprintf(stderr,"creating boot image...\n");
-    bdata = mkbootimg(kdata, ksize, kernel_offset,
+    int64_t bsize = 0;
+    void* bdata = mkbootimg(kdata, ksize, kernel_offset,
                       rdata, rsize, ramdisk_offset,
-                      0, 0, second_offset,
+                      sdata, ssize, second_offset,
                       page_size, base_addr, tags_offset, &bsize);
-    if(bdata == 0) {
+    if (bdata == nullptr) {
         fprintf(stderr,"failed to create boot.img\n");
         return 0;
     }
-    if(cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
-    fprintf(stderr,"creating boot image - %d bytes\n", bsize);
+    if (cmdline) bootimg_set_cmdline((boot_img_hdr*) bdata, cmdline);
+    fprintf(stderr, "creating boot image - %" PRId64 " bytes\n", bsize);
     *sz = bsize;
 
     return bdata;
 }
 
-static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, unsigned* sz)
+static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, int64_t* sz)
 {
-    ZipEntryName zip_entry_name(entry_name);
+    ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
@@ -396,8 +402,8 @@ static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, unsigned*
     *sz = zip_entry.uncompressed_length;
 
     uint8_t* data = reinterpret_cast<uint8_t*>(malloc(zip_entry.uncompressed_length));
-    if (data == NULL) {
-        fprintf(stderr, "failed to allocate %u bytes for '%s'\n", *sz, entry_name);
+    if (data == nullptr) {
+        fprintf(stderr, "failed to allocate %" PRId64 " bytes for '%s'\n", *sz, entry_name);
         return 0;
     }
 
@@ -442,13 +448,13 @@ static FILE* win32_tmpfile() {
 
 static int unzip_to_file(ZipArchiveHandle zip, char* entry_name) {
     FILE* fp = tmpfile();
-    if (fp == NULL) {
+    if (fp == nullptr) {
         fprintf(stderr, "failed to create temporary file for '%s': %s\n",
                 entry_name, strerror(errno));
         return -1;
     }
 
-    ZipEntryName zip_entry_name(entry_name);
+    ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
@@ -482,7 +488,7 @@ static char *strip(char *s)
 static int setup_requirement_line(char *name)
 {
     char *val[MAX_OPTIONS];
-    char *prod = NULL;
+    char *prod = nullptr;
     unsigned n, count;
     char *x;
     int invert = 0;
@@ -543,13 +549,10 @@ static int setup_requirement_line(char *name)
     return 0;
 }
 
-static void setup_requirements(char *data, unsigned sz)
-{
-    char *s;
-
-    s = data;
+static void setup_requirements(char* data, int64_t sz) {
+    char* s = data;
     while (sz-- > 0) {
-        if(*s == '\n') {
+        if (*s == '\n') {
             *s++ = 0;
             if (setup_requirement_line(data)) {
                 die("out of memory");
@@ -561,8 +564,7 @@ static void setup_requirements(char *data, unsigned sz)
     }
 }
 
-void queue_info_dump(void)
-{
+static void queue_info_dump() {
     fb_queue_notice("--------------------------------------------");
     fb_queue_display("version-bootloader", "Bootloader Version...");
     fb_queue_display("version-baseband",   "Baseband Version.....");
@@ -577,7 +579,7 @@ static struct sparse_file **load_sparse_files(int fd, int max_size)
         die("cannot sparse read file\n");
     }
 
-    int files = sparse_file_resparse(s, max_size, NULL, 0);
+    int files = sparse_file_resparse(s, max_size, nullptr, 0);
     if (files < 0) {
         die("Failed to resparse\n");
     }
@@ -595,25 +597,28 @@ static struct sparse_file **load_sparse_files(int fd, int max_size)
     return out_s;
 }
 
-static int64_t get_target_sparse_limit(struct usb_handle *usb)
-{
-    int64_t limit = 0;
-    char response[FB_RESPONSE_SZ + 1];
-    int status = fb_getvar(usb, response, "max-download-size");
-
-    if (!status) {
-        limit = strtoul(response, NULL, 0);
-        if (limit > 0) {
-            fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n",
-                    limit);
-        }
+static int64_t get_target_sparse_limit(usb_handle* usb) {
+    std::string max_download_size;
+    if (!fb_getvar(usb, "max-download-size", &max_download_size) || max_download_size.empty()) {
+        fprintf(stderr, "target didn't report max-download-size\n");
+        return 0;
     }
 
+    // Some bootloaders (angler, for example) send spurious whitespace too.
+    max_download_size = android::base::Trim(max_download_size);
+
+    uint64_t limit;
+    if (!android::base::ParseUint(max_download_size.c_str(), &limit)) {
+        fprintf(stderr, "couldn't parse max-download-size '%s'\n", max_download_size.c_str());
+        return 0;
+    }
+    if (limit > 0) {
+        fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n", limit);
+    }
     return limit;
 }
 
-static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size)
-{
+static int64_t get_sparse_limit(usb_handle* usb, int64_t size) {
     int64_t limit;
 
     if (sparse_limit == 0) {
@@ -638,44 +643,35 @@ static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size)
     return 0;
 }
 
-/* Until we get lazy inode table init working in make_ext4fs, we need to
- * erase partitions of type ext4 before flashing a filesystem so no stale
- * inodes are left lying around.  Otherwise, e2fsck gets very upset.
- */
-static int needs_erase(usb_handle* usb, const char *part)
-{
-    /* The function fb_format_supported() currently returns the value
-     * we want, so just call it.
-     */
-     return fb_format_supported(usb, part, NULL);
+// Until we get lazy inode table init working in make_ext4fs, we need to
+// erase partitions of type ext4 before flashing a filesystem so no stale
+// inodes are left lying around.  Otherwise, e2fsck gets very upset.
+static bool needs_erase(usb_handle* usb, const char* partition) {
+    std::string partition_type;
+    if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
+        return false;
+    }
+    return partition_type == "ext4";
 }
 
-static int load_buf_fd(usb_handle *usb, int fd,
-        struct fastboot_buffer *buf)
-{
-    int64_t sz64;
-    void *data;
-    int64_t limit;
-
-
-    sz64 = file_size(fd);
-    if (sz64 < 0) {
+static int load_buf_fd(usb_handle* usb, int fd, struct fastboot_buffer* buf) {
+    int64_t sz = get_file_size(fd);
+    if (sz == -1) {
         return -1;
     }
 
-    lseek(fd, 0, SEEK_SET);
-    limit = get_sparse_limit(usb, sz64);
+    lseek64(fd, 0, SEEK_SET);
+    int64_t limit = get_sparse_limit(usb, sz);
     if (limit) {
-        struct sparse_file **s = load_sparse_files(fd, limit);
-        if (s == NULL) {
+        sparse_file** s = load_sparse_files(fd, limit);
+        if (s == nullptr) {
             return -1;
         }
         buf->type = FB_BUFFER_SPARSE;
         buf->data = s;
     } else {
-        unsigned int sz;
-        data = load_fd(fd, &sz);
-        if (data == 0) return -1;
+        void* data = load_fd(fd, &sz);
+        if (data == nullptr) return -1;
         buf->type = FB_BUFFER;
         buf->data = data;
         buf->sz = sz;
@@ -705,8 +701,8 @@ static void flash_buf(const char *pname, struct fastboot_buffer *buf)
         case FB_BUFFER_SPARSE:
             s = reinterpret_cast<sparse_file**>(buf->data);
             while (*s) {
-                int64_t sz64 = sparse_file_len(*s, true, false);
-                fb_queue_flash_sparse(pname, *s++, sz64);
+                int64_t sz = sparse_file_len(*s, true, false);
+                fb_queue_flash_sparse(pname, *s++, sz);
             }
             break;
         case FB_BUFFER:
@@ -717,8 +713,81 @@ static void flash_buf(const char *pname, struct fastboot_buffer *buf)
     }
 }
 
-void do_flash(usb_handle *usb, const char *pname, const char *fname)
-{
+static std::vector<std::string> get_suffixes(usb_handle* usb) {
+    std::vector<std::string> suffixes;
+    std::string suffix_list;
+    if (!fb_getvar(usb, "slot-suffixes", &suffix_list)) {
+        die("Could not get suffixes.\n");
+    }
+    return android::base::Split(suffix_list, ",");
+}
+
+static std::string verify_slot(usb_handle* usb, const char *slot) {
+    if (strcmp(slot, "all") == 0) {
+        return "all";
+    }
+    std::vector<std::string> suffixes = get_suffixes(usb);
+    for (const std::string &suffix : suffixes) {
+        if (suffix == slot)
+            return slot;
+    }
+    fprintf(stderr, "Slot %s does not exist. supported slots are:\n", slot);
+    for (const std::string &suffix : suffixes) {
+        fprintf(stderr, "%s\n", suffix.c_str());
+    }
+    exit(1);
+}
+
+static void do_for_partition(usb_handle* usb, const char *part, const char *slot, std::function<void(const std::string&)> func, bool force_slot) {
+    std::string has_slot;
+    std::string current_slot;
+
+    if (!fb_getvar(usb, std::string("has-slot:")+part, &has_slot)) {
+        /* If has-slot is not supported, the answer is no. */
+        has_slot = "no";
+    }
+    if (has_slot == "yes") {
+        if (!slot || slot[0] == 0) {
+            if (!fb_getvar(usb, "current-slot", &current_slot)) {
+                die("Failed to identify current slot.\n");
+            }
+            func(std::string(part) + current_slot);
+        } else {
+            func(std::string(part) + slot);
+        }
+    } else {
+        if (force_slot && slot && slot[0]) {
+             fprintf(stderr, "Warning: %s does not support slots, and slot %s was requested.\n", part, slot);
+        }
+        func(part);
+    }
+}
+
+/* This function will find the real partition name given a base name, and a slot. If slot is NULL or empty,
+ * it will use the current slot. If slot is "all", it will return a list of all possible partition names.
+ * If force_slot is true, it will fail if a slot is specified, and the given partition does not support slots.
+ */
+static void do_for_partitions(usb_handle* usb, const char *part, const char *slot, std::function<void(const std::string&)> func, bool force_slot) {
+    std::string has_slot;
+
+    if (slot && strcmp(slot, "all") == 0) {
+        if (!fb_getvar(usb, std::string("has-slot:") + part, &has_slot)) {
+            die("Could not check if partition %s has slot.", part);
+        }
+        if (has_slot == "yes") {
+            std::vector<std::string> suffixes = get_suffixes(usb);
+            for (std::string &suffix : suffixes) {
+                do_for_partition(usb, part, suffix.c_str(), func, force_slot);
+            }
+        } else {
+            do_for_partition(usb, part, "", func, force_slot);
+        }
+    } else {
+        do_for_partition(usb, part, slot, func, force_slot);
+    }
+}
+
+static void do_flash(usb_handle* usb, const char* pname, const char* fname) {
     struct fastboot_buffer buf;
 
     if (load_buf(usb, fname, &buf)) {
@@ -727,17 +796,15 @@ void do_flash(usb_handle *usb, const char *pname, const char *fname)
     flash_buf(pname, &buf);
 }
 
-void do_update_signature(ZipArchiveHandle zip, char *fn)
-{
-    unsigned sz;
+static void do_update_signature(ZipArchiveHandle zip, char* fn) {
+    int64_t sz;
     void* data = unzip_file(zip, fn, &sz);
-    if (data == 0) return;
+    if (data == nullptr) return;
     fb_queue_download("signature", data, sz);
     fb_queue_command("signature", "installing signature");
 }
 
-void do_update(usb_handle *usb, const char *filename, int erase_first)
-{
+static void do_update(usb_handle* usb, const char* filename, const char* slot_override, bool erase_first) {
     queue_info_dump();
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
@@ -749,9 +816,9 @@ void do_update(usb_handle *usb, const char *filename, int erase_first)
         die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
     }
 
-    unsigned sz;
+    int64_t sz;
     void* data = unzip_file(zip, "android-info.txt", &sz);
-    if (data == 0) {
+    if (data == nullptr) {
         CloseArchive(zip);
         die("update package '%s' has no android-info.txt", filename);
     }
@@ -770,50 +837,51 @@ void do_update(usb_handle *usb, const char *filename, int erase_first)
         fastboot_buffer buf;
         int rc = load_buf_fd(usb, fd, &buf);
         if (rc) die("cannot load %s from flash", images[i].img_name);
-        do_update_signature(zip, images[i].sig_name);
-        if (erase_first && needs_erase(usb, images[i].part_name)) {
-            fb_queue_erase(images[i].part_name);
-        }
-        flash_buf(images[i].part_name, &buf);
-        /* not closing the fd here since the sparse code keeps the fd around
-         * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
-         * program exits.
-         */
+
+        auto update = [&](const std::string &partition) {
+            do_update_signature(zip, images[i].sig_name);
+            if (erase_first && needs_erase(usb, partition.c_str())) {
+                fb_queue_erase(partition.c_str());
+            }
+            flash_buf(partition.c_str(), &buf);
+            /* not closing the fd here since the sparse code keeps the fd around
+             * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
+             * program exits.
+             */
+        };
+        do_for_partitions(usb, images[i].part_name, slot_override, update, false);
     }
 
     CloseArchive(zip);
 }
 
-void do_send_signature(char *fn)
-{
-    void *data;
-    unsigned sz;
-    char *xtn;
-
-    xtn = strrchr(fn, '.');
+static void do_send_signature(char* fn) {
+    char* xtn = strrchr(fn, '.');
     if (!xtn) return;
+
     if (strcmp(xtn, ".img")) return;
 
-    strcpy(xtn,".sig");
-    data = load_file(fn, &sz);
-    strcpy(xtn,".img");
-    if (data == 0) return;
+    strcpy(xtn, ".sig");
+
+    int64_t sz;
+    void* data = load_file(fn, &sz);
+    strcpy(xtn, ".img");
+    if (data == nullptr) return;
     fb_queue_download("signature", data, sz);
     fb_queue_command("signature", "installing signature");
 }
 
-void do_flashall(usb_handle *usb, int erase_first)
-{
+static void do_flashall(usb_handle* usb, const char *slot_override, int erase_first) {
     queue_info_dump();
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
 
     char* fname = find_item("info", product);
-    if (fname == 0) die("cannot find android-info.txt");
+    if (fname == nullptr) die("cannot find android-info.txt");
 
-    unsigned sz;
+    int64_t sz;
     void* data = load_file(fname, &sz);
-    if (data == 0) die("could not load android-info.txt: %s", strerror(errno));
+    if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
 
     setup_requirements(reinterpret_cast<char*>(data), sz);
 
@@ -825,22 +893,23 @@ void do_flashall(usb_handle *usb, int erase_first)
                 continue;
             die("could not load %s\n", images[i].img_name);
         }
-        do_send_signature(fname);
-        if (erase_first && needs_erase(usb, images[i].part_name)) {
-            fb_queue_erase(images[i].part_name);
-        }
-        flash_buf(images[i].part_name, &buf);
+
+        auto flashall = [&](const std::string &partition) {
+            do_send_signature(fname);
+            if (erase_first && needs_erase(usb, partition.c_str())) {
+                fb_queue_erase(partition.c_str());
+            }
+            flash_buf(partition.c_str(), &buf);
+        };
+        do_for_partitions(usb, images[i].part_name, slot_override, flashall, false);
     }
 }
 
 #define skip(n) do { argc -= (n); argv += (n); } while (0)
 #define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0)
 
-int do_bypass_unlock_command(int argc, char **argv)
+static int do_bypass_unlock_command(int argc, char **argv)
 {
-    unsigned sz;
-    void *data;
-
     if (argc <= 2) return 0;
     skip(2);
 
@@ -849,15 +918,17 @@ int do_bypass_unlock_command(int argc, char **argv)
      * and send that to the remote device.
      */
     require(1);
-    data = load_file(*argv, &sz);
-    if (data == 0) die("could not load '%s': %s", *argv, strerror(errno));
+
+    int64_t sz;
+    void* data = load_file(*argv, &sz);
+    if (data == nullptr) die("could not load '%s': %s", *argv, strerror(errno));
     fb_queue_download("unlock_message", data, sz);
     fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
     skip(1);
     return 0;
 }
 
-int do_oem_command(int argc, char **argv)
+static int do_oem_command(int argc, char **argv)
 {
     char command[256];
     if (argc <= 1) return 0;
@@ -915,126 +986,140 @@ static int64_t parse_num(const char *arg)
     return num;
 }
 
-void fb_perform_format(usb_handle* usb,
-                       const char *partition, int skip_if_not_supported,
-                       const char *type_override, const char *size_override)
-{
-    char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1];
-    char *pType = pTypeBuff;
-    char *pSize = pSizeBuff;
-    unsigned int limit = INT_MAX;
+static void fb_perform_format(usb_handle* usb,
+                              const char* partition, int skip_if_not_supported,
+                              const char* type_override, const char* size_override) {
+    std::string partition_type, partition_size;
+
     struct fastboot_buffer buf;
-    const char *errMsg = NULL;
-    const struct fs_generator *gen;
-    uint64_t pSz;
-    int status;
+    const char* errMsg = nullptr;
+    const struct fs_generator* gen = nullptr;
     int fd;
 
-    if (target_sparse_limit > 0 && target_sparse_limit < limit)
+    unsigned int limit = INT_MAX;
+    if (target_sparse_limit > 0 && target_sparse_limit < limit) {
         limit = target_sparse_limit;
-    if (sparse_limit > 0 && sparse_limit < limit)
+    }
+    if (sparse_limit > 0 && sparse_limit < limit) {
         limit = sparse_limit;
+    }
 
-    status = fb_getvar(usb, pType, "partition-type:%s", partition);
-    if (status) {
+    if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
     if (type_override) {
-        if (strcmp(type_override, pType)) {
-            fprintf(stderr,
-                    "Warning: %s type is %s, but %s was requested for formating.\n",
-                    partition, pType, type_override);
+        if (partition_type != type_override) {
+            fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
+                    partition, partition_type.c_str(), type_override);
         }
-        pType = (char *)type_override;
+        partition_type = type_override;
     }
 
-    status = fb_getvar(usb, pSize, "partition-size:%s", partition);
-    if (status) {
+    if (!fb_getvar(usb, std::string("partition-size:") + partition, &partition_size)) {
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
     if (size_override) {
-        if (strcmp(size_override, pSize)) {
-            fprintf(stderr,
-                    "Warning: %s size is %s, but %s was requested for formating.\n",
-                    partition, pSize, size_override);
+        if (partition_size != size_override) {
+            fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
+                    partition, partition_size.c_str(), size_override);
         }
-        pSize = (char *)size_override;
+        partition_size = size_override;
     }
+    // Some bootloaders (angler, for example), send spurious leading whitespace.
+    partition_size = android::base::Trim(partition_size);
+    // Some bootloaders (hammerhead, for example) use implicit hex.
+    // This code used to use strtol with base 16.
+    if (!android::base::StartsWith(partition_size, "0x")) partition_size = "0x" + partition_size;
 
-    gen = fs_get_generator(pType);
+    gen = fs_get_generator(partition_type);
     if (!gen) {
         if (skip_if_not_supported) {
             fprintf(stderr, "Erase successful, but not automatically formatting.\n");
-            fprintf(stderr, "File system type %s not supported.\n", pType);
+            fprintf(stderr, "File system type %s not supported.\n", partition_type.c_str());
             return;
         }
-        fprintf(stderr, "Formatting is not supported for filesystem with type '%s'.\n", pType);
+        fprintf(stderr, "Formatting is not supported for file system with type '%s'.\n",
+                partition_type.c_str());
         return;
     }
 
-    pSz = strtoll(pSize, (char **)NULL, 16);
+    int64_t size;
+    if (!android::base::ParseInt(partition_size.c_str(), &size)) {
+        fprintf(stderr, "Couldn't parse partition size '%s'.\n", partition_size.c_str());
+        return;
+    }
 
     fd = fileno(tmpfile());
-    if (fs_generator_generate(gen, fd, pSz)) {
+    if (fs_generator_generate(gen, fd, size)) {
+        fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
         close(fd);
-        fprintf(stderr, "Cannot generate image.\n");
         return;
     }
 
     if (load_buf_fd(usb, fd, &buf)) {
-        fprintf(stderr, "Cannot read image.\n");
+        fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
         close(fd);
         return;
     }
     flash_buf(partition, &buf);
-
     return;
 
-
 failed:
     if (skip_if_not_supported) {
         fprintf(stderr, "Erase successful, but not automatically formatting.\n");
-        if (errMsg)
-            fprintf(stderr, "%s", errMsg);
+        if (errMsg) fprintf(stderr, "%s", errMsg);
     }
     fprintf(stderr,"FAILED (%s)\n", fb_get_error());
 }
 
 int main(int argc, char **argv)
 {
-    int wants_wipe = 0;
-    int wants_reboot = 0;
-    int wants_reboot_bootloader = 0;
-    int erase_first = 1;
+    bool wants_wipe = false;
+    bool wants_reboot = false;
+    bool wants_reboot_bootloader = false;
+    bool wants_set_active = false;
+    bool erase_first = true;
     void *data;
-    unsigned sz;
-    int status;
-    int c;
+    int64_t sz;
     int longindex;
+    std::string slot_override;
+    std::string next_active;
 
     const struct option longopts[] = {
         {"base", required_argument, 0, 'b'},
         {"kernel_offset", required_argument, 0, 'k'},
+        {"kernel-offset", required_argument, 0, 'k'},
         {"page_size", required_argument, 0, 'n'},
+        {"page-size", required_argument, 0, 'n'},
         {"ramdisk_offset", required_argument, 0, 'r'},
+        {"ramdisk-offset", required_argument, 0, 'r'},
         {"tags_offset", required_argument, 0, 't'},
+        {"tags-offset", required_argument, 0, 't'},
         {"help", no_argument, 0, 'h'},
         {"unbuffered", no_argument, 0, 0},
         {"version", no_argument, 0, 0},
+        {"slot", required_argument, 0, 0},
+        {"set_active", optional_argument, 0, 'a'},
+        {"set-active", optional_argument, 0, 'a'},
         {0, 0, 0, 0}
     };
 
     serial = getenv("ANDROID_SERIAL");
 
     while (1) {
-        c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
+        int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:ha::", longopts, &longindex);
         if (c < 0) {
             break;
         }
         /* Alphabetical cases */
         switch (c) {
+        case 'a':
+            wants_set_active = true;
+            if (optarg)
+                next_active = optarg;
+            break;
         case 'b':
             base_addr = strtoul(optarg, 0, 16);
             break;
@@ -1045,7 +1130,7 @@ int main(int argc, char **argv)
             usage();
             return 1;
         case 'i': {
-                char *endptr = NULL;
+                char *endptr = nullptr;
                 unsigned long val;
 
                 val = strtoul(optarg, &endptr, 0);
@@ -1061,7 +1146,7 @@ int main(int argc, char **argv)
             long_listing = 1;
             break;
         case 'n':
-            page_size = (unsigned)strtoul(optarg, NULL, 0);
+            page_size = (unsigned)strtoul(optarg, nullptr, 0);
             if (!page_size) die("invalid page size");
             break;
         case 'p':
@@ -1083,20 +1168,22 @@ int main(int argc, char **argv)
             }
             break;
         case 'u':
-            erase_first = 0;
+            erase_first = false;
             break;
         case 'w':
-            wants_wipe = 1;
+            wants_wipe = true;
             break;
         case '?':
             return 1;
         case 0:
             if (strcmp("unbuffered", longopts[longindex].name) == 0) {
-                setvbuf(stdout, NULL, _IONBF, 0);
-                setvbuf(stderr, NULL, _IONBF, 0);
+                setvbuf(stdout, nullptr, _IONBF, 0);
+                setvbuf(stderr, nullptr, _IONBF, 0);
             } else if (strcmp("version", longopts[longindex].name) == 0) {
                 fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
                 return 0;
+            } else if (strcmp("slot", longopts[longindex].name) == 0) {
+                slot_override = std::string(optarg);
             }
             break;
         default:
@@ -1107,7 +1194,7 @@ int main(int argc, char **argv)
     argc -= optind;
     argv += optind;
 
-    if (argc == 0 && !wants_wipe) {
+    if (argc == 0 && !wants_wipe && !wants_set_active) {
         usage();
         return 1;
     }
@@ -1124,25 +1211,45 @@ int main(int argc, char **argv)
     }
 
     usb_handle* usb = open_device();
+    if (slot_override != "")
+        slot_override = verify_slot(usb, slot_override.c_str());
+    if (next_active != "")
+        next_active = verify_slot(usb, next_active.c_str());
+
+    if (wants_set_active) {
+        if (next_active == "") {
+            if (slot_override == "") {
+                wants_set_active = false;
+            } else {
+                next_active = slot_override;
+            }
+        }
+    }
 
     while (argc > 0) {
-        if(!strcmp(*argv, "getvar")) {
+        if (!strcmp(*argv, "getvar")) {
             require(2);
             fb_queue_display(argv[1], argv[1]);
             skip(2);
         } else if(!strcmp(*argv, "erase")) {
             require(2);
 
-            if (fb_format_supported(usb, argv[1], NULL)) {
-                fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
-            }
+            auto erase = [&](const std::string &partition) {
+            std::string partition_type;
+                if (fb_getvar(usb, std::string("partition-type:") + argv[1], &partition_type) &&
+                    fs_get_generator(partition_type) != nullptr) {
+                    fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
+                            partition_type.c_str());
+                }
 
-            fb_queue_erase(argv[1]);
+                fb_queue_erase(partition.c_str());
+            };
+            do_for_partitions(usb, argv[1], slot_override.c_str(), erase, true);
             skip(2);
         } else if(!strncmp(*argv, "format", strlen("format"))) {
             char *overrides;
-            char *type_override = NULL;
-            char *size_override = NULL;
+            char *type_override = nullptr;
+            char *size_override = nullptr;
             require(2);
             /*
              * Parsing for: "format[:[type][:[size]]]"
@@ -1163,34 +1270,38 @@ int main(int argc, char **argv)
                 }
                 type_override = overrides;
             }
-            if (type_override && !type_override[0]) type_override = NULL;
-            if (size_override && !size_override[0]) size_override = NULL;
-            if (erase_first && needs_erase(usb, argv[1])) {
-                fb_queue_erase(argv[1]);
-            }
-            fb_perform_format(usb, argv[1], 0, type_override, size_override);
+            if (type_override && !type_override[0]) type_override = nullptr;
+            if (size_override && !size_override[0]) size_override = nullptr;
+
+            auto format = [&](const std::string &partition) {
+                if (erase_first && needs_erase(usb, partition.c_str())) {
+                    fb_queue_erase(partition.c_str());
+                }
+                fb_perform_format(usb, partition.c_str(), 0, type_override, size_override);
+            };
+            do_for_partitions(usb, argv[1], slot_override.c_str(), format, true);
             skip(2);
         } else if(!strcmp(*argv, "signature")) {
             require(2);
             data = load_file(argv[1], &sz);
-            if (data == 0) die("could not load '%s': %s", argv[1], strerror(errno));
+            if (data == nullptr) die("could not load '%s': %s", argv[1], strerror(errno));
             if (sz != 256) die("signature must be 256 bytes");
             fb_queue_download("signature", data, sz);
             fb_queue_command("signature", "installing signature");
             skip(2);
         } else if(!strcmp(*argv, "reboot")) {
-            wants_reboot = 1;
+            wants_reboot = true;
             skip(1);
             if (argc > 0) {
                 if (!strcmp(*argv, "bootloader")) {
-                    wants_reboot = 0;
-                    wants_reboot_bootloader = 1;
+                    wants_reboot = false;
+                    wants_reboot_bootloader = true;
                     skip(1);
                 }
             }
             require(0);
         } else if(!strcmp(*argv, "reboot-bootloader")) {
-            wants_reboot_bootloader = 1;
+            wants_reboot_bootloader = true;
             skip(1);
         } else if (!strcmp(*argv, "continue")) {
             fb_queue_command("continue", "resuming boot");
@@ -1198,6 +1309,7 @@ int main(int argc, char **argv)
         } else if(!strcmp(*argv, "boot")) {
             char *kname = 0;
             char *rname = 0;
+            char *sname = 0;
             skip(1);
             if (argc > 0) {
                 kname = argv[0];
@@ -1207,7 +1319,11 @@ int main(int argc, char **argv)
                 rname = argv[0];
                 skip(1);
             }
-            data = load_bootable_image(kname, rname, &sz, cmdline);
+            if (argc > 0) {
+                sname = argv[0];
+                skip(1);
+            }
+            data = load_bootable_image(kname, rname, sname, &sz, cmdline);
             if (data == 0) return 1;
             fb_queue_download("boot.img", data, sz);
             fb_queue_command("boot", "booting");
@@ -1223,37 +1339,47 @@ int main(int argc, char **argv)
                 skip(2);
             }
             if (fname == 0) die("cannot determine image filename for '%s'", pname);
-            if (erase_first && needs_erase(usb, pname)) {
-                fb_queue_erase(pname);
-            }
-            do_flash(usb, pname, fname);
+
+            auto flash = [&](const std::string &partition) {
+                if (erase_first && needs_erase(usb, partition.c_str())) {
+                    fb_queue_erase(partition.c_str());
+                }
+                do_flash(usb, partition.c_str(), fname);
+            };
+            do_for_partitions(usb, pname, slot_override.c_str(), flash, true);
         } else if(!strcmp(*argv, "flash:raw")) {
-            char *pname = argv[1];
             char *kname = argv[2];
             char *rname = 0;
+            char *sname = 0;
             require(3);
-            if(argc > 3) {
-                rname = argv[3];
-                skip(4);
-            } else {
-                skip(3);
+            skip(3);
+            if (argc > 0) {
+                rname = argv[0];
+                skip(1);
             }
-            data = load_bootable_image(kname, rname, &sz, cmdline);
+            if (argc > 0) {
+                sname = argv[0];
+                skip(1);
+            }
+            data = load_bootable_image(kname, rname, sname, &sz, cmdline);
             if (data == 0) die("cannot load bootable image");
-            fb_queue_flash(pname, data, sz);
+            auto flashraw = [&](const std::string &partition) {
+                fb_queue_flash(partition.c_str(), data, sz);
+            };
+            do_for_partitions(usb, argv[1], slot_override.c_str(), flashraw, true);
         } else if(!strcmp(*argv, "flashall")) {
             skip(1);
-            do_flashall(usb, erase_first);
-            wants_reboot = 1;
+            do_flashall(usb, slot_override.c_str(), erase_first);
+            wants_reboot = true;
         } else if(!strcmp(*argv, "update")) {
             if (argc > 1) {
-                do_update(usb, argv[1], erase_first);
+                do_update(usb, argv[1], slot_override.c_str(), erase_first);
                 skip(2);
             } else {
-                do_update(usb, "update.zip", erase_first);
+                do_update(usb, "update.zip", slot_override.c_str(), erase_first);
                 skip(1);
             }
-            wants_reboot = 1;
+            wants_reboot = true;
         } else if(!strcmp(*argv, "oem")) {
             argc = do_oem_command(argc, argv);
         } else if(!strcmp(*argv, "flashing")) {
@@ -1279,10 +1405,19 @@ int main(int argc, char **argv)
     }
 
     if (wants_wipe) {
+        fprintf(stderr, "wiping userdata...\n");
         fb_queue_erase("userdata");
-        fb_perform_format(usb, "userdata", 1, NULL, NULL);
-        fb_queue_erase("cache");
-        fb_perform_format(usb, "cache", 1, NULL, NULL);
+        fb_perform_format(usb, "userdata", 1, nullptr, nullptr);
+
+        std::string cache_type;
+        if (fb_getvar(usb, "partition-type:cache", &cache_type) && !cache_type.empty()) {
+            fprintf(stderr, "wiping cache...\n");
+            fb_queue_erase("cache");
+            fb_perform_format(usb, "cache", 1, nullptr, nullptr);
+        }
+    }
+    if (wants_set_active) {
+        fb_set_active(next_active.c_str());
     }
     if (wants_reboot) {
         fb_queue_reboot();
@@ -1292,9 +1427,5 @@ int main(int argc, char **argv)
         fb_queue_wait_for_disconnect();
     }
 
-    if (fb_queue_is_empty())
-        return 0;
-
-    status = fb_execute_queue(usb);
-    return (status) ? 1 : 0;
+    return fb_execute_queue(usb) ? EXIT_FAILURE : EXIT_SUCCESS;
 }
index 481c501..784ec5c 100644 (file)
 #ifndef _FASTBOOT_H_
 #define _FASTBOOT_H_
 
-#include "usb.h"
+#include <inttypes.h>
+#include <stdlib.h>
 
-#if defined(__cplusplus)
-extern "C" {
-#endif
+#include <string>
+
+#include "usb.h"
 
 struct sparse_file;
 
 /* protocol.c - fastboot protocol */
 int fb_command(usb_handle *usb, const char *cmd);
 int fb_command_response(usb_handle *usb, const char *cmd, char *response);
-int fb_download_data(usb_handle *usb, const void *data, unsigned size);
+int fb_download_data(usb_handle *usb, const void *data, uint32_t size);
 int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s);
 char *fb_get_error(void);
 
@@ -48,23 +49,22 @@ char *fb_get_error(void);
 #define FB_RESPONSE_SZ 64
 
 /* engine.c - high level command queue engine */
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...);
-int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override);
-void fb_queue_flash(const char *ptn, void *data, unsigned sz);
-void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz);
+bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value);
+void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
+void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, uint32_t sz);
 void fb_queue_erase(const char *ptn);
-void fb_queue_format(const char *ptn, int skip_if_not_supported, unsigned int max_chunk_sz);
-void fb_queue_require(const char *prod, const char *var, int invert,
-        unsigned nvalues, const char **value);
+void fb_queue_format(const char *ptn, int skip_if_not_supported, int32_t max_chunk_sz);
+void fb_queue_require(const char *prod, const char *var, bool invert,
+                      size_t nvalues, const char **value);
 void fb_queue_display(const char *var, const char *prettyname);
-void fb_queue_query_save(const char *var, char *dest, unsigned dest_size);
+void fb_queue_query_save(const char *var, char *dest, uint32_t dest_size);
 void fb_queue_reboot(void);
 void fb_queue_command(const char *cmd, const char *msg);
-void fb_queue_download(const char *name, void *data, unsigned size);
+void fb_queue_download(const char *name, void *data, uint32_t size);
 void fb_queue_notice(const char *notice);
 void fb_queue_wait_for_disconnect(void);
 int fb_execute_queue(usb_handle *usb);
-int fb_queue_is_empty(void);
+void fb_set_active(const char *slot);
 
 /* util stuff */
 double now();
@@ -76,8 +76,4 @@ void get_my_path(char *path);
 /* Current product */
 extern char cur_product[FB_RESPONSE_SZ + 1];
 
-#if defined(__cplusplus)
-}
-#endif
-
 #endif
index 37b1959..bb73d8a 100644 (file)
@@ -41,7 +41,7 @@ Transport and Framing
 
    d. DATA -> the requested command is ready for the data phase.
       A DATA response packet will be 12 bytes long, in the form of
-      DATA00000000 where the 8 digit hexidecimal number represents
+      DATA00000000 where the 8 digit hexadecimal number represents
       the total data size to transfer.
 
 3. Data phase.  Depending on the command, the host or client will 
similarity index 69%
rename from fastboot/fs.c
rename to fastboot/fs.cpp
index 8a15e6f..90d8474 100644 (file)
@@ -6,21 +6,12 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <stdarg.h>
-#include <stdbool.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sparse/sparse.h>
 #include <unistd.h>
 
-#ifdef USE_MINGW
-#include <fcntl.h>
-#else
-#include <sys/mman.h>
-#endif
-
-
+#include <sparse/sparse.h>
 
 static int generate_ext4_image(int fd, long long partSize)
 {
@@ -38,7 +29,7 @@ static int generate_f2fs_image(int fd, long long partSize)
 
 static const struct fs_generator {
 
-    char *fs_type;  //must match what fastboot reports for partition type
+    const char* fs_type;  //must match what fastboot reports for partition type
     int (*generate)(int fd, long long partSize); //returns 0 or error value
 
 } generators[] = {
@@ -48,15 +39,13 @@ static const struct fs_generator {
 #endif
 };
 
-const struct fs_generator* fs_get_generator(const char *fs_type)
-{
-    unsigned i;
-
-    for (i = 0; i < sizeof(generators) / sizeof(*generators); i++)
-        if (!strcmp(generators[i].fs_type, fs_type))
+const struct fs_generator* fs_get_generator(const std::string& fs_type) {
+    for (size_t i = 0; i < sizeof(generators) / sizeof(*generators); i++) {
+        if (fs_type == generators[i].fs_type) {
             return generators + i;
-
-    return NULL;
+        }
+    }
+    return nullptr;
 }
 
 int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize)
index 307772b..289488b 100644 (file)
@@ -3,18 +3,9 @@
 
 #include <stdint.h>
 
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
 struct fs_generator;
 
-const struct fs_generator* fs_get_generator(const char *fs_type);
+const struct fs_generator* fs_get_generator(const std::string& fs_type);
 int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize);
 
-#if defined(__cplusplus)
-}
 #endif
-
-#endif
-
diff --git a/fastboot/p12topem.sh b/fastboot/p12topem.sh
deleted file mode 100755 (executable)
index f081eb5..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-
-if [ $# -ne 2 ]
-then
- echo "Usage: $0 alias passphrase"
- exit -1
-fi
-
-openssl pkcs12 -passin pass:"$2" -passout pass:"$2" -in $1.p12 -out $1.pem
similarity index 68%
rename from fastboot/protocol.c
rename to fastboot/protocol.cpp
index 5b97600..cbd48e8 100644 (file)
@@ -26,8 +26,6 @@
  * SUCH DAMAGE.
  */
 
-#define min(a, b) \
-    ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
 #define round_down(a, b) \
     ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
 
@@ -36,6 +34,8 @@
 #include <string.h>
 #include <errno.h>
 
+#include <algorithm>
+
 #include <sparse/sparse.h>
 
 #include "fastboot.h"
@@ -47,40 +47,38 @@ char *fb_get_error(void)
     return ERROR;
 }
 
-static int check_response(usb_handle *usb, unsigned int size, char *response)
-{
-    unsigned char status[65];
-    int r;
+static int check_response(usb_handle* usb, uint32_t size, char* response) {
+    char status[65];
 
-    for(;;) {
-        r = usb_read(usb, status, 64);
-        if(r < 0) {
+    while (true) {
+        int r = usb_read(usb, status, 64);
+        if (r < 0) {
             sprintf(ERROR, "status read failed (%s)", strerror(errno));
             usb_close(usb);
             return -1;
         }
         status[r] = 0;
 
-        if(r < 4) {
+        if (r < 4) {
             sprintf(ERROR, "status malformed (%d bytes)", r);
             usb_close(usb);
             return -1;
         }
 
-        if(!memcmp(status, "INFO", 4)) {
+        if (!memcmp(status, "INFO", 4)) {
             fprintf(stderr,"(bootloader) %s\n", status + 4);
             continue;
         }
 
-        if(!memcmp(status, "OKAY", 4)) {
-            if(response) {
+        if (!memcmp(status, "OKAY", 4)) {
+            if (response) {
                 strcpy(response, (char*) status + 4);
             }
             return 0;
         }
 
-        if(!memcmp(status, "FAIL", 4)) {
-            if(r > 4) {
+        if (!memcmp(status, "FAIL", 4)) {
+            if (r > 4) {
                 sprintf(ERROR, "remote: %s", status + 4);
             } else {
                 strcpy(ERROR, "remote failure");
@@ -88,9 +86,9 @@ static int check_response(usb_handle *usb, unsigned int size, char *response)
             return -1;
         }
 
-        if(!memcmp(status, "DATA", 4) && size > 0){
-            unsigned dsize = strtoul((char*) status + 4, 0, 16);
-            if(dsize > size) {
+        if (!memcmp(status, "DATA", 4) && size > 0){
+            uint32_t dsize = strtol(status + 4, 0, 16);
+            if (dsize > size) {
                 strcpy(ERROR, "data size too large");
                 usb_close(usb);
                 return -1;
@@ -106,22 +104,19 @@ static int check_response(usb_handle *usb, unsigned int size, char *response)
     return -1;
 }
 
-static int _command_start(usb_handle *usb, const char *cmd, unsigned size,
-                          char *response)
-{
-    int cmdsize = strlen(cmd);
-
-    if(response) {
-        response[0] = 0;
+static int _command_start(usb_handle* usb, const char* cmd, uint32_t size, char* response) {
+    size_t cmdsize = strlen(cmd);
+    if (cmdsize > 64) {
+        sprintf(ERROR, "command too large");
+        return -1;
     }
 
-    if(cmdsize > 64) {
-        sprintf(ERROR,"command too large");
-        return -1;
+    if (response) {
+        response[0] = 0;
     }
 
-    if(usb_write(usb, cmd, cmdsize) != cmdsize) {
-        sprintf(ERROR,"command write failed (%s)", strerror(errno));
+    if (usb_write(usb, cmd, cmdsize) != static_cast<int>(cmdsize)) {
+        sprintf(ERROR, "command write failed (%s)", strerror(errno));
         usb_close(usb);
         return -1;
     }
@@ -129,45 +124,32 @@ static int _command_start(usb_handle *usb, const char *cmd, unsigned size,
     return check_response(usb, size, response);
 }
 
-static int _command_data(usb_handle *usb, const void *data, unsigned size)
-{
-    int r;
-
-    r = usb_write(usb, data, size);
-    if(r < 0) {
+static int _command_data(usb_handle* usb, const void* data, uint32_t size) {
+    int r = usb_write(usb, data, size);
+    if (r < 0) {
         sprintf(ERROR, "data transfer failure (%s)", strerror(errno));
         usb_close(usb);
         return -1;
     }
-    if(r != ((int) size)) {
+    if (r != ((int) size)) {
         sprintf(ERROR, "data transfer failure (short transfer)");
         usb_close(usb);
         return -1;
     }
-
     return r;
 }
 
-static int _command_end(usb_handle *usb)
-{
-    int r;
-    r = check_response(usb, 0, 0);
-    if(r < 0) {
-        return -1;
-    }
-    return 0;
+static int _command_end(usb_handle* usb) {
+    return check_response(usb, 0, 0) < 0 ? -1 : 0;
 }
 
-static int _command_send(usb_handle *usb, const char *cmd,
-                         const void *data, unsigned size,
-                         char *response)
-{
-    int r;
+static int _command_send(usb_handle* usb, const char* cmd, const void* data, uint32_t size,
+                         char* response) {
     if (size == 0) {
         return -1;
     }
 
-    r = _command_start(usb, cmd, size, response);
+    int r = _command_start(usb, cmd, size, response);
     if (r < 0) {
         return -1;
     }
@@ -178,42 +160,29 @@ static int _command_send(usb_handle *usb, const char *cmd,
     }
 
     r = _command_end(usb);
-    if(r < 0) {
+    if (r < 0) {
         return -1;
     }
 
     return size;
 }
 
-static int _command_send_no_data(usb_handle *usb, const char *cmd,
-                                 char *response)
-{
+static int _command_send_no_data(usb_handle* usb, const char* cmd, char* response) {
     return _command_start(usb, cmd, 0, response);
 }
 
-int fb_command(usb_handle *usb, const char *cmd)
-{
+int fb_command(usb_handle* usb, const char* cmd) {
     return _command_send_no_data(usb, cmd, 0);
 }
 
-int fb_command_response(usb_handle *usb, const char *cmd, char *response)
-{
+int fb_command_response(usb_handle* usb, const char* cmd, char* response) {
     return _command_send_no_data(usb, cmd, response);
 }
 
-int fb_download_data(usb_handle *usb, const void *data, unsigned size)
-{
+int fb_download_data(usb_handle* usb, const void* data, uint32_t size) {
     char cmd[64];
-    int r;
-
     sprintf(cmd, "download:%08x", size);
-    r = _command_send(usb, cmd, data, size, 0);
-
-    if(r < 0) {
-        return -1;
-    } else {
-        return 0;
-    }
+    return _command_send(usb, cmd, data, size, 0) < 0 ? -1 : 0;
 }
 
 #define USB_BUF_SIZE 1024
@@ -223,12 +192,12 @@ static int usb_buf_len;
 static int fb_download_data_sparse_write(void *priv, const void *data, int len)
 {
     int r;
-    usb_handle *usb = priv;
+    usb_handle* usb = reinterpret_cast<usb_handle*>(priv);
     int to_write;
-    const char *ptr = data;
+    const char* ptr = reinterpret_cast<const char*>(data);
 
     if (usb_buf_len) {
-        to_write = min(USB_BUF_SIZE - usb_buf_len, len);
+        to_write = std::min(USB_BUF_SIZE - usb_buf_len, len);
 
         memcpy(usb_buf + usb_buf_len, ptr, to_write);
         usb_buf_len += to_write;
@@ -270,32 +239,25 @@ static int fb_download_data_sparse_write(void *priv, const void *data, int len)
     return 0;
 }
 
-static int fb_download_data_sparse_flush(usb_handle *usb)
-{
-    int r;
-
+static int fb_download_data_sparse_flush(usb_handle* usb) {
     if (usb_buf_len > 0) {
-        r = _command_data(usb, usb_buf, usb_buf_len);
-        if (r != usb_buf_len) {
+        if (_command_data(usb, usb_buf, usb_buf_len) != usb_buf_len) {
             return -1;
         }
         usb_buf_len = 0;
     }
-
     return 0;
 }
 
-int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s)
-{
-    char cmd[64];
-    int r;
+int fb_download_data_sparse(usb_handle* usb, struct sparse_file* s) {
     int size = sparse_file_len(s, true, false);
     if (size <= 0) {
         return -1;
     }
 
+    char cmd[64];
     sprintf(cmd, "download:%08x", size);
-    r = _command_start(usb, cmd, size, 0);
+    int r = _command_start(usb, cmd, size, 0);
     if (r < 0) {
         return -1;
     }
diff --git a/fastboot/signfile.sh b/fastboot/signfile.sh
deleted file mode 100755 (executable)
index 3188d2d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-if [ $# -ne 3 ]
-then
- echo "Usage: $0 alias filename passpharse"
- exit -1
-fi
-
-openssl dgst -passin pass:"$3" -binary -sha1 -sign $1.pem $2 > $2.sign
-
index c7b748e..0fda41a 100644 (file)
 #ifndef _USB_H_
 #define _USB_H_
 
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-typedef struct usb_handle usb_handle;
-
-typedef struct usb_ifc_info usb_ifc_info;
+struct usb_handle;
 
-struct usb_ifc_info
-{
+struct usb_ifc_info {
         /* from device descriptor */
     unsigned short dev_vendor;
     unsigned short dev_product;
@@ -68,8 +61,4 @@ int usb_read(usb_handle *h, void *_data, int len);
 int usb_write(usb_handle *h, const void *_data, int len);
 int usb_wait_for_disconnect(usb_handle *h);
 
-#if defined(__cplusplus)
-}
-#endif
-
 #endif
similarity index 93%
rename from fastboot/usb_linux.c
rename to fastboot/usb_linux.cpp
index 022f364..7b87907 100644 (file)
  * SUCH DAMAGE.
  */
 
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
-
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <pthread.h>
-#include <ctype.h>
+#include <unistd.h>
 
 #include <linux/usbdevice_fs.h>
-#include <linux/usbdevice_fs.h>
 #include <linux/version.h>
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
 #include <linux/usb/ch9.h>
-#else
-#include <linux/usb_ch9.h>
-#endif
-#include <asm/byteorder.h>
 
 #include "fastboot.h"
 #include "usb.h"
 #define DBG1(x...)
 #endif
 
-/* The max bulk size for linux is 16384 which is defined
- * in drivers/usb/core/devio.c.
- */
+// Kernels before 3.3 have a 16KiB transfer limit. That limit was replaced
+// with a 16MiB global limit in 3.3, but each URB submitted required a
+// contiguous kernel allocation, so you would get ENOMEM if you tried to
+// send something larger than the biggest available contiguous kernel
+// memory region. 256KiB contiguous allocations are generally not reliable
+// on a device kernel that has been running for a while fragmenting its
+// memory, but that shouldn't be a problem for fastboot on the host.
+// In 3.6, the contiguous buffer limit was removed by allocating multiple
+// 16KiB chunks and having the USB driver stitch them back together while
+// transmitting using a scatter-gather list, so 256KiB bulk transfers should
+// be reliable.
+// 256KiB seems to work, but 1MiB bulk transfers lock up my z620 with a 3.13
+// kernel.
 #define MAX_USBFS_BULK_SIZE (16 * 1024)
 
 struct usb_handle
@@ -340,7 +343,7 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
 
             if(filter_usb_device(de->d_name, desc, n, writable, callback,
                                  &in, &out, &ifc) == 0) {
-                usb = calloc(1, sizeof(usb_handle));
+                usb = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
                 strcpy(usb->fname, devname);
                 usb->ep_in = in;
                 usb->ep_out = out;
similarity index 96%
rename from fastboot/usb_osx.c
rename to fastboot/usb_osx.cpp
index 0b6c515..45ae833 100644 (file)
@@ -26,6 +26,7 @@
  * SUCH DAMAGE.
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/IOKitLib.h>
@@ -74,7 +75,6 @@ static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
     HRESULT result;
     SInt32 score;
     UInt8 interfaceNumEndpoints;
-    UInt8 endpoint;
     UInt8 configuration;
 
     // Placing the constant KIOUSBFindInterfaceDontCare into the following
@@ -121,7 +121,7 @@ static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
         result = (*plugInInterface)->QueryInterface(
                 plugInInterface,
                 CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
-                (LPVOID) &interface);
+                (LPVOID*) &interface);
 
         // No longer need the intermediate plugin
         (*plugInInterface)->Release(plugInInterface);
@@ -181,7 +181,7 @@ static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
 
         // Iterate over the endpoints for this interface and see if there
         // are any that do bulk in/out.
-        for (endpoint = 0; endpoint <= interfaceNumEndpoints; endpoint++) {
+        for (UInt8 endpoint = 1; endpoint <= interfaceNumEndpoints; endpoint++) {
             UInt8   transferType;
             UInt16  maxPacketSize;
             UInt8   interval;
@@ -209,7 +209,7 @@ static int try_interfaces(IOUSBDeviceInterface182 **dev, usb_handle *handle) {
                     handle->zero_mask = maxPacketSize - 1;
                 }
             } else {
-                ERR("could not get pipe properties\n");
+                ERR("could not get pipe properties for endpoint %u (%08x)\n", endpoint, kr);
             }
 
             if (handle->info.has_bulk_in && handle->info.has_bulk_out) {
@@ -279,7 +279,7 @@ static int try_device(io_service_t device, usb_handle *handle) {
 
     // Now create the device interface.
     result = (*plugin)->QueryInterface(plugin,
-            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
+            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &dev);
     if ((result != 0) || (dev == NULL)) {
         ERR("Couldn't create a device interface (%08x)\n", (int) result);
         goto error;
@@ -293,6 +293,13 @@ static int try_device(io_service_t device, usb_handle *handle) {
 
     // So, we have a device, finally. Grab its vitals.
 
+
+    kr = (*dev)->USBDeviceOpen(dev);
+    if (kr != 0) {
+        WARN("USBDeviceOpen");
+        goto out;
+    }
+
     kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);
     if (kr != 0) {
         ERR("GetDeviceVendor");
@@ -365,12 +372,16 @@ static int try_device(io_service_t device, usb_handle *handle) {
         goto error;
     }
 
+    out:
+
+    (*dev)->USBDeviceClose(dev);
     (*dev)->Release(dev);
     return 0;
 
     error:
 
     if (dev != NULL) {
+        (*dev)->USBDeviceClose(dev);
         (*dev)->Release(dev);
     }
 
@@ -432,7 +443,7 @@ static int init_usb(ifc_match_func callback, usb_handle **handle) {
         }
 
         if (h.success) {
-            *handle = calloc(1, sizeof(usb_handle));
+            *handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
             memcpy(*handle, &h, sizeof(usb_handle));
             ret = 0;
             break;
similarity index 100%
rename from fastboot/usbtest.c
rename to fastboot/usbtest.cpp
similarity index 100%
rename from fastboot/util.c
rename to fastboot/util.cpp
similarity index 98%
rename from fastboot/util_linux.c
rename to fastboot/util_linux.cpp
index 91c3776..b788199 100644 (file)
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
similarity index 98%
rename from fastboot/util_osx.c
rename to fastboot/util_osx.cpp
index e718562..ae0b024 100644 (file)
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #import <Carbon/Carbon.h>
 #include <unistd.h>
 
similarity index 98%
rename from fastboot/util_windows.c
rename to fastboot/util_windows.cpp
index 74a5c27..ec52f39 100644 (file)
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
index 8ed5cc9..28fff3f 100644 (file)
@@ -1,48 +1,59 @@
 # Copyright 2011 The Android Open Source Project
 
 LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c fs_mgr_fstab.c
-LOCAL_SRC_FILES += fs_mgr_format.c
+common_static_libraries := \
+    liblogwrap \
+    libfec \
+    libfec_rs \
+    libbase \
+    libmincrypt \
+    libcrypto_static \
+    libext4_utils_static \
+    libsquashfs_utils
 
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include \
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+LOCAL_SRC_FILES:= \
+    fs_mgr.c \
+    fs_mgr_format.c \
+    fs_mgr_fstab.c \
+    fs_mgr_slotselect.c \
+    fs_mgr_verity.cpp
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/include \
     system/vold \
     system/extras/ext4_utils \
-    external/openssl/include
-
+    external/openssl/include \
+    bootable/recovery
 LOCAL_MODULE:= libfs_mgr
-LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static libsquashfs_utils
-LOCAL_C_INCLUDES += system/extras/ext4_utils system/extras/squashfs_utils
+LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_CFLAGS := -Werror
-
 ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT)))
 LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
 endif
-
 include $(BUILD_STATIC_LIBRARY)
 
-
-
 include $(CLEAR_VARS)
-
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
 LOCAL_SRC_FILES:= fs_mgr_main.c
-
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
 LOCAL_MODULE:= fs_mgr
-
 LOCAL_MODULE_TAGS := optional
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
 LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
-
-LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static libsquashfs_utils
-LOCAL_STATIC_LIBRARIES += libsparse_static libz libselinux
+LOCAL_STATIC_LIBRARIES := libfs_mgr \
+    $(common_static_libraries) \
+    libcutils \
+    liblog \
+    libc \
+    libsparse_static \
+    libz \
+    libselinux
 LOCAL_CXX_STL := libc++_static
-
 LOCAL_CFLAGS := -Werror
-
 include $(BUILD_EXECUTABLE)
-
index 391b4db..3fda34f 100644 (file)
@@ -98,7 +98,7 @@ static void check_fs(char *blk_device, char *fs_type, char *target)
     int status;
     int ret;
     long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
-    char *tmpmnt_opts = "nomblk_io_submit,errors=remount-ro";
+    char tmpmnt_opts[64] = "errors=remount-ro";
     char *e2fsck_argv[] = {
         E2FSCK_BIN,
         "-y",
@@ -121,6 +121,10 @@ static void check_fs(char *blk_device, char *fs_type, char *target)
          * fix the filesystem.
          */
         errno = 0;
+        if (!strcmp(fs_type, "ext4")) {
+            // This option is only valid with ext4
+            strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
+        }
         ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
         INFO("%s(): mount(%s,%s,%s)=%d: %s\n",
              __func__, blk_device, target, fs_type, ret, strerror(errno));
@@ -150,8 +154,8 @@ static void check_fs(char *blk_device, char *fs_type, char *target)
             INFO("Running %s on %s\n", E2FSCK_BIN, blk_device);
 
             ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv,
-                                        &status, true, LOG_KLOG | LOG_FILE,
-                                        true, FSCK_LOG_FILE);
+                                          &status, true, LOG_KLOG | LOG_FILE,
+                                          true, FSCK_LOG_FILE, NULL, 0);
 
             if (ret < 0) {
                 /* No need to check for error in fork, we can't really handle it now */
@@ -161,14 +165,14 @@ static void check_fs(char *blk_device, char *fs_type, char *target)
     } else if (!strcmp(fs_type, "f2fs")) {
             char *f2fs_fsck_argv[] = {
                     F2FS_FSCK_BIN,
-                    "-f",
+                    "-a",
                     blk_device
             };
-        INFO("Running %s -f %s\n", F2FS_FSCK_BIN, blk_device);
+        INFO("Running %s -a %s\n", F2FS_FSCK_BIN, blk_device);
 
         ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv,
                                       &status, true, LOG_KLOG | LOG_FILE,
-                                      true, FSCK_LOG_FILE);
+                                      true, FSCK_LOG_FILE, NULL, 0);
         if (ret < 0) {
             /* No need to check for error in fork, we can't really handle it now */
             ERROR("Failed trying to run %s\n", F2FS_FSCK_BIN);
@@ -438,8 +442,15 @@ out:
 // Check to see if a mountable volume has encryption requirements
 static int handle_encryptable(struct fstab *fstab, const struct fstab_rec* rec)
 {
+    /* Check for existence of convert_fbe breadcrumb file */
+    char convert_fbe_name[PATH_MAX];
+    snprintf(convert_fbe_name, sizeof(convert_fbe_name),
+             "%s/convert_fbe", rec->mount_point);
+    bool convert_fbe = (access(convert_fbe_name, F_OK) == 0);
+
     /* If this is block encryptable, need to trigger encryption */
     if (   (rec->fs_mgr_flags & MF_FORCECRYPT)
+        || ((rec->fs_mgr_flags & MF_FORCEFDEORFBE) && !convert_fbe)
         || (device_is_force_encrypted() && fs_mgr_is_encryptable(rec))) {
         if (umount(rec->mount_point) == 0) {
             return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
@@ -451,7 +462,8 @@ static int handle_encryptable(struct fstab *fstab, const struct fstab_rec* rec)
     }
 
     // Deal with file level encryption
-    if (rec->fs_mgr_flags & MF_FILEENCRYPTION) {
+    if (   (rec->fs_mgr_flags & MF_FILEENCRYPTION)
+        || ((rec->fs_mgr_flags & MF_FORCEFDEORFBE) && convert_fbe)) {
         // Default or not yet initialized encryption requires no more work here
         if (!e4crypt_non_default_key(rec->mount_point)) {
             INFO("%s is default file encrypted\n", rec->mount_point);
@@ -824,7 +836,8 @@ int fs_mgr_swapon_all(struct fstab *fstab)
         /* Initialize the swap area */
         mkswap_argv[1] = fstab->recs[i].blk_device;
         err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv,
-                                      &status, true, LOG_KLOG, false, NULL);
+                                      &status, true, LOG_KLOG, false, NULL,
+                                      NULL, 0);
         if (err) {
             ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device);
             ret = -1;
@@ -876,7 +889,8 @@ int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_dev
         if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
             continue;
         }
-        if (!(fstab->recs[i].fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT))) {
+        if (!(fstab->recs[i].fs_mgr_flags
+              & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE))) {
             continue;
         }
 
index a44d034..c8c624d 100644 (file)
@@ -22,8 +22,6 @@
 #include <sys/mount.h>
 #include <unistd.h>
 
-#include <cutils/properties.h>
-
 #include "fs_mgr_priv.h"
 
 struct fs_mgr_flag_values {
@@ -66,6 +64,7 @@ static struct flag_list fs_mgr_flags[] = {
     { "encryptable=",MF_CRYPT },
     { "forceencrypt=",MF_FORCECRYPT },
     { "fileencryption",MF_FILEENCRYPTION },
+    { "forcefdeorfbe=",MF_FORCEFDEORFBE },
     { "nonremovable",MF_NONREMOVABLE },
     { "voldmanaged=",MF_VOLDMANAGED},
     { "length=",     MF_LENGTH },
@@ -142,6 +141,11 @@ static int parse_flags(char *flags, struct flag_list *fl,
                      * location of the keys.  Get it and return it.
                      */
                     flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+                } else if ((fl[i].flag == MF_FORCEFDEORFBE) && flag_vals) {
+                    /* The forcefdeorfbe flag is followed by an = and the
+                     * location of the keys.  Get it and return it.
+                     */
+                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
                 } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
                     /* The length flag is followed by an = and the
                      * size of the partition.  Get it and return it.
@@ -332,25 +336,13 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path)
         fstab->recs[cnt].partnum = flag_vals.partnum;
         fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
         fstab->recs[cnt].zram_size = flag_vals.zram_size;
-
-        /* If an A/B partition, modify block device to be the real block device */
-        if (fstab->recs[cnt].fs_mgr_flags & MF_SLOTSELECT) {
-            char propbuf[PROPERTY_VALUE_MAX];
-            char *tmp;
-
-            /* use the kernel parameter if set */
-            property_get("ro.boot.slot_suffix", propbuf, "");
-
-            if (asprintf(&tmp, "%s%s", fstab->recs[cnt].blk_device, propbuf) > 0) {
-                free(fstab->recs[cnt].blk_device);
-                fstab->recs[cnt].blk_device = tmp;
-            } else {
-                ERROR("Error updating block device name\n");
-                goto err;
-            }
-        }
         cnt++;
     }
+    /* If an A/B partition, modify block device to be the real block device */
+    if (fs_mgr_update_for_slotselect(fstab) != 0) {
+        ERROR("Error updating for slotselect\n");
+        goto err;
+    }
     fclose(fstab_file);
     free(line);
     return fstab;
@@ -478,7 +470,7 @@ int fs_mgr_is_verified(const struct fstab_rec *fstab)
 
 int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
 {
-    return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT);
+    return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);
 }
 
 int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
@@ -486,6 +478,11 @@ int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
     return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
 }
 
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_FORCEFDEORFBE;
+}
+
 int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
 {
     return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
index a87092e..181b6cd 100644 (file)
@@ -20,6 +20,8 @@
 #include <cutils/klog.h>
 #include <fs_mgr.h>
 
+__BEGIN_DECLS
+
 #define INFO(x...)    KLOG_INFO("fs_mgr", x)
 #define WARNING(x...) KLOG_WARNING("fs_mgr", x)
 #define ERROR(x...)   KLOG_ERROR("fs_mgr", x)
 #define MF_FILEENCRYPTION 0x2000
 #define MF_FORMATTABLE  0x4000
 #define MF_SLOTSELECT   0x8000
+#define MF_FORCEFDEORFBE 0x10000
 
 #define DM_BUF_SIZE 4096
 
 int fs_mgr_set_blk_ro(const char *blockdev);
+int fs_mgr_update_for_slotselect(struct fstab *fstab);
+
+__END_DECLS
 
 #endif /* __CORE_FS_MGR_PRIV_H */
index f90e596..cd673f3 100644 (file)
  * limitations under the License.
  */
 
+#include <sys/cdefs.h>
+
 #define FS_MGR_SETUP_VERITY_DISABLED -2
 #define FS_MGR_SETUP_VERITY_FAIL -1
 #define FS_MGR_SETUP_VERITY_SUCCESS 0
+
+__BEGIN_DECLS
+
 int fs_mgr_setup_verity(struct fstab_rec *fstab);
+
+__END_DECLS
diff --git a/fs_mgr/fs_mgr_slotselect.c b/fs_mgr/fs_mgr_slotselect.c
new file mode 100644 (file)
index 0000000..ca07b18
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cutils/properties.h>
+
+#include "fs_mgr.h"
+#include "fs_mgr_priv.h"
+
+#include "bootloader.h"
+
+// Copies slot_suffix from misc into |out_suffix|. Returns 0 on
+// success, -1 on error or if there is no non-empty slot_suffix.
+static int get_active_slot_suffix_from_misc(struct fstab *fstab,
+                                            char *out_suffix,
+                                            size_t suffix_len)
+{
+    int n;
+    int misc_fd;
+    ssize_t num_read;
+    struct bootloader_message msg;
+
+    misc_fd = -1;
+    for (n = 0; n < fstab->num_entries; n++) {
+        if (strcmp(fstab->recs[n].mount_point, "/misc") == 0) {
+            misc_fd = open(fstab->recs[n].blk_device, O_RDONLY);
+            if (misc_fd == -1) {
+                ERROR("Error opening misc partition \"%s\" (%s)\n",
+                      fstab->recs[n].blk_device,
+                      strerror(errno));
+                return -1;
+            } else {
+                break;
+            }
+        }
+    }
+
+    if (misc_fd == -1) {
+        ERROR("Error finding misc partition\n");
+        return -1;
+    }
+
+    num_read = TEMP_FAILURE_RETRY(read(misc_fd, &msg, sizeof(msg)));
+    // Linux will never return partial reads when reading from block
+    // devices so no need to worry about them.
+    if (num_read != sizeof(msg)) {
+        ERROR("Error reading bootloader_message (%s)\n", strerror(errno));
+        close(misc_fd);
+        return -1;
+    }
+    close(misc_fd);
+    if (msg.slot_suffix[0] == '\0')
+        return -1;
+    strncpy(out_suffix, msg.slot_suffix, suffix_len);
+    return 0;
+}
+
+// Gets slot_suffix from either the kernel cmdline / firmware or the
+// misc partition. Sets |out_suffix| on success and returns 0. Returns
+// -1 if slot_suffix could not be determined.
+static int get_active_slot_suffix(struct fstab *fstab, char *out_suffix,
+                                  size_t suffix_len)
+{
+    char propbuf[PROPERTY_VALUE_MAX];
+
+    // Get the suffix from the kernel commandline (note that we don't
+    // allow the empty suffix). On bootloaders natively supporting A/B
+    // we'll hit this path every time so don't bother logging it.
+    property_get("ro.boot.slot_suffix", propbuf, "");
+    if (propbuf[0] != '\0') {
+        strncpy(out_suffix, propbuf, suffix_len);
+        return 0;
+    }
+
+    // If we couldn't get the suffix from the kernel cmdline, try the
+    // the misc partition.
+    if (get_active_slot_suffix_from_misc(fstab, out_suffix, suffix_len) == 0) {
+        INFO("Using slot suffix \"%s\" from misc\n", out_suffix);
+        return 0;
+    }
+
+    ERROR("Error determining slot_suffix\n");
+
+    return -1;
+}
+
+// Updates |fstab| for slot_suffix. Returns 0 on success, -1 on error.
+int fs_mgr_update_for_slotselect(struct fstab *fstab)
+{
+    int n;
+    char suffix[PROPERTY_VALUE_MAX];
+    int got_suffix = 0;
+
+    for (n = 0; n < fstab->num_entries; n++) {
+        if (fstab->recs[n].fs_mgr_flags & MF_SLOTSELECT) {
+            char *tmp;
+
+            if (!got_suffix) {
+                memset(suffix, '\0', sizeof(suffix));
+                if (get_active_slot_suffix(fstab, suffix,
+                                           sizeof(suffix) - 1) != 0) {
+                  return -1;
+                }
+                got_suffix = 1;
+            }
+
+            if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device,
+                         suffix) > 0) {
+                free(fstab->recs[n].blk_device);
+                fstab->recs[n].blk_device = tmp;
+            } else {
+                return -1;
+            }
+        }
+    }
+    return 0;
+}
similarity index 69%
rename from fs_mgr/fs_mgr_verity.c
rename to fs_mgr/fs_mgr_verity.cpp
index a9e3358..6983b72 100644 (file)
@@ -29,6 +29,7 @@
 #include <libgen.h>
 #include <time.h>
 
+#include <base/file.h>
 #include <private/android_filesystem_config.h>
 #include <cutils/properties.h>
 #include <logwrap/logwrap.h>
 #include "mincrypt/sha.h"
 #include "mincrypt/sha256.h"
 
-#include "ext4_sb.h"
-#include "squashfs_utils.h"
+#include "fec/io.h"
 
+#include "fs_mgr.h"
 #include "fs_mgr_priv.h"
 #include "fs_mgr_priv_verity.h"
 
 #define FSTAB_PREFIX "/fstab."
 
-#define VERITY_METADATA_SIZE 32768
 #define VERITY_TABLE_RSA_KEY "/verity_key"
 #define VERITY_TABLE_HASH_IDX 8
 #define VERITY_TABLE_SALT_IDX 9
 
+#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
+#define VERITY_TABLE_OPT_LOGGING "ignore_corruption"
+#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
+
+#define VERITY_TABLE_OPT_FEC_FORMAT \
+    "use_fec_from_device %s fec_start %" PRIu64 " fec_blocks %" PRIu64 \
+    " fec_roots %u " VERITY_TABLE_OPT_IGNZERO
+#define VERITY_TABLE_OPT_FEC_ARGS 9
+
 #define METADATA_MAGIC 0x01564c54
 #define METADATA_TAG_MAX_LENGTH 63
 #define METADATA_EOD "eod"
@@ -74,18 +83,15 @@ struct verity_state {
 
 extern struct fs_info info;
 
-static RSAPublicKey *load_key(char *path)
+static RSAPublicKey *load_key(const char *path)
 {
-    FILE *f;
-    RSAPublicKey *key;
-
-    key = malloc(sizeof(RSAPublicKey));
+    RSAPublicKey* key = static_cast<RSAPublicKey*>(malloc(sizeof(RSAPublicKey)));
     if (!key) {
         ERROR("Can't malloc key\n");
         return NULL;
     }
 
-    f = fopen(path, "r");
+    FILE* f = fopen(path, "r");
     if (!f) {
         ERROR("Can't open '%s'\n", path);
         free(key);
@@ -93,7 +99,7 @@ static RSAPublicKey *load_key(char *path)
     }
 
     if (!fread(key, sizeof(*key), 1, f)) {
-        ERROR("Could not read key!");
+        ERROR("Could not read key!\n");
         fclose(f);
         free(key);
         return NULL;
@@ -110,7 +116,8 @@ static RSAPublicKey *load_key(char *path)
     return key;
 }
 
-static int verify_table(char *signature, char *table, int table_length)
+static int verify_table(const uint8_t *signature, const char *table,
+        uint32_t table_length)
 {
     RSAPublicKey *key;
     uint8_t hash_buf[SHA256_DIGEST_SIZE];
@@ -122,17 +129,17 @@ static int verify_table(char *signature, char *table, int table_length)
     // Now get the public key from the keyfile
     key = load_key(VERITY_TABLE_RSA_KEY);
     if (!key) {
-        ERROR("Couldn't load verity keys");
+        ERROR("Couldn't load verity keys\n");
         goto out;
     }
 
     // verify the result
     if (!RSA_verify(key,
-                    (uint8_t*) signature,
+                    signature,
                     RSANUMBYTES,
                     (uint8_t*) hash_buf,
                     SHA256_DIGEST_SIZE)) {
-        ERROR("Couldn't verify table.");
+        ERROR("Couldn't verify table\n");
         goto out;
     }
 
@@ -143,11 +150,11 @@ out:
     return retval;
 }
 
-static int invalidate_table(char *table, int table_length)
+static int invalidate_table(char *table, size_t table_length)
 {
-    int n = 0;
-    int idx = 0;
-    int cleared = 0;
+    size_t n = 0;
+    size_t idx = 0;
+    size_t cleared = 0;
 
     while (n < table_length) {
         if (table[n++] == ' ') {
@@ -170,181 +177,6 @@ static int invalidate_table(char *table, int table_length)
     return -1;
 }
 
-static int squashfs_get_target_device_size(char *blk_device, uint64_t *device_size)
-{
-    struct squashfs_info sq_info;
-
-    if (squashfs_parse_sb(blk_device, &sq_info) >= 0) {
-        *device_size = sq_info.bytes_used_4K_padded;
-        return 0;
-    } else {
-        return -1;
-    }
-}
-
-static int ext4_get_target_device_size(char *blk_device, uint64_t *device_size)
-{
-    int data_device;
-    struct ext4_super_block sb;
-    struct fs_info info;
-
-    info.len = 0;  /* Only len is set to 0 to ask the device for real size. */
-
-    data_device = TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC));
-    if (data_device == -1) {
-        ERROR("Error opening block device (%s)", strerror(errno));
-        return -1;
-    }
-
-    if (TEMP_FAILURE_RETRY(lseek64(data_device, 1024, SEEK_SET)) < 0) {
-        ERROR("Error seeking to superblock");
-        close(data_device);
-        return -1;
-    }
-
-    if (TEMP_FAILURE_RETRY(read(data_device, &sb, sizeof(sb))) != sizeof(sb)) {
-        ERROR("Error reading superblock");
-        close(data_device);
-        return -1;
-    }
-
-    ext4_parse_sb(&sb, &info);
-    *device_size = info.len;
-
-    close(data_device);
-    return 0;
-}
-
-static int get_fs_size(char *fs_type, char *blk_device, uint64_t *device_size) {
-    if (!strcmp(fs_type, "ext4")) {
-        if (ext4_get_target_device_size(blk_device, device_size) < 0) {
-            ERROR("Failed to get ext4 fs size on %s.", blk_device);
-            return -1;
-        }
-    } else if (!strcmp(fs_type, "squashfs")) {
-        if (squashfs_get_target_device_size(blk_device, device_size) < 0) {
-            ERROR("Failed to get squashfs fs size on %s.", blk_device);
-            return -1;
-        }
-    } else {
-        ERROR("%s: Unsupported filesystem for verity.", fs_type);
-        return -1;
-    }
-    return 0;
-}
-
-static int read_verity_metadata(uint64_t device_size, char *block_device, char **signature,
-        char **table)
-{
-    unsigned magic_number;
-    unsigned table_length;
-    int protocol_version;
-    int device;
-    int retval = FS_MGR_SETUP_VERITY_FAIL;
-
-    *signature = NULL;
-
-    if (table) {
-        *table = NULL;
-    }
-
-    device = TEMP_FAILURE_RETRY(open(block_device, O_RDONLY | O_CLOEXEC));
-    if (device == -1) {
-        ERROR("Could not open block device %s (%s).\n", block_device, strerror(errno));
-        goto out;
-    }
-
-    if (TEMP_FAILURE_RETRY(lseek64(device, device_size, SEEK_SET)) < 0) {
-        ERROR("Could not seek to start of verity metadata block.\n");
-        goto out;
-    }
-
-    // check the magic number
-    if (TEMP_FAILURE_RETRY(read(device, &magic_number, sizeof(magic_number))) !=
-            sizeof(magic_number)) {
-        ERROR("Couldn't read magic number!\n");
-        goto out;
-    }
-
-#ifdef ALLOW_ADBD_DISABLE_VERITY
-    if (magic_number == VERITY_METADATA_MAGIC_DISABLE) {
-        retval = FS_MGR_SETUP_VERITY_DISABLED;
-        INFO("Attempt to cleanly disable verity - only works in USERDEBUG");
-        goto out;
-    }
-#endif
-
-    if (magic_number != VERITY_METADATA_MAGIC_NUMBER) {
-        ERROR("Couldn't find verity metadata at offset %"PRIu64"!\n", device_size);
-        goto out;
-    }
-
-    // check the protocol version
-    if (TEMP_FAILURE_RETRY(read(device, &protocol_version,
-            sizeof(protocol_version))) != sizeof(protocol_version)) {
-        ERROR("Couldn't read verity metadata protocol version!\n");
-        goto out;
-    }
-    if (protocol_version != 0) {
-        ERROR("Got unknown verity metadata protocol version %d!\n", protocol_version);
-        goto out;
-    }
-
-    // get the signature
-    *signature = (char*) malloc(RSANUMBYTES);
-    if (!*signature) {
-        ERROR("Couldn't allocate memory for signature!\n");
-        goto out;
-    }
-    if (TEMP_FAILURE_RETRY(read(device, *signature, RSANUMBYTES)) != RSANUMBYTES) {
-        ERROR("Couldn't read signature from verity metadata!\n");
-        goto out;
-    }
-
-    if (!table) {
-        retval = FS_MGR_SETUP_VERITY_SUCCESS;
-        goto out;
-    }
-
-    // get the size of the table
-    if (TEMP_FAILURE_RETRY(read(device, &table_length, sizeof(table_length))) !=
-            sizeof(table_length)) {
-        ERROR("Couldn't get the size of the verity table from metadata!\n");
-        goto out;
-    }
-
-    // get the table + null terminator
-    *table = malloc(table_length + 1);
-    if (!*table) {
-        ERROR("Couldn't allocate memory for verity table!\n");
-        goto out;
-    }
-    if (TEMP_FAILURE_RETRY(read(device, *table, table_length)) !=
-            (ssize_t)table_length) {
-        ERROR("Couldn't read the verity table from metadata!\n");
-        goto out;
-    }
-
-    (*table)[table_length] = 0;
-    retval = FS_MGR_SETUP_VERITY_SUCCESS;
-
-out:
-    if (device != -1)
-        close(device);
-
-    if (retval != FS_MGR_SETUP_VERITY_SUCCESS) {
-        free(*signature);
-        *signature = NULL;
-
-        if (table) {
-            free(*table);
-            *table = NULL;
-        }
-    }
-
-    return retval;
-}
-
 static void verity_ioctl_init(struct dm_ioctl *io, char *name, unsigned flags)
 {
     memset(io, 0, DM_BUF_SIZE);
@@ -384,8 +216,76 @@ static int get_verity_device_name(struct dm_ioctl *io, char *name, int fd, char
     return 0;
 }
 
-static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd, char *table,
-        int mode)
+struct verity_table_params {
+    const char *table;
+    int mode;
+    struct fec_ecc_metadata ecc;
+    const char *ecc_dev;
+};
+
+typedef bool (*format_verity_table_func)(char *buf, const size_t bufsize,
+        const struct verity_table_params *params);
+
+static bool format_verity_table(char *buf, const size_t bufsize,
+        const struct verity_table_params *params)
+{
+    const char *mode_flag = NULL;
+    int res = -1;
+
+    if (params->mode == VERITY_MODE_RESTART) {
+        mode_flag = VERITY_TABLE_OPT_RESTART;
+    } else if (params->mode == VERITY_MODE_LOGGING) {
+        mode_flag = VERITY_TABLE_OPT_LOGGING;
+    }
+
+    if (params->ecc.valid) {
+        if (mode_flag) {
+            res = snprintf(buf, bufsize,
+                    "%s %u %s " VERITY_TABLE_OPT_FEC_FORMAT,
+                    params->table, 1 + VERITY_TABLE_OPT_FEC_ARGS, mode_flag, params->ecc_dev,
+                    params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+        } else {
+            res = snprintf(buf, bufsize,
+                    "%s %u " VERITY_TABLE_OPT_FEC_FORMAT,
+                    params->table, VERITY_TABLE_OPT_FEC_ARGS, params->ecc_dev,
+                    params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+        }
+    } else if (mode_flag) {
+        res = snprintf(buf, bufsize, "%s 2 " VERITY_TABLE_OPT_IGNZERO " %s", params->table,
+                    mode_flag);
+    } else {
+        res = strlcpy(buf, params->table, bufsize);
+    }
+
+    if (res < 0 || (size_t)res >= bufsize) {
+        ERROR("Error building verity table; insufficient buffer size?\n");
+        return false;
+    }
+
+    return true;
+}
+
+static bool format_legacy_verity_table(char *buf, const size_t bufsize,
+        const struct verity_table_params *params)
+{
+    int res;
+
+    if (params->mode == VERITY_MODE_EIO) {
+        res = strlcpy(buf, params->table, bufsize);
+    } else {
+        res = snprintf(buf, bufsize, "%s %d", params->table, params->mode);
+    }
+
+    if (res < 0 || (size_t)res >= bufsize) {
+        ERROR("Error building verity table; insufficient buffer size?\n");
+        return false;
+    }
+
+    return true;
+}
+
+static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_size, int fd,
+        const struct verity_table_params *params, format_verity_table_func format)
 {
     char *verity_params;
     char *buffer = (char*) io;
@@ -395,35 +295,32 @@ static int load_verity_table(struct dm_ioctl *io, char *name, uint64_t device_si
 
     struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
 
-    // set tgt arguments here
+    // set tgt arguments
     io->target_count = 1;
-    tgt->status=0;
-    tgt->sector_start=0;
-    tgt->length=device_size/512;
+    tgt->status = 0;
+    tgt->sector_start = 0;
+    tgt->length = device_size / 512;
     strcpy(tgt->target_type, "verity");
 
-    // build the verity params here
+    // build the verity params
     verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
     bufsize = DM_BUF_SIZE - (verity_params - buffer);
 
-    if (mode == VERITY_MODE_EIO) {
-        // allow operation with older dm-verity drivers that are unaware
-        // of the mode parameter by omitting it; this also means that we
-        // cannot use logging mode with these drivers, they always cause
-        // an I/O error for corrupted blocks
-        strcpy(verity_params, table);
-    } else if (snprintf(verity_params, bufsize, "%s %d", table, mode) < 0) {
+    if (!format(verity_params, bufsize, params)) {
+        ERROR("Failed to format verity parameters\n");
         return -1;
     }
 
+    INFO("loading verity table: '%s'", verity_params);
+
     // set next target boundary
     verity_params += strlen(verity_params) + 1;
-    verity_params = (char*) (((unsigned long)verity_params + 7) & ~8);
+    verity_params = (char*)(((unsigned long)verity_params + 7) & ~8);
     tgt->next = verity_params - buffer;
 
     // send the ioctl to load the verity table
     if (ioctl(fd, DM_TABLE_LOAD, io)) {
-        ERROR("Error loading verity table (%s)", strerror(errno));
+        ERROR("Error loading verity table (%s)\n", strerror(errno));
         return -1;
     }
 
@@ -485,7 +382,7 @@ static int check_verity_restart(const char *fname)
         goto out;
     }
 
-    if (TEMP_FAILURE_RETRY(read(fd, buffer, size)) != size) {
+    if (!android::base::ReadFully(fd, buffer, size)) {
         ERROR("Failed to read %zd bytes from %s (%s)\n", size, fname,
             strerror(errno));
         goto out;
@@ -708,28 +605,31 @@ out:
 static int compare_last_signature(struct fstab_rec *fstab, int *match)
 {
     char tag[METADATA_TAG_MAX_LENGTH + 1];
-    char *signature = NULL;
     int fd = -1;
     int rc = -1;
+    off64_t offset = 0;
+    struct fec_handle *f = NULL;
+    struct fec_verity_metadata verity;
     uint8_t curr[SHA256_DIGEST_SIZE];
     uint8_t prev[SHA256_DIGEST_SIZE];
-    off64_t offset = 0;
-    uint64_t device_size;
 
     *match = 1;
 
-    // get verity filesystem size
-    if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
-        ERROR("Failed to get filesystem size\n");
-        goto out;
+    if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
+            FEC_DEFAULT_ROOTS) == -1) {
+        ERROR("Failed to open '%s' (%s)\n", fstab->blk_device,
+            strerror(errno));
+        return rc;
     }
 
-    if (read_verity_metadata(device_size, fstab->blk_device, &signature, NULL) < 0) {
-        ERROR("Failed to read verity signature from %s\n", fstab->mount_point);
+    // read verity metadata
+    if (fec_verity_get_metadata(f, &verity) == -1) {
+        ERROR("Failed to get verity metadata '%s' (%s)\n", fstab->blk_device,
+            strerror(errno));
         goto out;
     }
 
-    SHA256_hash(signature, RSANUMBYTES, curr);
+    SHA256_hash(verity.signature, RSANUMBYTES, curr);
 
     if (snprintf(tag, sizeof(tag), VERITY_LASTSIG_TAG "_%s",
             basename(fstab->mount_point)) >= (int)sizeof(tag)) {
@@ -771,12 +671,7 @@ static int compare_last_signature(struct fstab_rec *fstab, int *match)
     rc = 0;
 
 out:
-    free(signature);
-
-    if (fd != -1) {
-        close(fd);
-    }
-
+    fec_close(f);
     return rc;
 }
 
@@ -870,8 +765,9 @@ int fs_mgr_load_verity_state(int *mode)
             continue;
         }
 
-        if (current == VERITY_MODE_LOGGING) {
+        if (current != VERITY_MODE_DEFAULT) {
             *mode = current;
+            break;
         }
     }
 
@@ -887,7 +783,7 @@ out:
 
 int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
 {
-    _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
+    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
     bool use_state = true;
     char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
     char *mount_point;
@@ -905,12 +801,13 @@ int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
     property_get("ro.boot.veritymode", propbuf, "");
 
     if (*propbuf != '\0') {
-        if (fs_mgr_load_verity_state(&mode) == -1) {
-            return -1;
-        }
         use_state = false; /* state is kept by the bootloader */
     }
 
+    if (fs_mgr_load_verity_state(&mode) == -1) {
+        return -1;
+    }
+
     fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
 
     if (fd == -1) {
@@ -933,13 +830,6 @@ int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
             continue;
         }
 
-        if (use_state) {
-            if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
-                read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
-                continue;
-            }
-        }
-
         mount_point = basename(fstab->recs[i].mount_point);
         verity_ioctl_init(io, mount_point, 0);
 
@@ -977,91 +867,105 @@ out:
     return rc;
 }
 
-int fs_mgr_setup_verity(struct fstab_rec *fstab) {
-
+int fs_mgr_setup_verity(struct fstab_rec *fstab)
+{
     int retval = FS_MGR_SETUP_VERITY_FAIL;
     int fd = -1;
-    int mode;
-
-    char *verity_blk_name = 0;
-    char *verity_table = 0;
-    char *verity_table_signature = 0;
-    int verity_table_length = 0;
-    uint64_t device_size = 0;
+    char *invalid_table = NULL;
+    char *verity_blk_name = NULL;
+    struct fec_handle *f = NULL;
+    struct fec_verity_metadata verity;
+    struct verity_table_params params;
 
-    _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
+    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
     struct dm_ioctl *io = (struct dm_ioctl *) buffer;
     char *mount_point = basename(fstab->mount_point);
 
-    // set the dm_ioctl flags
-    io->flags |= 1;
-    io->target_count = 1;
-
-    // get verity filesystem size
-    if (get_fs_size(fstab->fs_type, fstab->blk_device, &device_size) < 0) {
+    if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
+            FEC_DEFAULT_ROOTS) < 0) {
+        ERROR("Failed to open '%s' (%s)\n", fstab->blk_device,
+            strerror(errno));
         return retval;
     }
 
-    // read the verity block at the end of the block device
-    // send error code up the chain so we can detect attempts to disable verity
-    retval = read_verity_metadata(device_size,
-                                  fstab->blk_device,
-                                  &verity_table_signature,
-                                  &verity_table);
-    if (retval < 0) {
+    // read verity metadata
+    if (fec_verity_get_metadata(f, &verity) < 0) {
+        ERROR("Failed to get verity metadata '%s' (%s)\n", fstab->blk_device,
+            strerror(errno));
         goto out;
     }
 
-    retval = FS_MGR_SETUP_VERITY_FAIL;
-    verity_table_length = strlen(verity_table);
+#ifdef ALLOW_ADBD_DISABLE_VERITY
+    if (verity.disabled) {
+        retval = FS_MGR_SETUP_VERITY_DISABLED;
+        INFO("Attempt to cleanly disable verity - only works in USERDEBUG\n");
+        goto out;
+    }
+#endif
+
+    // read ecc metadata
+    if (fec_ecc_get_metadata(f, &params.ecc) < 0) {
+        params.ecc.valid = false;
+    }
+
+    params.ecc_dev = fstab->blk_device;
 
     // get the device mapper fd
     if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
-        ERROR("Error opening device mapper (%s)", strerror(errno));
+        ERROR("Error opening device mapper (%s)\n", strerror(errno));
         goto out;
     }
 
     // create the device
     if (create_verity_device(io, mount_point, fd) < 0) {
-        ERROR("Couldn't create verity device!");
+        ERROR("Couldn't create verity device!\n");
         goto out;
     }
 
     // get the name of the device file
     if (get_verity_device_name(io, mount_point, fd, &verity_blk_name) < 0) {
-        ERROR("Couldn't get verity device number!");
+        ERROR("Couldn't get verity device number!\n");
         goto out;
     }
 
-    if (load_verity_state(fstab, &mode) < 0) {
+    if (load_verity_state(fstab, &params.mode) < 0) {
         /* if accessing or updating the state failed, switch to the default
          * safe mode. This makes sure the device won't end up in an endless
          * restart loop, and no corrupted data will be exposed to userspace
          * without a warning. */
-        mode = VERITY_MODE_EIO;
+        params.mode = VERITY_MODE_EIO;
     }
 
     // verify the signature on the table
-    if (verify_table(verity_table_signature,
-                            verity_table,
-                            verity_table_length) < 0) {
-        if (mode == VERITY_MODE_LOGGING) {
+    if (verify_table(verity.signature, verity.table,
+            verity.table_length) < 0) {
+        if (params.mode == VERITY_MODE_LOGGING) {
             // the user has been warned, allow mounting without dm-verity
             retval = FS_MGR_SETUP_VERITY_SUCCESS;
             goto out;
         }
 
         // invalidate root hash and salt to trigger device-specific recovery
-        if (invalidate_table(verity_table, verity_table_length) < 0) {
+        invalid_table = strdup(verity.table);
+
+        if (!invalid_table ||
+                invalidate_table(invalid_table, verity.table_length) < 0) {
             goto out;
         }
+
+        params.table = invalid_table;
+    } else {
+        params.table = verity.table;
     }
 
-    INFO("Enabling dm-verity for %s (mode %d)\n",  mount_point, mode);
+    INFO("Enabling dm-verity for %s (mode %d)\n", mount_point, params.mode);
 
     // load the verity mapping table
-    if (load_verity_table(io, mount_point, device_size, fd, verity_table,
-            mode) < 0) {
+    if (load_verity_table(io, mount_point, verity.data_size, fd, &params,
+            format_verity_table) < 0 &&
+        // try the legacy format for backwards compatibility
+        load_verity_table(io, mount_point, verity.data_size, fd, &params,
+            format_legacy_verity_table) < 0) {
         goto out;
     }
 
@@ -1090,8 +994,8 @@ out:
         close(fd);
     }
 
-    free(verity_table);
-    free(verity_table_signature);
+    fec_close(f);
+    free(invalid_table);
     free(verity_blk_name);
 
     return retval;
index 27fccf7..eb0a7fc 100644 (file)
@@ -102,6 +102,7 @@ int fs_mgr_is_nonremovable(const struct fstab_rec *fstab);
 int fs_mgr_is_verified(const struct fstab_rec *fstab);
 int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
 int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab);
 int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
 int fs_mgr_is_notrim(struct fstab_rec *fstab);
 int fs_mgr_is_formattable(struct fstab_rec *fstab);
index 55b2d5e..3f78955 100644 (file)
@@ -36,6 +36,7 @@ LOCAL_SHARED_LIBRARIES := \
        libkeystore_binder
 LOCAL_STATIC_LIBRARIES := libscrypt_static
 LOCAL_C_INCLUDES := external/scrypt/lib/crypto
+LOCAL_INIT_RC := gatekeeperd.rc
 include $(BUILD_EXECUTABLE)
 
 include $(call first-makefiles-under,$(LOCAL_PATH))
index 75fe11d..c8010ca 100644 (file)
@@ -25,8 +25,10 @@ extern "C" {
 #include <crypto_scrypt.h>
 }
 
+#include <base/memory.h>
 #include <UniquePtr.h>
 #include <gatekeeper/gatekeeper.h>
+
 #include <iostream>
 #include <unordered_map>
 
@@ -150,14 +152,15 @@ public:
     }
 
     bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) {
-        FastHashMap::const_iterator it = fast_hash_map_.find(expected_handle->user_id);
+        uint64_t user_id = android::base::get_unaligned(&expected_handle->user_id);
+        FastHashMap::const_iterator it = fast_hash_map_.find(user_id);
         if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) {
             return true;
         } else {
             if (GateKeeper::DoVerify(expected_handle, password)) {
                 uint64_t salt;
                 GetRandom(&salt, sizeof(salt));
-                fast_hash_map_[expected_handle->user_id] = ComputeFastHash(password, salt);
+                fast_hash_map_[user_id] = ComputeFastHash(password, salt);
                 return true;
             }
         }
diff --git a/gatekeeperd/gatekeeperd.rc b/gatekeeperd/gatekeeperd.rc
new file mode 100644 (file)
index 0000000..3f1b92d
--- /dev/null
@@ -0,0 +1,3 @@
+service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper
+    class late_start
+    user system
index 6fc4ac0..a62b1d4 100644 (file)
@@ -20,7 +20,7 @@ include $(CLEAR_VARS)
 LOCAL_MODULE := gatekeeperd-unit-tests
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
-LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto
+LOCAL_SHARED_LIBRARIES := libgatekeeper libcrypto libbase
 LOCAL_STATIC_LIBRARIES := libscrypt_static
 LOCAL_C_INCLUDES := external/scrypt/lib/crypto
 LOCAL_SRC_FILES := \
index c504f92..47a8bfa 100644 (file)
@@ -18,9 +18,8 @@
 #include <iostream>
 
 #include <gtest/gtest.h>
-#include <UniquePtr.h>
-
 #include <hardware/hw_auth_token.h>
+#include <UniquePtr.h>
 
 #include "../SoftGateKeeper.h"
 
diff --git a/gpttool/Android.mk b/gpttool/Android.mk
deleted file mode 100644 (file)
index 64ad945..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-ifeq ($(HOST_OS),linux)
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := gpttool.c
-LOCAL_STATIC_LIBRARIES := libz
-LOCAL_CFLAGS := -Werror
-
-LOCAL_MODULE := gpttool
-
-include $(BUILD_HOST_EXECUTABLE)
-
-endif
diff --git a/gpttool/gpttool.c b/gpttool/gpttool.c
deleted file mode 100644 (file)
index 398362f..0000000
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <zlib.h>
-
-#include <linux/fs.h>
-
-typedef unsigned char u8;
-typedef unsigned short u16;
-typedef unsigned int u32;
-typedef unsigned long long u64;
-
-const u8 partition_type_uuid[16] = {
-       0xa2, 0xa0, 0xd0, 0xeb, 0xe5, 0xb9, 0x33, 0x44,
-       0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7,
-};
-
-
-#define EFI_VERSION 0x00010000
-#define EFI_MAGIC "EFI PART"
-#define EFI_ENTRIES 128
-#define EFI_NAMELEN 36
-
-struct efi_header {
-       u8 magic[8];
-
-       u32 version;
-       u32 header_sz;
-
-       u32 crc32;
-       u32 reserved;
-
-       u64 header_lba;
-       u64 backup_lba;
-       u64 first_lba;
-       u64 last_lba;
-
-       u8 volume_uuid[16];
-
-       u64 entries_lba;
-
-       u32 entries_count;
-       u32 entries_size;
-       u32 entries_crc32;
-} __attribute__((packed));
-
-struct efi_entry {
-       u8 type_uuid[16];
-       u8 uniq_uuid[16];
-       u64 first_lba;
-       u64 last_lba;
-       u64 attr;
-       u16 name[EFI_NAMELEN];
-};
-
-struct ptable {
-       u8 mbr[512];
-       union {
-               struct efi_header header;
-               u8 block[512];
-       };
-       struct efi_entry entry[EFI_ENTRIES];    
-};
-
-void get_uuid(u8 *uuid)
-{
-       int fd;
-       fd = open("/dev/urandom", O_RDONLY);
-       read(fd, uuid, 16);
-       close(fd);
-}
-
-void init_mbr(u8 *mbr, u32 blocks)
-{
-       mbr[0x1be] = 0x00; // nonbootable
-       mbr[0x1bf] = 0xFF; // bogus CHS
-       mbr[0x1c0] = 0xFF;
-       mbr[0x1c1] = 0xFF;
-
-       mbr[0x1c2] = 0xEE; // GPT partition
-       mbr[0x1c3] = 0xFF; // bogus CHS
-       mbr[0x1c4] = 0xFF;
-       mbr[0x1c5] = 0xFF;
-
-       mbr[0x1c6] = 0x01; // start
-       mbr[0x1c7] = 0x00;
-       mbr[0x1c8] = 0x00;
-       mbr[0x1c9] = 0x00;
-
-       memcpy(mbr + 0x1ca, &blocks, sizeof(u32));
-
-       mbr[0x1fe] = 0x55;
-       mbr[0x1ff] = 0xaa;
-}
-
-int add_ptn(struct ptable *ptbl, u64 first, u64 last, const char *name)
-{
-       struct efi_header *hdr = &ptbl->header;
-       struct efi_entry *entry = ptbl->entry;
-       unsigned n;
-
-       if (first < 34) {
-               fprintf(stderr,"partition '%s' overlaps partition table\n", name);
-               return -1;
-       }
-
-       if (last > hdr->last_lba) {
-               fprintf(stderr,"partition '%s' does not fit on disk\n", name);
-               return -1;
-       }
-       for (n = 0; n < EFI_ENTRIES; n++, entry++) {
-               if (entry->type_uuid[0])
-                       continue;
-               memcpy(entry->type_uuid, partition_type_uuid, 16);
-               get_uuid(entry->uniq_uuid);
-               entry->first_lba = first;
-               entry->last_lba = last;
-               for (n = 0; (n < EFI_NAMELEN) && *name; n++)
-                       entry->name[n] = *name++;
-               return 0;
-       }
-       fprintf(stderr,"out of partition table entries\n");
-       return -1;
-}
-
-int usage(void)
-{
-       fprintf(stderr,
-               "usage: gpttool write <disk> [ <partition> ]*\n"
-               "       gpttool read <disk>\n"
-               "       gpttool test [ <partition> ]*\n"
-               "\n"
-               "partition:  [<name>]:<size>[kmg] | @<file-of-partitions>\n"
-               );
-       return 0;
-}
-
-void show(struct ptable *ptbl)
-{
-       struct efi_entry *entry = ptbl->entry;
-       unsigned n, m;
-       char name[EFI_NAMELEN + 1];
-
-       fprintf(stderr,"ptn  start block   end block     name\n");
-       fprintf(stderr,"---- ------------- ------------- --------------------\n");
-
-       for (n = 0; n < EFI_ENTRIES; n++, entry++) {
-               if (entry->type_uuid[0] == 0)
-                       break;
-               for (m = 0; m < EFI_NAMELEN; m++) {
-                       name[m] = entry->name[m] & 127;
-               }
-               name[m] = 0;
-               fprintf(stderr,"#%03d %13lld %13lld %s\n",
-                       n + 1, entry->first_lba, entry->last_lba, name);
-       }
-}
-
-u64 find_next_lba(struct ptable *ptbl)
-{
-       struct efi_entry *entry = ptbl->entry;
-       unsigned n;
-       u64 a = 0;
-       for (n = 0; n < EFI_ENTRIES; n++, entry++) {
-               if ((entry->last_lba + 1) > a)
-                       a = entry->last_lba + 1;
-       }
-       return a;
-}
-
-u64 next_lba = 0;
-
-u64 parse_size(char *sz)
-{
-       int l = strlen(sz);
-       u64 n = strtoull(sz, 0, 10);
-       if (l) {
-               switch(sz[l-1]){
-               case 'k':
-               case 'K':
-                       n *= 1024;
-                       break;
-               case 'm':
-               case 'M':
-                       n *= (1024 * 1024);
-                       break;
-               case 'g':
-               case 'G':
-                       n *= (1024 * 1024 * 1024);
-                       break;
-               }
-       }
-       return n;
-}
-
-int parse_ptn(struct ptable *ptbl, char *x)
-{
-       char *y = strchr(x, ':');
-       u64 sz;
-
-       if (!y) {
-               fprintf(stderr,"invalid partition entry: %s\n", x);
-               return -1;
-       }
-       *y++ = 0;
-
-       if (*y == 0) {
-               sz = ptbl->header.last_lba - next_lba;
-       } else {
-               sz = parse_size(y);
-               if (sz & 511) {
-                       fprintf(stderr,"partition size must be multiple of 512\n");
-                       return -1;
-               }
-               sz /= 512;
-       }
-
-       if (sz == 0) {
-               fprintf(stderr,"zero size partitions not allowed\n");
-               return -1;
-       }
-
-       if (x[0] && add_ptn(ptbl, next_lba, next_lba + sz - 1, x))
-               return -1;
-
-       next_lba = next_lba + sz;
-       return 0;
-}
-
-int main(int argc, char **argv)
-{
-       struct ptable ptbl;
-       struct efi_header *hdr = &ptbl.header;
-       u32 n;
-       u64 sz;
-       int fd;
-       const char *device;
-       int real_disk = 0;
-
-       if (argc < 2)
-               return usage();
-
-       if (!strcmp(argv[1], "write")) {
-               if (argc < 3)
-                       return usage();
-               device = argv[2];
-               argc -= 2;
-               argv += 2;
-               real_disk = 1;
-       } else if (!strcmp(argv[1], "test")) {
-               argc -= 1;
-               argv += 1;
-               real_disk = 0;
-               sz = 2097152 * 16;
-               fprintf(stderr,"< simulating 16GB disk >\n\n");
-       } else {
-               return usage();
-       }
-
-       if (real_disk) {
-               if (!strcmp(device, "/dev/sda") || 
-                   !strcmp(device, "/dev/sdb")) {
-                       fprintf(stderr,"error: refusing to partition sda or sdb\n");
-                       return -1;
-               }
-               
-               fd = open(device, O_RDWR);
-               if (fd < 0) {
-                       fprintf(stderr,"error: cannot open '%s'\n", device);
-                       return -1;
-               }
-               if (ioctl(fd, BLKGETSIZE64, &sz)) {
-                       fprintf(stderr,"error: cannot query block device size\n");
-                       return -1;
-               }
-               sz /= 512;
-               fprintf(stderr,"blocks %lld\n", sz);
-       }
-
-       memset(&ptbl, 0, sizeof(ptbl));
-
-       init_mbr(ptbl.mbr, sz - 1);
-
-       memcpy(hdr->magic, EFI_MAGIC, sizeof(hdr->magic));
-       hdr->version = EFI_VERSION;
-       hdr->header_sz = sizeof(struct efi_header);
-       hdr->header_lba = 1;
-       hdr->backup_lba = sz - 1;
-       hdr->first_lba = 34;
-       hdr->last_lba = sz - 1;
-       get_uuid(hdr->volume_uuid);
-       hdr->entries_lba = 2;
-       hdr->entries_count = 128;
-       hdr->entries_size = sizeof(struct efi_entry);
-
-       while (argc > 1) {
-               if (argv[1][0] == '@') {
-                       char line[256], *p;
-                       FILE *f;
-                       f = fopen(argv[1] + 1, "r");
-                       if (!f) {
-                               fprintf(stderr,"cannot read partitions from '%s\n", argv[1]);
-                               return -1;
-                       }
-                       while (fgets(line, sizeof(line), f)) {
-                               p = line + strlen(line);
-                               while (p > line) {
-                                       p--;
-                                       if (*p > ' ')
-                                               break;
-                                       *p = 0;
-                               }
-                               p = line;
-                               while (*p && (*p <= ' '))
-                                       p++;
-                               if (*p == '#')
-                                       continue;
-                               if (*p == 0)
-                                       continue;
-                               if (parse_ptn(&ptbl, p))
-                                       return -1;
-                       }
-                       fclose(f);
-               } else {        
-                       if (parse_ptn(&ptbl, argv[1]))
-                               return -1;
-               }
-               argc--;
-               argv++;
-       }
-
-       n = crc32(0, Z_NULL, 0);
-       n = crc32(n, (void*) ptbl.entry, sizeof(ptbl.entry));
-       hdr->entries_crc32 = n;
-
-       n = crc32(0, Z_NULL, 0);
-       n = crc32(n, (void*) &ptbl.header, sizeof(ptbl.header));
-       hdr->crc32 = n;
-
-       show(&ptbl);
-
-       if (real_disk) {
-               write(fd, &ptbl, sizeof(ptbl));
-               fsync(fd);
-
-               if (ioctl(fd, BLKRRPART, 0)) {
-                       fprintf(stderr,"could not re-read partition table\n");
-               }
-               close(fd);
-       }
-       return 0;
-}
index 07e1d73..27c985c 100644 (file)
@@ -35,7 +35,7 @@ endif
 
 LOCAL_C_INCLUDES := bootable/recovery
 
-LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
+LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libcutils liblog libm libc
 
 ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
 LOCAL_STATIC_LIBRARIES += libsuspend
index 396dfef..582821d 100644 (file)
@@ -40,6 +40,8 @@
 #define FAKE_BATTERY_CAPACITY 42
 #define FAKE_BATTERY_TEMPERATURE 424
 #define ALWAYS_PLUGGED_CAPACITY 100
+#define MILLION 10000000.0
+#define DEFAULT_VBUS_VOLTAGE 5000000
 
 namespace android {
 
@@ -137,6 +139,9 @@ BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String
             { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },
             { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },
             { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },
+            { "USB_C", ANDROID_POWER_SUPPLY_TYPE_AC },
+            { "USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC },
+            { "USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB },
             { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },
             { NULL, 0 },
     };
@@ -185,6 +190,7 @@ bool BatteryMonitor::update(void) {
     props.batteryStatus = BATTERY_STATUS_UNKNOWN;
     props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
     props.maxChargingCurrent = 0;
+    props.maxChargingVoltage = 0;
 
     if (!mHealthdConfig->batteryPresentPath.isEmpty())
         props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -196,6 +202,15 @@ bool BatteryMonitor::update(void) {
         getIntField(mHealthdConfig->batteryCapacityPath);
     props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
 
+    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+        props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath) / 1000;
+
+    if (!mHealthdConfig->batteryFullChargePath.isEmpty())
+        props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);
+
+    if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
+        props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
+
     props.batteryTemperature = mBatteryFixedTemperature ?
         mBatteryFixedTemperature :
         getIntField(mHealthdConfig->batteryTemperaturePath);
@@ -223,6 +238,7 @@ bool BatteryMonitor::update(void) {
         props.batteryTechnology = String8(buf);
 
     unsigned int i;
+    double MaxPower = 0;
 
     for (i = 0; i < mChargerNames.size(); i++) {
         String8 path;
@@ -251,11 +267,23 @@ bool BatteryMonitor::update(void) {
                 path.clear();
                 path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
                                   mChargerNames[i].string());
-                if (access(path.string(), R_OK) == 0) {
-                    int maxChargingCurrent = getIntField(path);
-                    if (props.maxChargingCurrent < maxChargingCurrent) {
-                        props.maxChargingCurrent = maxChargingCurrent;
-                    }
+                int ChargingCurrent =
+                    (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
+
+                path.clear();
+                path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
+                                  mChargerNames[i].string());
+
+                int ChargingVoltage =
+                    (access(path.string(), R_OK) == 0) ? getIntField(path) :
+                    DEFAULT_VBUS_VOLTAGE;
+
+                double power = ((double)ChargingCurrent / MILLION) *
+                        ((double)ChargingVoltage / MILLION);
+                if (MaxPower < power) {
+                    props.maxChargingCurrent = ChargingCurrent;
+                    props.maxChargingVoltage = ChargingVoltage;
+                    MaxPower = power;
                 }
             }
         }
@@ -265,7 +293,7 @@ bool BatteryMonitor::update(void) {
 
     if (logthis) {
         char dmesgline[256];
-
+        size_t len;
         if (props.batteryPresent) {
             snprintf(dmesgline, sizeof(dmesgline),
                  "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
@@ -275,19 +303,27 @@ bool BatteryMonitor::update(void) {
                  abs(props.batteryTemperature % 10), props.batteryHealth,
                  props.batteryStatus);
 
+            len = strlen(dmesgline);
             if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
-                int c = getIntField(mHealthdConfig->batteryCurrentNowPath);
-                char b[20];
+                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+                                " c=%d", props.batteryCurrent);
+            }
+
+            if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+                                " fc=%d", props.batteryFullCharge);
+            }
 
-                snprintf(b, sizeof(b), " c=%d", c / 1000);
-                strlcat(dmesgline, b, sizeof(dmesgline));
+            if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+                len += snprintf(dmesgline + len, sizeof(dmesgline) - len,
+                                " cc=%d", props.batteryCycleCount);
             }
         } else {
             snprintf(dmesgline, sizeof(dmesgline),
                  "battery none");
         }
 
-        size_t len = strlen(dmesgline);
+        len = strlen(dmesgline);
         snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
                  props.chargerAcOnline ? "a" : "",
                  props.chargerUsbOnline ? "u" : "",
@@ -385,9 +421,10 @@ void BatteryMonitor::dumpState(int fd) {
     int v;
     char vs[128];
 
-    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d\n",
+    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
              props.chargerAcOnline, props.chargerUsbOnline,
-             props.chargerWirelessOnline, props.maxChargingCurrent);
+             props.chargerWirelessOnline, props.maxChargingCurrent,
+             props.maxChargingVoltage);
     write(fd, vs, strlen(vs));
     snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
              props.batteryStatus, props.batteryHealth, props.batteryPresent);
@@ -414,6 +451,21 @@ void BatteryMonitor::dumpState(int fd) {
         snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
         write(fd, vs, strlen(vs));
     }
+
+    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+        snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrent);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+        snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+        snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullCharge);
+        write(fd, vs, strlen(vs));
+    }
 }
 
 void BatteryMonitor::init(struct healthd_config *hc) {
@@ -496,6 +548,14 @@ void BatteryMonitor::init(struct healthd_config *hc) {
                     }
                 }
 
+                if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/charge_full",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryFullChargePath = path;
+                }
+
                 if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
                     path.clear();
                     path.appendFormat("%s/%s/current_now",
@@ -504,6 +564,14 @@ void BatteryMonitor::init(struct healthd_config *hc) {
                         mHealthdConfig->batteryCurrentNowPath = path;
                 }
 
+                if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/cycle_count",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryCycleCountPath = path;
+                }
+
                 if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
                     path.clear();
                     path.appendFormat("%s/%s/current_avg",
@@ -580,6 +648,12 @@ void BatteryMonitor::init(struct healthd_config *hc) {
             KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
         if (mHealthdConfig->batteryTechnologyPath.isEmpty())
             KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
+        if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
+        if (mHealthdConfig->batteryFullChargePath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
+        if (mHealthdConfig->batteryCycleCountPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
     }
 
     if (property_get("ro.boot.fake_battery", pval, NULL) > 0
index b0002cc..85888c3 100644 (file)
@@ -52,6 +52,8 @@ static struct healthd_config healthd_config = {
     .batteryCurrentNowPath = String8(String8::kEmptyString),
     .batteryCurrentAvgPath = String8(String8::kEmptyString),
     .batteryChargeCounterPath = String8(String8::kEmptyString),
+    .batteryFullChargePath = String8(String8::kEmptyString),
+    .batteryCycleCountPath = String8(String8::kEmptyString),
     .energyCounter = NULL,
     .boot_min_cap = 0,
     .screen_on = NULL,
index 84b6d76..34ea55f 100644 (file)
@@ -65,6 +65,8 @@ struct healthd_config {
     android::String8 batteryCurrentNowPath;
     android::String8 batteryCurrentAvgPath;
     android::String8 batteryChargeCounterPath;
+    android::String8 batteryFullChargePath;
+    android::String8 batteryCycleCountPath;
 
     int (*energyCounter)(int64_t *);
     int boot_min_cap;
index 290682a..f440bd2 100644 (file)
@@ -52,6 +52,12 @@ struct ucontext;
 typedef ucontext ucontext_t;
 #endif
 
+struct backtrace_stackinfo_t {
+  uint64_t start;
+  uint64_t end;
+  const uint8_t* data;
+};
+
 class Backtrace {
 public:
   // Create the correct Backtrace object based on what is to be unwound.
@@ -66,6 +72,14 @@ public:
   // If map is not NULL, the map is still owned by the caller.
   static Backtrace* Create(pid_t pid, pid_t tid, BacktraceMap* map = NULL);
 
+  // Create an offline Backtrace object that can be used to do an unwind without a process
+  // that is still running. If cache_file is set to true, then elf information will be cached
+  // for this call. The cached information survives until the calling process ends. This means
+  // that subsequent calls to create offline Backtrace objects will continue to use the same
+  // cache. It also assumes that the elf files used for each offline unwind are the same.
+  static Backtrace* CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
+                                  const backtrace_stackinfo_t& stack, bool cache_file = false);
+
   virtual ~Backtrace();
 
   // Get the current stack trace and store in the backtrace_ structure.
index bb18aa2..2373c45 100644 (file)
@@ -31,6 +31,7 @@
 
 #include <deque>
 #include <string>
+#include <vector>
 
 struct backtrace_map_t {
   uintptr_t start = 0;
@@ -48,6 +49,8 @@ public:
   // is unsupported.
   static BacktraceMap* Create(pid_t pid, bool uncached = false);
 
+  static BacktraceMap* Create(pid_t pid, const std::vector<backtrace_map_t>& maps);
+
   virtual ~BacktraceMap();
 
   // Fill in the map data structure for the given address.
diff --git a/include/binderwrapper/binder_test_base.h b/include/binderwrapper/binder_test_base.h
new file mode 100644 (file)
index 0000000..06543de
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
+
+#include <base/macros.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+class StubBinderWrapper;
+
+// Class that can be inherited from (or aliased via typedef/using) when writing
+// tests that use StubBinderManager.
+class BinderTestBase : public ::testing::Test {
+ public:
+  BinderTestBase();
+  ~BinderTestBase() override;
+
+  StubBinderWrapper* binder_wrapper() { return binder_wrapper_; }
+
+ protected:
+  StubBinderWrapper* binder_wrapper_;  // Not owned.
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BinderTestBase);
+};
+
+}  // namespace android
+
+#endif  // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
diff --git a/include/binderwrapper/binder_wrapper.h b/include/binderwrapper/binder_wrapper.h
new file mode 100644 (file)
index 0000000..921c4ed
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
+
+#include <sys/types.h>
+
+#include <string>
+
+#include <base/callback.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class BBinder;
+class IBinder;
+
+// Wraps libbinder to make it testable.
+class BinderWrapper {
+ public:
+  virtual ~BinderWrapper() {}
+
+  // Creates and initializes the singleton (using a wrapper that communicates
+  // with the real binder system).
+  static void Create();
+
+  // Initializes |wrapper| as the singleton, taking ownership of it. Tests that
+  // want to inject their own wrappers should call this instead of Create().
+  static void InitForTesting(BinderWrapper* wrapper);
+
+  // Destroys the singleton. Must be called before calling Create() or
+  // InitForTesting() a second time.
+  static void Destroy();
+
+  // Returns the singleton instance previously created by Create() or set by
+  // InitForTesting().
+  static BinderWrapper* Get();
+
+  // Gets the binder for communicating with the service identified by
+  // |service_name|, returning null immediately if it doesn't exist.
+  virtual sp<IBinder> GetService(const std::string& service_name) = 0;
+
+  // Registers |binder| as |service_name| with the service manager.
+  virtual bool RegisterService(const std::string& service_name,
+                               const sp<IBinder>& binder) = 0;
+
+  // Creates a local binder object.
+  virtual sp<BBinder> CreateLocalBinder() = 0;
+
+  // Registers |callback| to be invoked when |binder| dies. If another callback
+  // is currently registered for |binder|, it will be replaced.
+  virtual bool RegisterForDeathNotifications(
+      const sp<IBinder>& binder,
+      const base::Closure& callback) = 0;
+
+  // Unregisters the callback, if any, for |binder|.
+  virtual bool UnregisterForDeathNotifications(const sp<IBinder>& binder) = 0;
+
+  // When called while in a transaction, returns the caller's UID or PID.
+  virtual uid_t GetCallingUid() = 0;
+  virtual pid_t GetCallingPid() = 0;
+
+ private:
+  static BinderWrapper* instance_;
+};
+
+}  // namespace android
+
+#endif  // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
diff --git a/include/binderwrapper/stub_binder_wrapper.h b/include/binderwrapper/stub_binder_wrapper.h
new file mode 100644 (file)
index 0000000..01c9648
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binderwrapper/binder_wrapper.h>
+
+namespace android {
+
+// Stub implementation of BinderWrapper for testing.
+//
+// Example usage:
+//
+// First, assuming a base IFoo binder interface, create a stub class that
+// derives from BnFoo to implement the receiver side of the communication:
+//
+//   class StubFoo : public BnFoo {
+//    public:
+//     ...
+//     status_t doSomething(int arg) override {
+//       // e.g. save passed-in value for later inspection by tests.
+//       return OK;
+//     }
+//   };
+//
+// Next, from your test code, inject a StubBinderManager either directly or by
+// inheriting from the BinderTestBase class:
+//
+//   StubBinderWrapper* wrapper = new StubBinderWrapper();
+//   BinderWrapper::InitForTesting(wrapper);  // Takes ownership.
+//
+// Also from your test, create a StubFoo and register it with the wrapper:
+//
+//   StubFoo* foo = new StubFoo();
+//   sp<IBinder> binder(foo);
+//   wrapper->SetBinderForService("foo", binder);
+//
+// The code being tested can now use the wrapper to get the stub and call it:
+//
+//   sp<IBinder> binder = BinderWrapper::Get()->GetService("foo");
+//   CHECK(binder.get());
+//   sp<IFoo> foo = interface_cast<IFoo>(binder);
+//   CHECK_EQ(foo->doSomething(3), OK);
+//
+// To create a local BBinder object, production code can call
+// CreateLocalBinder(). Then, a test can get the BBinder's address via
+// local_binders() to check that they're passed as expected in binder calls.
+//
+class StubBinderWrapper : public BinderWrapper {
+ public:
+  StubBinderWrapper();
+  ~StubBinderWrapper() override;
+
+  const std::vector<sp<BBinder>>& local_binders() const {
+    return local_binders_;
+  }
+  void clear_local_binders() { local_binders_.clear(); }
+
+  void set_calling_uid(uid_t uid) { calling_uid_ = uid; }
+  void set_calling_pid(pid_t pid) { calling_pid_ = pid; }
+
+  // Sets the binder to return when |service_name| is passed to GetService() or
+  // WaitForService().
+  void SetBinderForService(const std::string& service_name,
+                           const sp<IBinder>& binder);
+
+  // Returns the binder previously registered for |service_name| via
+  // RegisterService(), or null if the service hasn't been registered.
+  sp<IBinder> GetRegisteredService(const std::string& service_name) const;
+
+  // Run the calback in |death_callbacks_| corresponding to |binder|.
+  void NotifyAboutBinderDeath(const sp<IBinder>& binder);
+
+  // BinderWrapper:
+  sp<IBinder> GetService(const std::string& service_name) override;
+  bool RegisterService(const std::string& service_name,
+                       const sp<IBinder>& binder) override;
+  sp<BBinder> CreateLocalBinder() override;
+  bool RegisterForDeathNotifications(const sp<IBinder>& binder,
+                                     const base::Closure& callback) override;
+  bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
+  uid_t GetCallingUid() override;
+  pid_t GetCallingPid() override;
+
+ private:
+  using ServiceMap = std::map<std::string, sp<IBinder>>;
+
+  // Map from service name to associated binder handle. Used by GetService() and
+  // WaitForService().
+  ServiceMap services_to_return_;
+
+  // Map from service name to associated binder handle. Updated by
+  // RegisterService().
+  ServiceMap registered_services_;
+
+  // Local binders returned by CreateLocalBinder().
+  std::vector<sp<BBinder>> local_binders_;
+
+  // Map from binder handle to the callback that should be invoked on binder
+  // death.
+  std::map<sp<IBinder>, base::Closure> death_callbacks_;
+
+  // Values to return from GetCallingUid() and GetCallingPid();
+  uid_t calling_uid_;
+  pid_t calling_pid_;
+
+  DISALLOW_COPY_AND_ASSIGN(StubBinderWrapper);
+};
+
+}  // namespace android
+
+#endif  // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
index 85e1b7e..a3861a0 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef __CUTILS_ANDROID_REBOOT_H__
 #define __CUTILS_ANDROID_REBOOT_H__
 
+#include <mntent.h>
+
 __BEGIN_DECLS
 
 /* Commands */
@@ -28,6 +30,9 @@ __BEGIN_DECLS
 #define ANDROID_RB_PROPERTY "sys.powerctl"
 
 int android_reboot(int cmd, int flags, const char *arg);
+int android_reboot_with_callback(
+    int cmd, int flags, const char *arg,
+    void (*cb_on_remount)(const struct mntent*));
 
 __END_DECLS
 
index f8076ca..2d3c743 100644 (file)
@@ -23,7 +23,7 @@
 #include <string.h>
 #include <stdbool.h>
 
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 #include <winsock2.h>
 typedef int  socklen_t;
 #else
@@ -77,7 +77,7 @@ static inline int android_get_control_socket(const char *name)
 extern int socket_loopback_client(int port, int type);
 extern int socket_network_client(const char *host, int port, int type);
 extern int socket_network_client_timeout(const char *host, int port, int type,
-                                         int timeout);
+                                         int timeout, int* getaddrinfo_error);
 extern int socket_loopback_server(int port, int type);
 extern int socket_local_server(const char *name, int namespaceId, int type);
 extern int socket_local_server_bind(int s, const char *name, int namespaceId);
index e4ed179..087a0c4 100644 (file)
@@ -67,10 +67,12 @@ __BEGIN_DECLS
 #define ATRACE_TAG_RS               (1<<15)
 #define ATRACE_TAG_BIONIC           (1<<16)
 #define ATRACE_TAG_POWER            (1<<17)
-#define ATRACE_TAG_LAST             ATRACE_TAG_POWER
+#define ATRACE_TAG_PACKAGE_MANAGER  (1<<18)
+#define ATRACE_TAG_SYSTEM_SERVER    (1<<19)
+#define ATRACE_TAG_LAST             ATRACE_TAG_SYSTEM_SERVER
 
 // Reserved for initialization.
-#define ATRACE_TAG_NOT_READY        (1LL<<63)
+#define ATRACE_TAG_NOT_READY        (1ULL<<63)
 
 #define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)
 
index f030dab..c795253 100644 (file)
@@ -183,6 +183,8 @@ struct logger_list *android_logger_list_open(log_id_t id,
                                              pid_t pid);
 #define android_logger_list_close android_logger_list_free
 
+char android_log_timestamp();
+
 /*
  * log_id_t helpers
  */
index 26b1ee5..204b3f2 100644 (file)
@@ -42,6 +42,8 @@ typedef enum {
     FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
     FORMAT_MODIFIER_YEAR,      /* Adds year to date */
     FORMAT_MODIFIER_ZONE,      /* Adds zone to date */
+    FORMAT_MODIFIER_EPOCH,     /* Print time as seconds since Jan 1 1970 */
+    FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
 } AndroidLogPrintFormat;
 
 typedef struct AndroidLogFormat_t AndroidLogFormat;
index 2ed27dc..a1d2539 100644 (file)
 #include <sys/types.h>
 #include <stdint.h>
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 #include <linux/capability.h>
 #else
 #include "android_filesystem_capability.h"
 #endif
 
+#define CAP_MASK_LONG(cap_name)  (1ULL << (cap_name))
+
 /* This is the master Users and Groups config for the platform.
  * DO NOT EVER RENUMBER
  */
@@ -77,6 +79,9 @@
 #define AID_SDCARD_ALL    1035  /* access all users external storage */
 #define AID_LOGD          1036  /* log daemon */
 #define AID_SHARED_RELRO  1037  /* creator of shared GNU RELRO files */
+#define AID_DBUS          1038  /* dbus-daemon IPC broker process */
+#define AID_TLSDATE       1039  /* tlsdate unprivileged user */
+#define AID_MEDIA_EX      1040  /* mediaextractor process */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
 #define AID_CACHE         2001  /* cache access */
 #define AID_NET_BW_STATS  3006  /* read bandwidth statistics */
 #define AID_NET_BW_ACCT   3007  /* change bandwidth statistics accounting */
 #define AID_NET_BT_STACK  3008  /* bluetooth: access config files */
+#define AID_READPROC      3009  /* Allow /proc read access */
+
+/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
+#define AID_OEM_RESERVED_2_START 5000
+#define AID_OEM_RESERVED_2_END   5999
 
 #define AID_EVERYBODY     9997  /* shared between all apps in the same profile */
 #define AID_MISC          9998  /* access to misc storage */
@@ -168,6 +178,9 @@ static const struct android_id_info android_ids[] = {
     { "sdcard_all",    AID_SDCARD_ALL, },
     { "logd",          AID_LOGD, },
     { "shared_relro",  AID_SHARED_RELRO, },
+    { "dbus",          AID_DBUS, },
+    { "tlsdate",       AID_TLSDATE, },
+    { "mediaex",       AID_MEDIA_EX, },
 
     { "shell",         AID_SHELL, },
     { "cache",         AID_CACHE, },
@@ -181,6 +194,7 @@ static const struct android_id_info android_ids[] = {
     { "net_bw_stats",  AID_NET_BW_STATS, },
     { "net_bw_acct",   AID_NET_BW_ACCT, },
     { "net_bt_stack",  AID_NET_BT_STACK, },
+    { "readproc",      AID_READPROC, },
 
     { "everybody",     AID_EVERYBODY, },
     { "misc",          AID_MISC, },
index a088526..9e291c8 100644 (file)
@@ -94,6 +94,7 @@ typedef struct radio_hal_fm_band_config {
     radio_rds_t         rds;        /* RDS variants supported */
     bool                ta;         /* Traffic Announcement supported */
     bool                af;         /* Alternate Frequency supported */
+    bool                ea;         /* Emergency announcements supported */
 } radio_hal_fm_band_config_t;
 
 /* Additional attributes for an AM band configuration */
@@ -184,6 +185,7 @@ enum {
     RADIO_EVENT_METADATA    = 4,  /* New meta data received */
     RADIO_EVENT_TA          = 5,  /* Traffic announcement start or stop */
     RADIO_EVENT_AF_SWITCH   = 6,  /* Switch to Alternate Frequency */
+    RADIO_EVENT_EA          = 7,  /* Emergency announcement start or stop */
     // begin framework only events
     RADIO_EVENT_CONTROL     = 100, /* loss/gain of tuner control */
     RADIO_EVENT_SERVER_DIED = 101, /* radio service died */
@@ -195,7 +197,8 @@ typedef struct radio_hal_event {
     radio_event_type_t  type;       /* event type */
     int                 status;     /* used by RADIO_EVENT_CONFIG, RADIO_EVENT_TUNED */
     union {
-        bool                    on;     /* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA */
+        /* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA, RADIO_EVENT_EA */
+        bool                    on;
         radio_hal_band_config_t config; /* RADIO_EVENT_CONFIG */
         radio_program_info_t    info;   /* RADIO_EVENT_TUNED, RADIO_EVENT_AF_SWITCH */
         radio_metadata_t        *metadata; /* RADIO_EVENT_METADATA */
index 508ce00..9f6e951 100644 (file)
@@ -25,6 +25,7 @@
 #include <sys/cdefs.h>
 #include <system/graphics.h>
 #include <unistd.h>
+#include <stdbool.h>
 
 #ifndef __UNUSED
 #define __UNUSED __attribute__((__unused__))
@@ -311,6 +312,7 @@ enum {
     NATIVE_WINDOW_SET_SIDEBAND_STREAM       = 18,
     NATIVE_WINDOW_SET_BUFFERS_DATASPACE     = 19,
     NATIVE_WINDOW_SET_SURFACE_DAMAGE        = 20,   /* private */
+    NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE    = 21,
 };
 
 /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -949,6 +951,18 @@ static inline int native_window_set_surface_damage(
             rects, numRects);
 }
 
+/*
+ * native_window_set_single_buffer_mode(..., bool singleBufferMode)
+ * Enable/disable single buffer mode
+ */
+static inline int native_window_set_single_buffer_mode(
+        struct ANativeWindow* window,
+        bool singleBufferMode)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE,
+            singleBufferMode);
+}
+
 __END_DECLS
 
 #endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
index aad1e82..4c2dd49 100644 (file)
@@ -73,7 +73,7 @@ extern void androidSetCreateThreadFunc(android_create_thread_fn func);
 // ------------------------------------------------------------------
 // Extra functions working with raw pids.
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 // Change the priority AND scheduling group of a particular thread.  The priority
 // should be one of the ANDROID_PRIORITY constants.  Returns INVALID_OPERATION
 // if the priority set failed, else another value if just the group set failed;
diff --git a/include/utils/BasicHashtable.h b/include/utils/BasicHashtable.h
deleted file mode 100644 (file)
index c235d62..0000000
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_BASIC_HASHTABLE_H
-#define ANDROID_BASIC_HASHTABLE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/SharedBuffer.h>
-#include <utils/TypeHelpers.h>
-
-namespace android {
-
-/* Implementation type.  Nothing to see here. */
-class BasicHashtableImpl {
-protected:
-    struct Bucket {
-        // The collision flag indicates that the bucket is part of a collision chain
-        // such that at least two entries both hash to this bucket.  When true, we
-        // may need to seek further along the chain to find the entry.
-        static const uint32_t COLLISION = 0x80000000UL;
-
-        // The present flag indicates that the bucket contains an initialized entry value.
-        static const uint32_t PRESENT   = 0x40000000UL;
-
-        // Mask for 30 bits worth of the hash code that are stored within the bucket to
-        // speed up lookups and rehashing by eliminating the need to recalculate the
-        // hash code of the entry's key.
-        static const uint32_t HASH_MASK = 0x3fffffffUL;
-
-        // Combined value that stores the collision and present flags as well as
-        // a 30 bit hash code.
-        uint32_t cookie;
-
-        // Storage for the entry begins here.
-        char entry[0];
-    };
-
-    BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor,
-            size_t minimumInitialCapacity, float loadFactor);
-    BasicHashtableImpl(const BasicHashtableImpl& other);
-    virtual ~BasicHashtableImpl();
-
-    void dispose();
-
-    inline void edit() {
-        if (mBuckets && !SharedBuffer::bufferFromData(mBuckets)->onlyOwner()) {
-            clone();
-        }
-    }
-
-    void setTo(const BasicHashtableImpl& other);
-    void clear();
-
-    ssize_t next(ssize_t index) const;
-    ssize_t find(ssize_t index, hash_t hash, const void* __restrict__ key) const;
-    size_t add(hash_t hash, const void* __restrict__ entry);
-    void removeAt(size_t index);
-    void rehash(size_t minimumCapacity, float loadFactor);
-
-    const size_t mBucketSize; // number of bytes per bucket including the entry
-    const bool mHasTrivialDestructor; // true if the entry type does not require destruction
-    size_t mCapacity;         // number of buckets that can be filled before exceeding load factor
-    float mLoadFactor;        // load factor
-    size_t mSize;             // number of elements actually in the table
-    size_t mFilledBuckets;    // number of buckets for which collision or present is true
-    size_t mBucketCount;      // number of slots in the mBuckets array
-    void* mBuckets;           // array of buckets, as a SharedBuffer
-
-    inline const Bucket& bucketAt(const void* __restrict__ buckets, size_t index) const {
-        return *reinterpret_cast<const Bucket*>(
-                static_cast<const uint8_t*>(buckets) + index * mBucketSize);
-    }
-
-    inline Bucket& bucketAt(void* __restrict__ buckets, size_t index) const {
-        return *reinterpret_cast<Bucket*>(static_cast<uint8_t*>(buckets) + index * mBucketSize);
-    }
-
-    virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const = 0;
-    virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const = 0;
-    virtual void destroyBucketEntry(Bucket& bucket) const = 0;
-
-private:
-    void clone();
-
-    // Allocates a bucket array as a SharedBuffer.
-    void* allocateBuckets(size_t count) const;
-
-    // Releases a bucket array's associated SharedBuffer.
-    void releaseBuckets(void* __restrict__ buckets, size_t count) const;
-
-    // Destroys the contents of buckets (invokes destroyBucketEntry for each
-    // populated bucket if needed).
-    void destroyBuckets(void* __restrict__ buckets, size_t count) const;
-
-    // Copies the content of buckets (copies the cookie and invokes copyBucketEntry
-    // for each populated bucket if needed).
-    void copyBuckets(const void* __restrict__ fromBuckets,
-            void* __restrict__ toBuckets, size_t count) const;
-
-    // Determines the appropriate size of a bucket array to store a certain minimum
-    // number of entries and returns its effective capacity.
-    static void determineCapacity(size_t minimumCapacity, float loadFactor,
-            size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity);
-
-    // Trim a hash code to 30 bits to match what we store in the bucket's cookie.
-    inline static hash_t trimHash(hash_t hash) {
-        return (hash & Bucket::HASH_MASK) ^ (hash >> 30);
-    }
-
-    // Returns the index of the first bucket that is in the collision chain
-    // for the specified hash code, given the total number of buckets.
-    // (Primary hash)
-    inline static size_t chainStart(hash_t hash, size_t count) {
-        return hash % count;
-    }
-
-    // Returns the increment to add to a bucket index to seek to the next bucket
-    // in the collision chain for the specified hash code, given the total number of buckets.
-    // (Secondary hash)
-    inline static size_t chainIncrement(hash_t hash, size_t count) {
-        return ((hash >> 7) | (hash << 25)) % (count - 1) + 1;
-    }
-
-    // Returns the index of the next bucket that is in the collision chain
-    // that is defined by the specified increment, given the total number of buckets.
-    inline static size_t chainSeek(size_t index, size_t increment, size_t count) {
-        return (index + increment) % count;
-    }
-};
-
-/*
- * A BasicHashtable stores entries that are indexed by hash code in place
- * within an array.  The basic operations are finding entries by key,
- * adding new entries and removing existing entries.
- *
- * This class provides a very limited set of operations with simple semantics.
- * It is intended to be used as a building block to construct more complex
- * and interesting data structures such as HashMap.  Think very hard before
- * adding anything extra to BasicHashtable, it probably belongs at a
- * higher level of abstraction.
- *
- * TKey: The key type.
- * TEntry: The entry type which is what is actually stored in the array.
- *
- * TKey must support the following contract:
- *     bool operator==(const TKey& other) const;  // return true if equal
- *     bool operator!=(const TKey& other) const;  // return true if unequal
- *
- * TEntry must support the following contract:
- *     const TKey& getKey() const;  // get the key from the entry
- *
- * This class supports storing entries with duplicate keys.  Of course, it can't
- * tell them apart during removal so only the first entry will be removed.
- * We do this because it means that operations like add() can't fail.
- */
-template <typename TKey, typename TEntry>
-class BasicHashtable : private BasicHashtableImpl {
-public:
-    /* Creates a hashtable with the specified minimum initial capacity.
-     * The underlying array will be created when the first entry is added.
-     *
-     * minimumInitialCapacity: The minimum initial capacity for the hashtable.
-     *     Default is 0.
-     * loadFactor: The desired load factor for the hashtable, between 0 and 1.
-     *     Default is 0.75.
-     */
-    BasicHashtable(size_t minimumInitialCapacity = 0, float loadFactor = 0.75f);
-
-    /* Copies a hashtable.
-     * The underlying storage is shared copy-on-write.
-     */
-    BasicHashtable(const BasicHashtable& other);
-
-    /* Clears and destroys the hashtable.
-     */
-    virtual ~BasicHashtable();
-
-    /* Making this hashtable a copy of the other hashtable.
-     * The underlying storage is shared copy-on-write.
-     *
-     * other: The hashtable to copy.
-     */
-    inline BasicHashtable<TKey, TEntry>& operator =(const BasicHashtable<TKey, TEntry> & other) {
-        setTo(other);
-        return *this;
-    }
-
-    /* Returns the number of entries in the hashtable.
-     */
-    inline size_t size() const {
-        return mSize;
-    }
-
-    /* Returns the capacity of the hashtable, which is the number of elements that can
-     * added to the hashtable without requiring it to be grown.
-     */
-    inline size_t capacity() const {
-        return mCapacity;
-    }
-
-    /* Returns the number of buckets that the hashtable has, which is the size of its
-     * underlying array.
-     */
-    inline size_t bucketCount() const {
-        return mBucketCount;
-    }
-
-    /* Returns the load factor of the hashtable. */
-    inline float loadFactor() const {
-        return mLoadFactor;
-    };
-
-    /* Returns a const reference to the entry at the specified index.
-     *
-     * index:   The index of the entry to retrieve.  Must be a valid index within
-     *          the bounds of the hashtable.
-     */
-    inline const TEntry& entryAt(size_t index) const {
-        return entryFor(bucketAt(mBuckets, index));
-    }
-
-    /* Returns a non-const reference to the entry at the specified index.
-     *
-     * index: The index of the entry to edit.  Must be a valid index within
-     *        the bounds of the hashtable.
-     */
-    inline TEntry& editEntryAt(size_t index) {
-        edit();
-        return entryFor(bucketAt(mBuckets, index));
-    }
-
-    /* Clears the hashtable.
-     * All entries in the hashtable are destroyed immediately.
-     * If you need to do something special with the entries in the hashtable then iterate
-     * over them and do what you need before clearing the hashtable.
-     */
-    inline void clear() {
-        BasicHashtableImpl::clear();
-    }
-
-    /* Returns the index of the next entry in the hashtable given the index of a previous entry.
-     * If the given index is -1, then returns the index of the first entry in the hashtable,
-     * if there is one, or -1 otherwise.
-     * If the given index is not -1, then returns the index of the next entry in the hashtable,
-     * in strictly increasing order, or -1 if there are none left.
-     *
-     * index:   The index of the previous entry that was iterated, or -1 to begin
-     *          iteration at the beginning of the hashtable.
-     */
-    inline ssize_t next(ssize_t index) const {
-        return BasicHashtableImpl::next(index);
-    }
-
-    /* Finds the index of an entry with the specified key.
-     * If the given index is -1, then returns the index of the first matching entry,
-     * otherwise returns the index of the next matching entry.
-     * If the hashtable contains multiple entries with keys that match the requested
-     * key, then the sequence of entries returned is arbitrary.
-     * Returns -1 if no entry was found.
-     *
-     * index:   The index of the previous entry with the specified key, or -1 to
-     *          find the first matching entry.
-     * hash:    The hashcode of the key.
-     * key:     The key.
-     */
-    inline ssize_t find(ssize_t index, hash_t hash, const TKey& key) const {
-        return BasicHashtableImpl::find(index, hash, &key);
-    }
-
-    /* Adds the entry to the hashtable.
-     * Returns the index of the newly added entry.
-     * If an entry with the same key already exists, then a duplicate entry is added.
-     * If the entry will not fit, then the hashtable's capacity is increased and
-     * its contents are rehashed.  See rehash().
-     *
-     * hash:    The hashcode of the key.
-     * entry:   The entry to add.
-     */
-    inline size_t add(hash_t hash, const TEntry& entry) {
-        return BasicHashtableImpl::add(hash, &entry);
-    }
-
-    /* Removes the entry with the specified index from the hashtable.
-     * The entry is destroyed immediately.
-     * The index must be valid.
-     *
-     * The hashtable is not compacted after an item is removed, so it is legal
-     * to continue iterating over the hashtable using next() or find().
-     *
-     * index:   The index of the entry to remove.  Must be a valid index within the
-     *          bounds of the hashtable, and it must refer to an existing entry.
-     */
-    inline void removeAt(size_t index) {
-        BasicHashtableImpl::removeAt(index);
-    }
-
-    /* Rehashes the contents of the hashtable.
-     * Grows the hashtable to at least the specified minimum capacity or the
-     * current number of elements, whichever is larger.
-     *
-     * Rehashing causes all entries to be copied and the entry indices may change.
-     * Although the hash codes are cached by the hashtable, rehashing can be an
-     * expensive operation and should be avoided unless the hashtable's size
-     * needs to be changed.
-     *
-     * Rehashing is the only way to change the capacity or load factor of the
-     * hashtable once it has been created.  It can be used to compact the
-     * hashtable by choosing a minimum capacity that is smaller than the current
-     * capacity (such as 0).
-     *
-     * minimumCapacity: The desired minimum capacity after rehashing.
-     * loadFactor: The desired load factor after rehashing.
-     */
-    inline void rehash(size_t minimumCapacity, float loadFactor) {
-        BasicHashtableImpl::rehash(minimumCapacity, loadFactor);
-    }
-
-    /* Determines whether there is room to add another entry without rehashing.
-     * When this returns true, a subsequent add() operation is guaranteed to
-     * complete without performing a rehash.
-     */
-    inline bool hasMoreRoom() const {
-        return mCapacity > mFilledBuckets;
-    }
-
-protected:
-    static inline const TEntry& entryFor(const Bucket& bucket) {
-        return reinterpret_cast<const TEntry&>(bucket.entry);
-    }
-
-    static inline TEntry& entryFor(Bucket& bucket) {
-        return reinterpret_cast<TEntry&>(bucket.entry);
-    }
-
-    virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const;
-    virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const;
-    virtual void destroyBucketEntry(Bucket& bucket) const;
-
-private:
-    // For dumping the raw contents of a hashtable during testing.
-    friend class BasicHashtableTest;
-    inline uint32_t cookieAt(size_t index) const {
-        return bucketAt(mBuckets, index).cookie;
-    }
-};
-
-template <typename TKey, typename TEntry>
-BasicHashtable<TKey, TEntry>::BasicHashtable(size_t minimumInitialCapacity, float loadFactor) :
-        BasicHashtableImpl(sizeof(TEntry), traits<TEntry>::has_trivial_dtor,
-                minimumInitialCapacity, loadFactor) {
-}
-
-template <typename TKey, typename TEntry>
-BasicHashtable<TKey, TEntry>::BasicHashtable(const BasicHashtable<TKey, TEntry>& other) :
-        BasicHashtableImpl(other) {
-}
-
-template <typename TKey, typename TEntry>
-BasicHashtable<TKey, TEntry>::~BasicHashtable() {
-    dispose();
-}
-
-template <typename TKey, typename TEntry>
-bool BasicHashtable<TKey, TEntry>::compareBucketKey(const Bucket& bucket,
-        const void* __restrict__ key) const {
-    return entryFor(bucket).getKey() == *static_cast<const TKey*>(key);
-}
-
-template <typename TKey, typename TEntry>
-void BasicHashtable<TKey, TEntry>::initializeBucketEntry(Bucket& bucket,
-        const void* __restrict__ entry) const {
-    if (!traits<TEntry>::has_trivial_copy) {
-        new (&entryFor(bucket)) TEntry(*(static_cast<const TEntry*>(entry)));
-    } else {
-        memcpy(&entryFor(bucket), entry, sizeof(TEntry));
-    }
-}
-
-template <typename TKey, typename TEntry>
-void BasicHashtable<TKey, TEntry>::destroyBucketEntry(Bucket& bucket) const {
-    if (!traits<TEntry>::has_trivial_dtor) {
-        entryFor(bucket).~TEntry();
-    }
-}
-
-}; // namespace android
-
-#endif // ANDROID_BASIC_HASHTABLE_H
index baa3a83..44ea13d 100644 (file)
@@ -21,7 +21,7 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 #include <winsock2.h>
 #else
 #include <netinet/in.h>
index 7d96310..b2ba55e 100644 (file)
@@ -33,6 +33,10 @@ static inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset)
     return pread(fd, buf, nbytes, offset);
 }
 
+static inline ssize_t pwrite64(int fd, const void* buf, size_t nbytes, off64_t offset) {
+    return pwrite(fd, buf, nbytes, offset);
+}
+
 #endif /* __APPLE__ */
 
 #if defined(_WIN32)
@@ -75,4 +79,10 @@ static inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset)
     _rc; })
 #endif
 
+#if defined(_WIN32)
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
+#endif
+
 #endif /* __LIB_UTILS_COMPAT_H */
index 46173db..16e1fa2 100644 (file)
@@ -23,7 +23,7 @@
 namespace android {
 
 // use this type to return error codes
-#ifdef HAVE_MS_C_RUNTIME
+#ifdef _WIN32
 typedef int         status_t;
 #else
 typedef int32_t     status_t;
@@ -58,8 +58,7 @@ enum {
     ALREADY_EXISTS      = -EEXIST,
     DEAD_OBJECT         = -EPIPE,
     FAILED_TRANSACTION  = (UNKNOWN_ERROR + 2),
-    JPARKS_BROKE_IT     = -EPIPE,
-#if !defined(HAVE_MS_C_RUNTIME)
+#if !defined(_WIN32)
     BAD_INDEX           = -EOVERFLOW,
     NOT_ENOUGH_DATA     = -ENODATA,
     WOULD_BLOCK         = -EWOULDBLOCK, 
@@ -73,6 +72,7 @@ enum {
     UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6),
 #endif    
     FDS_NOT_ALLOWED     = (UNKNOWN_ERROR + 7),
+    UNEXPECTED_NULL     = (UNKNOWN_ERROR + 8),
 };
 
 // Restore define; enumeration is in "android" namespace, so the value defined
index f70fc92..7d372e1 100644 (file)
@@ -26,7 +26,7 @@
 
 #if defined(__MINGW32__)
 // Ensure that we always pull in winsock2.h before windows.h
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 #include <winsock2.h>
 #endif
 #include <windows.h>
@@ -52,6 +52,9 @@ class FileMap {
 public:
     FileMap(void);
 
+    FileMap(FileMap&& f);
+    FileMap& operator=(FileMap&& f);
+
     /*
      * Create a new mapping on an open file.
      *
index 7da5dbd..027c10c 100644 (file)
@@ -29,6 +29,9 @@ namespace android {
 /* The Jenkins hash of a sequence of 32 bit words A, B, C is:
  * Whiten(Mix(Mix(Mix(0, A), B), C)) */
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 inline uint32_t JenkinsHashMix(uint32_t hash, uint32_t data) {
     hash += data;
     hash += (hash << 10);
index cd9d7f9..1741fb2 100644 (file)
 #ifndef ANDROID_UTILS_LRU_CACHE_H
 #define ANDROID_UTILS_LRU_CACHE_H
 
-#include <UniquePtr.h>
-#include <utils/BasicHashtable.h>
+#include <memory>
+#include <unordered_set>
+
+#include "utils/TypeHelpers.h"  // hash_t
 
 namespace android {
 
@@ -36,6 +38,7 @@ template <typename TKey, typename TValue>
 class LruCache {
 public:
     explicit LruCache(uint32_t maxCapacity);
+    virtual ~LruCache();
 
     enum Capacity {
         kUnlimitedCapacity,
@@ -50,32 +53,6 @@ public:
     void clear();
     const TValue& peekOldestValue();
 
-    class Iterator {
-    public:
-        Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIndex(-1) {
-        }
-
-        bool next() {
-            mIndex = mCache.mTable->next(mIndex);
-            return (ssize_t)mIndex != -1;
-        }
-
-        size_t index() const {
-            return mIndex;
-        }
-
-        const TValue& value() const {
-            return mCache.mTable->entryAt(mIndex).value;
-        }
-
-        const TKey& key() const {
-            return mCache.mTable->entryAt(mIndex).key;
-        }
-    private:
-        const LruCache<TKey, TValue>& mCache;
-        size_t mIndex;
-    };
-
 private:
     LruCache(const LruCache& that);  // disallow copy constructor
 
@@ -90,27 +67,92 @@ private:
         const TKey& getKey() const { return key; }
     };
 
+    struct HashForEntry : public std::unary_function<Entry*, hash_t> {
+        size_t operator() (const Entry* entry) const {
+            return hash_type(entry->key);
+        };
+    };
+
+    struct EqualityForHashedEntries : public std::unary_function<Entry*, hash_t> {
+        bool operator() (const Entry* lhs, const Entry* rhs) const {
+            return lhs->key == rhs->key;
+        };
+    };
+
+    typedef std::unordered_set<Entry*, HashForEntry, EqualityForHashedEntries> LruCacheSet;
+
     void attachToCache(Entry& entry);
     void detachFromCache(Entry& entry);
-    void rehash(size_t newCapacity);
 
-    UniquePtr<BasicHashtable<TKey, Entry> > mTable;
+    typename LruCacheSet::iterator findByKey(const TKey& key) {
+        Entry entryForSearch(key, mNullValue);
+        typename LruCacheSet::iterator result = mSet->find(&entryForSearch);
+        return result;
+    }
+
+    std::unique_ptr<LruCacheSet> mSet;
     OnEntryRemoved<TKey, TValue>* mListener;
     Entry* mOldest;
     Entry* mYoungest;
     uint32_t mMaxCapacity;
     TValue mNullValue;
+
+public:
+    // To be used like:
+    // while (it.next()) {
+    //   it.value(); it.key();
+    // }
+    class Iterator {
+    public:
+        Iterator(const LruCache<TKey, TValue>& cache):
+                mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {
+        }
+
+        bool next() {
+            if (mIterator == mCache.mSet->end()) {
+                return false;
+            }
+            if (!mBeginReturned) {
+                // mIterator has been initialized to the beginning and
+                // hasn't been returned. Do not advance:
+                mBeginReturned = true;
+            } else {
+                std::advance(mIterator, 1);
+            }
+            bool ret = (mIterator != mCache.mSet->end());
+            return ret;
+        }
+
+        const TValue& value() const {
+            return (*mIterator)->value;
+        }
+
+        const TKey& key() const {
+            return (*mIterator)->key;
+        }
+    private:
+        const LruCache<TKey, TValue>& mCache;
+        typename LruCacheSet::iterator mIterator;
+        bool mBeginReturned;
+    };
 };
 
 // Implementation is here, because it's fully templated
 template <typename TKey, typename TValue>
 LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
-    : mTable(new BasicHashtable<TKey, Entry>)
+    : mSet(new LruCacheSet())
     , mListener(NULL)
     , mOldest(NULL)
     , mYoungest(NULL)
     , mMaxCapacity(maxCapacity)
     , mNullValue(NULL) {
+    mSet->max_load_factor(1.0);
+};
+
+template <typename TKey, typename TValue>
+LruCache<TKey, TValue>::~LruCache() {
+    // Need to delete created entries.
+    clear();
 };
 
 template<typename K, typename V>
@@ -120,20 +162,19 @@ void LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
 
 template <typename TKey, typename TValue>
 size_t LruCache<TKey, TValue>::size() const {
-    return mTable->size();
+    return mSet->size();
 }
 
 template <typename TKey, typename TValue>
 const TValue& LruCache<TKey, TValue>::get(const TKey& key) {
-    hash_t hash = hash_type(key);
-    ssize_t index = mTable->find(-1, hash, key);
-    if (index == -1) {
+    typename LruCacheSet::const_iterator find_result = findByKey(key);
+    if (find_result == mSet->end()) {
         return mNullValue;
     }
-    Entry& entry = mTable->editEntryAt(index);
-    detachFromCache(entry);
-    attachToCache(entry);
-    return entry.value;
+    Entry *entry = *find_result;
+    detachFromCache(*entry);
+    attachToCache(*entry);
+    return entry->value;
 }
 
 template <typename TKey, typename TValue>
@@ -142,36 +183,29 @@ bool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {
         removeOldest();
     }
 
-    hash_t hash = hash_type(key);
-    ssize_t index = mTable->find(-1, hash, key);
-    if (index >= 0) {
+    if (findByKey(key) != mSet->end()) {
         return false;
     }
-    if (!mTable->hasMoreRoom()) {
-        rehash(mTable->capacity() * 2);
-    }
 
-    // Would it be better to initialize a blank entry and assign key, value?
-    Entry initEntry(key, value);
-    index = mTable->add(hash, initEntry);
-    Entry& entry = mTable->editEntryAt(index);
-    attachToCache(entry);
+    Entry* newEntry = new Entry(key, value);
+    mSet->insert(newEntry);
+    attachToCache(*newEntry);
     return true;
 }
 
 template <typename TKey, typename TValue>
 bool LruCache<TKey, TValue>::remove(const TKey& key) {
-    hash_t hash = hash_type(key);
-    ssize_t index = mTable->find(-1, hash, key);
-    if (index < 0) {
+    typename LruCacheSet::const_iterator find_result = findByKey(key);
+    if (find_result == mSet->end()) {
         return false;
     }
-    Entry& entry = mTable->editEntryAt(index);
+    Entry* entry = *find_result;
     if (mListener) {
-        (*mListener)(entry.key, entry.value);
+        (*mListener)(entry->key, entry->value);
     }
-    detachFromCache(entry);
-    mTable->removeAt(index);
+    detachFromCache(*entry);
+    mSet->erase(entry);
+    delete entry;
     return true;
 }
 
@@ -201,7 +235,10 @@ void LruCache<TKey, TValue>::clear() {
     }
     mYoungest = NULL;
     mOldest = NULL;
-    mTable->clear();
+    for (auto entry : *mSet.get()) {
+        delete entry;
+    }
+    mSet->clear();
 }
 
 template <typename TKey, typename TValue>
@@ -232,19 +269,5 @@ void LruCache<TKey, TValue>::detachFromCache(Entry& entry) {
     entry.child = NULL;
 }
 
-template <typename TKey, typename TValue>
-void LruCache<TKey, TValue>::rehash(size_t newCapacity) {
-    UniquePtr<BasicHashtable<TKey, Entry> > oldTable(mTable.release());
-    Entry* oldest = mOldest;
-
-    mOldest = NULL;
-    mYoungest = NULL;
-    mTable.reset(new BasicHashtable<TKey, Entry>(newCapacity));
-    for (Entry* p = oldest; p != NULL; p = p->child) {
-        put(p->key, p->value);
-    }
-}
-
 }
-
 #endif // ANDROID_UTILS_LRU_CACHE_H
index 757519b..f027c79 100644 (file)
@@ -59,7 +59,7 @@ public:
     // lock if possible; returns 0 on success, error otherwise
     status_t    tryLock();
 
-#if HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     // lock the mutex, but don't wait longer than timeoutMilliseconds.
     // Returns 0 on success, TIMED_OUT for failure due to timeout expiration.
     //
@@ -128,7 +128,7 @@ inline void Mutex::unlock() {
 inline status_t Mutex::tryLock() {
     return -pthread_mutex_trylock(&mMutex);
 }
-#if HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 inline status_t Mutex::timedLock(nsecs_t timeoutNs) {
     const struct timespec ts = {
         /* .tv_sec = */ static_cast<time_t>(timeoutNs / 1000000000),
index d131bfc..9a67c7a 100644 (file)
@@ -18,7 +18,6 @@
 #define ANDROID_STRING16_H
 
 #include <utils/Errors.h>
-#include <utils/SharedBuffer.h>
 #include <utils/Unicode.h>
 #include <utils/TypeHelpers.h>
 
@@ -34,6 +33,7 @@ namespace android {
 
 // ---------------------------------------------------------------------------
 
+class SharedBuffer;
 class String8;
 class TextOutput;
 
@@ -64,10 +64,8 @@ public:
                                 ~String16();
     
     inline  const char16_t*     string() const;
-    inline  size_t              size() const;
-    
-    inline  const SharedBuffer* sharedBuffer() const;
     
+            size_t              size() const;
             void                setTo(const String16& other);
             status_t            setTo(const char16_t* other);
             status_t            setTo(const char16_t* other, size_t len);
@@ -144,16 +142,6 @@ inline const char16_t* String16::string() const
     return mString;
 }
 
-inline size_t String16::size() const
-{
-    return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
-}
-
-inline const SharedBuffer* String16::sharedBuffer() const
-{
-    return SharedBuffer::bufferFromData(mString);
-}
-
 inline String16& String16::operator=(const String16& other)
 {
     setTo(other);
index ecfcf10..2a75b98 100644 (file)
@@ -18,7 +18,6 @@
 #define ANDROID_STRING8_H
 
 #include <utils/Errors.h>
-#include <utils/SharedBuffer.h>
 #include <utils/Unicode.h>
 #include <utils/TypeHelpers.h>
 
@@ -65,11 +64,10 @@ public:
 
     inline  const char*         string() const;
     inline  size_t              size() const;
-    inline  size_t              length() const;
     inline  size_t              bytes() const;
     inline  bool                isEmpty() const;
     
-    inline  const SharedBuffer* sharedBuffer() const;
+            size_t              length() const;
     
             void                clear();
 
@@ -263,11 +261,6 @@ inline const char* String8::string() const
     return mString;
 }
 
-inline size_t String8::length() const
-{
-    return SharedBuffer::sizeFromData(mString)-1;
-}
-
 inline size_t String8::size() const
 {
     return length();
@@ -280,12 +273,7 @@ inline bool String8::isEmpty() const
 
 inline size_t String8::bytes() const
 {
-    return SharedBuffer::sizeFromData(mString)-1;
-}
-
-inline const SharedBuffer* String8::sharedBuffer() const
-{
-    return SharedBuffer::bufferFromData(mString);
+    return length();
 }
 
 inline bool String8::contains(const char* other) const
index aba9577..50fde35 100644 (file)
@@ -62,8 +62,10 @@ public:
 
     sp(T* other);
     sp(const sp<T>& other);
+    sp(sp<T>&& other);
     template<typename U> sp(U* other);
     template<typename U> sp(const sp<U>& other);
+    template<typename U> sp(sp<U>&& other);
 
     ~sp();
 
@@ -71,8 +73,10 @@ public:
 
     sp& operator = (T* other);
     sp& operator = (const sp<T>& other);
+    sp& operator = (sp<T>&& other);
 
     template<typename U> sp& operator = (const sp<U>& other);
+    template<typename U> sp& operator = (sp<U>&& other);
     template<typename U> sp& operator = (U* other);
 
     //! Special optimization for use by ProcessState (and nobody else).
@@ -123,6 +127,12 @@ sp<T>::sp(const sp<T>& other)
         m_ptr->incStrong(this);
 }
 
+template<typename T>
+sp<T>::sp(sp<T>&& other)
+        : m_ptr(other.m_ptr) {
+    other.m_ptr = nullptr;
+}
+
 template<typename T> template<typename U>
 sp<T>::sp(U* other)
         : m_ptr(other) {
@@ -137,6 +147,12 @@ sp<T>::sp(const sp<U>& other)
         m_ptr->incStrong(this);
 }
 
+template<typename T> template<typename U>
+sp<T>::sp(sp<U>&& other)
+        : m_ptr(other.m_ptr) {
+    other.m_ptr = nullptr;
+}
+
 template<typename T>
 sp<T>::~sp() {
     if (m_ptr)
@@ -155,6 +171,15 @@ sp<T>& sp<T>::operator =(const sp<T>& other) {
 }
 
 template<typename T>
+sp<T>& sp<T>::operator =(sp<T>&& other) {
+    if (m_ptr)
+        m_ptr->decStrong(this);
+    m_ptr = other.m_ptr;
+    other.m_ptr = nullptr;
+    return *this;
+}
+
+template<typename T>
 sp<T>& sp<T>::operator =(T* other) {
     if (other)
         other->incStrong(this);
@@ -176,6 +201,15 @@ sp<T>& sp<T>::operator =(const sp<U>& other) {
 }
 
 template<typename T> template<typename U>
+sp<T>& sp<T>::operator =(sp<U>&& other) {
+    if (m_ptr)
+        m_ptr->decStrong(this);
+    m_ptr = other.m_ptr;
+    other.m_ptr = nullptr;
+    return *this;
+}
+
+template<typename T> template<typename U>
 sp<T>& sp<T>::operator =(U* other) {
     if (other)
         ((T*) other)->incStrong(this);
index 28839fd..1532b7e 100644 (file)
@@ -70,7 +70,7 @@ public:
     // Indicates whether this thread is running or not.
             bool        isRunning() const;
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     // Return the thread's kernel ID, same as the thread itself calling gettid(),
     // or -1 if the thread is not running.
             pid_t       getTid() const;
@@ -101,7 +101,7 @@ private:
     volatile bool           mExitPending;
     volatile bool           mRunning;
             sp<Thread>      mHoldSelf;
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     // legacy for debugging, not used by getTid() as it is set by the child thread
     // and so is not initialized until the child reaches that point
             pid_t           mTid;
index 6ee343d..6ba68f6 100644 (file)
@@ -17,7 +17,7 @@
 #ifndef ANDROID_TRACE_H
 #define ANDROID_TRACE_H
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 
 #include <fcntl.h>
 #include <stdint.h>
@@ -59,11 +59,11 @@ private:
 
 }; // namespace android
 
-#else // HAVE_ANDROID_OS
+#else // !__ANDROID__
 
 #define ATRACE_NAME(...)
 #define ATRACE_CALL()
 
-#endif // HAVE_ANDROID_OS
+#endif // __ANDROID__
 
 #endif // ANDROID_TRACE_H
index 13c9081..61d618e 100644 (file)
@@ -131,7 +131,8 @@ int compare_type(const TYPE& lhs, const TYPE& rhs) {
 template<typename TYPE> inline
 void construct_type(TYPE* p, size_t n) {
     if (!traits<TYPE>::has_trivial_ctor) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             new(p++) TYPE;
         }
     }
@@ -140,7 +141,8 @@ void construct_type(TYPE* p, size_t n) {
 template<typename TYPE> inline
 void destroy_type(TYPE* p, size_t n) {
     if (!traits<TYPE>::has_trivial_dtor) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             p->~TYPE();
             p++;
         }
@@ -150,7 +152,8 @@ void destroy_type(TYPE* p, size_t n) {
 template<typename TYPE> inline
 void copy_type(TYPE* d, const TYPE* s, size_t n) {
     if (!traits<TYPE>::has_trivial_copy) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             new(d) TYPE(*s);
             d++, s++;
         }
@@ -162,12 +165,14 @@ void copy_type(TYPE* d, const TYPE* s, size_t n) {
 template<typename TYPE> inline
 void splat_type(TYPE* where, const TYPE* what, size_t n) {
     if (!traits<TYPE>::has_trivial_copy) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             new(where) TYPE(*what);
             where++;
         }
     } else {
-        while (n--) {
+        while (n > 0) {
+            n--;
             *where++ = *what;
         }
     }
@@ -182,7 +187,8 @@ void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
     } else {
         d += n;
         s += n;
-        while (n--) {
+        while (n > 0) {
+            n--;
             --d, --s;
             if (!traits<TYPE>::has_trivial_copy) {
                 new(d) TYPE(*s);
@@ -203,7 +209,8 @@ void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
     {
         memmove(d,s,n*sizeof(TYPE));
     } else {
-        while (n--) {
+        while (n > 0) {
+            n--;
             if (!traits<TYPE>::has_trivial_copy) {
                 new(d) TYPE(*s);
             } else {
index 3b00683..3591a6b 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <stdint.h>
 #include <string.h>
+#include <sys/cdefs.h>
 #include <sys/types.h>
 #include <utils/Compat.h>
 
@@ -33,17 +34,33 @@ enum {
   kCompressDeflated   = 8,        // standard deflate
 };
 
-struct ZipEntryName {
+struct ZipString {
   const uint8_t* name;
   uint16_t name_length;
 
-  ZipEntryName() {}
+  ZipString() {}
 
   /*
    * entry_name has to be an c-style string with only ASCII characters.
    */
-  explicit ZipEntryName(const char* entry_name)
+  explicit ZipString(const char* entry_name)
       : name(reinterpret_cast<const uint8_t*>(entry_name)), name_length(strlen(entry_name)) {}
+
+  bool operator==(const ZipString& rhs) const {
+    return name && (name_length == rhs.name_length) &&
+        (memcmp(name, rhs.name, name_length) == 0);
+  }
+
+  bool StartsWith(const ZipString& prefix) const {
+    return name && (name_length >= prefix.name_length) &&
+        (memcmp(name, prefix.name, prefix.name_length) == 0);
+  }
+
+  bool EndsWith(const ZipString& suffix) const {
+    return name && (name_length >= suffix.name_length) &&
+        (memcmp(name + name_length - suffix.name_length, suffix.name,
+                suffix.name_length) == 0);
+  }
 };
 
 /*
@@ -136,7 +153,7 @@ void CloseArchive(ZipArchiveHandle handle);
  * and length, a call to VerifyCrcAndLengths must be made after entry data
  * has been processed.
  */
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipEntryName& entryName,
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
                   ZipEntry* data);
 
 /*
@@ -147,15 +164,14 @@ int32_t FindEntry(const ZipArchiveHandle handle, const ZipEntryName& entryName,
  * calls to Next. All calls to StartIteration must be matched by a call to
  * EndIteration to free any allocated memory.
  *
- * This method also accepts an optional prefix to restrict iteration to
- * entry names that start with |optional_prefix|.
+ * This method also accepts optional prefix and suffix to restrict iteration to
+ * entry names that start with |optional_prefix| or end with |optional_suffix|.
  *
  * Returns 0 on success and negative values on failure.
  */
 int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
-                       const ZipEntryName* optional_prefix,
-                       // TODO: Remove the default parameter.
-                       const ZipEntryName* optional_suffix = NULL);
+                       const ZipString* optional_prefix,
+                       const ZipString* optional_suffix);
 
 /*
  * Advance to the next element in the zipfile in iteration order.
@@ -163,7 +179,7 @@ int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
  * Returns 0 on success, -1 if there are no more elements in this
  * archive and lower negative values on failure.
  */
-int32_t Next(void* cookie, ZipEntry* data, ZipEntryName *name);
+int32_t Next(void* cookie, ZipEntry* data, ZipString* name);
 
 /*
  * End iteration over all entries of a zip file and frees the memory allocated
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
new file mode 100644 (file)
index 0000000..d996c4a
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBZIPARCHIVE_ZIPWRITER_H_
+#define LIBZIPARCHIVE_ZIPWRITER_H_
+
+#include "base/macros.h"
+#include <utils/Compat.h>
+
+#include <cstdio>
+#include <ctime>
+#include <memory>
+#include <string>
+#include <vector>
+#include <zlib.h>
+
+/**
+ * Writes a Zip file via a stateful interface.
+ *
+ * Example:
+ *
+ *   FILE* file = fopen("path/to/zip.zip", "wb");
+ *
+ *   ZipWriter writer(file);
+ *
+ *   writer.StartEntry("test.txt", ZipWriter::kCompress | ZipWriter::kAlign);
+ *   writer.WriteBytes(buffer, bufferLen);
+ *   writer.WriteBytes(buffer2, bufferLen2);
+ *   writer.FinishEntry();
+ *
+ *   writer.StartEntry("empty.txt", 0);
+ *   writer.FinishEntry();
+ *
+ *   writer.Finish();
+ *
+ *   fclose(file);
+ */
+class ZipWriter {
+public:
+  enum {
+    /**
+     * Flag to compress the zip entry using deflate.
+     */
+    kCompress = 0x01,
+
+    /**
+     * Flag to align the zip entry data on a 32bit boundary. Useful for
+     * mmapping the data at runtime.
+     */
+    kAlign32 = 0x02,
+  };
+
+  static const char* ErrorCodeString(int32_t error_code);
+
+  /**
+   * Create a ZipWriter that will write into a FILE stream. The file should be opened with
+   * open mode of "wb" or "w+b". ZipWriter does not take ownership of the file stream. The
+   * caller is responsible for closing the file.
+   */
+  explicit ZipWriter(FILE* f);
+
+  // Move constructor.
+  ZipWriter(ZipWriter&& zipWriter);
+
+  // Move assignment.
+  ZipWriter& operator=(ZipWriter&& zipWriter);
+
+  /**
+   * Starts a new zip entry with the given path and flags.
+   * Flags can be a bitwise OR of ZipWriter::kCompress and ZipWriter::kAlign.
+   * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
+   * Returns 0 on success, and an error value < 0 on failure.
+   */
+  int32_t StartEntry(const char* path, size_t flags);
+
+  /**
+   * Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
+   */
+  int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);
+
+  /**
+   * Writes bytes to the zip file for the previously started zip entry.
+   * Returns 0 on success, and an error value < 0 on failure.
+   */
+  int32_t WriteBytes(const void* data, size_t len);
+
+  /**
+   * Finish a zip entry started with StartEntry(const char*, size_t) or
+   * StartEntryWithTime(const char*, size_t, time_t). This must be called before
+   * any new zip entries are started, or before Finish() is called.
+   * Returns 0 on success, and an error value < 0 on failure.
+   */
+  int32_t FinishEntry();
+
+  /**
+   * Writes the Central Directory Headers and flushes the zip file stream.
+   * Returns 0 on success, and an error value < 0 on failure.
+   */
+  int32_t Finish();
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(ZipWriter);
+
+  struct FileInfo {
+    std::string path;
+    uint16_t compression_method;
+    uint32_t crc32;
+    uint32_t compressed_size;
+    uint32_t uncompressed_size;
+    uint16_t last_mod_time;
+    uint16_t last_mod_date;
+    uint32_t local_file_header_offset;
+  };
+
+  int32_t HandleError(int32_t error_code);
+  int32_t PrepareDeflate();
+  int32_t StoreBytes(FileInfo* file, const void* data, size_t len);
+  int32_t CompressBytes(FileInfo* file, const void* data, size_t len);
+  int32_t FlushCompressedBytes(FileInfo* file);
+
+  enum class State {
+    kWritingZip,
+    kWritingEntry,
+    kDone,
+    kError,
+  };
+
+  FILE* file_;
+  off64_t current_offset_;
+  State state_;
+  std::vector<FileInfo> files_;
+
+  std::unique_ptr<z_stream, void(*)(z_stream*)> z_stream_;
+  std::vector<uint8_t> buffer_;
+};
+
+#endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
index de065dc..d6cb4e5 100644 (file)
@@ -5,9 +5,9 @@ LOCAL_PATH:= $(call my-dir)
 # --
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-init_options += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_DISABLE_SELINUX=1
+init_options += -DALLOW_LOCAL_PROP_OVERRIDE=1 -DALLOW_PERMISSIVE_SELINUX=1
 else
-init_options += -DALLOW_LOCAL_PROP_OVERRIDE=0 -DALLOW_DISABLE_SELINUX=0
+init_options += -DALLOW_LOCAL_PROP_OVERRIDE=0 -DALLOW_PERMISSIVE_SELINUX=0
 endif
 
 init_options += -DLOG_UEVENTS=0
@@ -18,21 +18,44 @@ init_cflags += \
     -Wno-unused-parameter \
     -Werror \
 
-init_clang := true
-
 # --
 
+# If building on Linux, then build unit test for the host.
+ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
 LOCAL_CPPFLAGS := $(init_cflags)
 LOCAL_SRC_FILES:= \
+    parser/tokenizer.cpp \
+
+LOCAL_MODULE := libinit_parser
+LOCAL_CLANG := true
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := init_parser_tests
+LOCAL_SRC_FILES := \
+    parser/tokenizer_test.cpp \
+
+LOCAL_STATIC_LIBRARIES := libinit_parser
+LOCAL_CLANG := true
+include $(BUILD_HOST_NATIVE_TEST)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_CPPFLAGS := $(init_cflags)
+LOCAL_SRC_FILES:= \
+    action.cpp \
+    import_parser.cpp \
     init_parser.cpp \
     log.cpp \
     parser.cpp \
+    service.cpp \
     util.cpp \
 
 LOCAL_STATIC_LIBRARIES := libbase
 LOCAL_MODULE := libinit
-LOCAL_CLANG := $(init_clang)
+LOCAL_SANITIZE := integer
+LOCAL_CLANG := true
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -61,16 +84,19 @@ LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
 LOCAL_STATIC_LIBRARIES := \
     libinit \
     libfs_mgr \
+    libfec \
+    libfec_rs \
     libsquashfs_utils \
     liblogwrap \
     libcutils \
     libbase \
     libext4_utils_static \
     libutils \
-    liblog \
     libc \
     libselinux \
+    liblog \
     libmincrypt \
+    libcrypto_static \
     libc++_static \
     libdl \
     libsparse_static \
@@ -81,7 +107,8 @@ LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
 
-LOCAL_CLANG := $(init_clang)
+LOCAL_SANITIZE := integer
+LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
 
 
@@ -98,5 +125,6 @@ LOCAL_SHARED_LIBRARIES += \
     libbase \
 
 LOCAL_STATIC_LIBRARIES := libinit
-LOCAL_CLANG := $(init_clang)
+LOCAL_SANITIZE := integer
+LOCAL_CLANG := true
 include $(BUILD_NATIVE_TEST)
diff --git a/init/action.cpp b/init/action.cpp
new file mode 100644 (file)
index 0000000..c6cbc2e
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "action.h"
+
+#include <errno.h>
+
+#include <base/strings.h>
+#include <base/stringprintf.h>
+
+#include "builtins.h"
+#include "error.h"
+#include "init_parser.h"
+#include "log.h"
+#include "property_service.h"
+#include "util.h"
+
+using android::base::Join;
+using android::base::StringPrintf;
+
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
+                 const std::string& filename, int line)
+    : func_(f), args_(args), filename_(filename), line_(line) {
+}
+
+int Command::InvokeFunc() const {
+    std::vector<std::string> expanded_args;
+    expanded_args.resize(args_.size());
+    expanded_args[0] = args_[0];
+    for (std::size_t i = 1; i < args_.size(); ++i) {
+        if (!expand_props(args_[i], &expanded_args[i])) {
+            ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
+            return -EINVAL;
+        }
+    }
+
+    return func_(expanded_args);
+}
+
+std::string Command::BuildCommandString() const {
+    return Join(args_, ' ');
+}
+
+std::string Command::BuildSourceString() const {
+    if (!filename_.empty()) {
+        return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
+    } else {
+        return std::string();
+    }
+}
+
+Action::Action(bool oneshot) : oneshot_(oneshot) {
+}
+
+const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
+
+bool Action::AddCommand(const std::vector<std::string>& args,
+                        const std::string& filename, int line, std::string* err) {
+    if (!function_map_) {
+        *err = "no function map available";
+        return false;
+    }
+
+    if (args.empty()) {
+        *err = "command needed, but not provided";
+        return false;
+    }
+
+    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
+    if (!function) {
+        return false;
+    }
+
+    AddCommand(function, args, filename, line);
+    return true;
+}
+
+void Action::AddCommand(BuiltinFunction f,
+                        const std::vector<std::string>& args,
+                        const std::string& filename, int line) {
+    commands_.emplace_back(f, args, filename, line);
+}
+
+void Action::CombineAction(const Action& action) {
+    for (const auto& c : action.commands_) {
+        commands_.emplace_back(c);
+    }
+}
+
+std::size_t Action::NumCommands() const {
+    return commands_.size();
+}
+
+void Action::ExecuteOneCommand(std::size_t command) const {
+    ExecuteCommand(commands_[command]);
+}
+
+void Action::ExecuteAllCommands() const {
+    for (const auto& c : commands_) {
+        ExecuteCommand(c);
+    }
+}
+
+void Action::ExecuteCommand(const Command& command) const {
+    Timer t;
+    int result = command.InvokeFunc();
+
+    if (klog_get_level() >= KLOG_INFO_LEVEL) {
+        std::string trigger_name = BuildTriggersString();
+        std::string cmd_str = command.BuildCommandString();
+        std::string source = command.BuildSourceString();
+
+        INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
+             cmd_str.c_str(), trigger_name.c_str(), source.c_str(),
+             result, t.duration());
+    }
+}
+
+bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
+    const static std::string prop_str("property:");
+    std::string prop_name(trigger.substr(prop_str.length()));
+    size_t equal_pos = prop_name.find('=');
+    if (equal_pos == std::string::npos) {
+        *err = "property trigger found without matching '='";
+        return false;
+    }
+
+    std::string prop_value(prop_name.substr(equal_pos + 1));
+    prop_name.erase(equal_pos);
+
+    auto res = property_triggers_.emplace(prop_name, prop_value);
+    if (res.second == false) {
+        *err = "multiple property triggers found for same property";
+        return false;
+    }
+    return true;
+}
+
+bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
+    const static std::string prop_str("property:");
+    for (std::size_t i = 0; i < args.size(); ++i) {
+        if (i % 2) {
+            if (args[i] != "&&") {
+                *err = "&& is the only symbol allowed to concatenate actions";
+                return false;
+            } else {
+                continue;
+            }
+        }
+
+        if (!args[i].compare(0, prop_str.length(), prop_str)) {
+            if (!ParsePropertyTrigger(args[i], err)) {
+                return false;
+            }
+        } else {
+            if (!event_trigger_.empty()) {
+                *err = "multiple event triggers are not allowed";
+                return false;
+            }
+
+            event_trigger_ = args[i];
+        }
+    }
+
+    return true;
+}
+
+bool Action::InitSingleTrigger(const std::string& trigger) {
+    std::vector<std::string> name_vector{trigger};
+    std::string err;
+    return InitTriggers(name_vector, &err);
+}
+
+// This function checks that all property triggers are satisfied, that is
+// for each (name, value) in property_triggers_, check that the current
+// value of the property 'name' == value.
+//
+// It takes an optional (name, value) pair, which if provided must
+// be present in property_triggers_; it skips the check of the current
+// property value for this pair.
+bool Action::CheckPropertyTriggers(const std::string& name,
+                                   const std::string& value) const {
+    if (property_triggers_.empty()) {
+        return true;
+    }
+
+    bool found = name.empty();
+    for (const auto& t : property_triggers_) {
+        const auto& trigger_name = t.first;
+        const auto& trigger_value = t.second;
+        if (trigger_name == name) {
+            if (trigger_value != "*" && trigger_value != value) {
+                return false;
+            } else {
+                found = true;
+            }
+        } else {
+            std::string prop_val = property_get(trigger_name.c_str());
+            if (prop_val.empty() || (trigger_value != "*" &&
+                                     trigger_value != prop_val)) {
+                return false;
+            }
+        }
+    }
+    return found;
+}
+
+bool Action::CheckEventTrigger(const std::string& trigger) const {
+    return !event_trigger_.empty() &&
+        trigger == event_trigger_ &&
+        CheckPropertyTriggers();
+}
+
+bool Action::CheckPropertyTrigger(const std::string& name,
+                                  const std::string& value) const {
+    return event_trigger_.empty() && CheckPropertyTriggers(name, value);
+}
+
+bool Action::TriggersEqual(const Action& other) const {
+    return property_triggers_ == other.property_triggers_ &&
+        event_trigger_ == other.event_trigger_;
+}
+
+std::string Action::BuildTriggersString() const {
+    std::string result;
+
+    for (const auto& t : property_triggers_) {
+        result += t.first;
+        result += '=';
+        result += t.second;
+        result += ' ';
+    }
+    if (!event_trigger_.empty()) {
+        result += event_trigger_;
+        result += ' ';
+    }
+    result.pop_back();
+    return result;
+}
+
+void Action::DumpState() const {
+    std::string trigger_name = BuildTriggersString();
+    INFO("on %s\n", trigger_name.c_str());
+
+    for (const auto& c : commands_) {
+        std::string cmd_str = c.BuildCommandString();
+        INFO(" %s\n", cmd_str.c_str());
+    }
+    INFO("\n");
+}
+
+class EventTrigger : public Trigger {
+public:
+    EventTrigger(const std::string& trigger) : trigger_(trigger) {
+    }
+    bool CheckTriggers(const Action& action) const override {
+        return action.CheckEventTrigger(trigger_);
+    }
+private:
+    const std::string trigger_;
+};
+
+class PropertyTrigger : public Trigger {
+public:
+    PropertyTrigger(const std::string& name, const std::string& value)
+        : name_(name), value_(value) {
+    }
+    bool CheckTriggers(const Action& action) const override {
+        return action.CheckPropertyTrigger(name_, value_);
+    }
+private:
+    const std::string name_;
+    const std::string value_;
+};
+
+class BuiltinTrigger : public Trigger {
+public:
+    BuiltinTrigger(Action* action) : action_(action) {
+    }
+    bool CheckTriggers(const Action& action) const override {
+        return action_ == &action;
+    }
+private:
+    const Action* action_;
+};
+
+ActionManager::ActionManager() : current_command_(0) {
+}
+
+ActionManager& ActionManager::GetInstance() {
+    static ActionManager instance;
+    return instance;
+}
+
+void ActionManager::AddAction(std::unique_ptr<Action> action) {
+    auto old_action_it =
+        std::find_if(actions_.begin(), actions_.end(),
+                     [&action] (std::unique_ptr<Action>& a) {
+                         return action->TriggersEqual(*a);
+                     });
+
+    if (old_action_it != actions_.end()) {
+        (*old_action_it)->CombineAction(*action);
+    } else {
+        actions_.emplace_back(std::move(action));
+    }
+}
+
+void ActionManager::QueueEventTrigger(const std::string& trigger) {
+    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
+}
+
+void ActionManager::QueuePropertyTrigger(const std::string& name,
+                                         const std::string& value) {
+    trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
+}
+
+void ActionManager::QueueAllPropertyTriggers() {
+    QueuePropertyTrigger("", "");
+}
+
+void ActionManager::QueueBuiltinAction(BuiltinFunction func,
+                                       const std::string& name) {
+    auto action = std::make_unique<Action>(true);
+    std::vector<std::string> name_vector{name};
+
+    if (!action->InitSingleTrigger(name)) {
+        return;
+    }
+
+    action->AddCommand(func, name_vector);
+
+    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
+    actions_.emplace_back(std::move(action));
+}
+
+void ActionManager::ExecuteOneCommand() {
+    // Loop through the trigger queue until we have an action to execute
+    while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
+        for (const auto& action : actions_) {
+            if (trigger_queue_.front()->CheckTriggers(*action)) {
+                current_executing_actions_.emplace(action.get());
+            }
+        }
+        trigger_queue_.pop();
+    }
+
+    if (current_executing_actions_.empty()) {
+        return;
+    }
+
+    auto action = current_executing_actions_.front();
+
+    if (current_command_ == 0) {
+        std::string trigger_name = action->BuildTriggersString();
+        INFO("processing action (%s)\n", trigger_name.c_str());
+    }
+
+    action->ExecuteOneCommand(current_command_);
+
+    // If this was the last command in the current action, then remove
+    // the action from the executing list.
+    // If this action was oneshot, then also remove it from actions_.
+    ++current_command_;
+    if (current_command_ == action->NumCommands()) {
+        current_executing_actions_.pop();
+        current_command_ = 0;
+        if (action->oneshot()) {
+            auto eraser = [&action] (std::unique_ptr<Action>& a) {
+                return a.get() == action;
+            };
+            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
+        }
+    }
+}
+
+bool ActionManager::HasMoreCommands() const {
+    return !current_executing_actions_.empty() || !trigger_queue_.empty();
+}
+
+void ActionManager::DumpState() const {
+    for (const auto& a : actions_) {
+        a->DumpState();
+    }
+    INFO("\n");
+}
+
+bool ActionParser::ParseSection(const std::vector<std::string>& args,
+                                std::string* err) {
+    std::vector<std::string> triggers(args.begin() + 1, args.end());
+    if (triggers.size() < 1) {
+        *err = "actions must have a trigger";
+        return false;
+    }
+
+    auto action = std::make_unique<Action>(false);
+    if (!action->InitTriggers(triggers, err)) {
+        return false;
+    }
+
+    action_ = std::move(action);
+    return true;
+}
+
+bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
+                                    const std::string& filename, int line,
+                                    std::string* err) const {
+    return action_ ? action_->AddCommand(args, filename, line, err) : false;
+}
+
+void ActionParser::EndSection() {
+    if (action_ && action_->NumCommands() > 0) {
+        ActionManager::GetInstance().AddAction(std::move(action_));
+    }
+}
diff --git a/init/action.h b/init/action.h
new file mode 100644 (file)
index 0000000..6dee2d0
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_ACTION_H
+#define _INIT_ACTION_H
+
+#include <map>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "builtins.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+
+class Command {
+public:
+    Command(BuiltinFunction f, const std::vector<std::string>& args,
+            const std::string& filename, int line);
+
+    int InvokeFunc() const;
+    std::string BuildCommandString() const;
+    std::string BuildSourceString() const;
+
+private:
+    BuiltinFunction func_;
+    std::vector<std::string> args_;
+    std::string filename_;
+    int line_;
+};
+
+class Action {
+public:
+    Action(bool oneshot = false);
+
+    bool AddCommand(const std::vector<std::string>& args,
+                    const std::string& filename, int line, std::string* err);
+    void AddCommand(BuiltinFunction f,
+                    const std::vector<std::string>& args,
+                    const std::string& filename = "", int line = 0);
+    void CombineAction(const Action& action);
+    bool InitTriggers(const std::vector<std::string>& args, std::string* err);
+    bool InitSingleTrigger(const std::string& trigger);
+    std::size_t NumCommands() const;
+    void ExecuteOneCommand(std::size_t command) const;
+    void ExecuteAllCommands() const;
+    bool CheckEventTrigger(const std::string& trigger) const;
+    bool CheckPropertyTrigger(const std::string& name,
+                              const std::string& value) const;
+    bool TriggersEqual(const Action& other) const;
+    std::string BuildTriggersString() const;
+    void DumpState() const;
+
+    bool oneshot() const { return oneshot_; }
+    static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
+        function_map_ = function_map;
+    }
+
+
+private:
+    void ExecuteCommand(const Command& command) const;
+    bool CheckPropertyTriggers(const std::string& name = "",
+                               const std::string& value = "") const;
+    bool ParsePropertyTrigger(const std::string& trigger, std::string* err);
+
+    std::map<std::string, std::string> property_triggers_;
+    std::string event_trigger_;
+    std::vector<Command> commands_;
+    bool oneshot_;
+    static const KeywordMap<BuiltinFunction>* function_map_;
+};
+
+class Trigger {
+public:
+    virtual ~Trigger() { }
+    virtual bool CheckTriggers(const Action& action) const = 0;
+};
+
+class ActionManager {
+public:
+    static ActionManager& GetInstance();
+
+    void AddAction(std::unique_ptr<Action> action);
+    void QueueEventTrigger(const std::string& trigger);
+    void QueuePropertyTrigger(const std::string& name, const std::string& value);
+    void QueueAllPropertyTriggers();
+    void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
+    void ExecuteOneCommand();
+    bool HasMoreCommands() const;
+    void DumpState() const;
+
+private:
+    ActionManager();
+
+    ActionManager(ActionManager const&) = delete;
+    void operator=(ActionManager const&) = delete;
+
+    std::vector<std::unique_ptr<Action>> actions_;
+    std::queue<std::unique_ptr<Trigger>> trigger_queue_;
+    std::queue<const Action*> current_executing_actions_;
+    std::size_t current_command_;
+};
+
+class ActionParser : public SectionParser {
+public:
+    ActionParser() : action_(nullptr) {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override;
+    void EndSection() override;
+    void EndFile(const std::string&) override {
+    }
+private:
+    std::unique_ptr<Action> action_;
+};
+
+#endif
index df8359d..e1e0c48 100644 (file)
@@ -15,7 +15,6 @@
  */
 
 #include "bootchart.h"
-#include "keywords.h"
 #include "log.h"
 #include "property_service.h"
 
@@ -32,6 +31,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <base/file.h>
 
@@ -77,8 +77,8 @@ static void log_header() {
         return;
     }
 
-    char fingerprint[PROP_VALUE_MAX];
-    if (property_get("ro.build.fingerprint", fingerprint) == -1) {
+    std::string fingerprint = property_get("ro.build.fingerprint");
+    if (fingerprint.empty()) {
         return;
     }
 
@@ -92,7 +92,7 @@ static void log_header() {
     fprintf(out, "version = Android init 0.8\n");
     fprintf(out, "title = Boot chart for Android (%s)\n", date);
     fprintf(out, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
-    fprintf(out, "system.release = %s\n", fingerprint);
+    fprintf(out, "system.release = %s\n", fingerprint.c_str());
     // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
     fprintf(out, "system.cpu = %s\n", uts.machine);
     fprintf(out, "system.kernel.options = %s\n", kernel_cmdline.c_str());
@@ -164,10 +164,11 @@ static int bootchart_init() {
         // timeout. this is useful when using -wipe-data since the /data
         // partition is fresh.
         std::string cmdline;
+        const char* s;
         android::base::ReadFileToString("/proc/cmdline", &cmdline);
 #define KERNEL_OPTION  "androidboot.bootchart="
-        if (strstr(cmdline.c_str(), KERNEL_OPTION) != NULL) {
-            timeout = atoi(cmdline.c_str() + sizeof(KERNEL_OPTION) - 1);
+        if ((s = strstr(cmdline.c_str(), KERNEL_OPTION)) != NULL) {
+            timeout = atoi(s + sizeof(KERNEL_OPTION) - 1);
         }
     }
     if (timeout == 0)
@@ -202,7 +203,7 @@ static int bootchart_init() {
     return count;
 }
 
-int do_bootchart_init(int nargs, char** args) {
+int do_bootchart_init(const std::vector<std::string>& args) {
     g_remaining_samples = bootchart_init();
     if (g_remaining_samples < 0) {
         ERROR("Bootcharting init failure: %s\n", strerror(errno));
index cf61d83..47eda7a 100644 (file)
 #ifndef _BOOTCHART_H
 #define _BOOTCHART_H
 
+#include <string>
+#include <vector>
+
+int do_bootchart_init(const std::vector<std::string>& args);
 void bootchart_sample(int* timeout);
 
 #endif /* _BOOTCHART_H */
index 8eb5b5b..36ecbb8 100644 (file)
  * limitations under the License.
  */
 
+#include "builtins.h"
+
 #include <errno.h>
 #include <fcntl.h>
+#include <mntent.h>
 #include <net/if.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <base/stringprintf.h>
 #include <cutils/partition_utils.h>
 #include <cutils/android_reboot.h>
+#include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
 
-#include "init.h"
-#include "keywords.h"
-#include "property_service.h"
+#include "action.h"
+#include "bootchart.h"
 #include "devices.h"
+#include "init.h"
 #include "init_parser.h"
-#include "util.h"
 #include "log.h"
+#include "property_service.h"
+#include "service.h"
+#include "util.h"
 
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
-
-int add_environment(const char *name, const char *value);
+#define UNMOUNT_CHECK_MS 5000
+#define UNMOUNT_CHECK_TIMES 10
 
 // System call provided by bionic but not in any header file.
 extern "C" int init_module(void *, unsigned long, const char *);
 
-static int insmod(const char *filename, char *options)
-{
-    char filename_val[PROP_VALUE_MAX];
-    if (expand_props(filename_val, filename, sizeof(filename_val)) == -1) {
-        ERROR("insmod: cannot expand '%s'\n", filename);
-        return -EINVAL;
-    }
-
+static int insmod(const char *filename, const char *options) {
     std::string module;
-    if (!read_file(filename_val, &module)) {
+    if (!read_file(filename, &module)) {
         return -1;
     }
 
@@ -72,8 +72,7 @@ static int insmod(const char *filename, char *options)
     return init_module(&module[0], module.size(), options);
 }
 
-static int __ifupdown(const char *interface, int up)
-{
+static int __ifupdown(const char *interface, int up) {
     struct ifreq ifr;
     int s, ret;
 
@@ -100,154 +99,179 @@ done:
     return ret;
 }
 
-static void service_start_if_not_disabled(struct service *svc)
-{
-    if (!(svc->flags & SVC_DISABLED)) {
-        service_start(svc, NULL);
-    } else {
-        svc->flags |= SVC_DISABLED_START;
+static void unmount_and_fsck(const struct mntent *entry) {
+    if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
+        return;
+
+    /* First, lazily unmount the directory. This unmount request finishes when
+     * all processes that open a file or directory in |entry->mnt_dir| exit.
+     */
+    TEMP_FAILURE_RETRY(umount2(entry->mnt_dir, MNT_DETACH));
+
+    /* Next, kill all processes except init, kthreadd, and kthreadd's
+     * children to finish the lazy unmount. Killing all processes here is okay
+     * because this callback function is only called right before reboot().
+     * It might be cleaner to selectively kill processes that actually use
+     * |entry->mnt_dir| rather than killing all, probably by reusing a function
+     * like killProcessesWithOpenFiles() in vold/, but the selinux policy does
+     * not allow init to scan /proc/<pid> files which the utility function
+     * heavily relies on. The policy does not allow the process to execute
+     * killall/pkill binaries either. Note that some processes might
+     * automatically restart after kill(), but that is not really a problem
+     * because |entry->mnt_dir| is no longer visible to such new processes.
+     */
+    ServiceManager::GetInstance().ForEachService([] (Service* s) { s->Stop(); });
+    TEMP_FAILURE_RETRY(kill(-1, SIGKILL));
+
+    int count = 0;
+    while (count++ < UNMOUNT_CHECK_TIMES) {
+        int fd = TEMP_FAILURE_RETRY(open(entry->mnt_fsname, O_RDONLY | O_EXCL));
+        if (fd >= 0) {
+            /* |entry->mnt_dir| has sucessfully been unmounted. */
+            close(fd);
+            break;
+        } else if (errno == EBUSY) {
+            /* Some processes using |entry->mnt_dir| are still alive. Wait for a
+             * while then retry.
+             */
+            TEMP_FAILURE_RETRY(
+                usleep(UNMOUNT_CHECK_MS * 1000 / UNMOUNT_CHECK_TIMES));
+            continue;
+        } else {
+            /* Cannot open the device. Give up. */
+            return;
+        }
+    }
+
+    int st;
+    if (!strcmp(entry->mnt_type, "f2fs")) {
+        const char *f2fs_argv[] = {
+            "/system/bin/fsck.f2fs", "-f", entry->mnt_fsname,
+        };
+        android_fork_execvp_ext(ARRAY_SIZE(f2fs_argv), (char **)f2fs_argv,
+                                &st, true, LOG_KLOG, true, NULL, NULL, 0);
+    } else if (!strcmp(entry->mnt_type, "ext4")) {
+        const char *ext4_argv[] = {
+            "/system/bin/e2fsck", "-f", "-y", entry->mnt_fsname,
+        };
+        android_fork_execvp_ext(ARRAY_SIZE(ext4_argv), (char **)ext4_argv,
+                                &st, true, LOG_KLOG, true, NULL, NULL, 0);
     }
 }
 
-int do_class_start(int nargs, char **args)
-{
+static int do_class_start(const std::vector<std::string>& args) {
         /* Starting a class does not start services
          * which are explicitly disabled.  They must
          * be started individually.
          */
-    service_for_each_class(args[1], service_start_if_not_disabled);
+    ServiceManager::GetInstance().
+        ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });
     return 0;
 }
 
-int do_class_stop(int nargs, char **args)
-{
-    service_for_each_class(args[1], service_stop);
+static int do_class_stop(const std::vector<std::string>& args) {
+    ServiceManager::GetInstance().
+        ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); });
     return 0;
 }
 
-int do_class_reset(int nargs, char **args)
-{
-    service_for_each_class(args[1], service_reset);
+static int do_class_reset(const std::vector<std::string>& args) {
+    ServiceManager::GetInstance().
+        ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); });
     return 0;
 }
 
-int do_domainname(int nargs, char **args)
-{
-    return write_file("/proc/sys/kernel/domainname", args[1]);
+static int do_domainname(const std::vector<std::string>& args) {
+    return write_file("/proc/sys/kernel/domainname", args[1].c_str());
 }
 
-int do_enable(int nargs, char **args)
-{
-    struct service *svc;
-    svc = service_find_by_name(args[1]);
-    if (svc) {
-        svc->flags &= ~(SVC_DISABLED | SVC_RC_DISABLED);
-        if (svc->flags & SVC_DISABLED_START) {
-            service_start(svc, NULL);
-        }
-    } else {
+static int do_enable(const std::vector<std::string>& args) {
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+    if (!svc) {
         return -1;
     }
-    return 0;
+    return svc->Enable();
 }
 
-int do_exec(int nargs, char** args) {
-    service* svc = make_exec_oneshot_service(nargs, args);
-    if (svc == NULL) {
+static int do_exec(const std::vector<std::string>& args) {
+    Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args);
+    if (!svc) {
         return -1;
     }
-    service_start(svc, NULL);
+    if (!svc->Start()) {
+        return -1;
+    }
+    waiting_for_exec = true;
     return 0;
 }
 
-int do_export(int nargs, char **args)
-{
-    return add_environment(args[1], args[2]);
+static int do_export(const std::vector<std::string>& args) {
+    return add_environment(args[1].c_str(), args[2].c_str());
 }
 
-int do_hostname(int nargs, char **args)
-{
-    return write_file("/proc/sys/kernel/hostname", args[1]);
+static int do_hostname(const std::vector<std::string>& args) {
+    return write_file("/proc/sys/kernel/hostname", args[1].c_str());
 }
 
-int do_ifup(int nargs, char **args)
-{
-    return __ifupdown(args[1], 1);
+static int do_ifup(const std::vector<std::string>& args) {
+    return __ifupdown(args[1].c_str(), 1);
 }
 
+static int do_insmod(const std::vector<std::string>& args) {
+    std::string options;
 
-static int do_insmod_inner(int nargs, char **args, int opt_len)
-{
-    char options[opt_len + 1];
-    int i;
-
-    options[0] = '\0';
-    if (nargs > 2) {
-        strcpy(options, args[2]);
-        for (i = 3; i < nargs; ++i) {
-            strcat(options, " ");
-            strcat(options, args[i]);
+    if (args.size() > 2) {
+        options += args[2];
+        for (std::size_t i = 3; i < args.size(); ++i) {
+            options += ' ';
+            options += args[i];
         }
     }
 
-    return insmod(args[1], options);
-}
-
-int do_insmod(int nargs, char **args)
-{
-    int i;
-    int size = 0;
-
-    if (nargs > 2) {
-        for (i = 2; i < nargs; ++i)
-            size += strlen(args[i]) + 1;
-    }
-
-    return do_insmod_inner(nargs, args, size);
+    return insmod(args[1].c_str(), options.c_str());
 }
 
-int do_mkdir(int nargs, char **args)
-{
+static int do_mkdir(const std::vector<std::string>& args) {
     mode_t mode = 0755;
     int ret;
 
     /* mkdir <path> [mode] [owner] [group] */
 
-    if (nargs >= 3) {
-        mode = strtoul(args[2], 0, 8);
+    if (args.size() >= 3) {
+        mode = std::stoul(args[2], 0, 8);
     }
 
-    ret = make_dir(args[1], mode);
+    ret = make_dir(args[1].c_str(), mode);
     /* chmod in case the directory already exists */
     if (ret == -1 && errno == EEXIST) {
-        ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
+        ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
     }
     if (ret == -1) {
         return -errno;
     }
 
-    if (nargs >= 4) {
-        uid_t uid = decode_uid(args[3]);
+    if (args.size() >= 4) {
+        uid_t uid = decode_uid(args[3].c_str());
         gid_t gid = -1;
 
-        if (nargs == 5) {
-            gid = decode_uid(args[4]);
+        if (args.size() == 5) {
+            gid = decode_uid(args[4].c_str());
         }
 
-        if (lchown(args[1], uid, gid) == -1) {
+        if (lchown(args[1].c_str(), uid, gid) == -1) {
             return -errno;
         }
 
         /* chown may have cleared S_ISUID and S_ISGID, chmod again */
         if (mode & (S_ISUID | S_ISGID)) {
-            ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
+            ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
             if (ret == -1) {
                 return -errno;
             }
         }
     }
 
-    return e4crypt_set_directory_policy(args[1]);
+    return e4crypt_set_directory_policy(args[1].c_str());
 }
 
 static struct {
@@ -275,35 +299,35 @@ static struct {
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-int do_mount(int nargs, char **args)
-{
+static int do_mount(const std::vector<std::string>& args) {
     char tmp[64];
-    char *source, *target, *system;
-    char *options = NULL;
+    const char *source, *target, *system;
+    const char *options = NULL;
     unsigned flags = 0;
+    std::size_t na = 0;
     int n, i;
     int wait = 0;
 
-    for (n = 4; n < nargs; n++) {
+    for (na = 4; na < args.size(); na++) {
         for (i = 0; mount_flags[i].name; i++) {
-            if (!strcmp(args[n], mount_flags[i].name)) {
+            if (!args[na].compare(mount_flags[i].name)) {
                 flags |= mount_flags[i].flag;
                 break;
             }
         }
 
         if (!mount_flags[i].name) {
-            if (!strcmp(args[n], "wait"))
+            if (!args[na].compare("wait"))
                 wait = 1;
             /* if our last argument isn't a flag, wolf it up as an option string */
-            else if (n + 1 == nargs)
-                options = args[n];
+            else if (na + 1 == args.size())
+                options = args[na].c_str();
         }
     }
 
-    system = args[1];
-    source = args[2];
-    target = args[3];
+    system = args[1].c_str();
+    source = args[2].c_str();
+    target = args[3].c_str();
 
     if (!strncmp(source, "mtd@", 4)) {
         n = mtd_name_to_number(source + 4);
@@ -375,8 +399,7 @@ exit_success:
 
 }
 
-static int wipe_data_via_recovery()
-{
+static int wipe_data_via_recovery() {
     mkdir("/cache/recovery", 0700);
     int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);
     if (fd >= 0) {
@@ -391,22 +414,31 @@ static int wipe_data_via_recovery()
     while (1) { pause(); }  // never reached
 }
 
+static void import_late() {
+    static const std::vector<std::string> init_directories = {
+        "/system/etc/init",
+        "/vendor/etc/init",
+        "/odm/etc/init"
+    };
+
+    Parser& parser = Parser::GetInstance();
+    for (const auto& dir : init_directories) {
+        parser.ParseConfig(dir.c_str());
+    }
+}
+
 /*
  * This function might request a reboot, in which case it will
  * not return.
  */
-int do_mount_all(int nargs, char **args)
-{
+static int do_mount_all(const std::vector<std::string>& args) {
     pid_t pid;
     int ret = -1;
     int child_ret = -1;
     int status;
     struct fstab *fstab;
 
-    if (nargs != 2) {
-        return -1;
-    }
-
+    const char* fstabfile = args[1].c_str();
     /*
      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
      * do the call in the child to provide protection to the main init
@@ -430,7 +462,7 @@ int do_mount_all(int nargs, char **args)
     } else if (pid == 0) {
         /* child, call fs_mgr_mount_all() */
         klog_set_level(6);  /* So we can see what fs_mgr_mount_all() does */
-        fstab = fs_mgr_read_fstab(args[1]);
+        fstab = fs_mgr_read_fstab(fstabfile);
         child_ret = fs_mgr_mount_all(fstab);
         fs_mgr_free_fstab(fstab);
         if (child_ret == -1) {
@@ -442,6 +474,8 @@ int do_mount_all(int nargs, char **args)
         return -1;
     }
 
+    import_late();
+
     if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
         property_set("vold.decrypt", "trigger_encryption");
     } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
@@ -453,7 +487,7 @@ int do_mount_all(int nargs, char **args)
         /* If fs_mgr determined this is an unencrypted device, then trigger
          * that action.
          */
-        action_for_each_trigger("nonencrypted", action_add_queue_tail);
+        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
     } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
         ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
@@ -468,7 +502,7 @@ int do_mount_all(int nargs, char **args)
 
         // Although encrypted, we have device key, so we do not need to
         // do anything different from the nonencrypted case.
-        action_for_each_trigger("nonencrypted", action_add_queue_tail);
+        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
     } else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) {
         if (e4crypt_install_keyring()) {
             return -1;
@@ -484,87 +518,70 @@ int do_mount_all(int nargs, char **args)
     return ret;
 }
 
-int do_swapon_all(int nargs, char **args)
-{
+static int do_swapon_all(const std::vector<std::string>& args) {
     struct fstab *fstab;
     int ret;
 
-    fstab = fs_mgr_read_fstab(args[1]);
+    fstab = fs_mgr_read_fstab(args[1].c_str());
     ret = fs_mgr_swapon_all(fstab);
     fs_mgr_free_fstab(fstab);
 
     return ret;
 }
 
-int do_setprop(int nargs, char **args)
-{
-    const char *name = args[1];
-    const char *value = args[2];
-    char prop_val[PROP_VALUE_MAX];
-    int ret;
-
-    ret = expand_props(prop_val, value, sizeof(prop_val));
-    if (ret) {
-        ERROR("cannot expand '%s' while assigning to '%s'\n", value, name);
-        return -EINVAL;
-    }
-    property_set(name, prop_val);
+static int do_setprop(const std::vector<std::string>& args) {
+    const char* name = args[1].c_str();
+    const char* value = args[2].c_str();
+    property_set(name, value);
     return 0;
 }
 
-int do_setrlimit(int nargs, char **args)
-{
+static int do_setrlimit(const std::vector<std::string>& args) {
     struct rlimit limit;
     int resource;
-    resource = atoi(args[1]);
-    limit.rlim_cur = atoi(args[2]);
-    limit.rlim_max = atoi(args[3]);
+    resource = std::stoi(args[1]);
+    limit.rlim_cur = std::stoi(args[2]);
+    limit.rlim_max = std::stoi(args[3]);
     return setrlimit(resource, &limit);
 }
 
-int do_start(int nargs, char **args)
-{
-    struct service *svc;
-    svc = service_find_by_name(args[1]);
-    if (svc) {
-        service_start(svc, NULL);
+static int do_start(const std::vector<std::string>& args) {
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+    if (!svc) {
+        ERROR("do_start: Service %s not found\n", args[1].c_str());
+        return -1;
     }
+    if (!svc->Start())
+        return -1;
     return 0;
 }
 
-int do_stop(int nargs, char **args)
-{
-    struct service *svc;
-    svc = service_find_by_name(args[1]);
-    if (svc) {
-        service_stop(svc);
+static int do_stop(const std::vector<std::string>& args) {
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+    if (!svc) {
+        ERROR("do_stop: Service %s not found\n", args[1].c_str());
+        return -1;
     }
+    svc->Stop();
     return 0;
 }
 
-int do_restart(int nargs, char **args)
-{
-    struct service *svc;
-    svc = service_find_by_name(args[1]);
-    if (svc) {
-        service_restart(svc);
+static int do_restart(const std::vector<std::string>& args) {
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
+    if (!svc) {
+        ERROR("do_restart: Service %s not found\n", args[1].c_str());
+        return -1;
     }
+    svc->Restart();
     return 0;
 }
 
-int do_powerctl(int nargs, char **args)
-{
-    char command[PROP_VALUE_MAX];
-    int res;
+static int do_powerctl(const std::vector<std::string>& args) {
+    const char* command = args[1].c_str();
     int len = 0;
-    int cmd = 0;
-    const char *reboot_target;
-
-    res = expand_props(command, args[1], sizeof(command));
-    if (res) {
-        ERROR("powerctl: cannot expand '%s'\n", args[1]);
-        return -EINVAL;
-    }
+    unsigned int cmd = 0;
+    const char *reboot_target = "";
+    void (*callback_on_ro_remount)(const struct mntent*) = NULL;
 
     if (strncmp(command, "shutdown", 8) == 0) {
         cmd = ANDROID_RB_POWEROFF;
@@ -578,85 +595,76 @@ int do_powerctl(int nargs, char **args)
     }
 
     if (command[len] == ',') {
-        reboot_target = &command[len + 1];
-    } else if (command[len] == '\0') {
-        reboot_target = "";
-    } else {
+        if (cmd == ANDROID_RB_POWEROFF &&
+            !strcmp(&command[len + 1], "userrequested")) {
+            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
+            // Run fsck once the file system is remounted in read-only mode.
+            callback_on_ro_remount = unmount_and_fsck;
+        } else if (cmd == ANDROID_RB_RESTART2) {
+            reboot_target = &command[len + 1];
+        }
+    } else if (command[len] != '\0') {
         ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]);
         return -EINVAL;
     }
 
-    return android_reboot(cmd, 0, reboot_target);
+    return android_reboot_with_callback(cmd, 0, reboot_target,
+                                        callback_on_ro_remount);
 }
 
-int do_trigger(int nargs, char **args)
-{
-    action_for_each_trigger(args[1], action_add_queue_tail);
+static int do_trigger(const std::vector<std::string>& args) {
+    ActionManager::GetInstance().QueueEventTrigger(args[1]);
     return 0;
 }
 
-int do_symlink(int nargs, char **args)
-{
-    return symlink(args[1], args[2]);
+static int do_symlink(const std::vector<std::string>& args) {
+    return symlink(args[1].c_str(), args[2].c_str());
 }
 
-int do_rm(int nargs, char **args)
-{
-    return unlink(args[1]);
+static int do_rm(const std::vector<std::string>& args) {
+    return unlink(args[1].c_str());
 }
 
-int do_rmdir(int nargs, char **args)
-{
-    return rmdir(args[1]);
+static int do_rmdir(const std::vector<std::string>& args) {
+    return rmdir(args[1].c_str());
 }
 
-int do_sysclktz(int nargs, char **args)
-{
+static int do_sysclktz(const std::vector<std::string>& args) {
     struct timezone tz;
 
-    if (nargs != 2)
-        return -1;
-
     memset(&tz, 0, sizeof(tz));
-    tz.tz_minuteswest = atoi(args[1]);
+    tz.tz_minuteswest = std::stoi(args[1]);
     if (settimeofday(NULL, &tz))
         return -1;
     return 0;
 }
 
-int do_verity_load_state(int nargs, char **args) {
+static int do_verity_load_state(const std::vector<std::string>& args) {
     int mode = -1;
     int rc = fs_mgr_load_verity_state(&mode);
     if (rc == 0 && mode == VERITY_MODE_LOGGING) {
-        action_for_each_trigger("verity-logging", action_add_queue_tail);
+        ActionManager::GetInstance().QueueEventTrigger("verity-logging");
     }
     return rc;
 }
 
-static void verity_update_property(fstab_rec *fstab, const char *mount_point, int mode, int status) {
+static void verity_update_property(fstab_rec *fstab, const char *mount_point,
+                                   int mode, int status) {
     property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
                  android::base::StringPrintf("%d", mode).c_str());
 }
 
-int do_verity_update_state(int nargs, char** args) {
+static int do_verity_update_state(const std::vector<std::string>& args) {
     return fs_mgr_update_verity_state(verity_update_property);
 }
 
-int do_write(int nargs, char **args)
-{
-    const char *path = args[1];
-    const char *value = args[2];
-
-    char expanded_value[256];
-    if (expand_props(expanded_value, value, sizeof(expanded_value))) {
-        ERROR("cannot expand '%s' while writing to '%s'\n", value, path);
-        return -EINVAL;
-    }
-    return write_file(path, expanded_value);
+static int do_write(const std::vector<std::string>& args) {
+    const char* path = args[1].c_str();
+    const char* value = args[2].c_str();
+    return write_file(path, value);
 }
 
-int do_copy(int nargs, char **args)
-{
+static int do_copy(const std::vector<std::string>& args) {
     char *buffer = NULL;
     int rc = 0;
     int fd1 = -1, fd2 = -1;
@@ -664,16 +672,13 @@ int do_copy(int nargs, char **args)
     int brtw, brtr;
     char *p;
 
-    if (nargs != 3)
-        return -1;
-
-    if (stat(args[1], &info) < 0)
+    if (stat(args[1].c_str(), &info) < 0)
         return -1;
 
-    if ((fd1 = open(args[1], O_RDONLY|O_CLOEXEC)) < 0)
+    if ((fd1 = open(args[1].c_str(), O_RDONLY|O_CLOEXEC)) < 0)
         goto out_err;
 
-    if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
+    if ((fd2 = open(args[2].c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
         goto out_err;
 
     if (!(buffer = (char*) malloc(info.st_size)))
@@ -717,13 +722,14 @@ out:
     return rc;
 }
 
-int do_chown(int nargs, char **args) {
+static int do_chown(const std::vector<std::string>& args) {
     /* GID is optional. */
-    if (nargs == 3) {
-        if (lchown(args[2], decode_uid(args[1]), -1) == -1)
+    if (args.size() == 3) {
+        if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
             return -errno;
-    } else if (nargs == 4) {
-        if (lchown(args[3], decode_uid(args[1]), decode_uid(args[2])) == -1)
+    } else if (args.size() == 4) {
+        if (lchown(args[3].c_str(), decode_uid(args[1].c_str()),
+                   decode_uid(args[2].c_str())) == -1)
             return -errno;
     } else {
         return -1;
@@ -744,49 +750,36 @@ static mode_t get_mode(const char *s) {
     return mode;
 }
 
-int do_chmod(int nargs, char **args) {
-    mode_t mode = get_mode(args[1]);
-    if (fchmodat(AT_FDCWD, args[2], mode, AT_SYMLINK_NOFOLLOW) < 0) {
+static int do_chmod(const std::vector<std::string>& args) {
+    mode_t mode = get_mode(args[1].c_str());
+    if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
         return -errno;
     }
     return 0;
 }
 
-int do_restorecon(int nargs, char **args) {
-    int i;
+static int do_restorecon(const std::vector<std::string>& args) {
     int ret = 0;
 
-    for (i = 1; i < nargs; i++) {
-        if (restorecon(args[i]) < 0)
+    for (auto it = std::next(args.begin()); it != args.end(); ++it) {
+        if (restorecon(it->c_str()) < 0)
             ret = -errno;
     }
     return ret;
 }
 
-int do_restorecon_recursive(int nargs, char **args) {
-    int i;
+static int do_restorecon_recursive(const std::vector<std::string>& args) {
     int ret = 0;
 
-    for (i = 1; i < nargs; i++) {
-        if (restorecon_recursive(args[i]) < 0)
+    for (auto it = std::next(args.begin()); it != args.end(); ++it) {
+        if (restorecon_recursive(it->c_str()) < 0)
             ret = -errno;
     }
     return ret;
 }
 
-int do_loglevel(int nargs, char **args) {
-    int log_level;
-    char log_level_str[PROP_VALUE_MAX] = "";
-    if (nargs != 2) {
-        ERROR("loglevel: missing argument\n");
-        return -EINVAL;
-    }
-
-    if (expand_props(log_level_str, args[1], sizeof(log_level_str))) {
-        ERROR("loglevel: cannot expand '%s'\n", args[1]);
-        return -EINVAL;
-    }
-    log_level = atoi(log_level_str);
+static int do_loglevel(const std::vector<std::string>& args) {
+    int log_level = std::stoi(args[1]);
     if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
         ERROR("loglevel: invalid log level'%d'\n", log_level);
         return -EINVAL;
@@ -795,28 +788,21 @@ int do_loglevel(int nargs, char **args) {
     return 0;
 }
 
-int do_load_persist_props(int nargs, char **args) {
-    if (nargs == 1) {
-        load_persist_props();
-        return 0;
-    }
-    return -1;
+static int do_load_persist_props(const std::vector<std::string>& args) {
+    load_persist_props();
+    return 0;
 }
 
-int do_load_system_props(int nargs, char **args) {
-    if (nargs == 1) {
-        load_system_props();
-        return 0;
-    }
-    return -1;
+static int do_load_system_props(const std::vector<std::string>& args) {
+    load_system_props();
+    return 0;
 }
 
-int do_wait(int nargs, char **args)
-{
-    if (nargs == 2) {
-        return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
-    } else if (nargs == 3) {
-        return wait_for_file(args[1], atoi(args[2]));
+static int do_wait(const std::vector<std::string>& args) {
+    if (args.size() == 2) {
+        return wait_for_file(args[1].c_str(), COMMAND_RETRY_TIMEOUT);
+    } else if (args.size() == 3) {
+        return wait_for_file(args[1].c_str(), std::stoi(args[2]));
     } else
         return -1;
 }
@@ -824,8 +810,7 @@ int do_wait(int nargs, char **args)
 /*
  * Callback to make a directory from the ext4 code
  */
-static int do_installkeys_ensure_dir_exists(const char* dir)
-{
+static int do_installkeys_ensure_dir_exists(const char* dir) {
     if (make_dir(dir, 0700) && errno != EEXIST) {
         return -1;
     }
@@ -834,30 +819,68 @@ static int do_installkeys_ensure_dir_exists(const char* dir)
 }
 
 static bool is_file_crypto() {
-    char prop_value[PROP_VALUE_MAX] = {0};
-    property_get("ro.crypto.type", prop_value);
-    return strcmp(prop_value, "file") == 0;
+    std::string value = property_get("ro.crypto.type");
+    return value == "file";
 }
 
-int do_installkey(int nargs, char **args)
-{
-    if (nargs != 2) {
-        return -1;
-    }
+static int do_installkey(const std::vector<std::string>& args) {
     if (!is_file_crypto()) {
         return 0;
     }
-    return e4crypt_create_device_key(args[1],
+    return e4crypt_create_device_key(args[1].c_str(),
                                      do_installkeys_ensure_dir_exists);
 }
 
-int do_setusercryptopolicies(int nargs, char **args)
-{
-    if (nargs != 2) {
-        return -1;
-    }
+static int do_setusercryptopolicies(const std::vector<std::string>& args) {
     if (!is_file_crypto()) {
         return 0;
     }
-    return e4crypt_set_user_crypto_policies(args[1]);
+    return e4crypt_set_user_crypto_policies(args[1].c_str());
+}
+
+BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+    static const Map builtin_functions = {
+        {"bootchart_init",          {0,     0,    do_bootchart_init}},
+        {"chmod",                   {2,     2,    do_chmod}},
+        {"chown",                   {2,     3,    do_chown}},
+        {"class_reset",             {1,     1,    do_class_reset}},
+        {"class_start",             {1,     1,    do_class_start}},
+        {"class_stop",              {1,     1,    do_class_stop}},
+        {"copy",                    {2,     2,    do_copy}},
+        {"domainname",              {1,     1,    do_domainname}},
+        {"enable",                  {1,     1,    do_enable}},
+        {"exec",                    {1,     kMax, do_exec}},
+        {"export",                  {2,     2,    do_export}},
+        {"hostname",                {1,     1,    do_hostname}},
+        {"ifup",                    {1,     1,    do_ifup}},
+        {"insmod",                  {1,     kMax, do_insmod}},
+        {"installkey",              {1,     1,    do_installkey}},
+        {"load_persist_props",      {0,     0,    do_load_persist_props}},
+        {"load_system_props",       {0,     0,    do_load_system_props}},
+        {"loglevel",                {1,     1,    do_loglevel}},
+        {"mkdir",                   {1,     4,    do_mkdir}},
+        {"mount_all",               {1,     1,    do_mount_all}},
+        {"mount",                   {3,     kMax, do_mount}},
+        {"powerctl",                {1,     1,    do_powerctl}},
+        {"restart",                 {1,     1,    do_restart}},
+        {"restorecon",              {1,     kMax, do_restorecon}},
+        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
+        {"rm",                      {1,     1,    do_rm}},
+        {"rmdir",                   {1,     1,    do_rmdir}},
+        {"setprop",                 {2,     2,    do_setprop}},
+        {"setrlimit",               {3,     3,    do_setrlimit}},
+        {"setusercryptopolicies",   {1,     1,    do_setusercryptopolicies}},
+        {"start",                   {1,     1,    do_start}},
+        {"stop",                    {1,     1,    do_stop}},
+        {"swapon_all",              {1,     1,    do_swapon_all}},
+        {"symlink",                 {2,     2,    do_symlink}},
+        {"sysclktz",                {1,     1,    do_sysclktz}},
+        {"trigger",                 {1,     1,    do_trigger}},
+        {"verity_load_state",       {0,     0,    do_verity_load_state}},
+        {"verity_update_state",     {0,     0,    do_verity_update_state}},
+        {"wait",                    {1,     2,    do_wait}},
+        {"write",                   {2,     2,    do_write}},
+    };
+    return builtin_functions;
 }
similarity index 58%
rename from adb/get_my_path_windows.cpp
rename to init/builtins.h
index 9d23e1c..53f4a71 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * limitations under the License.
  */
 
-#include <assert.h>
-#include <limits.h>
-#include <windows.h>
+#ifndef _INIT_BUILTINS_H
+#define _INIT_BUILTINS_H
 
-#include "adb.h"
+#include <map>
+#include <string>
+#include <vector>
 
-void get_my_path(char *exe, size_t maxLen)
-{
-    char  *r;
+#include "keyword_map.h"
 
-    /* XXX: should be GetModuleFileNameA */
-    if (GetModuleFileName(NULL, exe, maxLen) > 0) {
-        r = strrchr(exe, '\\');
-        if (r != NULL)
-            *r = '\0';
-    } else {
-        exe[0] = '\0';
+using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
+public:
+    BuiltinFunctionMap() {
     }
-}
+private:
+    Map& map() const override;
+};
 
+#endif
diff --git a/init/compare-bootcharts.py b/init/compare-bootcharts.py
new file mode 100755 (executable)
index 0000000..2057b55
--- /dev/null
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Compare two bootcharts and list start/end timestamps on key processes.
+
+This script extracts two bootchart.tgz files and compares the timestamps
+in proc_ps.log for selected processes. The proc_ps.log file consists of
+repetitive blocks of the following format:
+
+timestamp1 (jiffies)
+dumps of /proc/<pid>/stat
+
+timestamp2
+dumps of /proc/<pid>/stat
+
+The timestamps are 200ms apart, and the creation time of selected processes
+are listed. The termination time of the boot animation process is also listed
+as a coarse indication about when the boot process is complete as perceived by
+the user.
+"""
+
+import sys
+import tarfile
+
+# The bootchart timestamps are 200ms apart, but the USER_HZ value is not
+# reported in the bootchart, so we use the first two timestamps to calculate
+# the wall clock time of a jiffy.
+jiffy_to_wallclock = {
+   '1st_timestamp': -1,
+   '2nd_timestamp': -1,
+   'jiffy_to_wallclock': -1
+}
+
+def analyze_process_maps(process_map1, process_map2, jiffy_record):
+    # List interesting processes here
+    processes_of_interest = [
+        '/init',
+        '/system/bin/surfaceflinger',
+        '/system/bin/bootanimation',
+        'zygote64',
+        'zygote',
+        'system_server'
+    ]
+
+    jw = jiffy_record['jiffy_to_wallclock']
+    print "process: baseline experiment (delta)"
+    print " - Unit is ms (a jiffy is %d ms on the system)" % jw
+    print "------------------------------------"
+    for p in processes_of_interest:
+        # e.g., 32-bit system doesn't have zygote64
+        if p in process_map1 and p in process_map2:
+            print "%s: %d %d (%+d)" % (
+                p, process_map1[p]['start_time'] * jw,
+                process_map2[p]['start_time'] * jw,
+                (process_map2[p]['start_time'] -
+                 process_map1[p]['start_time']) * jw)
+
+    # Print the last tick for the bootanimation process
+    print "bootanimation ends at: %d %d (%+d)" % (
+        process_map1['/system/bin/bootanimation']['last_tick'] * jw,
+        process_map2['/system/bin/bootanimation']['last_tick'] * jw,
+        (process_map2['/system/bin/bootanimation']['last_tick'] -
+            process_map1['/system/bin/bootanimation']['last_tick']) * jw)
+
+def parse_proc_file(pathname, process_map, jiffy_record=None):
+    # Uncompress bootchart.tgz
+    with tarfile.open(pathname + '/bootchart.tgz', 'r:*') as tf:
+        try:
+            # Read proc_ps.log
+            f = tf.extractfile('proc_ps.log')
+
+            # Break proc_ps into chunks based on timestamps
+            blocks = f.read().split('\n\n')
+            for b in blocks:
+                lines = b.split('\n')
+                if not lines[0]:
+                    break
+
+                # 200ms apart in jiffies
+                timestamp = int(lines[0]);
+
+                # Figure out the wall clock time of a jiffy
+                if jiffy_record is not None:
+                    if jiffy_record['1st_timestamp'] == -1:
+                        jiffy_record['1st_timestamp'] = timestamp
+                    elif jiffy_record['jiffy_to_wallclock'] == -1:
+                        # Not really needed but for debugging purposes
+                        jiffy_record['2nd_timestamp'] = timestamp
+                        value = 200 / (timestamp -
+                                       jiffy_record['1st_timestamp'])
+                        # Fix the rounding error
+                        # e.g., 201 jiffies in 200ms when USER_HZ is 1000
+                        if value == 0:
+                            value = 1
+                        # e.g., 21 jiffies in 200ms when USER_HZ is 100
+                        elif value == 9:
+                            value = 10
+                        jiffy_record['jiffy_to_wallclock'] = value
+
+                # Populate the process_map table
+                for line in lines[1:]:
+                    segs = line.split(' ')
+
+                    #  0: pid
+                    #  1: process name
+                    # 17: priority
+                    # 18: nice
+                    # 21: creation time
+
+                    proc_name = segs[1].strip('()')
+                    if proc_name in process_map:
+                        process = process_map[proc_name]
+                    else:
+                        process = {'start_time': int(segs[21])}
+                        process_map[proc_name] = process
+
+                    process['last_tick'] = timestamp
+        finally:
+            f.close()
+
+def main():
+    if len(sys.argv) != 3:
+        print "Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0]
+        sys.exit(1)
+
+    process_map1 = {}
+    process_map2 = {}
+    parse_proc_file(sys.argv[1], process_map1, jiffy_to_wallclock)
+    parse_proc_file(sys.argv[2], process_map2)
+    analyze_process_maps(process_map1, process_map2, jiffy_to_wallclock)
+
+if __name__ == "__main__":
+    main()
index 4944cec..d556e30 100644 (file)
@@ -241,10 +241,8 @@ static void make_device(const char *path,
 
     mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
 
-    if (sehandle) {
-        selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
-        setfscreatecon(secontext);
-    }
+    selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
+    setfscreatecon(secontext);
 
     dev = makedev(major, minor);
     /* Temporarily change egid to avoid race condition setting the gid of the
@@ -907,7 +905,7 @@ void handle_device_fd()
         struct uevent uevent;
         parse_event(msg, &uevent);
 
-        if (sehandle && selinux_status_updated() > 0) {
+        if (selinux_status_updated() > 0) {
             struct selabel_handle *sehandle2;
             sehandle2 = selinux_android_file_context_handle();
             if (sehandle2) {
@@ -974,11 +972,8 @@ static void coldboot(const char *path)
 }
 
 void device_init() {
-    sehandle = NULL;
-    if (is_selinux_enabled() > 0) {
-        sehandle = selinux_android_file_context_handle();
-        selinux_status_open(true);
-    }
+    sehandle = selinux_android_file_context_handle();
+    selinux_status_open(true);
 
     /* is 256K enough? udev uses 16MB! */
     device_fd = uevent_open_socket(256*1024, true);
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
new file mode 100644 (file)
index 0000000..e2a0f83
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "import_parser.h"
+
+#include "errno.h"
+
+#include <string>
+#include <vector>
+
+#include "log.h"
+#include "util.h"
+
+bool ImportParser::ParseSection(const std::vector<std::string>& args,
+                                std::string* err) {
+    if (args.size() != 2) {
+        *err = "single argument needed for import\n";
+        return false;
+    }
+
+    std::string conf_file;
+    bool ret = expand_props(args[1], &conf_file);
+    if (!ret) {
+        *err = "error while expanding import";
+        return false;
+    }
+
+    INFO("Added '%s' to import list\n", conf_file.c_str());
+    imports_.emplace_back(std::move(conf_file));
+    return true;
+}
+
+void ImportParser::EndFile(const std::string& filename) {
+    auto current_imports = std::move(imports_);
+    imports_.clear();
+    for (const auto& s : current_imports) {
+        if (!Parser::GetInstance().ParseConfig(s)) {
+            ERROR("could not import file '%s' from '%s': %s\n",
+                  s.c_str(), filename.c_str(), strerror(errno));
+        }
+    }
+}
diff --git a/init/import_parser.h b/init/import_parser.h
new file mode 100644 (file)
index 0000000..0e91025
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_IMPORT_PARSER_H
+#define _INIT_IMPORT_PARSER_H
+
+#include "init_parser.h"
+
+#include <string>
+#include <vector>
+
+class ImportParser : public SectionParser {
+public:
+    ImportParser()  {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override {
+        return true;
+    }
+    void EndSection() override {
+    }
+    void EndFile(const std::string& filename) override;
+private:
+    std::vector<std::string> imports_;
+};
+
+#endif
index 93fe944..86aed9a 100644 (file)
@@ -32,7 +32,6 @@
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
-#include <termios.h>
 #include <unistd.h>
 
 #include <mtd/mtd-user.h>
@@ -43,6 +42,7 @@
 
 #include <base/file.h>
 #include <base/stringprintf.h>
+#include <base/strings.h>
 #include <cutils/android_reboot.h>
 #include <cutils/fs.h>
 #include <cutils/iosched_policy.h>
 
 #include <memory>
 
+#include "action.h"
+#include "bootchart.h"
 #include "devices.h"
+#include "import_parser.h"
 #include "init.h"
+#include "init_parser.h"
+#include "keychords.h"
 #include "log.h"
 #include "property_service.h"
-#include "bootchart.h"
+#include "service.h"
 #include "signal_handler.h"
-#include "keychords.h"
-#include "init_parser.h"
-#include "util.h"
 #include "ueventd.h"
+#include "util.h"
 #include "watchdogd.h"
 
 struct selabel_handle *sehandle;
@@ -71,14 +74,11 @@ static int property_triggers_enabled = 0;
 
 static char qemu[32];
 
-static struct action *cur_action = NULL;
-static struct command *cur_command = NULL;
-
-static int have_console;
-static char console_name[PROP_VALUE_MAX] = "/dev/console";
+int have_console;
+std::string console_name = "/dev/console";
 static time_t process_needs_restart;
 
-static const char *ENV[32];
+const char *ENV[32];
 
 bool waiting_for_exec = false;
 
@@ -93,27 +93,6 @@ void register_epoll_handler(int fd, void (*fn)()) {
     }
 }
 
-void service::NotifyStateChange(const char* new_state) {
-    if (!properties_initialized()) {
-        // If properties aren't available yet, we can't set them.
-        return;
-    }
-
-    if ((flags & SVC_EXEC) != 0) {
-        // 'exec' commands don't have properties tracking their state.
-        return;
-    }
-
-    char prop_name[PROP_NAME_MAX];
-    if (snprintf(prop_name, sizeof(prop_name), "init.svc.%s", name) >= PROP_NAME_MAX) {
-        // If the property name would be too long, we can't set it.
-        ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n", name, new_state);
-        return;
-    }
-
-    property_set(prop_name, new_state);
-}
-
 /* add_environment - add "key=value" to the current environment */
 int add_environment(const char *key, const char *val)
 {
@@ -146,486 +125,80 @@ int add_environment(const char *key, const char *val)
     return -1;
 }
 
-void zap_stdio(void)
-{
-    int fd;
-    fd = open("/dev/null", O_RDWR);
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    close(fd);
-}
-
-static void open_console()
-{
-    int fd;
-    if ((fd = open(console_name, O_RDWR)) < 0) {
-        fd = open("/dev/null", O_RDWR);
-    }
-    ioctl(fd, TIOCSCTTY, 0);
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    close(fd);
-}
-
-static void publish_socket(const char *name, int fd)
-{
-    char key[64] = ANDROID_SOCKET_ENV_PREFIX;
-    char val[64];
-
-    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
-            name,
-            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
-    snprintf(val, sizeof(val), "%d", fd);
-    add_environment(key, val);
-
-    /* make sure we don't close-on-exec */
-    fcntl(fd, F_SETFD, 0);
-}
-
-void service_start(struct service *svc, const char *dynamic_args)
-{
-    // Starting a service removes it from the disabled or reset state and
-    // immediately takes it out of the restarting state if it was in there.
-    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
-    svc->time_started = 0;
-
-    // Running processes require no additional work --- if they're in the
-    // process of exiting, we've ensured that they will immediately restart
-    // on exit, unless they are ONESHOT.
-    if (svc->flags & SVC_RUNNING) {
-        return;
-    }
-
-    bool needs_console = (svc->flags & SVC_CONSOLE);
-    if (needs_console && !have_console) {
-        ERROR("service '%s' requires console\n", svc->name);
-        svc->flags |= SVC_DISABLED;
-        return;
-    }
-
-    struct stat s;
-    if (stat(svc->args[0], &s) != 0) {
-        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
-        svc->flags |= SVC_DISABLED;
-        return;
-    }
-
-    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
-        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
-               svc->args[0]);
-        svc->flags |= SVC_DISABLED;
-        return;
-    }
-
-    char* scon = NULL;
-    if (is_selinux_enabled() > 0) {
-        if (svc->seclabel) {
-            scon = strdup(svc->seclabel);
-            if (!scon) {
-                ERROR("Out of memory while starting '%s'\n", svc->name);
-                return;
-            }
-        } else {
-            char *mycon = NULL, *fcon = NULL;
-
-            INFO("computing context for service '%s'\n", svc->args[0]);
-            int rc = getcon(&mycon);
-            if (rc < 0) {
-                ERROR("could not get context while starting '%s'\n", svc->name);
-                return;
-            }
-
-            rc = getfilecon(svc->args[0], &fcon);
-            if (rc < 0) {
-                ERROR("could not get context while starting '%s'\n", svc->name);
-                freecon(mycon);
-                return;
-            }
-
-            rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
-            if (rc == 0 && !strcmp(scon, mycon)) {
-                ERROR("Warning!  Service %s needs a SELinux domain defined; please fix!\n", svc->name);
-            }
-            freecon(mycon);
-            freecon(fcon);
-            if (rc < 0) {
-                ERROR("could not get context while starting '%s'\n", svc->name);
-                return;
-            }
-        }
-    }
-
-    NOTICE("Starting service '%s'...\n", svc->name);
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        struct socketinfo *si;
-        struct svcenvinfo *ei;
-        char tmp[32];
-        int fd, sz;
-
-        umask(077);
-        if (properties_initialized()) {
-            get_property_workspace(&fd, &sz);
-            snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);
-            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
-        }
-
-        for (ei = svc->envvars; ei; ei = ei->next)
-            add_environment(ei->name, ei->value);
-
-        for (si = svc->sockets; si; si = si->next) {
-            int socket_type = (
-                    !strcmp(si->type, "stream") ? SOCK_STREAM :
-                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
-            int s = create_socket(si->name, socket_type,
-                                  si->perm, si->uid, si->gid, si->socketcon ?: scon);
-            if (s >= 0) {
-                publish_socket(si->name, s);
-            }
-        }
-
-        freecon(scon);
-        scon = NULL;
-
-        if (svc->writepid_files_) {
-            std::string pid_str = android::base::StringPrintf("%d", pid);
-            for (auto& file : *svc->writepid_files_) {
-                if (!android::base::WriteStringToFile(pid_str, file)) {
-                    ERROR("couldn't write %s to %s: %s\n",
-                          pid_str.c_str(), file.c_str(), strerror(errno));
-                }
-            }
-        }
-
-        if (svc->ioprio_class != IoSchedClass_NONE) {
-            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
-                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
-                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
-            }
-        }
-
-        if (needs_console) {
-            setsid();
-            open_console();
-        } else {
-            zap_stdio();
-        }
-
-        if (false) {
-            for (size_t n = 0; svc->args[n]; n++) {
-                INFO("args[%zu] = '%s'\n", n, svc->args[n]);
-            }
-            for (size_t n = 0; ENV[n]; n++) {
-                INFO("env[%zu] = '%s'\n", n, ENV[n]);
-            }
-        }
-
-        setpgid(0, getpid());
-
-        // As requested, set our gid, supplemental gids, and uid.
-        if (svc->gid) {
-            if (setgid(svc->gid) != 0) {
-                ERROR("setgid failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (svc->nr_supp_gids) {
-            if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
-                ERROR("setgroups failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (svc->uid) {
-            if (setuid(svc->uid) != 0) {
-                ERROR("setuid failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (svc->seclabel) {
-            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
-                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
-                _exit(127);
-            }
-        }
-
-        if (!dynamic_args) {
-            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
-                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
-            }
-        } else {
-            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
-            int arg_idx = svc->nargs;
-            char *tmp = strdup(dynamic_args);
-            char *next = tmp;
-            char *bword;
-
-            /* Copy the static arguments */
-            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
-
-            while((bword = strsep(&next, " "))) {
-                arg_ptrs[arg_idx++] = bword;
-                if (arg_idx == INIT_PARSER_MAXARGS)
-                    break;
-            }
-            arg_ptrs[arg_idx] = NULL;
-            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
-        }
-        _exit(127);
-    }
-
-    freecon(scon);
-
-    if (pid < 0) {
-        ERROR("failed to start '%s'\n", svc->name);
-        svc->pid = 0;
-        return;
-    }
-
-    svc->time_started = gettime();
-    svc->pid = pid;
-    svc->flags |= SVC_RUNNING;
-
-    if ((svc->flags & SVC_EXEC) != 0) {
-        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
-             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
-             svc->seclabel ? : "default");
-        waiting_for_exec = true;
-    }
-
-    svc->NotifyStateChange("running");
-}
-
-/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
-static void service_stop_or_reset(struct service *svc, int how)
-{
-    /* The service is still SVC_RUNNING until its process exits, but if it has
-     * already exited it shoudn't attempt a restart yet. */
-    svc->flags &= ~(SVC_RESTARTING | SVC_DISABLED_START);
-
-    if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
-        /* Hrm, an illegal flag.  Default to SVC_DISABLED */
-        how = SVC_DISABLED;
-    }
-        /* if the service has not yet started, prevent
-         * it from auto-starting with its class
-         */
-    if (how == SVC_RESET) {
-        svc->flags |= (svc->flags & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
-    } else {
-        svc->flags |= how;
-    }
-
-    if (svc->pid) {
-        NOTICE("Service '%s' is being killed...\n", svc->name);
-        kill(-svc->pid, SIGKILL);
-        svc->NotifyStateChange("stopping");
-    } else {
-        svc->NotifyStateChange("stopped");
-    }
-}
-
-void service_reset(struct service *svc)
-{
-    service_stop_or_reset(svc, SVC_RESET);
-}
-
-void service_stop(struct service *svc)
-{
-    service_stop_or_reset(svc, SVC_DISABLED);
-}
-
-void service_restart(struct service *svc)
-{
-    if (svc->flags & SVC_RUNNING) {
-        /* Stop, wait, then start the service. */
-        service_stop_or_reset(svc, SVC_RESTART);
-    } else if (!(svc->flags & SVC_RESTARTING)) {
-        /* Just start the service since it's not running. */
-        service_start(svc, NULL);
-    } /* else: Service is restarting anyways. */
-}
-
 void property_changed(const char *name, const char *value)
 {
     if (property_triggers_enabled)
-        queue_property_triggers(name, value);
-}
-
-static void restart_service_if_needed(struct service *svc)
-{
-    time_t next_start_time = svc->time_started + 5;
-
-    if (next_start_time <= gettime()) {
-        svc->flags &= (~SVC_RESTARTING);
-        service_start(svc, NULL);
-        return;
-    }
-
-    if ((next_start_time < process_needs_restart) ||
-        (process_needs_restart == 0)) {
-        process_needs_restart = next_start_time;
-    }
+        ActionManager::GetInstance().QueuePropertyTrigger(name, value);
 }
 
 static void restart_processes()
 {
     process_needs_restart = 0;
-    service_for_each_flags(SVC_RESTARTING,
-                           restart_service_if_needed);
+    ServiceManager::GetInstance().
+        ForEachServiceWithFlags(SVC_RESTARTING, [] (Service* s) {
+                s->RestartIfNeeded(process_needs_restart);
+            });
 }
 
-static void msg_start(const char *name)
+static void msg_start(const std::string& name)
 {
-    struct service *svc = NULL;
-    char *tmp = NULL;
-    char *args = NULL;
-
-    if (!strchr(name, ':'))
-        svc = service_find_by_name(name);
-    else {
-        tmp = strdup(name);
-        if (tmp) {
-            args = strchr(tmp, ':');
-            *args = '\0';
-            args++;
-
-            svc = service_find_by_name(tmp);
-        }
+    Service* svc = nullptr;
+    std::vector<std::string> vargs;
+
+    size_t colon_pos = name.find(':');
+    if (colon_pos == std::string::npos) {
+        svc = ServiceManager::GetInstance().FindServiceByName(name);
+    } else {
+        std::string service_name(name.substr(0, colon_pos));
+        std::string args(name.substr(colon_pos + 1));
+        vargs = android::base::Split(args, " ");
+
+        svc = ServiceManager::GetInstance().FindServiceByName(service_name);
     }
 
     if (svc) {
-        service_start(svc, args);
+        svc->Start(vargs);
     } else {
-        ERROR("no such service '%s'\n", name);
+        ERROR("no such service '%s'\n", name.c_str());
     }
-    if (tmp)
-        free(tmp);
 }
 
-static void msg_stop(const char *name)
+static void msg_stop(const std::string& name)
 {
-    struct service *svc = service_find_by_name(name);
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
 
     if (svc) {
-        service_stop(svc);
+        svc->Stop();
     } else {
-        ERROR("no such service '%s'\n", name);
+        ERROR("no such service '%s'\n", name.c_str());
     }
 }
 
-static void msg_restart(const char *name)
+static void msg_restart(const std::string& name)
 {
-    struct service *svc = service_find_by_name(name);
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
 
     if (svc) {
-        service_restart(svc);
+        svc->Restart();
     } else {
-        ERROR("no such service '%s'\n", name);
+        ERROR("no such service '%s'\n", name.c_str());
     }
 }
 
-void handle_control_message(const char *msg, const char *arg)
+void handle_control_message(const std::string& msg, const std::string& arg)
 {
-    if (!strcmp(msg,"start")) {
+    if (msg == "start") {
         msg_start(arg);
-    } else if (!strcmp(msg,"stop")) {
+    } else if (msg == "stop") {
         msg_stop(arg);
-    } else if (!strcmp(msg,"restart")) {
+    } else if (msg == "restart") {
         msg_restart(arg);
     } else {
-        ERROR("unknown control msg '%s'\n", msg);
-    }
-}
-
-static struct command *get_first_command(struct action *act)
-{
-    struct listnode *node;
-    node = list_head(&act->commands);
-    if (!node || list_empty(&act->commands))
-        return NULL;
-
-    return node_to_item(node, struct command, clist);
-}
-
-static struct command *get_next_command(struct action *act, struct command *cmd)
-{
-    struct listnode *node;
-    node = cmd->clist.next;
-    if (!node)
-        return NULL;
-    if (node == &act->commands)
-        return NULL;
-
-    return node_to_item(node, struct command, clist);
-}
-
-static int is_last_command(struct action *act, struct command *cmd)
-{
-    return (list_tail(&act->commands) == &cmd->clist);
-}
-
-
-void build_triggers_string(char *name_str, int length, struct action *cur_action) {
-    struct listnode *node;
-    struct trigger *cur_trigger;
-
-    list_for_each(node, &cur_action->triggers) {
-        cur_trigger = node_to_item(node, struct trigger, nlist);
-        if (node != cur_action->triggers.next) {
-            strlcat(name_str, " " , length);
-        }
-        strlcat(name_str, cur_trigger->name , length);
+        ERROR("unknown control msg '%s'\n", msg.c_str());
     }
 }
 
-void execute_one_command() {
-    Timer t;
-
-    char cmd_str[256] = "";
-    char name_str[256] = "";
-
-    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
-        cur_action = action_remove_queue_head();
-        cur_command = NULL;
-        if (!cur_action) {
-            return;
-        }
-
-        build_triggers_string(name_str, sizeof(name_str), cur_action);
-
-        INFO("processing action %p (%s)\n", cur_action, name_str);
-        cur_command = get_first_command(cur_action);
-    } else {
-        cur_command = get_next_command(cur_action, cur_command);
-    }
-
-    if (!cur_command) {
-        return;
-    }
-
-    int result = cur_command->func(cur_command->nargs, cur_command->args);
-    if (klog_get_level() >= KLOG_INFO_LEVEL) {
-        for (int i = 0; i < cur_command->nargs; i++) {
-            strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str));
-            if (i < cur_command->nargs - 1) {
-                strlcat(cmd_str, " ", sizeof(cmd_str));
-            }
-        }
-        char source[256];
-        if (cur_command->filename) {
-            snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line);
-        } else {
-            *source = '\0';
-        }
-        INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
-             cmd_str, cur_action ? name_str : "", source, result, t.duration());
-    }
-}
-
-static int wait_for_coldboot_done_action(int nargs, char **args) {
+static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
     Timer t;
 
     NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
@@ -655,7 +228,7 @@ static int wait_for_coldboot_done_action(int nargs, char **args) {
  * time. We do not reboot or halt on failures, as this is a best-effort
  * attempt.
  */
-static int mix_hwrng_into_linux_rng_action(int nargs, char **args)
+static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args)
 {
     int result = -1;
     int hwrandom_fd = -1;
@@ -717,20 +290,20 @@ ret:
     return result;
 }
 
-static int keychord_init_action(int nargs, char **args)
+static int keychord_init_action(const std::vector<std::string>& args)
 {
     keychord_init();
     return 0;
 }
 
-static int console_init_action(int nargs, char **args)
+static int console_init_action(const std::vector<std::string>& args)
 {
-    char console[PROP_VALUE_MAX];
-    if (property_get("ro.boot.console", console) > 0) {
-        snprintf(console_name, sizeof(console_name), "/dev/%s", console);
+    std::string console = property_get("ro.boot.console");
+    if (!console.empty()) {
+        console_name = "/dev/" + console;
     }
 
-    int fd = open(console_name, O_RDWR | O_CLOEXEC);
+    int fd = open(console_name.c_str(), O_RDWR | O_CLOEXEC);
     if (fd >= 0)
         have_console = 1;
     close(fd);
@@ -760,36 +333,20 @@ static int console_init_action(int nargs, char **args)
     return 0;
 }
 
-static void import_kernel_nv(char *name, bool for_emulator)
-{
-    char *value = strchr(name, '=');
-    int name_len = strlen(name);
-
-    if (value == 0) return;
-    *value++ = 0;
-    if (name_len == 0) return;
+static void import_kernel_nv(const std::string& key, const std::string& value, bool for_emulator) {
+    if (key.empty()) return;
 
     if (for_emulator) {
-        /* in the emulator, export any kernel option with the
-         * ro.kernel. prefix */
-        char buff[PROP_NAME_MAX];
-        int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
-
-        if (len < (int)sizeof(buff))
-            property_set( buff, value );
+        // In the emulator, export any kernel option with the "ro.kernel." prefix.
+        property_set(android::base::StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
         return;
     }
 
-    if (!strcmp(name,"qemu")) {
-        strlcpy(qemu, value, sizeof(qemu));
-    } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
-        const char *boot_prop_name = name + 12;
-        char prop[PROP_NAME_MAX];
-        int cnt;
-
-        cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
-        if (cnt < PROP_NAME_MAX)
-            property_set(prop, value);
+    if (key == "qemu") {
+        strlcpy(qemu, value.c_str(), sizeof(qemu));
+    } else if (android::base::StartsWith(key, "androidboot.")) {
+        property_set(android::base::StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(),
+                     value.c_str());
     }
 }
 
@@ -807,14 +364,12 @@ static void export_kernel_boot_props() {
         { "ro.boot.revision",   "ro.revision",   "0", },
     };
     for (size_t i = 0; i < ARRAY_SIZE(prop_map); i++) {
-        char value[PROP_VALUE_MAX];
-        int rc = property_get(prop_map[i].src_prop, value);
-        property_set(prop_map[i].dst_prop, (rc > 0) ? value : prop_map[i].default_value);
+        std::string value = property_get(prop_map[i].src_prop);
+        property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
     }
 }
 
-static void process_kernel_dt(void)
-{
+static void process_kernel_dt() {
     static const char android_dir[] = "/proc/device-tree/firmware/android";
 
     std::string file_name = android::base::StringPrintf("%s/compatible", android_dir);
@@ -827,13 +382,13 @@ static void process_kernel_dt(void)
     }
 
     std::unique_ptr<DIR, int(*)(DIR*)>dir(opendir(android_dir), closedir);
-    if (!dir)
-        return;
+    if (!dir) return;
 
     struct dirent *dp;
     while ((dp = readdir(dir.get())) != NULL) {
-        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible"))
+        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
             continue;
+        }
 
         file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);
 
@@ -845,23 +400,20 @@ static void process_kernel_dt(void)
     }
 }
 
-static void process_kernel_cmdline(void)
-{
-    /* don't expose the raw commandline to nonpriv processes */
+static void process_kernel_cmdline() {
+    // Don't expose the raw commandline to unprivileged processes.
     chmod("/proc/cmdline", 0440);
 
-    /* first pass does the common stuff, and finds if we are in qemu.
-     * second pass is only necessary for qemu to export all kernel params
-     * as props.
-     */
+    // The first pass does the common stuff, and finds if we are in qemu.
+    // The second pass is only necessary for qemu to export all kernel params
+    // as properties.
     import_kernel_cmdline(false, import_kernel_nv);
-    if (qemu[0])
-        import_kernel_cmdline(true, import_kernel_nv);
+    if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
 }
 
-static int queue_property_triggers_action(int nargs, char **args)
+static int queue_property_triggers_action(const std::vector<std::string>& args)
 {
-    queue_all_property_triggers();
+    ActionManager::GetInstance().QueueAllPropertyTriggers();
     /* enable property triggers */
     property_triggers_enabled = 1;
     return 0;
@@ -874,46 +426,23 @@ static void selinux_init_all_handles(void)
     sehandle_prop = selinux_android_prop_context_handle();
 }
 
-enum selinux_enforcing_status { SELINUX_DISABLED, SELINUX_PERMISSIVE, SELINUX_ENFORCING };
+enum selinux_enforcing_status { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
 
 static selinux_enforcing_status selinux_status_from_cmdline() {
     selinux_enforcing_status status = SELINUX_ENFORCING;
 
-    std::function<void(char*,bool)> fn = [&](char* name, bool in_qemu) {
-        char *value = strchr(name, '=');
-        if (value == nullptr) { return; }
-        *value++ = '\0';
-        if (strcmp(name, "androidboot.selinux") == 0) {
-            if (strcmp(value, "disabled") == 0) {
-                status = SELINUX_DISABLED;
-            } else if (strcmp(value, "permissive") == 0) {
-                status = SELINUX_PERMISSIVE;
-            }
+    import_kernel_cmdline(false, [&](const std::string& key, const std::string& value, bool in_qemu) {
+        if (key == "androidboot.selinux" && value == "permissive") {
+            status = SELINUX_PERMISSIVE;
         }
-    };
-    import_kernel_cmdline(false, fn);
+    });
 
     return status;
 }
 
-
-static bool selinux_is_disabled(void)
-{
-    if (ALLOW_DISABLE_SELINUX) {
-        if (access("/sys/fs/selinux", F_OK) != 0) {
-            // SELinux is not compiled into the kernel, or has been disabled
-            // via the kernel command line "selinux=0".
-            return true;
-        }
-        return selinux_status_from_cmdline() == SELINUX_DISABLED;
-    }
-
-    return false;
-}
-
 static bool selinux_is_enforcing(void)
 {
-    if (ALLOW_DISABLE_SELINUX) {
+    if (ALLOW_PERMISSIVE_SELINUX) {
         return selinux_status_from_cmdline() == SELINUX_ENFORCING;
     }
     return true;
@@ -921,10 +450,6 @@ static bool selinux_is_enforcing(void)
 
 int selinux_reload_policy(void)
 {
-    if (selinux_is_disabled()) {
-        return -1;
-    }
-
     INFO("SELinux: Attempting to reload policy files\n");
 
     if (selinux_android_reload_policy() == -1) {
@@ -942,7 +467,16 @@ int selinux_reload_policy(void)
 }
 
 static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
-    snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data);
+
+    property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
+
+    if (!d || !d->name || !d->cr) {
+        ERROR("audit_callback invoked with null data arguments!");
+        return 0;
+    }
+
+    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
+            d->cr->pid, d->cr->uid, d->cr->gid);
     return 0;
 }
 
@@ -961,10 +495,6 @@ static void selinux_initialize(bool in_kernel_domain) {
     cb.func_audit = audit_callback;
     selinux_set_callback(SELINUX_CB_AUDIT, cb);
 
-    if (selinux_is_disabled()) {
-        return;
-    }
-
     if (in_kernel_domain) {
         INFO("Loading SELinux policy...\n");
         if (selinux_android_load_policy() < 0) {
@@ -972,8 +502,15 @@ static void selinux_initialize(bool in_kernel_domain) {
             security_failure();
         }
 
+        bool kernel_enforcing = (security_getenforce() == 1);
         bool is_enforcing = selinux_is_enforcing();
-        security_setenforce(is_enforcing);
+        if (kernel_enforcing != is_enforcing) {
+            if (security_setenforce(is_enforcing)) {
+                ERROR("security_setenforce(%s) failed: %s\n",
+                      is_enforcing ? "true" : "false", strerror(errno));
+                security_failure();
+            }
+        }
 
         if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
             security_failure();
@@ -1009,7 +546,8 @@ int main(int argc, char** argv) {
         mkdir("/dev/pts", 0755);
         mkdir("/dev/socket", 0755);
         mount("devpts", "/dev/pts", "devpts", 0, NULL);
-        mount("proc", "/proc", "proc", 0, NULL);
+        #define MAKE_STR(x) __STRING(x)
+        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
         mount("sysfs", "/sys", "sysfs", 0, NULL);
     }
 
@@ -1021,7 +559,7 @@ int main(int argc, char** argv) {
     klog_init();
     klog_set_level(KLOG_NOTICE_LEVEL);
 
-    NOTICE("init%s started!\n", is_first_stage ? "" : " second stage");
+    NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
 
     if (!is_first_stage) {
         // Indicate that booting is in progress to background fw loaders, etc.
@@ -1034,7 +572,7 @@ int main(int argc, char** argv) {
         process_kernel_dt();
         process_kernel_cmdline();
 
-        // Propogate the kernel variables to internal variables
+        // Propagate the kernel variables to internal variables
         // used by init as well as the current required properties.
         export_kernel_boot_props();
     }
@@ -1060,7 +598,7 @@ int main(int argc, char** argv) {
     // These directories were necessarily created before initial policy load
     // and therefore need their security context restored to the proper value.
     // This must happen before /dev is populated by ueventd.
-    INFO("Running restorecon...\n");
+    NOTICE("Running restorecon...\n");
     restorecon("/dev");
     restorecon("/dev/socket");
     restorecon("/dev/__properties__");
@@ -1077,38 +615,47 @@ int main(int argc, char** argv) {
     property_load_boot_defaults();
     start_property_service();
 
-    init_parse_config_file("/init.rc");
+    const BuiltinFunctionMap function_map;
+    Action::set_function_map(&function_map);
+
+    Parser& parser = Parser::GetInstance();
+    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
+    parser.AddSectionParser("on", std::make_unique<ActionParser>());
+    parser.AddSectionParser("import", std::make_unique<ImportParser>());
+    parser.ParseConfig("/init.rc");
+
+    ActionManager& am = ActionManager::GetInstance();
 
-    action_for_each_trigger("early-init", action_add_queue_tail);
+    am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
-    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
     // ... so that we can start queuing up actions that require stuff from /dev.
-    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
-    queue_builtin_action(keychord_init_action, "keychord_init");
-    queue_builtin_action(console_init_action, "console_init");
+    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
+    am.QueueBuiltinAction(console_init_action, "console_init");
 
     // Trigger all the boot actions to get us started.
-    action_for_each_trigger("init", action_add_queue_tail);
+    am.QueueEventTrigger("init");
 
     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     // wasn't ready immediately after wait_for_coldboot_done
-    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
 
     // Don't mount filesystems or start core system services in charger mode.
-    char bootmode[PROP_VALUE_MAX];
-    if (property_get("ro.bootmode", bootmode) > 0 && strcmp(bootmode, "charger") == 0) {
-        action_for_each_trigger("charger", action_add_queue_tail);
+    std::string bootmode = property_get("ro.bootmode");
+    if (bootmode == "charger") {
+        am.QueueEventTrigger("charger");
     } else {
-        action_for_each_trigger("late-init", action_add_queue_tail);
+        am.QueueEventTrigger("late-init");
     }
 
     // Run all property triggers based on current state of the properties.
-    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
+    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
 
     while (true) {
         if (!waiting_for_exec) {
-            execute_one_command();
+            am.ExecuteOneCommand();
             restart_processes();
         }
 
@@ -1119,7 +666,7 @@ int main(int argc, char** argv) {
                 timeout = 0;
         }
 
-        if (!action_queue_empty() || cur_action) {
+        if (am.HasMoreCommands()) {
             timeout = 0;
         }
 
index c166969..345d442 100644 (file)
 #ifndef _INIT_INIT_H
 #define _INIT_INIT_H
 
-#include <sys/types.h>
-
 #include <string>
-#include <vector>
-
-#include <cutils/list.h>
-#include <cutils/iosched_policy.h>
-
-struct command
-{
-        /* list of commands in an action */
-    struct listnode clist;
-
-    int (*func)(int nargs, char **args);
-
-    int line;
-    const char *filename;
-
-    int nargs;
-    char *args[1];
-};
-
-struct trigger {
-    struct listnode nlist;
-    const char *name;
-};
 
-struct action {
-        /* node in list of all actions */
-    struct listnode alist;
-        /* node in the queue of pending actions */
-    struct listnode qlist;
-        /* node in list of actions for a trigger */
-    struct listnode tlist;
-
-    unsigned hash;
-
-        /* list of actions which triggers the commands*/
-    struct listnode triggers;
-    struct listnode commands;
-    struct command *current;
-};
-
-struct socketinfo {
-    struct socketinfo *next;
-    const char *name;
-    const char *type;
-    uid_t uid;
-    gid_t gid;
-    int perm;
-    const char *socketcon;
-};
-
-struct svcenvinfo {
-    struct svcenvinfo *next;
-    const char *name;
-    const char *value;
-};
-
-#define SVC_DISABLED       0x001  // do not autostart with class
-#define SVC_ONESHOT        0x002  // do not restart on exit
-#define SVC_RUNNING        0x004  // currently active
-#define SVC_RESTARTING     0x008  // waiting to restart
-#define SVC_CONSOLE        0x010  // requires console
-#define SVC_CRITICAL       0x020  // will reboot into recovery if keeps crashing
-#define SVC_RESET          0x040  // Use when stopping a process, but not disabling so it can be restarted with its class.
-#define SVC_RC_DISABLED    0x080  // Remember if the disabled flag was set in the rc script.
-#define SVC_RESTART        0x100  // Use to safely restart (stop, wait, start) a service.
-#define SVC_DISABLED_START 0x200  // A start was requested but it was disabled at the time.
-#define SVC_EXEC           0x400  // This synthetic service corresponds to an 'exec'.
-
-#define NR_SVC_SUPP_GIDS 12    /* twelve supplementary groups */
+class Action;
+class Service;
 
 #define COMMAND_RETRY_TIMEOUT 5
 
-struct service {
-    void NotifyStateChange(const char* new_state);
-
-        /* list of all services */
-    struct listnode slist;
-
-    char *name;
-    const char *classname;
-
-    unsigned flags;
-    pid_t pid;
-    time_t time_started;    /* time of last start */
-    time_t time_crashed;    /* first crash within inspection window */
-    int nr_crashed;         /* number of times crashed within window */
-
-    uid_t uid;
-    gid_t gid;
-    gid_t supp_gids[NR_SVC_SUPP_GIDS];
-    size_t nr_supp_gids;
-
-    const char* seclabel;
-
-    struct socketinfo *sockets;
-    struct svcenvinfo *envvars;
-
-    struct action onrestart;  /* Actions to execute on restart. */
-
-    std::vector<std::string>* writepid_files_;
-
-    /* keycodes for triggering this service via /dev/keychord */
-    int *keycodes;
-    int nkeycodes;
-    int keychord_id;
-
-    IoSchedClass ioprio_class;
-    int ioprio_pri;
-
-    int nargs;
-    /* "MUST BE AT THE END OF THE STRUCT" */
-    char *args[1];
-}; /*     ^-------'args' MUST be at the end of this struct! */
-
+extern const char *ENV[32];
 extern bool waiting_for_exec;
+extern int have_console;
+extern std::string console_name;
 extern struct selabel_handle *sehandle;
 extern struct selabel_handle *sehandle_prop;
 
-void build_triggers_string(char *name_str, int length, struct action *cur_action);
+void handle_control_message(const std::string& msg, const std::string& arg);
 
-void handle_control_message(const char *msg, const char *arg);
-
-struct service *service_find_by_name(const char *name);
-struct service *service_find_by_pid(pid_t pid);
-struct service *service_find_by_keychord(int keychord_id);
-void service_for_each(void (*func)(struct service *svc));
-void service_for_each_class(const char *classname,
-                            void (*func)(struct service *svc));
-void service_for_each_flags(unsigned matchflags,
-                            void (*func)(struct service *svc));
-void service_stop(struct service *svc);
-void service_reset(struct service *svc);
-void service_restart(struct service *svc);
-void service_start(struct service *svc, const char *dynamic_args);
 void property_changed(const char *name, const char *value);
 
 int selinux_reload_policy(void);
 
-void zap_stdio(void);
-
 void register_epoll_handler(int fd, void (*fn)());
 
-#endif /* _INIT_INIT_H */
+int add_environment(const char* key, const char* val);
+
+#endif  /* _INIT_INIT_H */
index 9bab67d..5b8e27b 100644 (file)
  * limitations under the License.
  */
 
-#include <ctype.h>
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <inttypes.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
 
-#include "init.h"
-#include "parser.h"
+#include "action.h"
 #include "init_parser.h"
 #include "log.h"
-#include "property_service.h"
+#include "parser.h"
+#include "service.h"
 #include "util.h"
 
-#include <cutils/iosched_policy.h>
-#include <cutils/list.h>
-
-static list_declare(service_list);
-static list_declare(action_list);
-static list_declare(action_queue);
-
-struct import {
-    struct listnode list;
-    const char *filename;
-};
-
-static void *parse_service(struct parse_state *state, int nargs, char **args);
-static void parse_line_service(struct parse_state *state, int nargs, char **args);
-
-static void *parse_action(struct parse_state *state, int nargs, char **args);
-static void parse_line_action(struct parse_state *state, int nargs, char **args);
-
-#define SECTION 0x01
-#define COMMAND 0x02
-#define OPTION  0x04
-
-#include "keywords.h"
-
-#define KEYWORD(symbol, flags, nargs, func) \
-    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
-
-static struct {
-    const char *name;
-    int (*func)(int nargs, char **args);
-    unsigned char nargs;
-    unsigned char flags;
-} keyword_info[KEYWORD_COUNT] = {
-    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
-#include "keywords.h"
-};
-#undef KEYWORD
-
-#define kw_is(kw, type) (keyword_info[kw].flags & (type))
-#define kw_name(kw) (keyword_info[kw].name)
-#define kw_func(kw) (keyword_info[kw].func)
-#define kw_nargs(kw) (keyword_info[kw].nargs)
-
-void dump_parser_state() {
-    if (false) {
-        struct listnode* node;
-        list_for_each(node, &service_list) {
-            service* svc = node_to_item(node, struct service, slist);
-            INFO("service %s\n", svc->name);
-            INFO("  class '%s'\n", svc->classname);
-            INFO("  exec");
-            for (int n = 0; n < svc->nargs; n++) {
-                INFO(" '%s'", svc->args[n]);
-            }
-            INFO("\n");
-            for (socketinfo* si = svc->sockets; si; si = si->next) {
-                INFO("  socket %s %s 0%o\n", si->name, si->type, si->perm);
-            }
-        }
-
-        list_for_each(node, &action_list) {
-            action* act = node_to_item(node, struct action, alist);
-            INFO("on ");
-            char name_str[256] = "";
-            build_triggers_string(name_str, sizeof(name_str), act);
-            INFO("%s", name_str);
-            INFO("\n");
-
-            struct listnode* node2;
-            list_for_each(node2, &act->commands) {
-                command* cmd = node_to_item(node2, struct command, clist);
-                INFO("  %p", cmd->func);
-                for (int n = 0; n < cmd->nargs; n++) {
-                    INFO(" %s", cmd->args[n]);
-                }
-                INFO("\n");
-            }
-            INFO("\n");
-        }
-    }
-}
-
-static int lookup_keyword(const char *s)
-{
-    switch (*s++) {
-    case 'b':
-        if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
-        break;
-    case 'c':
-        if (!strcmp(s, "opy")) return K_copy;
-        if (!strcmp(s, "lass")) return K_class;
-        if (!strcmp(s, "lass_start")) return K_class_start;
-        if (!strcmp(s, "lass_stop")) return K_class_stop;
-        if (!strcmp(s, "lass_reset")) return K_class_reset;
-        if (!strcmp(s, "onsole")) return K_console;
-        if (!strcmp(s, "hown")) return K_chown;
-        if (!strcmp(s, "hmod")) return K_chmod;
-        if (!strcmp(s, "ritical")) return K_critical;
-        break;
-    case 'd':
-        if (!strcmp(s, "isabled")) return K_disabled;
-        if (!strcmp(s, "omainname")) return K_domainname;
-        break;
-    case 'e':
-        if (!strcmp(s, "nable")) return K_enable;
-        if (!strcmp(s, "xec")) return K_exec;
-        if (!strcmp(s, "xport")) return K_export;
-        break;
-    case 'g':
-        if (!strcmp(s, "roup")) return K_group;
-        break;
-    case 'h':
-        if (!strcmp(s, "ostname")) return K_hostname;
-        break;
-    case 'i':
-        if (!strcmp(s, "oprio")) return K_ioprio;
-        if (!strcmp(s, "fup")) return K_ifup;
-        if (!strcmp(s, "nsmod")) return K_insmod;
-        if (!strcmp(s, "mport")) return K_import;
-        if (!strcmp(s, "nstallkey")) return K_installkey;
-        break;
-    case 'k':
-        if (!strcmp(s, "eycodes")) return K_keycodes;
-        break;
-    case 'l':
-        if (!strcmp(s, "oglevel")) return K_loglevel;
-        if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
-        if (!strcmp(s, "oad_system_props")) return K_load_system_props;
-        break;
-    case 'm':
-        if (!strcmp(s, "kdir")) return K_mkdir;
-        if (!strcmp(s, "ount_all")) return K_mount_all;
-        if (!strcmp(s, "ount")) return K_mount;
-        break;
-    case 'o':
-        if (!strcmp(s, "n")) return K_on;
-        if (!strcmp(s, "neshot")) return K_oneshot;
-        if (!strcmp(s, "nrestart")) return K_onrestart;
-        break;
-    case 'p':
-        if (!strcmp(s, "owerctl")) return K_powerctl;
-        break;
-    case 'r':
-        if (!strcmp(s, "estart")) return K_restart;
-        if (!strcmp(s, "estorecon")) return K_restorecon;
-        if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive;
-        if (!strcmp(s, "mdir")) return K_rmdir;
-        if (!strcmp(s, "m")) return K_rm;
-        break;
-    case 's':
-        if (!strcmp(s, "eclabel")) return K_seclabel;
-        if (!strcmp(s, "ervice")) return K_service;
-        if (!strcmp(s, "etenv")) return K_setenv;
-        if (!strcmp(s, "etprop")) return K_setprop;
-        if (!strcmp(s, "etrlimit")) return K_setrlimit;
-        if (!strcmp(s, "etusercryptopolicies")) return K_setusercryptopolicies;
-        if (!strcmp(s, "ocket")) return K_socket;
-        if (!strcmp(s, "tart")) return K_start;
-        if (!strcmp(s, "top")) return K_stop;
-        if (!strcmp(s, "wapon_all")) return K_swapon_all;
-        if (!strcmp(s, "ymlink")) return K_symlink;
-        if (!strcmp(s, "ysclktz")) return K_sysclktz;
-        break;
-    case 't':
-        if (!strcmp(s, "rigger")) return K_trigger;
-        break;
-    case 'u':
-        if (!strcmp(s, "ser")) return K_user;
-        break;
-    case 'v':
-        if (!strcmp(s, "erity_load_state")) return K_verity_load_state;
-        if (!strcmp(s, "erity_update_state")) return K_verity_update_state;
-        break;
-    case 'w':
-        if (!strcmp(s, "rite")) return K_write;
-        if (!strcmp(s, "ritepid")) return K_writepid;
-        if (!strcmp(s, "ait")) return K_wait;
-        break;
-    }
-    return K_UNKNOWN;
-}
-
-static void parse_line_no_op(struct parse_state*, int, char**) {
-}
-
-static int push_chars(char **dst, int *len, const char *chars, int cnt)
-{
-    if (cnt > *len)
-        return -1;
-
-    memcpy(*dst, chars, cnt);
-    *dst += cnt;
-    *len -= cnt;
-
-    return 0;
-}
-
-int expand_props(char *dst, const char *src, int dst_size)
-{
-    char *dst_ptr = dst;
-    const char *src_ptr = src;
-    int ret = 0;
-    int left = dst_size - 1;
-
-    if (!src || !dst || dst_size == 0)
-        return -1;
-
-    /* - variables can either be $x.y or ${x.y}, in case they are only part
-     *   of the string.
-     * - will accept $$ as a literal $.
-     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
-     *   bad things will happen
-     */
-    while (*src_ptr && left > 0) {
-        char *c;
-        char prop[PROP_NAME_MAX + 1];
-        char prop_val[PROP_VALUE_MAX];
-        int prop_len = 0;
-        int prop_val_len;
-
-        c = strchr(src_ptr, '$');
-        if (!c) {
-            while (left-- > 0 && *src_ptr)
-                *(dst_ptr++) = *(src_ptr++);
-            break;
-        }
-
-        memset(prop, 0, sizeof(prop));
-
-        ret = push_chars(&dst_ptr, &left, src_ptr, c - src_ptr);
-        if (ret < 0)
-            goto err_nospace;
-        c++;
-
-        if (*c == '$') {
-            *(dst_ptr++) = *(c++);
-            src_ptr = c;
-            left--;
-            continue;
-        } else if (*c == '\0') {
-            break;
-        }
-
-        if (*c == '{') {
-            c++;
-            while (*c && *c != '}' && prop_len < PROP_NAME_MAX)
-                prop[prop_len++] = *(c++);
-            if (*c != '}') {
-                /* failed to find closing brace, abort. */
-                if (prop_len == PROP_NAME_MAX)
-                    ERROR("prop name too long during expansion of '%s'\n",
-                          src);
-                else if (*c == '\0')
-                    ERROR("unexpected end of string in '%s', looking for }\n",
-                          src);
-                goto err;
-            }
-            prop[prop_len] = '\0';
-            c++;
-        } else if (*c) {
-            while (*c && prop_len < PROP_NAME_MAX)
-                prop[prop_len++] = *(c++);
-            if (prop_len == PROP_NAME_MAX && *c != '\0') {
-                ERROR("prop name too long in '%s'\n", src);
-                goto err;
-            }
-            prop[prop_len] = '\0';
-            ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
-                  prop);
-        }
-
-        if (prop_len == 0) {
-            ERROR("invalid zero-length prop name in '%s'\n", src);
-            goto err;
-        }
-
-        prop_val_len = property_get(prop, prop_val);
-        if (!prop_val_len) {
-            ERROR("property '%s' doesn't exist while expanding '%s'\n",
-                  prop, src);
-            goto err;
-        }
-
-        ret = push_chars(&dst_ptr, &left, prop_val, prop_val_len);
-        if (ret < 0)
-            goto err_nospace;
-        src_ptr = c;
-        continue;
-    }
-
-    *dst_ptr = '\0';
-    return 0;
+#include <base/stringprintf.h>
 
-err_nospace:
-    ERROR("destination buffer overflow while expanding '%s'\n", src);
-err:
-    return -1;
+Parser::Parser() {
 }
 
-static void parse_import(struct parse_state *state, int nargs, char **args)
-{
-    struct listnode *import_list = (listnode*) state->priv;
-    char conf_file[PATH_MAX];
-    int ret;
-
-    if (nargs != 2) {
-        ERROR("single argument needed for import\n");
-        return;
-    }
-
-    ret = expand_props(conf_file, args[1], sizeof(conf_file));
-    if (ret) {
-        ERROR("error while handling import on line '%d' in '%s'\n",
-              state->line, state->filename);
-        return;
-    }
-
-    struct import* import = (struct import*) calloc(1, sizeof(struct import));
-    import->filename = strdup(conf_file);
-    list_add_tail(import_list, &import->list);
-    INFO("Added '%s' to import list\n", import->filename);
+Parser& Parser::GetInstance() {
+    static Parser instance;
+    return instance;
 }
 
-static void parse_new_section(struct parse_state *state, int kw,
-                       int nargs, char **args)
-{
-    printf("[ %s %s ]\n", args[0],
-           nargs > 1 ? args[1] : "");
-    switch(kw) {
-    case K_service:
-        state->context = parse_service(state, nargs, args);
-        if (state->context) {
-            state->parse_line = parse_line_service;
-            return;
-        }
-        break;
-    case K_on:
-        state->context = parse_action(state, nargs, args);
-        if (state->context) {
-            state->parse_line = parse_line_action;
-            return;
-        }
-        break;
-    case K_import:
-        parse_import(state, nargs, args);
-        break;
-    }
-    state->parse_line = parse_line_no_op;
+void Parser::AddSectionParser(const std::string& name,
+                              std::unique_ptr<SectionParser> parser) {
+    section_parsers_[name] = std::move(parser);
 }
 
-static void parse_config(const char *fn, const std::string& data)
-{
-    struct listnode import_list;
-    struct listnode *node;
-    char *args[INIT_PARSER_MAXARGS];
-
-    int nargs = 0;
+void Parser::ParseData(const std::string& filename, const std::string& data) {
+    //TODO: Use a parser with const input and remove this copy
+    std::vector<char> data_copy(data.begin(), data.end());
+    data_copy.push_back('\0');
 
     parse_state state;
-    state.filename = fn;
+    state.filename = filename.c_str();
     state.line = 0;
-    state.ptr = strdup(data.c_str());  // TODO: fix this code!
+    state.ptr = &data_copy[0];
     state.nexttoken = 0;
-    state.parse_line = parse_line_no_op;
 
-    list_init(&import_list);
-    state.priv = &import_list;
+    SectionParser* section_parser = nullptr;
+    std::vector<std::string> args;
 
     for (;;) {
         switch (next_token(&state)) {
         case T_EOF:
-            state.parse_line(&state, 0, 0);
-            goto parser_done;
+            if (section_parser) {
+                section_parser->EndSection();
+            }
+            return;
         case T_NEWLINE:
             state.line++;
-            if (nargs) {
-                int kw = lookup_keyword(args[0]);
-                if (kw_is(kw, SECTION)) {
-                    state.parse_line(&state, 0, 0);
-                    parse_new_section(&state, kw, nargs, args);
-                } else {
-                    state.parse_line(&state, nargs, args);
+            if (args.empty()) {
+                break;
+            }
+            if (section_parsers_.count(args[0])) {
+                if (section_parser) {
+                    section_parser->EndSection();
+                }
+                section_parser = section_parsers_[args[0]].get();
+                std::string ret_err;
+                if (!section_parser->ParseSection(args, &ret_err)) {
+                    parse_error(&state, "%s\n", ret_err.c_str());
+                    section_parser = nullptr;
+                }
+            } else if (section_parser) {
+                std::string ret_err;
+                if (!section_parser->ParseLineSection(args, state.filename,
+                                                      state.line, &ret_err)) {
+                    parse_error(&state, "%s\n", ret_err.c_str());
                 }
-                nargs = 0;
             }
+            args.clear();
             break;
         case T_TEXT:
-            if (nargs < INIT_PARSER_MAXARGS) {
-                args[nargs++] = state.text;
-            }
+            args.emplace_back(state.text);
             break;
         }
     }
-
-parser_done:
-    list_for_each(node, &import_list) {
-         struct import *import = node_to_item(node, struct import, list);
-         int ret;
-
-         ret = init_parse_config_file(import->filename);
-         if (ret)
-             ERROR("could not import file '%s' from '%s'\n",
-                   import->filename, fn);
-    }
 }
 
-int init_parse_config_file(const char* path) {
-    INFO("Parsing %s...\n", path);
+bool Parser::ParseConfigFile(const std::string& path) {
+    INFO("Parsing file %s...\n", path.c_str());
     Timer t;
     std::string data;
-    if (!read_file(path, &data)) {
-        return -1;
+    if (!read_file(path.c_str(), &data)) {
+        return false;
     }
 
     data.push_back('\n'); // TODO: fix parse_config.
-    parse_config(path, data);
-    dump_parser_state();
-
-    NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
-    return 0;
-}
-
-static int valid_name(const char *name)
-{
-    if (strlen(name) > 16) {
-        return 0;
-    }
-    while (*name) {
-        if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
-            return 0;
-        }
-        name++;
-    }
-    return 1;
-}
-
-struct service *service_find_by_name(const char *name)
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        if (!strcmp(svc->name, name)) {
-            return svc;
-        }
-    }
-    return 0;
-}
-
-struct service *service_find_by_pid(pid_t pid)
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        if (svc->pid == pid) {
-            return svc;
-        }
-    }
-    return 0;
-}
-
-struct service *service_find_by_keychord(int keychord_id)
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        if (svc->keychord_id == keychord_id) {
-            return svc;
-        }
-    }
-    return 0;
-}
-
-void service_for_each(void (*func)(struct service *svc))
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        func(svc);
-    }
-}
-
-void service_for_each_class(const char *classname,
-                            void (*func)(struct service *svc))
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        if (!strcmp(svc->classname, classname)) {
-            func(svc);
-        }
-    }
-}
-
-void service_for_each_flags(unsigned matchflags,
-                            void (*func)(struct service *svc))
-{
-    struct listnode *node;
-    struct service *svc;
-    list_for_each(node, &service_list) {
-        svc = node_to_item(node, struct service, slist);
-        if (svc->flags & matchflags) {
-            func(svc);
-        }
-    }
-}
-
-void action_for_each_trigger(const char *trigger,
-                             void (*func)(struct action *act))
-{
-    struct listnode *node, *node2;
-    struct action *act;
-    struct trigger *cur_trigger;
-
-    list_for_each(node, &action_list) {
-        act = node_to_item(node, struct action, alist);
-        list_for_each(node2, &act->triggers) {
-            cur_trigger = node_to_item(node2, struct trigger, nlist);
-            if (!strcmp(cur_trigger->name, trigger)) {
-                func(act);
+    ParseData(path, data);
+    for (const auto& sp : section_parsers_) {
+        sp.second->EndFile(path);
+    }
+
+    // Turning this on and letting the INFO logging be discarded adds 0.2s to
+    // Nexus 9 boot time, so it's disabled by default.
+    if (false) DumpState();
+
+    NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
+    return true;
+}
+
+bool Parser::ParseConfigDir(const std::string& path) {
+    INFO("Parsing directory %s...\n", path.c_str());
+    std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
+    if (!config_dir) {
+        ERROR("Could not import directory '%s'\n", path.c_str());
+        return false;
+    }
+    dirent* current_file;
+    while ((current_file = readdir(config_dir.get()))) {
+        std::string current_path =
+            android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
+        // Ignore directories and only process regular files.
+        if (current_file->d_type == DT_REG) {
+            if (!ParseConfigFile(current_path)) {
+                ERROR("could not import file '%s'\n", current_path.c_str());
             }
         }
     }
+    return true;
 }
 
-
-void queue_property_triggers(const char *name, const char *value)
-{
-    struct listnode *node, *node2;
-    struct action *act;
-    struct trigger *cur_trigger;
-    bool match;
-    int name_length;
-
-    list_for_each(node, &action_list) {
-        act = node_to_item(node, struct action, alist);
-        match = !name;
-        list_for_each(node2, &act->triggers) {
-            cur_trigger = node_to_item(node2, struct trigger, nlist);
-            if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {
-                const char *test = cur_trigger->name + strlen("property:");
-                if (!match) {
-                    name_length = strlen(name);
-                    if (!strncmp(name, test, name_length) &&
-                        test[name_length] == '=' &&
-                        (!strcmp(test + name_length + 1, value) ||
-                        !strcmp(test + name_length + 1, "*"))) {
-                        match = true;
-                        continue;
-                    }
-                }
-                const char* equals = strchr(test, '=');
-                if (equals) {
-                    char prop_name[PROP_NAME_MAX + 1];
-                    char value[PROP_VALUE_MAX];
-                    int length = equals - test;
-                    if (length <= PROP_NAME_MAX) {
-                        int ret;
-                        memcpy(prop_name, test, length);
-                        prop_name[length] = 0;
-
-                        /* does the property exist, and match the trigger value? */
-                        ret = property_get(prop_name, value);
-                        if (ret > 0 && (!strcmp(equals + 1, value) ||
-                                        !strcmp(equals + 1, "*"))) {
-                            continue;
-                        }
-                    }
-                }
-            }
-            match = false;
-            break;
-        }
-        if (match) {
-            action_add_queue_tail(act);
-        }
+bool Parser::ParseConfig(const std::string& path) {
+    if (is_dir(path.c_str())) {
+        return ParseConfigDir(path);
     }
+    return ParseConfigFile(path);
 }
 
-void queue_all_property_triggers()
-{
-    queue_property_triggers(NULL, NULL);
-}
-
-void queue_builtin_action(int (*func)(int nargs, char **args), const char *name)
-{
-    action* act = (action*) calloc(1, sizeof(*act));
-    trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
-    cur_trigger->name = name;
-    list_init(&act->triggers);
-    list_add_tail(&act->triggers, &cur_trigger->nlist);
-    list_init(&act->commands);
-    list_init(&act->qlist);
-
-    command* cmd = (command*) calloc(1, sizeof(*cmd));
-    cmd->func = func;
-    cmd->args[0] = const_cast<char*>(name);
-    cmd->nargs = 1;
-    list_add_tail(&act->commands, &cmd->clist);
-
-    list_add_tail(&action_list, &act->alist);
-    action_add_queue_tail(act);
-}
-
-void action_add_queue_tail(struct action *act)
-{
-    if (list_empty(&act->qlist)) {
-        list_add_tail(&action_queue, &act->qlist);
-    }
-}
-
-struct action *action_remove_queue_head(void)
-{
-    if (list_empty(&action_queue)) {
-        return 0;
-    } else {
-        struct listnode *node = list_head(&action_queue);
-        struct action *act = node_to_item(node, struct action, qlist);
-        list_remove(node);
-        list_init(node);
-        return act;
-    }
-}
-
-int action_queue_empty()
-{
-    return list_empty(&action_queue);
-}
-
-service* make_exec_oneshot_service(int nargs, char** args) {
-    // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
-    // SECLABEL can be a - to denote default
-    int command_arg = 1;
-    for (int i = 1; i < nargs; ++i) {
-        if (strcmp(args[i], "--") == 0) {
-            command_arg = i + 1;
-            break;
-        }
-    }
-    if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
-        ERROR("exec called with too many supplementary group ids\n");
-        return NULL;
-    }
-
-    int argc = nargs - command_arg;
-    char** argv = (args + command_arg);
-    if (argc < 1) {
-        ERROR("exec called without command\n");
-        return NULL;
-    }
-
-    service* svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * argc);
-    if (svc == NULL) {
-        ERROR("Couldn't allocate service for exec of '%s': %s", argv[0], strerror(errno));
-        return NULL;
-    }
-
-    if ((command_arg > 2) && strcmp(args[1], "-")) {
-        svc->seclabel = args[1];
-    }
-    if (command_arg > 3) {
-        svc->uid = decode_uid(args[2]);
-    }
-    if (command_arg > 4) {
-        svc->gid = decode_uid(args[3]);
-        svc->nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
-        for (size_t i = 0; i < svc->nr_supp_gids; ++i) {
-            svc->supp_gids[i] = decode_uid(args[4 + i]);
-        }
-    }
-
-    static int exec_count; // Every service needs a unique name.
-    char* name = NULL;
-    asprintf(&name, "exec %d (%s)", exec_count++, argv[0]);
-    if (name == NULL) {
-        ERROR("Couldn't allocate name for exec service '%s'\n", argv[0]);
-        free(svc);
-        return NULL;
-    }
-    svc->name = name;
-    svc->classname = "default";
-    svc->flags = SVC_EXEC | SVC_ONESHOT;
-    svc->nargs = argc;
-    memcpy(svc->args, argv, sizeof(char*) * svc->nargs);
-    svc->args[argc] = NULL;
-    list_add_tail(&service_list, &svc->slist);
-    return svc;
-}
-
-static void *parse_service(struct parse_state *state, int nargs, char **args)
-{
-    if (nargs < 3) {
-        parse_error(state, "services must have a name and a program\n");
-        return 0;
-    }
-    if (!valid_name(args[1])) {
-        parse_error(state, "invalid service name '%s'\n", args[1]);
-        return 0;
-    }
-
-    service* svc = (service*) service_find_by_name(args[1]);
-    if (svc) {
-        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
-        return 0;
-    }
-
-    nargs -= 2;
-    svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
-    if (!svc) {
-        parse_error(state, "out of memory\n");
-        return 0;
-    }
-    svc->name = strdup(args[1]);
-    svc->classname = "default";
-    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
-    trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
-    svc->args[nargs] = 0;
-    svc->nargs = nargs;
-    list_init(&svc->onrestart.triggers);
-    cur_trigger->name = "onrestart";
-    list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
-    list_init(&svc->onrestart.commands);
-    list_add_tail(&service_list, &svc->slist);
-    return svc;
-}
-
-static void parse_line_service(struct parse_state *state, int nargs, char **args)
-{
-    struct service *svc = (service*) state->context;
-    struct command *cmd;
-    int i, kw, kw_nargs;
-
-    if (nargs == 0) {
-        return;
-    }
-
-    svc->ioprio_class = IoSchedClass_NONE;
-
-    kw = lookup_keyword(args[0]);
-    switch (kw) {
-    case K_class:
-        if (nargs != 2) {
-            parse_error(state, "class option requires a classname\n");
-        } else {
-            svc->classname = args[1];
-        }
-        break;
-    case K_console:
-        svc->flags |= SVC_CONSOLE;
-        break;
-    case K_disabled:
-        svc->flags |= SVC_DISABLED;
-        svc->flags |= SVC_RC_DISABLED;
-        break;
-    case K_ioprio:
-        if (nargs != 3) {
-            parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
-        } else {
-            svc->ioprio_pri = strtoul(args[2], 0, 8);
-
-            if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
-                parse_error(state, "priority value must be range 0 - 7\n");
-                break;
-            }
-
-            if (!strcmp(args[1], "rt")) {
-                svc->ioprio_class = IoSchedClass_RT;
-            } else if (!strcmp(args[1], "be")) {
-                svc->ioprio_class = IoSchedClass_BE;
-            } else if (!strcmp(args[1], "idle")) {
-                svc->ioprio_class = IoSchedClass_IDLE;
-            } else {
-                parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
-            }
-        }
-        break;
-    case K_group:
-        if (nargs < 2) {
-            parse_error(state, "group option requires a group id\n");
-        } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
-            parse_error(state, "group option accepts at most %d supp. groups\n",
-                        NR_SVC_SUPP_GIDS);
-        } else {
-            int n;
-            svc->gid = decode_uid(args[1]);
-            for (n = 2; n < nargs; n++) {
-                svc->supp_gids[n-2] = decode_uid(args[n]);
-            }
-            svc->nr_supp_gids = n - 2;
-        }
-        break;
-    case K_keycodes:
-        if (nargs < 2) {
-            parse_error(state, "keycodes option requires atleast one keycode\n");
-        } else {
-            svc->keycodes = (int*) malloc((nargs - 1) * sizeof(svc->keycodes[0]));
-            if (!svc->keycodes) {
-                parse_error(state, "could not allocate keycodes\n");
-            } else {
-                svc->nkeycodes = nargs - 1;
-                for (i = 1; i < nargs; i++) {
-                    svc->keycodes[i - 1] = atoi(args[i]);
-                }
-            }
-        }
-        break;
-    case K_oneshot:
-        svc->flags |= SVC_ONESHOT;
-        break;
-    case K_onrestart:
-        nargs--;
-        args++;
-        kw = lookup_keyword(args[0]);
-        if (!kw_is(kw, COMMAND)) {
-            parse_error(state, "invalid command '%s'\n", args[0]);
-            break;
-        }
-        kw_nargs = kw_nargs(kw);
-        if (nargs < kw_nargs) {
-            parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
-                kw_nargs > 2 ? "arguments" : "argument");
-            break;
-        }
-
-        cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
-        cmd->func = kw_func(kw);
-        cmd->nargs = nargs;
-        memcpy(cmd->args, args, sizeof(char*) * nargs);
-        list_add_tail(&svc->onrestart.commands, &cmd->clist);
-        break;
-    case K_critical:
-        svc->flags |= SVC_CRITICAL;
-        break;
-    case K_setenv: { /* name value */
-        if (nargs < 3) {
-            parse_error(state, "setenv option requires name and value arguments\n");
-            break;
-        }
-        svcenvinfo* ei = (svcenvinfo*) calloc(1, sizeof(*ei));
-        if (!ei) {
-            parse_error(state, "out of memory\n");
-            break;
-        }
-        ei->name = args[1];
-        ei->value = args[2];
-        ei->next = svc->envvars;
-        svc->envvars = ei;
-        break;
-    }
-    case K_socket: {/* name type perm [ uid gid context ] */
-        if (nargs < 4) {
-            parse_error(state, "socket option requires name, type, perm arguments\n");
-            break;
-        }
-        if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
-                && strcmp(args[2],"seqpacket")) {
-            parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
-            break;
-        }
-        socketinfo* si = (socketinfo*) calloc(1, sizeof(*si));
-        if (!si) {
-            parse_error(state, "out of memory\n");
-            break;
-        }
-        si->name = args[1];
-        si->type = args[2];
-        si->perm = strtoul(args[3], 0, 8);
-        if (nargs > 4)
-            si->uid = decode_uid(args[4]);
-        if (nargs > 5)
-            si->gid = decode_uid(args[5]);
-        if (nargs > 6)
-            si->socketcon = args[6];
-        si->next = svc->sockets;
-        svc->sockets = si;
-        break;
-    }
-    case K_user:
-        if (nargs != 2) {
-            parse_error(state, "user option requires a user id\n");
-        } else {
-            svc->uid = decode_uid(args[1]);
-        }
-        break;
-    case K_seclabel:
-        if (nargs != 2) {
-            parse_error(state, "seclabel option requires a label string\n");
-        } else {
-            svc->seclabel = args[1];
-        }
-        break;
-    case K_writepid:
-        if (nargs < 2) {
-            parse_error(state, "writepid option requires at least one filename\n");
-            break;
-        }
-        svc->writepid_files_ = new std::vector<std::string>;
-        for (int i = 1; i < nargs; ++i) {
-            svc->writepid_files_->push_back(args[i]);
-        }
-        break;
-
-    default:
-        parse_error(state, "invalid option '%s'\n", args[0]);
-    }
-}
-
-static void *parse_action(struct parse_state *state, int nargs, char **args)
-{
-    struct trigger *cur_trigger;
-    int i;
-    if (nargs < 2) {
-        parse_error(state, "actions must have a trigger\n");
-        return 0;
-    }
-
-    action* act = (action*) calloc(1, sizeof(*act));
-    list_init(&act->triggers);
-
-    for (i = 1; i < nargs; i++) {
-        if (!(i % 2)) {
-            if (strcmp(args[i], "&&")) {
-                struct listnode *node;
-                struct listnode *node2;
-                parse_error(state, "& is the only symbol allowed to concatenate actions\n");
-                list_for_each_safe(node, node2, &act->triggers) {
-                    struct trigger *trigger = node_to_item(node, struct trigger, nlist);
-                    free(trigger);
-                }
-                free(act);
-                return 0;
-            } else
-                continue;
-        }
-        cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
-        cur_trigger->name = args[i];
-        list_add_tail(&act->triggers, &cur_trigger->nlist);
-    }
-
-    list_init(&act->commands);
-    list_init(&act->qlist);
-    list_add_tail(&action_list, &act->alist);
-        /* XXX add to hash */
-    return act;
-}
-
-static void parse_line_action(struct parse_state* state, int nargs, char **args)
-{
-    struct action *act = (action*) state->context;
-    int kw, n;
-
-    if (nargs == 0) {
-        return;
-    }
-
-    kw = lookup_keyword(args[0]);
-    if (!kw_is(kw, COMMAND)) {
-        parse_error(state, "invalid command '%s'\n", args[0]);
-        return;
-    }
-
-    n = kw_nargs(kw);
-    if (nargs < n) {
-        parse_error(state, "%s requires %d %s\n", args[0], n - 1,
-            n > 2 ? "arguments" : "argument");
-        return;
-    }
-    command* cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
-    cmd->func = kw_func(kw);
-    cmd->line = state->line;
-    cmd->filename = state->filename;
-    cmd->nargs = nargs;
-    memcpy(cmd->args, args, sizeof(char*) * nargs);
-    list_add_tail(&act->commands, &cmd->clist);
+void Parser::DumpState() const {
+    ServiceManager::GetInstance().DumpState();
+    ActionManager::GetInstance().DumpState();
 }
index 6348607..5ed30ad 100644 (file)
 #ifndef _INIT_INIT_PARSER_H_
 #define _INIT_INIT_PARSER_H_
 
-#define INIT_PARSER_MAXARGS 64
+#include <map>
+#include <string>
+#include <vector>
 
-struct action;
-struct service;
+class SectionParser {
+public:
+    virtual ~SectionParser() {
+    }
+    virtual bool ParseSection(const std::vector<std::string>& args,
+                              std::string* err) = 0;
+    virtual bool ParseLineSection(const std::vector<std::string>& args,
+                                  const std::string& filename, int line,
+                                  std::string* err) const = 0;
+    virtual void EndSection() = 0;
+    virtual void EndFile(const std::string& filename) = 0;
+};
 
-struct action *action_remove_queue_head(void);
-void action_add_queue_tail(struct action *act);
-void action_for_each_trigger(const char *trigger,
-                             void (*func)(struct action *act));
-int action_queue_empty(void);
-void queue_property_triggers(const char *name, const char *value);
-void queue_all_property_triggers();
-void queue_builtin_action(int (*func)(int nargs, char **args), const char *name);
+class Parser {
+public:
+    static Parser& GetInstance();
+    void DumpState() const;
+    bool ParseConfig(const std::string& path);
+    void AddSectionParser(const std::string& name,
+                          std::unique_ptr<SectionParser> parser);
 
-int init_parse_config_file(const char *fn);
-int expand_props(char *dst, const char *src, int len);
+private:
+    Parser();
 
-service* make_exec_oneshot_service(int argc, char** argv);
+    void ParseData(const std::string& filename, const std::string& data);
+    bool ParseConfigFile(const std::string& path);
+    bool ParseConfigDir(const std::string& path);
+
+    std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+};
 
 #endif
index 170a73a..52aaa37 100644 (file)
 #include "init_parser.h"
 
 #include "init.h"
+#include "service.h"
 #include "util.h"
 
 #include <errno.h>
 #include <gtest/gtest.h>
 
-TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
-    char* argv[10];
-    memset(argv, 0, sizeof(argv));
+#include <string>
+#include <vector>
 
+TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
+    ServiceManager& sm = ServiceManager::GetInstance();
+    std::vector<std::string> args;
     // Nothing.
-    ASSERT_EQ(nullptr, make_exec_oneshot_service(0, argv));
+    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
 
     // No arguments to 'exec'.
-    argv[0] = const_cast<char*>("exec");
-    ASSERT_EQ(nullptr, make_exec_oneshot_service(1, argv));
+    args.push_back("exec");
+    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
 
     // No command in "exec --".
-    argv[1] = const_cast<char*>("--");
-    ASSERT_EQ(nullptr, make_exec_oneshot_service(2, argv));
+    args.push_back("--");
+    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
 }
 
 TEST(init_parser, make_exec_oneshot_service_too_many_supplementary_gids) {
-    int argc = 0;
-    char* argv[4 + NR_SVC_SUPP_GIDS + 3];
-    argv[argc++] = const_cast<char*>("exec");
-    argv[argc++] = const_cast<char*>("seclabel");
-    argv[argc++] = const_cast<char*>("root"); // uid.
-    argv[argc++] = const_cast<char*>("root"); // gid.
+    ServiceManager& sm = ServiceManager::GetInstance();
+    std::vector<std::string> args;
+    args.push_back("exec");
+    args.push_back("seclabel");
+    args.push_back("root"); // uid.
+    args.push_back("root"); // gid.
     for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
-        argv[argc++] = const_cast<char*>("root"); // Supplementary gid.
+        args.push_back("root"); // Supplementary gid.
     }
-    argv[argc++] = const_cast<char*>("--");
-    argv[argc++] = const_cast<char*>("/system/bin/id");
-    argv[argc] = nullptr;
-    ASSERT_EQ(nullptr, make_exec_oneshot_service(argc, argv));
+    args.push_back("--");
+    args.push_back("/system/bin/id");
+    ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
 }
 
-static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid, bool supplementary_gids) {
-    int argc = 0;
-    char* argv[10];
-    argv[argc++] = const_cast<char*>("exec");
+static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid,
+                                           bool gid, bool supplementary_gids) {
+    ServiceManager& sm = ServiceManager::GetInstance();
+    std::vector<std::string> args;
+    args.push_back("exec");
     if (seclabel) {
-        argv[argc++] = const_cast<char*>("u:r:su:s0"); // seclabel
+        args.push_back("u:r:su:s0"); // seclabel
         if (uid) {
-            argv[argc++] = const_cast<char*>("log");      // uid
+            args.push_back("log");      // uid
             if (gid) {
-                argv[argc++] = const_cast<char*>("shell");     // gid
+                args.push_back("shell");     // gid
                 if (supplementary_gids) {
-                    argv[argc++] = const_cast<char*>("system");    // supplementary gid 0
-                    argv[argc++] = const_cast<char*>("adb");       // supplementary gid 1
+                    args.push_back("system");    // supplementary gid 0
+                    args.push_back("adb");       // supplementary gid 1
                 }
             }
         }
     }
     if (dash_dash) {
-        argv[argc++] = const_cast<char*>("--");
+        args.push_back("--");
     }
-    argv[argc++] = const_cast<char*>("/system/bin/toybox");
-    argv[argc++] = const_cast<char*>("id");
-    argv[argc] = nullptr;
-    service* svc = make_exec_oneshot_service(argc, argv);
+    args.push_back("/system/bin/toybox");
+    args.push_back("id");
+    Service* svc = sm.MakeExecOneshotService(args);
     ASSERT_NE(nullptr, svc);
 
     if (seclabel) {
-        ASSERT_STREQ("u:r:su:s0", svc->seclabel);
+        ASSERT_EQ("u:r:su:s0", svc->seclabel());
     } else {
-        ASSERT_EQ(nullptr, svc->seclabel);
+        ASSERT_EQ("", svc->seclabel());
     }
     if (uid) {
-        ASSERT_EQ(decode_uid("log"), svc->uid);
+        ASSERT_EQ(decode_uid("log"), svc->uid());
     } else {
-        ASSERT_EQ(0U, svc->uid);
+        ASSERT_EQ(0U, svc->uid());
     }
     if (gid) {
-        ASSERT_EQ(decode_uid("shell"), svc->gid);
+        ASSERT_EQ(decode_uid("shell"), svc->gid());
     } else {
-        ASSERT_EQ(0U, svc->gid);
+        ASSERT_EQ(0U, svc->gid());
     }
     if (supplementary_gids) {
-        ASSERT_EQ(2U, svc->nr_supp_gids);
-        ASSERT_EQ(decode_uid("system"), svc->supp_gids[0]);
-        ASSERT_EQ(decode_uid("adb"), svc->supp_gids[1]);
+        ASSERT_EQ(2U, svc->supp_gids().size());
+        ASSERT_EQ(decode_uid("system"), svc->supp_gids()[0]);
+        ASSERT_EQ(decode_uid("adb"), svc->supp_gids()[1]);
     } else {
-        ASSERT_EQ(0U, svc->nr_supp_gids);
+        ASSERT_EQ(0U, svc->supp_gids().size());
     }
 
-    ASSERT_EQ(2, svc->nargs);
-    ASSERT_EQ("/system/bin/toybox", svc->args[0]);
-    ASSERT_EQ("id", svc->args[1]);
-    ASSERT_EQ(nullptr, svc->args[2]);
+    ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
+    ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
+    ASSERT_EQ("id", svc->args()[1]);
 }
 
 TEST(init_parser, make_exec_oneshot_service_with_everything) {
index 10d9573..7a7838d 100644 (file)
 #include "init.h"
 #include "log.h"
 #include "property_service.h"
+#include "service.h"
 
 static struct input_keychord *keychords = 0;
 static int keychords_count = 0;
 static int keychords_length = 0;
 static int keychord_fd = -1;
 
-void add_service_keycodes(struct service *svc)
+void add_service_keycodes(Service* svc)
 {
     struct input_keychord *keychord;
-    int i, size;
+    size_t i, size;
 
-    if (svc->keycodes) {
+    if (!svc->keycodes().empty()) {
         /* add a new keychord to the list */
-        size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]);
+        size = sizeof(*keychord) + svc->keycodes().size() * sizeof(keychord->keycodes[0]);
         keychords = (input_keychord*) realloc(keychords, keychords_length + size);
         if (!keychords) {
             ERROR("could not allocate keychords\n");
@@ -51,11 +52,11 @@ void add_service_keycodes(struct service *svc)
         keychord = (struct input_keychord *)((char *)keychords + keychords_length);
         keychord->version = KEYCHORD_VERSION;
         keychord->id = keychords_count + 1;
-        keychord->count = svc->nkeycodes;
-        svc->keychord_id = keychord->id;
+        keychord->count = svc->keycodes().size();
+        svc->set_keychord_id(keychord->id);
 
-        for (i = 0; i < svc->nkeycodes; i++) {
-            keychord->keycodes[i] = svc->keycodes[i];
+        for (i = 0; i < svc->keycodes().size(); i++) {
+            keychord->keycodes[i] = svc->keycodes()[i];
         }
         keychords_count++;
         keychords_length += size;
@@ -63,24 +64,22 @@ void add_service_keycodes(struct service *svc)
 }
 
 static void handle_keychord() {
-    struct service *svc;
-    char adb_enabled[PROP_VALUE_MAX];
     int ret;
     __u16 id;
 
-    // Only handle keychords if adb is enabled.
-    property_get("init.svc.adbd", adb_enabled);
     ret = read(keychord_fd, &id, sizeof(id));
     if (ret != sizeof(id)) {
         ERROR("could not read keychord id\n");
         return;
     }
 
-    if (!strcmp(adb_enabled, "running")) {
-        svc = service_find_by_keychord(id);
+    // Only handle keychords if adb is enabled.
+    std::string adb_enabled = property_get("init.svc.adbd");
+    if (adb_enabled == "running") {
+        Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
         if (svc) {
-            INFO("Starting service %s from keychord\n", svc->name);
-            service_start(svc, NULL);
+            INFO("Starting service %s from keychord\n", svc->name().c_str());
+            svc->Start();
         } else {
             ERROR("service for keychord %d not found\n", id);
         }
@@ -88,7 +87,7 @@ static void handle_keychord() {
 }
 
 void keychord_init() {
-    service_for_each(add_service_keycodes);
+    ServiceManager::GetInstance().ForEachService(add_service_keycodes);
 
     // Nothing to do if no services require keychords.
     if (!keychords) {
diff --git a/init/keyword_map.h b/init/keyword_map.h
new file mode 100644 (file)
index 0000000..dc2357b
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_KEYWORD_MAP_H_
+#define _INIT_KEYWORD_MAP_H_
+
+#include <map>
+#include <string>
+
+#include <base/stringprintf.h>
+
+template <typename Function>
+class KeywordMap {
+public:
+    using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
+    using Map = const std::map<std::string, FunctionInfo>;
+
+    virtual ~KeywordMap() {
+    }
+
+    const Function FindFunction(const std::string& keyword,
+                                size_t num_args,
+                                std::string* err) const {
+        using android::base::StringPrintf;
+
+        auto function_info_it = map().find(keyword);
+        if (function_info_it == map().end()) {
+            *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
+            return nullptr;
+        }
+
+        auto function_info = function_info_it->second;
+
+        auto min_args = std::get<0>(function_info);
+        auto max_args = std::get<1>(function_info);
+        if (min_args == max_args && num_args != min_args) {
+            *err = StringPrintf("%s requires %zu argument%s",
+                                keyword.c_str(), min_args,
+                                (min_args > 1 || min_args == 0) ? "s" : "");
+            return nullptr;
+        }
+
+        if (num_args < min_args || num_args > max_args) {
+            if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
+                *err = StringPrintf("%s requires at least %zu argument%s",
+                                    keyword.c_str(), min_args,
+                                    min_args > 1 ? "s" : "");
+            } else {
+                *err = StringPrintf("%s requires between %zu and %zu arguments",
+                                    keyword.c_str(), min_args, max_args);
+            }
+            return nullptr;
+        }
+
+        return std::get<Function>(function_info);
+    }
+
+private:
+//Map of keyword ->
+//(minimum number of arguments, maximum number of arguments, function pointer)
+    virtual Map& map() const = 0;
+};
+
+#endif
diff --git a/init/keywords.h b/init/keywords.h
deleted file mode 100644 (file)
index 0910f60..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-#ifndef KEYWORD
-int do_bootchart_init(int nargs, char **args);
-int do_class_start(int nargs, char **args);
-int do_class_stop(int nargs, char **args);
-int do_class_reset(int nargs, char **args);
-int do_domainname(int nargs, char **args);
-int do_enable(int nargs, char **args);
-int do_exec(int nargs, char **args);
-int do_export(int nargs, char **args);
-int do_hostname(int nargs, char **args);
-int do_ifup(int nargs, char **args);
-int do_insmod(int nargs, char **args);
-int do_installkey(int nargs, char **args);
-int do_mkdir(int nargs, char **args);
-int do_mount_all(int nargs, char **args);
-int do_mount(int nargs, char **args);
-int do_powerctl(int nargs, char **args);
-int do_restart(int nargs, char **args);
-int do_restorecon(int nargs, char **args);
-int do_restorecon_recursive(int nargs, char **args);
-int do_rm(int nargs, char **args);
-int do_rmdir(int nargs, char **args);
-int do_setprop(int nargs, char **args);
-int do_setrlimit(int nargs, char **args);
-int do_setusercryptopolicies(int nargs, char **args);
-int do_start(int nargs, char **args);
-int do_stop(int nargs, char **args);
-int do_swapon_all(int nargs, char **args);
-int do_trigger(int nargs, char **args);
-int do_symlink(int nargs, char **args);
-int do_sysclktz(int nargs, char **args);
-int do_write(int nargs, char **args);
-int do_copy(int nargs, char **args);
-int do_chown(int nargs, char **args);
-int do_chmod(int nargs, char **args);
-int do_loglevel(int nargs, char **args);
-int do_load_persist_props(int nargs, char **args);
-int do_load_system_props(int nargs, char **args);
-int do_verity_load_state(int nargs, char **args);
-int do_verity_update_state(int nargs, char **args);
-int do_wait(int nargs, char **args);
-#define __MAKE_KEYWORD_ENUM__
-#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
-enum {
-    K_UNKNOWN,
-#endif
-    KEYWORD(bootchart_init,        COMMAND, 0, do_bootchart_init)
-    KEYWORD(chmod,       COMMAND, 2, do_chmod)
-    KEYWORD(chown,       COMMAND, 2, do_chown)
-    KEYWORD(class,       OPTION,  0, 0)
-    KEYWORD(class_reset, COMMAND, 1, do_class_reset)
-    KEYWORD(class_start, COMMAND, 1, do_class_start)
-    KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
-    KEYWORD(console,     OPTION,  0, 0)
-    KEYWORD(copy,        COMMAND, 2, do_copy)
-    KEYWORD(critical,    OPTION,  0, 0)
-    KEYWORD(disabled,    OPTION,  0, 0)
-    KEYWORD(domainname,  COMMAND, 1, do_domainname)
-    KEYWORD(enable,      COMMAND, 1, do_enable)
-    KEYWORD(exec,        COMMAND, 1, do_exec)
-    KEYWORD(export,      COMMAND, 2, do_export)
-    KEYWORD(group,       OPTION,  0, 0)
-    KEYWORD(hostname,    COMMAND, 1, do_hostname)
-    KEYWORD(ifup,        COMMAND, 1, do_ifup)
-    KEYWORD(import,      SECTION, 1, 0)
-    KEYWORD(insmod,      COMMAND, 1, do_insmod)
-    KEYWORD(installkey,  COMMAND, 1, do_installkey)
-    KEYWORD(ioprio,      OPTION,  0, 0)
-    KEYWORD(keycodes,    OPTION,  0, 0)
-    KEYWORD(load_system_props,     COMMAND, 0, do_load_system_props)
-    KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)
-    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
-    KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
-    KEYWORD(mount_all,   COMMAND, 1, do_mount_all)
-    KEYWORD(mount,       COMMAND, 3, do_mount)
-    KEYWORD(oneshot,     OPTION,  0, 0)
-    KEYWORD(onrestart,   OPTION,  0, 0)
-    KEYWORD(on,          SECTION, 0, 0)
-    KEYWORD(powerctl,    COMMAND, 1, do_powerctl)
-    KEYWORD(restart,     COMMAND, 1, do_restart)
-    KEYWORD(restorecon,  COMMAND, 1, do_restorecon)
-    KEYWORD(restorecon_recursive,  COMMAND, 1, do_restorecon_recursive)
-    KEYWORD(rm,          COMMAND, 1, do_rm)
-    KEYWORD(rmdir,       COMMAND, 1, do_rmdir)
-    KEYWORD(seclabel,    OPTION,  0, 0)
-    KEYWORD(service,     SECTION, 0, 0)
-    KEYWORD(setenv,      OPTION,  2, 0)
-    KEYWORD(setprop,     COMMAND, 2, do_setprop)
-    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
-    KEYWORD(setusercryptopolicies,   COMMAND, 1, do_setusercryptopolicies)
-    KEYWORD(socket,      OPTION,  0, 0)
-    KEYWORD(start,       COMMAND, 1, do_start)
-    KEYWORD(stop,        COMMAND, 1, do_stop)
-    KEYWORD(swapon_all,  COMMAND, 1, do_swapon_all)
-    KEYWORD(symlink,     COMMAND, 1, do_symlink)
-    KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)
-    KEYWORD(trigger,     COMMAND, 1, do_trigger)
-    KEYWORD(user,        OPTION,  0, 0)
-    KEYWORD(verity_load_state,      COMMAND, 0, do_verity_load_state)
-    KEYWORD(verity_update_state,    COMMAND, 0, do_verity_update_state)
-    KEYWORD(wait,        COMMAND, 1, do_wait)
-    KEYWORD(write,       COMMAND, 2, do_write)
-    KEYWORD(writepid,    OPTION,  0, 0)
-#ifdef __MAKE_KEYWORD_ENUM__
-    KEYWORD_COUNT,
-};
-#undef __MAKE_KEYWORD_ENUM__
-#undef KEYWORD
-#endif
index d32f2da..eb5ec42 100644 (file)
  * limitations under the License.
  */
 
+#include "log.h"
+
 #include <stdlib.h>
 #include <string.h>
 #include <sys/uio.h>
 
 #include <selinux/selinux.h>
 
-#include "log.h"
+#include <base/stringprintf.h>
 
 static void init_klog_vwrite(int level, const char* fmt, va_list ap) {
     static const char* tag = basename(getprogname());
 
-    char prefix[64];
-    snprintf(prefix, sizeof(prefix), "<%d>%s: ", level, tag);
-
-    char msg[512];
-    vsnprintf(msg, sizeof(msg), fmt, ap);
+    // The kernel's printk buffer is only 1024 bytes.
+    // TODO: should we automatically break up long lines into multiple lines?
+    // Or we could log but with something like "..." at the end?
+    char buf[1024];
+    size_t prefix_size = snprintf(buf, sizeof(buf), "<%d>%s: ", level, tag);
+    size_t msg_size = vsnprintf(buf + prefix_size, sizeof(buf) - prefix_size, fmt, ap);
+    if (msg_size >= sizeof(buf) - prefix_size) {
+        msg_size = snprintf(buf + prefix_size, sizeof(buf) - prefix_size,
+                            "(%zu-byte message too long for printk)\n", msg_size);
+    }
 
-    iovec iov[2];
-    iov[0].iov_base = prefix;
-    iov[0].iov_len = strlen(prefix);
-    iov[1].iov_base = msg;
-    iov[1].iov_len = strlen(msg);
+    iovec iov[1];
+    iov[0].iov_base = buf;
+    iov[0].iov_len = prefix_size + msg_size;
 
-    klog_writev(level, iov, 2);
+    klog_writev(level, iov, 1);
 }
 
 void init_klog_write(int level, const char* fmt, ...) {
index b804d1f..c5c30af 100644 (file)
 #include <cutils/klog.h>
 
 #define ERROR(x...)   init_klog_write(KLOG_ERROR_LEVEL, x)
+#define WARNING(x...) init_klog_write(KLOG_WARNING_LEVEL, x)
 #define NOTICE(x...)  init_klog_write(KLOG_NOTICE_LEVEL, x)
 #define INFO(x...)    init_klog_write(KLOG_INFO_LEVEL, x)
+#define DEBUG(x...)   init_klog_write(KLOG_DEBUG_LEVEL, x)
+#define VERBOSE(x...) init_klog_write(KLOG_DEBUG_LEVEL, x)
 
 void init_klog_write(int level, const char* fmt, ...) __printflike(2, 3);
 int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
diff --git a/init/parser/tokenizer.cpp b/init/parser/tokenizer.cpp
new file mode 100644 (file)
index 0000000..340e0d9
--- /dev/null
@@ -0,0 +1,129 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "tokenizer.h"
+
+namespace init {
+
+Tokenizer::Tokenizer(const std::string& data)
+    : data_(data), eof_(false), pos_(0), tok_start_(0) {
+  current_.type = TOK_START;
+
+  if (data.size() > 0) {
+    cur_char_ = data[0];
+  } else {
+    eof_ = true;
+    cur_char_ = '\0';
+  }
+}
+
+Tokenizer::~Tokenizer() {}
+
+const Tokenizer::Token& Tokenizer::current() {
+  return current_;
+}
+
+bool Tokenizer::Next() {
+  while (!eof_) {
+    AdvWhiteSpace();
+
+    // Check for comments.
+    if (cur_char_ == '#') {
+      AdvChar();
+      // Skip rest of line
+      while (!eof_ && cur_char_ != '\n') {
+        AdvChar();
+      }
+    }
+
+    if (eof_) {
+      break;
+    }
+
+    if (cur_char_ == '\0') {
+      AdvChar();
+    } else if (cur_char_ == '\n') {
+      current_.type = TOK_NEWLINE;
+      current_.text.clear();
+      AdvChar();
+      return true;
+    } else if (cur_char_ == '\\') {
+      AdvChar();  // skip backslash
+      // This is line continuation so
+      // do not generated TOK_NEWLINE at
+      // the next \n.
+      AdvUntil('\n');
+      AdvChar();  // skip \n
+    } else if (cur_char_ == '\"') {
+      AdvChar();
+      StartText();
+      // Grab everything until the next quote.
+      AdvUntil('\"');
+      EndText();
+      AdvChar();  // skip quote.
+      return true;
+    } else {
+      StartText();
+      AdvText();
+      EndText();
+      return true;
+    }
+  }
+  current_.type = TOK_END;
+  current_.text.clear();
+  return false;
+}
+
+void Tokenizer::AdvChar() {
+  pos_++;
+  if (pos_ < data_.size()) {
+    cur_char_ = data_[pos_];
+  } else {
+    eof_ = true;
+    cur_char_ = '\0';
+  }
+}
+
+void Tokenizer::AdvWhiteSpace() {
+  while (cur_char_ == '\t' || cur_char_ == '\r' || cur_char_ == ' ') {
+    AdvChar();
+  }
+}
+
+void Tokenizer::AdvUntil(char x) {
+  while (!eof_ && cur_char_ != x) {
+    AdvChar();
+  }
+}
+
+void Tokenizer::AdvText() {
+  while (cur_char_ != '\t' && cur_char_ != '\r' && cur_char_ != '\0' &&
+         cur_char_ != ' ' && cur_char_ != '\n' && cur_char_ != '#') {
+    AdvChar();
+  }
+}
+
+void Tokenizer::StartText() {
+  current_.text.clear();
+  tok_start_ = pos_;
+  current_.type = TOK_TEXT;
+}
+
+void Tokenizer::EndText() {
+  if (pos_ != tok_start_) {
+    current_.text.append(data_, tok_start_, pos_ - tok_start_);
+  }
+}
+
+}  // namespace init
\ No newline at end of file
diff --git a/init/parser/tokenizer.h b/init/parser/tokenizer.h
new file mode 100644 (file)
index 0000000..8312a08
--- /dev/null
@@ -0,0 +1,74 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef _INIT_PARSER_TOKENIZER_H
+#define _INIT_PARSER_TOKENIZER_H
+
+#include <string>
+
+namespace init {
+
+// Used to tokenize a std::string.
+// Call Next() to advance through each token until it returns false,
+// indicating there are no more tokens left in the string.
+// The current token can be accessed with current(), which returns
+// a Token.
+// Supported tokens are:
+// TOK_START - Next() has yet to be called
+// TOK_END - At the end of string
+// TOK_NEWLINE - The end of a line denoted by \n.
+// TOK_TEXT - A word.
+// Comments are denoted with '#' and the tokenizer will ignore
+// the rest of the line.
+// Double quotes can be used to insert whitespace into words.
+// A backslash at the end of a line denotes continuation and
+// a TOK_NEWLINE will not be generated for that line.
+class Tokenizer {
+ public:
+  Tokenizer(const std::string& data);
+  ~Tokenizer();
+
+  enum TokenType { TOK_START, TOK_END, TOK_NEWLINE, TOK_TEXT };
+  struct Token {
+    TokenType type;
+    std::string text;
+  };
+
+  // Returns the curret token.
+  const Token& current();
+
+  // Move to the next token, returns false at the end of input.
+  bool Next();
+
+ private:
+  void GetData();
+  void AdvChar();
+  void AdvText();
+  void AdvUntil(char x);
+  void AdvWhiteSpace();
+  void StartText();
+  void EndText();
+
+  const std::string& data_;
+  Token current_;
+
+  bool eof_;
+  size_t pos_;
+  char cur_char_;
+  size_t tok_start_;
+};
+
+}  // namespace init
+
+#endif
diff --git a/init/parser/tokenizer_test.cpp b/init/parser/tokenizer_test.cpp
new file mode 100644 (file)
index 0000000..c4a48df
--- /dev/null
@@ -0,0 +1,230 @@
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "tokenizer.h"
+
+#include <errno.h>
+#include <gtest/gtest.h>
+
+#include <string>
+
+namespace init {
+
+#define SETUP_TEST(test_data)  \
+  std::string data(test_data); \
+  Tokenizer tokenizer(data);   \
+  ASSERT_EQ(Tokenizer::TOK_START, tokenizer.current().type)
+
+#define ASSERT_TEXT_TOKEN(test_text)              \
+  ASSERT_TRUE(tokenizer.Next());                  \
+  ASSERT_EQ(test_text, tokenizer.current().text); \
+  ASSERT_EQ(Tokenizer::TOK_TEXT, tokenizer.current().type)
+
+#define ASSERT_NEWLINE_TOKEN()   \
+  ASSERT_TRUE(tokenizer.Next()); \
+  ASSERT_EQ(Tokenizer::TOK_NEWLINE, tokenizer.current().type)
+
+TEST(Tokenizer, Empty) {
+  SETUP_TEST("");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, Simple) {
+  SETUP_TEST("test");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, LeadingWhiteSpace) {
+  SETUP_TEST(" \t  \r  test");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, TrailingWhiteSpace) {
+  SETUP_TEST("test \t  \r  ");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, WhiteSpace) {
+  SETUP_TEST(" \t  \r  test \t  \r  ");
+  ASSERT_TEXT_TOKEN("test");
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, TwoTokens) {
+  SETUP_TEST("  foo   bar ");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("bar");
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, MultiToken) {
+  SETUP_TEST("one two three four five");
+  ASSERT_TEXT_TOKEN("one");
+  ASSERT_TEXT_TOKEN("two");
+  ASSERT_TEXT_TOKEN("three");
+  ASSERT_TEXT_TOKEN("four");
+  ASSERT_TEXT_TOKEN("five");
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, NewLine) {
+  SETUP_TEST("\n");
+  ASSERT_NEWLINE_TOKEN();
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, TextNewLine) {
+  SETUP_TEST("test\n");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_NEWLINE_TOKEN();
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, MultiTextNewLine) {
+  SETUP_TEST("one\ntwo\nthree\n");
+  ASSERT_TEXT_TOKEN("one");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_TEXT_TOKEN("two");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_TEXT_TOKEN("three");
+  ASSERT_NEWLINE_TOKEN();
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, MultiTextNewLineNoLineEnding) {
+  SETUP_TEST("one\ntwo\nthree");
+  ASSERT_TEXT_TOKEN("one");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_TEXT_TOKEN("two");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_TEXT_TOKEN("three");
+
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, Comment) {
+  SETUP_TEST("#test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWhiteSpace) {
+  SETUP_TEST(" \t  \r  #test \t  \r  ");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentNewLine) {
+  SETUP_TEST(" #test   \n");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentTwoNewLine) {
+  SETUP_TEST(" #test   \n#test");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithText) {
+  SETUP_TEST("foo bar #test");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithTextNoSpace) {
+  SETUP_TEST("foo bar#test");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithTextLineFeed) {
+  SETUP_TEST("foo bar #test\n");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, CommentWithMultiTextLineFeed) {
+  SETUP_TEST("#blah\nfoo bar #test\n#blah");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_NEWLINE_TOKEN();
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, SimpleEscaped) {
+  SETUP_TEST("fo\\no bar");
+  ASSERT_TEXT_TOKEN("fo\\no");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineContNoLineFeed) {
+  SETUP_TEST("fo\\no bar \\ hello");
+  ASSERT_TEXT_TOKEN("fo\\no");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineContLineFeed) {
+  SETUP_TEST("fo\\no bar \\ hello\n");
+  ASSERT_TEXT_TOKEN("fo\\no");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineCont) {
+  SETUP_TEST("fo\\no bar \\\ntest");
+  ASSERT_TEXT_TOKEN("fo\\no");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, EscapedLineContWithBadChars) {
+  SETUP_TEST("fo\\no bar \\bad bad bad\ntest");
+  ASSERT_TEXT_TOKEN("fo\\no");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_TEXT_TOKEN("test");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, SimpleQuotes) {
+  SETUP_TEST("foo \"single token\" bar");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("single token");
+  ASSERT_TEXT_TOKEN("bar");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+TEST(Tokenizer, BadQuotes) {
+  SETUP_TEST("foo \"single token");
+  ASSERT_TEXT_TOKEN("foo");
+  ASSERT_TEXT_TOKEN("single token");
+  ASSERT_FALSE(tokenizer.Next());
+}
+
+}  // namespace init
diff --git a/init/perfboot.py b/init/perfboot.py
new file mode 100755 (executable)
index 0000000..91e6c2b
--- /dev/null
@@ -0,0 +1,462 @@
+#!/usr/bin/env python
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Record the event logs during boot and output them to a file.
+
+This script repeats the record of each event log during Android boot specified
+times. By default, interval between measurements is adjusted in such a way that
+CPUs are cooled down sufficiently to avoid boot time slowdown caused by CPU
+thermal throttling. The result is output in a tab-separated value format.
+
+Examples:
+
+Repeat measurements 10 times. Interval between iterations is adjusted based on
+CPU temperature of the device.
+
+$ ./perfboot.py --iterations=10
+
+Repeat measurements 20 times. 60 seconds interval is taken between each
+iteration.
+
+$ ./perfboot.py --iterations=20 --interval=60
+
+Repeat measurements 20 times, show verbose output, output the result to
+data.tsv, and read event tags from eventtags.txt.
+
+$ ./perfboot.py --iterations=30 -v --output=data.tsv --tags=eventtags.txt
+"""
+
+import argparse
+import atexit
+import cStringIO
+import glob
+import inspect
+import logging
+import math
+import os
+import re
+import subprocess
+import sys
+import threading
+import time
+
+sys.path.append(os.path.dirname(os.path.dirname(__file__)))
+import adb
+
+# The default event tags to record.
+_DEFAULT_EVENT_TAGS = [
+    'boot_progress_start',
+    'boot_progress_preload_start',
+    'boot_progress_preload_end',
+    'boot_progress_system_run',
+    'boot_progress_pms_start',
+    'boot_progress_pms_system_scan_start',
+    'boot_progress_pms_data_scan_start',
+    'boot_progress_pms_scan_end',
+    'boot_progress_pms_ready',
+    'boot_progress_ams_ready',
+    'boot_progress_enable_screen',
+    'sf_stop_bootanim',
+    'wm_boot_animation_done',
+]
+
+
+class IntervalAdjuster(object):
+    """A helper class to take suffficient interval between iterations."""
+
+    # CPU temperature values per product used to decide interval
+    _CPU_COOL_DOWN_THRESHOLDS = {
+        'flo': 40,
+        'flounder': 40000,
+        'razor': 40,
+        'volantis': 40000,
+    }
+    # The interval between CPU temperature checks
+    _CPU_COOL_DOWN_WAIT_INTERVAL = 10
+    # The wait time used when the value of _CPU_COOL_DOWN_THRESHOLDS for
+    # the product is not defined.
+    _CPU_COOL_DOWN_WAIT_TIME_DEFAULT = 120
+
+    def __init__(self, interval, device):
+        self._interval = interval
+        self._device = device
+        self._temp_paths = device.shell(
+            ['ls', '/sys/class/thermal/thermal_zone*/temp'])[0].splitlines()
+        self._product = device.get_prop('ro.build.product')
+        self._waited = False
+
+    def wait(self):
+        """Waits certain amount of time for CPUs cool-down."""
+        if self._interval is None:
+            self._wait_cpu_cool_down(self._product, self._temp_paths)
+        else:
+            if self._waited:
+                print 'Waiting for %d seconds' % self._interval
+                time.sleep(self._interval)
+        self._waited = True
+
+    def _get_cpu_temp(self, threshold):
+        max_temp = 0
+        for temp_path in self._temp_paths:
+            temp = int(self._device.shell(['cat', temp_path])[0].rstrip())
+            max_temp = max(max_temp, temp)
+            if temp >= threshold:
+                return temp
+        return max_temp
+
+    def _wait_cpu_cool_down(self, product, temp_paths):
+        threshold = IntervalAdjuster._CPU_COOL_DOWN_THRESHOLDS.get(
+            self._product)
+        if threshold is None:
+            print 'No CPU temperature threshold is set for ' + self._product
+            print ('Just wait %d seconds' %
+                   IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)
+            time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)
+            return
+        while True:
+            temp = self._get_cpu_temp(threshold)
+            if temp < threshold:
+                logging.info('Current CPU temperature %s' % temp)
+                return
+            print 'Waiting until CPU temperature (%d) falls below %d' % (
+                temp, threshold)
+            time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_INTERVAL)
+
+
+class WatchdogTimer(object):
+    """A timer that makes is_timedout() return true in |timeout| seconds."""
+    def __init__(self, timeout):
+        self._timedout = False
+
+        def notify_timeout():
+            self._timedout = True
+        self._timer = threading.Timer(timeout, notify_timeout)
+        self._timer.daemon = True
+        self._timer.start()
+
+    def is_timedout(self):
+        return self._timedout
+
+    def cancel(self):
+        self._timer.cancel()
+
+
+def readlines_unbuffered(proc):
+    """Read lines from |proc|'s standard out without buffering."""
+    while True:
+        buf = []
+        c = proc.stdout.read(1)
+        if c == '' and proc.poll() is not None:
+            break
+        while c != '\n':
+            if c == '' and proc.poll() is not None:
+                break
+            buf.append(c)
+            c = proc.stdout.read(1)
+        yield ''.join(buf)
+
+
+def disable_dropbox(device):
+    """Removes the files created by Dropbox and avoids creating the files."""
+    device.root()
+    device.wait()
+    device.shell(['rm', '-rf', '/system/data/dropbox'])
+    original_dropbox_max_files = device.shell(
+        ['settings', 'get', 'global', 'dropbox_max_files'])[0].rstrip()
+    device.shell(['settings', 'put', 'global', 'dropbox_max_files', '0'])
+    return original_dropbox_max_files
+
+
+def restore_dropbox(device, original_dropbox_max_files):
+    """Restores the dropbox_max_files setting."""
+    device.root()
+    device.wait()
+    if original_dropbox_max_files == 'null':
+        device.shell(['settings', 'delete', 'global', 'dropbox_max_files'])
+    else:
+        device.shell(['settings', 'put', 'global', 'dropbox_max_files',
+                      original_dropbox_max_files])
+
+
+def init_perf(device, output, record_list, tags):
+    device.wait()
+    build_type = device.get_prop('ro.build.type')
+    original_dropbox_max_files = None
+    if build_type != 'user':
+        # Workaround for Dropbox issue (http://b/20890386).
+        original_dropbox_max_files = disable_dropbox(device)
+
+    def cleanup():
+        try:
+            if record_list:
+                print_summary(record_list, tags[-1])
+                output_results(output, record_list, tags)
+            if original_dropbox_max_files is not None:
+                restore_dropbox(device, original_dropbox_max_files)
+        except (subprocess.CalledProcessError, RuntimeError):
+            pass
+    atexit.register(cleanup)
+
+
+def check_dm_verity_settings(device):
+    device.wait()
+    for partition in ['system', 'vendor']:
+        verity_mode = device.get_prop('partition.%s.verified' % partition)
+        if verity_mode is None:
+            logging.warning('dm-verity is not enabled for /%s. Did you run '
+                            'adb disable-verity? That may skew the result.',
+                            partition)
+
+
+def read_event_tags(tags_file):
+    """Reads event tags from |tags_file|."""
+    if not tags_file:
+        return _DEFAULT_EVENT_TAGS
+    tags = []
+    with open(tags_file) as f:
+        for line in f:
+            if '#' in line:
+                line = line[:line.find('#')]
+            line = line.strip()
+            if line:
+                tags.append(line)
+    return tags
+
+
+def make_event_tags_re(tags):
+    """Makes a regular expression object that matches event logs of |tags|."""
+    return re.compile(r'(?P<pid>[0-9]+) +[0-9]+ I (?P<tag>%s): (?P<time>\d+)' %
+                      '|'.join(tags))
+
+
+def filter_event_tags(tags, device):
+    """Drop unknown tags not listed in device's event-log-tags file."""
+    device.wait()
+    supported_tags = set()
+    for l in device.shell(
+        ['cat', '/system/etc/event-log-tags'])[0].splitlines():
+        tokens = l.split(' ')
+        if len(tokens) >= 2:
+            supported_tags.add(tokens[1])
+    filtered = []
+    for tag in tags:
+        if tag in supported_tags:
+            filtered.append(tag)
+        else:
+            logging.warning('Unknown tag \'%s\'. Ignoring...', tag)
+    return filtered
+
+
+def get_values(record, tag):
+    """Gets values that matches |tag| from |record|."""
+    keys = [key for key in record.keys() if key[0] == tag]
+    return [record[k] for k in sorted(keys)]
+
+
+def get_last_value(record, tag):
+    """Gets the last value that matches |tag| from |record|."""
+    values = get_values(record, tag)
+    if not values:
+        return 0
+    return values[-1]
+
+
+def output_results(filename, record_list, tags):
+    """Outputs |record_list| into |filename| in a TSV format."""
+    # First, count the number of the values of each tag.
+    # This is for dealing with events that occur multiple times.
+    # For instance, boot_progress_preload_start and boot_progress_preload_end
+    # are recorded twice on 64-bit system. One is for 64-bit zygote process
+    # and the other is for 32-bit zygote process.
+    values_counter = {}
+    for record in record_list:
+        for tag in tags:
+            # Some record might lack values for some tags due to unanticipated
+            # problems (e.g. timeout), so take the maximum count among all the
+            # record.
+            values_counter[tag] = max(values_counter.get(tag, 1),
+                                      len(get_values(record, tag)))
+
+    # Then creates labels for the data. If there are multiple values for one
+    # tag, labels for these values are numbered except the first one as
+    # follows:
+    #
+    # event_tag event_tag2 event_tag3
+    #
+    # The corresponding values are sorted in an ascending order of PID.
+    labels = []
+    for tag in tags:
+        for i in range(1, values_counter[tag] + 1):
+            labels.append('%s%s' % (tag, '' if i == 1 else str(i)))
+
+    # Finally write the data into the file.
+    with open(filename, 'w') as f:
+        f.write('\t'.join(labels) + '\n')
+        for record in record_list:
+            line = cStringIO.StringIO()
+            invalid_line = False
+            for i, tag in enumerate(tags):
+                if i != 0:
+                    line.write('\t')
+                values = get_values(record, tag)
+                if len(values) < values_counter[tag]:
+                    invalid_line = True
+                    # Fill invalid record with 0
+                    values += [0] * (values_counter[tag] - len(values))
+                line.write('\t'.join(str(t) for t in values))
+            if invalid_line:
+                logging.error('Invalid record found: ' + line.getvalue())
+            line.write('\n')
+            f.write(line.getvalue())
+    print 'Wrote: ' + filename
+
+
+def median(data):
+    """Calculates the median value from |data|."""
+    data = sorted(data)
+    n = len(data)
+    if n % 2 == 1:
+        return data[n / 2]
+    else:
+        n2 = n / 2
+        return (data[n2 - 1] + data[n2]) / 2.0
+
+
+def mean(data):
+    """Calculates the mean value from |data|."""
+    return float(sum(data)) / len(data)
+
+
+def stddev(data):
+    """Calculates the standard deviation value from |value|."""
+    m = mean(data)
+    return math.sqrt(sum((x - m) ** 2 for x in data) / len(data))
+
+
+def print_summary(record_list, end_tag):
+    """Prints the summary of |record_list|."""
+    # Filter out invalid data.
+    end_times = [get_last_value(record, end_tag) for record in record_list
+                 if get_last_value(record, end_tag) != 0]
+    print 'mean: ', mean(end_times)
+    print 'median:', median(end_times)
+    print 'standard deviation:', stddev(end_times)
+
+
+def do_iteration(device, interval_adjuster, event_tags_re, end_tag):
+    """Measures the boot time once."""
+    device.wait()
+    interval_adjuster.wait()
+    device.reboot()
+    print 'Rebooted the device'
+    record = {}
+    booted = False
+    while not booted:
+        device.wait()
+        # Stop the iteration if it does not finish within 120 seconds.
+        timeout = 120
+        t = WatchdogTimer(timeout)
+        p = subprocess.Popen(
+                ['adb', 'logcat', '-b', 'events', '-v', 'threadtime'],
+                stdout=subprocess.PIPE)
+        for line in readlines_unbuffered(p):
+            if t.is_timedout():
+                print '*** Timed out ***'
+                return record
+            m = event_tags_re.search(line)
+            if not m:
+                continue
+            tag = m.group('tag')
+            event_time = int(m.group('time'))
+            pid = m.group('pid')
+            record[(tag, pid)] = event_time
+            print 'Event log recorded: %s (%s) - %d ms' % (
+                tag, pid, event_time)
+            if tag == end_tag:
+                booted = True
+                t.cancel()
+                break
+    return record
+
+
+def parse_args():
+    """Parses the command line arguments."""
+    parser = argparse.ArgumentParser(
+        description=inspect.getdoc(sys.modules[__name__]),
+        formatter_class=argparse.RawDescriptionHelpFormatter)
+    parser.add_argument('--iterations', type=int, default=5,
+                        help='Number of times to repeat boot measurements.')
+    parser.add_argument('--interval', type=int,
+                        help=('Duration between iterations. If this is not '
+                              'set explicitly, durations are determined '
+                              'adaptively based on CPUs temperature.'))
+    parser.add_argument('-o', '--output', help='File name of output data.')
+    parser.add_argument('-v', '--verbose', action='store_true',
+                        help='Show verbose output.')
+    parser.add_argument('-s', '--serial', default=os.getenv('ANDROID_SERIAL'),
+                        help='Adb device serial number.')
+    parser.add_argument('-t', '--tags', help='Specify the filename from which '
+                        'event tags are read. Every line contains one event '
+                        'tag and the last event tag is used to detect that '
+                        'the device has finished booting unless --end-tag is '
+                        'specified.')
+    parser.add_argument('--end-tag', help='An event tag on which the script '
+                        'stops measuring the boot time.')
+    parser.add_argument('--apk-dir', help='Specify the directory which contains '
+                        'APK files to be installed before measuring boot time.')
+    return parser.parse_args()
+
+
+def install_apks(device, apk_dir):
+    for apk in glob.glob(os.path.join(apk_dir, '*.apk')):
+        print 'Installing: ' + apk
+        device.install(apk, replace=True)
+
+
+def main():
+    args = parse_args()
+    if args.verbose:
+        logging.getLogger().setLevel(logging.INFO)
+
+    device = adb.get_device(args.serial)
+
+    if not args.output:
+        device.wait()
+        args.output = 'perf-%s-%s.tsv' % (
+            device.get_prop('ro.build.flavor'),
+            device.get_prop('ro.build.version.incremental'))
+    check_dm_verity_settings(device)
+
+    if args.apk_dir:
+        install_apks(device, args.apk_dir)
+
+    record_list = []
+    event_tags = filter_event_tags(read_event_tags(args.tags), device)
+    end_tag = args.end_tag or event_tags[-1]
+    if end_tag not in event_tags:
+        sys.exit('%s is not a valid tag.' % end_tag)
+    event_tags = event_tags[0 : event_tags.index(end_tag) + 1]
+    init_perf(device, args.output, record_list, event_tags)
+    interval_adjuster = IntervalAdjuster(args.interval, device)
+    event_tags_re = make_event_tags_re(event_tags)
+
+    for i in range(args.iterations):
+        print 'Run #%d ' % i
+        record = do_iteration(
+            device, interval_adjuster, event_tags_re, end_tag)
+        record_list.append(record)
+
+
+if __name__ == '__main__':
+    main()
index 52f6b98..96b4a37 100644 (file)
@@ -90,13 +90,11 @@ void property_init() {
     }
 }
 
-static int check_mac_perms(const char *name, char *sctx)
+static int check_mac_perms(const char *name, char *sctx, struct ucred *cr)
 {
-    if (is_selinux_enabled() <= 0)
-        return 1;
-
     char *tctx = NULL;
     int result = 0;
+    property_audit_data audit_data;
 
     if (!sctx)
         goto err;
@@ -107,7 +105,10 @@ static int check_mac_perms(const char *name, char *sctx)
     if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
         goto err;
 
-    if (selinux_check_access(sctx, tctx, "property_service", "set", (void*) name) == 0)
+    audit_data.name = name;
+    audit_data.cr = cr;
+
+    if (selinux_check_access(sctx, tctx, "property_service", "set", reinterpret_cast<void*>(&audit_data)) == 0)
         result = 1;
 
     freecon(tctx);
@@ -115,7 +116,7 @@ static int check_mac_perms(const char *name, char *sctx)
     return result;
 }
 
-static int check_control_mac_perms(const char *name, char *sctx)
+static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
 {
     /*
      *  Create a name prefix out of ctl.<service name>
@@ -129,24 +130,25 @@ static int check_control_mac_perms(const char *name, char *sctx)
     if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
         return 0;
 
-    return check_mac_perms(ctl_name, sctx);
+    return check_mac_perms(ctl_name, sctx, cr);
 }
 
 /*
  * Checks permissions for setting system properties.
  * Returns 1 if uid allowed, 0 otherwise.
  */
-static int check_perms(const char *name, char *sctx)
+static int check_perms(const char *name, char *sctx, struct ucred *cr)
 {
     if(!strncmp(name, "ro.", 3))
         name +=3;
 
-    return check_mac_perms(name, sctx);
+    return check_mac_perms(name, sctx, cr);
 }
 
-int __property_get(const char *name, char *value)
-{
-    return __system_property_get(name, value);
+std::string property_get(const char* name) {
+    char value[PROP_VALUE_MAX] = {0};
+    __system_property_get(name, value);
+    return value;
 }
 
 static void write_persistent_property(const char *name, const char *value)
@@ -323,14 +325,14 @@ static void handle_property_set_fd()
             // Keep the old close-socket-early behavior when handling
             // ctl.* properties.
             close(s);
-            if (check_control_mac_perms(msg.value, source_ctx)) {
+            if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
                 handle_control_message((char*) msg.name + 4, (char*) msg.value);
             } else {
                 ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
                         msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
             }
         } else {
-            if (check_perms(msg.name, source_ctx)) {
+            if (check_perms(msg.name, source_ctx, &cr)) {
                 property_set((char*) msg.name, (char*) msg.value);
             } else {
                 ERROR("sys_prop: permission denied uid:%d  name:%s\n",
@@ -501,9 +503,8 @@ bool properties_initialized() {
 
 static void load_override_properties() {
     if (ALLOW_LOCAL_PROP_OVERRIDE) {
-        char debuggable[PROP_VALUE_MAX];
-        int ret = property_get("ro.debuggable", debuggable);
-        if (ret && (strcmp(debuggable, "1") == 0)) {
+        std::string debuggable = property_get("ro.debuggable");
+        if (debuggable == "1") {
             load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL);
         }
     }
@@ -521,19 +522,17 @@ void load_persist_props(void) {
 }
 
 void load_recovery_id_prop() {
-    char fstab_filename[PROP_VALUE_MAX + sizeof(FSTAB_PREFIX)];
-    char propbuf[PROP_VALUE_MAX];
-    int ret = property_get("ro.hardware", propbuf);
-    if (!ret) {
+    std::string ro_hardware = property_get("ro.hardware");
+    if (ro_hardware.empty()) {
         ERROR("ro.hardware not set - unable to load recovery id\n");
         return;
     }
-    snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX "%s", propbuf);
+    std::string fstab_filename = FSTAB_PREFIX + ro_hardware;
 
-    std::unique_ptr<fstab, void(*)(fstab*)> tab(fs_mgr_read_fstab(fstab_filename),
+    std::unique_ptr<fstab, void(*)(fstab*)> tab(fs_mgr_read_fstab(fstab_filename.c_str()),
             fs_mgr_free_fstab);
     if (!tab) {
-        ERROR("unable to read fstab %s: %s\n", fstab_filename, strerror(errno));
+        ERROR("unable to read fstab %s: %s\n", fstab_filename.c_str(), strerror(errno));
         return;
     }
 
index 303f251..8b76d2b 100644 (file)
 #define _INIT_PROPERTY_H
 
 #include <stddef.h>
+#include <sys/socket.h>
 #include <sys/system_properties.h>
+#include <string>
+
+struct property_audit_data {
+    ucred *cr;
+    const char* name;
+};
 
 extern void property_init(void);
 extern void property_load_boot_defaults(void);
@@ -26,30 +33,9 @@ extern void load_persist_props(void);
 extern void load_system_props(void);
 extern void start_property_service(void);
 void get_property_workspace(int *fd, int *sz);
-extern int __property_get(const char *name, char *value);
+std::string property_get(const char* name);
 extern int property_set(const char *name, const char *value);
 extern bool properties_initialized();
 
-#ifndef __clang__
-extern void __property_get_size_error()
-    __attribute__((__error__("property_get called with too small buffer")));
-#else
-extern void __property_get_size_error();
-#endif
-
-static inline
-__attribute__ ((always_inline))
-__attribute__ ((gnu_inline))
-#ifndef __clang__
-__attribute__ ((artificial))
-#endif
-int property_get(const char *name, char *value)
-{
-    size_t value_len = __builtin_object_size(value, 0);
-    if (value_len != PROP_VALUE_MAX)
-        __property_get_size_error();
-
-    return __property_get(name, value);
-}
 
 #endif /* _INIT_PROPERTY_H */
index 9e3394e..bf440c2 100644 (file)
@@ -2,8 +2,8 @@
 Android Init Language
 ---------------------
 
-The Android Init Language consists of four broad classes of statements,
-which are Actions, Commands, Services, and Options.
+The Android Init Language consists of five broad classes of statements,
+which are Actions, Commands, Services, Options, and Imports.
 
 All of these are line-oriented, consisting of tokens separated by
 whitespace.  The c-style backslash escapes may be used to insert
@@ -17,9 +17,37 @@ Actions and Services implicitly declare a new section.  All commands
 or options belong to the section most recently declared.  Commands
 or options before the first section are ignored.
 
-Actions and Services have unique names.  If a second Action or Service
-is declared with the same name as an existing one, it is ignored as
-an error.  (??? should we override instead)
+Actions and Services have unique names.  If a second Action is defined
+with the same name as an existing one, its commands are appended to
+the commands of the existing action.  If a second Service is defined
+with the same name as an existing one, it is ignored and an error
+message is logged.
+
+
+Init .rc Files
+--------------
+The init language is used in plaintext files that take the .rc file
+extension.  These are typically multiple of these in multiple
+locations on the system, described below.
+
+/init.rc is the primary .rc file and is loaded by the init executable
+at the beginning of its execution.  It is responsible for the initial
+set up of the system.  It imports /init.${ro.hardware}.rc which is the
+primary vendor supplied .rc file.
+
+During the mount_all command, the init executable loads all of the
+files contained within the /{system,vendor,odm}/etc/init/ directories.
+These directories are intended for all Actions and Services used after
+file system mounting.
+
+The intention of these directories is as follows
+   1) /system/etc/init/ is for core system items such as
+      SurfaceFlinger and MediaService.
+   2) /vendor/etc/init/ is for SoC vendor items such as actions or
+      daemons needed for core SoC functionality.
+   3) /odm/etc/init/ is for device manufacturer items such as
+      actions or daemons needed for motion sensor or other peripheral
+      functionality.
 
 
 Actions
@@ -37,7 +65,7 @@ that action is executed in sequence.  Init handles other activities
 
 Actions take the form of:
 
-on <trigger>
+on <trigger> [&& <trigger>]*
    <command>
    <command>
    <command>
@@ -117,25 +145,36 @@ writepid <file...>
 
 Triggers
 --------
-Triggers are strings which can be used to match certain kinds
-of events and used to cause an action to occur.
+Triggers are strings which can be used to match certain kinds of
+events and used to cause an action to occur.
+
+Triggers are subdivided into event triggers and property triggers.
 
-boot
-   This is the first trigger that will occur when init starts
-   (after /init.conf is loaded)
+Event triggers are strings triggered by the 'trigger' command or by
+the QueueEventTrigger() function within the init executable.  These
+take the form of a simple string such as 'boot' or 'late-init'.
 
-<name>=<value>
-   Triggers of this form occur when the property <name> is set
-   to the specific value <value>.
+Property triggers are strings triggered when a named property changes
+value to a given new value or when a named property changes value to
+any new value.  These take the form of 'property:<name>=<value>' and
+'property:<name>=*' respectively.  Property triggers are additionally
+evaluated and triggered accordingly during the initial boot phase of
+init.
 
-   One can also test multiple properties to execute a group
-   of commands. For example:
+An Action can have multiple property triggers but may only have one
+event trigger.
 
-   on property:test.a=1 && property:test.b=1
-       setprop test.c 1
+For example:
+'on boot && property:a=b' defines an action that is only executed when
+the 'boot' event trigger happens and the property a equals b.
 
-   The above stub sets test.c to 1 only when
-   both test.a=1 and test.b=1
+'on property:a=b && property:c=d' defines an action that is executed
+at three times,
+   1) During initial boot if property a=b and property c=d
+   2) Any time that property a transitions to value b, while property
+      c already equals d.
+   3) Any time that property c transitions to value d, while property
+      a already equals b.
 
 
 Commands
@@ -197,9 +236,6 @@ hostname <name>
 ifup <interface>
    Bring the network interface <interface> online.
 
-import <filename>
-   Parse an init config file, extending the current configuration.
-
 insmod <path>
    Install the module at <path>
 
@@ -301,19 +337,30 @@ write <path> <content>
    it will be truncated. Properties are expanded within <content>.
 
 
-Properties
-----------
-Init updates some system properties to provide some insight into
-what it's doing:
+Imports
+-------
+The import keyword is not a command, but rather its own section and is
+handled immediately after the .rc file that contains it has finished
+being parsed.  It takes the below form:
+
+import <path>
+   Parse an init config file, extending the current configuration.
+   If <path> is a directory, each file in the directory is parsed as
+   a config file. It is not recursive, nested directories will
+   not be parsed.
 
-init.action
-   Equal to the name of the action currently being executed or "" if none
+There are only two times where the init executable imports .rc files,
+   1) When it imports /init.rc during initial boot
+   2) When it imports /{system,vendor,odm}/etc/init/ during mount_all
 
-init.command
-   Equal to the command being executed or "" if none.
+
+Properties
+----------
+Init provides information about the services that it is responsible
+for via the below properties.
 
 init.svc.<name>
-   State of a named service ("stopped", "running", "restarting")
+   State of a named service ("stopped", "stopping", "running", "restarting")
 
 
 Bootcharting
@@ -351,6 +398,52 @@ running at 0s. You'll have to look at dmesg to work out when the kernel
 actually started init.
 
 
+Comparing two bootcharts
+------------------------
+A handy script named compare-bootcharts.py can be used to compare the
+start/end time of selected processes. The aforementioned grab-bootchart.sh
+will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
+If two such barballs are preserved on the host machine under different
+directories, the script can list the timestamps differences. For example:
+
+Usage: system/core/init/compare-bootcharts.py base_bootchart_dir
+       exp_bootchart_dir
+
+process: baseline experiment (delta)
+ - Unit is ms (a jiffy is 10 ms on the system)
+------------------------------------
+/init: 50 40 (-10)
+/system/bin/surfaceflinger: 4320 4470 (+150)
+/system/bin/bootanimation: 6980 6990 (+10)
+zygote64: 10410 10640 (+230)
+zygote: 10410 10640 (+230)
+system_server: 15350 15150 (-200)
+bootanimation ends at: 33790 31230 (-2560)
+
+
+Systrace
+--------
+Systrace [1] can be used for obtaining performance analysis reports during boot
+time on userdebug or eng builds.
+Here is an example of trace events of "wm" and "am" categories:
+
+  $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py wm am --boot
+
+This command will cause the device to reboot. After the device is rebooted and
+the boot sequence has finished, the trace report is obtained from the device
+and written as trace.html on the host by hitting Ctrl+C.
+
+LIMITATION
+Recording trace events is started after persistent properties are loaded, so
+the trace events that are emitted before that are not recorded. Several
+services such as vold, surfaceflinger, and servicemanager are affected by this
+limitation since they are started before persistent properties are loaded.
+Zygote initialization and the processes that are forked from the zygote are not
+affected.
+
+[1] http://developer.android.com/tools/help/systrace.html
+
+
 Debugging init
 --------------
 By default, programs executed by init will drop stdout and stderr into
diff --git a/init/service.cpp b/init/service.cpp
new file mode 100644 (file)
index 0000000..a3c5ca4
--- /dev/null
@@ -0,0 +1,822 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "service.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <selinux/selinux.h>
+
+#include <base/file.h>
+#include <base/stringprintf.h>
+#include <cutils/android_reboot.h>
+#include <cutils/sockets.h>
+
+#include "action.h"
+#include "init.h"
+#include "init_parser.h"
+#include "log.h"
+#include "property_service.h"
+#include "util.h"
+
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+
+#define CRITICAL_CRASH_THRESHOLD    4       // if we crash >4 times ...
+#define CRITICAL_CRASH_WINDOW       (4*60)  // ... in 4 minutes, goto recovery
+
+SocketInfo::SocketInfo() : uid(0), gid(0), perm(0) {
+}
+
+SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+                       gid_t gid, int perm, const std::string& socketcon)
+    : name(name), type(type), uid(uid), gid(gid), perm(perm), socketcon(socketcon) {
+}
+
+ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
+}
+
+ServiceEnvironmentInfo::ServiceEnvironmentInfo(const std::string& name,
+                                               const std::string& value)
+    : name(name), value(value) {
+}
+
+Service::Service(const std::string& name, const std::string& classname,
+                 const std::vector<std::string>& args)
+    : name_(name), classname_(classname), flags_(0), pid_(0), time_started_(0),
+      time_crashed_(0), nr_crashed_(0), uid_(0), gid_(0), seclabel_(""),
+      ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+    onrestart_.InitSingleTrigger("onrestart");
+}
+
+Service::Service(const std::string& name, const std::string& classname,
+                 unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
+                 const std::string& seclabel,  const std::vector<std::string>& args)
+    : name_(name), classname_(classname), flags_(flags), pid_(0), time_started_(0),
+      time_crashed_(0), nr_crashed_(0), uid_(uid), gid_(gid), supp_gids_(supp_gids),
+      seclabel_(seclabel), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), args_(args) {
+    onrestart_.InitSingleTrigger("onrestart");
+}
+
+void Service::NotifyStateChange(const std::string& new_state) const {
+    if (!properties_initialized()) {
+        // If properties aren't available yet, we can't set them.
+        return;
+    }
+
+    if ((flags_ & SVC_EXEC) != 0) {
+        // 'exec' commands don't have properties tracking their state.
+        return;
+    }
+
+    std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
+    if (prop_name.length() >= PROP_NAME_MAX) {
+        // If the property name would be too long, we can't set it.
+        ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n",
+              name_.c_str(), new_state.c_str());
+        return;
+    }
+
+    property_set(prop_name.c_str(), new_state.c_str());
+}
+
+bool Service::Reap() {
+    if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
+        NOTICE("Service '%s' (pid %d) killing any children in process group\n",
+               name_.c_str(), pid_);
+        kill(-pid_, SIGKILL);
+    }
+
+    // Remove any sockets we may have created.
+    for (const auto& si : sockets_) {
+        std::string tmp = StringPrintf(ANDROID_SOCKET_DIR "/%s", si.name.c_str());
+        unlink(tmp.c_str());
+    }
+
+    if (flags_ & SVC_EXEC) {
+        INFO("SVC_EXEC pid %d finished...\n", pid_);
+        return true;
+    }
+
+    pid_ = 0;
+    flags_ &= (~SVC_RUNNING);
+
+    // Oneshot processes go into the disabled state on exit,
+    // except when manually restarted.
+    if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) {
+        flags_ |= SVC_DISABLED;
+    }
+
+    // Disabled and reset processes do not get restarted automatically.
+    if (flags_ & (SVC_DISABLED | SVC_RESET))  {
+        NotifyStateChange("stopped");
+        return false;
+    }
+
+    time_t now = gettime();
+    if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
+        if (time_crashed_ + CRITICAL_CRASH_WINDOW >= now) {
+            if (++nr_crashed_ > CRITICAL_CRASH_THRESHOLD) {
+                ERROR("critical process '%s' exited %d times in %d minutes; "
+                      "rebooting into recovery mode\n", name_.c_str(),
+                      CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
+                android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
+                return false;
+            }
+        } else {
+            time_crashed_ = now;
+            nr_crashed_ = 1;
+        }
+    }
+
+    flags_ &= (~SVC_RESTART);
+    flags_ |= SVC_RESTARTING;
+
+    // Execute all onrestart commands for this service.
+    onrestart_.ExecuteAllCommands();
+
+    NotifyStateChange("restarting");
+    return false;
+}
+
+void Service::DumpState() const {
+    INFO("service %s\n", name_.c_str());
+    INFO("  class '%s'\n", classname_.c_str());
+    INFO("  exec");
+    for (const auto& s : args_) {
+        INFO(" '%s'", s.c_str());
+    }
+    INFO("\n");
+    for (const auto& si : sockets_) {
+        INFO("  socket %s %s 0%o\n", si.name.c_str(), si.type.c_str(), si.perm);
+    }
+}
+
+bool Service::HandleClass(const std::vector<std::string>& args, std::string* err) {
+    classname_ = args[1];
+    return true;
+}
+
+bool Service::HandleConsole(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_CONSOLE;
+    return true;
+}
+
+bool Service::HandleCritical(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_CRITICAL;
+    return true;
+}
+
+bool Service::HandleDisabled(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_DISABLED;
+    flags_ |= SVC_RC_DISABLED;
+    return true;
+}
+
+bool Service::HandleGroup(const std::vector<std::string>& args, std::string* err) {
+    gid_ = decode_uid(args[1].c_str());
+    for (std::size_t n = 2; n < args.size(); n++) {
+        supp_gids_.emplace_back(decode_uid(args[n].c_str()));
+    }
+    return true;
+}
+
+bool Service::HandleIoprio(const std::vector<std::string>& args, std::string* err) {
+    ioprio_pri_ = std::stoul(args[2], 0, 8);
+
+    if (ioprio_pri_ < 0 || ioprio_pri_ > 7) {
+        *err = "priority value must be range 0 - 7";
+        return false;
+    }
+
+    if (args[1] == "rt") {
+        ioprio_class_ = IoSchedClass_RT;
+    } else if (args[1] == "be") {
+        ioprio_class_ = IoSchedClass_BE;
+    } else if (args[1] == "idle") {
+        ioprio_class_ = IoSchedClass_IDLE;
+    } else {
+        *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
+        return false;
+    }
+
+    return true;
+}
+
+bool Service::HandleKeycodes(const std::vector<std::string>& args, std::string* err) {
+    for (std::size_t i = 1; i < args.size(); i++) {
+        keycodes_.emplace_back(std::stoi(args[i]));
+    }
+    return true;
+}
+
+bool Service::HandleOneshot(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_ONESHOT;
+    return true;
+}
+
+bool Service::HandleOnrestart(const std::vector<std::string>& args, std::string* err) {
+    std::vector<std::string> str_args(args.begin() + 1, args.end());
+    onrestart_.AddCommand(str_args, "", 0, err);
+    return true;
+}
+
+bool Service::HandleSeclabel(const std::vector<std::string>& args, std::string* err) {
+    seclabel_ = args[1];
+    return true;
+}
+
+bool Service::HandleSetenv(const std::vector<std::string>& args, std::string* err) {
+    envvars_.emplace_back(args[1], args[2]);
+    return true;
+}
+
+/* name type perm [ uid gid context ] */
+bool Service::HandleSocket(const std::vector<std::string>& args, std::string* err) {
+    if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
+        *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
+        return false;
+    }
+
+    int perm = std::stoul(args[3], 0, 8);
+    uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
+    gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+    std::string socketcon = args.size() > 6 ? args[6] : "";
+
+    sockets_.emplace_back(args[1], args[2], uid, gid, perm, socketcon);
+    return true;
+}
+
+bool Service::HandleUser(const std::vector<std::string>& args, std::string* err) {
+    uid_ = decode_uid(args[1].c_str());
+    return true;
+}
+
+bool Service::HandleWritepid(const std::vector<std::string>& args, std::string* err) {
+    writepid_files_.assign(args.begin() + 1, args.end());
+    return true;
+}
+
+class Service::OptionHandlerMap : public KeywordMap<OptionHandler> {
+public:
+    OptionHandlerMap() {
+    }
+private:
+    Map& map() const override;
+};
+
+Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
+    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+    static const Map option_handlers = {
+        {"class",       {1,     1,    &Service::HandleClass}},
+        {"console",     {0,     0,    &Service::HandleConsole}},
+        {"critical",    {0,     0,    &Service::HandleCritical}},
+        {"disabled",    {0,     0,    &Service::HandleDisabled}},
+        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
+        {"ioprio",      {2,     2,    &Service::HandleIoprio}},
+        {"keycodes",    {1,     kMax, &Service::HandleKeycodes}},
+        {"oneshot",     {0,     0,    &Service::HandleOneshot}},
+        {"onrestart",   {1,     kMax, &Service::HandleOnrestart}},
+        {"seclabel",    {1,     1,    &Service::HandleSeclabel}},
+        {"setenv",      {2,     2,    &Service::HandleSetenv}},
+        {"socket",      {3,     6,    &Service::HandleSocket}},
+        {"user",        {1,     1,    &Service::HandleUser}},
+        {"writepid",    {1,     kMax, &Service::HandleWritepid}},
+    };
+    return option_handlers;
+}
+
+bool Service::HandleLine(const std::vector<std::string>& args, std::string* err) {
+    if (args.empty()) {
+        *err = "option needed, but not provided";
+        return false;
+    }
+
+    static const OptionHandlerMap handler_map;
+    auto handler = handler_map.FindFunction(args[0], args.size() - 1, err);
+
+    if (!handler) {
+        return false;
+    }
+
+    return (this->*handler)(args, err);
+}
+
+bool Service::Start(const std::vector<std::string>& dynamic_args) {
+    // Starting a service removes it from the disabled or reset state and
+    // immediately takes it out of the restarting state if it was in there.
+    flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
+    time_started_ = 0;
+
+    // Running processes require no additional work --- if they're in the
+    // process of exiting, we've ensured that they will immediately restart
+    // on exit, unless they are ONESHOT.
+    if (flags_ & SVC_RUNNING) {
+        return false;
+    }
+
+    bool needs_console = (flags_ & SVC_CONSOLE);
+    if (needs_console && !have_console) {
+        ERROR("service '%s' requires console\n", name_.c_str());
+        flags_ |= SVC_DISABLED;
+        return false;
+    }
+
+    struct stat sb;
+    if (stat(args_[0].c_str(), &sb) == -1) {
+        ERROR("cannot find '%s' (%s), disabling '%s'\n",
+              args_[0].c_str(), strerror(errno), name_.c_str());
+        flags_ |= SVC_DISABLED;
+        return false;
+    }
+
+    if ((!(flags_ & SVC_ONESHOT)) && !dynamic_args.empty()) {
+        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
+              args_[0].c_str());
+        flags_ |= SVC_DISABLED;
+        return false;
+    }
+
+    std::string scon;
+    if (!seclabel_.empty()) {
+        scon = seclabel_;
+    } else {
+        char* mycon = nullptr;
+        char* fcon = nullptr;
+
+        INFO("computing context for service '%s'\n", args_[0].c_str());
+        int rc = getcon(&mycon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", name_.c_str());
+            return false;
+        }
+
+        rc = getfilecon(args_[0].c_str(), &fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", name_.c_str());
+            free(mycon);
+            return false;
+        }
+
+        char* ret_scon = nullptr;
+        rc = security_compute_create(mycon, fcon, string_to_security_class("process"),
+                                     &ret_scon);
+        if (rc == 0) {
+            scon = ret_scon;
+            free(ret_scon);
+        }
+        if (rc == 0 && scon == mycon) {
+            ERROR("Service %s does not have a SELinux domain defined.\n", name_.c_str());
+            free(mycon);
+            free(fcon);
+            return false;
+        }
+        free(mycon);
+        free(fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", name_.c_str());
+            return false;
+        }
+    }
+
+    NOTICE("Starting service '%s'...\n", name_.c_str());
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        int fd, sz;
+
+        umask(077);
+        if (properties_initialized()) {
+            get_property_workspace(&fd, &sz);
+            std::string tmp = StringPrintf("%d,%d", dup(fd), sz);
+            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp.c_str());
+        }
+
+        for (const auto& ei : envvars_) {
+            add_environment(ei.name.c_str(), ei.value.c_str());
+        }
+
+        for (const auto& si : sockets_) {
+            int socket_type = ((si.type == "stream" ? SOCK_STREAM :
+                                (si.type == "dgram" ? SOCK_DGRAM :
+                                 SOCK_SEQPACKET)));
+            const char* socketcon =
+                !si.socketcon.empty() ? si.socketcon.c_str() : scon.c_str();
+
+            int s = create_socket(si.name.c_str(), socket_type, si.perm,
+                                  si.uid, si.gid, socketcon);
+            if (s >= 0) {
+                PublishSocket(si.name, s);
+            }
+        }
+
+        std::string pid_str = StringPrintf("%d", pid);
+        for (const auto& file : writepid_files_) {
+            if (!WriteStringToFile(pid_str, file)) {
+                ERROR("couldn't write %s to %s: %s\n",
+                      pid_str.c_str(), file.c_str(), strerror(errno));
+            }
+        }
+
+        if (ioprio_class_ != IoSchedClass_NONE) {
+            if (android_set_ioprio(getpid(), ioprio_class_, ioprio_pri_)) {
+                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
+                      getpid(), ioprio_class_, ioprio_pri_, strerror(errno));
+            }
+        }
+
+        if (needs_console) {
+            setsid();
+            OpenConsole();
+        } else {
+            ZapStdio();
+        }
+
+        setpgid(0, getpid());
+
+        // As requested, set our gid, supplemental gids, and uid.
+        if (gid_) {
+            if (setgid(gid_) != 0) {
+                ERROR("setgid failed: %s\n", strerror(errno));
+                _exit(127);
+            }
+        }
+        if (!supp_gids_.empty()) {
+            if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
+                ERROR("setgroups failed: %s\n", strerror(errno));
+                _exit(127);
+            }
+        }
+        if (uid_) {
+            if (setuid(uid_) != 0) {
+                ERROR("setuid failed: %s\n", strerror(errno));
+                _exit(127);
+            }
+        }
+        if (!seclabel_.empty()) {
+            if (setexeccon(seclabel_.c_str()) < 0) {
+                ERROR("cannot setexeccon('%s'): %s\n",
+                      seclabel_.c_str(), strerror(errno));
+                _exit(127);
+            }
+        }
+
+        std::vector<char*> strs;
+        for (const auto& s : args_) {
+            strs.push_back(const_cast<char*>(s.c_str()));
+        }
+        for (const auto& s : dynamic_args) {
+            strs.push_back(const_cast<char*>(s.c_str()));
+        }
+        strs.push_back(nullptr);
+        if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {
+            ERROR("cannot execve('%s'): %s\n", args_[0].c_str(), strerror(errno));
+        }
+
+        _exit(127);
+    }
+
+    if (pid < 0) {
+        ERROR("failed to start '%s'\n", name_.c_str());
+        pid_ = 0;
+        return false;
+    }
+
+    time_started_ = gettime();
+    pid_ = pid;
+    flags_ |= SVC_RUNNING;
+
+    if ((flags_ & SVC_EXEC) != 0) {
+        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
+             pid_, uid_, gid_, supp_gids_.size(),
+             !seclabel_.empty() ? seclabel_.c_str() : "default");
+    }
+
+    NotifyStateChange("running");
+    return true;
+}
+
+bool Service::Start() {
+    const std::vector<std::string> null_dynamic_args;
+    return Start(null_dynamic_args);
+}
+
+bool Service::StartIfNotDisabled() {
+    if (!(flags_ & SVC_DISABLED)) {
+        return Start();
+    } else {
+        flags_ |= SVC_DISABLED_START;
+    }
+    return true;
+}
+
+bool Service::Enable() {
+    flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);
+    if (flags_ & SVC_DISABLED_START) {
+        return Start();
+    }
+    return true;
+}
+
+void Service::Reset() {
+    StopOrReset(SVC_RESET);
+}
+
+void Service::Stop() {
+    StopOrReset(SVC_DISABLED);
+}
+
+void Service::Restart() {
+    if (flags_ & SVC_RUNNING) {
+        /* Stop, wait, then start the service. */
+        StopOrReset(SVC_RESTART);
+    } else if (!(flags_ & SVC_RESTARTING)) {
+        /* Just start the service since it's not running. */
+        Start();
+    } /* else: Service is restarting anyways. */
+}
+
+void Service::RestartIfNeeded(time_t& process_needs_restart) {
+    time_t next_start_time = time_started_ + 5;
+
+    if (next_start_time <= gettime()) {
+        flags_ &= (~SVC_RESTARTING);
+        Start();
+        return;
+    }
+
+    if ((next_start_time < process_needs_restart) ||
+        (process_needs_restart == 0)) {
+        process_needs_restart = next_start_time;
+    }
+}
+
+/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
+void Service::StopOrReset(int how) {
+    /* The service is still SVC_RUNNING until its process exits, but if it has
+     * already exited it shoudn't attempt a restart yet. */
+    flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);
+
+    if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
+        /* Hrm, an illegal flag.  Default to SVC_DISABLED */
+        how = SVC_DISABLED;
+    }
+        /* if the service has not yet started, prevent
+         * it from auto-starting with its class
+         */
+    if (how == SVC_RESET) {
+        flags_ |= (flags_ & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
+    } else {
+        flags_ |= how;
+    }
+
+    if (pid_) {
+        NOTICE("Service '%s' is being killed...\n", name_.c_str());
+        kill(-pid_, SIGKILL);
+        NotifyStateChange("stopping");
+    } else {
+        NotifyStateChange("stopped");
+    }
+}
+
+void Service::ZapStdio() const {
+    int fd;
+    fd = open("/dev/null", O_RDWR);
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    close(fd);
+}
+
+void Service::OpenConsole() const {
+    int fd;
+    if ((fd = open(console_name.c_str(), O_RDWR)) < 0) {
+        fd = open("/dev/null", O_RDWR);
+    }
+    ioctl(fd, TIOCSCTTY, 0);
+    dup2(fd, 0);
+    dup2(fd, 1);
+    dup2(fd, 2);
+    close(fd);
+}
+
+void Service::PublishSocket(const std::string& name, int fd) const {
+    std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str());
+    std::string val = StringPrintf("%d", fd);
+    add_environment(key.c_str(), val.c_str());
+
+    /* make sure we don't close-on-exec */
+    fcntl(fd, F_SETFD, 0);
+}
+
+int ServiceManager::exec_count_ = 0;
+
+ServiceManager::ServiceManager() {
+}
+
+ServiceManager& ServiceManager::GetInstance() {
+    static ServiceManager instance;
+    return instance;
+}
+
+void ServiceManager::AddService(std::unique_ptr<Service> service) {
+    Service* old_service = FindServiceByName(service->name());
+    if (old_service) {
+        ERROR("ignored duplicate definition of service '%s'",
+              service->name().c_str());
+        return;
+    }
+    services_.emplace_back(std::move(service));
+}
+
+Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
+    // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
+    // SECLABEL can be a - to denote default
+    std::size_t command_arg = 1;
+    for (std::size_t i = 1; i < args.size(); ++i) {
+        if (args[i] == "--") {
+            command_arg = i + 1;
+            break;
+        }
+    }
+    if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
+        ERROR("exec called with too many supplementary group ids\n");
+        return nullptr;
+    }
+
+    if (command_arg >= args.size()) {
+        ERROR("exec called without command\n");
+        return nullptr;
+    }
+    std::vector<std::string> str_args(args.begin() + command_arg, args.end());
+
+    exec_count_++;
+    std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
+    unsigned flags = SVC_EXEC | SVC_ONESHOT;
+
+    std::string seclabel = "";
+    if (command_arg > 2 && args[1] != "-") {
+        seclabel = args[1];
+    }
+    uid_t uid = 0;
+    if (command_arg > 3) {
+        uid = decode_uid(args[2].c_str());
+    }
+    gid_t gid = 0;
+    std::vector<gid_t> supp_gids;
+    if (command_arg > 4) {
+        gid = decode_uid(args[3].c_str());
+        std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
+        for (size_t i = 0; i < nr_supp_gids; ++i) {
+            supp_gids.push_back(decode_uid(args[4 + i].c_str()));
+        }
+    }
+
+    std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid,
+                                               supp_gids, seclabel, str_args));
+    if (!svc_p) {
+        ERROR("Couldn't allocate service for exec of '%s'",
+              str_args[0].c_str());
+        return nullptr;
+    }
+    Service* svc = svc_p.get();
+    services_.push_back(std::move(svc_p));
+
+    return svc;
+}
+
+Service* ServiceManager::FindServiceByName(const std::string& name) const {
+    auto svc = std::find_if(services_.begin(), services_.end(),
+                            [&name] (const std::unique_ptr<Service>& s) {
+                                return name == s->name();
+                            });
+    if (svc != services_.end()) {
+        return svc->get();
+    }
+    return nullptr;
+}
+
+Service* ServiceManager::FindServiceByPid(pid_t pid) const {
+    auto svc = std::find_if(services_.begin(), services_.end(),
+                            [&pid] (const std::unique_ptr<Service>& s) {
+                                return s->pid() == pid;
+                            });
+    if (svc != services_.end()) {
+        return svc->get();
+    }
+    return nullptr;
+}
+
+Service* ServiceManager::FindServiceByKeychord(int keychord_id) const {
+    auto svc = std::find_if(services_.begin(), services_.end(),
+                            [&keychord_id] (const std::unique_ptr<Service>& s) {
+                                return s->keychord_id() == keychord_id;
+                            });
+
+    if (svc != services_.end()) {
+        return svc->get();
+    }
+    return nullptr;
+}
+
+void ServiceManager::ForEachService(void (*func)(Service* svc)) const {
+    for (const auto& s : services_) {
+        func(s.get());
+    }
+}
+
+void ServiceManager::ForEachServiceInClass(const std::string& classname,
+                                           void (*func)(Service* svc)) const {
+    for (const auto& s : services_) {
+        if (classname == s->classname()) {
+            func(s.get());
+        }
+    }
+}
+
+void ServiceManager::ForEachServiceWithFlags(unsigned matchflags,
+                                             void (*func)(Service* svc)) const {
+    for (const auto& s : services_) {
+        if (s->flags() & matchflags) {
+            func(s.get());
+        }
+    }
+}
+
+void ServiceManager::RemoveService(const Service& svc) {
+    auto svc_it = std::find_if(services_.begin(), services_.end(),
+                               [&svc] (const std::unique_ptr<Service>& s) {
+                                   return svc.name() == s->name();
+                               });
+    if (svc_it == services_.end()) {
+        return;
+    }
+
+    services_.erase(svc_it);
+}
+
+void ServiceManager::DumpState() const {
+    for (const auto& s : services_) {
+        s->DumpState();
+    }
+    INFO("\n");
+}
+
+bool ServiceParser::ParseSection(const std::vector<std::string>& args,
+                                 std::string* err) {
+    if (args.size() < 3) {
+        *err = "services must have a name and a program";
+        return false;
+    }
+
+    const std::string& name = args[1];
+    if (!IsValidName(name)) {
+        *err = StringPrintf("invalid service name '%s'", name.c_str());
+        return false;
+    }
+
+    std::vector<std::string> str_args(args.begin() + 2, args.end());
+    service_ = std::make_unique<Service>(name, "default", str_args);
+    return true;
+}
+
+bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
+                                     const std::string& filename, int line,
+                                     std::string* err) const {
+    return service_ ? service_->HandleLine(args, err) : false;
+}
+
+void ServiceParser::EndSection() {
+    if (service_) {
+        ServiceManager::GetInstance().AddService(std::move(service_));
+    }
+}
+
+bool ServiceParser::IsValidName(const std::string& name) const {
+    if (name.size() > 16) {
+        return false;
+    }
+    for (const auto& c : name) {
+        if (!isalnum(c) && (c != '_') && (c != '-')) {
+            return false;
+        }
+    }
+    return true;
+}
diff --git a/init/service.h b/init/service.h
new file mode 100644 (file)
index 0000000..10eb736
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _INIT_SERVICE_H
+#define _INIT_SERVICE_H
+
+#include <sys/types.h>
+
+#include <cutils/iosched_policy.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "action.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+
+#define SVC_DISABLED       0x001  // do not autostart with class
+#define SVC_ONESHOT        0x002  // do not restart on exit
+#define SVC_RUNNING        0x004  // currently active
+#define SVC_RESTARTING     0x008  // waiting to restart
+#define SVC_CONSOLE        0x010  // requires console
+#define SVC_CRITICAL       0x020  // will reboot into recovery if keeps crashing
+#define SVC_RESET          0x040  // Use when stopping a process,
+                                  // but not disabling so it can be restarted with its class.
+#define SVC_RC_DISABLED    0x080  // Remember if the disabled flag was set in the rc script.
+#define SVC_RESTART        0x100  // Use to safely restart (stop, wait, start) a service.
+#define SVC_DISABLED_START 0x200  // A start was requested but it was disabled at the time.
+#define SVC_EXEC           0x400  // This synthetic service corresponds to an 'exec'.
+
+#define NR_SVC_SUPP_GIDS 12    // twelve supplementary groups
+
+class Action;
+class ServiceManager;
+
+struct SocketInfo {
+    SocketInfo();
+    SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+                       gid_t gid, int perm, const std::string& socketcon);
+    std::string name;
+    std::string type;
+    uid_t uid;
+    gid_t gid;
+    int perm;
+    std::string socketcon;
+};
+
+struct ServiceEnvironmentInfo {
+    ServiceEnvironmentInfo();
+    ServiceEnvironmentInfo(const std::string& name, const std::string& value);
+    std::string name;
+    std::string value;
+};
+
+class Service {
+public:
+    Service(const std::string& name, const std::string& classname,
+            const std::vector<std::string>& args);
+
+    Service(const std::string& name, const std::string& classname,
+            unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
+            const std::string& seclabel,  const std::vector<std::string>& args);
+
+    bool HandleLine(const std::vector<std::string>& args, std::string* err);
+    bool Start(const std::vector<std::string>& dynamic_args);
+    bool Start();
+    bool StartIfNotDisabled();
+    bool Enable();
+    void Reset();
+    void Stop();
+    void Restart();
+    void RestartIfNeeded(time_t& process_needs_restart);
+    bool Reap();
+    void DumpState() const;
+
+    const std::string& name() const { return name_; }
+    const std::string& classname() const { return classname_; }
+    unsigned flags() const { return flags_; }
+    pid_t pid() const { return pid_; }
+    uid_t uid() const { return uid_; }
+    gid_t gid() const { return gid_; }
+    const std::vector<gid_t>& supp_gids() const { return supp_gids_; }
+    const std::string& seclabel() const { return seclabel_; }
+    const std::vector<int>& keycodes() const { return keycodes_; }
+    int keychord_id() const { return keychord_id_; }
+    void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
+    const std::vector<std::string>& args() const { return args_; }
+
+private:
+    using OptionHandler = bool (Service::*) (const std::vector<std::string>& args,
+                                             std::string* err);
+    class OptionHandlerMap;
+
+    void NotifyStateChange(const std::string& new_state) const;
+    void StopOrReset(int how);
+    void ZapStdio() const;
+    void OpenConsole() const;
+    void PublishSocket(const std::string& name, int fd) const;
+
+    bool HandleClass(const std::vector<std::string>& args, std::string* err);
+    bool HandleConsole(const std::vector<std::string>& args, std::string* err);
+    bool HandleCritical(const std::vector<std::string>& args, std::string* err);
+    bool HandleDisabled(const std::vector<std::string>& args, std::string* err);
+    bool HandleGroup(const std::vector<std::string>& args, std::string* err);
+    bool HandleIoprio(const std::vector<std::string>& args, std::string* err);
+    bool HandleKeycodes(const std::vector<std::string>& args, std::string* err);
+    bool HandleOneshot(const std::vector<std::string>& args, std::string* err);
+    bool HandleOnrestart(const std::vector<std::string>& args, std::string* err);
+    bool HandleSeclabel(const std::vector<std::string>& args, std::string* err);
+    bool HandleSetenv(const std::vector<std::string>& args, std::string* err);
+    bool HandleSocket(const std::vector<std::string>& args, std::string* err);
+    bool HandleUser(const std::vector<std::string>& args, std::string* err);
+    bool HandleWritepid(const std::vector<std::string>& args, std::string* err);
+
+    std::string name_;
+    std::string classname_;
+
+    unsigned flags_;
+    pid_t pid_;
+    time_t time_started_;    // time of last start
+    time_t time_crashed_;    // first crash within inspection window
+    int nr_crashed_;         // number of times crashed within window
+
+    uid_t uid_;
+    gid_t gid_;
+    std::vector<gid_t> supp_gids_;
+
+    std::string seclabel_;
+
+    std::vector<SocketInfo> sockets_;
+    std::vector<ServiceEnvironmentInfo> envvars_;
+
+    Action onrestart_;  // Commands to execute on restart.
+
+    std::vector<std::string> writepid_files_;
+
+    // keycodes for triggering this service via /dev/keychord
+    std::vector<int> keycodes_;
+    int keychord_id_;
+
+    IoSchedClass ioprio_class_;
+    int ioprio_pri_;
+
+    std::vector<std::string> args_;
+};
+
+class ServiceManager {
+public:
+    static ServiceManager& GetInstance();
+
+    void AddService(std::unique_ptr<Service> service);
+    Service* MakeExecOneshotService(const std::vector<std::string>& args);
+    Service* FindServiceByName(const std::string& name) const;
+    Service* FindServiceByPid(pid_t pid) const;
+    Service* FindServiceByKeychord(int keychord_id) const;
+    void ForEachService(void (*func)(Service* svc)) const;
+    void ForEachServiceInClass(const std::string& classname,
+                               void (*func)(Service* svc)) const;
+    void ForEachServiceWithFlags(unsigned matchflags,
+                             void (*func)(Service* svc)) const;
+    void RemoveService(const Service& svc);
+    void DumpState() const;
+
+private:
+    ServiceManager();
+
+    static int exec_count_; // Every service needs a unique name.
+    std::vector<std::unique_ptr<Service>> services_;
+};
+
+class ServiceParser : public SectionParser {
+public:
+    ServiceParser() : service_(nullptr) {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override;
+    void EndSection() override;
+    void EndFile(const std::string&) override {
+    }
+private:
+    bool IsValidName(const std::string& name) const;
+
+    std::unique_ptr<Service> service_;
+};
+
+#endif
index 39a466d..867abbc 100644 (file)
 #include <cutils/list.h>
 #include <cutils/sockets.h>
 
+#include "action.h"
 #include "init.h"
 #include "log.h"
+#include "service.h"
 #include "util.h"
 
-#define CRITICAL_CRASH_THRESHOLD    4       /* if we crash >4 times ... */
-#define CRITICAL_CRASH_WINDOW       (4*60)  /* ... in 4 minutes, goto recovery */
-
 static int signal_write_fd = -1;
 static int signal_read_fd = -1;
 
@@ -60,11 +59,11 @@ static bool wait_for_one_process() {
         return false;
     }
 
-    service* svc = service_find_by_pid(pid);
+    Service* svc = ServiceManager::GetInstance().FindServiceByPid(pid);
 
     std::string name;
     if (svc) {
-        name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name, pid);
+        name = android::base::StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
     } else {
         name = android::base::StringPrintf("Untracked pid %d", pid);
     }
@@ -75,70 +74,11 @@ static bool wait_for_one_process() {
         return true;
     }
 
-    // TODO: all the code from here down should be a member function on service.
-
-    if (!(svc->flags & SVC_ONESHOT) || (svc->flags & SVC_RESTART)) {
-        NOTICE("Service '%s' (pid %d) killing any children in process group\n", svc->name, pid);
-        kill(-pid, SIGKILL);
-    }
-
-    // Remove any sockets we may have created.
-    for (socketinfo* si = svc->sockets; si; si = si->next) {
-        char tmp[128];
-        snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name);
-        unlink(tmp);
-    }
-
-    if (svc->flags & SVC_EXEC) {
-        INFO("SVC_EXEC pid %d finished...\n", svc->pid);
+    if (svc->Reap()) {
         waiting_for_exec = false;
-        list_remove(&svc->slist);
-        free(svc->name);
-        free(svc);
-        return true;
+        ServiceManager::GetInstance().RemoveService(*svc);
     }
 
-    svc->pid = 0;
-    svc->flags &= (~SVC_RUNNING);
-
-    // Oneshot processes go into the disabled state on exit,
-    // except when manually restarted.
-    if ((svc->flags & SVC_ONESHOT) && !(svc->flags & SVC_RESTART)) {
-        svc->flags |= SVC_DISABLED;
-    }
-
-    // Disabled and reset processes do not get restarted automatically.
-    if (svc->flags & (SVC_DISABLED | SVC_RESET))  {
-        svc->NotifyStateChange("stopped");
-        return true;
-    }
-
-    time_t now = gettime();
-    if ((svc->flags & SVC_CRITICAL) && !(svc->flags & SVC_RESTART)) {
-        if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) {
-            if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) {
-                ERROR("critical process '%s' exited %d times in %d minutes; "
-                      "rebooting into recovery mode\n", svc->name,
-                      CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60);
-                android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
-                return true;
-            }
-        } else {
-            svc->time_crashed = now;
-            svc->nr_crashed = 1;
-        }
-    }
-
-    svc->flags &= (~SVC_RESTART);
-    svc->flags |= SVC_RESTARTING;
-
-    // Execute all onrestart commands for this service.
-    struct listnode* node;
-    list_for_each(node, &svc->onrestart.commands) {
-        command* cmd = node_to_item(node, struct command, clist);
-        cmd->func(cmd->nargs, cmd->args);
-    }
-    svc->NotifyStateChange("restarting");
     return true;
 }
 
index c63fdaa..75924cb 100644 (file)
@@ -59,11 +59,10 @@ int ueventd_main(int argc, char **argv)
     cb.func_log = selinux_klog_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
-    char hardware[PROP_VALUE_MAX];
-    property_get("ro.hardware", hardware);
+    std::string hardware = property_get("ro.hardware");
 
     ueventd_parse_config_file("/ueventd.rc");
-    ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware).c_str());
+    ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
 
     device_init();
 
index 497c606..09f4638 100644 (file)
@@ -233,7 +233,6 @@ int ueventd_parse_config_file(const char *fn)
 
     data.push_back('\n'); // TODO: fix parse_config.
     parse_config(fn, data);
-    dump_parser_state();
     return 0;
 }
 
index a5392c6..b316d6e 100644 (file)
@@ -33,6 +33,7 @@
 #include <sys/un.h>
 
 #include <base/file.h>
+#include <base/strings.h>
 
 /* for ANDROID_SOCKET_* */
 #include <cutils/sockets.h>
 
 #include "init.h"
 #include "log.h"
+#include "property_service.h"
 #include "util.h"
 
 /*
  * android_name_to_id - returns the integer uid/gid associated with the given
- * name, or -1U on error.
+ * name, or UINT_MAX on error.
  */
 static unsigned int android_name_to_id(const char *name)
 {
@@ -58,27 +60,35 @@ static unsigned int android_name_to_id(const char *name)
             return info[n].aid;
     }
 
-    return -1U;
+    return UINT_MAX;
 }
 
-/*
- * decode_uid - decodes and returns the given string, which can be either the
- * numeric or name representation, into the integer uid or gid. Returns -1U on
- * error.
- */
-unsigned int decode_uid(const char *s)
+static unsigned int do_decode_uid(const char *s)
 {
     unsigned int v;
 
     if (!s || *s == '\0')
-        return -1U;
+        return UINT_MAX;
     if (isalpha(s[0]))
         return android_name_to_id(s);
 
     errno = 0;
     v = (unsigned int) strtoul(s, 0, 0);
     if (errno)
-        return -1U;
+        return UINT_MAX;
+    return v;
+}
+
+/*
+ * decode_uid - decodes and returns the given string, which can be either the
+ * numeric or name representation, into the integer uid or gid. Returns
+ * UINT_MAX on error.
+ */
+unsigned int decode_uid(const char *s) {
+    unsigned int v = do_decode_uid(s);
+    if (v == UINT_MAX) {
+        ERROR("decode_uid: Unable to find UID for '%s'. Returning UINT_MAX\n", s);
+    }
     return v;
 }
 
@@ -401,32 +411,16 @@ void open_devnull_stdio(void)
     }
 }
 
-void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)> import_kernel_nv)
-{
-    char cmdline[2048];
-    char *ptr;
-    int fd;
+void import_kernel_cmdline(bool in_qemu,
+                           std::function<void(const std::string&, const std::string&, bool)> fn) {
+    std::string cmdline;
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
 
-    fd = open("/proc/cmdline", O_RDONLY | O_CLOEXEC);
-    if (fd >= 0) {
-        int n = read(fd, cmdline, sizeof(cmdline) - 1);
-        if (n < 0) n = 0;
-
-        /* get rid of trailing newline, it happens */
-        if (n > 0 && cmdline[n-1] == '\n') n--;
-
-        cmdline[n] = 0;
-        close(fd);
-    } else {
-        cmdline[0] = 0;
-    }
-
-    ptr = cmdline;
-    while (ptr && *ptr) {
-        char *x = strchr(ptr, ' ');
-        if (x != 0) *x++ = 0;
-        import_kernel_nv(ptr, in_qemu);
-        ptr = x;
+    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
+        std::vector<std::string> pieces = android::base::Split(entry, "=");
+        if (pieces.size() == 2) {
+            fn(pieces[0], pieces[1], in_qemu);
+        }
     }
 }
 
@@ -472,3 +466,84 @@ std::string bytes_to_hex(const uint8_t* bytes, size_t bytes_len) {
         android::base::StringAppendF(&hex, "%02x", bytes[i]);
     return hex;
 }
+
+/*
+ * Returns true is pathname is a directory
+ */
+bool is_dir(const char* pathname) {
+    struct stat info;
+    if (stat(pathname, &info) == -1) {
+        return false;
+    }
+    return S_ISDIR(info.st_mode);
+}
+
+bool expand_props(const std::string& src, std::string* dst) {
+    const char* src_ptr = src.c_str();
+
+    if (!dst) {
+        return false;
+    }
+
+    /* - variables can either be $x.y or ${x.y}, in case they are only part
+     *   of the string.
+     * - will accept $$ as a literal $.
+     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
+     *   bad things will happen
+     */
+    while (*src_ptr) {
+        const char* c;
+
+        c = strchr(src_ptr, '$');
+        if (!c) {
+            dst->append(src_ptr);
+            return true;
+        }
+
+        dst->append(src_ptr, c);
+        c++;
+
+        if (*c == '$') {
+            dst->push_back(*(c++));
+            src_ptr = c;
+            continue;
+        } else if (*c == '\0') {
+            return true;
+        }
+
+        std::string prop_name;
+        if (*c == '{') {
+            c++;
+            const char* end = strchr(c, '}');
+            if (!end) {
+                // failed to find closing brace, abort.
+                ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
+                return false;
+            }
+            prop_name = std::string(c, end);
+            c = end + 1;
+        } else {
+            prop_name = c;
+            ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
+                  c);
+            c += prop_name.size();
+        }
+
+        if (prop_name.empty()) {
+            ERROR("invalid zero-length prop name in '%s'\n", src.c_str());
+            return false;
+        }
+
+        std::string prop_val = property_get(prop_name.c_str());
+        if (prop_val.empty()) {
+            ERROR("property '%s' doesn't exist while expanding '%s'\n",
+                  prop_name.c_str(), src.c_str());
+            return false;
+        }
+
+        dst->append(prop_val);
+        src_ptr = c;
+    }
+
+    return true;
+}
index 09d64cd..c2efb01 100644 (file)
@@ -58,9 +58,12 @@ void make_link_init(const char *oldpath, const char *newpath);
 void remove_link(const char *oldpath, const char *newpath);
 int wait_for_file(const char *filename, int timeout);
 void open_devnull_stdio(void);
-void import_kernel_cmdline(bool in_qemu, std::function<void(char*,bool)>);
+void import_kernel_cmdline(bool in_qemu,
+                           std::function<void(const std::string&, const std::string&, bool)>);
 int make_dir(const char *path, mode_t mode);
 int restorecon(const char *pathname);
 int restorecon_recursive(const char *pathname);
 std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
+bool is_dir(const char* pathname);
+bool expand_props(const std::string& src, std::string* dst);
 #endif
index 5b3ab50..228954b 100644 (file)
@@ -38,6 +38,6 @@ TEST(util, read_file_success) {
 
 TEST(util, decode_uid) {
   EXPECT_EQ(0U, decode_uid("root"));
-  EXPECT_EQ(-1U, decode_uid("toot"));
+  EXPECT_EQ(UINT_MAX, decode_uid("toot"));
   EXPECT_EQ(123U, decode_uid("123"));
 }
index 881a4df..0d16db9 100644 (file)
@@ -38,29 +38,30 @@ int watchdogd_main(int argc, char **argv) {
     int margin = 10;
     if (argc >= 3) margin = atoi(argv[2]);
 
-    NOTICE("watchdogd started (interval %d, margin %d)!\n", interval, margin);
+    NOTICE("started (interval %d, margin %d)!\n", interval, margin);
 
     int fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
     if (fd == -1) {
-        ERROR("watchdogd: Failed to open %s: %s\n", DEV_NAME, strerror(errno));
+        ERROR("Failed to open %s: %s\n", DEV_NAME, strerror(errno));
         return 1;
     }
 
     int timeout = interval + margin;
     int ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
     if (ret) {
-        ERROR("watchdogd: Failed to set timeout to %d: %s\n", timeout, strerror(errno));
+        ERROR("Failed to set timeout to %d: %s\n", timeout, strerror(errno));
         ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
         if (ret) {
-            ERROR("watchdogd: Failed to get timeout: %s\n", strerror(errno));
+            ERROR("Failed to get timeout: %s\n", strerror(errno));
         } else {
             if (timeout > margin) {
                 interval = timeout - margin;
             } else {
                 interval = 1;
             }
-            ERROR("watchdogd: Adjusted interval to timeout returned by driver: timeout %d, interval %d, margin %d\n",
-                  timeout, interval, margin);
+            WARNING("Adjusted interval to timeout returned by driver:"
+                    " timeout %d, interval %d, margin %d\n",
+                    timeout, interval, margin);
         }
     }
 
index 35fed6d..2467f3e 100644 (file)
@@ -20,17 +20,23 @@ LOCAL_MODULE := $(module)
 LOCAL_MODULE_TAGS := $(module_tag)
 LOCAL_MULTILIB := $($(module)_multilib)
 ifeq ($(LOCAL_MULTILIB),both)
-ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRRARY))
+ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRARY))
   LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
   LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 endif
 endif
 
-LOCAL_ADDITIONAL_DEPENDENCIES := \
+ifeq ($(build_type),target)
+  include $(LLVM_DEVICE_BUILD_MK)
+else
+  include $(LLVM_HOST_BUILD_MK)
+endif
+
+LOCAL_ADDITIONAL_DEPENDENCIES += \
     $(LOCAL_PATH)/Android.mk \
     $(LOCAL_PATH)/Android.build.mk \
 
-LOCAL_CFLAGS := \
+LOCAL_CFLAGS += \
     $(libbacktrace_common_cflags) \
     $($(module)_cflags) \
     $($(module)_cflags_$(build_type)) \
@@ -48,7 +54,7 @@ LOCAL_CPPFLAGS += \
     $($(module)_cppflags) \
     $($(module)_cppflags_$(build_type)) \
 
-LOCAL_C_INCLUDES := \
+LOCAL_C_INCLUDES += \
     $(libbacktrace_common_c_includes) \
     $($(module)_c_includes) \
     $($(module)_c_includes_$(build_type)) \
@@ -57,18 +63,20 @@ LOCAL_SRC_FILES := \
     $($(module)_src_files) \
     $($(module)_src_files_$(build_type)) \
 
-LOCAL_STATIC_LIBRARIES := \
+LOCAL_STATIC_LIBRARIES += \
     $($(module)_static_libraries) \
     $($(module)_static_libraries_$(build_type)) \
 
-LOCAL_SHARED_LIBRARIES := \
+LOCAL_SHARED_LIBRARIES += \
     $($(module)_shared_libraries) \
     $($(module)_shared_libraries_$(build_type)) \
 
-LOCAL_LDLIBS := \
+LOCAL_LDLIBS += \
     $($(module)_ldlibs) \
     $($(module)_ldlibs_$(build_type)) \
 
+LOCAL_STRIP_MODULE := $($(module)_strip_module)
+
 ifeq ($(build_type),target)
   include $(BUILD_$(build_target))
 endif
@@ -76,6 +84,11 @@ endif
 ifeq ($(build_type),host)
   # Only build if host builds are supported.
   ifeq ($(build_host),true)
+    # -fno-omit-frame-pointer should be set for host build. Because currently
+    # libunwind can't recognize .debug_frame using dwarf version 4, and it relies
+    # on stack frame pointer to do unwinding on x86.
+    # $(LLVM_HOST_BUILD_MK) overwrites -fno-omit-frame-pointer. so the below line
+    # must be after the include.
     LOCAL_CFLAGS += -Wno-extern-c-compat -fno-omit-frame-pointer
     include $(BUILD_HOST_$(build_target))
   endif
index 6a689a6..9c6742e 100644 (file)
@@ -25,6 +25,7 @@ libbacktrace_common_conlyflags := \
 
 libbacktrace_common_cppflags := \
        -std=gnu++11 \
+       -I external/libunwind/include/tdep \
 
 # The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
 libbacktrace_common_clang_cflags += \
@@ -37,6 +38,9 @@ build_host := true
 endif
 endif
 
+LLVM_ROOT_PATH := external/llvm
+include $(LLVM_ROOT_PATH)/llvm.mk
+
 #-------------------------------------------------------------------------
 # The libbacktrace library.
 #-------------------------------------------------------------------------
@@ -44,6 +48,7 @@ libbacktrace_src_files := \
        Backtrace.cpp \
        BacktraceCurrent.cpp \
        BacktraceMap.cpp \
+       BacktraceOffline.cpp \
        BacktracePtrace.cpp \
        thread_utils.c \
        ThreadEntry.cpp \
@@ -51,18 +56,24 @@ libbacktrace_src_files := \
        UnwindMap.cpp \
        UnwindPtrace.cpp \
 
-libbacktrace_shared_libraries_target := \
-       libcutils \
-
 libbacktrace_shared_libraries := \
        libbase \
+       liblog \
        libunwind \
 
-libbacktrace_shared_libraries_host := \
-       liblog \
+# Use shared llvm library on device to save space.
+libbacktrace_shared_libraries_target := \
+       libLLVM \
 
+# Use static llvm libraries on host to remove dependency on 32-bit llvm shared library
+# which is not included in the prebuilt.
 libbacktrace_static_libraries_host := \
-       libcutils \
+       libLLVMObject \
+       libLLVMBitReader \
+       libLLVMMC \
+       libLLVMMCParser \
+       libLLVMCore \
+       libLLVMSupport \
 
 libbacktrace_ldlibs_host := \
        -lpthread \
@@ -76,6 +87,14 @@ include $(LOCAL_PATH)/Android.build.mk
 build_type := host
 libbacktrace_multilib := both
 include $(LOCAL_PATH)/Android.build.mk
+libbacktrace_static_libraries := \
+       libbase \
+       liblog \
+       libunwind \
+
+build_target := STATIC_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+libbacktrace_static_libraries :=
 
 #-------------------------------------------------------------------------
 # The libbacktrace_test library needed by backtrace_test.
@@ -86,6 +105,8 @@ libbacktrace_test_cflags := \
 libbacktrace_test_src_files := \
        backtrace_testlib.c \
 
+libbacktrace_test_strip_module := false
+
 module := libbacktrace_test
 module_tag := debug
 build_type := target
@@ -107,6 +128,7 @@ backtrace_test_cflags_target := \
        -DENABLE_PSS_TESTS \
 
 backtrace_test_src_files := \
+       backtrace_offline_test.cpp \
        backtrace_test.cpp \
        GetPss.cpp \
        thread_utils.c \
@@ -120,6 +142,7 @@ backtrace_test_shared_libraries := \
        libbacktrace \
        libbase \
        libcutils \
+       libunwind \
 
 backtrace_test_shared_libraries_target += \
        libdl \
@@ -127,6 +150,8 @@ backtrace_test_shared_libraries_target += \
 backtrace_test_ldlibs_host += \
        -ldl \
 
+backtrace_test_strip_module := false
+
 module := backtrace_test
 module_tag := debug
 build_type := target
index 97f0ef4..9ead452 100644 (file)
@@ -30,6 +30,7 @@
 #include <cutils/threads.h>
 
 #include "BacktraceLog.h"
+#include "BacktraceOffline.h"
 #include "thread_utils.h"
 #include "UnwindCurrent.h"
 #include "UnwindPtrace.h"
@@ -140,3 +141,8 @@ Backtrace* Backtrace::Create(pid_t pid, pid_t tid, BacktraceMap* map) {
     return new UnwindPtrace(pid, tid, map);
   }
 }
+
+Backtrace* Backtrace::CreateOffline(pid_t pid, pid_t tid, BacktraceMap* map,
+                                    const backtrace_stackinfo_t& stack, bool cache_file) {
+  return new BacktraceOffline(pid, tid, map, stack, cache_file);
+}
index b0ada46..ba86632 100644 (file)
@@ -63,7 +63,7 @@ bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) {
 // 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so\n
 // 012345678901234567890123456789012345678901234567890123456789
 // 0         1         2         3         4         5
-  if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n",
+  if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d %n",
              &start, &end, permissions, &name_pos) != 3) {
 #endif
     return false;
@@ -82,9 +82,6 @@ bool BacktraceMap::ParseLine(const char* line, backtrace_map_t* map) {
     map->flags |= PROT_EXEC;
   }
 
-  while (isspace(line[name_pos])) {
-    name_pos += 1;
-  }
   map->name = line+name_pos;
   if (!map->name.empty() && map->name[map->name.length()-1] == '\n') {
     map->name.erase(map->name.length()-1);
@@ -135,7 +132,7 @@ bool BacktraceMap::Build() {
 #if defined(__APPLE__)
 // Corkscrew and libunwind don't compile on the mac, so create a generic
 // map object.
-BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
+BacktraceMap* BacktraceMap::Create(pid_t pid, bool /*uncached*/) {
   BacktraceMap* map = new BacktraceMap(pid);
   if (!map->Build()) {
     delete map;
@@ -144,3 +141,13 @@ BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
   return map;
 }
 #endif
+
+BacktraceMap* BacktraceMap::Create(pid_t pid, const std::vector<backtrace_map_t>& maps) {
+    BacktraceMap* backtrace_map = new BacktraceMap(pid);
+    backtrace_map->maps_.insert(backtrace_map->maps_.begin(), maps.begin(), maps.end());
+    std::sort(backtrace_map->maps_.begin(), backtrace_map->maps_.end(),
+            [](const backtrace_map_t& map1, const backtrace_map_t& map2) {
+              return map1.start < map2.start;
+            });
+    return backtrace_map;
+}
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
new file mode 100644 (file)
index 0000000..27dfb83
--- /dev/null
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BacktraceOffline.h"
+
+extern "C" {
+#define UNW_REMOTE_ONLY
+#include <dwarf.h>
+}
+
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+
+#include <llvm/ADT/StringRef.h>
+#include <llvm/Object/Binary.h>
+#include <llvm/Object/ELFObjectFile.h>
+#include <llvm/Object/ObjectFile.h>
+
+#pragma clang diagnostic pop
+
+#include "BacktraceLog.h"
+
+void Space::Clear() {
+  start = 0;
+  end = 0;
+  data = nullptr;
+}
+
+size_t Space::Read(uint64_t addr, uint8_t* buffer, size_t size) {
+  if (addr >= start && addr < end) {
+    size_t read_size = std::min(size, static_cast<size_t>(end - addr));
+    memcpy(buffer, data + (addr - start), read_size);
+    return read_size;
+  }
+  return 0;
+}
+
+static int FindProcInfo(unw_addr_space_t addr_space, unw_word_t ip, unw_proc_info* proc_info,
+                        int need_unwind_info, void* arg) {
+  BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
+  bool result = backtrace->FindProcInfo(addr_space, ip, proc_info, need_unwind_info);
+  return result ? 0 : -UNW_EINVAL;
+}
+
+static void PutUnwindInfo(unw_addr_space_t, unw_proc_info_t*, void*) {
+}
+
+static int GetDynInfoListAddr(unw_addr_space_t, unw_word_t*, void*) {
+  return -UNW_ENOINFO;
+}
+
+static int AccessMem(unw_addr_space_t, unw_word_t addr, unw_word_t* value, int write, void* arg) {
+  if (write == 1) {
+    return -UNW_EINVAL;
+  }
+  BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
+  *value = 0;
+  size_t read_size = backtrace->Read(addr, reinterpret_cast<uint8_t*>(value), sizeof(unw_word_t));
+  // Strictly we should check if read_size matches sizeof(unw_word_t), but it is possible in
+  // .eh_frame_hdr that the section can end at a position not aligned in sizeof(unw_word_t), and
+  // we should permit the read at the end of the section.
+  return (read_size > 0u ? 0 : -UNW_EINVAL);
+}
+
+static int AccessReg(unw_addr_space_t, unw_regnum_t unwind_reg, unw_word_t* value, int write,
+                     void* arg) {
+  if (write == 1) {
+    return -UNW_EINVAL;
+  }
+  BacktraceOffline* backtrace = reinterpret_cast<BacktraceOffline*>(arg);
+  uint64_t reg_value;
+  bool result = backtrace->ReadReg(unwind_reg, &reg_value);
+  if (result) {
+    *value = static_cast<unw_word_t>(reg_value);
+  }
+  return result ? 0 : -UNW_EINVAL;
+}
+
+static int AccessFpReg(unw_addr_space_t, unw_regnum_t, unw_fpreg_t*, int, void*) {
+  return -UNW_EINVAL;
+}
+
+static int Resume(unw_addr_space_t, unw_cursor_t*, void*) {
+  return -UNW_EINVAL;
+}
+
+static int GetProcName(unw_addr_space_t, unw_word_t, char*, size_t, unw_word_t*, void*) {
+  return -UNW_EINVAL;
+}
+
+static unw_accessors_t accessors = {
+    .find_proc_info = FindProcInfo,
+    .put_unwind_info = PutUnwindInfo,
+    .get_dyn_info_list_addr = GetDynInfoListAddr,
+    .access_mem = AccessMem,
+    .access_reg = AccessReg,
+    .access_fpreg = AccessFpReg,
+    .resume = Resume,
+    .get_proc_name = GetProcName,
+};
+
+bool BacktraceOffline::Unwind(size_t num_ignore_frames, ucontext_t* context) {
+  if (context == nullptr) {
+    BACK_LOGW("The context is needed for offline backtracing.");
+    return false;
+  }
+  context_ = context;
+
+  unw_addr_space_t addr_space = unw_create_addr_space(&accessors, 0);
+  unw_cursor_t cursor;
+  int ret = unw_init_remote(&cursor, addr_space, this);
+  if (ret != 0) {
+    BACK_LOGW("unw_init_remote failed %d", ret);
+    unw_destroy_addr_space(addr_space);
+    return false;
+  }
+  size_t num_frames = 0;
+  do {
+    unw_word_t pc;
+    ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
+    if (ret < 0) {
+      BACK_LOGW("Failed to read IP %d", ret);
+      break;
+    }
+    unw_word_t sp;
+    ret = unw_get_reg(&cursor, UNW_REG_SP, &sp);
+    if (ret < 0) {
+      BACK_LOGW("Failed to read SP %d", ret);
+      break;
+    }
+
+    if (num_ignore_frames == 0) {
+      frames_.resize(num_frames + 1);
+      backtrace_frame_data_t* frame = &frames_[num_frames];
+      frame->num = num_frames;
+      frame->pc = static_cast<uintptr_t>(pc);
+      frame->sp = static_cast<uintptr_t>(sp);
+      frame->stack_size = 0;
+
+      if (num_frames > 0) {
+        backtrace_frame_data_t* prev = &frames_[num_frames - 1];
+        prev->stack_size = frame->sp - prev->sp;
+      }
+      frame->func_name = GetFunctionName(frame->pc, &frame->func_offset);
+      FillInMap(frame->pc, &frame->map);
+      num_frames++;
+    } else {
+      num_ignore_frames--;
+    }
+    ret = unw_step(&cursor);
+  } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES);
+
+  unw_destroy_addr_space(addr_space);
+  context_ = nullptr;
+  return true;
+}
+
+bool BacktraceOffline::ReadWord(uintptr_t ptr, word_t* out_value) {
+  size_t bytes_read = Read(ptr, reinterpret_cast<uint8_t*>(out_value), sizeof(word_t));
+  return bytes_read == sizeof(word_t);
+}
+
+size_t BacktraceOffline::Read(uintptr_t addr, uint8_t* buffer, size_t bytes) {
+  // Normally, libunwind needs stack information and call frame information to do remote unwinding.
+  // If call frame information is stored in .debug_frame, libunwind can read it from file
+  // by itself. If call frame information is stored in .eh_frame, we need to provide data in
+  // .eh_frame/.eh_frame_hdr sections.
+  // The order of readings below doesn't matter, as the spaces don't overlap with each other.
+  size_t read_size = eh_frame_hdr_space_.Read(addr, buffer, bytes);
+  if (read_size != 0) {
+    return read_size;
+  }
+  read_size = eh_frame_space_.Read(addr, buffer, bytes);
+  if (read_size != 0) {
+    return read_size;
+  }
+  read_size = stack_space_.Read(addr, buffer, bytes);
+  return read_size;
+}
+
+static bool FileOffsetToVaddr(
+    const std::vector<DebugFrameInfo::EhFrame::ProgramHeader>& program_headers,
+    uint64_t file_offset, uint64_t* vaddr) {
+  for (auto& header : program_headers) {
+    if (file_offset >= header.file_offset && file_offset < header.file_offset + header.file_size) {
+      // TODO: Consider load_bias?
+      *vaddr = file_offset - header.file_offset + header.vaddr;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool BacktraceOffline::FindProcInfo(unw_addr_space_t addr_space, uint64_t ip,
+                                    unw_proc_info_t* proc_info, int need_unwind_info) {
+  backtrace_map_t map;
+  FillInMap(ip, &map);
+  if (!BacktraceMap::IsValid(map)) {
+    return false;
+  }
+  const std::string& filename = map.name;
+  DebugFrameInfo* debug_frame = GetDebugFrameInFile(filename);
+  if (debug_frame == nullptr) {
+    return false;
+  }
+  if (debug_frame->is_eh_frame) {
+    uint64_t ip_offset = ip - map.start + map.offset;
+    uint64_t ip_vaddr;  // vaddr in the elf file.
+    bool result = FileOffsetToVaddr(debug_frame->eh_frame.program_headers, ip_offset, &ip_vaddr);
+    if (!result) {
+      return false;
+    }
+    // Calculate the addresses where .eh_frame_hdr and .eh_frame stay when the process was running.
+    eh_frame_hdr_space_.start = (ip - ip_vaddr) + debug_frame->eh_frame.eh_frame_hdr_vaddr;
+    eh_frame_hdr_space_.end =
+        eh_frame_hdr_space_.start + debug_frame->eh_frame.eh_frame_hdr_data.size();
+    eh_frame_hdr_space_.data = debug_frame->eh_frame.eh_frame_hdr_data.data();
+
+    eh_frame_space_.start = (ip - ip_vaddr) + debug_frame->eh_frame.eh_frame_vaddr;
+    eh_frame_space_.end = eh_frame_space_.start + debug_frame->eh_frame.eh_frame_data.size();
+    eh_frame_space_.data = debug_frame->eh_frame.eh_frame_data.data();
+
+    unw_dyn_info di;
+    memset(&di, '\0', sizeof(di));
+    di.start_ip = map.start;
+    di.end_ip = map.end;
+    di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
+    di.u.rti.name_ptr = 0;
+    di.u.rti.segbase = eh_frame_hdr_space_.start;
+    di.u.rti.table_data =
+        eh_frame_hdr_space_.start + debug_frame->eh_frame.fde_table_offset_in_eh_frame_hdr;
+    di.u.rti.table_len = (eh_frame_hdr_space_.end - di.u.rti.table_data) / sizeof(unw_word_t);
+    int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this);
+    return ret == 0;
+  }
+
+  eh_frame_hdr_space_.Clear();
+  eh_frame_space_.Clear();
+  unw_dyn_info_t di;
+  unw_word_t segbase = map.start - map.offset;
+  int found = dwarf_find_debug_frame(0, &di, ip, segbase, filename.c_str(), map.start, map.end);
+  if (found == 1) {
+    int ret = dwarf_search_unwind_table(addr_space, ip, &di, proc_info, need_unwind_info, this);
+    return ret == 0;
+  }
+  return false;
+}
+
+bool BacktraceOffline::ReadReg(size_t reg, uint64_t* value) {
+  bool result = true;
+#if defined(__arm__)
+  switch (reg) {
+    case UNW_ARM_R0:
+      *value = context_->uc_mcontext.arm_r0;
+      break;
+    case UNW_ARM_R1:
+      *value = context_->uc_mcontext.arm_r1;
+      break;
+    case UNW_ARM_R2:
+      *value = context_->uc_mcontext.arm_r2;
+      break;
+    case UNW_ARM_R3:
+      *value = context_->uc_mcontext.arm_r3;
+      break;
+    case UNW_ARM_R4:
+      *value = context_->uc_mcontext.arm_r4;
+      break;
+    case UNW_ARM_R5:
+      *value = context_->uc_mcontext.arm_r5;
+      break;
+    case UNW_ARM_R6:
+      *value = context_->uc_mcontext.arm_r6;
+      break;
+    case UNW_ARM_R7:
+      *value = context_->uc_mcontext.arm_r7;
+      break;
+    case UNW_ARM_R8:
+      *value = context_->uc_mcontext.arm_r8;
+      break;
+    case UNW_ARM_R9:
+      *value = context_->uc_mcontext.arm_r9;
+      break;
+    case UNW_ARM_R10:
+      *value = context_->uc_mcontext.arm_r10;
+      break;
+    case UNW_ARM_R11:
+      *value = context_->uc_mcontext.arm_fp;
+      break;
+    case UNW_ARM_R12:
+      *value = context_->uc_mcontext.arm_ip;
+      break;
+    case UNW_ARM_R13:
+      *value = context_->uc_mcontext.arm_sp;
+      break;
+    case UNW_ARM_R14:
+      *value = context_->uc_mcontext.arm_lr;
+      break;
+    case UNW_ARM_R15:
+      *value = context_->uc_mcontext.arm_pc;
+      break;
+    default:
+      result = false;
+  }
+#elif defined(__aarch64__)
+  if (reg <= UNW_AARCH64_PC) {
+    *value = context_->uc_mcontext.regs[reg];
+  } else {
+    result = false;
+  }
+#elif defined(__x86_64__)
+  switch (reg) {
+    case UNW_X86_64_R8:
+      *value = context_->uc_mcontext.gregs[REG_R8];
+      break;
+    case UNW_X86_64_R9:
+      *value = context_->uc_mcontext.gregs[REG_R9];
+      break;
+    case UNW_X86_64_R10:
+      *value = context_->uc_mcontext.gregs[REG_R10];
+      break;
+    case UNW_X86_64_R11:
+      *value = context_->uc_mcontext.gregs[REG_R11];
+      break;
+    case UNW_X86_64_R12:
+      *value = context_->uc_mcontext.gregs[REG_R12];
+      break;
+    case UNW_X86_64_R13:
+      *value = context_->uc_mcontext.gregs[REG_R13];
+      break;
+    case UNW_X86_64_R14:
+      *value = context_->uc_mcontext.gregs[REG_R14];
+      break;
+    case UNW_X86_64_R15:
+      *value = context_->uc_mcontext.gregs[REG_R15];
+      break;
+    case UNW_X86_64_RDI:
+      *value = context_->uc_mcontext.gregs[REG_RDI];
+      break;
+    case UNW_X86_64_RSI:
+      *value = context_->uc_mcontext.gregs[REG_RSI];
+      break;
+    case UNW_X86_64_RBP:
+      *value = context_->uc_mcontext.gregs[REG_RBP];
+      break;
+    case UNW_X86_64_RBX:
+      *value = context_->uc_mcontext.gregs[REG_RBX];
+      break;
+    case UNW_X86_64_RDX:
+      *value = context_->uc_mcontext.gregs[REG_RDX];
+      break;
+    case UNW_X86_64_RAX:
+      *value = context_->uc_mcontext.gregs[REG_RAX];
+      break;
+    case UNW_X86_64_RCX:
+      *value = context_->uc_mcontext.gregs[REG_RCX];
+      break;
+    case UNW_X86_64_RSP:
+      *value = context_->uc_mcontext.gregs[REG_RSP];
+      break;
+    case UNW_X86_64_RIP:
+      *value = context_->uc_mcontext.gregs[REG_RIP];
+      break;
+    default:
+      result = false;
+  }
+#elif defined(__i386__)
+  switch (reg) {
+    case UNW_X86_GS:
+      *value = context_->uc_mcontext.gregs[REG_GS];
+      break;
+    case UNW_X86_FS:
+      *value = context_->uc_mcontext.gregs[REG_FS];
+      break;
+    case UNW_X86_ES:
+      *value = context_->uc_mcontext.gregs[REG_ES];
+      break;
+    case UNW_X86_DS:
+      *value = context_->uc_mcontext.gregs[REG_DS];
+      break;
+    case UNW_X86_EAX:
+      *value = context_->uc_mcontext.gregs[REG_EAX];
+      break;
+    case UNW_X86_EBX:
+      *value = context_->uc_mcontext.gregs[REG_EBX];
+      break;
+    case UNW_X86_ECX:
+      *value = context_->uc_mcontext.gregs[REG_ECX];
+      break;
+    case UNW_X86_EDX:
+      *value = context_->uc_mcontext.gregs[REG_EDX];
+      break;
+    case UNW_X86_ESI:
+      *value = context_->uc_mcontext.gregs[REG_ESI];
+      break;
+    case UNW_X86_EDI:
+      *value = context_->uc_mcontext.gregs[REG_EDI];
+      break;
+    case UNW_X86_EBP:
+      *value = context_->uc_mcontext.gregs[REG_EBP];
+      break;
+    case UNW_X86_EIP:
+      *value = context_->uc_mcontext.gregs[REG_EIP];
+      break;
+    case UNW_X86_ESP:
+      *value = context_->uc_mcontext.gregs[REG_ESP];
+      break;
+    case UNW_X86_TRAPNO:
+      *value = context_->uc_mcontext.gregs[REG_TRAPNO];
+      break;
+    case UNW_X86_CS:
+      *value = context_->uc_mcontext.gregs[REG_CS];
+      break;
+    case UNW_X86_EFLAGS:
+      *value = context_->uc_mcontext.gregs[REG_EFL];
+      break;
+    case UNW_X86_SS:
+      *value = context_->uc_mcontext.gregs[REG_SS];
+      break;
+    default:
+      result = false;
+  }
+#endif
+  return result;
+}
+
+std::string BacktraceOffline::GetFunctionNameRaw(uintptr_t, uintptr_t* offset) {
+  // We don't have enough information to support this. And it is expensive.
+  *offset = 0;
+  return "";
+}
+
+std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>> BacktraceOffline::debug_frames_;
+std::unordered_set<std::string> BacktraceOffline::debug_frame_missing_files_;
+
+static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename);
+
+DebugFrameInfo* BacktraceOffline::GetDebugFrameInFile(const std::string& filename) {
+  if (cache_file_) {
+    auto it = debug_frames_.find(filename);
+    if (it != debug_frames_.end()) {
+      return it->second.get();
+    }
+    if (debug_frame_missing_files_.find(filename) != debug_frame_missing_files_.end()) {
+      return nullptr;
+    }
+  }
+  DebugFrameInfo* debug_frame = ReadDebugFrameFromFile(filename);
+  if (cache_file_) {
+    if (debug_frame != nullptr) {
+      debug_frames_.emplace(filename, std::unique_ptr<DebugFrameInfo>(debug_frame));
+    } else {
+      debug_frame_missing_files_.insert(filename);
+    }
+  } else {
+    if (last_debug_frame_ != nullptr) {
+      delete last_debug_frame_;
+    }
+    last_debug_frame_ = debug_frame;
+  }
+  return debug_frame;
+}
+
+static bool OmitEncodedValue(uint8_t encode, const uint8_t*& p) {
+  if (encode == DW_EH_PE_omit) {
+    return 0;
+  }
+  uint8_t format = encode & 0x0f;
+  switch (format) {
+    case DW_EH_PE_ptr:
+      p += sizeof(unw_word_t);
+      break;
+    case DW_EH_PE_uleb128:
+    case DW_EH_PE_sleb128:
+      while ((*p & 0x80) != 0) {
+        ++p;
+      }
+      ++p;
+      break;
+    case DW_EH_PE_udata2:
+    case DW_EH_PE_sdata2:
+      p += 2;
+      break;
+    case DW_EH_PE_udata4:
+    case DW_EH_PE_sdata4:
+      p += 4;
+      break;
+    case DW_EH_PE_udata8:
+    case DW_EH_PE_sdata8:
+      p += 8;
+      break;
+    default:
+      return false;
+  }
+  return true;
+}
+
+static bool GetFdeTableOffsetInEhFrameHdr(const std::vector<uint8_t>& data,
+                                          uint64_t* table_offset_in_eh_frame_hdr) {
+  const uint8_t* p = data.data();
+  const uint8_t* end = p + data.size();
+  if (p + 4 > end) {
+    return false;
+  }
+  uint8_t version = *p++;
+  if (version != 1) {
+    return false;
+  }
+  uint8_t eh_frame_ptr_encode = *p++;
+  uint8_t fde_count_encode = *p++;
+  uint8_t fde_table_encode = *p++;
+
+  if (fde_table_encode != (DW_EH_PE_datarel | DW_EH_PE_sdata4)) {
+    return false;
+  }
+
+  if (!OmitEncodedValue(eh_frame_ptr_encode, p) || !OmitEncodedValue(fde_count_encode, p)) {
+    return false;
+  }
+  if (p >= end) {
+    return false;
+  }
+  *table_offset_in_eh_frame_hdr = p - data.data();
+  return true;
+}
+
+using ProgramHeader = DebugFrameInfo::EhFrame::ProgramHeader;
+
+template <class ELFT>
+DebugFrameInfo* ReadDebugFrameFromELFFile(const llvm::object::ELFFile<ELFT>* elf) {
+  bool has_eh_frame_hdr = false;
+  uint64_t eh_frame_hdr_vaddr = 0;
+  std::vector<uint8_t> eh_frame_hdr_data;
+  bool has_eh_frame = false;
+  uint64_t eh_frame_vaddr = 0;
+  std::vector<uint8_t> eh_frame_data;
+
+  for (auto it = elf->begin_sections(); it != elf->end_sections(); ++it) {
+    llvm::ErrorOr<llvm::StringRef> name = elf->getSectionName(&*it);
+    if (name) {
+      if (name.get() == ".debug_frame") {
+        DebugFrameInfo* debug_frame = new DebugFrameInfo;
+        debug_frame->is_eh_frame = false;
+        return debug_frame;
+      }
+      if (name.get() == ".eh_frame_hdr") {
+        has_eh_frame_hdr = true;
+        eh_frame_hdr_vaddr = it->sh_addr;
+        llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
+        if (data) {
+          eh_frame_hdr_data.insert(eh_frame_hdr_data.begin(), data->data(),
+                                   data->data() + data->size());
+        } else {
+          return nullptr;
+        }
+      } else if (name.get() == ".eh_frame") {
+        has_eh_frame = true;
+        eh_frame_vaddr = it->sh_addr;
+        llvm::ErrorOr<llvm::ArrayRef<uint8_t>> data = elf->getSectionContents(&*it);
+        if (data) {
+          eh_frame_data.insert(eh_frame_data.begin(), data->data(), data->data() + data->size());
+        } else {
+          return nullptr;
+        }
+      }
+    }
+  }
+  if (!(has_eh_frame_hdr && has_eh_frame)) {
+    return nullptr;
+  }
+  uint64_t fde_table_offset;
+  if (!GetFdeTableOffsetInEhFrameHdr(eh_frame_hdr_data, &fde_table_offset)) {
+    return nullptr;
+  }
+
+  std::vector<ProgramHeader> program_headers;
+  for (auto it = elf->begin_program_headers(); it != elf->end_program_headers(); ++it) {
+    ProgramHeader header;
+    header.vaddr = it->p_vaddr;
+    header.file_offset = it->p_offset;
+    header.file_size = it->p_filesz;
+    program_headers.push_back(header);
+  }
+  DebugFrameInfo* debug_frame = new DebugFrameInfo;
+  debug_frame->is_eh_frame = true;
+  debug_frame->eh_frame.eh_frame_hdr_vaddr = eh_frame_hdr_vaddr;
+  debug_frame->eh_frame.eh_frame_vaddr = eh_frame_vaddr;
+  debug_frame->eh_frame.fde_table_offset_in_eh_frame_hdr = fde_table_offset;
+  debug_frame->eh_frame.eh_frame_hdr_data = std::move(eh_frame_hdr_data);
+  debug_frame->eh_frame.eh_frame_data = std::move(eh_frame_data);
+  debug_frame->eh_frame.program_headers = program_headers;
+  return debug_frame;
+}
+
+static DebugFrameInfo* ReadDebugFrameFromFile(const std::string& filename) {
+  auto owning_binary = llvm::object::createBinary(llvm::StringRef(filename));
+  if (owning_binary.getError()) {
+    return nullptr;
+  }
+  llvm::object::Binary* binary = owning_binary.get().getBinary();
+  auto obj = llvm::dyn_cast<llvm::object::ObjectFile>(binary);
+  if (obj == nullptr) {
+    return nullptr;
+  }
+  if (auto elf = llvm::dyn_cast<llvm::object::ELF32LEObjectFile>(obj)) {
+    return ReadDebugFrameFromELFFile(elf->getELFFile());
+  }
+  if (auto elf = llvm::dyn_cast<llvm::object::ELF64LEObjectFile>(obj)) {
+    return ReadDebugFrameFromELFFile(elf->getELFFile());
+  }
+  return nullptr;
+}
diff --git a/libbacktrace/BacktraceOffline.h b/libbacktrace/BacktraceOffline.h
new file mode 100644 (file)
index 0000000..42f826d
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBBACKTRACE_UNWIND_OFFLINE_H
+#define _LIBBACKTRACE_UNWIND_OFFLINE_H
+
+#include <libunwind.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <ucontext.h>
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include <backtrace/Backtrace.h>
+
+struct Space {
+  uint64_t start;
+  uint64_t end;
+  const uint8_t* data;
+
+  Space() {
+    Clear();
+  }
+
+  void Clear();
+  size_t Read(uint64_t addr, uint8_t* buffer, size_t size);
+};
+
+struct DebugFrameInfo {
+  bool is_eh_frame;
+  struct EhFrame {
+    uint64_t eh_frame_hdr_vaddr;
+    uint64_t eh_frame_vaddr;
+    uint64_t fde_table_offset_in_eh_frame_hdr;
+    std::vector<uint8_t> eh_frame_hdr_data;
+    std::vector<uint8_t> eh_frame_data;
+    struct ProgramHeader {
+      uint64_t vaddr;
+      uint64_t file_offset;
+      uint64_t file_size;
+    };
+    std::vector<ProgramHeader> program_headers;
+  } eh_frame;
+};
+
+class BacktraceOffline : public Backtrace {
+ public:
+  BacktraceOffline(pid_t pid, pid_t tid, BacktraceMap* map, const backtrace_stackinfo_t& stack,
+                   bool cache_file)
+      : Backtrace(pid, tid, map),
+        cache_file_(cache_file),
+        context_(nullptr),
+        last_debug_frame_(nullptr) {
+    stack_space_.start = stack.start;
+    stack_space_.end = stack.end;
+    stack_space_.data = stack.data;
+  }
+
+  virtual ~BacktraceOffline() {
+    if (last_debug_frame_ != nullptr) {
+      delete last_debug_frame_;
+    }
+  }
+
+  bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
+
+  bool ReadWord(uintptr_t ptr, word_t* out_value) override;
+
+  size_t Read(uintptr_t addr, uint8_t* buffer, size_t bytes) override;
+
+  bool FindProcInfo(unw_addr_space_t addr_space, uint64_t ip, unw_proc_info_t* proc_info,
+                    int need_unwind_info);
+
+  bool ReadReg(size_t reg_index, uint64_t* value);
+
+ protected:
+  std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
+  DebugFrameInfo* GetDebugFrameInFile(const std::string& filename);
+
+  static std::unordered_map<std::string, std::unique_ptr<DebugFrameInfo>> debug_frames_;
+  static std::unordered_set<std::string> debug_frame_missing_files_;
+
+  bool cache_file_;
+  ucontext_t* context_;
+  Space eh_frame_hdr_space_;
+  Space eh_frame_space_;
+  Space stack_space_;
+  DebugFrameInfo* last_debug_frame_;
+};
+
+#endif  // _LIBBACKTRACE_BACKTRACE_OFFLINE_H
diff --git a/libbacktrace/backtrace_offline_test.cpp b/libbacktrace/backtrace_offline_test.cpp
new file mode 100644 (file)
index 0000000..88a3533
--- /dev/null
@@ -0,0 +1,189 @@
+#include <libunwind.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <backtrace/Backtrace.h>
+#include <backtrace/BacktraceMap.h>
+#include <cutils/threads.h>
+
+#include <gtest/gtest.h>
+
+extern "C" {
+// Prototypes for functions in the test library.
+int test_level_one(int, int, int, int, void (*)(void*), void*);
+int test_level_two(int, int, int, int, void (*)(void*), void*);
+int test_level_three(int, int, int, int, void (*)(void*), void*);
+int test_level_four(int, int, int, int, void (*)(void*), void*);
+int test_recursive_call(int, void (*)(void*), void*);
+}
+
+static volatile bool g_exit_flag = false;
+
+static void GetContextAndExit(void* arg) {
+  unw_context_t* unw_context = reinterpret_cast<unw_context_t*>(arg);
+  unw_getcontext(unw_context);
+  // Don't touch the stack anymore.
+  while (!g_exit_flag) {
+  }
+}
+
+struct OfflineThreadArg {
+  unw_context_t unw_context;
+  pid_t tid;
+  std::function<int(void (*)(void*), void*)> function;
+};
+
+static void* OfflineThreadFunc(void* arg) {
+  OfflineThreadArg* fn_arg = reinterpret_cast<OfflineThreadArg*>(arg);
+  fn_arg->tid = gettid();
+  fn_arg->function(GetContextAndExit, &fn_arg->unw_context);
+  return nullptr;
+}
+
+static ucontext_t GetUContextFromUnwContext(const unw_context_t& unw_context) {
+  ucontext_t ucontext;
+  memset(&ucontext, 0, sizeof(ucontext));
+#if defined(__arm__)
+  ucontext.uc_mcontext.arm_r0 = unw_context.regs[0];
+  ucontext.uc_mcontext.arm_r1 = unw_context.regs[1];
+  ucontext.uc_mcontext.arm_r2 = unw_context.regs[2];
+  ucontext.uc_mcontext.arm_r3 = unw_context.regs[3];
+  ucontext.uc_mcontext.arm_r4 = unw_context.regs[4];
+  ucontext.uc_mcontext.arm_r5 = unw_context.regs[5];
+  ucontext.uc_mcontext.arm_r6 = unw_context.regs[6];
+  ucontext.uc_mcontext.arm_r7 = unw_context.regs[7];
+  ucontext.uc_mcontext.arm_r8 = unw_context.regs[8];
+  ucontext.uc_mcontext.arm_r9 = unw_context.regs[9];
+  ucontext.uc_mcontext.arm_r10 = unw_context.regs[10];
+  ucontext.uc_mcontext.arm_fp = unw_context.regs[11];
+  ucontext.uc_mcontext.arm_ip = unw_context.regs[12];
+  ucontext.uc_mcontext.arm_sp = unw_context.regs[13];
+  ucontext.uc_mcontext.arm_lr = unw_context.regs[14];
+  ucontext.uc_mcontext.arm_pc = unw_context.regs[15];
+#else
+  ucontext.uc_mcontext = unw_context.uc_mcontext;
+#endif
+  return ucontext;
+}
+
+static void OfflineBacktraceFunctionCall(std::function<int(void (*)(void*), void*)> function,
+                                         std::vector<uintptr_t>* pc_values) {
+  // Create a thread to generate the needed stack and registers information.
+  g_exit_flag = false;
+  const size_t stack_size = 1024 * 1024;
+  void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  ASSERT_NE(MAP_FAILED, stack);
+  uintptr_t stack_addr = reinterpret_cast<uintptr_t>(stack);
+  pthread_attr_t attr;
+  ASSERT_EQ(0, pthread_attr_init(&attr));
+  ASSERT_EQ(0, pthread_attr_setstack(&attr, reinterpret_cast<void*>(stack), stack_size));
+  pthread_t thread;
+  OfflineThreadArg arg;
+  arg.function = function;
+  ASSERT_EQ(0, pthread_create(&thread, &attr, OfflineThreadFunc, &arg));
+  // Wait for the offline thread to generate the stack and unw_context information.
+  sleep(1);
+  // Copy the stack information.
+  std::vector<uint8_t> stack_data(reinterpret_cast<uint8_t*>(stack),
+                                  reinterpret_cast<uint8_t*>(stack) + stack_size);
+  g_exit_flag = true;
+  ASSERT_EQ(0, pthread_join(thread, nullptr));
+  ASSERT_EQ(0, munmap(stack, stack_size));
+
+  // Do offline backtrace.
+  std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
+  ASSERT_TRUE(map != nullptr);
+
+  backtrace_stackinfo_t stack_info;
+  stack_info.start = stack_addr;
+  stack_info.end = stack_addr + stack_size;
+  stack_info.data = stack_data.data();
+
+  std::unique_ptr<Backtrace> backtrace(
+      Backtrace::CreateOffline(getpid(), arg.tid, map.get(), stack_info));
+  ASSERT_TRUE(backtrace != nullptr);
+
+  ucontext_t ucontext = GetUContextFromUnwContext(arg.unw_context);
+  ASSERT_TRUE(backtrace->Unwind(0, &ucontext));
+
+  // Collect pc values of the call stack frames.
+  for (size_t i = 0; i < backtrace->NumFrames(); ++i) {
+    pc_values->push_back(backtrace->GetFrame(i)->pc);
+  }
+}
+
+// Return the name of the function which matches the address. Although we don't know the
+// exact end of each function, it is accurate enough for the tests.
+static std::string FunctionNameForAddress(uintptr_t addr) {
+  struct FunctionSymbol {
+    std::string name;
+    uintptr_t start;
+    uintptr_t end;
+  };
+
+  static std::vector<FunctionSymbol> symbols;
+  if (symbols.empty()) {
+    symbols = std::vector<FunctionSymbol>{
+        {"unknown_start", 0, 0},
+        {"test_level_one", reinterpret_cast<uintptr_t>(&test_level_one), 0},
+        {"test_level_two", reinterpret_cast<uintptr_t>(&test_level_two), 0},
+        {"test_level_three", reinterpret_cast<uintptr_t>(&test_level_three), 0},
+        {"test_level_four", reinterpret_cast<uintptr_t>(&test_level_four), 0},
+        {"test_recursive_call", reinterpret_cast<uintptr_t>(&test_recursive_call), 0},
+        {"GetContextAndExit", reinterpret_cast<uintptr_t>(&GetContextAndExit), 0},
+        {"unknown_end", static_cast<uintptr_t>(-1), static_cast<uintptr_t>(-1)},
+    };
+    std::sort(
+        symbols.begin(), symbols.end(),
+        [](const FunctionSymbol& s1, const FunctionSymbol& s2) { return s1.start < s2.start; });
+    for (size_t i = 0; i + 1 < symbols.size(); ++i) {
+      symbols[i].end = symbols[i + 1].start;
+    }
+  }
+  for (auto& symbol : symbols) {
+    if (addr >= symbol.start && addr < symbol.end) {
+      return symbol.name;
+    }
+  }
+  return "";
+}
+
+TEST(libbacktrace, offline) {
+  std::function<int(void (*)(void*), void*)> function =
+      std::bind(test_level_one, 1, 2, 3, 4, std::placeholders::_1, std::placeholders::_2);
+  std::vector<uintptr_t> pc_values;
+  OfflineBacktraceFunctionCall(function, &pc_values);
+  ASSERT_FALSE(pc_values.empty());
+  ASSERT_LE(pc_values.size(), static_cast<size_t>(MAX_BACKTRACE_FRAMES));
+
+  size_t test_one_index = 0;
+  for (size_t i = 0; i < pc_values.size(); ++i) {
+    if (FunctionNameForAddress(pc_values[i]) == "test_level_one") {
+      test_one_index = i;
+      break;
+    }
+  }
+
+  ASSERT_GE(test_one_index, 3u);
+  ASSERT_EQ("test_level_one", FunctionNameForAddress(pc_values[test_one_index]));
+  ASSERT_EQ("test_level_two", FunctionNameForAddress(pc_values[test_one_index - 1]));
+  ASSERT_EQ("test_level_three", FunctionNameForAddress(pc_values[test_one_index - 2]));
+  ASSERT_EQ("test_level_four", FunctionNameForAddress(pc_values[test_one_index - 3]));
+}
+
+TEST(libbacktrace, offline_max_trace) {
+  std::function<int(void (*)(void*), void*)> function = std::bind(
+      test_recursive_call, MAX_BACKTRACE_FRAMES + 10, std::placeholders::_1, std::placeholders::_2);
+  std::vector<uintptr_t> pc_values;
+  OfflineBacktraceFunctionCall(function, &pc_values);
+  ASSERT_FALSE(pc_values.empty());
+  ASSERT_EQ(static_cast<size_t>(MAX_BACKTRACE_FRAMES), pc_values.size());
+  ASSERT_EQ("test_recursive_call", FunctionNameForAddress(pc_values.back()));
+}
index c650755..9ebd639 100644 (file)
@@ -983,8 +983,8 @@ void RunReadTest(Backtrace* backtrace, uintptr_t read_addr) {
           << "Offset at " << i << " length " << j << " wrote too much data";
     }
   }
-  delete data;
-  delete expected;
+  delete[] data;
+  delete[] expected;
 }
 
 TEST(libbacktrace, thread_read) {
diff --git a/libbacktrace/map_info.c b/libbacktrace/map_info.c
deleted file mode 100644 (file)
index 073b24a..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <log/log.h>
-#include <sys/time.h>
-
-#include <backtrace/backtrace.h>
-
-#if defined(__APPLE__)
-
-// Mac OS vmmap(1) output:
-// __TEXT                 0009f000-000a1000 [    8K     8K] r-x/rwx SM=COW  /Volumes/android/dalvik-dev/out/host/darwin-x86/bin/libcorkscrew_test\n
-// 012345678901234567890123456789012345678901234567890123456789
-// 0         1         2         3         4         5
-static backtrace_map_info_t* parse_vmmap_line(const char* line) {
-  unsigned long int start;
-  unsigned long int end;
-  char permissions[4];
-  int name_pos;
-  if (sscanf(line, "%*21c %lx-%lx [%*13c] %3c/%*3c SM=%*3c  %n",
-             &start, &end, permissions, &name_pos) != 3) {
-    return NULL;
-  }
-
-  const char* name = line + name_pos;
-  size_t name_len = strlen(name);
-
-  backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len);
-  if (mi != NULL) {
-    mi->start = start;
-    mi->end = end;
-    mi->is_readable = permissions[0] == 'r';
-    mi->is_writable = permissions[1] == 'w';
-    mi->is_executable = permissions[2] == 'x';
-    memcpy(mi->name, name, name_len);
-    mi->name[name_len - 1] = '\0';
-    ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
-          "is_readable=%d, is_writable=%d is_executable=%d, name=%s",
-          mi->start, mi->end,
-          mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
-  }
-  return mi;
-}
-
-backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid) {
-  char cmd[1024];
-  if (pid < 0) {
-    pid = getpid();
-  }
-  snprintf(cmd, sizeof(cmd), "vmmap -w -resident -submap -allSplitLibs -interleaved %d", pid);
-  FILE* fp = popen(cmd, "r");
-  if (fp == NULL) {
-    return NULL;
-  }
-
-  char line[1024];
-  backtrace_map_info_t* milist = NULL;
-  while (fgets(line, sizeof(line), fp) != NULL) {
-    backtrace_map_info_t* mi = parse_vmmap_line(line);
-    if (mi != NULL) {
-      mi->next = milist;
-      milist = mi;
-    }
-  }
-  pclose(fp);
-  return milist;
-}
-
-#else
-
-// Linux /proc/<pid>/maps lines:
-// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so\n
-// 012345678901234567890123456789012345678901234567890123456789
-// 0         1         2         3         4         5
-static backtrace_map_info_t* parse_maps_line(const char* line)
-{
-  unsigned long int start;
-  unsigned long int end;
-  char permissions[5];
-  int name_pos;
-  if (sscanf(line, "%lx-%lx %4s %*x %*x:%*x %*d%n", &start, &end,
-             permissions, &name_pos) != 3) {
-    return NULL;
-  }
-
-  while (isspace(line[name_pos])) {
-    name_pos += 1;
-  }
-  const char* name = line + name_pos;
-  size_t name_len = strlen(name);
-  if (name_len && name[name_len - 1] == '\n') {
-    name_len -= 1;
-  }
-
-  backtrace_map_info_t* mi = calloc(1, sizeof(backtrace_map_info_t) + name_len + 1);
-  if (mi) {
-    mi->start = start;
-    mi->end = end;
-    mi->is_readable = strlen(permissions) == 4 && permissions[0] == 'r';
-    mi->is_writable = strlen(permissions) == 4 && permissions[1] == 'w';
-    mi->is_executable = strlen(permissions) == 4 && permissions[2] == 'x';
-    memcpy(mi->name, name, name_len);
-    mi->name[name_len] = '\0';
-    ALOGV("Parsed map: start=0x%08x, end=0x%08x, "
-          "is_readable=%d, is_writable=%d, is_executable=%d, name=%s",
-          mi->start, mi->end,
-          mi->is_readable, mi->is_writable, mi->is_executable, mi->name);
-  }
-  return mi;
-}
-
-backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid) {
-  char path[PATH_MAX];
-  char line[1024];
-  FILE* fp;
-  backtrace_map_info_t* milist = NULL;
-
-  if (tid < 0) {
-    tid = getpid();
-  }
-  snprintf(path, PATH_MAX, "/proc/%d/maps", tid);
-  fp = fopen(path, "r");
-  if (fp) {
-    while(fgets(line, sizeof(line), fp)) {
-      backtrace_map_info_t* mi = parse_maps_line(line);
-      if (mi) {
-        mi->next = milist;
-        milist = mi;
-      }
-    }
-    fclose(fp);
-  }
-  return milist;
-}
-
-#endif
-
-void backtrace_destroy_map_info_list(backtrace_map_info_t* milist) {
-  while (milist) {
-    backtrace_map_info_t* next = milist->next;
-    free(milist);
-    milist = next;
-  }
-}
-
-const backtrace_map_info_t* backtrace_find_map_info(
-    const backtrace_map_info_t* milist, uintptr_t addr) {
-  const backtrace_map_info_t* mi = milist;
-  while (mi && !(addr >= mi->start && addr < mi->end)) {
-    mi = mi->next;
-  }
-  return mi;
-}
diff --git a/libbinderwrapper/Android.mk b/libbinderwrapper/Android.mk
new file mode 100644 (file)
index 0000000..23c2246
--- /dev/null
@@ -0,0 +1,62 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+binderwrapperCommonCFlags := -Wall -Werror -Wno-unused-parameter
+binderwrapperCommonCFlags += -Wno-sign-promo  # for libchrome
+binderwrapperCommonExportCIncludeDirs := $(LOCAL_PATH)/../include
+binderwrapperCommonCIncludes := $(LOCAL_PATH)/../include
+binderwrapperCommonSharedLibraries := \
+  libbinder \
+  libchrome \
+  libutils \
+
+# libbinderwrapper shared library
+# ========================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbinderwrapper
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS := $(binderwrapperCommonCFlags)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(binderwrapperCommonExportCIncludeDirs)
+LOCAL_C_INCLUDES := $(binderwrapperCommonCIncludes)
+LOCAL_SHARED_LIBRARIES := $(binderwrapperCommonSharedLibraries)
+LOCAL_SRC_FILES := \
+  binder_wrapper.cc \
+  real_binder_wrapper.cc \
+
+include $(BUILD_SHARED_LIBRARY)
+
+# libbinderwrapper_test_support shared library
+# ========================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libbinderwrapper_test_support
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS := $(binderwrapperCommonCFlags)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(binderwrapperCommonExportCIncludeDirs)
+LOCAL_C_INCLUDES := $(binderwrapperCommonCIncludes)
+LOCAL_STATIC_LIBRARIES := libgtest
+LOCAL_SHARED_LIBRARIES := \
+  $(binderwrapperCommonSharedLibraries) \
+  libbinderwrapper \
+
+LOCAL_SRC_FILES := \
+  binder_test_base.cc \
+  stub_binder_wrapper.cc \
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libbinderwrapper/binder_test_base.cc b/libbinderwrapper/binder_test_base.cc
new file mode 100644 (file)
index 0000000..af93a04
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binderwrapper/binder_test_base.h>
+
+#include <binderwrapper/binder_wrapper.h>
+#include <binderwrapper/stub_binder_wrapper.h>
+
+namespace android {
+
+BinderTestBase::BinderTestBase() : binder_wrapper_(new StubBinderWrapper()) {
+  // Pass ownership.
+  BinderWrapper::InitForTesting(binder_wrapper_);
+}
+
+BinderTestBase::~BinderTestBase() {
+  BinderWrapper::Destroy();
+}
+
+}  // namespace android
diff --git a/libbinderwrapper/binder_wrapper.cc b/libbinderwrapper/binder_wrapper.cc
new file mode 100644 (file)
index 0000000..0b5ff96
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binderwrapper/binder_wrapper.h>
+
+#include <base/logging.h>
+
+#include "real_binder_wrapper.h"
+
+namespace android {
+
+// Singleton instance.
+BinderWrapper* BinderWrapper::instance_ = nullptr;
+
+// static
+void BinderWrapper::Create() {
+  CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
+  instance_ = new RealBinderWrapper();
+}
+
+// static
+void BinderWrapper::InitForTesting(BinderWrapper* wrapper) {
+  CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
+  instance_ = wrapper;
+}
+
+// static
+void BinderWrapper::Destroy() {
+  CHECK(instance_) << "Not initialized; missing call to Create()?";
+  delete instance_;
+  instance_ = nullptr;
+}
+
+// static
+BinderWrapper* BinderWrapper::Get() {
+  CHECK(instance_) << "Not initialized; missing call to Create()?";
+  return instance_;
+}
+
+}  // namespace android
diff --git a/libbinderwrapper/real_binder_wrapper.cc b/libbinderwrapper/real_binder_wrapper.cc
new file mode 100644 (file)
index 0000000..1c51822
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "real_binder_wrapper.h"
+
+#include <base/logging.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// Class that handles binder death notifications. libbinder wants the recipient
+// to be wrapped in sp<>, so registering RealBinderWrapper as a recipient would
+// be awkward.
+class RealBinderWrapper::DeathRecipient : public IBinder::DeathRecipient {
+ public:
+  explicit DeathRecipient(const base::Closure& callback)
+      : callback_(callback) {}
+  ~DeathRecipient() = default;
+
+  // IBinder::DeathRecipient:
+  void binderDied(const wp<IBinder>& who) override {
+    callback_.Run();
+  }
+
+ private:
+  // Callback to run in response to binder death.
+  base::Closure callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeathRecipient);
+};
+
+RealBinderWrapper::RealBinderWrapper() = default;
+
+RealBinderWrapper::~RealBinderWrapper() = default;
+
+sp<IBinder> RealBinderWrapper::GetService(const std::string& service_name) {
+  sp<IServiceManager> service_manager = defaultServiceManager();
+  if (!service_manager.get()) {
+    LOG(ERROR) << "Unable to get service manager";
+    return sp<IBinder>();
+  }
+  sp<IBinder> binder =
+      service_manager->checkService(String16(service_name.c_str()));
+  if (!binder.get())
+    LOG(ERROR) << "Unable to get \"" << service_name << "\" service";
+  return binder;
+}
+
+bool RealBinderWrapper::RegisterService(const std::string& service_name,
+                                        const sp<IBinder>& binder) {
+  sp<IServiceManager> service_manager = defaultServiceManager();
+  if (!service_manager.get()) {
+    LOG(ERROR) << "Unable to get service manager";
+    return false;
+  }
+  status_t status = defaultServiceManager()->addService(
+      String16(service_name.c_str()), binder);
+  if (status != OK) {
+    LOG(ERROR) << "Failed to register \"" << service_name << "\" with service "
+               << "manager";
+    return false;
+  }
+  return true;
+}
+
+sp<BBinder> RealBinderWrapper::CreateLocalBinder() {
+  return sp<BBinder>(new BBinder());
+}
+
+bool RealBinderWrapper::RegisterForDeathNotifications(
+    const sp<IBinder>& binder,
+    const base::Closure& callback) {
+  sp<DeathRecipient> recipient(new DeathRecipient(callback));
+  if (binder->linkToDeath(recipient) != OK) {
+    LOG(ERROR) << "Failed to register for death notifications on "
+               << binder.get();
+    return false;
+  }
+  death_recipients_[binder] = recipient;
+  return true;
+}
+
+bool RealBinderWrapper::UnregisterForDeathNotifications(
+    const sp<IBinder>& binder) {
+  auto it = death_recipients_.find(binder);
+  if (it == death_recipients_.end()) {
+    LOG(ERROR) << "Not registered for death notifications on " << binder.get();
+    return false;
+  }
+  if (binder->unlinkToDeath(it->second) != OK) {
+    LOG(ERROR) << "Failed to unregister for death notifications on "
+               << binder.get();
+    return false;
+  }
+  death_recipients_.erase(it);
+  return true;
+}
+
+uid_t RealBinderWrapper::GetCallingUid() {
+  return IPCThreadState::self()->getCallingUid();
+}
+
+pid_t RealBinderWrapper::GetCallingPid() {
+  return IPCThreadState::self()->getCallingPid();
+}
+
+}  // namespace android
diff --git a/libbinderwrapper/real_binder_wrapper.h b/libbinderwrapper/real_binder_wrapper.h
new file mode 100644 (file)
index 0000000..ea08371
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
+#define SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
+
+#include <base/macros.h>
+#include <binderwrapper/binder_wrapper.h>
+
+namespace android {
+
+class IBinder;
+
+// Real implementation of BinderWrapper.
+class RealBinderWrapper : public BinderWrapper {
+ public:
+  RealBinderWrapper();
+  ~RealBinderWrapper() override;
+
+  // BinderWrapper:
+  sp<IBinder> GetService(const std::string& service_name) override;
+  bool RegisterService(const std::string& service_name,
+                       const sp<IBinder>& binder) override;
+  sp<BBinder> CreateLocalBinder() override;
+  bool RegisterForDeathNotifications(const sp<IBinder>& binder,
+                                     const base::Closure& callback) override;
+  bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
+  uid_t GetCallingUid() override;
+  pid_t GetCallingPid() override;
+
+ private:
+  class DeathRecipient;
+
+  // Map from binder handle to object that should be notified of the binder's
+  // death.
+  std::map<sp<IBinder>, sp<DeathRecipient>> death_recipients_;
+
+  DISALLOW_COPY_AND_ASSIGN(RealBinderWrapper);
+};
+
+}  // namespace android
+
+#endif  // SYSTEM_CORE_LIBBINDER_WRAPPER_REAL_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/stub_binder_wrapper.cc b/libbinderwrapper/stub_binder_wrapper.cc
new file mode 100644 (file)
index 0000000..87c6ab7
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binderwrapper/stub_binder_wrapper.h>
+
+#include <base/logging.h>
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+
+namespace android {
+
+StubBinderWrapper::StubBinderWrapper()
+    : calling_uid_(-1),
+      calling_pid_(-1) {}
+
+StubBinderWrapper::~StubBinderWrapper() = default;
+
+void StubBinderWrapper::SetBinderForService(const std::string& service_name,
+                                            const sp<IBinder>& binder) {
+  services_to_return_[service_name] = binder;
+}
+
+sp<IBinder> StubBinderWrapper::GetRegisteredService(
+    const std::string& service_name) const {
+  const auto it = registered_services_.find(service_name);
+  return it != registered_services_.end() ? it->second : sp<IBinder>();
+}
+
+void StubBinderWrapper::NotifyAboutBinderDeath(const sp<IBinder>& binder) {
+  const auto it = death_callbacks_.find(binder);
+  if (it != death_callbacks_.end())
+    it->second.Run();
+}
+
+sp<IBinder> StubBinderWrapper::GetService(const std::string& service_name) {
+  const auto it = services_to_return_.find(service_name);
+  return it != services_to_return_.end() ? it->second : sp<IBinder>();
+}
+
+bool StubBinderWrapper::RegisterService(const std::string& service_name,
+                                        const sp<IBinder>& binder) {
+  registered_services_[service_name] = binder;
+  return true;
+}
+
+sp<BBinder> StubBinderWrapper::CreateLocalBinder() {
+  sp<BBinder> binder(new BBinder());
+  local_binders_.push_back(binder);
+  return binder;
+}
+
+bool StubBinderWrapper::RegisterForDeathNotifications(
+    const sp<IBinder>& binder,
+    const base::Closure& callback) {
+  death_callbacks_[binder] = callback;
+  return true;
+}
+
+bool StubBinderWrapper::UnregisterForDeathNotifications(
+    const sp<IBinder>& binder) {
+  death_callbacks_.erase(binder);
+  return true;
+}
+
+uid_t StubBinderWrapper::GetCallingUid() {
+  return calling_uid_;
+}
+
+pid_t StubBinderWrapper::GetCallingPid() {
+  return calling_pid_;
+}
+
+}  // namespace android
index da4338b..0f79145 100644 (file)
@@ -37,19 +37,7 @@ commonSources := \
 # 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 += \
+nonWindowsSources := \
         fs.c \
         multiuser.c \
         socket_inaddr_any_server.c \
@@ -60,31 +48,32 @@ ifneq ($(WINDOWS_HOST_ONLY),1)
         socket_network_client.c \
         sockets.c \
 
-    commonHostSources += \
+nonWindowsHostSources := \
         ashmem-host.c \
         trace-host.c
 
-endif
-
 
 # Shared and static library for host
 # ========================================================
 LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES := $(commonSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES_darwin := $(nonWindowsSources) $(nonWindowsHostSources)
+LOCAL_SRC_FILES_linux := $(nonWindowsSources) $(nonWindowsHostSources)
 LOCAL_STATIC_LIBRARIES := liblog
-ifneq ($(HOST_OS),windows)
-LOCAL_CFLAGS += -Werror
-endif
+LOCAL_CFLAGS_darwin := -Werror -Wall -Wextra
+LOCAL_CFLAGS_linux := -Werror -Wall -Wextra
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES := $(commonSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES_darwin := $(nonWindowsSources) $(nonWindowsHostSources)
+LOCAL_SRC_FILES_linux := $(nonWindowsSources) $(nonWindowsHostSources)
 LOCAL_SHARED_LIBRARIES := liblog
-ifneq ($(HOST_OS),windows)
-LOCAL_CFLAGS += -Werror
-endif
+LOCAL_CFLAGS_darwin := -Werror -Wall -Wextra
+LOCAL_CFLAGS_linux := -Werror -Wall -Wextra
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_SHARED_LIBRARY)
 
@@ -96,6 +85,7 @@ include $(BUILD_HOST_SHARED_LIBRARY)
 include $(CLEAR_VARS)
 LOCAL_MODULE := libcutils
 LOCAL_SRC_FILES := $(commonSources) \
+        $(nonWindowsSources) \
         android_reboot.c \
         ashmem-dev.c \
         debugger.c \
@@ -106,9 +96,6 @@ LOCAL_SRC_FILES := $(commonSources) \
         trace-dev.c \
         uevent.c \
 
-# arch-arm/memset32.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
-
 LOCAL_SRC_FILES_arm += arch-arm/memset32.S
 LOCAL_SRC_FILES_arm64 += arch-arm64/android_memset.S
 
@@ -131,7 +118,9 @@ endif
 ifneq ($(ENABLE_SCHEDBOOST),)
 LOCAL_CFLAGS += -DUSE_SCHEDBOOST
 endif
-LOCAL_CFLAGS += -Werror -std=gnu90
+LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -146,8 +135,10 @@ endif
 ifneq ($(ENABLE_SCHEDBOOST),)
 LOCAL_CFLAGS += -DUSE_SCHEDBOOST
 endif
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -Wall -Wextra
 LOCAL_C_INCLUDES := $(libcutils_c_includes)
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
index 6ae23c1..af7e189 100644 (file)
  * limitations under the License.
  */
 
-#include <unistd.h>
-#include <sys/reboot.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <mntent.h>
+#include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <sys/cdefs.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <cutils/android_reboot.h>
+#include <cutils/klog.h>
+#include <cutils/list.h>
 
-#define UNUSED __attribute__((unused))
+#define TAG "android_reboot"
+#define READONLY_CHECK_MS 5000
+#define READONLY_CHECK_TIMES 50
 
-/* Check to see if /proc/mounts contains any writeable filesystems
- * backed by a block device.
- * Return true if none found, else return false.
+typedef struct {
+    struct listnode list;
+    struct mntent entry;
+} mntent_list;
+
+static bool has_mount_option(const char* opts, const char* opt_to_find)
+{
+  bool ret = false;
+  char* copy = NULL;
+  char* opt;
+  char* rem;
+
+  while ((opt = strtok_r(copy ? NULL : (copy = strdup(opts)), ",", &rem))) {
+      if (!strcmp(opt, opt_to_find)) {
+          ret = true;
+          break;
+      }
+  }
+
+  free(copy);
+  return ret;
+}
+
+static bool is_block_device(const char* fsname)
+{
+    return !strncmp(fsname, "/dev/block", 10);
+}
+
+/* Find all read+write block devices in /proc/mounts and add them to
+ * |rw_entries|.
  */
-static int remount_ro_done(void)
+static void find_rw(struct listnode* rw_entries)
 {
     FILE* fp;
     struct mntent* mentry;
-    int found_rw_fs = 0;
 
     if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
-        /* If we can't read /proc/mounts, just give up. */
-        return 1;
+        KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
+        return;
     }
     while ((mentry = getmntent(fp)) != NULL) {
-        if (!strncmp(mentry->mnt_fsname, "/dev/block", 10) && strstr(mentry->mnt_opts, "rw,")) {
-            found_rw_fs = 1;
-            break;
+        if (is_block_device(mentry->mnt_fsname) &&
+            has_mount_option(mentry->mnt_opts, "rw")) {
+            mntent_list* item = (mntent_list*)calloc(1, sizeof(mntent_list));
+            item->entry = *mentry;
+            item->entry.mnt_fsname = strdup(mentry->mnt_fsname);
+            item->entry.mnt_dir = strdup(mentry->mnt_dir);
+            item->entry.mnt_type = strdup(mentry->mnt_type);
+            item->entry.mnt_opts = strdup(mentry->mnt_opts);
+            list_add_tail(rw_entries, &item->list);
         }
     }
     endmntent(fp);
+}
+
+static void free_entries(struct listnode* entries)
+{
+    struct listnode* node;
+    struct listnode* n;
+    list_for_each_safe(node, n, entries) {
+        mntent_list* item = node_to_item(node, mntent_list, list);
+        free(item->entry.mnt_fsname);
+        free(item->entry.mnt_dir);
+        free(item->entry.mnt_type);
+        free(item->entry.mnt_opts);
+        free(item);
+    }
+}
 
-    return !found_rw_fs;
+static mntent_list* find_item(struct listnode* rw_entries, const char* fsname_to_find)
+{
+    struct listnode* node;
+    list_for_each(node, rw_entries) {
+        mntent_list* item = node_to_item(node, mntent_list, list);
+        if (!strcmp(item->entry.mnt_fsname, fsname_to_find)) {
+            return item;
+        }
+    }
+    return NULL;
 }
 
 /* Remounting filesystems read-only is difficult when there are files
@@ -64,38 +129,92 @@ static int remount_ro_done(void)
  * repeatedly until there are no more writable filesystems mounted on
  * block devices.
  */
-static void remount_ro(void)
+static void remount_ro(void (*cb_on_remount)(const struct mntent*))
 {
-    int fd, cnt = 0;
+    int fd, cnt;
+    FILE* fp;
+    struct mntent* mentry;
+    struct listnode* node;
+
+    list_declare(rw_entries);
+    list_declare(ro_entries);
+
+    sync();
+    find_rw(&rw_entries);
 
     /* Trigger the remount of the filesystems as read-only,
      * which also marks them clean.
      */
-    fd = open("/proc/sysrq-trigger", O_WRONLY);
+    fd = TEMP_FAILURE_RETRY(open("/proc/sysrq-trigger", O_WRONLY));
     if (fd < 0) {
-        return;
+        KLOG_WARNING(TAG, "Failed to open sysrq-trigger.\n");
+        /* TODO: Try to remount each rw parition manually in readonly mode.
+         * This may succeed if no process is using the partition.
+         */
+        goto out;
+    }
+    if (TEMP_FAILURE_RETRY(write(fd, "u", 1)) != 1) {
+        close(fd);
+        KLOG_WARNING(TAG, "Failed to write to sysrq-trigger.\n");
+        /* TODO: The same. Manually remount the paritions. */
+        goto out;
     }
-    write(fd, "u", 1);
     close(fd);
 
-
     /* Now poll /proc/mounts till it's done */
-    while (!remount_ro_done() && (cnt < 50)) {
-        usleep(100000);
+    cnt = 0;
+    while (cnt < READONLY_CHECK_TIMES) {
+        if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
+            /* If we can't read /proc/mounts, just give up. */
+            KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
+            goto out;
+        }
+        while ((mentry = getmntent(fp)) != NULL) {
+            if (!is_block_device(mentry->mnt_fsname) ||
+                !has_mount_option(mentry->mnt_opts, "ro")) {
+                continue;
+            }
+            mntent_list* item = find_item(&rw_entries, mentry->mnt_fsname);
+            if (item) {
+                /* |item| has now been ro remounted. */
+                list_remove(&item->list);
+                list_add_tail(&ro_entries, &item->list);
+            }
+        }
+        endmntent(fp);
+        if (list_empty(&rw_entries)) {
+            /* All rw block devices are now readonly. */
+            break;
+        }
+        TEMP_FAILURE_RETRY(
+            usleep(READONLY_CHECK_MS * 1000 / READONLY_CHECK_TIMES));
         cnt++;
     }
 
-    return;
-}
+    list_for_each(node, &rw_entries) {
+        mntent_list* item = node_to_item(node, mntent_list, list);
+        KLOG_WARNING(TAG, "Failed to remount %s in readonly mode.\n",
+                     item->entry.mnt_fsname);
+    }
+
+    if (cb_on_remount) {
+        list_for_each(node, &ro_entries) {
+            mntent_list* item = node_to_item(node, mntent_list, list);
+            cb_on_remount(&item->entry);
+        }
+    }
 
+out:
+    free_entries(&rw_entries);
+    free_entries(&ro_entries);
+}
 
-int android_reboot(int cmd, int flags UNUSED, const char *arg)
+int android_reboot_with_callback(
+    int cmd, int flags __unused, const char *arg,
+    void (*cb_on_remount)(const struct mntent*))
 {
     int ret;
-
-    sync();
-    remount_ro();
-
+    remount_ro(cb_on_remount);
     switch (cmd) {
         case ANDROID_RB_RESTART:
             ret = reboot(RB_AUTOBOOT);
@@ -117,3 +236,7 @@ int android_reboot(int cmd, int flags UNUSED, const char *arg)
     return ret;
 }
 
+int android_reboot(int cmd, int flags, const char *arg)
+{
+    return android_reboot_with_callback(cmd, flags, arg, NULL);
+}
index 6efab9f..1e89636 100644 (file)
@@ -18,6 +18,8 @@
  *
  */
 
+    .syntax unified
+
     .text
     .align
 
@@ -45,7 +47,7 @@ android_memset16:
 
         /* align to 32 bits */
         tst         r0, #2
-        strneh      r1, [r0], #2
+        strhne      r1, [r0], #2
         subne       r2, r2, #2
         .fnend
 
@@ -68,27 +70,27 @@ android_memset32:
 
         /* 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}
+        stmiacs     r0!, {r1, lr}
+        stmiacs     r0!, {r1, lr}
+        stmiami     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}
+        stmiahs     r0!, {r1,r3,r12,lr}
+        stmiahs     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}
+        stmiacs     r0!, {r1,r3,r12,lr}
+        stmiami     r0!, {r1,lr}
         movs        r2, r2, lsl #2
         strcs       r1, [r0], #4
-        strmih      lr, [r0], #2
+        strhmi      lr, [r0], #2
 
         ldr         lr, [sp], #4
         .cfi_def_cfa_offset 0
index a6b7496..c0fe3d1 100644 (file)
@@ -30,6 +30,9 @@
 
 #include <cutils/memory.h>
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 void android_memset16(uint16_t* dst, uint16_t value, size_t size)
 {
    /* optimized version of
@@ -46,7 +49,7 @@ void android_memset16(uint16_t* dst, uint16_t value, size_t size)
    }
    /* dst is now 32-bit-aligned */
    /* fill body with 32-bit pairs */
-   uint32_t value32 = (value << 16) | value;
+   uint32_t value32 = (((uint32_t)value) << 16) | ((uint32_t)value);
    android_memset32((uint32_t*) dst, value32, size<<1);
    if (size & 1) {
       dst[size-1] = value;  /* fill unpaired last elem */
@@ -54,6 +57,9 @@ void android_memset16(uint16_t* dst, uint16_t value, size_t size)
 }
 
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 void android_memset32(uint32_t* dst, uint32_t value, size_t size)
 {
    /* optimized version of
@@ -70,7 +76,7 @@ void android_memset32(uint32_t* dst, uint32_t value, size_t size)
    }
    /* dst is now 64-bit aligned */
    /* fill body with 64-bit pairs */
-   uint64_t value64 = (((uint64_t)value)<<32) | value;
+   uint64_t value64 = (((uint64_t)value) << 32) | ((uint64_t)value);
    uint64_t* dst64 = (uint64_t*)dst;
 
    while (size >= 12) {
@@ -86,7 +92,8 @@ void android_memset32(uint32_t* dst, uint32_t value, size_t size)
 
    /* fill remainder with original 32-bit single-elem loop */
    dst = (uint32_t*) dst64;
-   while (size--) {
+   while (size != 0) {
+       size--;
       *dst++ = value;
    }
 
index da66546..f4454bb 100644 (file)
@@ -88,11 +88,13 @@ static const struct fs_path_config android_dirs[] = {
     { 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" },
     { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
     { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
+    { 00750, AID_ROOT,   AID_SHELL,  0, "data/nativetest" },
+    { 00750, AID_ROOT,   AID_SHELL,  0, "data/nativetest64" },
     { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
     { 00755, AID_ROOT,   AID_SYSTEM, 0, "mnt" },
     { 00755, AID_ROOT,   AID_ROOT,   0, "root" },
     { 00750, AID_ROOT,   AID_SHELL,  0, "sbin" },
-    { 00755, AID_ROOT,   AID_ROOT, 0, "storage" },
+    { 00751, AID_ROOT,   AID_SDCARD_R, 0, "storage" },
     { 00755, AID_ROOT,   AID_SHELL,  0, "system/bin" },
     { 00755, AID_ROOT,   AID_SHELL,  0, "system/vendor" },
     { 00755, AID_ROOT,   AID_SHELL,  0, "system/xbin" },
@@ -125,18 +127,21 @@ static const struct fs_path_config android_files[] = {
     { 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/*" },
+    { 00640, AID_ROOT,      AID_SHELL,     0, "data/nativetest/tests.txt" },
+    { 00640, AID_ROOT,      AID_SHELL,     0, "data/nativetest64/tests.txt" },
+    { 00750, AID_ROOT,      AID_SHELL,     0, "data/nativetest/*" },
+    { 00750, AID_ROOT,      AID_SHELL,     0, "data/nativetest64/*" },
 
-    /* the following five files are INTENTIONALLY set-uid, but they
+    /* the following four files are INTENTIONALLY set-uid, but they
      * are NOT included on user builds. */
     { 04750, AID_ROOT,      AID_SHELL,     0, "system/xbin/su" },
     { 06755, AID_ROOT,      AID_ROOT,      0, "system/xbin/librank" },
-    { 06755, AID_ROOT,      AID_ROOT,      0, "system/xbin/procrank" },
     { 06755, AID_ROOT,      AID_ROOT,      0, "system/xbin/procmem" },
     { 04770, AID_ROOT,      AID_RADIO,     0, "system/bin/pppd-ril" },
 
     /* the following files have enhanced capabilities and ARE included in user builds. */
-    { 00750, AID_ROOT,      AID_SHELL,     (1ULL << CAP_SETUID) | (1ULL << CAP_SETGID), "system/bin/run-as" },
-    { 00700, AID_SYSTEM,    AID_SHELL,     (1ULL << CAP_BLOCK_SUSPEND), "system/bin/inputflinger" },
+    { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) | CAP_MASK_LONG(CAP_SETGID), "system/bin/run-as" },
+    { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND), "system/bin/inputflinger" },
 
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/uncrypt" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/install-recovery.sh" },
@@ -168,8 +173,7 @@ static int fs_config_open(int dir, const char *target_out_path)
         if (target_out_path[target_out_path_len] == '/') {
             skip_len++;
         }
-        asprintf(&name, "%s%s", target_out_path, (dir ? conf_dir : conf_file) + skip_len);
-        if (name) {
+        if (asprintf(&name, "%s%s", target_out_path, (dir ? conf_dir : conf_file) + skip_len) != -1) {
             fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
             free(name);
         }
index 65539ea..ede3b98 100644 (file)
@@ -77,6 +77,9 @@ Hashmap* hashmapCreate(size_t initialCapacity,
 /**
  * Hashes the given key.
  */
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 static inline int hashKey(Hashmap* map, void* key) {
     int h = map->hash(key);
 
@@ -152,6 +155,10 @@ void hashmapFree(Hashmap* map) {
     free(map);
 }
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
+/* FIXME: relies on signed integer overflow, which is undefined behavior */
 int hashmapHash(void* key, size_t keySize) {
     int h = keySize;
     char* data = (char*) key;
index 8946d3c..71bc94b 100644 (file)
@@ -23,7 +23,7 @@
 
 #include <cutils/iosched_policy.h>
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 #include <linux/ioprio.h>
 #include <sys/syscall.h>
 #define __android_unused
@@ -32,7 +32,7 @@
 #endif
 
 int android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_unused, int ioprio __android_unused) {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     if (syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio | (clazz << IOPRIO_CLASS_SHIFT))) {
         return -1;
     }
@@ -41,7 +41,7 @@ int android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_un
 }
 
 int android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *ioprio) {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     int rc;
 
     if ((rc = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid)) < 0) {
index cc931eb..5d28b6f 100644 (file)
 #include <unistd.h>
 
 #include <cutils/process_name.h>
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 #include <cutils/properties.h>
 #endif
 
 #define PROCESS_NAME_DEVICE "/sys/qemu_trace/process_name"
 
 static const char* process_name = "unknown";
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 static int running_in_emulator = -1;
 #endif
 
 void set_process_name(const char* new_name) {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     char  propBuf[PROPERTY_VALUE_MAX];
 #endif
 
@@ -59,7 +59,7 @@ void set_process_name(const char* new_name) {
     }
 #endif
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     // If we know we are not running in the emulator, then return.
     if (running_in_emulator == 0) {
         return;
index 6994904..2bc4226 100644 (file)
@@ -22,7 +22,7 @@
 #include <cutils/record_stream.h>
 #include <string.h>
 #include <stdint.h>
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 #include <winsock2.h>   /* for ntohl */
 #else
 #include <netinet/in.h>
index 46bd5ac..6bba3a7 100644 (file)
@@ -37,7 +37,7 @@ static inline SchedPolicy _policy(SchedPolicy p)
    return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;
 }
 
-#if defined(HAVE_ANDROID_OS)
+#if defined(__ANDROID__)
 
 #include <pthread.h>
 #include <sched.h>
@@ -60,11 +60,13 @@ static int __sys_supports_schedgroups = -1;
 static int bg_cgroup_fd = -1;
 static int fg_cgroup_fd = -1;
 
+#ifdef USE_CPUSETS
 // File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
 static int bg_cpuset_fd = -1;
 static int fg_cpuset_fd = -1;
 static int bg_schedboost_fd = -1;
 static int fg_schedboost_fd = -1;
+#endif
 
 /* Add tid to the scheduling group defined by the policy */
 static int add_tid_to_cgroup(int tid, int fd)
@@ -152,7 +154,7 @@ static void __initialize(void) {
  */
 static int getSchedulerGroup(int tid, char* buf, size_t bufLen)
 {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     char pathBuf[32];
     char lineBuf[256];
     FILE *fp;
index 6c849de..7f0ccb8 100644 (file)
@@ -20,7 +20,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#ifndef HAVE_WINSOCK
+#if !defined(_WIN32)
 #include <sys/socket.h>
 #include <sys/select.h>
 #include <sys/types.h>
index 7b42daa..2526146 100644 (file)
@@ -22,7 +22,7 @@
 
 #include <cutils/sockets.h>
 
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 
 int socket_local_client(const char *name, int namespaceId, int type)
 {
@@ -30,7 +30,7 @@ int socket_local_client(const char *name, int namespaceId, int type)
     return -1;
 }
 
-#else /* !HAVE_WINSOCK */
+#else /* !_WIN32 */
 
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -165,4 +165,4 @@ int socket_local_client(const char *name, int namespaceId, int type)
     return s;
 }
 
-#endif /* !HAVE_WINSOCK */
+#endif /* !_WIN32 */
index 60eb86b..c9acdad 100644 (file)
@@ -23,7 +23,7 @@
 #include <errno.h>
 #include <stddef.h>
 
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 
 int socket_local_server(const char *name, int namespaceId, int type)
 {
@@ -31,7 +31,7 @@ int socket_local_server(const char *name, int namespaceId, int type)
     return -1;
 }
 
-#else /* !HAVE_WINSOCK */
+#else /* !_WIN32 */
 
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -123,4 +123,4 @@ int socket_local_server(const char *name, int namespace, int type)
     return s;
 }
 
-#endif /* !HAVE_WINSOCK */
+#endif /* !_WIN32 */
index 9aed7b7..e14cffb 100644 (file)
@@ -20,7 +20,7 @@
 #include <string.h>
 #include <unistd.h>
 
-#ifndef HAVE_WINSOCK
+#if !defined(_WIN32)
 #include <sys/socket.h>
 #include <sys/select.h>
 #include <sys/types.h>
index 71afce7..b600e34 100644 (file)
@@ -22,7 +22,7 @@
 
 #define LISTEN_BACKLOG 4
 
-#ifndef HAVE_WINSOCK
+#if !defined(_WIN32)
 #include <sys/socket.h>
 #include <sys/select.h>
 #include <sys/types.h>
index e0031ba..3300b8f 100644 (file)
 
 #include <cutils/sockets.h>
 
-/* 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)
-{
-    return socket_network_client_timeout(host, port, type, 0);
+static int toggle_O_NONBLOCK(int s) {
+    int flags = fcntl(s, F_GETFL);
+    if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {
+        close(s);
+        return -1;
+    }
+    return s;
 }
 
-/* Connect to port on the IP interface. type is SOCK_STREAM or SOCK_DGRAM.
- * timeout in seconds return is a file descriptor or -1 on error
- */
-int socket_network_client_timeout(const char *host, int port, int type, int timeout)
-{
-    struct hostent *hp;
-    struct sockaddr_in addr;
-    int s;
-    int flags = 0, error = 0, ret = 0;
-    fd_set rset, wset;
-    socklen_t len = sizeof(error);
-    struct timeval ts;
-
-    ts.tv_sec = timeout;
-    ts.tv_usec = 0;
-
-    hp = gethostbyname(host);
-    if (hp == 0) return -1;
+// Connect to the given host and port.
+// 'timeout' is in seconds (0 for no timeout).
+// Returns a file descriptor or -1 on error.
+// On error, check *getaddrinfo_error (for use with gai_strerror) first;
+// if that's 0, use errno instead.
+int socket_network_client_timeout(const char* host, int port, int type, int timeout,
+                                  int* getaddrinfo_error) {
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = type;
+
+    char port_str[16];
+    snprintf(port_str, sizeof(port_str), "%d", port);
+
+    struct addrinfo* addrs;
+    *getaddrinfo_error = getaddrinfo(host, port_str, &hints, &addrs);
+    if (*getaddrinfo_error != 0) {
+        return -1;
+    }
 
-    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);
+    // TODO: try all the addresses if there's more than one?
+    int family = addrs[0].ai_family;
+    int protocol = addrs[0].ai_protocol;
+    socklen_t addr_len = addrs[0].ai_addrlen;
+    struct sockaddr_storage addr;
+    memcpy(&addr, addrs[0].ai_addr, addr_len);
 
-    s = socket(hp->h_addrtype, type, 0);
-    if (s < 0) return -1;
+    freeaddrinfo(addrs);
 
-    if ((flags = fcntl(s, F_GETFL, 0)) < 0) {
-        close(s);
-        return -1;
-    }
+    // The Mac doesn't have SOCK_NONBLOCK.
+    int s = socket(family, type, protocol);
+    if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
 
-    if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
+    int rc = connect(s, (const struct sockaddr*) &addr, addr_len);
+    if (rc == 0) {
+        return toggle_O_NONBLOCK(s);
+    } else if (rc == -1 && errno != EINPROGRESS) {
         close(s);
         return -1;
     }
 
-    if ((ret = connect(s, (struct sockaddr *) &addr, sizeof(addr))) < 0) {
-        if (errno != EINPROGRESS) {
-            close(s);
-            return -1;
-        }
-    }
+    fd_set r_set;
+    FD_ZERO(&r_set);
+    FD_SET(s, &r_set);
+    fd_set w_set = r_set;
 
-    if (ret == 0)
-        goto done;
-
-    FD_ZERO(&rset);
-    FD_SET(s, &rset);
-    wset = rset;
-
-    if ((ret = select(s + 1, &rset, &wset, NULL, (timeout) ? &ts : NULL)) < 0) {
+    struct timeval ts;
+    ts.tv_sec = timeout;
+    ts.tv_usec = 0;
+    if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
         close(s);
         return -1;
     }
-    if (ret == 0) {   // we had a timeout
+    if (rc == 0) {   // we had a timeout
         errno = ETIMEDOUT;
         close(s);
         return -1;
     }
 
-    if (FD_ISSET(s, &rset) || FD_ISSET(s, &wset)) {
+    int error = 0;
+    socklen_t len = sizeof(error);
+    if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
         if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
             close(s);
             return -1;
@@ -115,11 +116,10 @@ int socket_network_client_timeout(const char *host, int port, int type, int time
         return -1;
     }
 
-done:
-    if (fcntl(s, F_SETFL, flags) < 0) {
-        close(s);
-        return -1;
-    }
+    return toggle_O_NONBLOCK(s);
+}
 
-    return s;
+int socket_network_client(const char* host, int port, int type) {
+    int getaddrinfo_error;
+    return socket_network_client_timeout(host, port, type, 0, &getaddrinfo_error);
 }
index 15ede2b..d438782 100644 (file)
@@ -17,7 +17,7 @@
 #include <cutils/sockets.h>
 #include <log/log.h>
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 /* For the socket trust (credentials) check */
 #include <private/android_filesystem_config.h>
 #define __android_unused
@@ -27,7 +27,7 @@
 
 bool socket_peer_is_trusted(int fd __android_unused)
 {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     struct ucred cr;
     socklen_t len = sizeof(cr);
     int n = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len);
index 924289a..4f23d09 100644 (file)
@@ -42,6 +42,9 @@ static bool str_eq(void *key_a, void *key_b)
 }
 
 /* use djb hash unless we find it inadequate */
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 static int str_hash_fn(void *str)
 {
     uint32_t hash = 5381;
index 1a8ba86..4dc987e 100644 (file)
@@ -55,7 +55,8 @@ extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
     /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
      */
     if (len < (SIZE_MAX-1)/3) {
-        while (len--) {
+        while (len != 0) {
+            len--;
             unsigned int uic = *utf16Str++;
 
             if (uic > 0x07ff)
@@ -69,7 +70,8 @@ extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
     }
 
     /* The slower but paranoid version */
-    while (len--) {
+    while (len != 0) {
+        len--;
         unsigned int  uic     = *utf16Str++;
         size_t        utf8Cur = utf8Len;
 
@@ -112,7 +114,8 @@ extern char* strncpy16to8(char* utf8Str, const char16_t* utf16Str, size_t len)
      * strnlen16to8() properly or at a minimum checked the result of
      * its malloc(SIZE_MAX) in case of overflow.
      */
-    while (len--) {
+    while (len != 0) {
+        len--;
         unsigned int uic = *utf16Str++;
 
         if (uic > 0x07ff) {
index 45efc51..a98485f 100644 (file)
@@ -20,6 +20,8 @@
 #include <sys/mman.h>
 #include <sys/types.h>
 
+#include <memory>
+
 #include <cutils/memory.h>
 #include <gtest/gtest.h>
 
@@ -127,14 +129,14 @@ void RunMemsetTests(test_e test_type, uint32_t value, int align[][2], size_t num
     min_incr = 2;
     value |= value << 16;
   }
-  uint32_t* expected_buf = new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)];
+  std::unique_ptr<uint32_t[]> expected_buf(new uint32_t[MAX_TEST_SIZE/sizeof(uint32_t)]);
   for (size_t i = 0; i < MAX_TEST_SIZE/sizeof(uint32_t); i++) {
     expected_buf[i] = value;
   }
 
   // Allocate one large buffer with lots of extra space so that we can
   // guarantee that all possible alignments will fit.
-  uint8_t *buf = new uint8_t[3*MAX_TEST_SIZE];
+  std::unique_ptr<uint8_t[]> buf(new uint8_t[3*MAX_TEST_SIZE]);
   uint8_t *buf_align;
   for (size_t i = 0; i < num_aligns; i++) {
     size_t incr = min_incr;
@@ -142,7 +144,7 @@ void RunMemsetTests(test_e test_type, uint32_t value, int align[][2], size_t num
       incr = GetIncrement(len, min_incr);
 
       buf_align = reinterpret_cast<uint8_t*>(GetAlignedPtr(
-          buf+FENCEPOST_LENGTH, align[i][0], align[i][1]));
+          buf.get()+FENCEPOST_LENGTH, align[i][0], align[i][1]));
 
       SetFencepost(&buf_align[-FENCEPOST_LENGTH]);
       SetFencepost(&buf_align[len]);
@@ -153,15 +155,13 @@ void RunMemsetTests(test_e test_type, uint32_t value, int align[][2], size_t num
       } else {
         android_memset32(reinterpret_cast<uint32_t*>(buf_align), value, len);
       }
-      ASSERT_EQ(0, memcmp(expected_buf, buf_align, len))
+      ASSERT_EQ(0, memcmp(expected_buf.get(), buf_align, len))
           << "Failed size " << len << " align " << align[i][0] << " " << align[i][1] << "\n";
 
       VerifyFencepost(&buf_align[-FENCEPOST_LENGTH]);
       VerifyFencepost(&buf_align[len]);
     }
   }
-  delete expected_buf;
-  delete buf;
 }
 
 TEST(libcutils, android_memset16_non_zero) {
diff --git a/liblog/Android.bp b/liblog/Android.bp
new file mode 100644 (file)
index 0000000..34e7f92
--- /dev/null
@@ -0,0 +1,76 @@
+//
+// Copyright (C) 2008-2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+liblog_host_sources = [
+    "logd_write.c",
+    "log_event_write.c",
+    "fake_log_device.c",
+    //"event.logtags",
+]
+liblog_target_sources = [
+    "logd_write.c",
+    "log_event_write.c",
+    "event_tag_map.c",
+    "log_time.cpp",
+    "log_is_loggable.c",
+    "logprint.c",
+    "log_read.c",
+]
+
+// Shared and static library for host and device
+// ========================================================
+cc_library {
+    name: "liblog",
+    host_supported: true,
+
+    target: {
+        host: {
+            srcs: liblog_host_sources,
+            cflags: ["-DFAKE_LOG_DEVICE=1"],
+        },
+        android: {
+            srcs: liblog_target_sources,
+            // AddressSanitizer runtime library depends on liblog.
+            sanitize: ["never"],
+        },
+        android_arm: {
+            // TODO: This is to work around b/24465209. Remove after root cause is fixed
+            ldflags: ["-Wl,--hash-style=both"],
+        },
+        windows: {
+            srcs: ["uio.c"],
+        },
+        not_windows: {
+            srcs: ["event_tag_map.c"],
+        },
+        linux: {
+            host_ldlibs: ["-lrt"],
+        },
+    },
+
+    cflags: [
+        "-Werror",
+        // This is what we want to do:
+        //  liblog_cflags := $(shell \
+        //   sed -n \
+        //       's/^\([0-9]*\)[ \t]*liblog[ \t].*/-DLIBLOG_LOG_TAG=\1/p' \
+        //       $(LOCAL_PATH)/event.logtags)
+        // so make sure we do not regret hard-coding it as follows:
+        "-DLIBLOG_LOG_TAG=1005",
+    ],
+    compile_multilib: "both",
+    stl: "none",
+}
index 115dd79..a183db8 100644 (file)
@@ -24,50 +24,33 @@ include $(CLEAR_VARS)
 # so make sure we do not regret hard-coding it as follows:
 liblog_cflags := -DLIBLOG_LOG_TAG=1005
 
-ifneq ($(TARGET_USES_LOGD),false)
-liblog_sources := logd_write.c log_event_write.c
-else
-liblog_sources := logd_write_kern.c
-endif
-
-# some files must not be compiled when building against Mingw
-# they correspond to features not used by our host development tools
-# which are also hard or even impossible to port to native Win32
-
-ifeq ($(strip $(USE_MINGW)),)
-    liblog_sources += \
-        event_tag_map.c
-else
-    liblog_sources += \
-        uio.c
-endif
-
-liblog_host_sources := $(liblog_sources) fake_log_device.c event.logtags
-liblog_target_sources := $(liblog_sources) log_time.cpp log_is_loggable.c
-ifeq ($(strip $(USE_MINGW)),)
+liblog_host_sources := logd_write.c log_event_write.c fake_log_device.c event.logtags
+liblog_target_sources := logd_write.c log_event_write.c event_tag_map.c log_time.cpp log_is_loggable.c
 liblog_target_sources += logprint.c
-endif
-ifneq ($(TARGET_USES_LOGD),false)
 liblog_target_sources += log_read.c
-else
-liblog_target_sources += log_read_kern.c
-endif
 
 # Shared and static library for host
 # ========================================================
 LOCAL_MODULE := liblog
 LOCAL_SRC_FILES := $(liblog_host_sources)
+# 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
+LOCAL_SRC_FILES_darwin := event_tag_map.c
+LOCAL_SRC_FILES_linux := event_tag_map.c
+LOCAL_SRC_FILES_windows := uio.c
 LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror $(liblog_cflags)
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := liblog
 LOCAL_WHOLE_STATIC_LIBRARIES := liblog
-ifeq ($(strip $(HOST_OS)),linux)
-LOCAL_LDLIBS := -lrt
-endif
+LOCAL_LDLIBS_linux := -lrt
 LOCAL_MULTILIB := both
+LOCAL_CXX_STL := none
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 
@@ -77,6 +60,8 @@ include $(CLEAR_VARS)
 LOCAL_MODULE := liblog
 LOCAL_SRC_FILES := $(liblog_target_sources)
 LOCAL_CFLAGS := -Werror $(liblog_cflags)
+# AddressSanitizer runtime library depends on liblog.
+LOCAL_SANITIZE := never
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -84,9 +69,12 @@ LOCAL_MODULE := liblog
 LOCAL_WHOLE_STATIC_LIBRARIES := liblog
 LOCAL_CFLAGS := -Werror $(liblog_cflags)
 
-# TODO: This is to work around b/19059885. Remove after root cause is fixed
+# TODO: This is to work around b/24465209. Remove after root cause is fixed
 LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
 
+LOCAL_SANITIZE := never
+LOCAL_CXX_STL := none
+
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call first-makefiles-under,$(LOCAL_PATH))
index 7a8e33f..814d96d 100644 (file)
@@ -93,7 +93,7 @@ static int __android_log_level(const char *tag, int def)
     if (taglen) {
         uint32_t current_local_serial = current_global_serial;
 
-        if (!last_tag || strcmp(last_tag, tag)) {
+        if (!last_tag || (last_tag[0] != tag[0]) || strcmp(last_tag + 1, tag + 1)) {
             /* invalidate log.tag.<tag> cache */
             for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
                 tag_cache[i].pinfo = NULL;
@@ -176,3 +176,28 @@ int __android_log_is_loggable(int prio, const char *tag, int def)
     int logLevel = __android_log_level(tag, def);
     return logLevel >= 0 && prio >= logLevel;
 }
+
+char android_log_timestamp()
+{
+    static struct cache r_time_cache = { NULL, -1, 0 };
+    static struct cache p_time_cache = { NULL, -1, 0 };
+    static uint32_t serial;
+    uint32_t current_serial;
+    char retval;
+
+    pthread_mutex_lock(&lock);
+
+    current_serial = __system_property_area_serial();
+    if (current_serial != serial) {
+        refresh_cache(&r_time_cache, "ro.logd.timestamp");
+        refresh_cache(&p_time_cache, "persist.logd.timestamp");
+        serial = current_serial;
+    }
+    if (!(retval = p_time_cache.c)) {
+        retval = r_time_cache.c;
+    }
+
+    pthread_mutex_unlock(&lock);
+
+    return tolower(retval ?: 'r');
+}
index 9c4af30..cfc8a7a 100644 (file)
@@ -37,7 +37,7 @@
 /* branchless on many architectures. */
 #define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
 
-#if (defined(USE_MINGW) || defined(HAVE_WINSOCK))
+#if defined(_WIN32)
 #define WEAK static
 #else
 #define WEAK __attribute__((weak))
@@ -48,7 +48,7 @@
 
 /* Private copy of ../libcutils/socket_local_client.c prevent library loops */
 
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 
 int WEAK socket_local_client(const char *name, int namespaceId, int type)
 {
@@ -56,7 +56,7 @@ int WEAK socket_local_client(const char *name, int namespaceId, int type)
     return -ENOSYS;
 }
 
-#else /* !HAVE_WINSOCK */
+#else /* !_WIN32 */
 
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -193,7 +193,7 @@ int WEAK socket_local_client(const char *name, int namespaceId, int type)
     return s;
 }
 
-#endif /* !HAVE_WINSOCK */
+#endif /* !_WIN32 */
 /* End of ../libcutils/socket_local_client.c */
 
 #define logger_for_each(logger, logger_list) \
diff --git a/liblog/log_read_kern.c b/liblog/log_read_kern.c
deleted file mode 100644 (file)
index 69b405c..0000000
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
-** Copyright 2013-2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define _GNU_SOURCE /* asprintf for x86 host */
-#include <errno.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/cdefs.h>
-#include <sys/ioctl.h>
-
-#include <cutils/list.h>
-#include <log/log.h>
-#include <log/logger.h>
-
-#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 */
-
-typedef char bool;
-#define false (const bool)0
-#define true (const bool)1
-
-#define LOG_FILE_DIR "/dev/log/"
-
-/* timeout in milliseconds */
-#define LOG_TIMEOUT_FLUSH 5
-#define LOG_TIMEOUT_NEVER -1
-
-#define logger_for_each(logger, logger_list) \
-    for (logger = node_to_item((logger_list)->node.next, struct logger, node); \
-         logger != node_to_item(&(logger_list)->node, struct logger, node); \
-         logger = node_to_item((logger)->node.next, struct logger, node))
-
-#ifndef __unused
-#define __unused __attribute__((unused))
-#endif
-
-/* In the future, we would like to make this list extensible */
-static const char *LOG_NAME[LOG_ID_MAX] = {
-    [LOG_ID_MAIN] = "main",
-    [LOG_ID_RADIO] = "radio",
-    [LOG_ID_EVENTS] = "events",
-    [LOG_ID_SYSTEM] = "system",
-    [LOG_ID_CRASH] = "crash",
-    [LOG_ID_KERNEL] = "kernel",
-};
-
-const char *android_log_id_to_name(log_id_t log_id)
-{
-    if (log_id >= LOG_ID_MAX) {
-        log_id = LOG_ID_MAIN;
-    }
-    return LOG_NAME[log_id];
-}
-
-static int accessmode(int mode)
-{
-    if ((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_WRONLY) {
-        return W_OK;
-    }
-    if ((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_RDWR) {
-        return R_OK | W_OK;
-    }
-    return R_OK;
-}
-
-/* repeated fragment */
-static int check_allocate_accessible(char **n, const char *b, int mode)
-{
-    *n = NULL;
-
-    if (!b) {
-        return -EINVAL;
-    }
-
-    asprintf(n, LOG_FILE_DIR "%s", b);
-    if (!*n) {
-        return -1;
-    }
-
-    return access(*n, accessmode(mode));
-}
-
-log_id_t android_name_to_log_id(const char *logName)
-{
-    const char *b;
-    char *n;
-    int ret;
-
-    if (!logName) {
-        return -1; /* NB: log_id_t is unsigned */
-    }
-    b = strrchr(logName, '/');
-    if (!b) {
-        b = logName;
-    } else {
-        ++b;
-    }
-
-    ret = check_allocate_accessible(&n, b, ANDROID_LOG_RDONLY);
-    free(n);
-    if (ret) {
-        return ret;
-    }
-
-    for(ret = LOG_ID_MIN; ret < LOG_ID_MAX; ++ret) {
-        const char *l = LOG_NAME[ret];
-        if (l && !strcmp(b, l)) {
-            return ret;
-        }
-    }
-    return -1;   /* should never happen */
-}
-
-struct logger_list {
-    struct listnode node;
-    int mode;
-    unsigned int tail;
-    pid_t pid;
-    unsigned int queued_lines;
-    int timeout_ms;
-    int error;
-    bool flush;
-    bool valid_entry; /* valiant(?) effort to deal with memory starvation */
-    struct log_msg entry;
-};
-
-struct log_list {
-    struct listnode node;
-    struct log_msg entry; /* Truncated to event->len() + 1 to save space */
-};
-
-struct logger {
-    struct listnode node;
-    struct logger_list *top;
-    int fd;
-    log_id_t id;
-    short *revents;
-    struct listnode log_list;
-};
-
-/* android_logger_alloc unimplemented, no use case */
-/* android_logger_free not exported */
-static void android_logger_free(struct logger *logger)
-{
-    if (!logger) {
-        return;
-    }
-
-    while (!list_empty(&logger->log_list)) {
-        struct log_list *entry = node_to_item(
-            list_head(&logger->log_list), struct log_list, node);
-        list_remove(&entry->node);
-        free(entry);
-        if (logger->top->queued_lines) {
-            logger->top->queued_lines--;
-        }
-    }
-
-    if (logger->fd >= 0) {
-        close(logger->fd);
-    }
-
-    list_remove(&logger->node);
-
-    free(logger);
-}
-
-log_id_t android_logger_get_id(struct logger *logger)
-{
-    return logger->id;
-}
-
-/* worker for sending the command to the logger */
-static int logger_ioctl(struct logger *logger, int cmd, int mode)
-{
-    char *n;
-    int  f, ret;
-
-    if (!logger || !logger->top) {
-        return -EFAULT;
-    }
-
-    if (((mode & ANDROID_LOG_ACCMODE) == ANDROID_LOG_RDWR)
-            || (((mode ^ logger->top->mode) & ANDROID_LOG_ACCMODE) == 0)) {
-        return ioctl(logger->fd, cmd);
-    }
-
-    /* We go here if android_logger_list_open got mode wrong for this ioctl */
-    ret = check_allocate_accessible(&n, android_log_id_to_name(logger->id), mode);
-    if (ret) {
-        free(n);
-        return ret;
-    }
-
-    f = open(n, mode);
-    free(n);
-    if (f < 0) {
-        return f;
-    }
-
-    ret = ioctl(f, cmd);
-    close (f);
-
-    return ret;
-}
-
-int android_logger_clear(struct logger *logger)
-{
-    return logger_ioctl(logger, LOGGER_FLUSH_LOG, ANDROID_LOG_WRONLY);
-}
-
-/* returns the total size of the log's ring buffer */
-long android_logger_get_log_size(struct logger *logger)
-{
-    return logger_ioctl(logger, LOGGER_GET_LOG_BUF_SIZE, ANDROID_LOG_RDWR);
-}
-
-int android_logger_set_log_size(struct logger *logger __unused,
-                                unsigned long size __unused)
-{
-    return -ENOTSUP;
-}
-
-/*
- * returns the readable size of the log's ring buffer (that is, amount of the
- * log consumed)
- */
-long android_logger_get_log_readable_size(struct logger *logger)
-{
-    return logger_ioctl(logger, LOGGER_GET_LOG_LEN, ANDROID_LOG_RDONLY);
-}
-
-/*
- * returns the logger version
- */
-int android_logger_get_log_version(struct logger *logger)
-{
-    int ret = logger_ioctl(logger, LOGGER_GET_VERSION, ANDROID_LOG_RDWR);
-    return (ret < 0) ? 1 : ret;
-}
-
-/*
- * returns statistics
- */
-static const char unsupported[] = "18\nNot Supported\n\f";
-
-ssize_t android_logger_get_statistics(struct logger_list *logger_list __unused,
-                                      char *buf, size_t len)
-{
-    strncpy(buf, unsupported, len);
-    return -ENOTSUP;
-}
-
-ssize_t android_logger_get_prune_list(struct logger_list *logger_list __unused,
-                                      char *buf, size_t len)
-{
-    strncpy(buf, unsupported, len);
-    return -ENOTSUP;
-}
-
-int android_logger_set_prune_list(struct logger_list *logger_list __unused,
-                                  char *buf, size_t len)
-{
-    static const char unsupported_error[] = "Unsupported";
-    strncpy(buf, unsupported, len);
-    return -ENOTSUP;
-}
-
-struct logger_list *android_logger_list_alloc(int mode,
-                                              unsigned int tail,
-                                              pid_t pid)
-{
-    struct logger_list *logger_list;
-
-    logger_list = calloc(1, sizeof(*logger_list));
-    if (!logger_list) {
-        return NULL;
-    }
-    list_init(&logger_list->node);
-    logger_list->mode = mode;
-    logger_list->tail = tail;
-    logger_list->pid = pid;
-    return logger_list;
-}
-
-struct logger_list *android_logger_list_alloc_time(int mode,
-                                                   log_time start __unused,
-                                                   pid_t pid)
-{
-    return android_logger_list_alloc(mode, 0, pid);
-}
-
-/* android_logger_list_register unimplemented, no use case */
-/* android_logger_list_unregister unimplemented, no use case */
-
-/* Open the named log and add it to the logger list */
-struct logger *android_logger_open(struct logger_list *logger_list,
-                                   log_id_t id)
-{
-    struct listnode *node;
-    struct logger *logger;
-    char *n;
-
-    if (!logger_list || (id >= LOG_ID_MAX)) {
-        goto err;
-    }
-
-    logger_for_each(logger, logger_list) {
-        if (logger->id == id) {
-            goto ok;
-        }
-    }
-
-    logger = calloc(1, sizeof(*logger));
-    if (!logger) {
-        goto err;
-    }
-
-    if (check_allocate_accessible(&n, android_log_id_to_name(id),
-                                  logger_list->mode)) {
-        goto err_name;
-    }
-
-    logger->fd = open(n, logger_list->mode & (ANDROID_LOG_ACCMODE | ANDROID_LOG_NONBLOCK));
-    if (logger->fd < 0) {
-        goto err_name;
-    }
-
-    free(n);
-    logger->id = id;
-    list_init(&logger->log_list);
-    list_add_tail(&logger_list->node, &logger->node);
-    logger->top = logger_list;
-    logger_list->timeout_ms = LOG_TIMEOUT_FLUSH;
-    goto ok;
-
-err_name:
-    free(n);
-err_logger:
-    free(logger);
-err:
-    logger = NULL;
-ok:
-    return logger;
-}
-
-/* Open the single named log and make it part of a new logger list */
-struct logger_list *android_logger_list_open(log_id_t id,
-                                             int mode,
-                                             unsigned int tail,
-                                             pid_t pid)
-{
-    struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid);
-    if (!logger_list) {
-        return NULL;
-    }
-
-    if (!android_logger_open(logger_list, id)) {
-        android_logger_list_free(logger_list);
-        return NULL;
-    }
-
-    return logger_list;
-}
-
-/* prevent memory starvation when backfilling */
-static unsigned int queue_threshold(struct logger_list *logger_list)
-{
-    return (logger_list->tail < 64) ? 64 : logger_list->tail;
-}
-
-static bool low_queue(struct listnode *node)
-{
-    /* low is considered less than 2 */
-    return list_head(node) == list_tail(node);
-}
-
-/* Flush queues in sequential order, one at a time */
-static int android_logger_list_flush(struct logger_list *logger_list,
-                                     struct log_msg *log_msg)
-{
-    int ret = 0;
-    struct log_list *firstentry = NULL;
-
-    while ((ret == 0)
-            && (logger_list->flush
-                || (logger_list->queued_lines > logger_list->tail))) {
-        struct logger *logger;
-
-        /* Merge sort */
-        bool at_least_one_is_low = false;
-        struct logger *firstlogger = NULL;
-        firstentry = NULL;
-
-        logger_for_each(logger, logger_list) {
-            struct listnode *node;
-            struct log_list *oldest = NULL;
-
-            /* kernel logger channels not necessarily time-sort order */
-            list_for_each(node, &logger->log_list) {
-                struct log_list *entry = node_to_item(node,
-                                                      struct log_list, node);
-                if (!oldest
-                        || (entry->entry.entry.sec < oldest->entry.entry.sec)
-                        || ((entry->entry.entry.sec == oldest->entry.entry.sec)
-                            && (entry->entry.entry.nsec < oldest->entry.entry.nsec))) {
-                    oldest = entry;
-                }
-            }
-
-            if (!oldest) {
-                at_least_one_is_low = true;
-                continue;
-            } else if (low_queue(&logger->log_list)) {
-                at_least_one_is_low = true;
-            }
-
-            if (!firstentry
-                    || (oldest->entry.entry.sec < firstentry->entry.entry.sec)
-                    || ((oldest->entry.entry.sec == firstentry->entry.entry.sec)
-                        && (oldest->entry.entry.nsec < firstentry->entry.entry.nsec))) {
-                firstentry = oldest;
-                firstlogger = logger;
-            }
-        }
-
-        if (!firstentry) {
-            break;
-        }
-
-        /* when trimming list, tries to keep one entry behind in each bucket */
-        if (!logger_list->flush
-                && at_least_one_is_low
-                && (logger_list->queued_lines < queue_threshold(logger_list))) {
-            break;
-        }
-
-        /* within tail?, send! */
-        if ((logger_list->tail == 0)
-                || (logger_list->queued_lines <= logger_list->tail)) {
-            int diff;
-            ret = firstentry->entry.entry.hdr_size;
-            if (!ret) {
-                ret = sizeof(firstentry->entry.entry_v1);
-            }
-
-            /* Promote entry to v3 format */
-            memcpy(log_msg->buf, firstentry->entry.buf, ret);
-            diff = sizeof(firstentry->entry.entry_v3) - ret;
-            if (diff < 0) {
-                diff = 0;
-            } else if (diff > 0) {
-                memset(log_msg->buf + ret, 0, diff);
-            }
-            memcpy(log_msg->buf + ret + diff, firstentry->entry.buf + ret,
-                   firstentry->entry.entry.len + 1);
-            ret += diff;
-            log_msg->entry.hdr_size = ret;
-            log_msg->entry.lid = firstlogger->id;
-
-            ret += firstentry->entry.entry.len;
-        }
-
-        /* next entry */
-        list_remove(&firstentry->node);
-        free(firstentry);
-        if (logger_list->queued_lines) {
-            logger_list->queued_lines--;
-        }
-    }
-
-    /* Flushed the list, no longer in tail mode for continuing content */
-    if (logger_list->flush && !firstentry) {
-        logger_list->tail = 0;
-    }
-    return ret;
-}
-
-/* Read from the selected logs */
-int android_logger_list_read(struct logger_list *logger_list,
-                             struct log_msg *log_msg)
-{
-    struct logger *logger;
-    nfds_t nfds;
-    struct pollfd *p, *pollfds = NULL;
-    int error = 0, ret = 0;
-
-    memset(log_msg, 0, sizeof(struct log_msg));
-
-    if (!logger_list) {
-        return -ENODEV;
-    }
-
-    if (!(accessmode(logger_list->mode) & R_OK)) {
-        logger_list->error = EPERM;
-        goto done;
-    }
-
-    nfds = 0;
-    logger_for_each(logger, logger_list) {
-        ++nfds;
-    }
-    if (nfds <= 0) {
-        error = ENODEV;
-        goto done;
-    }
-
-    /* Do we have anything to offer from the buffer or state? */
-    if (logger_list->valid_entry) { /* implies we are also in a flush state */
-        goto flush;
-    }
-
-    ret = android_logger_list_flush(logger_list, log_msg);
-    if (ret) {
-        goto done;
-    }
-
-    if (logger_list->error) { /* implies we are also in a flush state */
-        goto done;
-    }
-
-    /* Lets start grinding on metal */
-    pollfds = calloc(nfds, sizeof(struct pollfd));
-    if (!pollfds) {
-        error = ENOMEM;
-        goto flush;
-    }
-
-    p = pollfds;
-    logger_for_each(logger, logger_list) {
-        p->fd = logger->fd;
-        p->events = POLLIN;
-        logger->revents = &p->revents;
-        ++p;
-    }
-
-    while (!ret && !error) {
-        int result;
-
-        /* If we oversleep it's ok, i.e. ignore EINTR. */
-        result = TEMP_FAILURE_RETRY(
-                    poll(pollfds, nfds, logger_list->timeout_ms));
-
-        if (result <= 0) {
-            if (result) {
-                error = errno;
-            } else if (logger_list->mode & ANDROID_LOG_NONBLOCK) {
-                error = EAGAIN;
-            } else {
-                logger_list->timeout_ms = LOG_TIMEOUT_NEVER;
-            }
-
-            logger_list->flush = true;
-            goto try_flush;
-        }
-
-        logger_list->timeout_ms = LOG_TIMEOUT_FLUSH;
-
-        /* Anti starvation */
-        if (!logger_list->flush
-                && (logger_list->queued_lines > (queue_threshold(logger_list) / 2))) {
-            /* Any queues with input pending that is low? */
-            bool starving = false;
-            logger_for_each(logger, logger_list) {
-                if ((*(logger->revents) & POLLIN)
-                        && low_queue(&logger->log_list)) {
-                    starving = true;
-                    break;
-                }
-            }
-
-            /* pushback on any queues that are not low */
-            if (starving) {
-                logger_for_each(logger, logger_list) {
-                    if ((*(logger->revents) & POLLIN)
-                            && !low_queue(&logger->log_list)) {
-                        *(logger->revents) &= ~POLLIN;
-                    }
-                }
-            }
-        }
-
-        logger_for_each(logger, logger_list) {
-            unsigned int hdr_size;
-            struct log_list *entry;
-            int diff;
-
-            if (!(*(logger->revents) & POLLIN)) {
-                continue;
-            }
-
-            memset(logger_list->entry.buf, 0, sizeof(struct log_msg));
-            /* NOTE: driver guarantees we read exactly one full entry */
-            result = read(logger->fd, logger_list->entry.buf,
-                          LOGGER_ENTRY_MAX_LEN);
-            if (result <= 0) {
-                if (!result) {
-                    error = EIO;
-                } else if (errno != EINTR) {
-                    error = errno;
-                }
-                continue;
-            }
-
-            if (logger_list->pid
-                    && (logger_list->pid != logger_list->entry.entry.pid)) {
-                continue;
-            }
-
-            hdr_size = logger_list->entry.entry.hdr_size;
-            if (!hdr_size) {
-                hdr_size = sizeof(logger_list->entry.entry_v1);
-            }
-
-            if ((hdr_size > sizeof(struct log_msg))
-                    || (logger_list->entry.entry.len
-                        > sizeof(logger_list->entry.buf) - hdr_size)
-                    || (logger_list->entry.entry.len != result - hdr_size)) {
-                error = EINVAL;
-                continue;
-            }
-
-            /* Promote entry to v3 format */
-            diff = sizeof(logger_list->entry.entry_v3) - hdr_size;
-            if (diff > 0) {
-                if (logger_list->entry.entry.len
-                        > sizeof(logger_list->entry.buf) - hdr_size - diff) {
-                    error = EINVAL;
-                    continue;
-                }
-                result += diff;
-                memmove(logger_list->entry.buf + hdr_size + diff,
-                        logger_list->entry.buf + hdr_size,
-                        logger_list->entry.entry.len + 1);
-                memset(logger_list->entry.buf + hdr_size, 0, diff);
-                logger_list->entry.entry.hdr_size = hdr_size + diff;
-            }
-            logger_list->entry.entry.lid = logger->id;
-
-            /* speedup: If not tail, and only one list, send directly */
-            if (!logger_list->tail
-                    && (list_head(&logger_list->node)
-                        == list_tail(&logger_list->node))) {
-                ret = result;
-                memcpy(log_msg->buf, logger_list->entry.buf, result + 1);
-                break;
-            }
-
-            entry = malloc(sizeof(*entry) - sizeof(entry->entry) + result + 1);
-
-            if (!entry) {
-                logger_list->valid_entry = true;
-                error = ENOMEM;
-                break;
-            }
-
-            logger_list->queued_lines++;
-
-            memcpy(entry->entry.buf, logger_list->entry.buf, result);
-            entry->entry.buf[result] = '\0';
-            list_add_tail(&logger->log_list, &entry->node);
-        }
-
-        if (ret <= 0) {
-try_flush:
-            ret = android_logger_list_flush(logger_list, log_msg);
-        }
-    }
-
-    free(pollfds);
-
-flush:
-    if (error) {
-        logger_list->flush = true;
-    }
-
-    if (ret <= 0) {
-        ret = android_logger_list_flush(logger_list, log_msg);
-
-        if (!ret && logger_list->valid_entry) {
-            ret = logger_list->entry.entry.hdr_size;
-            if (!ret) {
-                ret = sizeof(logger_list->entry.entry_v1);
-            }
-            ret += logger_list->entry.entry.len;
-
-            memcpy(log_msg->buf, logger_list->entry.buf,
-                   sizeof(struct log_msg));
-            logger_list->valid_entry = false;
-        }
-    }
-
-done:
-    if (logger_list->error) {
-        error = logger_list->error;
-    }
-    if (error) {
-        logger_list->error = error;
-        if (!ret) {
-            ret = -error;
-        }
-    }
-    return ret;
-}
-
-/* Close all the logs */
-void android_logger_list_free(struct logger_list *logger_list)
-{
-    if (logger_list == NULL) {
-        return;
-    }
-
-    while (!list_empty(&logger_list->node)) {
-        struct listnode *node = list_head(&logger_list->node);
-        struct logger *logger = node_to_item(node, struct logger, node);
-        android_logger_free(logger);
-    }
-
-    free(logger_list);
-}
index bdee28f..a4310ae 100644 (file)
@@ -191,7 +191,11 @@ static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr)
      *  };
      */
 
-    clock_gettime(CLOCK_REALTIME, &ts);
+    if (android_log_timestamp() == 'm') {
+        clock_gettime(CLOCK_MONOTONIC, &ts);
+    } else {
+        clock_gettime(CLOCK_REALTIME, &ts);
+    }
 
     pmsg_header.magic = LOGGER_MAGIC;
     pmsg_header.len = sizeof(pmsg_header) + sizeof(header);
diff --git a/liblog/logd_write_kern.c b/liblog/logd_write_kern.c
deleted file mode 100644 (file)
index 8742b34..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Copyright (C) 2007-2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <android/set_abort_message.h>
-
-#include <log/log.h>
-#include <log/logd.h>
-#include <log/logger.h>
-
-#define LOGGER_LOG_MAIN                "log/main"
-#define LOGGER_LOG_RADIO       "log/radio"
-#define LOGGER_LOG_EVENTS      "log/events"
-#define LOGGER_LOG_SYSTEM      "log/system"
-
-#define LOG_BUF_SIZE 1024
-
-#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)
-
-static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
-static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
-
-static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
-
-#ifndef __unused
-#define __unused  __attribute__((__unused__))
-#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 __unused, struct iovec *vec __unused,
-                               size_t nr __unused)
-{
-    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) {
-        if (log_id == LOG_ID_CRASH) {
-            log_id = LOG_ID_MAIN;
-        }
-        log_fd = log_fds[(int)log_id];
-    } else {
-        return -EBADF;
-    }
-
-    do {
-        ret = log_writev(log_fd, vec, nr);
-        if (ret < 0) {
-            ret = -errno;
-        }
-    } while (ret == -EINTR);
-
-    return ret;
-}
-
-static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
-{
-    pthread_mutex_lock(&log_init_lock);
-
-    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];
-        }
-    }
-
-    pthread_mutex_unlock(&log_init_lock);
-
-    return write_to_log(log_id, vec, nr);
-}
-
-int __android_log_write(int prio, const char *tag, const char *msg)
-{
-    return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
-}
-
-int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
-{
-    struct iovec vec[3];
-    char tmp_tag[32];
-
-    if (!tag)
-        tag = "";
-
-    /* XXX: This needs to go! */
-    if ((bufID != LOG_ID_RADIO) &&
-         (!strcmp(tag, "HTC_RIL") ||
-        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
-        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
-        !strcmp(tag, "AT") ||
-        !strcmp(tag, "GSM") ||
-        !strcmp(tag, "STK") ||
-        !strcmp(tag, "CDMA") ||
-        !strcmp(tag, "PHONE") ||
-        !strcmp(tag, "SMS"))) {
-            bufID = LOG_ID_RADIO;
-            /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
-            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
-            tag = tmp_tag;
-    }
-
-    if (prio == ANDROID_LOG_FATAL) {
-        android_set_abort_message(msg);
-    }
-
-    vec[0].iov_base   = (unsigned char *) &prio;
-    vec[0].iov_len    = 1;
-    vec[1].iov_base   = (void *) tag;
-    vec[1].iov_len    = strlen(tag) + 1;
-    vec[2].iov_base   = (void *) msg;
-    vec[2].iov_len    = strlen(msg) + 1;
-
-    return write_to_log(bufID, vec, 3);
-}
-
-int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
-{
-    char buf[LOG_BUF_SIZE];
-
-    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-
-    return __android_log_write(prio, tag, buf);
-}
-
-int __android_log_print(int prio, const char *tag, const char *fmt, ...)
-{
-    va_list ap;
-    char buf[LOG_BUF_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-    va_end(ap);
-
-    return __android_log_write(prio, tag, buf);
-}
-
-int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
-{
-    va_list ap;
-    char buf[LOG_BUF_SIZE];
-
-    va_start(ap, fmt);
-    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-    va_end(ap);
-
-    return __android_log_buf_write(bufID, prio, tag, buf);
-}
-
-void __android_log_assert(const char *cond, const char *tag,
-                          const char *fmt, ...)
-{
-    char buf[LOG_BUF_SIZE];
-
-    if (fmt) {
-        va_list ap;
-        va_start(ap, fmt);
-        vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
-        va_end(ap);
-    } else {
-        /* Msg not provided, log condition.  N.B. Do not use cond directly as
-         * format string as it could contain spurious '%' syntax (e.g.
-         * "%d" in "blocks%devs == 0").
-         */
-        if (cond)
-            snprintf(buf, LOG_BUF_SIZE, "Assertion failed: %s", cond);
-        else
-            strcpy(buf, "Unspecified assertion failed");
-    }
-
-    __android_log_write(ANDROID_LOG_FATAL, tag, buf);
-    abort(); /* abort so we have a chance to debug the situation */
-    /* NOTREACHED */
-}
-
-int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
-{
-    struct iovec vec[2];
-
-    vec[0].iov_base = &tag;
-    vec[0].iov_len = sizeof(tag);
-    vec[1].iov_base = (void*)payload;
-    vec[1].iov_len = len;
-
-    return write_to_log(LOG_ID_EVENTS, vec, 2);
-}
-
-/*
- * Like __android_log_bwrite, but takes the type as well.  Doesn't work
- * for the general case where we're generating lists of stuff, but very
- * handy if we just want to dump an integer into the log.
- */
-int __android_log_btwrite(int32_t tag, char type, const void *payload,
-                          size_t len)
-{
-    struct iovec vec[3];
-
-    vec[0].iov_base = &tag;
-    vec[0].iov_len = sizeof(tag);
-    vec[1].iov_base = &type;
-    vec[1].iov_len = sizeof(type);
-    vec[2].iov_base = (void*)payload;
-    vec[2].iov_len = len;
-
-    return write_to_log(LOG_ID_EVENTS, vec, 3);
-}
-
-/*
- * Like __android_log_bwrite, but used for writing strings to the
- * event log.
- */
-int __android_log_bswrite(int32_t tag, const char *payload)
-{
-    struct iovec vec[4];
-    char type = EVENT_TYPE_STRING;
-    uint32_t len = strlen(payload);
-
-    vec[0].iov_base = &tag;
-    vec[0].iov_len = sizeof(tag);
-    vec[1].iov_base = &type;
-    vec[1].iov_len = sizeof(type);
-    vec[2].iov_base = &len;
-    vec[2].iov_len = sizeof(len);
-    vec[3].iov_base = (void*)payload;
-    vec[3].iov_len = len;
-
-    return write_to_log(LOG_ID_EVENTS, vec, 4);
-}
index b6dba2e..9f12c96 100644 (file)
 #include <inttypes.h>
 #include <sys/param.h>
 
+#include <cutils/list.h>
 #include <log/logd.h>
 #include <log/logprint.h>
 
+#define MS_PER_NSEC 1000000
+#define US_PER_NSEC 1000
+
 /* open coded fragment, prevent circular dependencies */
 #define WEAK static
 
@@ -50,6 +54,8 @@ struct AndroidLogFormat_t {
     bool printable_output;
     bool year_output;
     bool zone_output;
+    bool epoch_output;
+    bool monotonic_output;
 };
 
 /*
@@ -196,10 +202,14 @@ AndroidLogFormat *android_log_format_new()
     p_ret->printable_output = false;
     p_ret->year_output = false;
     p_ret->zone_output = false;
+    p_ret->epoch_output = false;
+    p_ret->monotonic_output = android_log_timestamp() == 'm';
 
     return p_ret;
 }
 
+static list_declare(convertHead);
+
 void android_log_format_free(AndroidLogFormat *p_format)
 {
     FilterInfo *p_info, *p_info_old;
@@ -214,9 +224,14 @@ void android_log_format_free(AndroidLogFormat *p_format)
     }
 
     free(p_format);
-}
-
 
+    /* Free conversion resource, can always be reconstructed */
+    while (!list_empty(&convertHead)) {
+        struct listnode *node = list_head(&convertHead);
+        list_remove(node);
+        free(node);
+    }
+}
 
 int android_log_setPrintFormat(AndroidLogFormat *p_format,
         AndroidLogPrintFormat format)
@@ -237,6 +252,12 @@ int android_log_setPrintFormat(AndroidLogFormat *p_format,
     case FORMAT_MODIFIER_ZONE:
         p_format->zone_output = !p_format->zone_output;
         return 0;
+    case FORMAT_MODIFIER_EPOCH:
+        p_format->epoch_output = true;
+        return 0;
+    case FORMAT_MODIFIER_MONOTONIC:
+        p_format->monotonic_output = true;
+        return 0;
     default:
         break;
     }
@@ -267,6 +288,8 @@ AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
     else if (strcmp(formatString, "printable") == 0) format = FORMAT_MODIFIER_PRINTABLE;
     else if (strcmp(formatString, "year") == 0) format = FORMAT_MODIFIER_YEAR;
     else if (strcmp(formatString, "zone") == 0) format = FORMAT_MODIFIER_ZONE;
+    else if (strcmp(formatString, "epoch") == 0) format = FORMAT_MODIFIER_EPOCH;
+    else if (strcmp(formatString, "monotonic") == 0) format = FORMAT_MODIFIER_MONOTONIC;
     else {
         extern char *tzname[2];
         static const char gmt[] = "GMT";
@@ -913,6 +936,285 @@ static size_t convertPrintable(char *p, const char *message, size_t messageLen)
     return p - begin;
 }
 
+char *readSeconds(char *e, struct timespec *t)
+{
+    unsigned long multiplier;
+    char *p;
+    t->tv_sec = strtoul(e, &p, 10);
+    if (*p != '.') {
+        return NULL;
+    }
+    t->tv_nsec = 0;
+    multiplier = NS_PER_SEC;
+    while (isdigit(*++p) && (multiplier /= 10)) {
+        t->tv_nsec += (*p - '0') * multiplier;
+    }
+    return p;
+}
+
+static struct timespec *sumTimespec(struct timespec *left,
+                                    struct timespec *right)
+{
+    left->tv_nsec += right->tv_nsec;
+    left->tv_sec += right->tv_sec;
+    if (left->tv_nsec >= (long)NS_PER_SEC) {
+        left->tv_nsec -= NS_PER_SEC;
+        left->tv_sec += 1;
+    }
+    return left;
+}
+
+static struct timespec *subTimespec(struct timespec *result,
+                                    struct timespec *left,
+                                    struct timespec *right)
+{
+    result->tv_nsec = left->tv_nsec - right->tv_nsec;
+    result->tv_sec = left->tv_sec - right->tv_sec;
+    if (result->tv_nsec < 0) {
+        result->tv_nsec += NS_PER_SEC;
+        result->tv_sec -= 1;
+    }
+    return result;
+}
+
+static long long nsecTimespec(struct timespec *now)
+{
+    return (long long)now->tv_sec * NS_PER_SEC + now->tv_nsec;
+}
+
+static void convertMonotonic(struct timespec *result,
+                             const AndroidLogEntry *entry)
+{
+    struct listnode *node;
+    struct conversionList {
+        struct listnode node; /* first */
+        struct timespec time;
+        struct timespec convert;
+    } *list, *next;
+    struct timespec time, convert;
+
+    /* If we do not have a conversion list, build one up */
+    if (list_empty(&convertHead)) {
+        bool suspended_pending = false;
+        struct timespec suspended_monotonic = { 0, 0 };
+        struct timespec suspended_diff = { 0, 0 };
+
+        /*
+         * Read dmesg for _some_ synchronization markers and insert
+         * Anything in the Android Logger before the dmesg logging span will
+         * be highly suspect regarding the monotonic time calculations.
+         */
+        FILE *p = popen("/system/bin/dmesg", "r");
+        if (p) {
+            char *line = NULL;
+            size_t len = 0;
+            while (getline(&line, &len, p) > 0) {
+                static const char suspend[] = "PM: suspend entry ";
+                static const char resume[] = "PM: suspend exit ";
+                static const char healthd[] = "healthd";
+                static const char battery[] = ": battery ";
+                static const char suspended[] = "Suspended for ";
+                struct timespec monotonic;
+                struct tm tm;
+                char *cp, *e = line;
+                bool add_entry = true;
+
+                if (*e == '<') {
+                    while (*e && (*e != '>')) {
+                        ++e;
+                    }
+                    if (*e != '>') {
+                        continue;
+                    }
+                }
+                if (*e != '[') {
+                    continue;
+                }
+                while (*++e == ' ') {
+                    ;
+                }
+                e = readSeconds(e, &monotonic);
+                if (!e || (*e != ']')) {
+                    continue;
+                }
+
+                if ((e = strstr(e, suspend))) {
+                    e += sizeof(suspend) - 1;
+                } else if ((e = strstr(line, resume))) {
+                    e += sizeof(resume) - 1;
+                } else if (((e = strstr(line, healthd)))
+                        && ((e = strstr(e + sizeof(healthd) - 1, battery)))) {
+                    /* NB: healthd is roughly 150us late, worth the price to
+                     * deal with ntp-induced or hardware clock drift. */
+                    e += sizeof(battery) - 1;
+                } else if ((e = strstr(line, suspended))) {
+                    e += sizeof(suspended) - 1;
+                    e = readSeconds(e, &time);
+                    if (!e) {
+                        continue;
+                    }
+                    add_entry = false;
+                    suspended_pending = true;
+                    suspended_monotonic = monotonic;
+                    suspended_diff = time;
+                } else {
+                    continue;
+                }
+                if (add_entry) {
+                    /* look for "????-??-?? ??:??:??.????????? UTC" */
+                    cp = strstr(e, " UTC");
+                    if (!cp || ((cp - e) < 29) || (cp[-10] != '.')) {
+                        continue;
+                    }
+                    e = cp - 29;
+                    cp = readSeconds(cp - 10, &time);
+                    if (!cp) {
+                        continue;
+                    }
+                    cp = strptime(e, "%Y-%m-%d %H:%M:%S.", &tm);
+                    if (!cp) {
+                        continue;
+                    }
+                    cp = getenv(tz);
+                    if (cp) {
+                        cp = strdup(cp);
+                    }
+                    setenv(tz, utc, 1);
+                    time.tv_sec = mktime(&tm);
+                    if (cp) {
+                        setenv(tz, cp, 1);
+                        free(cp);
+                    } else {
+                        unsetenv(tz);
+                    }
+                    list = calloc(1, sizeof(struct conversionList));
+                    list_init(&list->node);
+                    list->time = time;
+                    subTimespec(&list->convert, &time, &monotonic);
+                    list_add_tail(&convertHead, &list->node);
+                }
+                if (suspended_pending && !list_empty(&convertHead)) {
+                    list = node_to_item(list_tail(&convertHead),
+                                        struct conversionList, node);
+                    if (subTimespec(&time,
+                                    subTimespec(&time,
+                                                &list->time,
+                                                &list->convert),
+                                    &suspended_monotonic)->tv_sec > 0) {
+                        /* resume, what is convert factor before? */
+                        subTimespec(&convert, &list->convert, &suspended_diff);
+                    } else {
+                        /* suspend */
+                        convert = list->convert;
+                    }
+                    time = suspended_monotonic;
+                    sumTimespec(&time, &convert);
+                    /* breakpoint just before sleep */
+                    list = calloc(1, sizeof(struct conversionList));
+                    list_init(&list->node);
+                    list->time = time;
+                    list->convert = convert;
+                    list_add_tail(&convertHead, &list->node);
+                    /* breakpoint just after sleep */
+                    list = calloc(1, sizeof(struct conversionList));
+                    list_init(&list->node);
+                    list->time = time;
+                    sumTimespec(&list->time, &suspended_diff);
+                    list->convert = convert;
+                    sumTimespec(&list->convert, &suspended_diff);
+                    list_add_tail(&convertHead, &list->node);
+                    suspended_pending = false;
+                }
+            }
+            pclose(p);
+        }
+        /* last entry is our current time conversion */
+        list = calloc(1, sizeof(struct conversionList));
+        list_init(&list->node);
+        clock_gettime(CLOCK_REALTIME, &list->time);
+        clock_gettime(CLOCK_MONOTONIC, &convert);
+        clock_gettime(CLOCK_MONOTONIC, &time);
+        /* Correct for instant clock_gettime latency (syscall or ~30ns) */
+        subTimespec(&time, &convert, subTimespec(&time, &time, &convert));
+        /* Calculate conversion factor */
+        subTimespec(&list->convert, &list->time, &time);
+        list_add_tail(&convertHead, &list->node);
+        if (suspended_pending) {
+            /* manufacture a suspend @ point before */
+            subTimespec(&convert, &list->convert, &suspended_diff);
+            time = suspended_monotonic;
+            sumTimespec(&time, &convert);
+            /* breakpoint just after sleep */
+            list = calloc(1, sizeof(struct conversionList));
+            list_init(&list->node);
+            list->time = time;
+            sumTimespec(&list->time, &suspended_diff);
+            list->convert = convert;
+            sumTimespec(&list->convert, &suspended_diff);
+            list_add_head(&convertHead, &list->node);
+            /* breakpoint just before sleep */
+            list = calloc(1, sizeof(struct conversionList));
+            list_init(&list->node);
+            list->time = time;
+            list->convert = convert;
+            list_add_head(&convertHead, &list->node);
+        }
+    }
+
+    /* Find the breakpoint in the conversion list */
+    list = node_to_item(list_head(&convertHead), struct conversionList, node);
+    next = NULL;
+    list_for_each(node, &convertHead) {
+        next = node_to_item(node, struct conversionList, node);
+        if (entry->tv_sec < next->time.tv_sec) {
+            break;
+        } else if (entry->tv_sec == next->time.tv_sec) {
+            if (entry->tv_nsec < next->time.tv_nsec) {
+                break;
+            }
+        }
+        list = next;
+    }
+
+    /* blend time from one breakpoint to the next */
+    convert = list->convert;
+    if (next) {
+        unsigned long long total, run;
+
+        total = nsecTimespec(subTimespec(&time, &next->time, &list->time));
+        time.tv_sec = entry->tv_sec;
+        time.tv_nsec = entry->tv_nsec;
+        run = nsecTimespec(subTimespec(&time, &time, &list->time));
+        if (run < total) {
+            long long crun;
+
+            float f = nsecTimespec(subTimespec(&time, &next->convert, &convert));
+            f *= run;
+            f /= total;
+            crun = f;
+            convert.tv_sec += crun / (long long)NS_PER_SEC;
+            if (crun < 0) {
+                convert.tv_nsec -= (-crun) % NS_PER_SEC;
+                if (convert.tv_nsec < 0) {
+                    convert.tv_nsec += NS_PER_SEC;
+                    convert.tv_sec -= 1;
+                }
+            } else {
+                convert.tv_nsec += crun % NS_PER_SEC;
+                if (convert.tv_nsec >= (long)NS_PER_SEC) {
+                    convert.tv_nsec -= NS_PER_SEC;
+                    convert.tv_sec += 1;
+                }
+            }
+        }
+    }
+
+    /* Apply the correction factor */
+    result->tv_sec = entry->tv_sec;
+    result->tv_nsec = entry->tv_nsec;
+    subTimespec(result, result, &convert);
+}
+
 /**
  * Formats a log message into a buffer
  *
@@ -936,7 +1238,9 @@ char *android_log_formatLogLine (
     char prefixBuf[128], suffixBuf[128];
     char priChar;
     int prefixSuffixIsHeaderFooter = 0;
-    char * ret = NULL;
+    char *ret = NULL;
+    time_t now;
+    unsigned long nsec;
 
     priChar = filterPriToChar(entry->priority);
     size_t prefixLen = 0, suffixLen = 0;
@@ -954,23 +1258,44 @@ char *android_log_formatLogLine (
      * The caller may have affected the timezone environment, this is
      * expected to be sensitive to that.
      */
+    now = entry->tv_sec;
+    nsec = entry->tv_nsec;
+    if (p_format->monotonic_output) {
+        // prevent convertMonotonic from being called if logd is monotonic
+        if (android_log_timestamp() != 'm') {
+            struct timespec time;
+            convertMonotonic(&time, entry);
+            now = time.tv_sec;
+            nsec = time.tv_nsec;
+        }
+    }
+    if (now < 0) {
+        nsec = NS_PER_SEC - nsec;
+    }
+    if (p_format->epoch_output || p_format->monotonic_output) {
+        ptm = NULL;
+        snprintf(timeBuf, sizeof(timeBuf),
+                 p_format->monotonic_output ? "%6lld" : "%19lld",
+                 (long long)now);
+    } else {
 #if !defined(_WIN32)
-    ptm = localtime_r(&(entry->tv_sec), &tmBuf);
+        ptm = localtime_r(&now, &tmBuf);
 #else
-    ptm = localtime(&(entry->tv_sec));
+        ptm = localtime(&now);
 #endif
-    strftime(timeBuf, sizeof(timeBuf),
-             &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3],
-             ptm);
+        strftime(timeBuf, sizeof(timeBuf),
+                 &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3],
+                 ptm);
+    }
     len = strlen(timeBuf);
     if (p_format->usec_time_output) {
         len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
-                        ".%06ld", entry->tv_nsec / 1000);
+                        ".%06ld", nsec / US_PER_NSEC);
     } else {
         len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
-                        ".%03ld", entry->tv_nsec / 1000000);
+                        ".%03ld", nsec / MS_PER_NSEC);
     }
-    if (p_format->zone_output) {
+    if (p_format->zone_output && ptm) {
         strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm);
     }
 
index a407c50..8229859 100644 (file)
@@ -65,10 +65,6 @@ ifneq ($(wildcard $(LOCAL_PATH)/../../../../bionic/libc/bionic/libc_logging.cpp)
 test_src_files += \
     libc_test.cpp
 
-ifneq ($(TARGET_USES_LOGD),false)
-test_c_flags += -DTARGET_USES_LOGD
-endif
-
 endif
 
 # Build tests for the device (with .so). Run with:
index 0e84f4e..9dd6f03 100644 (file)
@@ -25,9 +25,7 @@
 #define _ANDROID_LOG_H // Priorities redefined
 #define _LIBS_LOG_LOG_H // log ids redefined
 typedef unsigned char log_id_t; // log_id_t missing as a result
-#ifdef TARGET_USES_LOGD
 #define _LIBS_LOG_LOG_READ_H // log_time redefined
-#endif
 
 #include <log/log.h>
 #include <log/logger.h>
index a8fb3eb..7b170f5 100644 (file)
@@ -13,7 +13,6 @@ include $(BUILD_SHARED_LIBRARY)
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := memtrack_test.c
 LOCAL_MODULE := memtrack_test
-LOCAL_C_INCLUDES := $(call include-path-for, libpagemap)
 LOCAL_SHARED_LIBRARIES := libmemtrack libpagemap
 LOCAL_CFLAGS := -Wall -Werror
 include $(BUILD_EXECUTABLE)
index 83169eb..d20d44c 100644 (file)
@@ -37,4 +37,22 @@ LOCAL_MULTILIB := both
 
 include $(BUILD_HOST_SHARED_LIBRARY)
 
+# Static library for host
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
 include $(LOCAL_PATH)/tests/Android.mk
index f63497b..32a65ea 100644 (file)
@@ -109,6 +109,13 @@ static bool CharacterAllowed(char c, bool first) {
   }
 }
 
+static void ReleaseAppCodeCacheDir() {
+  if (app_code_cache_dir != nullptr) {
+    delete[] app_code_cache_dir;
+    app_code_cache_dir = nullptr;
+  }
+}
+
 // We only allow simple names for the library. It is supposed to be a file in
 // /system/lib or /vendor/lib. Only allow a small range of characters, that is
 // names consisting of [a-zA-Z0-9._-] and starting with [a-zA-Z].
@@ -162,8 +169,7 @@ static bool VersionCheck(const NativeBridgeCallbacks* cb) {
 static void CloseNativeBridge(bool with_error) {
   state = NativeBridgeState::kClosed;
   had_error |= with_error;
-  delete[] app_code_cache_dir;
-  app_code_cache_dir = nullptr;
+  ReleaseAppCodeCacheDir();
 }
 
 bool LoadNativeBridge(const char* nb_library_filename,
@@ -289,13 +295,13 @@ bool PreInitializeNativeBridge(const char* app_data_dir_in, const char* instruct
   // so we save the extra file existence check.
   char cpuinfo_path[1024];
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
   snprintf(cpuinfo_path, sizeof(cpuinfo_path), "/system/lib"
 #ifdef __LP64__
       "64"
 #endif  // __LP64__
       "/%s/cpuinfo", instruction_set);
-#else   // !HAVE_ANDROID_OS
+#else   // !__ANDROID__
   // To be able to test on the host, we hardwire a relative path.
   snprintf(cpuinfo_path, sizeof(cpuinfo_path), "./cpuinfo");
 #endif
@@ -406,16 +412,16 @@ bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
     if (stat(app_code_cache_dir, &st) == -1) {
       if (errno == ENOENT) {
         if (mkdir(app_code_cache_dir, S_IRWXU | S_IRWXG | S_IXOTH) == -1) {
-          ALOGE("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
-          CloseNativeBridge(true);
+          ALOGW("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+          ReleaseAppCodeCacheDir();
         }
       } else {
-        ALOGE("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
-        CloseNativeBridge(true);
+        ALOGW("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+        ReleaseAppCodeCacheDir();
       }
     } else if (!S_ISDIR(st.st_mode)) {
-      ALOGE("Code cache is not a directory %s.", app_code_cache_dir);
-      CloseNativeBridge(true);
+      ALOGW("Code cache is not a directory %s.", app_code_cache_dir);
+      ReleaseAppCodeCacheDir();
     }
 
     // If we're still PreInitialized (dind't fail the code cache checks) try to initialize.
@@ -424,8 +430,7 @@ bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) {
         SetupEnvironment(callbacks, env, instruction_set);
         state = NativeBridgeState::kInitialized;
         // We no longer need the code cache path, release the memory.
-        delete[] app_code_cache_dir;
-        app_code_cache_dir = nullptr;
+        ReleaseAppCodeCacheDir();
       } else {
         // Unload the library.
         dlclose(native_bridge_handle);
index 285e8c2..7265939 100644 (file)
@@ -9,6 +9,7 @@ include $(CLEAR_VARS)
 test_src_files := \
     CodeCacheCreate_test.cpp \
     CodeCacheExists_test.cpp \
+    CodeCacheStatFail_test.cpp \
     CompleteFlow_test.cpp \
     InvalidCharsNativeBridge_test.cpp \
     NativeBridge2Signal_test.cpp \
diff --git a/libnativebridge/tests/CodeCacheStatFail_test.cpp b/libnativebridge/tests/CodeCacheStatFail_test.cpp
new file mode 100644 (file)
index 0000000..4ea519e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+namespace android {
+
+// Tests that the bridge is initialized without errors if the code_cache is
+// existed as a file.
+TEST_F(NativeBridgeTest, CodeCacheStatFail) {
+    int fd = creat(kCodeCache, O_RDWR);
+    ASSERT_NE(-1, fd);
+    close(fd);
+
+    struct stat st;
+    ASSERT_EQ(-1, stat(kCodeCacheStatFail, &st));
+    ASSERT_EQ(ENOTDIR, errno);
+
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+    ASSERT_TRUE(PreInitializeNativeBridge(kCodeCacheStatFail, "isa"));
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_FALSE(NativeBridgeError());
+
+    // Clean up
+    UnloadNativeBridge();
+
+    ASSERT_FALSE(NativeBridgeError());
+    unlink(kCodeCache);
+}
+
+}  // namespace android
index 6a5c126..d489420 100644 (file)
@@ -24,6 +24,7 @@
 
 constexpr const char* kNativeBridgeLibrary = "libnativebridge-dummy.so";
 constexpr const char* kCodeCache = "./code_cache";
+constexpr const char* kCodeCacheStatFail = "./code_cache/temp";
 
 namespace android {
 
index cec26ce..d3bbebe 100644 (file)
@@ -32,8 +32,8 @@ static constexpr const char* kTestData = "PreInitializeNativeBridge test.";
 
 TEST_F(NativeBridgeTest, PreInitializeNativeBridge) {
     ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
-#ifndef __APPLE__         // Mac OS does not support bind-mount.
-#ifndef HAVE_ANDROID_OS   // Cannot write into the hard-wired location.
+#if !defined(__APPLE__)         // Mac OS does not support bind-mount.
+#if !defined(__ANDROID__)       // Cannot write into the hard-wired location.
     // Try to create our mount namespace.
     if (unshare(CLONE_NEWNS) != -1) {
         // Create a dummy file.
index 352ac5e..a2d3869 100644 (file)
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <err.h>
 #include <errno.h>
 #include <error.h>
 #include <stdbool.h>
@@ -29,12 +30,14 @@ int main(int argc, char* argv[]) {
 
   char* interface = argv[1];
   if (ifc_init()) {
-    error(EXIT_FAILURE, errno, "dhcptool %s: ifc_init failed", interface);
+    err(errno, "dhcptool %s: ifc_init failed", interface);
+    ifc_close();
+    return EXIT_FAILURE;
   }
 
   int rc = do_dhcp(interface);
   if (rc) {
-    error(0, errno, "dhcptool %s: do_dhcp failed", interface);
+    err(errno, "dhcptool %s: do_dhcp failed", interface);
   }
 
   ifc_close();
index 7d2a5fb..aa18bc1 100644 (file)
@@ -19,6 +19,7 @@
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
+#include <pthread.h>
 
 #include <sys/socket.h>
 #include <sys/select.h>
 #define ALOGW printf
 #endif
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 /* SIOCKILLADDR is an Android extension. */
 #define SIOCKILLADDR 0x8939
 #endif
 
 static int ifc_ctl_sock = -1;
 static int ifc_ctl_sock6 = -1;
+static pthread_mutex_t ifc_sock_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static pthread_mutex_t ifc_sock6_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
 void printerr(char *fmt, ...);
 
 #define DBG 0
@@ -122,6 +125,8 @@ int string_to_ip(const char *string, struct sockaddr_storage *ss) {
 int ifc_init(void)
 {
     int ret;
+
+    pthread_mutex_lock(&ifc_sock_mutex);
     if (ifc_ctl_sock == -1) {
         ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
         if (ifc_ctl_sock < 0) {
@@ -136,6 +141,7 @@ int ifc_init(void)
 
 int ifc_init6(void)
 {
+    pthread_mutex_lock(&ifc_sock6_mutex);
     if (ifc_ctl_sock6 == -1) {
         ifc_ctl_sock6 = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
         if (ifc_ctl_sock6 < 0) {
@@ -152,6 +158,7 @@ void ifc_close(void)
         (void)close(ifc_ctl_sock);
         ifc_ctl_sock = -1;
     }
+    pthread_mutex_unlock(&ifc_sock_mutex);
 }
 
 void ifc_close6(void)
@@ -160,6 +167,7 @@ void ifc_close6(void)
         (void)close(ifc_ctl_sock6);
         ifc_ctl_sock6 = -1;
     }
+    pthread_mutex_unlock(&ifc_sock6_mutex);
 }
 
 static void ifc_init_ifr(const char *name, struct ifreq *ifr)
@@ -534,6 +542,7 @@ int ifc_act_on_ipv4_route(int action, const char *ifname, struct in_addr dst, in
     ifc_init();
 
     if (ifc_ctl_sock < 0) {
+        ifc_close();
         return -errno;
     }
 
@@ -596,7 +605,7 @@ int ifc_disable(const char *ifname)
 
 int ifc_reset_connections(const char *ifname, const int reset_mask)
 {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     int result, success;
     in_addr_t myaddr = 0;
     struct ifreq ifr;
diff --git a/libpackagelistparser/Android.mk b/libpackagelistparser/Android.mk
new file mode 100644 (file)
index 0000000..c8be050
--- /dev/null
@@ -0,0 +1,32 @@
+LOCAL_PATH:= $(call my-dir)
+
+#########################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libpackagelistparser
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := packagelistparser.c
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+
+include $(BUILD_SHARED_LIBRARY)
+
+#########################
+include $(CLEAR_VARS)
+
+
+LOCAL_MODULE := libpackagelistparser
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := packagelistparser.c
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
new file mode 100644 (file)
index 0000000..d602c26
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015, Intel Corporation
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Written by William Roberts <william.c.roberts@intel.com>
+ *
+ * This is a parser library for parsing the packages.list file generated
+ * by PackageManager service.
+ *
+ * This simple parser is sensitive to format changes in
+ * frameworks/base/services/core/java/com/android/server/pm/Settings.java
+ * A dependency note has been added to that file to correct
+ * this parser.
+ */
+
+#ifndef PACKAGELISTPARSER_H_
+#define PACKAGELISTPARSER_H_
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/** The file containing the list of installed packages on the system */
+#define PACKAGES_LIST_FILE  "/data/system/packages.list"
+
+typedef struct pkg_info pkg_info;
+typedef struct gid_list gid_list;
+
+struct gid_list {
+    size_t cnt;
+    gid_t *gids;
+};
+
+struct pkg_info {
+    char *name;
+    uid_t uid;
+    bool debuggable;
+    char *data_dir;
+    char *seinfo;
+    gid_list gids;
+    void *private_data;
+};
+
+/**
+ * Callback function to be used by packagelist_parse() routine.
+ * @param info
+ *  The parsed package information
+ * @param userdata
+ *  The supplied userdata pointer to packagelist_parse()
+ * @return
+ *  true to keep processing, false to stop.
+ */
+typedef bool (*pfn_on_package)(pkg_info *info, void *userdata);
+
+/**
+ * Parses the file specified by PACKAGES_LIST_FILE and invokes the callback on
+ * each entry found. Once the callback is invoked, ownership of the pkg_info pointer
+ * is passed to the callback routine, thus they are required to perform any cleanup
+ * desired.
+ * @param callback
+ *  The callback function called on each parsed line of the packages list.
+ * @param userdata
+ *  An optional userdata supplied pointer to pass to the callback function.
+ * @return
+ *  true on success false on failure.
+ */
+extern bool packagelist_parse(pfn_on_package callback, void *userdata);
+
+/**
+ * Frees a pkg_info structure.
+ * @param info
+ *  The struct to free
+ */
+extern void packagelist_free(pkg_info *info);
+
+__END_DECLS
+
+#endif /* PACKAGELISTPARSER_H_ */
diff --git a/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
new file mode 100644 (file)
index 0000000..16052e2
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2015, Intel Corporation
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Written by William Roberts <william.c.roberts@intel.com>
+ *
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/limits.h>
+
+#define LOG_TAG "packagelistparser"
+#include <utils/Log.h>
+
+#include <packagelistparser/packagelistparser.h>
+
+#define CLOGE(fmt, ...) \
+    do {\
+        IF_ALOGE() {\
+            ALOGE(fmt, ##__VA_ARGS__);\
+        }\
+    } while(0)
+
+static size_t get_gid_cnt(const char *gids)
+{
+    size_t cnt;
+
+    if (*gids == '\0') {
+        return 0;
+    }
+
+    if (!strcmp(gids, "none")) {
+        return 0;
+    }
+
+    for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++)
+        ;
+
+    return cnt;
+}
+
+static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt)
+{
+    gid_t gid;
+    char* token;
+    char *endptr;
+    size_t cmp = 0;
+
+    while ((token = strsep(&gids, ",\r\n"))) {
+
+        if (cmp > *cnt) {
+            return false;
+        }
+
+        gid = strtoul(token, &endptr, 10);
+        if (*endptr != '\0') {
+            return false;
+        }
+
+        /*
+         * if unsigned long is greater than size of gid_t,
+         * prevent a truncation based roll-over
+         */
+        if (gid > GID_MAX) {
+            CLOGE("A gid in field \"gid list\" greater than GID_MAX");
+            return false;
+        }
+
+        gid_list[cmp++] = gid;
+    }
+    return true;
+}
+
+extern bool packagelist_parse(pfn_on_package callback, void *userdata)
+{
+
+    FILE *fp;
+    char *cur;
+    char *next;
+    char *endptr;
+    unsigned long tmp;
+    ssize_t bytesread;
+
+    bool rc = false;
+    char *buf = NULL;
+    size_t buflen = 0;
+    unsigned long lineno = 1;
+    const char *errmsg = NULL;
+    struct pkg_info *pkg_info = NULL;
+
+    fp = fopen(PACKAGES_LIST_FILE, "re");
+    if (!fp) {
+        CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE,
+                strerror(errno));
+        return false;
+    }
+
+    while ((bytesread = getline(&buf, &buflen, fp)) > 0) {
+
+        pkg_info = calloc(1, sizeof(*pkg_info));
+        if (!pkg_info) {
+            goto err;
+        }
+
+        next = buf;
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for \"package name\"";
+            goto err;
+        }
+
+        pkg_info->name = strdup(cur);
+        if (!pkg_info->name) {
+            goto err;
+        }
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"uid\"";
+            goto err;
+        }
+
+        tmp = strtoul(cur, &endptr, 10);
+        if (*endptr != '\0') {
+            errmsg = "Could not convert field \"uid\" to integer value";
+            goto err;
+        }
+
+        /*
+         * if unsigned long is greater than size of uid_t,
+         * prevent a truncation based roll-over
+         */
+        if (tmp > UID_MAX) {
+            errmsg = "Field \"uid\" greater than UID_MAX";
+            goto err;
+        }
+
+        pkg_info->uid = (uid_t) tmp;
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"debuggable\"";
+            goto err;
+        }
+
+        tmp = strtoul(cur, &endptr, 10);
+        if (*endptr != '\0') {
+            errmsg = "Could not convert field \"debuggable\" to integer value";
+            goto err;
+        }
+
+        /* should be a valid boolean of 1 or 0 */
+        if (!(tmp == 0 || tmp == 1)) {
+            errmsg = "Field \"debuggable\" is not 0 or 1 boolean value";
+            goto err;
+        }
+
+        pkg_info->debuggable = (bool) tmp;
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"data dir\"";
+            goto err;
+        }
+
+        pkg_info->data_dir = strdup(cur);
+        if (!pkg_info->data_dir) {
+            goto err;
+        }
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"seinfo\"";
+            goto err;
+        }
+
+        pkg_info->seinfo = strdup(cur);
+        if (!pkg_info->seinfo) {
+            goto err;
+        }
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"gid(s)\"";
+            goto err;
+        }
+
+        /*
+         * Parse the gid list, could be in the form of none, single gid or list:
+         * none
+         * gid
+         * gid, gid ...
+         */
+        pkg_info->gids.cnt = get_gid_cnt(cur);
+        if (pkg_info->gids.cnt > 0) {
+
+            pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t));
+            if (!pkg_info->gids.gids) {
+                goto err;
+            }
+
+            rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt);
+            if (!rc) {
+                errmsg = "Could not parse field \"gid list\"";
+                goto err;
+            }
+        }
+
+        rc = callback(pkg_info, userdata);
+        if (rc == false) {
+            /*
+             * We do not log this as this can be intentional from
+             * callback to abort processing. We go to out to not
+             * free the pkg_info
+             */
+            rc = true;
+            goto out;
+        }
+        lineno++;
+    }
+
+    rc = true;
+
+out:
+    free(buf);
+    fclose(fp);
+    return rc;
+
+err:
+    if (errmsg) {
+        CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s",
+                PACKAGES_LIST_FILE, lineno, errmsg);
+    }
+    rc = false;
+    packagelist_free(pkg_info);
+    goto out;
+}
+
+void packagelist_free(pkg_info *info)
+{
+    if (info) {
+        free(info->name);
+        free(info->data_dir);
+        free(info->seinfo);
+        free(info->gids.gids);
+        free(info);
+    }
+}
index f02da7f..9a937f8 100644 (file)
@@ -50,6 +50,14 @@ PIXELFLINGER_SRC_FILES_mips := \
        arch-mips/t32cb16blend.S \
 
 endif
+
+PIXELFLINGER_SRC_FILES_mips64 := \
+        codeflinger/MIPSAssembler.cpp \
+       codeflinger/MIPS64Assembler.cpp \
+       codeflinger/mips64_disassem.c \
+       arch-mips64/col32cb16blend.S \
+       arch-mips64/t32cb16blend.S \
+
 #
 # Shared library
 #
@@ -59,6 +67,7 @@ LOCAL_SRC_FILES := $(PIXELFLINGER_SRC_FILES)
 LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm)
 LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
 LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
+LOCAL_SRC_FILES_mips64 := $(PIXELFLINGER_SRC_FILES_mips64)
 LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) \
@@ -69,10 +78,6 @@ LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
 # libhardware, but this at least gets us built.
 LOCAL_SHARED_LIBRARIES += libhardware_legacy
 LOCAL_CFLAGS += -DWITH_LIB_HARDWARE
-# t32cb16blend.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
-# arch-arm64/col32cb16blend.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
index 18a01fd..8d9c7c4 100644 (file)
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  */
     .text
-    .align
+    .align 0
 
     .global scanline_col32cb16blend_arm64
 
index 7da8cf5..230f47b 100644 (file)
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  */
     .text
-    .align
+    .align 0
 
     .global scanline_t32cb16blend_arm64
 
diff --git a/libpixelflinger/arch-mips/col32cb16blend.S b/libpixelflinger/arch-mips/col32cb16blend.S
new file mode 100644 (file)
index 0000000..5d18e55
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+       .macro pixel dreg src f sR sG sB shift
+
+#if __mips==32 && __mips_isa_rev>=2
+       /* extract red */
+       ext $t4,\src,\shift+11,5
+       mul $t4,$t4,\f
+
+       /* extract green */
+       ext $t5,\src,\shift+5,6
+       mul $t5,$t5,\f
+
+       /* extract blue */
+       ext $t6,\src,\shift,5
+       mul $t6,$t6,\f
+#else
+       /* extract red */
+       srl $t4,\src,\shift+11
+       andi $t4, 0x1f
+       mul $t4,$t4,\f
+
+       /* extract green */
+       srl $t5,\src,\shift+5
+       andi $t5, 0x3f
+       mul $t5,$t5,\f
+
+       /* extract blue */
+       srl $t6,\src,\shift
+       andi $t6, 0x1f
+       mul $t6,$t6,\f
+#endif
+
+       srl $t4,$t4,8
+       srl $t5,$t5,8
+       srl $t6,$t6,8
+       addu $t4,$t4,\sR
+       addu $t5,$t5,\sG
+       addu \dreg,$t6,\sB
+       sll $t4,$t4,11
+       sll $t5,$t5,5
+       or \dreg,\dreg,$t4
+       or \dreg,\dreg,$t5
+       andi \dreg, 0xffff
+       .endm
+
+       .text
+       .align
+
+       .global scanline_col32cb16blend_mips
+       .ent    scanline_col32cb16blend_mips
+scanline_col32cb16blend_mips:
+
+       /* check if count is zero */
+       srl     $v0,$a1,24 /* sA */
+       beqz    $a2,done
+       li      $t4, 0x100
+       srl     $v1,$v0,7
+       addu    $v0,$v1,$v0
+       subu    $v0,$t4,$v0 /* f */
+#if __mips==32 && __mips_isa_rev>=2
+       ext     $a3,$a1,3,5 /* sR */
+       ext     $t0,$a1,10,6 /* sG */
+       ext     $t1,$a1,19,5 /* sB */
+#else
+       srl     $a3, $a1, 3
+       andi    $a3, 0x1f    /* sR */
+       srl     $t0, $a1, 10
+       andi    $t0, 0x3f    /* sG */
+       srl     $t1, $a1, 19
+       andi    $t1, 0x1f    /* sB */
+#endif
+
+       /* check if cnt is at least 4 */
+       addiu   $a2,$a2,-4
+       bltz    $a2,tail
+
+loop_4pixels:
+       lw      $t7,0($a0)
+       lw      $t8,4($a0)
+       addiu   $a0,$a0,8
+       addiu   $a2,$a2,-4
+       pixel   $t2 $t7 $v0 $a3 $t0 $t1 0
+       pixel   $t3 $t7 $v0 $a3 $t0 $t1 16
+#if __mips==32 && __mips_isa_rev>=2
+       ins     $t2,$t3,16,16
+#else
+       sll $t3, 16
+       or  $t2, $t2, $t3
+#endif
+       pixel   $t7 $t8 $v0 $a3 $t0 $t1 0
+       pixel   $t3 $t8 $v0 $a3 $t0 $t1 16
+#if __mips==32 && __mips_isa_rev>=2
+       ins     $t7,$t3,16,16
+#else
+       sll $t3, 16
+       or  $t7, $t7, $t3
+#endif
+       sw      $t2,-8($a0)
+       sw      $t7,-4($a0)
+       bgez    $a2, loop_4pixels
+
+tail:
+       /* the pixel count underran, restore it now */
+       addiu   $a2,$a2,4
+
+       /* handle the last 0..3 pixels */
+       beqz    $a2,done
+
+loop_1pixel:
+       lhu     $t7,0($a0)
+       addiu   $a0,$a0,2
+       addiu   $a2,$a2,-1
+       pixel   $t2 $t7 $v0 $a3 $t0 $t1 0
+       sh      $t2, -2($a0)
+       bnez    $a2,loop_1pixel
+
+done:
+       j       $ra
+       .end    scanline_col32cb16blend_mips
index c911fbb..236a2c9 100644 (file)
  */
 
 #if __mips==32 && __mips_isa_rev>=2
-       .macro pixel dreg src fb shift
-       /*
-        * sA = s >> 24
-        * f = 0x100 - (sA + (sA>>7))
-        */
-DBG    .set    noat
-DBG    rdhwr   $at,$2
-DBG    .set    at
+    .macro pixel dreg src fb shift
+    /*
+     * sA = s >> 24
+     * f = 0x100 - (sA + (sA>>7))
+     */
+DBG .set    noat
+DBG rdhwr   $at,$2
+DBG .set    at
 
-       srl     $t7,\src,24
-       srl     $t6,$t7,7
-       addu    $t7,$t6
-       li      $t6,0x100
-       subu    $t7,$t6,$t7
+    srl  $t7,\src,24
+    srl  $t6,$t7,7
+    addu $t7,$t6
+    li   $t6,0x100
+    subu $t7,$t6,$t7
 
-       /* red */
-       ext     $t8,\dreg,\shift+6+5,5                  # dst[\shift:15..11]
-       mul     $t6,$t8,$t7
-       ext     $t0,\dreg,\shift+5,6                    # start green extraction dst[\shift:10..5]
-       ext     $t8,\src,3,5                            # src[7..3]
-       srl     $t6,8
-       addu    $t8,$t6
-       ins     \fb,$t8,\shift+6+5,5                    # dst[\shift:15..11]
+    /* red */
+    ext  $t8,\dreg,\shift+6+5,5         # dst[\shift:15..11]
+    mul  $t6,$t8,$t7
+    ext  $t0,\dreg,\shift+5,6           # start green extraction dst[\shift:10..5]
+    ext  $t8,\src,3,5               # src[7..3]
+    srl  $t6,8
+    addu $t8,$t6
+.if \shift!=0
+    sll  $t8,\shift+11
+    or   \fb,$t8
+.else
+    sll  \fb,$t8,11
+.endif
 
-        /* green */
-       mul     $t8,$t0,$t7
-       ext     $t0,\dreg,\shift,5                      # start blue extraction dst[\shift:4..0]
-       ext     $t6,\src,2+8,6                          # src[15..10]
-       srl     $t8,8
-        addu   $t8,$t6
+    /* green */
+    mul  $t8,$t0,$t7
+    ext  $t0,\dreg,\shift,5         # start blue extraction dst[\shift:4..0]
+    ext  $t6,\src,2+8,6             # src[15..10]
+    srl  $t8,8
+    addu $t8,$t6
 
-       /* blue */
-       mul     $t0,$t0,$t7
-       ins     \fb,$t8,\shift+5,6                      # finish green insertion dst[\shift:10..5]
-       ext     $t6,\src,(3+8+8),5
-       srl     $t8,$t0,8
-       addu    $t8,$t6
-       ins     \fb,$t8,\shift,5
+    /* blue */
+    mul  $t0,$t0,$t7
+    sll  $t8, $t8, \shift+5
+    or   \fb, \fb, $t8
+    ext  $t6,\src,(3+8+8),5
+    srl  $t8,$t0,8
+    addu $t8,$t6
+    sll  $t8, $t8, \shift
+    or   \fb, \fb, $t8
 
-DBG    .set    noat
-DBG    rdhwr   $t8,$2
-DBG    subu    $t8,$at
-DBG    sltu    $at,$t8,$v0
-DBG    movn    $v0,$t8,$at
-DBG    sgtu    $at,$t8,$v1
-DBG    movn    $v1,$t8,$at
-DBG    .set    at
-       .endm
+DBG .set    noat
+DBG rdhwr $t8,$2
+DBG subu  $t8,$at
+DBG sltu  $at,$t8,$v0
+DBG movn  $v0,$t8,$at
+DBG sgtu  $at,$t8,$v1
+DBG movn  $v1,$t8,$at
+DBG .set    at
+    .endm
 
 #else
 
-       .macro pixel dreg src fb shift
-       /*
-        * sA = s >> 24
-        * f = 0x100 - (sA + (sA>>7))
-        */
-DBG    .set    push
-DBG    .set    noat
-DBG    .set    mips32r2
-DBG    rdhwr   $at,$2
-DBG    .set    pop
+    .macro pixel dreg src fb shift
+    /*
+     * sA = s >> 24
+     * f = 0x100 - (sA + (sA>>7))
+     */
+DBG .set    push
+DBG .set    noat
+DBG .set    mips32r2
+DBG rdhwr   $at,$2
+DBG .set    pop
 
-       srl     $t7,\src,24
-       srl     $t6,$t7,7
-       addu    $t7,$t6
-       li      $t6,0x100
-       subu    $t7,$t6,$t7
+    srl  $t7,\src,24
+    srl  $t6,$t7,7
+    addu $t7,$t6
+    li   $t6,0x100
+    subu $t7,$t6,$t7
 
-       /*
-        * red
-        * dR = (d >> (6 + 5)) & 0x1f;
-        * dR = (f*dR)>>8
-        * sR = (s >> (   3)) & 0x1f;
-        * sR += dR
-        * fb |= sR << 11
-        */
-       srl     $t8,\dreg,\shift+6+5
+    /*
+     * red
+     * dR = (d >> (6 + 5)) & 0x1f;
+     * dR = (f*dR)>>8
+     * sR = (s >> (   3)) & 0x1f;
+     * sR += dR
+     * fb |= sR << 11
+     */
+    srl  $t8,\dreg,\shift+6+5
 .if \shift==0
-       and     $t8,0x1f
+    and  $t8,0x1f
 .endif
-       mul     $t8,$t8,$t7
-       srl     $t6,\src,3
-       and     $t6,0x1f
-       srl     $t8,8
-       addu    $t8,$t6
+    mul  $t8,$t8,$t7
+    srl  $t6,\src,3
+    and  $t6,0x1f
+    srl  $t8,8
+    addu $t8,$t6
 .if \shift!=0
-       sll     $t8,\shift+11
-       or      \fb,$t8
+    sll  $t8,\shift+11
+    or   \fb,$t8
 .else
-       sll     \fb,$t8,11
+    sll  \fb,$t8,11
 .endif
 
         /*
-        * green
-        * dG = (d >> 5) & 0x3f
-        * dG = (f*dG) >> 8
-        * sG = (s >> ( 8+2))&0x3F;
-        */
-       srl     $t8,\dreg,\shift+5
-        and    $t8,0x3f
-       mul     $t8,$t8,$t7
-        srl    $t6,\src,8+2
-        and     $t6,0x3f
-       srl     $t8,8
-        addu   $t8,$t6
-       sll     $t8,\shift + 5
-       or      \fb,$t8
+     * green
+     * dG = (d >> 5) & 0x3f
+     * dG = (f*dG) >> 8
+     * sG = (s >> ( 8+2))&0x3F;
+     */
+    srl  $t8,\dreg,\shift+5
+    and  $t8,0x3f
+    mul  $t8,$t8,$t7
+    srl  $t6,\src,8+2
+    and  $t6,0x3f
+    srl  $t8,8
+    addu $t8,$t6
+    sll  $t8,\shift + 5
+    or   \fb,$t8
 
-       /* blue */
+    /* blue */
 .if \shift!=0
-       srl     $t8,\dreg,\shift
-       and     $t8,0x1f
+    srl  $t8,\dreg,\shift
+    and  $t8,0x1f
 .else
-       and     $t8,\dreg,0x1f
+    and  $t8,\dreg,0x1f
 .endif
-       mul     $t8,$t8,$t7
-       srl     $t6,\src,(8+8+3)
-       and     $t6,0x1f
-       srl     $t8,8
-       addu    $t8,$t6
+    mul  $t8,$t8,$t7
+    srl  $t6,\src,(8+8+3)
+    and  $t6,0x1f
+    srl  $t8,8
+    addu $t8,$t6
 .if \shift!=0
-       sll     $t8,\shift
+    sll  $t8,\shift
 .endif
-       or      \fb,$t8
-DBG    .set    push
-DBG    .set    noat
-DBG    .set    mips32r2
-DBG    rdhwr   $t8,$2
-DBG    subu    $t8,$at
-DBG    sltu    $at,$t8,$v0
-DBG    movn    $v0,$t8,$at
-DBG    sgtu    $at,$t8,$v1
-DBG    movn    $v1,$t8,$at
-DBG    .set    pop
-       .endm
+    or   \fb,$t8
+DBG .set    push
+DBG .set    noat
+DBG .set    mips32r2
+DBG rdhwr   $t8,$2
+DBG subu    $t8,$at
+DBG sltu    $at,$t8,$v0
+DBG movn    $v0,$t8,$at
+DBG sgtu    $at,$t8,$v1
+DBG movn    $v1,$t8,$at
+DBG .set    pop
+    .endm
 #endif
 
-       .text
-       .align
+    .text
+    .align
 
-       .global scanline_t32cb16blend_mips
-       .ent    scanline_t32cb16blend_mips
+    .global scanline_t32cb16blend_mips
+    .ent    scanline_t32cb16blend_mips
 scanline_t32cb16blend_mips:
-DBG    li      $v0,0xffffffff
-DBG    li      $v1,0
-       /* Align the destination if necessary */
-       and     $t0,$a0,3
-       beqz    $t0,aligned
+DBG li    $v0,0xffffffff
+DBG li    $v1,0
+    /* Align the destination if necessary */
+    and   $t0,$a0,3
+    beqz  $t0,aligned
 
-       /* as long as there is at least one pixel */
-       beqz    $a2,done
+    /* as long as there is at least one pixel */
+    beqz  $a2,done
 
-       lw      $t4,($a1)
-       addu    $a0,2
-       addu    $a1,4
-       beqz    $t4,1f
-       lhu     $t3,-2($a0)
-       pixel   $t3,$t4,$t1,0
-       sh      $t1,-2($a0)
-1:     subu    $a2,1
+    lw    $t4,($a1)
+    addu  $a0,2
+    addu  $a1,4
+    beqz  $t4,1f
+    lhu   $t3,-2($a0)
+    pixel $t3,$t4,$t1,0
+    sh    $t1,-2($a0)
+1:  subu  $a2,1
 
 aligned:
-       /* Check to see if its worth unrolling the loop */
-       subu    $a2,4
-       bltz    $a2,tail
+    /* Check to see if its worth unrolling the loop */
+    subu  $a2,4
+    bltz  $a2,tail
 
-       /* Process 4 pixels at a time */
+    /* Process 4 pixels at a time */
 fourpixels:
-       /* 1st pair of pixels */
-       lw      $t4,0($a1)
-       lw      $t5,4($a1)
-       addu    $a0,8
-       addu    $a1,16
+    /* 1st pair of pixels */
+    lw    $t4,0($a1)
+    lw    $t5,4($a1)
+    addu  $a0,8
+    addu  $a1,16
 
-       /* both are zero, skip this pair */
-       or      $t3,$t4,$t5
-       beqz    $t3,1f
+    /* both are zero, skip this pair */
+    or    $t3,$t4,$t5
+    beqz  $t3,1f
 
-       /* load the destination */
-       lw      $t3,-8($a0)
+    /* load the destination */
+    lw    $t3,-8($a0)
 
-       pixel   $t3,$t4,$t1,0
-       pixel   $t3,$t5,$t1,16
-       sw      $t1,-8($a0)
+    pixel $t3,$t4,$t1,0
+    andi  $t1, 0xFFFF
+    pixel $t3,$t5,$t1,16
+    sw    $t1,-8($a0)
 
 1:
-       /* 2nd pair of pixels */
-       lw      $t4,-8($a1)
-       lw      $t5,-4($a1)
+    /* 2nd pair of pixels */
+    lw    $t4,-8($a1)
+    lw    $t5,-4($a1)
 
-       /* both are zero, skip this pair */
-       or      $t3,$t4,$t5
-       beqz    $t3,1f
+    /* both are zero, skip this pair */
+    or    $t3,$t4,$t5
+    beqz  $t3,1f
 
-       /* load the destination */
-       lw      $t3,-4($a0)
+    /* load the destination */
+    lw    $t3,-4($a0)
 
-       pixel   $t3,$t4,$t1,0
-       pixel   $t3,$t5,$t1,16
-       sw      $t1,-4($a0)
+    pixel $t3,$t4,$t1,0
+    andi  $t1, 0xFFFF
+    pixel $t3,$t5,$t1,16
+    sw    $t1,-4($a0)
 
-1:     subu    $a2,4
-       bgtz    $a2,fourpixels
+1:  subu  $a2,4
+    bgtz  $a2,fourpixels
 
 tail:
-       /* the pixel count underran, restore it now */
-       addu    $a2,4
+    /* the pixel count underran, restore it now */
+    addu  $a2,4
 
-       /* handle the last 0..3 pixels */
-       beqz    $a2,done
+    /* handle the last 0..3 pixels */
+    beqz  $a2,done
 onepixel:
-       lw      $t4,($a1)
-       addu    $a0,2
-       addu    $a1,4
-       beqz    $t4,1f
-       lhu     $t3,-2($a0)
-       pixel   $t3,$t4,$t1,0
-       sh      $t1,-2($a0)
-1:     subu    $a2,1
-       bnez    $a2,onepixel
+    lw    $t4,($a1)
+    addu  $a0,2
+    addu  $a1,4
+    beqz  $t4,1f
+    lhu   $t3,-2($a0)
+    pixel $t3,$t4,$t1,0
+    sh    $t1,-2($a0)
+1:  subu  $a2,1
+    bnez  $a2,onepixel
 done:
-DBG    .set    push
-DBG    .set    mips32r2
-DBG    rdhwr   $a0,$3
-DBG    mul     $v0,$a0
-DBG    mul     $v1,$a0
-DBG    .set    pop
-       j       $ra
-       .end    scanline_t32cb16blend_mips
+DBG .set    push
+DBG .set    mips32r2
+DBG rdhwr   $a0,$3
+DBG mul     $v0,$a0
+DBG mul     $v1,$a0
+DBG .set    pop
+    j     $ra
+    .end    scanline_t32cb16blend_mips
diff --git a/libpixelflinger/arch-mips64/col32cb16blend.S b/libpixelflinger/arch-mips64/col32cb16blend.S
new file mode 100644 (file)
index 0000000..fea4491
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+    .macro pixel dreg src f sR sG sB shift
+
+    /* extract red */
+.if \shift < 32
+    dext   $t0,\src,\shift+11,5
+.else
+    dextu  $t0,\src,\shift+11,5
+.endif
+    mul    $t0,$t0,\f
+
+    /* extract green */
+.if \shift < 32
+    dext   $t1,\src,\shift+5,6
+.else
+    dextu  $t1,\src,\shift+5,6
+.endif
+    mul    $t1,$t1,\f
+
+    /* extract blue */
+.if \shift < 32
+    dext   $t2,\src,\shift,5
+.else
+    dextu  $t2,\src,\shift,5
+.endif
+    mul    $t2,$t2,\f
+
+    srl    $t0,$t0,8
+    srl    $t1,$t1,8
+    srl    $t2,$t2,8
+    addu   $t0,$t0,\sR
+    addu   $t1,$t1,\sG
+    addu   \dreg,$t2,\sB
+    sll    $t0,$t0,11
+    sll    $t1,$t1,5
+    or     \dreg,\dreg,$t0
+    or     \dreg,\dreg,$t1
+    .endm
+
+    .text
+    .align
+
+    .global scanline_col32cb16blend_mips64
+    .ent    scanline_col32cb16blend_mips64
+scanline_col32cb16blend_mips64:
+
+    /* check if count is zero */
+    srl     $v0,$a1,24 /* sA */
+    beqz    $a2,done
+    li      $t0, 0x100
+    srl     $v1,$v0,7
+    addu    $v0,$v1,$v0
+    subu    $v0,$t0,$v0 /* f */
+    ext     $a3,$a1,3,5 /* sR */
+    ext     $a4,$a1,10,6 /* sG */
+    ext     $a5,$a1,19,5 /* sB */
+
+    /* check if cnt is at least 4 */
+    addiu   $a2,$a2,-4
+    bltz    $a2,tail
+
+loop_4pixels:
+    ld      $t3,0($a0)
+    daddiu  $a0,$a0,8
+    addiu   $a2,$a2,-4
+    pixel   $a6 $t3 $v0 $a3 $a4 $a5 0
+    pixel   $a7 $t3 $v0 $a3 $a4 $a5 16
+    pixel   $t8 $t3 $v0 $a3 $a4 $a5 32
+    pixel   $t9 $t3 $v0 $a3 $a4 $a5 48
+    dins    $a6,$a7,16,16
+    dinsu   $a6,$t8,32,16
+    dinsu   $a6,$t9,48,16
+    sd      $a6,-8($a0)
+    bgez    $a2, loop_4pixels
+
+tail:
+    /* the pixel count underran, restore it now */
+    addiu   $a2,$a2,4
+
+    /* handle the last 0..3 pixels */
+    beqz    $a2,done
+
+loop_1pixel:
+    lhu     $t3,0($a0)
+    daddiu  $a0,$a0,2
+    addiu   $a2,$a2,-1
+    pixel   $a6 $t3 $v0 $a3 $a4 $a5 0
+    sh      $a6, -2($a0)
+    bnez    $a2,loop_1pixel
+
+done:
+    j       $ra
+    .end    scanline_col32cb16blend_mips64
diff --git a/libpixelflinger/arch-mips64/t32cb16blend.S b/libpixelflinger/arch-mips64/t32cb16blend.S
new file mode 100644 (file)
index 0000000..d2f4d49
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifdef DEBUG
+#define DBG
+#else
+#define DBG #
+#endif
+
+/*
+ * blend one of 2 16bpp RGB pixels held in dreg selected by shift
+ * with the 32bpp ABGR pixel held in src and store the result in fb
+ *
+ * Assumes that the dreg data is little endian and that
+ * the the second pixel (shift==16) will be merged into
+ * the fb result
+ *
+ * Uses $a4,$t2,$t3,$t8
+ */
+
+    .macro pixel dreg src fb shift
+    /*
+     * sA = s >> 24
+     * f = 0x100 - (sA + (sA>>7))
+     */
+    srl     $t3,\src,24
+    srl     $t2,$t3,7
+    addu    $t3,$t2
+    li      $t2,0x100
+    subu    $t3,$t2,$t3
+
+    /* red */
+    ext     $t8,\dreg,\shift+6+5,5                  # dst[\shift:15..11]
+    mul     $t2,$t8,$t3
+    ext     $a4,\dreg,\shift+5,6                    # start green extraction dst[\shift:10..5]
+    ext     $t8,\src,3,5                            # src[7..3]
+    srl     $t2,8
+    addu    $t8,$t2
+.if \shift!=0
+    sll     $t8,\shift+11                           # dst[\shift:15..11]
+    or      \fb,$t8
+.else
+    sll     \fb,$t8,11
+.endif
+
+    /* green */
+    mul     $t8,$a4,$t3
+    ext     $a4,\dreg,\shift,5                      # start blue extraction dst[\shift:4..0]
+    ext     $t2,\src,2+8,6                          # src[15..10]
+    srl     $t8,8
+    addu    $t8,$t2
+
+    /* blue */
+    mul     $a4,$a4,$t3
+    sll     $t8, $t8, \shift+5                  # finish green insertion dst[\shift:10..5]
+    or      \fb, \fb, $t8
+    ext     $t2,\src,(3+8+8),5
+    srl     $t8,$a4,8
+    addu    $t8,$t2
+    sll     $t8, $t8, \shift
+    or      \fb, \fb, $t8
+    .endm
+
+    .text
+    .align
+
+    .global scanline_t32cb16blend_mips64
+    .ent    scanline_t32cb16blend_mips64
+scanline_t32cb16blend_mips64:
+    daddiu  $sp, $sp, -40
+DBG li      $v0,0xffffffff
+DBG li      $v1,0
+    /* Align the destination if necessary */
+    and     $a4,$a0,3
+    beqz    $a4,aligned
+
+    /* as long as there is at least one pixel */
+    beqz    $a2,done
+
+    lw      $t0,($a1)
+    daddu   $a0,2
+    daddu   $a1,4
+    beqz    $t0,1f
+    lhu     $a7,-2($a0)
+    pixel   $a7,$t0,$a5,0
+    sh      $a5,-2($a0)
+1:  subu    $a2,1
+
+aligned:
+    /* Check to see if its worth unrolling the loop */
+    subu    $a2,4
+    bltz    $a2,tail
+
+    /* Process 4 pixels at a time */
+fourpixels:
+    /* 1st pair of pixels */
+    lw      $t0,0($a1)
+    lw      $t1,4($a1)
+    daddu   $a0,8
+    daddu   $a1,16
+
+    /* both are zero, skip this pair */
+    or      $a7,$t0,$t1
+    beqz    $a7,1f
+
+    /* load the destination */
+    lw      $a7,-8($a0)
+
+    pixel   $a7,$t0,$a5,0
+    andi    $a5, 0xFFFF
+    pixel   $a7,$t1,$a5,16
+    sw      $a5,-8($a0)
+
+1:
+    /* 2nd pair of pixels */
+    lw      $t0,-8($a1)
+    lw      $t1,-4($a1)
+
+    /* both are zero, skip this pair */
+    or      $a7,$t0,$t1
+    beqz    $a7,1f
+
+    /* load the destination */
+    lw      $a7,-4($a0)
+
+    pixel   $a7,$t0,$a5,0
+    andi    $a5, 0xFFFF
+    pixel   $a7,$t1,$a5,16
+    sw      $a5,-4($a0)
+
+1:  subu    $a2,4
+    bgtz    $a2,fourpixels
+
+tail:
+    /* the pixel count underran, restore it now */
+    addu    $a2,4
+
+    /* handle the last 0..3 pixels */
+    beqz    $a2,done
+onepixel:
+    lw      $t0,($a1)
+    daddu   $a0,2
+    daddu   $a1,4
+    beqz    $t0,1f
+    lhu     $a7,-2($a0)
+    pixel   $a7,$t0,$a5,0
+    sh      $a5,-2($a0)
+1:  subu    $a2,1
+    bnez    $a2,onepixel
+done:
+DBG .set    push
+DBG .set    mips32r2
+DBG rdhwr   $a0,$3
+DBG mul     $v0,$a0
+DBG mul     $v1,$a0
+DBG .set    pop
+    daddiu  $sp, $sp, 40
+    j       $ra
+    .end    scanline_t32cb16blend_mips64
index 40cbfcf..72935ac 100644 (file)
@@ -63,7 +63,7 @@ public:
     };
 
     enum {
-        CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64
+        CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64, CODEGEN_ARCH_MIPS64
     };
 
     // -----------------------------------------------------------------------
@@ -115,7 +115,8 @@ public:
     // data processing...
     enum {
         opAND, opEOR, opSUB, opRSB, opADD, opADC, opSBC, opRSC, 
-        opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN
+        opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN,
+        opADD64, opSUB64
     };
 
     virtual void
index 325caba..346779f 100644 (file)
@@ -893,7 +893,8 @@ void GGLAssembler::build_and_immediate(int d, int s, uint32_t mask, int bits)
         return;
     }
     
-    if (getCodegenArch() == CODEGEN_ARCH_MIPS) {
+    if ((getCodegenArch() == CODEGEN_ARCH_MIPS) ||
+        (getCodegenArch() == CODEGEN_ARCH_MIPS64)) {
         // MIPS can do 16-bit imm in 1 instr, 32-bit in 3 instr
         // the below ' while (mask)' code is buggy on mips
         // since mips returns true on isValidImmediate()
@@ -1057,7 +1058,8 @@ RegisterAllocator::RegisterFile& RegisterAllocator::registerFile()
 RegisterAllocator::RegisterFile::RegisterFile(int codegen_arch)
     : mRegs(0), mTouched(0), mStatus(0), mArch(codegen_arch), mRegisterOffset(0)
 {
-    if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) {
+    if ((mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) ||
+        (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS64)) {
         mRegisterOffset = 2;    // ARM has regs 0..15, MIPS offset to 2..17
     }
     reserve(ARMAssemblerInterface::SP);
@@ -1067,7 +1069,8 @@ RegisterAllocator::RegisterFile::RegisterFile(int codegen_arch)
 RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs, int codegen_arch)
     : mRegs(rhs.mRegs), mTouched(rhs.mTouched), mArch(codegen_arch), mRegisterOffset(0)
 {
-    if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) {
+    if ((mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) ||
+        (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS64)) {
         mRegisterOffset = 2;    // ARM has regs 0..15, MIPS offset to 2..17
     }
 }
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.cpp b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
new file mode 100644 (file)
index 0000000..a5305cc
--- /dev/null
@@ -0,0 +1,1452 @@
+/* libs/pixelflinger/codeflinger/MIPS64Assembler.cpp
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+/* MIPS64 assembler and ARM->MIPS64 assembly translator
+**
+** The approach is utilize MIPSAssembler generator, using inherited MIPS64Assembler
+** that overrides just the specific MIPS64r6 instructions.
+** For now ArmToMips64Assembler is copied over from ArmToMipsAssembler class,
+** changing some MIPS64r6 related stuff.
+**
+*/
+
+
+#define LOG_TAG "MIPS64Assembler"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#if defined(WITH_LIB_HARDWARE)
+#include <hardware_legacy/qemu_tracing.h>
+#endif
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include "MIPS64Assembler.h"
+#include "CodeCache.h"
+#include "mips64_disassem.h"
+
+
+#define NOT_IMPLEMENTED()  LOG_ALWAYS_FATAL("Arm instruction %s not yet implemented\n", __func__)
+
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark ArmToMips64Assembler...
+#endif
+
+ArmToMips64Assembler::ArmToMips64Assembler(const sp<Assembly>& assembly,
+                                           char *abuf, int linesz, int instr_count)
+    :   ARMAssemblerInterface(),
+        mArmDisassemblyBuffer(abuf),
+        mArmLineLength(linesz),
+        mArmInstrCount(instr_count),
+        mInum(0),
+        mAssembly(assembly)
+{
+    mMips = new MIPS64Assembler(assembly, this);
+    mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *));
+    init_conditional_labels();
+}
+
+ArmToMips64Assembler::ArmToMips64Assembler(void* assembly)
+    :   ARMAssemblerInterface(),
+        mArmDisassemblyBuffer(NULL),
+        mInum(0),
+        mAssembly(NULL)
+{
+    mMips = new MIPS64Assembler(assembly, this);
+    mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *));
+    init_conditional_labels();
+}
+
+ArmToMips64Assembler::~ArmToMips64Assembler()
+{
+    delete mMips;
+    free((void *) mArmPC);
+}
+
+uint32_t* ArmToMips64Assembler::pc() const
+{
+    return mMips->pc();
+}
+
+uint32_t* ArmToMips64Assembler::base() const
+{
+    return mMips->base();
+}
+
+void ArmToMips64Assembler::reset()
+{
+    cond.labelnum = 0;
+    mInum = 0;
+    mMips->reset();
+}
+
+int ArmToMips64Assembler::getCodegenArch()
+{
+    return CODEGEN_ARCH_MIPS64;
+}
+
+void ArmToMips64Assembler::comment(const char* string)
+{
+    mMips->comment(string);
+}
+
+void ArmToMips64Assembler::label(const char* theLabel)
+{
+    mMips->label(theLabel);
+}
+
+void ArmToMips64Assembler::disassemble(const char* name)
+{
+    mMips->disassemble(name);
+}
+
+void ArmToMips64Assembler::init_conditional_labels()
+{
+    int i;
+    for (i=0;i<99; ++i) {
+        sprintf(cond.label[i], "cond_%d", i);
+    }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Prolog/Epilog & Generate...
+#endif
+
+void ArmToMips64Assembler::prolog()
+{
+    mArmPC[mInum++] = pc();  // save starting PC for this instr
+
+    mMips->DADDIU(R_sp, R_sp, -(5 * 8));
+    mMips->SD(R_s0, R_sp, 0);
+    mMips->SD(R_s1, R_sp, 8);
+    mMips->SD(R_s2, R_sp, 16);
+    mMips->SD(R_s3, R_sp, 24);
+    mMips->SD(R_s4, R_sp, 32);
+    mMips->MOVE(R_v0, R_a0);    // move context * passed in a0 to v0 (arm r0)
+}
+
+void ArmToMips64Assembler::epilog(uint32_t touched)
+{
+    mArmPC[mInum++] = pc();  // save starting PC for this instr
+
+    mMips->LD(R_s0, R_sp, 0);
+    mMips->LD(R_s1, R_sp, 8);
+    mMips->LD(R_s2, R_sp, 16);
+    mMips->LD(R_s3, R_sp, 24);
+    mMips->LD(R_s4, R_sp, 32);
+    mMips->DADDIU(R_sp, R_sp, (5 * 8));
+    mMips->JR(R_ra);
+
+}
+
+int ArmToMips64Assembler::generate(const char* name)
+{
+    return mMips->generate(name);
+}
+
+void ArmToMips64Assembler::fix_branches()
+{
+    mMips->fix_branches();
+}
+
+uint32_t* ArmToMips64Assembler::pcForLabel(const char* label)
+{
+    return mMips->pcForLabel(label);
+}
+
+void ArmToMips64Assembler::set_condition(int mode, int R1, int R2) {
+    if (mode == 2) {
+        cond.type = SBIT_COND;
+    } else {
+        cond.type = CMP_COND;
+    }
+    cond.r1 = R1;
+    cond.r2 = R2;
+}
+
+//----------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Addressing modes & shifters...
+#endif
+
+
+// do not need this for MIPS, but it is in the Interface (virtual)
+int ArmToMips64Assembler::buildImmediate(
+        uint32_t immediate, uint32_t& rot, uint32_t& imm)
+{
+    // for MIPS, any 32-bit immediate is OK
+    rot = 0;
+    imm = immediate;
+    return 0;
+}
+
+// shifters...
+
+bool ArmToMips64Assembler::isValidImmediate(uint32_t immediate)
+{
+    // for MIPS, any 32-bit immediate is OK
+    return true;
+}
+
+uint32_t ArmToMips64Assembler::imm(uint32_t immediate)
+{
+    amode.value = immediate;
+    return AMODE_IMM;
+}
+
+uint32_t ArmToMips64Assembler::reg_imm(int Rm, int type, uint32_t shift)
+{
+    amode.reg = Rm;
+    amode.stype = type;
+    amode.value = shift;
+    return AMODE_REG_IMM;
+}
+
+uint32_t ArmToMips64Assembler::reg_rrx(int Rm)
+{
+    // reg_rrx mode is not used in the GLLAssember code at this time
+    return AMODE_UNSUPPORTED;
+}
+
+uint32_t ArmToMips64Assembler::reg_reg(int Rm, int type, int Rs)
+{
+    // reg_reg mode is not used in the GLLAssember code at this time
+    return AMODE_UNSUPPORTED;
+}
+
+
+// addressing modes...
+// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0)
+uint32_t ArmToMips64Assembler::immed12_pre(int32_t immed12, int W)
+{
+    LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+                        "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+                        immed12);
+    amode.value = immed12;
+    amode.writeback = W;
+    return AMODE_IMM_12_PRE;
+}
+
+uint32_t ArmToMips64Assembler::immed12_post(int32_t immed12)
+{
+    LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+                        "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+                        immed12);
+
+    amode.value = immed12;
+    return AMODE_IMM_12_POST;
+}
+
+uint32_t ArmToMips64Assembler::reg_scale_pre(int Rm, int type,
+        uint32_t shift, int W)
+{
+    LOG_ALWAYS_FATAL_IF(W | type | shift, "reg_scale_pre adv modes not yet implemented");
+
+    amode.reg = Rm;
+    // amode.stype = type;      // more advanced modes not used in GGLAssembler yet
+    // amode.value = shift;
+    // amode.writeback = W;
+    return AMODE_REG_SCALE_PRE;
+}
+
+uint32_t ArmToMips64Assembler::reg_scale_post(int Rm, int type, uint32_t shift)
+{
+    LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n");
+    return AMODE_UNSUPPORTED;
+}
+
+// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0)
+uint32_t ArmToMips64Assembler::immed8_pre(int32_t immed8, int W)
+{
+    LOG_ALWAYS_FATAL("adr mode immed8_pre not yet implemented\n");
+
+    LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+                        "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+                        immed8);
+    return AMODE_IMM_8_PRE;
+}
+
+uint32_t ArmToMips64Assembler::immed8_post(int32_t immed8)
+{
+    LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+                        "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+                        immed8);
+    amode.value = immed8;
+    return AMODE_IMM_8_POST;
+}
+
+uint32_t ArmToMips64Assembler::reg_pre(int Rm, int W)
+{
+    LOG_ALWAYS_FATAL_IF(W, "reg_pre writeback not yet implemented");
+    amode.reg = Rm;
+    return AMODE_REG_PRE;
+}
+
+uint32_t ArmToMips64Assembler::reg_post(int Rm)
+{
+    LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n");
+    return AMODE_UNSUPPORTED;
+}
+
+
+
+// ----------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Data Processing...
+#endif
+
+
+static const char * const dpOpNames[] = {
+    "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC",
+    "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN"
+};
+
+// check if the operand registers from a previous CMP or S-bit instruction
+// would be overwritten by this instruction. If so, move the value to a
+// safe register.
+// Note that we cannot tell at _this_ instruction time if a future (conditional)
+// instruction will _also_ use this value (a defect of the simple 1-pass, one-
+// instruction-at-a-time translation). Therefore we must be conservative and
+// save the value before it is overwritten. This costs an extra MOVE instr.
+
+void ArmToMips64Assembler::protectConditionalOperands(int Rd)
+{
+    if (Rd == cond.r1) {
+        mMips->MOVE(R_cmp, cond.r1);
+        cond.r1 = R_cmp;
+    }
+    if (cond.type == CMP_COND && Rd == cond.r2) {
+        mMips->MOVE(R_cmp2, cond.r2);
+        cond.r2 = R_cmp2;
+    }
+}
+
+
+// interprets the addressing mode, and generates the common code
+// used by the majority of data-processing ops. Many MIPS instructions
+// have a register-based form and a different immediate form. See
+// opAND below for an example. (this could be inlined)
+//
+// this works with the imm(), reg_imm() methods above, which are directly
+// called by the GLLAssembler.
+// note: _signed parameter defaults to false (un-signed)
+// note: tmpReg parameter defaults to 1, MIPS register AT
+int ArmToMips64Assembler::dataProcAdrModes(int op, int& source, bool _signed, int tmpReg)
+{
+    if (op < AMODE_REG) {
+        source = op;
+        return SRC_REG;
+    } else if (op == AMODE_IMM) {
+        if ((!_signed && amode.value > 0xffff)
+                || (_signed && ((int)amode.value < -32768 || (int)amode.value > 32767) )) {
+            mMips->LUI(tmpReg, (amode.value >> 16));
+            if (amode.value & 0x0000ffff) {
+                mMips->ORI(tmpReg, tmpReg, (amode.value & 0x0000ffff));
+            }
+            source = tmpReg;
+            return SRC_REG;
+        } else {
+            source = amode.value;
+            return SRC_IMM;
+        }
+    } else if (op == AMODE_REG_IMM) {
+        switch (amode.stype) {
+            case LSL: mMips->SLL(tmpReg, amode.reg, amode.value); break;
+            case LSR: mMips->SRL(tmpReg, amode.reg, amode.value); break;
+            case ASR: mMips->SRA(tmpReg, amode.reg, amode.value); break;
+            case ROR: mMips->ROTR(tmpReg, amode.reg, amode.value); break;
+        }
+        source = tmpReg;
+        return SRC_REG;
+    } else {  // adr mode RRX is not used in GGL Assembler at this time
+        // we are screwed, this should be exception, assert-fail or something
+        LOG_ALWAYS_FATAL("adr mode reg_rrx not yet implemented\n");
+        return SRC_ERROR;
+    }
+}
+
+
+void ArmToMips64Assembler::dataProcessing(int opcode, int cc,
+        int s, int Rd, int Rn, uint32_t Op2)
+{
+    int src;    // src is modified by dataProcAdrModes() - passed as int&
+
+    if (cc != AL) {
+        protectConditionalOperands(Rd);
+        // the branch tests register(s) set by prev CMP or instr with 'S' bit set
+        // inverse the condition to jump past this conditional instruction
+        ArmToMips64Assembler::B(cc^1, cond.label[++cond.labelnum]);
+    } else {
+        mArmPC[mInum++] = pc();  // save starting PC for this instr
+    }
+
+    switch (opcode) {
+    case opAND:
+        if (dataProcAdrModes(Op2, src) == SRC_REG) {
+            mMips->AND(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->ANDI(Rd, Rn, src);
+        }
+        break;
+
+    case opADD:
+        // set "signed" to true for adr modes
+        if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+            mMips->ADDU(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->ADDIU(Rd, Rn, src);
+        }
+        break;
+
+    case opSUB:
+        // set "signed" to true for adr modes
+        if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+            mMips->SUBU(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->SUBIU(Rd, Rn, src);
+        }
+        break;
+
+    case opADD64:
+        // set "signed" to true for adr modes
+        if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+            mMips->DADDU(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->DADDIU(Rd, Rn, src);
+        }
+        break;
+
+    case opSUB64:
+        // set "signed" to true for adr modes
+        if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+            mMips->DSUBU(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->DSUBIU(Rd, Rn, src);
+        }
+        break;
+
+    case opEOR:
+        if (dataProcAdrModes(Op2, src) == SRC_REG) {
+            mMips->XOR(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->XORI(Rd, Rn, src);
+        }
+        break;
+
+    case opORR:
+        if (dataProcAdrModes(Op2, src) == SRC_REG) {
+            mMips->OR(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->ORI(Rd, Rn, src);
+        }
+        break;
+
+    case opBIC:
+        if (dataProcAdrModes(Op2, src) == SRC_IMM) {
+            // if we are 16-bit imnmediate, load to AT reg
+            mMips->ORI(R_at, 0, src);
+            src = R_at;
+        }
+        mMips->NOT(R_at, src);
+        mMips->AND(Rd, Rn, R_at);
+        break;
+
+    case opRSB:
+        if (dataProcAdrModes(Op2, src) == SRC_IMM) {
+            // if we are 16-bit imnmediate, load to AT reg
+            mMips->ORI(R_at, 0, src);
+            src = R_at;
+        }
+        mMips->SUBU(Rd, src, Rn);   // subu with the parameters reversed
+        break;
+
+    case opMOV:
+        if (Op2 < AMODE_REG) {  // op2 is reg # in this case
+            mMips->MOVE(Rd, Op2);
+        } else if (Op2 == AMODE_IMM) {
+            if (amode.value > 0xffff) {
+                mMips->LUI(Rd, (amode.value >> 16));
+                if (amode.value & 0x0000ffff) {
+                    mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff));
+                }
+             } else {
+                mMips->ORI(Rd, 0, amode.value);
+            }
+        } else if (Op2 == AMODE_REG_IMM) {
+            switch (amode.stype) {
+            case LSL: mMips->SLL(Rd, amode.reg, amode.value); break;
+            case LSR: mMips->SRL(Rd, amode.reg, amode.value); break;
+            case ASR: mMips->SRA(Rd, amode.reg, amode.value); break;
+            case ROR: mMips->ROTR(Rd, amode.reg, amode.value); break;
+            }
+        }
+        else {
+            // adr mode RRX is not used in GGL Assembler at this time
+            mMips->UNIMPL();
+        }
+        break;
+
+    case opMVN:     // this is a 1's complement: NOT
+        if (Op2 < AMODE_REG) {  // op2 is reg # in this case
+            mMips->NOR(Rd, Op2, 0);     // NOT is NOR with 0
+            break;
+        } else if (Op2 == AMODE_IMM) {
+            if (amode.value > 0xffff) {
+                mMips->LUI(Rd, (amode.value >> 16));
+                if (amode.value & 0x0000ffff) {
+                    mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff));
+                }
+             } else {
+                mMips->ORI(Rd, 0, amode.value);
+             }
+        } else if (Op2 == AMODE_REG_IMM) {
+            switch (amode.stype) {
+            case LSL: mMips->SLL(Rd, amode.reg, amode.value); break;
+            case LSR: mMips->SRL(Rd, amode.reg, amode.value); break;
+            case ASR: mMips->SRA(Rd, amode.reg, amode.value); break;
+            case ROR: mMips->ROTR(Rd, amode.reg, amode.value); break;
+            }
+        }
+        else {
+            // adr mode RRX is not used in GGL Assembler at this time
+            mMips->UNIMPL();
+        }
+        mMips->NOR(Rd, Rd, 0);     // NOT is NOR with 0
+        break;
+
+    case opCMP:
+        // Either operand of a CMP instr could get overwritten by a subsequent
+        // conditional instruction, which is ok, _UNLESS_ there is a _second_
+        // conditional instruction. Under MIPS, this requires doing the comparison
+        // again (SLT), and the original operands must be available. (and this
+        // pattern of multiple conditional instructions from same CMP _is_ used
+        // in GGL-Assembler)
+        //
+        // For now, if a conditional instr overwrites the operands, we will
+        // move them to dedicated temp regs. This is ugly, and inefficient,
+        // and should be optimized.
+        //
+        // WARNING: making an _Assumption_ that CMP operand regs will NOT be
+        // trashed by intervening NON-conditional instructions. In the general
+        // case this is legal, but it is NOT currently done in GGL-Assembler.
+
+        cond.type = CMP_COND;
+        cond.r1 = Rn;
+        if (dataProcAdrModes(Op2, src, false, R_cmp2) == SRC_REG) {
+            cond.r2 = src;
+        } else {                        // adr mode was SRC_IMM
+            mMips->ORI(R_cmp2, R_zero, src);
+            cond.r2 = R_cmp2;
+        }
+
+        break;
+
+
+    case opTST:
+    case opTEQ:
+    case opCMN:
+    case opADC:
+    case opSBC:
+    case opRSC:
+        mMips->UNIMPL(); // currently unused in GGL Assembler code
+        break;
+    }
+
+    if (cc != AL) {
+        mMips->label(cond.label[cond.labelnum]);
+    }
+    if (s && opcode != opCMP) {
+        cond.type = SBIT_COND;
+        cond.r1 = Rd;
+    }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Multiply...
+#endif
+
+// multiply, accumulate
+void ArmToMips64Assembler::MLA(int cc, int s,
+        int Rd, int Rm, int Rs, int Rn) {
+
+    //ALOGW("MLA");
+    mArmPC[mInum++] = pc();  // save starting PC for this instr
+
+    mMips->MUL(R_at, Rm, Rs);
+    mMips->ADDU(Rd, R_at, Rn);
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = Rd;
+    }
+}
+
+void ArmToMips64Assembler::MUL(int cc, int s,
+        int Rd, int Rm, int Rs) {
+    mArmPC[mInum++] = pc();
+    mMips->MUL(Rd, Rm, Rs);
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = Rd;
+    }
+}
+
+void ArmToMips64Assembler::UMULL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    mArmPC[mInum++] = pc();
+    mMips->MUH(RdHi, Rm, Rs);
+    mMips->MUL(RdLo, Rm, Rs);
+
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = RdHi;     // BUG...
+        LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n");
+    }
+}
+
+void ArmToMips64Assembler::UMUAL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+                        "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+    // *mPC++ =    (cc<<28) | (1<<23) | (1<<21) | (s<<20) |
+    //             (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = RdHi;     // BUG...
+        LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n");
+    }
+}
+
+void ArmToMips64Assembler::SMULL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+                        "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+    // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (s<<20) |
+    //             (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = RdHi;     // BUG...
+        LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n");
+    }
+}
+void ArmToMips64Assembler::SMUAL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+                        "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+    // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) |
+    //             (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = RdHi;     // BUG...
+        LOG_ALWAYS_FATAL("Condition on SMUAL must be on 64-bit result\n");
+    }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Branches...
+#endif
+
+// branches...
+
+void ArmToMips64Assembler::B(int cc, const char* label)
+{
+    mArmPC[mInum++] = pc();
+    if (cond.type == SBIT_COND) { cond.r2 = R_zero; }
+
+    switch(cc) {
+        case EQ: mMips->BEQ(cond.r1, cond.r2, label); break;
+        case NE: mMips->BNE(cond.r1, cond.r2, label); break;
+        case HS: mMips->BGEU(cond.r1, cond.r2, label); break;
+        case LO: mMips->BLTU(cond.r1, cond.r2, label); break;
+        case MI: mMips->BLT(cond.r1, cond.r2, label); break;
+        case PL: mMips->BGE(cond.r1, cond.r2, label); break;
+
+        case HI: mMips->BGTU(cond.r1, cond.r2, label); break;
+        case LS: mMips->BLEU(cond.r1, cond.r2, label); break;
+        case GE: mMips->BGE(cond.r1, cond.r2, label); break;
+        case LT: mMips->BLT(cond.r1, cond.r2, label); break;
+        case GT: mMips->BGT(cond.r1, cond.r2, label); break;
+        case LE: mMips->BLE(cond.r1, cond.r2, label); break;
+        case AL: mMips->B(label); break;
+        case NV: /* B Never - no instruction */ break;
+
+        case VS:
+        case VC:
+        default:
+            LOG_ALWAYS_FATAL("Unsupported cc: %02x\n", cc);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::BL(int cc, const char* label)
+{
+    LOG_ALWAYS_FATAL("branch-and-link not supported yet\n");
+    mArmPC[mInum++] = pc();
+}
+
+// no use for Branches with integer PC, but they're in the Interface class ....
+void ArmToMips64Assembler::B(int cc, uint32_t* to_pc)
+{
+    LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+    mArmPC[mInum++] = pc();
+}
+
+void ArmToMips64Assembler::BL(int cc, uint32_t* to_pc)
+{
+    LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+    mArmPC[mInum++] = pc();
+}
+
+void ArmToMips64Assembler::BX(int cc, int Rn)
+{
+    LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+    mArmPC[mInum++] = pc();
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Data Transfer...
+#endif
+
+// data transfer...
+void ArmToMips64Assembler::LDR(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;      // convert LDR via Arm SP to LW via Mips SP
+            }
+            mMips->LW(Rd, Rn, amode.value);
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                mMips->DADDIU(Rn, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;      // convert STR thru Arm SP to STR thru Mips SP
+            }
+            mMips->LW(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->LW(Rd, R_at, 0);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::LDRB(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            mMips->LBU(Rd, Rn, amode.value);
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                mMips->DADDIU(Rn, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            mMips->LBU(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->LBU(Rd, R_at, 0);
+            break;
+    }
+
+}
+
+void ArmToMips64Assembler::STR(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;  // convert STR thru Arm SP to SW thru Mips SP
+            }
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                // If we will writeback, then update the index reg, then store.
+                // This correctly handles stack-push case.
+                mMips->DADDIU(Rn, Rn, amode.value);
+                mMips->SW(Rd, Rn, 0);
+            } else {
+                // No writeback so store offset by value
+                mMips->SW(Rd, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            mMips->SW(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);  // post index always writes back
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->SW(Rd, R_at, 0);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::STRB(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            mMips->SB(Rd, Rn, amode.value);
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                mMips->DADDIU(Rn, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            mMips->SB(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->SB(Rd, R_at, 0);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::LDRH(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed8_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_8_PRE:      // no support yet for writeback
+            mMips->LHU(Rd, Rn, amode.value);
+            break;
+        case AMODE_IMM_8_POST:
+            mMips->LHU(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_PRE:
+            // we only support simple base +/- index
+            if (amode.reg >= 0) {
+                mMips->DADDU(R_at, Rn, amode.reg);
+            } else {
+                mMips->DSUBU(R_at, Rn, abs(amode.reg));
+            }
+            mMips->LHU(Rd, R_at, 0);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::STRH(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed8_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_8_PRE:      // no support yet for writeback
+            mMips->SH(Rd, Rn, amode.value);
+            break;
+        case AMODE_IMM_8_POST:
+            mMips->SH(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_PRE:
+            // we only support simple base +/- index
+            if (amode.reg >= 0) {
+                mMips->DADDU(R_at, Rn, amode.reg);
+            } else {
+                mMips->DSUBU(R_at, Rn, abs(amode.reg));
+            }
+            mMips->SH(Rd, R_at, 0);
+            break;
+    }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Block Data Transfer...
+#endif
+
+// block data transfer...
+void ArmToMips64Assembler::LDM(int cc, int dir,
+        int Rn, int W, uint32_t reg_list)
+{   //                        ED FD EA FA      IB IA DB DA
+    // const uint8_t P[8] = { 1, 0, 1, 0,      1, 0, 1, 0 };
+    // const uint8_t U[8] = { 1, 1, 0, 0,      1, 1, 0, 0 };
+    // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+    //         (uint32_t(U[dir])<<23) | (1<<20) | (W<<21) | (Rn<<16) | reg_list;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::STM(int cc, int dir,
+        int Rn, int W, uint32_t reg_list)
+{   //                        FA EA FD ED      IB IA DB DA
+    // const uint8_t P[8] = { 0, 1, 0, 1,      1, 0, 1, 0 };
+    // const uint8_t U[8] = { 0, 0, 1, 1,      1, 1, 0, 0 };
+    // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+    //         (uint32_t(U[dir])<<23) | (0<<20) | (W<<21) | (Rn<<16) | reg_list;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Special...
+#endif
+
+// special...
+void ArmToMips64Assembler::SWP(int cc, int Rn, int Rd, int Rm) {
+    // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SWPB(int cc, int Rn, int Rd, int Rm) {
+    // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SWI(int cc, uint32_t comment) {
+    // *mPC++ = (cc<<28) | (0xF<<24) | comment;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+
+#if 0
+#pragma mark -
+#pragma mark DSP instructions...
+#endif
+
+// DSP instructions...
+void ArmToMips64Assembler::PLD(int Rn, uint32_t offset) {
+    LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))),
+                        "PLD only P=1, W=0");
+    // *mPC++ = 0xF550F000 | (Rn<<16) | offset;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::CLZ(int cc, int Rd, int Rm)
+{
+    mArmPC[mInum++] = pc();
+    mMips->CLZ(Rd, Rm);
+}
+
+void ArmToMips64Assembler::QADD(int cc,  int Rd, int Rm, int Rn)
+{
+    // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QDADD(int cc,  int Rd, int Rm, int Rn)
+{
+    // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QSUB(int cc,  int Rd, int Rm, int Rn)
+{
+    // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QDSUB(int cc,  int Rd, int Rm, int Rn)
+{
+    // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+// 16 x 16 signed multiply (like SMLAxx without the accumulate)
+void ArmToMips64Assembler::SMUL(int cc, int xy,
+                int Rd, int Rm, int Rs)
+{
+    mArmPC[mInum++] = pc();
+
+    // the 16 bits may be in the top or bottom half of 32-bit source reg,
+    // as defined by the codes BB, BT, TB, TT (compressed param xy)
+    // where x corresponds to Rm and y to Rs
+
+    // select half-reg for Rm
+    if (xy & xyTB) {
+        // use top 16-bits
+        mMips->SRA(R_at, Rm, 16);
+    } else {
+        // use bottom 16, but sign-extend to 32
+        mMips->SEH(R_at, Rm);
+    }
+    // select half-reg for Rs
+    if (xy & xyBT) {
+        // use top 16-bits
+        mMips->SRA(R_at2, Rs, 16);
+    } else {
+        // use bottom 16, but sign-extend to 32
+        mMips->SEH(R_at2, Rs);
+    }
+    mMips->MUL(Rd, R_at, R_at2);
+}
+
+// signed 32b x 16b multiple, save top 32-bits of 48-bit result
+void ArmToMips64Assembler::SMULW(int cc, int y,
+                int Rd, int Rm, int Rs)
+{
+    mArmPC[mInum++] = pc();
+
+    // the selector yT or yB refers to reg Rs
+    if (y & yT) {
+        // zero the bottom 16-bits, with 2 shifts, it can affect result
+        mMips->SRL(R_at, Rs, 16);
+        mMips->SLL(R_at, R_at, 16);
+
+    } else {
+        // move low 16-bit half, to high half
+        mMips->SLL(R_at, Rs, 16);
+    }
+    mMips->MUH(Rd, Rm, R_at);
+}
+
+// 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn
+void ArmToMips64Assembler::SMLA(int cc, int xy,
+                int Rd, int Rm, int Rs, int Rn)
+{
+    mArmPC[mInum++] = pc();
+
+    // the 16 bits may be in the top or bottom half of 32-bit source reg,
+    // as defined by the codes BB, BT, TB, TT (compressed param xy)
+    // where x corresponds to Rm and y to Rs
+
+    // select half-reg for Rm
+    if (xy & xyTB) {
+        // use top 16-bits
+        mMips->SRA(R_at, Rm, 16);
+    } else {
+        // use bottom 16, but sign-extend to 32
+        mMips->SEH(R_at, Rm);
+    }
+    // select half-reg for Rs
+    if (xy & xyBT) {
+        // use top 16-bits
+        mMips->SRA(R_at2, Rs, 16);
+    } else {
+        // use bottom 16, but sign-extend to 32
+        mMips->SEH(R_at2, Rs);
+    }
+
+    mMips->MUL(R_at, R_at, R_at2);
+    mMips->ADDU(Rd, R_at, Rn);
+}
+
+void ArmToMips64Assembler::SMLAL(int cc, int xy,
+                int RdHi, int RdLo, int Rs, int Rm)
+{
+    // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SMLAW(int cc, int y,
+                int Rd, int Rm, int Rs, int Rn)
+{
+    // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+// used by ARMv6 version of GGLAssembler::filter32
+void ArmToMips64Assembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+{
+    mArmPC[mInum++] = pc();
+
+    //Rd[31:16] := ZeroExtend((Rm ROR (8 * sh))[23:16]),
+    //Rd[15:0] := ZeroExtend((Rm ROR (8 * sh))[7:0]). sh 0-3.
+
+    mMips->ROTR(R_at2, Rm, rotate * 8);
+    mMips->LUI(R_at, 0xFF);
+    mMips->ORI(R_at, R_at, 0xFF);
+    mMips->AND(Rd, R_at2, R_at);
+}
+
+void ArmToMips64Assembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+{
+     /* Placeholder for UBFX */
+     mArmPC[mInum++] = pc();
+
+     mMips->NOP2();
+     NOT_IMPLEMENTED();
+}
+
+// ----------------------------------------------------------------------------
+// Address Processing...
+// ----------------------------------------------------------------------------
+
+void ArmToMips64Assembler::ADDR_ADD(int cc,
+        int s, int Rd, int Rn, uint32_t Op2)
+{
+//    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+//    if(s  != 0) { NOT_IMPLEMENTED(); return;} //Not required
+    dataProcessing(opADD64, cc, s, Rd, Rn, Op2);
+}
+
+void ArmToMips64Assembler::ADDR_SUB(int cc,
+        int s, int Rd, int Rn, uint32_t Op2)
+{
+//    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+//    if(s  != 0) { NOT_IMPLEMENTED(); return;} //Not required
+    dataProcessing(opSUB64, cc, s, Rd, Rn, Op2);
+}
+
+void ArmToMips64Assembler::ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset) {
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;      // convert LDR via Arm SP to LW via Mips SP
+            }
+            mMips->LD(Rd, Rn, amode.value);
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                mMips->DADDIU(Rn, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;      // convert STR thru Arm SP to STR thru Mips SP
+            }
+            mMips->LD(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->LD(Rd, R_at, 0);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::ADDR_STR(int cc, int Rd, int Rn, uint32_t offset) {
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;  // convert STR thru Arm SP to SW thru Mips SP
+            }
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                // If we will writeback, then update the index reg, then store.
+                // This correctly handles stack-push case.
+                mMips->DADDIU(Rn, Rn, amode.value);
+                mMips->SD(Rd, Rn, 0);
+            } else {
+                // No writeback so store offset by value
+                mMips->SD(Rd, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            mMips->SD(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);  // post index always writes back
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->SD(Rd, R_at, 0);
+            break;
+    }
+}
+
+#if 0
+#pragma mark -
+#pragma mark MIPS Assembler...
+#endif
+
+
+//**************************************************************************
+//**************************************************************************
+//**************************************************************************
+
+
+/* MIPS64 assembler
+** this is a subset of mips64r6, targeted specifically at ARM instruction
+** replacement in the pixelflinger/codeflinger code.
+**
+** This class is extended from MIPSAssembler class and overrides only
+** MIPS64r6 specific stuff.
+*/
+
+MIPS64Assembler::MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent)
+    : mParent(parent),
+    MIPSAssembler::MIPSAssembler(assembly, NULL)
+{
+}
+
+MIPS64Assembler::MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent)
+    : mParent(parent),
+    MIPSAssembler::MIPSAssembler(NULL, NULL)
+{
+    mBase = mPC = (uint32_t *)assembly;
+}
+
+MIPS64Assembler::~MIPS64Assembler()
+{
+}
+
+void MIPS64Assembler::reset()
+{
+    if (mAssembly != NULL) {
+        mBase = mPC = (uint32_t *)mAssembly->base();
+    } else {
+        mPC = mBase = base();
+    }
+    mBranchTargets.clear();
+    mLabels.clear();
+    mLabelsInverseMapping.clear();
+    mComments.clear();
+}
+
+
+void MIPS64Assembler::disassemble(const char* name)
+{
+    char di_buf[140];
+
+    bool arm_disasm_fmt = (mParent->mArmDisassemblyBuffer == NULL) ? false : true;
+
+    typedef char dstr[40];
+    dstr *lines = (dstr *)mParent->mArmDisassemblyBuffer;
+
+    if (mParent->mArmDisassemblyBuffer != NULL) {
+        for (int i=0; i<mParent->mArmInstrCount; ++i) {
+            string_detab(lines[i]);
+        }
+    }
+
+    // iArm is an index to Arm instructions 1...n for this assembly sequence
+    // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS
+    // instruction corresponding to that Arm instruction number
+
+    int iArm = 0;
+    size_t count = pc()-base();
+    uint32_t* mipsPC = base();
+
+    while (count--) {
+        ssize_t label = mLabelsInverseMapping.indexOfKey(mipsPC);
+        if (label >= 0) {
+            ALOGW("%s:\n", mLabelsInverseMapping.valueAt(label));
+        }
+        ssize_t comment = mComments.indexOfKey(mipsPC);
+        if (comment >= 0) {
+            ALOGW("; %s\n", mComments.valueAt(comment));
+        }
+        ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt);
+        string_detab(di_buf);
+        string_pad(di_buf, 30);
+        ALOGW("%08lx:    %08x    %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf);
+        mipsPC++;
+    }
+}
+
+void MIPS64Assembler::fix_branches()
+{
+    // fixup all the branches
+    size_t count = mBranchTargets.size();
+    while (count--) {
+        const branch_target_t& bt = mBranchTargets[count];
+        uint32_t* target_pc = mLabels.valueFor(bt.label);
+        LOG_ALWAYS_FATAL_IF(!target_pc,
+                "error resolving branch targets, target_pc is null");
+        int32_t offset = int32_t(target_pc - (bt.pc+1));
+        *bt.pc |= offset & 0x00FFFF;
+    }
+}
+
+void MIPS64Assembler::DADDU(int Rd, int Rs, int Rt)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (daddu_fn<<FUNC_SHF)
+                    | (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF);
+}
+
+void MIPS64Assembler::DADDIU(int Rt, int Rs, int16_t imm)
+{
+    *mPC++ = (daddiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16);
+}
+
+void MIPS64Assembler::DSUBU(int Rd, int Rs, int Rt)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (dsubu_fn<<FUNC_SHF) |
+                        (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::DSUBIU(int Rt, int Rs, int16_t imm)   // really addiu(d, s, -j)
+{
+    *mPC++ = (daddiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | ((-imm) & MSK_16);
+}
+
+void MIPS64Assembler::MUL(int Rd, int Rs, int Rt)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (mul_fn<<RE_SHF) | (sop30_fn<<FUNC_SHF) |
+                        (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::MUH(int Rd, int Rs, int Rt)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (muh_fn<<RE_SHF) | (sop30_fn<<FUNC_SHF) |
+                        (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::CLO(int Rd, int Rs)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (17<<FUNC_SHF) |
+                        (Rd<<RD_SHF) | (Rs<<RS_SHF) | (1<<RE_SHF);
+}
+
+void MIPS64Assembler::CLZ(int Rd, int Rs)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (16<<FUNC_SHF) |
+                        (Rd<<RD_SHF) | (Rs<<RS_SHF) | (1<<RE_SHF);
+}
+
+void MIPS64Assembler::LD(int Rt, int Rbase, int16_t offset)
+{
+    *mPC++ = (ld_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPS64Assembler::SD(int Rt, int Rbase, int16_t offset)
+{
+    *mPC++ = (sd_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPS64Assembler::LUI(int Rt, int16_t offset)
+{
+    *mPC++ = (aui_op<<OP_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+
+void MIPS64Assembler::JR(int Rs)
+{
+        *mPC++ = (spec_op<<OP_SHF) | (Rs<<RS_SHF) | (jalr_fn << FUNC_SHF);
+        MIPS64Assembler::NOP();
+}
+
+}; // namespace android:
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.h b/libpixelflinger/codeflinger/MIPS64Assembler.h
new file mode 100644 (file)
index 0000000..b43e5da
--- /dev/null
@@ -0,0 +1,404 @@
+/* libs/pixelflinger/codeflinger/MIPS64Assembler.h
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_MIPS64ASSEMBLER_H
+#define ANDROID_MIPS64ASSEMBLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "utils/KeyedVector.h"
+#include "utils/Vector.h"
+#include "tinyutils/smartpointer.h"
+
+#include "ARMAssemblerInterface.h"
+#include "MIPSAssembler.h"
+#include "CodeCache.h"
+
+namespace android {
+
+class MIPS64Assembler;    // forward reference
+
+// this class mimics ARMAssembler interface
+// intent is to translate each ARM instruction to 1 or more MIPS instr
+// implementation calls MIPS64Assembler class to generate mips code
+class ArmToMips64Assembler : public ARMAssemblerInterface
+{
+public:
+                ArmToMips64Assembler(const sp<Assembly>& assembly,
+                        char *abuf = 0, int linesz = 0, int instr_count = 0);
+                ArmToMips64Assembler(void* assembly);
+    virtual     ~ArmToMips64Assembler();
+
+    uint32_t*   base() const;
+    uint32_t*   pc() const;
+    void        disassemble(const char* name);
+
+    virtual void    reset();
+
+    virtual int     generate(const char* name);
+    virtual int     getCodegenArch();
+
+    virtual void    prolog();
+    virtual void    epilog(uint32_t touched);
+    virtual void    comment(const char* string);
+    // for testing purposes
+    void        fix_branches();
+    void        set_condition(int mode, int R1, int R2);
+
+
+    // -----------------------------------------------------------------------
+    // shifters and addressing modes
+    // -----------------------------------------------------------------------
+
+    // shifters...
+    virtual bool        isValidImmediate(uint32_t immed);
+    virtual int         buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm);
+
+    virtual uint32_t    imm(uint32_t immediate);
+    virtual uint32_t    reg_imm(int Rm, int type, uint32_t shift);
+    virtual uint32_t    reg_rrx(int Rm);
+    virtual uint32_t    reg_reg(int Rm, int type, int Rs);
+
+    // addressing modes...
+    // LDR(B)/STR(B)/PLD
+    // (immediate and Rm can be negative, which indicates U=0)
+    virtual uint32_t    immed12_pre(int32_t immed12, int W=0);
+    virtual uint32_t    immed12_post(int32_t immed12);
+    virtual uint32_t    reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0);
+    virtual uint32_t    reg_scale_post(int Rm, int type=0, uint32_t shift=0);
+
+    // LDRH/LDRSB/LDRSH/STRH
+    // (immediate and Rm can be negative, which indicates U=0)
+    virtual uint32_t    immed8_pre(int32_t immed8, int W=0);
+    virtual uint32_t    immed8_post(int32_t immed8);
+    virtual uint32_t    reg_pre(int Rm, int W=0);
+    virtual uint32_t    reg_post(int Rm);
+
+
+
+
+    virtual void    dataProcessing(int opcode, int cc, int s,
+                                int Rd, int Rn,
+                                uint32_t Op2);
+    virtual void MLA(int cc, int s,
+                int Rd, int Rm, int Rs, int Rn);
+    virtual void MUL(int cc, int s,
+                int Rd, int Rm, int Rs);
+    virtual void UMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void UMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void SMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void SMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+
+    virtual void B(int cc, uint32_t* pc);
+    virtual void BL(int cc, uint32_t* pc);
+    virtual void BX(int cc, int Rn);
+    virtual void label(const char* theLabel);
+    virtual void B(int cc, const char* label);
+    virtual void BL(int cc, const char* label);
+
+    virtual uint32_t* pcForLabel(const char* label);
+
+    virtual void LDR (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRB(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void STR (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void STRB(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRH (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRSB(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRSH(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void STRH (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+
+    virtual void LDM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list);
+    virtual void STM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list);
+
+    virtual void SWP(int cc, int Rn, int Rd, int Rm);
+    virtual void SWPB(int cc, int Rn, int Rd, int Rm);
+    virtual void SWI(int cc, uint32_t comment);
+
+    virtual void PLD(int Rn, uint32_t offset);
+    virtual void CLZ(int cc, int Rd, int Rm);
+    virtual void QADD(int cc, int Rd, int Rm, int Rn);
+    virtual void QDADD(int cc, int Rd, int Rm, int Rn);
+    virtual void QSUB(int cc, int Rd, int Rm, int Rn);
+    virtual void QDSUB(int cc, int Rd, int Rm, int Rn);
+    virtual void SMUL(int cc, int xy,
+                int Rd, int Rm, int Rs);
+    virtual void SMULW(int cc, int y,
+                int Rd, int Rm, int Rs);
+    virtual void SMLA(int cc, int xy,
+                int Rd, int Rm, int Rs, int Rn);
+    virtual void SMLAL(int cc, int xy,
+                int RdHi, int RdLo, int Rs, int Rm);
+    virtual void SMLAW(int cc, int y,
+                int Rd, int Rm, int Rs, int Rn);
+
+    // byte/half word extract...
+    virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+
+    // bit manipulation...
+    virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
+
+    // Address loading/storing/manipulation
+    virtual void ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset = __immed12_pre(0));
+    virtual void ADDR_STR(int cc, int Rd, int Rn, uint32_t offset = __immed12_pre(0));
+    virtual void ADDR_ADD(int cc, int s, int Rd, int Rn, uint32_t Op2);
+    virtual void ADDR_SUB(int cc, int s, int Rd, int Rn, uint32_t Op2);
+
+    // this is some crap to share is MIPS64Assembler class for debug
+    char *      mArmDisassemblyBuffer;
+    int         mArmLineLength;
+    int         mArmInstrCount;
+
+    int         mInum;      // current arm instuction number (0..n)
+    uint32_t**  mArmPC;     // array: PC for 1st mips instr of
+                            //      each translated ARM instr
+
+
+private:
+    ArmToMips64Assembler(const ArmToMips64Assembler& rhs);
+    ArmToMips64Assembler& operator = (const ArmToMips64Assembler& rhs);
+
+    void init_conditional_labels(void);
+
+    void protectConditionalOperands(int Rd);
+
+    // reg__tmp set to MIPS AT, reg 1
+    int dataProcAdrModes(int op, int& source, bool sign = false, int reg_tmp = 1);
+
+    sp<Assembly>        mAssembly;
+    MIPS64Assembler*    mMips;
+
+
+    enum misc_constants_t {
+        ARM_MAX_INSTUCTIONS = 512  // based on ASSEMBLY_SCRATCH_SIZE
+    };
+
+    enum {
+        SRC_REG = 0,
+        SRC_IMM,
+        SRC_ERROR = -1
+    };
+
+    enum addr_modes {
+        // start above the range of legal mips reg #'s (0-31)
+        AMODE_REG = 0x20,
+        AMODE_IMM, AMODE_REG_IMM,               // for data processing
+        AMODE_IMM_12_PRE, AMODE_IMM_12_POST,    // for load/store
+        AMODE_REG_SCALE_PRE, AMODE_IMM_8_PRE,
+        AMODE_IMM_8_POST, AMODE_REG_PRE,
+        AMODE_UNSUPPORTED
+    };
+
+    struct addr_mode_t {    // address modes for current ARM instruction
+        int         reg;
+        int         stype;
+        uint32_t    value;
+        bool        writeback;  // writeback the adr reg after modification
+    } amode;
+
+    enum cond_types {
+        CMP_COND = 1,
+        SBIT_COND
+    };
+
+    struct cond_mode_t {    // conditional-execution info for current ARM instruction
+        cond_types  type;
+        int         r1;
+        int         r2;
+        int         labelnum;
+        char        label[100][10];
+    } cond;
+};
+
+
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+// This is the basic MIPS64 assembler, which just creates the opcodes in memory.
+// All the more complicated work is done in ArmToMips64Assember above.
+// Inherits MIPSAssembler class, and overrides only MIPS64r6 specific stuff
+
+class MIPS64Assembler : public MIPSAssembler
+{
+public:
+                MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent);
+                MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent);
+    virtual     ~MIPS64Assembler();
+
+    virtual void        reset();
+    virtual void        disassemble(const char* name);
+
+    void        fix_branches();
+
+    // ------------------------------------------------------------------------
+    // MIPS64AssemblerInterface...
+    // ------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Arithmetic...
+#endif
+
+    void DADDU(int Rd, int Rs, int Rt);
+    void DADDIU(int Rt, int Rs, int16_t imm);
+    void DSUBU(int Rd, int Rs, int Rt);
+    void DSUBIU(int Rt, int Rs, int16_t imm);
+    virtual void MUL(int Rd, int Rs, int Rt);
+    void MUH(int Rd, int Rs, int Rt);
+
+#if 0
+#pragma mark -
+#pragma mark Logical...
+#endif
+
+    virtual void CLO(int Rd, int Rs);
+    virtual void CLZ(int Rd, int Rs);
+
+#if 0
+#pragma mark -
+#pragma mark Load/store...
+#endif
+
+    void LD(int Rt, int Rbase, int16_t offset);
+    void SD(int Rt, int Rbase, int16_t offset);
+    virtual void LUI(int Rt, int16_t offset);
+
+#if 0
+#pragma mark -
+#pragma mark Branch...
+#endif
+
+    void JR(int Rs);
+
+
+protected:
+    ArmToMips64Assembler *mParent;
+
+    // opcode field of all instructions
+    enum opcode_field {
+        spec_op, regimm_op, j_op, jal_op,                  // 0x00 - 0x03
+        beq_op, bne_op, pop06_op, pop07_op,                // 0x04 - 0x07
+        pop10_op, addiu_op, slti_op, sltiu_op,             // 0x08 - 0x0b
+        andi_op, ori_op, xori_op, aui_op,                  // 0x0c - 0x0f
+        cop0_op, cop1_op, cop2_op, rsrv_opc_0,             // 0x10 - 0x13
+        rsrv_opc_1, rsrv_opc_2, pop26_op, pop27_op,        // 0x14 - 0x17
+        pop30_op, daddiu_op, rsrv_opc_3, rsrv_opc_4,       // 0x18 - 0x1b
+        rsrv_opc_5, daui_op, msa_op, spec3_op,             // 0x1c - 0x1f
+        lb_op, lh_op, rsrv_opc_6, lw_op,                   // 0x20 - 0x23
+        lbu_op, lhu_op, rsrv_opc_7, lwu_op,                // 0x24 - 0x27
+        sb_op, sh_op, rsrv_opc_8, sw_op,                   // 0x28 - 0x2b
+        rsrv_opc_9, rsrv_opc_10, rsrv_opc_11, rsrv_opc_12, // 0x2c - 0x2f
+        rsrv_opc_13, lwc1_op, bc_op, rsrv_opc_14,          // 0x2c - 0x2f
+        rsrv_opc_15, ldc1_op, pop66_op, ld_op,             // 0x30 - 0x33
+        rsrv_opc_16, swc1_op, balc_op, pcrel_op,           // 0x34 - 0x37
+        rsrv_opc_17, sdc1_op, pop76_op, sd_op              // 0x38 - 0x3b
+    };
+
+
+    // func field for special opcode
+    enum func_spec_op {
+        sll_fn, rsrv_spec_0, srl_fn, sra_fn,
+        sllv_fn, lsa_fn, srlv_fn, srav_fn,
+        rsrv_spec_1, jalr_fn, rsrv_spec_2, rsrv_spec_3,
+        syscall_fn, break_fn, sdbbp_fn, sync_fn,
+        clz_fn, clo_fn, dclz_fn, dclo_fn,
+        dsllv_fn, dlsa_fn, dsrlv_fn, dsrav_fn,
+        sop30_fn, sop31_fn, sop32_fn, sop33_fn,
+        sop34_fn, sop35_fn, sop36_fn, sop37_fn,
+        add_fn, addu_fn, sub_fn, subu_fn,
+        and_fn, or_fn, xor_fn, nor_fn,
+        rsrv_spec_4, rsrv_spec_5, slt_fn, sltu_fn,
+        dadd_fn, daddu_fn, dsub_fn, dsubu_fn,
+        tge_fn, tgeu_fn, tlt_fn, tltu_fn,
+        teq_fn, seleqz_fn, tne_fn, selnez_fn,
+        dsll_fn, rsrv_spec_6, dsrl_fn, dsra_fn,
+        dsll32_fn, rsrv_spec_7, dsrl32_fn, dsra32_fn
+    };
+
+    // func field for spec3 opcode
+    enum func_spec3_op {
+        ext_fn, dextm_fn, dextu_fn, dext_fn,
+        ins_fn, dinsm_fn, dinsu_fn, dins_fn,
+        cachee_fn = 0x1b, sbe_fn, she_fn, sce_fn, swe_fn,
+        bshfl_fn, prefe_fn = 0x23, dbshfl_fn, cache_fn, sc_fn, scd_fn,
+        lbue_fn, lhue_fn, lbe_fn = 0x2c, lhe_fn, lle_fn, lwe_fn,
+        pref_fn = 0x35, ll_fn, lld_fn, rdhwr_fn = 0x3b
+    };
+
+    // sa field for spec3 opcodes, with BSHFL function
+    enum func_spec3_bshfl {
+        bitswap_fn,
+        wsbh_fn = 0x02,
+        dshd_fn = 0x05,
+        seb_fn = 0x10,
+        seh_fn = 0x18
+    };
+
+    // rt field of regimm opcodes.
+    enum regimm_fn {
+        bltz_fn, bgez_fn,
+        dahi_fn = 0x6,
+        nal_fn = 0x10, bal_fn, bltzall_fn, bgezall_fn,
+        sigrie_fn = 0x17,
+        dati_fn = 0x1e, synci_fn
+    };
+
+    enum muldiv_fn {
+        mul_fn = 0x02, muh_fn
+    };
+
+    enum mips_inst_shifts {
+        OP_SHF       = 26,
+        JTARGET_SHF  = 0,
+        RS_SHF       = 21,
+        RT_SHF       = 16,
+        RD_SHF       = 11,
+        RE_SHF       = 6,
+        SA_SHF       = RE_SHF,  // synonym
+        IMM_SHF      = 0,
+        FUNC_SHF     = 0,
+
+        // mask values
+        MSK_16       = 0xffff,
+
+
+        CACHEOP_SHF  = 18,
+        CACHESEL_SHF = 16,
+    };
+};
+
+
+}; // namespace android
+
+#endif //ANDROID_MIPS64ASSEMBLER_H
index a88d2fe..daa231f 100644 (file)
@@ -1358,7 +1358,7 @@ void MIPSAssembler::disassemble(const char* name)
         ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt);
         string_detab(di_buf);
         string_pad(di_buf, 30);
-        ALOGW("%08x:    %08x    %s", uint32_t(mipsPC), uint32_t(*mipsPC), di_buf);
+        ALOGW("%08x:    %08x    %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf);
         mipsPC++;
     }
 }
@@ -1407,7 +1407,7 @@ int MIPSAssembler::generate(const char* name)
 
 #if defined(WITH_LIB_HARDWARE)
     if (__builtin_expect(mQemuTracing, 0)) {
-        int err = qemu_add_mapping(int(base()), name);
+        int err = qemu_add_mapping(uintptr_t(base()), name);
         mQemuTracing = (err >= 0);
     }
 #endif
index 8fea8cb..06cb0d0 100644 (file)
@@ -244,20 +244,20 @@ public:
                 MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent);
     virtual     ~MIPSAssembler();
 
-    uint32_t*   base() const;
-    uint32_t*   pc() const;
-    void        reset();
+    virtual uint32_t*   base() const;
+    virtual uint32_t*   pc() const;
+    virtual void        reset();
 
-    void        disassemble(const char* name);
+    virtual void        disassemble(const char* name);
 
-    void        prolog();
-    void        epilog(uint32_t touched);
-    int         generate(const char* name);
-    void        comment(const char* string);
-    void        label(const char* string);
+    virtual void        prolog();
+    virtual void        epilog(uint32_t touched);
+    virtual int         generate(const char* name);
+    virtual void        comment(const char* string);
+    virtual void        label(const char* string);
 
     // valid only after generate() has been called
-    uint32_t*   pcForLabel(const char* label);
+    virtual uint32_t*   pcForLabel(const char* label);
 
 
     // ------------------------------------------------------------------------
@@ -399,9 +399,9 @@ public:
 
 
 
-private:
-    void string_detab(char *s);
-    void string_pad(char *s, int padded_len);
+protected:
+    virtual void string_detab(char *s);
+    virtual void string_pad(char *s, int padded_len);
 
     ArmToMipsAssembler *mParent;
     sp<Assembly>    mAssembly;
@@ -537,7 +537,11 @@ private:
 enum mips_regnames {
     R_zero = 0,
             R_at,   R_v0,   R_v1,   R_a0,   R_a1,   R_a2,   R_a3,
+#if __mips_isa_rev < 6
     R_t0,   R_t1,   R_t2,   R_t3,   R_t4,   R_t5,   R_t6,   R_t7,
+#else
+    R_a4,   R_a5,   R_a6,   R_a7,   R_t0,   R_t1,   R_t2,   R_t3,
+#endif
     R_s0,   R_s1,   R_s2,   R_s3,   R_s4,   R_s5,   R_s6,   R_s7,
     R_t8,   R_t9,   R_k0,   R_k1,   R_gp,   R_sp,   R_s8,   R_ra,
     R_lr = R_s8,
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
new file mode 100644 (file)
index 0000000..44b7fe7
--- /dev/null
@@ -0,0 +1,582 @@
+/*  $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $   */
+
+/*-
+ * Copyright (c) 1991, 1993
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *  from: @(#)kadb.c    8.1 (Berkeley) 6/10/93
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <sys/types.h>
+#include "mips_opcode.h"
+
+#include <cutils/log.h>
+
+static char *sprintf_buffer;
+static int sprintf_buf_len;
+
+
+typedef uint64_t db_addr_t;
+static void db_printf(const char* fmt, ...);
+
+static const char * const op_name[64] = {
+/* 0 */ "spec", "bcond", "j", "jal", "beq", "bne", "blez", "bgtz",
+/* 8 */ "pop10", "addiu", "slti", "sltiu", "andi", "ori", "xori", "aui",
+/*16 */ "cop0", "cop1", "cop2", "?", "?", "?", "pop26", "pop27",
+/*24 */ "pop30", "daddiu", "?", "?", "?", "daui", "msa", "op37",
+/*32 */ "lb", "lh", "?",  "lw", "lbu", "lhu", "?", "lwu",
+/*40 */ "sb", "sh", "?", "sw", "?", "?", "?", "?",
+/*48 */ "?", "lwc1", "bc", "?", "?",  "ldc1", "pop66", "ld",
+/*56 */ "?", "swc1", "balc", "pcrel", "?", "sdc1", "pop76", "sd"
+};
+
+static const char * const spec_name[64] = {
+/* 0 */ "sll", "?", "srl", "sra", "sllv", "?", "srlv", "srav",
+/* 8 */ "?", "jalr", "?", "?", "syscall", "break", "sdbpp", "sync",
+/*16 */ "clz", "clo", "dclz", "dclo", "dsllv", "dlsa", "dsrlv", "dsrav",
+/*24 */ "sop30", "sop31", "sop32", "sop33", "sop34", "sop35", "sop36", "sop37",
+/*32 */ "add", "addu", "sub", "subu", "and", "or", "xor", "nor",
+/*40 */ "?", "?", "slt", "sltu", "dadd", "daddu", "dsub", "dsubu",
+/*48 */ "tge", "tgeu", "tlt", "tltu", "teq", "seleqz", "tne", "selnez",
+/*56 */ "dsll", "?", "dsrl", "dsra", "dsll32", "?", "dsrl32", "dsra32"
+};
+
+static const char * const bcond_name[32] = {
+/* 0 */ "bltz", "bgez", "?", "?", "?", "?", "dahi", "?",
+/* 8 */ "?", "?", "?", "?", "?", "?", "?", "?",
+/*16 */ "nal", "bal", "?", "?", "?", "?", "?", "sigrie",
+/*24 */ "?", "?", "?", "?", "?", "?", "dati", "synci",
+};
+
+static const char * const cop1_name[64] = {
+/* 0 */ "fadd",  "fsub", "fmpy", "fdiv", "fsqrt","fabs", "fmov", "fneg",
+/* 8 */ "fop08","fop09","fop0a","fop0b","fop0c","fop0d","fop0e","fop0f",
+/*16 */ "fop10","fop11","fop12","fop13","fop14","fop15","fop16","fop17",
+/*24 */ "fop18","fop19","fop1a","fop1b","fop1c","fop1d","fop1e","fop1f",
+/*32 */ "fcvts","fcvtd","fcvte","fop23","fcvtw","fop25","fop26","fop27",
+/*40 */ "fop28","fop29","fop2a","fop2b","fop2c","fop2d","fop2e","fop2f",
+/*48 */ "fcmp.f","fcmp.un","fcmp.eq","fcmp.ueq","fcmp.olt","fcmp.ult",
+    "fcmp.ole","fcmp.ule",
+/*56 */ "fcmp.sf","fcmp.ngle","fcmp.seq","fcmp.ngl","fcmp.lt","fcmp.nge",
+    "fcmp.le","fcmp.ngt"
+};
+
+static const char * const fmt_name[16] = {
+    "s",    "d",    "e",    "fmt3",
+    "w",    "fmt5", "fmt6", "fmt7",
+    "fmt8", "fmt9", "fmta", "fmtb",
+    "fmtc", "fmtd", "fmte", "fmtf"
+};
+
+static char * const mips_reg_name[32] = {
+    "zero", "at",   "v0",   "v1",   "a0",   "a1",   "a2",   "a3",
+    "a4",   "a5",   "a6",   "a7",   "t0",   "t1",   "t2",   "t3",
+    "s0",   "s1",   "s2",   "s3",   "s4",   "s5",   "s6",   "s7",
+    "t8",   "t9",   "k0",   "k1",   "gp",   "sp",   "s8",   "ra"
+};
+
+static char * alt_arm_reg_name[32] = {  // hacked names for comparison with ARM code
+    "zero", "at",   "r0",   "r1",   "r2",   "r3",   "r4",   "r5",
+    "r6",   "r7",   "r8",   "r9",   "r10",  "r11",  "r12",  "r13",
+    "r14",  "r15",  "at2",  "cmp",  "s4",   "s5",   "s6",   "s7",
+    "t8",   "t9",   "k0",   "k1",   "gp",   "sp",   "s8",   "ra"
+};
+
+static char ** reg_name =  &mips_reg_name[0];
+
+static const char * const c0_opname[64] = {
+    "c0op00","tlbr",  "tlbwi", "c0op03","c0op04","c0op05","tlbwr", "c0op07",
+    "tlbp",  "c0op11","c0op12","c0op13","c0op14","c0op15","c0op16","c0op17",
+    "rfe",   "c0op21","c0op22","c0op23","c0op24","c0op25","c0op26","c0op27",
+    "eret",  "c0op31","c0op32","c0op33","c0op34","c0op35","c0op36","c0op37",
+    "c0op40","c0op41","c0op42","c0op43","c0op44","c0op45","c0op46","c0op47",
+    "c0op50","c0op51","c0op52","c0op53","c0op54","c0op55","c0op56","c0op57",
+    "c0op60","c0op61","c0op62","c0op63","c0op64","c0op65","c0op66","c0op67",
+    "c0op70","c0op71","c0op72","c0op73","c0op74","c0op75","c0op77","c0op77",
+};
+
+static const char * const c0_reg[32] = {
+    "index",    "random",   "tlblo0",  "tlblo1",
+    "context",  "pagemask", "wired",   "cp0r7",
+    "badvaddr", "count",    "tlbhi",   "compare",
+    "status",   "cause",    "epc",     "prid",
+    "config",   "lladdr",   "watchlo", "watchhi",
+    "xcontext", "cp0r21",   "cp0r22",  "debug",
+    "depc",     "perfcnt",  "ecc",     "cacheerr",
+    "taglo",    "taghi",    "errepc",  "desave"
+};
+
+static void print_addr(db_addr_t);
+db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format);
+
+
+/*
+ * Disassemble instruction 'insn' nominally at 'loc'.
+ * 'loc' may in fact contain a breakpoint instruction.
+ */
+static db_addr_t
+db_disasm_insn(int insn, db_addr_t loc, bool altfmt)
+{
+    bool bdslot = false;
+    InstFmt i;
+
+    i.word = insn;
+
+    switch (i.JType.op) {
+    case OP_SPECIAL:
+        if (i.word == 0) {
+            db_printf("nop");
+            break;
+        }
+        if (i.word == 0x0080) {
+            db_printf("NIY");
+            break;
+        }
+        if (i.word == 0x00c0) {
+            db_printf("NOT IMPL");
+            break;
+        }
+        /* Special cases --------------------------------------------------
+         * "addu" is a "move" only in 32-bit mode.  What's the correct
+         * answer - never decode addu/daddu as "move"?
+         */
+        if ( (i.RType.func == OP_ADDU && i.RType.rt == 0)  ||
+             (i.RType.func == OP_OR   && i.RType.rt == 0) ) {
+            db_printf("move\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rs]);
+            break;
+        }
+
+        if (i.RType.func == OP_SRL && (i.RType.rs & 1) == 1) {
+            db_printf("rotr\t%s,%s,%d", reg_name[i.RType.rd],
+                reg_name[i.RType.rt], i.RType.shamt);
+            break;
+        }
+        if (i.RType.func == OP_SRLV && (i.RType.shamt & 1) == 1) {
+            db_printf("rotrv\t%s,%s,%s", reg_name[i.RType.rd],
+                reg_name[i.RType.rt], reg_name[i.RType.rs]);
+            break;
+        }
+
+        if (i.RType.func == OP_SOP30) {
+            if (i.RType.shamt == OP_MUL) {
+                db_printf("mul");
+            } else if (i.RType.shamt == OP_MUH) {
+                db_printf("muh");
+            }
+            db_printf("\t%s,%s,%s", reg_name[i.RType.rd],
+                reg_name[i.RType.rs], reg_name[i.RType.rt]);
+            break;
+        }
+        if (i.RType.func == OP_SOP31) {
+            if (i.RType.shamt == OP_MUL) {
+                db_printf("mulu");
+            } else if (i.RType.shamt == OP_MUH) {
+                db_printf("muhu");
+            }
+            db_printf("\t%s,%s,%s", reg_name[i.RType.rd],
+                reg_name[i.RType.rs], reg_name[i.RType.rt]);
+            break;
+        }
+
+        if (i.RType.func == OP_JALR && i.RType.rd == 0) {
+            db_printf("jr\t%s", reg_name[i.RType.rs]);
+            bdslot = true;
+            break;
+        }
+
+        db_printf("%s", spec_name[i.RType.func]);
+        switch (i.RType.func) {
+        case OP_SLL:
+        case OP_SRL:
+        case OP_SRA:
+        case OP_DSLL:
+
+        case OP_DSRL:
+        case OP_DSRA:
+        case OP_DSLL32:
+        case OP_DSRL32:
+        case OP_DSRA32:
+            db_printf("\t%s,%s,%d",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt],
+                i.RType.shamt);
+            break;
+
+        case OP_SLLV:
+        case OP_SRLV:
+        case OP_SRAV:
+        case OP_DSLLV:
+        case OP_DSRLV:
+        case OP_DSRAV:
+            db_printf("\t%s,%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt],
+                reg_name[i.RType.rs]);
+            break;
+
+        case OP_CLZ:
+        case OP_CLO:
+        case OP_DCLZ:
+        case OP_DCLO:
+            db_printf("\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rs]);
+            break;
+
+        case OP_JALR:
+            db_printf("\t");
+            if (i.RType.rd != 31) {
+                db_printf("%s,", reg_name[i.RType.rd]);
+            }
+            db_printf("%s", reg_name[i.RType.rs]);
+            bdslot = true;
+            break;
+
+        case OP_SYSCALL:
+        case OP_SYNC:
+            break;
+
+        case OP_BREAK:
+            db_printf("\t%d", (i.RType.rs << 5) | i.RType.rt);
+            break;
+
+        default:
+            db_printf("\t%s,%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rs],
+                reg_name[i.RType.rt]);
+        }
+        break;
+
+    case OP_SPECIAL3:
+        if (i.RType.func == OP_EXT)
+            db_printf("ext\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd+1);
+        else if (i.RType.func == OP_DEXT)
+            db_printf("dext\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd+1);
+        else if (i.RType.func == OP_DEXTM)
+            db_printf("dextm\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd+33);
+        else if (i.RType.func == OP_DEXTU)
+            db_printf("dextu\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt+32,
+                    i.RType.rd+1);
+        else if (i.RType.func == OP_INS)
+            db_printf("ins\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd-i.RType.shamt+1);
+        else if (i.RType.func == OP_DINS)
+            db_printf("dins\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd-i.RType.shamt+1);
+        else if (i.RType.func == OP_DINSM)
+            db_printf("dinsm\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd-i.RType.shamt+33);
+        else if (i.RType.func == OP_DINSU)
+            db_printf("dinsu\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt+32,
+                    i.RType.rd-i.RType.shamt+1);
+        else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH)
+            db_printf("wsbh\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt]);
+        else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEB)
+            db_printf("seb\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt]);
+        else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEH)
+            db_printf("seh\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt]);
+        else if (i.RType.func == OP_RDHWR)
+            db_printf("rdhwr\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt]);
+        else
+            db_printf("Unknown");
+        break;
+
+    case OP_BCOND:
+        db_printf("%s\t%s,", bcond_name[i.IType.rt],
+            reg_name[i.IType.rs]);
+        goto pr_displ;
+
+    case OP_BLEZ:
+    case OP_BGTZ:
+        db_printf("%s\t%s,", op_name[i.IType.op],
+            reg_name[i.IType.rs]);
+        goto pr_displ;
+
+    case OP_BEQ:
+        if (i.IType.rs == 0 && i.IType.rt == 0) {
+            db_printf("b\t");
+            goto pr_displ;
+        }
+        /* FALLTHROUGH */
+    case OP_BNE:
+        db_printf("%s\t%s,%s,", op_name[i.IType.op],
+            reg_name[i.IType.rs],
+            reg_name[i.IType.rt]);
+    pr_displ:
+        print_addr(loc + 4 + ((short)i.IType.imm << 2));
+        bdslot = true;
+        break;
+
+    case OP_COP0:
+        switch (i.RType.rs) {
+        case OP_BCx:
+        case OP_BCy:
+
+            db_printf("bc0%c\t",
+                "ft"[i.RType.rt & COPz_BC_TF_MASK]);
+            goto pr_displ;
+
+        case OP_MT:
+            db_printf("mtc0\t%s,%s",
+                reg_name[i.RType.rt],
+                c0_reg[i.RType.rd]);
+            break;
+
+        case OP_DMT:
+            db_printf("dmtc0\t%s,%s",
+                reg_name[i.RType.rt],
+                c0_reg[i.RType.rd]);
+            break;
+
+        case OP_MF:
+            db_printf("mfc0\t%s,%s",
+                reg_name[i.RType.rt],
+                c0_reg[i.RType.rd]);
+            break;
+
+        case OP_DMF:
+            db_printf("dmfc0\t%s,%s",
+                reg_name[i.RType.rt],
+                c0_reg[i.RType.rd]);
+            break;
+
+        default:
+            db_printf("%s", c0_opname[i.FRType.func]);
+        }
+        break;
+
+    case OP_COP1:
+        switch (i.RType.rs) {
+        case OP_BCx:
+        case OP_BCy:
+            db_printf("bc1%c\t",
+                "ft"[i.RType.rt & COPz_BC_TF_MASK]);
+            goto pr_displ;
+
+        case OP_MT:
+            db_printf("mtc1\t%s,f%d",
+                reg_name[i.RType.rt],
+                i.RType.rd);
+            break;
+
+        case OP_MF:
+            db_printf("mfc1\t%s,f%d",
+                reg_name[i.RType.rt],
+                i.RType.rd);
+            break;
+
+        case OP_CT:
+            db_printf("ctc1\t%s,f%d",
+                reg_name[i.RType.rt],
+                i.RType.rd);
+            break;
+
+        case OP_CF:
+            db_printf("cfc1\t%s,f%d",
+                reg_name[i.RType.rt],
+                i.RType.rd);
+            break;
+
+        default:
+            db_printf("%s.%s\tf%d,f%d,f%d",
+                cop1_name[i.FRType.func],
+                fmt_name[i.FRType.fmt],
+                i.FRType.fd, i.FRType.fs, i.FRType.ft);
+        }
+        break;
+
+    case OP_J:
+    case OP_JAL:
+        db_printf("%s\t", op_name[i.JType.op]);
+        print_addr((loc & 0xFFFFFFFFF0000000) | (i.JType.target << 2));
+        bdslot = true;
+        break;
+
+    case OP_LWC1:
+    case OP_SWC1:
+        db_printf("%s\tf%d,", op_name[i.IType.op],
+            i.IType.rt);
+        goto loadstore;
+
+    case OP_LB:
+    case OP_LH:
+    case OP_LW:
+    case OP_LD:
+    case OP_LBU:
+    case OP_LHU:
+    case OP_LWU:
+    case OP_SB:
+    case OP_SH:
+    case OP_SW:
+    case OP_SD:
+        db_printf("%s\t%s,", op_name[i.IType.op],
+            reg_name[i.IType.rt]);
+    loadstore:
+        db_printf("%d(%s)", (short)i.IType.imm,
+            reg_name[i.IType.rs]);
+        break;
+
+    case OP_ORI:
+    case OP_XORI:
+        if (i.IType.rs == 0) {
+            db_printf("li\t%s,0x%x",
+                reg_name[i.IType.rt],
+                i.IType.imm);
+            break;
+        }
+        /* FALLTHROUGH */
+    case OP_ANDI:
+        db_printf("%s\t%s,%s,0x%x", op_name[i.IType.op],
+            reg_name[i.IType.rt],
+            reg_name[i.IType.rs],
+            i.IType.imm);
+        break;
+
+    case OP_AUI:
+        if (i.IType.rs == 0) {
+            db_printf("lui\t%s,0x%x", reg_name[i.IType.rt],
+                i.IType.imm);
+        } else {
+            db_printf("%s\t%s,%s,%d", op_name[i.IType.op],
+            reg_name[i.IType.rt], reg_name[i.IType.rs],
+            (short)i.IType.imm);
+        }
+        break;
+
+    case OP_ADDIU:
+    case OP_DADDIU:
+        if (i.IType.rs == 0) {
+            db_printf("li\t%s,%d",
+                reg_name[i.IType.rt],
+                (short)i.IType.imm);
+            break;
+        }
+        /* FALLTHROUGH */
+    default:
+        db_printf("%s\t%s,%s,%d", op_name[i.IType.op],
+            reg_name[i.IType.rt],
+            reg_name[i.IType.rs],
+            (short)i.IType.imm);
+    }
+    // db_printf("\n");
+    // if (bdslot) {
+    //     db_printf("   bd: ");
+    //     mips_disassem(loc+4);
+    //     return (loc + 8);
+    // }
+    return (loc + 4);
+}
+
+static void
+print_addr(db_addr_t loc)
+{
+    db_printf("0x%08lx", loc);
+}
+
+static void db_printf(const char* fmt, ...)
+{
+    int cnt;
+    va_list argp;
+    va_start(argp, fmt);
+    if (sprintf_buffer) {
+        cnt = vsnprintf(sprintf_buffer, sprintf_buf_len, fmt, argp);
+        sprintf_buffer += cnt;
+        sprintf_buf_len -= cnt;
+    } else {
+        vprintf(fmt, argp);
+    }
+}
+
+/*
+ * Disassemble instruction at 'loc'.
+ * Return address of start of next instruction.
+ * Since this function is used by 'examine' and by 'step'
+ * "next instruction" does NOT mean the next instruction to
+ * be executed but the 'linear' next instruction.
+ */
+db_addr_t
+mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format)
+{
+    u_int32_t instr;
+
+    if (alt_dis_format) {   // use ARM register names for disassembly
+        reg_name = &alt_arm_reg_name[0];
+    }
+
+    sprintf_buffer = di_buffer;     // quick 'n' dirty printf() vs sprintf()
+    sprintf_buf_len = 39;           // should be passed in
+
+    instr =  *(u_int32_t *)loc;
+    return (db_disasm_insn(instr, loc, false));
+}
diff --git a/libpixelflinger/codeflinger/mips64_disassem.h b/libpixelflinger/codeflinger/mips64_disassem.h
new file mode 100644 (file)
index 0000000..c94f04f
--- /dev/null
@@ -0,0 +1,56 @@
+/*  $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $   */
+
+/*-
+ * Copyright (c) 1991, 1993
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *  from: @(#)kadb.c    8.1 (Berkeley) 6/10/93
+ */
+
+
+
+#ifndef ANDROID_MIPS_DISASSEM_H
+#define ANDROID_MIPS_DISASSEM_H
+
+#include <sys/types.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+/* Prototypes for callable functions */
+
+void mips_disassem(uint32_t *location, char *di_buffer, int alt_fmt);
+
+#if __cplusplus
+}
+#endif
+
+#endif /* !ANDROID_MIPS_DISASSEM_H */
index 4ab9bd3..3007b15 100644 (file)
@@ -323,14 +323,14 @@ db_disasm_insn(int insn, db_addr_t loc, bool altfmt)
             db_printf("ext\t%s,%s,%d,%d",
                     reg_name[i.RType.rt],
                     reg_name[i.RType.rs],
-                    i.RType.rd+1,
-                    i.RType.shamt);
+                    i.RType.shamt,
+                    i.RType.rd+1);
         else if (i.RType.func == OP_INS)
             db_printf("ins\t%s,%s,%d,%d",
                     reg_name[i.RType.rt],
                     reg_name[i.RType.rs],
-                    i.RType.rd+1,
-                    i.RType.shamt);
+                    i.RType.shamt,
+                    i.RType.rd-i.RType.shamt+1);
         else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH)
             db_printf("wsbh\t%s,%s",
                 reg_name[i.RType.rd],
index 7ed5ef5..45bb19e 100644 (file)
@@ -125,69 +125,118 @@ typedef union {
 #define OP_BLEZ     006
 #define OP_BGTZ     007
 
+#if __mips_isa_rev < 6
 #define OP_ADDI     010
+#else
+#define OP_POP10    010
+#endif
+
 #define OP_ADDIU    011
 #define OP_SLTI     012
 #define OP_SLTIU    013
 #define OP_ANDI     014
 #define OP_ORI      015
 #define OP_XORI     016
+
+#if __mips_isa_rev < 6
 #define OP_LUI      017
+#else
+#define OP_AUI      017
+#endif
 
 #define OP_COP0     020
 #define OP_COP1     021
 #define OP_COP2     022
+
+#if __mips_isa_rev < 6
 #define OP_COP3     023
-#define OP_BEQL     024     /* MIPS-II, for r4000 port */
-#define OP_BNEL     025     /* MIPS-II, for r4000 port */
-#define OP_BLEZL    026     /* MIPS-II, for r4000 port */
-#define OP_BGTZL    027     /* MIPS-II, for r4000 port */
+#define OP_BEQL     024
+#define OP_BNEL     025
+#define OP_BLEZL    026
+#define OP_BGTZL    027
+#define OP_DADDI    030
+#else
+#define OP_POP26    026
+#define OP_POP27    027
+#define OP_POP30    030
+#endif
 
-#define OP_DADDI    030     /* MIPS-II, for r4000 port */
-#define OP_DADDIU   031     /* MIPS-II, for r4000 port */
-#define OP_LDL      032     /* MIPS-II, for r4000 port */
-#define OP_LDR      033     /* MIPS-II, for r4000 port */
+#define OP_DADDIU   031
 
-#define OP_SPECIAL2 034     /* QED opcodes */
-#define OP_SPECIAL3 037     /* mips32r2 opcodes */
+#if __mips_isa_rev < 6
+#define OP_LDL      032
+#define OP_LDR      033
+#define OP_SPECIAL2 034
+#else
+#define OP_DAUI     035
+#endif
+
+#define OP_SPECIAL3 037
 
 #define OP_LB       040
 #define OP_LH       041
+
+#if __mips_isa_rev < 6
 #define OP_LWL      042
+#endif
+
 #define OP_LW       043
 #define OP_LBU      044
 #define OP_LHU      045
 #define OP_LWR      046
 #define OP_LHU      045
+
+#if __mips_isa_rev < 6
 #define OP_LWR      046
-#define OP_LWU      047     /* MIPS-II, for r4000 port */
+#endif
+
+#define OP_LWU      047
 
 #define OP_SB       050
 #define OP_SH       051
+
+#if __mips_isa_rev < 6
 #define OP_SWL      052
+#endif
+
 #define OP_SW       053
-#define OP_SDL      054     /* MIPS-II, for r4000 port */
-#define OP_SDR      055     /* MIPS-II, for r4000 port */
-#define OP_SWR      056
-#define OP_CACHE    057     /* MIPS-II, for r4000 port */
 
+#if __mips_isa_rev < 6
+#define OP_SDL      054
+#define OP_SDR      055
+#define OP_SWR      056
+#define OP_CACHE    057
 #define OP_LL       060
-#define OP_LWC0     OP_LL   /* backwards source compatibility */
+#define OP_LWC0     OP_LL
 #define OP_LWC1     061
 #define OP_LWC2     062
 #define OP_LWC3     063
-#define OP_LLD      064     /* MIPS-II, for r4000 port */
+#define OP_LLD      064
+#else
+#define OP_LWC1     061
+#define OP_BC       062
+#endif
+
 #define OP_LDC1     065
-#define OP_LD       067     /* MIPS-II, for r4000 port */
+#define OP_LD       067
 
+#if __mips_isa_rev < 6
 #define OP_SC       070
-#define OP_SWC0     OP_SC   /* backwards source compatibility */
+#define OP_SWC0     OP_SC
+#endif
+
 #define OP_SWC1     071
+
+#if __mips_isa_rev < 6
 #define OP_SWC2     072
 #define OP_SWC3     073
-#define OP_SCD      074     /* MIPS-II, for r4000 port */
+#define OP_SCD      074
+#else
+#define OP_BALC     072
+#endif
+
 #define OP_SDC1     075
-#define OP_SD       077     /* MIPS-II, for r4000 port */
+#define OP_SD       077
 
 /*
  * Values for the 'func' field when 'op' == OP_SPECIAL.
@@ -199,28 +248,50 @@ typedef union {
 #define OP_SRLV     006
 #define OP_SRAV     007
 
+#if __mips_isa_rev < 6
 #define OP_JR       010
+#endif
+
 #define OP_JALR     011
 #define OP_SYSCALL  014
 #define OP_BREAK    015
-#define OP_SYNC     017     /* MIPS-II, for r4000 port */
+#define OP_SYNC     017
 
+#if __mips_isa_rev < 6
 #define OP_MFHI     020
 #define OP_MTHI     021
 #define OP_MFLO     022
 #define OP_MTLO     023
-#define OP_DSLLV    024     /* MIPS-II, for r4000 port */
-#define OP_DSRLV    026     /* MIPS-II, for r4000 port */
-#define OP_DSRAV    027     /* MIPS-II, for r4000 port */
+#else
+#define OP_CLZ      020
+#define OP_CLO      021
+#define OP_DCLZ     022
+#define OP_DCLO     023
+#endif
+
+#define OP_DSLLV    024
+#define OP_DSRLV    026
+#define OP_DSRAV    027
 
+#if __mips_isa_rev < 6
 #define OP_MULT     030
 #define OP_MULTU    031
 #define OP_DIV      032
 #define OP_DIVU     033
-#define OP_DMULT    034     /* MIPS-II, for r4000 port */
-#define OP_DMULTU   035     /* MIPS-II, for r4000 port */
-#define OP_DDIV     036     /* MIPS-II, for r4000 port */
-#define OP_DDIVU    037     /* MIPS-II, for r4000 port */
+#define OP_DMULT    034
+#define OP_DMULTU   035
+#define OP_DDIV     036
+#define OP_DDIVU    037
+#else
+#define OP_SOP30    030
+#define OP_SOP31    031
+#define OP_SOP32    032
+#define OP_SOP33    033
+#define OP_SOP34    034
+#define OP_SOP35    035
+#define OP_SOP36    036
+#define OP_SOP37    037
+#endif
 
 #define OP_ADD      040
 #define OP_ADDU     041
@@ -233,73 +304,96 @@ typedef union {
 
 #define OP_SLT      052
 #define OP_SLTU     053
-#define OP_DADD     054     /* MIPS-II, for r4000 port */
-#define OP_DADDU    055     /* MIPS-II, for r4000 port */
-#define OP_DSUB     056     /* MIPS-II, for r4000 port */
-#define OP_DSUBU    057     /* MIPS-II, for r4000 port */
-
-#define OP_TGE      060     /* MIPS-II, for r4000 port */
-#define OP_TGEU     061     /* MIPS-II, for r4000 port */
-#define OP_TLT      062     /* MIPS-II, for r4000 port */
-#define OP_TLTU     063     /* MIPS-II, for r4000 port */
-#define OP_TEQ      064     /* MIPS-II, for r4000 port */
-#define OP_TNE      066     /* MIPS-II, for r4000 port */
-
-#define OP_DSLL     070     /* MIPS-II, for r4000 port */
-#define OP_DSRL     072     /* MIPS-II, for r4000 port */
-#define OP_DSRA     073     /* MIPS-II, for r4000 port */
-#define OP_DSLL32   074     /* MIPS-II, for r4000 port */
-#define OP_DSRL32   076     /* MIPS-II, for r4000 port */
-#define OP_DSRA32   077     /* MIPS-II, for r4000 port */
-
+#define OP_DADD     054
+#define OP_DADDU    055
+#define OP_DSUB     056
+#define OP_DSUBU    057
+
+#define OP_TGE      060
+#define OP_TGEU     061
+#define OP_TLT      062
+#define OP_TLTU     063
+#define OP_TEQ      064
+#define OP_TNE      066
+
+#define OP_DSLL     070
+#define OP_DSRL     072
+#define OP_DSRA     073
+#define OP_DSLL32   074
+#define OP_DSRL32   076
+#define OP_DSRA32   077
+
+#if __mips_isa_rev < 6
 /*
  * Values for the 'func' field when 'op' == OP_SPECIAL2.
+ * OP_SPECIAL2 opcodes are removed in mips32r6
  */
 #define OP_MAD      000     /* QED */
 #define OP_MADU     001     /* QED */
 #define OP_MUL      002     /* QED */
+#endif
 
 /*
  * Values for the 'func' field when 'op' == OP_SPECIAL3.
  */
 #define OP_EXT      000
+#define OP_DEXTM    001
+#define OP_DEXTU    002
+#define OP_DEXT     003
 #define OP_INS      004
+#define OP_DINSM    005
+#define OP_DINSU    006
+#define OP_DINS     007
 #define OP_BSHFL    040
+#define OP_RDHWR    073
 
 /*
  * Values for the 'shamt' field when OP_SPECIAL3 && func OP_BSHFL.
  */
+
 #define OP_WSBH     002
 #define OP_SEB      020
 #define OP_SEH      030
 
+#if __mips_isa_rev == 6
+/*
+ * Values for the 'shamt' field when OP_SOP30.
+ */
+#define OP_MUL      002
+#define OP_MUH      003
+#endif
+
 /*
  * Values for the 'func' field when 'op' == OP_BCOND.
  */
 #define OP_BLTZ     000
 #define OP_BGEZ     001
-#define OP_BLTZL    002     /* MIPS-II, for r4000 port */
-#define OP_BGEZL    003     /* MIPS-II, for r4000 port */
 
-#define OP_TGEI     010     /* MIPS-II, for r4000 port */
-#define OP_TGEIU    011     /* MIPS-II, for r4000 port */
-#define OP_TLTI     012     /* MIPS-II, for r4000 port */
-#define OP_TLTIU    013     /* MIPS-II, for r4000 port */
-#define OP_TEQI     014     /* MIPS-II, for r4000 port */
-#define OP_TNEI     016     /* MIPS-II, for r4000 port */
-
-#define OP_BLTZAL   020     /* MIPS-II, for r4000 port */
+#if __mips_isa_rev < 6
+#define OP_BLTZL    002
+#define OP_BGEZL    003
+#define OP_TGEI     010
+#define OP_TGEIU    011
+#define OP_TLTI     012
+#define OP_TLTIU    013
+#define OP_TEQI     014
+#define OP_TNEI     016
+#define OP_BLTZAL   020
 #define OP_BGEZAL   021
 #define OP_BLTZALL  022
 #define OP_BGEZALL  023
+#else
+#define OP_NAL      020
+#define OP_BAL      021
+#endif
 
 /*
  * Values for the 'rs' field when 'op' == OP_COPz.
  */
 #define OP_MF       000
-#define OP_DMF      001     /* MIPS-II, for r4000 port */
+#define OP_DMF      001
 #define OP_MT       004
-#define OP_DMT      005     /* MIPS-II, for r4000 port */
+#define OP_DMT      005
 #define OP_BCx      010
 #define OP_BCy      014
 #define OP_CF       002
@@ -311,6 +405,6 @@ typedef union {
 #define COPz_BC_TF_MASK 0x01
 #define COPz_BC_TRUE    0x01
 #define COPz_BC_FALSE   0x00
-#define COPz_BCL_TF_MASK    0x02        /* MIPS-II, for r4000 port */
-#define COPz_BCL_TRUE   0x02        /* MIPS-II, for r4000 port */
-#define COPz_BCL_FALSE  0x00        /* MIPS-II, for r4000 port */
+#define COPz_BCL_TF_MASK    0x02
+#define COPz_BCL_TRUE   0x02
+#define COPz_BCL_FALSE  0x00
index d43655c..d45dabc 100644 (file)
@@ -42,7 +42,7 @@ inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) {
 #else
 
 inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) {
-#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
+#if defined(__mips__) && __mips_isa_rev>=2
     uint32_t r;
     __asm__("wsbh %0, %1;"
         "rotr %0, %0, 16"
@@ -55,7 +55,7 @@ inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) {
 #endif
 }
 inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) {
-#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
+#if defined(__mips__) && __mips_isa_rev>=2
     uint32_t r;
     __asm__("wsbh %0, %1;"
         "rotr %0, %0, 16"
@@ -234,7 +234,7 @@ enum {
 
 // ----------------------------------------------------------------------------
 
-class needs_filter_t;
+struct needs_filter_t;
 struct needs_t {
     inline int match(const needs_filter_t& filter);
     inline bool operator == (const needs_t& rhs) const {
index 787f620..17b85dd 100644 (file)
@@ -520,6 +520,252 @@ inline int64_t gglMulii(int32_t x, int32_t y)
     return res;
 }
 
+#elif defined(__mips__) && __mips_isa_rev == 6
+
+/*inline MIPS implementations*/
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) {
+    GGLfixed result,tmp,tmp1,tmp2;
+
+    if (__builtin_constant_p(shift)) {
+        if (shift == 0) {
+            asm ("mul %[res], %[a], %[b] \t\n"
+            : [res]"=&r"(result)
+            : [a]"r"(a),[b]"r"(b)
+            );
+        } else if (shift == 32)
+        {
+            asm ("mul %[res], %[a], %[b] \t\n"
+            "li  %[tmp],1\t\n"
+            "sll  %[tmp],%[tmp],0x1f\t\n"
+            "addu %[tmp1],%[tmp],%[res] \t\n"
+            "muh %[res], %[a], %[b] \t\n"
+            "sltu %[tmp1],%[tmp1],%[tmp]\t\n"   /*obit*/
+            "sra %[tmp],%[tmp],0x1f \t\n"
+            "addu %[res],%[res],%[tmp]\t\n"
+            "addu %[res],%[res],%[tmp1]\t\n"
+            : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1)
+            : [a]"r"(a),[b]"r"(b),[shift]"I"(shift)
+            );
+        } else if ((shift >0) && (shift < 32))
+        {
+            asm ("mul %[res], %[a], %[b] \t\n"
+            "li  %[tmp],1 \t\n"
+            "sll  %[tmp],%[tmp],%[shiftm1] \t\n"
+            "addu %[tmp1],%[tmp],%[res] \t\n"
+            "sltu %[tmp1],%[tmp1],%[tmp] \t\n"  /*obit?*/
+            "addu  %[res],%[res],%[tmp] \t\n"
+            "muh %[tmp], %[a], %[b] \t\n"
+            "addu  %[tmp],%[tmp],%[tmp1] \t\n"
+            "sll   %[tmp],%[tmp],%[lshift] \t\n"
+            "srl   %[res],%[res],%[rshift]    \t\n"
+            "or    %[res],%[res],%[tmp] \t\n"
+            : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+            : [a]"r"(a),[b]"r"(b),[lshift]"I"(32-shift),[rshift]"I"(shift),[shiftm1]"I"(shift-1)
+            );
+        } else {
+            asm ("mul %[res], %[a], %[b] \t\n"
+            "li  %[tmp],1 \t\n"
+            "sll  %[tmp],%[tmp],%[shiftm1] \t\n"
+            "addu %[tmp1],%[tmp],%[res] \t\n"
+            "sltu %[tmp1],%[tmp1],%[tmp] \t\n"  /*obit?*/
+            "sra  %[tmp2],%[tmp],0x1f \t\n"
+            "addu  %[res],%[res],%[tmp] \t\n"
+            "muh  %[tmp], %[a], %[b]   \t\n"
+            "addu  %[tmp],%[tmp],%[tmp2] \t\n"
+            "addu  %[tmp],%[tmp],%[tmp1] \t\n"            /*tmp=hi*/
+            "srl   %[tmp2],%[res],%[rshift]    \t\n"
+            "srav  %[res], %[tmp],%[rshift]\t\n"
+            "sll   %[tmp],%[tmp],1 \t\n"
+            "sll   %[tmp],%[tmp],%[norbits] \t\n"
+            "or    %[tmp],%[tmp],%[tmp2] \t\n"
+            "seleqz  %[tmp],%[tmp],%[bit5] \t\n"
+            "selnez  %[res],%[res],%[bit5] \t\n"
+            "or    %[res],%[res],%[tmp] \t\n"
+            : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+            : [a]"r"(a),[b]"r"(b),[norbits]"I"(~(shift)),[rshift]"I"(shift),[shiftm1] "I"(shift-1),[bit5]"I"(shift & 0x20)
+            );
+        }
+    } else {
+        asm ("mul %[res], %[a], %[b] \t\n"
+        "li  %[tmp],1 \t\n"
+        "sll  %[tmp],%[tmp],%[shiftm1] \t\n"
+        "addu %[tmp1],%[tmp],%[res] \t\n"
+        "sltu %[tmp1],%[tmp1],%[tmp] \t\n"  /*obit?*/
+        "sra  %[tmp2],%[tmp],0x1f \t\n"
+        "addu  %[res],%[res],%[tmp] \t\n"
+        "muh  %[tmp], %[a], %[b] \t\n"
+        "addu  %[tmp],%[tmp],%[tmp2] \t\n"
+        "addu  %[tmp],%[tmp],%[tmp1] \t\n"            /*tmp=hi*/
+        "srl   %[tmp2],%[res],%[rshift]    \t\n"
+        "srav  %[res], %[tmp],%[rshift]\t\n"
+        "sll   %[tmp],%[tmp],1 \t\n"
+        "sll   %[tmp],%[tmp],%[norbits] \t\n"
+        "or    %[tmp],%[tmp],%[tmp2] \t\n"
+        "seleqz  %[tmp],%[tmp],%[bit5] \t\n"
+        "selnez  %[res],%[res],%[bit5] \t\n"
+        "or    %[res],%[res],%[tmp] \t\n"
+         : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+         : [a]"r"(a),[b]"r"(b),[norbits]"r"(~(shift)),[rshift] "r"(shift),[shiftm1]"r"(shift-1),[bit5] "r"(shift & 0x20)
+         );
+        }
+        return result;
+}
+
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+    GGLfixed result,t,tmp1,tmp2;
+
+    if (__builtin_constant_p(shift)) {
+        if (shift == 0) {
+                 asm ("mul %[lo], %[a], %[b] \t\n"
+                 "addu  %[lo],%[lo],%[c]    \t\n"
+                 : [lo]"=&r"(result)
+                 : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+                 );
+                } else if (shift == 32) {
+                    asm ("muh %[lo], %[a], %[b] \t\n"
+                    "addu  %[lo],%[lo],%[c]    \t\n"
+                    : [lo]"=&r"(result)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+                    );
+                } else if ((shift>0) && (shift<32)) {
+                    asm ("mul %[res], %[a], %[b] \t\n"
+                    "muh  %[t], %[a], %[b] \t\n"
+                    "srl   %[res],%[res],%[rshift]    \t\n"
+                    "sll   %[t],%[t],%[lshift]     \t\n"
+                    "or  %[res],%[res],%[t]    \t\n"
+                    "addu  %[res],%[res],%[c]    \t\n"
+                    : [res]"=&r"(result),[t]"=&r"(t)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift)
+                    );
+                } else {
+                    asm ("mul %[res], %[a], %[b] \t\n"
+                    "muh %[t], %[a], %[b] \t\n"
+                    "nor %[tmp1],$zero,%[shift]\t\n"
+                    "srl   %[res],%[res],%[shift]    \t\n"
+                    "sll   %[tmp2],%[t],1     \t\n"
+                    "sllv  %[tmp2],%[tmp2],%[tmp1]     \t\n"
+                    "or  %[tmp1],%[tmp2],%[res]    \t\n"
+                    "srav  %[res],%[t],%[shift]     \t\n"
+                    "andi %[tmp2],%[shift],0x20\t\n"
+                    "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+                    "selnez %[res],%[res],%[tmp2]\t\n"
+                    "or %[res],%[res],%[tmp1]\t\n"
+                    "addu  %[res],%[res],%[c]    \t\n"
+                    : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift)
+                    );
+                }
+            } else {
+                asm ("mul %[res], %[a], %[b] \t\n"
+                "muh %[t], %[a], %[b] \t\n"
+                "nor %[tmp1],$zero,%[shift]\t\n"
+                "srl   %[res],%[res],%[shift]    \t\n"
+                "sll   %[tmp2],%[t],1     \t\n"
+                "sllv  %[tmp2],%[tmp2],%[tmp1]     \t\n"
+                "or  %[tmp1],%[tmp2],%[res]    \t\n"
+                "srav  %[res],%[t],%[shift]     \t\n"
+                "andi %[tmp2],%[shift],0x20\t\n"
+                "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+                "selnez %[res],%[res],%[tmp2]\t\n"
+                "or %[res],%[res],%[tmp1]\t\n"
+                "addu  %[res],%[res],%[c]    \t\n"
+                : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+                : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift)
+                );
+            }
+            return result;
+}
+
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+    GGLfixed result,t,tmp1,tmp2;
+
+    if (__builtin_constant_p(shift)) {
+        if (shift == 0) {
+                 asm ("mul %[lo], %[a], %[b] \t\n"
+                 "subu  %[lo],%[lo],%[c]    \t\n"
+                 : [lo]"=&r"(result)
+                 : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+                 );
+                } else if (shift == 32) {
+                    asm ("muh %[lo], %[a], %[b] \t\n"
+                    "subu  %[lo],%[lo],%[c]    \t\n"
+                    : [lo]"=&r"(result)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+                    );
+                } else if ((shift>0) && (shift<32)) {
+                    asm ("mul %[res], %[a], %[b] \t\n"
+                    "muh %[t], %[a], %[b] \t\n"
+                    "srl   %[res],%[res],%[rshift]    \t\n"
+                    "sll   %[t],%[t],%[lshift]     \t\n"
+                    "or  %[res],%[res],%[t]    \t\n"
+                    "subu  %[res],%[res],%[c]    \t\n"
+                    : [res]"=&r"(result),[t]"=&r"(t)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift)
+                    );
+                } else {
+                    asm ("mul %[res], %[a], %[b] \t\n"
+                    "muh %[t], %[a], %[b] \t\n"
+                    "nor %[tmp1],$zero,%[shift]\t\n"
+                    "srl   %[res],%[res],%[shift]    \t\n"
+                    "sll   %[tmp2],%[t],1     \t\n"
+                    "sllv  %[tmp2],%[tmp2],%[tmp1]     \t\n"
+                    "or  %[tmp1],%[tmp2],%[res]    \t\n"
+                    "srav  %[res],%[t],%[shift]     \t\n"
+                    "andi %[tmp2],%[shift],0x20\t\n"
+                    "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+                    "selnez %[res],%[res],%[tmp2]\t\n"
+                    "or %[res],%[res],%[tmp1]\t\n"
+                    "subu  %[res],%[res],%[c]    \t\n"
+                    : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift)
+                     );
+                    }
+                } else {
+                asm ("mul %[res], %[a], %[b] \t\n"
+                "muh %[t], %[a], %[b] \t\n"
+                "nor %[tmp1],$zero,%[shift]\t\n"
+                "srl   %[res],%[res],%[shift]    \t\n"
+                "sll   %[tmp2],%[t],1     \t\n"
+                "sllv  %[tmp2],%[tmp2],%[tmp1]     \t\n"
+                "or  %[tmp1],%[tmp2],%[res]    \t\n"
+                "srav  %[res],%[t],%[shift]     \t\n"
+                "andi %[tmp2],%[shift],0x20\t\n"
+                "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+                "selnez %[res],%[res],%[tmp2]\t\n"
+                "or %[res],%[res],%[tmp1]\t\n"
+                "subu  %[res],%[res],%[c]    \t\n"
+                : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+                : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift)
+                );
+            }
+    return result;
+}
+
+inline int64_t gglMulii(int32_t x, int32_t y) CONST;
+inline int64_t gglMulii(int32_t x, int32_t y) {
+    union {
+        struct {
+#if defined(__MIPSEL__)
+            int32_t lo;
+            int32_t hi;
+#elif defined(__MIPSEB__)
+            int32_t hi;
+            int32_t lo;
+#endif
+        } s;
+        int64_t res;
+    }u;
+    asm("mul %0, %2, %3 \t\n"
+        "muh %1, %2, %3 \t\n"
+        : "=r"(u.s.lo), "=&r"(u.s.hi)
+        : "%r"(x), "r"(y)
+        );
+    return u.res;
+}
+
 #else // ----------------------------------------------------------------------
 
 inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
index 3d14531..a718b02 100644 (file)
@@ -41,6 +41,8 @@
 #include "codeflinger/Arm64Assembler.h"
 #elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
 #include "codeflinger/MIPSAssembler.h"
+#elif defined(__mips__) && defined(__LP64__)
+#include "codeflinger/MIPS64Assembler.h"
 #endif
 //#include "codeflinger/ARMAssemblerOptimizer.h"
 
@@ -59,7 +61,7 @@
 #   define ANDROID_CODEGEN      ANDROID_CODEGEN_GENERATED
 #endif
 
-#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)
+#if defined(__arm__) || (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))) || defined(__aarch64__)
 #   define ANDROID_ARM_CODEGEN  1
 #else
 #   define ANDROID_ARM_CODEGEN  0
@@ -73,7 +75,7 @@
  */
 #define DEBUG_NEEDS  0
 
-#if defined( __mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined( __mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))
 #define ASSEMBLY_SCRATCH_SIZE   4096
 #elif defined(__aarch64__)
 #define ASSEMBLY_SCRATCH_SIZE   8192
@@ -136,6 +138,9 @@ extern "C" void scanline_t32cb16blend_arm64(uint16_t*, uint32_t*, size_t);
 extern "C" void scanline_col32cb16blend_arm64(uint16_t *dst, uint32_t col, size_t ct);
 #elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
 extern "C" void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t);
+#elif defined(__mips__) && defined(__LP64__)
+extern "C" void scanline_t32cb16blend_mips64(uint16_t*, uint32_t*, size_t);
+extern "C" void scanline_col32cb16blend_mips64(uint16_t *dst, uint32_t col, size_t ct);
 #endif
 
 // ----------------------------------------------------------------------------
@@ -286,7 +291,7 @@ static  const needs_filter_t fill16noblend = {
 
 #if ANDROID_ARM_CODEGEN
 
-#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))
 static CodeCache gCodeCache(32 * 1024);
 #elif defined(__aarch64__)
 static CodeCache gCodeCache(48 * 1024);
@@ -406,8 +411,10 @@ static void pick_scanline(context_t* c)
         //GGLAssembler assembler(
         //        new ARMAssemblerOptimizer(new ARMAssembler(a)) );
 #endif
-#if defined(__mips__)
+#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
         GGLAssembler assembler( new ArmToMipsAssembler(a) );
+#elif defined(__mips__) && defined(__LP64__)
+        GGLAssembler assembler( new ArmToMips64Assembler(a) );
 #elif defined(__aarch64__)
         GGLAssembler assembler( new ArmToArm64Assembler(a) );
 #endif
@@ -2103,6 +2110,8 @@ void scanline_col32cb16blend(context_t* c)
 #endif // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
 #elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__aarch64__))
     scanline_col32cb16blend_arm64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
+#elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__mips__) && defined(__LP64__)))
+    scanline_col32cb16blend_mips64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
 #else
     uint32_t s = GGL_RGBA_TO_HOST(c->packed8888);
     int sA = (s>>24);
@@ -2175,7 +2184,8 @@ last_one:
 
 void scanline_t32cb16blend(context_t* c)
 {
-#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)))
+#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || defined(__aarch64__) || \
+    (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__)))))
     int32_t x = c->iterators.xl;
     size_t ct = c->iterators.xr - x;
     int32_t y = c->iterators.y;
@@ -2191,8 +2201,10 @@ void scanline_t32cb16blend(context_t* c)
     scanline_t32cb16blend_arm(dst, src, ct);
 #elif defined(__aarch64__)
     scanline_t32cb16blend_arm64(dst, src, ct);
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
     scanline_t32cb16blend_mips(dst, src, ct);
+#elif defined(__mips__) && defined(__LP64__)
+    scanline_t32cb16blend_mips64(dst, src, ct);
 #endif
 #else
     dst_iterator16  di(c);
index caf9eb7..1d40ad4 100644 (file)
@@ -17,6 +17,7 @@
 
 
        .text
+       .syntax unified
        .align
        
        .global scanline_t32cb16blend_arm
@@ -146,7 +147,7 @@ scanline_t32cb16blend_arm:
     tst     r0, #0x3
     beq     aligned
     subs    r2, r2, #1
-    ldmlofd    sp!, {r4-r7, lr}        // return
+    ldmfdlo sp!, {r4-r7, lr}        // return
     bxlo    lr
 
 last:
@@ -197,6 +198,6 @@ aligned:
     mov     r4, r5
 
 9:  adds    r2, r2, #1
-    ldmlofd sp!, {r4-r7, lr}        // return
+    ldmfdlo sp!, {r4-r7, lr}        // return
     bxlo    lr
     b       last
index 448d298..bd0f24b 100644 (file)
@@ -5,9 +5,6 @@ LOCAL_SRC_FILES:= \
     arm64_assembler_test.cpp\
     asm_test_jacket.S
 
-# asm_test_jacket.S does not compile with Clang.
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
-
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
     libpixelflinger
index 5d69203..3368eb0 100644 (file)
@@ -5,8 +5,6 @@ LOCAL_SRC_FILES:= \
     col32cb16blend_test.c \
     ../../../arch-arm64/col32cb16blend.S
 
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
-
 LOCAL_SHARED_LIBRARIES :=
 
 LOCAL_C_INCLUDES :=
index 2c1379b..8e5ec5e 100644 (file)
@@ -5,8 +5,6 @@ LOCAL_SRC_FILES:= \
     t32cb16blend_test.c \
     ../../../arch-arm64/t32cb16blend.S
 
-LOCAL_CLANG_ASFLAGS_arm64 += -no-integrated-as
-
 LOCAL_SHARED_LIBRARIES :=
 
 LOCAL_C_INCLUDES :=
diff --git a/libpixelflinger/tests/arch-mips/Android.mk b/libpixelflinger/tests/arch-mips/Android.mk
new file mode 100644 (file)
index 0000000..fe6979e
--- /dev/null
@@ -0,0 +1,6 @@
+ifeq ($(TARGET_ARCH),mips)
+include $(all-subdir-makefiles)
+endif
+ifeq ($(TARGET_ARCH),mipsel)
+include $(all-subdir-makefiles)
+endif
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
new file mode 100644 (file)
index 0000000..40f197f
--- /dev/null
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    col32cb16blend_test.c \
+    ../../../arch-mips/col32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips-col32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 32
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c
new file mode 100644 (file)
index 0000000..dd0e60f
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+
+#define ARGB_8888_MAX   0xFFFFFFFF
+#define ARGB_8888_MIN   0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+    char name[256];
+    uint32_t src_color;
+    uint16_t dst_color;
+    size_t count;
+};
+
+struct test_t tests[] =
+{
+    {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+    {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+    {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+    {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+    {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+    {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+    {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+    {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+    {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+    {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+};
+
+void scanline_col32cb16blend_mips(uint16_t *dst, uint32_t src, size_t count);
+void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count)
+{
+    uint32_t srcAlpha = (src>>24);
+    uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7));
+
+    while (count--)
+    {
+        uint16_t d = *dst;
+        int dstR = (d>>11)&0x1f;
+        int dstG = (d>>5)&0x3f;
+        int dstB = (d)&0x1f;
+        int srcR = (src >> (   3))&0x1F;
+        int srcG = (src >> ( 8+2))&0x3F;
+        int srcB = (src >> (16+3))&0x1F;
+        srcR += (f*dstR)>>8;
+        srcG += (f*dstG)>>8;
+        srcB += (f*dstB)>>8;
+        *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+    }
+}
+
+void scanline_col32cb16blend_test()
+{
+    uint16_t dst_c[16], dst_asm[16];
+    uint32_t i, j;
+
+    for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+    {
+        struct test_t test = tests[i];
+
+        printf("Testing - %s:",test.name);
+
+        memset(dst_c, 0, sizeof(dst_c));
+        memset(dst_asm, 0, sizeof(dst_asm));
+
+        for(j = 0; j < test.count; ++j)
+        {
+            dst_c[j]   = test.dst_color;
+            dst_asm[j] = test.dst_color;
+        }
+
+
+        scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
+        scanline_col32cb16blend_mips(dst_asm, test.src_color, test.count);
+
+        if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+            printf("Passed\n");
+        else
+            printf("Failed\n");
+
+        for(j = 0; j < test.count; ++j)
+        {
+            printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+        }
+    }
+}
+
+int main()
+{
+    scanline_col32cb16blend_test();
+    return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
new file mode 100644 (file)
index 0000000..d0c0ae4
--- /dev/null
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    t32cb16blend_test.c \
+    ../../../arch-mips/t32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips-t32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 32
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c b/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c
new file mode 100644 (file)
index 0000000..c6d6937
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define ARGB_8888_MAX   0xFFFFFFFF
+#define ARGB_8888_MIN   0x00000000
+#define RGB_565_MAX     0xFFFF
+#define RGB_565_MIN     0x0000
+
+struct test_t
+{
+    char name[256];
+    uint32_t src_color;
+    uint16_t dst_color;
+    size_t count;
+};
+
+struct test_t tests[] =
+{
+    {"Count 0", 0, 0, 0},
+    {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+    {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+    {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+    {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+    {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+    {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+    {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+    {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+    {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+    {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+
+};
+
+void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t);
+void scanline_t32cb16blend_c(uint16_t * dst, uint32_t* src, size_t count)
+{
+    while (count--)
+    {
+        uint16_t d = *dst;
+        uint32_t s = *src++;
+        int dstR = (d>>11)&0x1f;
+        int dstG = (d>>5)&0x3f;
+        int dstB = (d)&0x1f;
+        int srcR = (s >> (   3))&0x1F;
+        int srcG = (s >> ( 8+2))&0x3F;
+        int srcB = (s >> (16+3))&0x1F;
+        int srcAlpha = (s>>24) & 0xFF;
+
+
+        int f = 0x100 - (srcAlpha + ((srcAlpha>>7) & 0x1));
+        srcR += (f*dstR)>>8;
+        srcG += (f*dstG)>>8;
+        srcB += (f*dstB)>>8;
+        // srcR = srcR > 0x1F? 0x1F: srcR;
+        // srcG = srcG > 0x3F? 0x3F: srcG;
+        // srcB = srcB > 0x1F? 0x1F: srcB;
+        *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+    }
+}
+
+void scanline_t32cb16blend_test()
+{
+    uint16_t dst_c[16], dst_asm[16];
+    uint32_t src[16];
+    uint32_t i;
+    uint32_t  j;
+
+    for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+    {
+        struct test_t test = tests[i];
+
+        printf("Testing - %s:",test.name);
+
+        memset(dst_c, 0, sizeof(dst_c));
+        memset(dst_asm, 0, sizeof(dst_asm));
+
+        for(j = 0; j < test.count; ++j)
+        {
+            dst_c[j]   = test.dst_color;
+            dst_asm[j] = test.dst_color;
+            src[j] = test.src_color;
+        }
+
+        scanline_t32cb16blend_c(dst_c,src,test.count);
+        scanline_t32cb16blend_mips(dst_asm,src,test.count);
+
+
+        if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+            printf("Passed\n");
+        else
+            printf("Failed\n");
+
+        for(j = 0; j < test.count; ++j)
+        {
+            printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+        }
+    }
+}
+
+int main()
+{
+    scanline_t32cb16blend_test();
+    return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/Android.mk b/libpixelflinger/tests/arch-mips64/Android.mk
new file mode 100644 (file)
index 0000000..3b1c64e
--- /dev/null
@@ -0,0 +1,3 @@
+ifeq ($(TARGET_ARCH),mips64)
+include $(all-subdir-makefiles)
+endif
diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.mk b/libpixelflinger/tests/arch-mips64/assembler/Android.mk
new file mode 100644 (file)
index 0000000..4699961
--- /dev/null
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    mips64_assembler_test.cpp\
+    asm_mips_test_jacket.S
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libpixelflinger
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/../../..
+
+LOCAL_MODULE:= test-pixelflinger-mips64-assembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
new file mode 100644 (file)
index 0000000..8a7f742
--- /dev/null
@@ -0,0 +1,93 @@
+# /*
+#  * Copyright (C) 2015 The Android Open Source Project
+#  * All rights reserved.
+#  *
+#  * Redistribution and use in source and binary forms, with or without
+#  * modification, are permitted provided that the following conditions
+#  * are met:
+#  *  * Redistributions of source code must retain the above copyright
+#  *    notice, this list of conditions and the following disclaimer.
+#  *  * Redistributions in binary form must reproduce the above copyright
+#  *    notice, this list of conditions and the following disclaimer in
+#  *    the documentation and/or other materials provided with the
+#  *    distribution.
+#  *
+#  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+#  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+#  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+#  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+#  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+#  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+#  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+#  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+#  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+#  * SUCH DAMAGE.
+#  */
+
+    .text
+    .align 8
+
+    .global asm_mips_test_jacket
+
+    # // Set the register
+    # // Calls the asm function
+    # // Reads the register values to output register
+
+    # // Parameters
+    # // a0 - Function to jump
+    # // a1 - register values array
+    # // a2 - flag values array
+asm_mips_test_jacket:
+    # // Save registers to stack
+    daddiu $sp, $sp, -96
+    sd  $s0, 64($sp)
+    sd  $s1, 72($sp)
+    sd  $s2, 80($sp)
+    sd  $ra, 88($sp)
+
+    move $s0, $a0
+    move $s1, $a1
+    move $s2, $a2
+
+    ld  $v0, 16($s1)
+    ld  $v1, 24($s1)
+    ld  $a0, 32($s1)
+    ld  $a1, 40($s1)
+    ld  $a2, 48($s1)
+    ld  $a3, 56($s1)
+    ld  $a4, 64($s1)
+    ld  $a5, 72($s1)
+    ld  $a6, 80($s1)
+    ld  $a7, 88($s1)
+    ld  $t0, 96($s1)
+    ld  $t1, 104($s1)
+    ld  $t2, 112($s1)
+    ld  $t3, 120($s1)
+
+    jal $s0
+
+    sd  $v0, 16($s1)
+    sd  $v1, 24($s1)
+    sd  $a0, 32($s1)
+    sd  $a1, 40($s1)
+    sd  $a2, 48($s1)
+    sd  $a3, 56($s1)
+    sd  $a4, 64($s1)
+    sd  $a5, 72($s1)
+    sd  $a6, 80($s1)
+    sd  $a7, 88($s1)
+    sd  $t0, 96($s1)
+    sd  $t1, 104($s1)
+    sd  $t2, 112($s1)
+    sd  $t3, 120($s1)
+
+    ld  $s0, 64($sp)
+    ld  $s1, 72($sp)
+    ld  $s2, 80($sp)
+    ld  $ra, 88($sp)
+
+    daddiu $sp, $sp, 96
+
+    j   $ra
diff --git a/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp b/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp
new file mode 100644 (file)
index 0000000..2b25223
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+#include <cutils/atomic.h>
+#include <cutils/log.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include "codeflinger/ARMAssemblerInterface.h"
+#include "codeflinger/MIPS64Assembler.h"
+using namespace android;
+
+#define TESTS_DATAOP_ENABLE             1
+#define TESTS_DATATRANSFER_ENABLE       1
+#define ASSEMBLY_SCRATCH_SIZE           4096
+
+void *instrMem;
+uint32_t  instrMemSize = 128 * 1024;
+char     dataMem[8192];
+
+typedef void (*asm_function_t)();
+extern "C" void asm_mips_test_jacket(asm_function_t function,
+                                     int64_t regs[], int32_t flags[]);
+
+#define MAX_32BIT (uint32_t)(((uint64_t)1 << 32) - 1)
+#define MAX_64BIT ((uint64_t)0xFFFFFFFFFFFFFFFF)
+const uint32_t NA = 0;
+const uint32_t NUM_REGS = 32;
+const uint32_t NUM_FLAGS = 16;
+
+enum instr_t
+{
+    INSTR_ADD,
+    INSTR_SUB,
+    INSTR_AND,
+    INSTR_ORR,
+    INSTR_RSB,
+    INSTR_BIC,
+    INSTR_CMP,
+    INSTR_MOV,
+    INSTR_MVN,
+    INSTR_MUL,
+    INSTR_MLA,
+    INSTR_SMULBB,
+    INSTR_SMULBT,
+    INSTR_SMULTB,
+    INSTR_SMULTT,
+    INSTR_SMULWB,
+    INSTR_SMULWT,
+    INSTR_SMLABB,
+    INSTR_UXTB16,
+    INSTR_UBFX,
+    INSTR_ADDR_ADD,
+    INSTR_ADDR_SUB,
+    INSTR_LDR,
+    INSTR_LDRB,
+    INSTR_LDRH,
+    INSTR_ADDR_LDR,
+    INSTR_LDM,
+    INSTR_STR,
+    INSTR_STRB,
+    INSTR_STRH,
+    INSTR_ADDR_STR,
+    INSTR_STM
+};
+
+enum shift_t
+{
+    SHIFT_LSL,
+    SHIFT_LSR,
+    SHIFT_ASR,
+    SHIFT_ROR,
+    SHIFT_NONE
+};
+
+enum offset_t
+{
+    REG_SCALE_OFFSET,
+    REG_OFFSET,
+    IMM8_OFFSET,
+    IMM12_OFFSET,
+    NO_OFFSET
+};
+
+enum cond_t
+{
+    EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV,
+    HS = CS,
+    LO = CC
+};
+
+const char * cc_code[] =
+{
+    "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC",
+    "HI", "LS","GE","LT", "GT", "LE", "AL", "NV"
+};
+
+struct condTest_t
+{
+    int     mode;
+    int32_t Rcond1;
+    int32_t Rcond2;
+    uint64_t Rcond1Value;
+    uint64_t Rcond2Value;
+};
+
+
+struct dataOpTest_t
+{
+    uint32_t   id;
+    instr_t    op;
+    condTest_t preCond;
+    cond_t     cond;
+    bool       setFlags;
+    uint64_t   RnValue;
+    uint64_t   RsValue;
+    bool       immediate;
+    uint32_t   immValue;
+    uint64_t   RmValue;
+    uint32_t   shiftMode;
+    uint32_t   shiftAmount;
+    uint64_t   RdValue;
+    bool       checkRd;
+    uint64_t   postRdValue;
+};
+
+struct dataTransferTest_t
+{
+    uint32_t id;
+    instr_t  op;
+    uint32_t preFlag;
+    cond_t   cond;
+    bool     setMem;
+    uint64_t memOffset;
+    uint64_t memValue;
+    uint64_t RnValue;
+    offset_t offsetType;
+    uint64_t RmValue;
+    uint32_t immValue;
+    bool     writeBack;
+    bool     preIndex;
+    bool     postIndex;
+    uint64_t RdValue;
+    uint64_t postRdValue;
+    uint64_t postRnValue;
+    bool     checkMem;
+    uint64_t postMemOffset;
+    uint32_t postMemLength;
+    uint64_t postMemValue;
+};
+
+
+dataOpTest_t dataOpTests [] =
+{
+     {0xA000,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0},
+     {0xA001,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,MAX_64BIT},
+     {0xA002,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,NA,NA,NA,1,0},
+     {0xA003,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT-1,NA,NA,NA,1,MAX_64BIT},
+     {0xA004,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL, 0,NA,1,0},
+     {0xA005,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001},
+     {0xA006,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,2},
+     {0xA007,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,2},
+     {0xA008,INSTR_ADD,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+     {0xA009,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0},
+     {0xA010,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,0,0,0,NA,1,1},
+     {0xA011,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,0,0,0,NA,1,0},
+     {0xA012,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,1},
+     {0xA013,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,0},
+     {0xA014,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,1},
+     {0xA015,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0},
+     {0xA016,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+     {0xA017,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+     {0xA018,INSTR_AND,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,0},
+     {0xA019,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_ASR,31,NA,1,1},
+     {0xA020,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,1,MAX_32BIT,0,0,0,NA,1,MAX_64BIT},
+     {0xA021,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,1,MAX_32BIT-1,0,0,0,NA,1,MAX_64BIT-1},
+     {0xA022,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,0,0,MAX_32BIT,0,0,NA,1,MAX_64BIT},
+     {0xA023,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,0,0,MAX_32BIT-1,0,0,NA,1,MAX_64BIT-1},
+     {0xA024,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,MAX_64BIT},
+     {0xA025,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001},
+     {0xA026,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+     {0xA027,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+     {0xA028,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+     {0xA029,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,MAX_64BIT},
+     {0xA030,INSTR_CMP,{0,0,0,0,0},AL,1,0x10000,NA,1,0x10000,0,0,0,NA,0,0},
+     {0xA031,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x10000,0,0,0x10000,0,0,NA,1,0},
+     {0xA032,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x1000,0,0,0x10000,0,0,NA,1,0x10000000},
+     {0xA033,INSTR_MUL,{0,0,0,0,0},AL,0,0,MAX_32BIT,0,0,1,0,0,NA,1,MAX_64BIT},
+     {0xA034,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x10000,0,0,0x10000,0,0,NA,1,0x10000},
+     {0xA035,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x1000,0,0,0x10000,0,0,NA,1,0x10010000},
+     {0xA036,INSTR_SUB,{1,R_v1,R_a6,2,4},MI,0,2,NA,0,NA,1,NA,NA,2,1,1},
+     {0xA037,INSTR_SUB,{2,R_v1,R_a6,2,0},MI,0,2,NA,0,NA,1,NA,NA,2,1,2},
+     {0xA038,INSTR_SUB,{1,R_v1,R_a6,4,2},GE,0,2,NA,1,1,NA,NA,NA,2,1,1},
+     {0xA039,INSTR_SUB,{1,R_a5,R_a6,2,7},GE,0,2,NA,1,1,NA,NA,NA,2,1,2},
+     {0xA040,INSTR_SUB,{1,R_a5,R_a6,1,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,1},
+     {0xA041,INSTR_SUB,{1,R_a5,R_a6,0,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,2},
+     {0xA042,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1<< 16,0,0,0,NA,1,(uint64_t)(1 -(1<<16))},
+     {0xA043,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,0,0,0,NA,1,MAX_64BIT-1},
+     {0xA044,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1,0,0,0,NA,1,0},
+     {0xA045,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,(uint64_t)(1 -(1<<16))},
+     {0xA046,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,MAX_64BIT-1},
+     {0xA047,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0},
+     {0xA048,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(uint64_t)(1 -(1<<16))},
+     {0xA049,INSTR_SUB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT,SHIFT_LSL,31,NA,1,1},
+     {0xA050,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0},
+     {0xA051,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0},
+     {0xA052,INSTR_RSB,{1,R_a5,R_a6,4,1},GE,0,2,NA,1,0,NA,NA,NA,2,1,(uint64_t)-2},
+     {0xA053,INSTR_RSB,{1,R_a5,R_a6,-1,1},GE,0,2,NA,1,0,NA,NA,NA,2,1,2},
+     {0xA054,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1<<16,NA,NA,NA,NA,1,(1<<16)-1},
+     {0xA055,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,NA,NA,NA,NA,1,(uint64_t)(1-MAX_64BIT)},
+     {0xA056,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1,NA,NA,NA,NA,1,0},
+     {0xA057,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,(1<<16)-1},
+     {0xA058,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,(uint64_t)(1-MAX_64BIT)},
+     {0xA059,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0},
+     {0xA060,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(1<<16)-1},
+     {0xA061,INSTR_RSB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,(uint64_t)(-1)},
+     {0xA062,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0},
+     {0xA063,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0},
+     {0xA064,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,1,0x80000001,NA,NA,NA,NA,1,0xFFFFFFFF80000001},
+     {0xA065,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,0,0,NA,1,0xFFFFFFFF80000001},
+     {0xA066,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,NA,1,MAX_64BIT-1},
+     {0xA067,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000000},
+     {0xA068,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+     {0xA069,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+     {0xA070,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+     {0xA071,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_64BIT ,SHIFT_ASR,31,NA,1,MAX_64BIT},
+     {0xA072,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ROR,1,NA,1,0xFFFFFFFF80000001},
+     {0xA073,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,SHIFT_ROR,31,NA,1,3},
+     {0xA074,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,MAX_64BIT -1,SHIFT_ASR,1,NA,1,MAX_64BIT},
+     {0xA075,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+     {0xA076,INSTR_MOV,{2,R_a5,R_a6,6,8},MI,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+     {0xA077,INSTR_MOV,{2,R_a5,R_a6,-4,-8},MI,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001},
+     {0xA078,INSTR_MOV,{1,R_a5,R_a6,-1,-1},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+     {0xA079,INSTR_MOV,{1,R_a5,R_a6,-1,1},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+     {0xA080,INSTR_MOV,{1,R_a5,R_a6,-1,-5},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1},
+     {0xA081,INSTR_MOV,{1,R_a5,R_a6,5,5},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000},
+     {0xA082,INSTR_MOV,{1,R_a5,R_a6,-1,1},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,2},
+     {0xA083,INSTR_MOV,{1,R_a5,R_a6,4,1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2},
+     {0xA084,INSTR_MOV,{1,R_a5,R_a6,-1,-1},LE,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+     {0xA085,INSTR_MOV,{1,R_a5,R_a6,-1,1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000},
+     {0xA086,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+     {0xA087,INSTR_MOV,{1,R_a5,R_a6,-1,-3},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+     {0xA088,INSTR_MOV,{1,R_a5,R_a6,-1,0},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+     {0xA089,INSTR_MOV,{1,R_a5,R_a6,-1,-1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2},
+     {0xA090,INSTR_MOV,{1,R_a5,R_a6,6,1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001},
+     {0xA091,INSTR_MOV,{1,R_a5,R_a6,-1,1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2},
+     {0xA092,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2},
+     {0xA093,INSTR_MOV,{1,R_a5,R_a6,4,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1},
+     {0xA094,INSTR_MOV,{1,R_a5,R_a6,-1,1},GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2},
+     {0xA095,INSTR_MOV,{1,R_a5,R_a6,1,-1},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+     {0xA096,INSTR_MOV,{1,R_a5,R_a6,-1,1},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+     {0xA097,INSTR_MVN,{1,R_a5,R_a6,1,4},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,2},
+     {0xA098,INSTR_MVN,{1,R_a5,R_a6,-1,1},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,1},
+     {0xA099,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,1,0,NA,NA,NA,2,1,MAX_64BIT},
+     {0xA100,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,MAX_32BIT-1,NA,0,2,1,1},
+     {0xA101,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,0x80000001,NA,0,2,1,0x7FFFFFFE},
+     {0xA102,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0},
+     {0xA103,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,1},
+     {0xA104,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,0},
+     {0xA105,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,1},
+     {0xA106,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,3,SHIFT_ASR,1,NA,1,0xF0},
+     {0xA107,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0},
+     {0xA108,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA109,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF},
+     {0xA110,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA111,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1},
+     {0xA112,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA113,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF},
+     {0xA114,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA115,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1},
+     {0xA116,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA117,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+     {0xA118,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA119,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1},
+     {0xA120,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA121,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+     {0xA122,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA123,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1},
+     {0xA124,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+     {0xA125,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+     {0xA126,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA127,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0},
+     {0xA128,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+     {0xA129,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+     {0xA130,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA131,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0},
+     {0xA132,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0},
+     {0xA133,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00001000},
+     {0xA134,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+     {0xA135,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,0},
+     {0xA136,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,0,NA,1,0x00CD0001},
+     {0xA137,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,1,NA,1,0x00AB00EF},
+     {0xA138,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,2,NA,1,0x000100CD},
+     {0xA139,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,3,NA,1,0x00EF00AB},
+     {0xA140,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,SHIFT_LSL,1,NA,1,0xD00000001},
+     {0xA141,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0x01,NA,0,NA,0x1,SHIFT_LSL,2,NA,1,0x5},
+     {0xA142,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,NA,0,NA,1,0xD00000000},
+     {0xA143,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xD00000001,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,0xCFFFFFFFF},
+     {0xA144,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x020000,SHIFT_LSR,15,NA,1,0xCFFFFFFFB},
+     {0xA145,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,3,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,1},
+};
+
+dataTransferTest_t dataTransferTests [] =
+{
+    {0xB000,INSTR_LDR,AL,AL,1,24,0xABCDEF0123456789,0,REG_SCALE_OFFSET,24,NA,NA,NA,NA,NA,0x23456789,0,0,NA,NA,NA},
+    {0xB001,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4,1,0,1,NA,0x23456789,4,0,NA,NA,NA},
+    {0xB002,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x23456789,0,0,NA,NA,NA},
+    {0xB003,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,REG_SCALE_OFFSET,4064,NA,NA,NA,NA,NA,0x89,0,0,NA,NA,NA},
+    {0xB004,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,0,0,1,0,NA,0x67,4065,0,NA,NA,NA},
+    {0xB005,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,0,1,0,NA,0x45,4065,0,NA,NA,NA},
+    {0xB006,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,2,0,1,0,NA,0x23,4065,0,NA,NA,NA},
+    {0xB007,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,1,0,1,NA,0x67,4066,0,NA,NA,NA},
+    {0xB008,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x89,0,0,NA,NA,NA},
+    {0xB009,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,IMM8_OFFSET,NA,2,1,0,1,NA,0x6789,2,0,NA,NA,NA},
+    {0xB010,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4064,0,0,1,0,NA,0x6789,0,0,NA,NA,NA},
+    {0xB011,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4066,0,0,1,0,NA,0x2345,0,0,NA,NA,NA},
+    {0xB012,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,0,0,0,0,NA,0x6789,0,0,NA,NA,NA},
+    {0xB013,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,2,NO_OFFSET,NA,0,0,0,0,NA,0x2345,2,0,NA,NA,NA},
+    {0xB014,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,8,1,2,8,0xDEAD23456789BEEF},
+    {0xB015,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,2,8,0xDEAD23456789BEEF},
+    {0xB016,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,0,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+    {0xB017,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,1,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDE89BEEF},
+    {0xB018,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,2,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEF89ADBEEF},
+    {0xB019,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,5,1,0,8,0xDEADBEEFDEAD89EF},
+    {0xB020,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+    {0xB021,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,IMM8_OFFSET,NA,2,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,4072,1,4066,8,0xDEAD6789DEADBEEF},
+    {0xB022,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+};
+
+
+void flushcache()
+{
+    const long base = long(instrMem);
+    const long curr = base + long(instrMemSize);
+    __builtin___clear_cache((char*)base, (char*)curr);
+}
+
+void dataOpTest(dataOpTest_t test, ArmToMips64Assembler *a64asm, uint32_t Rd = R_v1,
+                uint32_t Rn = R_t0, uint32_t Rm = R_t1, uint32_t Rs = R_t2)
+{
+    int64_t  regs[NUM_REGS] = {0};
+    int32_t  flags[NUM_FLAGS] = {0};
+    int64_t  savedRegs[NUM_REGS] = {0};
+    uint32_t i;
+    uint32_t op2;
+
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        regs[i] = i;
+    }
+
+    regs[Rd] = test.RdValue;
+    regs[Rn] = test.RnValue;
+    regs[Rs] = test.RsValue;
+    a64asm->reset();
+    if (test.preCond.mode) {
+        a64asm->set_condition(test.preCond.mode, test.preCond.Rcond1, test.preCond.Rcond2);
+        regs[test.preCond.Rcond1] = test.preCond.Rcond1Value;
+        regs[test.preCond.Rcond2] = test.preCond.Rcond2Value;
+    }
+    a64asm->prolog();
+    if(test.immediate == true)
+    {
+        op2 = a64asm->imm(test.immValue);
+    }
+    else if(test.immediate == false && test.shiftAmount == 0)
+    {
+        op2 = Rm;
+        regs[Rm] = (int64_t)((int32_t)(test.RmValue));
+    }
+    else
+    {
+        op2 = a64asm->reg_imm(Rm, test.shiftMode, test.shiftAmount);
+        regs[Rm] = (int64_t)((int32_t)(test.RmValue));
+    }
+    switch(test.op)
+    {
+    case INSTR_ADD: a64asm->ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_SUB: a64asm->SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_RSB: a64asm->RSB(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_AND: a64asm->AND(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_ORR: a64asm->ORR(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_BIC: a64asm->BIC(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_MUL: a64asm->MUL(test.cond, test.setFlags, Rd,Rm,Rs); break;
+    case INSTR_MLA: a64asm->MLA(test.cond, test.setFlags, Rd,Rm,Rs,Rn); break;
+    case INSTR_CMP: a64asm->CMP(test.cond, Rn,op2); break;
+    case INSTR_MOV: a64asm->MOV(test.cond, test.setFlags,Rd,op2); break;
+    case INSTR_MVN: a64asm->MVN(test.cond, test.setFlags,Rd,op2); break;
+    case INSTR_SMULBB:a64asm->SMULBB(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULBT:a64asm->SMULBT(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULTB:a64asm->SMULTB(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULTT:a64asm->SMULTT(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULWB:a64asm->SMULWB(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULWT:a64asm->SMULWT(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMLABB:a64asm->SMLABB(test.cond, Rd,Rm,Rs,Rn); break;
+    case INSTR_UXTB16:a64asm->UXTB16(test.cond, Rd,Rm,test.shiftAmount); break;
+    case INSTR_ADDR_ADD: a64asm->ADDR_ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_ADDR_SUB: a64asm->ADDR_SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+    default: printf("Error"); return;
+    }
+    a64asm->epilog(0);
+    a64asm->fix_branches();
+    flushcache();
+
+    asm_function_t asm_function = (asm_function_t)(instrMem);
+
+    for(i = 0; i < NUM_REGS; ++i)
+        savedRegs[i] = regs[i];
+
+    asm_mips_test_jacket(asm_function, regs, flags);
+
+    /* Check if all regs except Rd is same */
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        if((i == Rd) || i == 2) continue;
+        if(regs[i] != savedRegs[i])
+        {
+            printf("Test %x failed Reg(%d) tampered Expected(0x%" PRIx64 "),"
+                   "Actual(0x%" PRIx64 ") t\n", test.id, i, savedRegs[i],
+                   regs[i]);
+            exit(0);
+            return;
+        }
+    }
+
+    if(test.checkRd == 1 && regs[Rd] != test.postRdValue)
+    {
+        printf("Test %x failed, Expected(%" PRIx64 "), Actual(%" PRIx64 ")\n",
+               test.id, test.postRdValue, regs[Rd]);
+        exit(0);
+    }
+    else
+    {
+        printf("Test %x passed\n", test.id);
+    }
+}
+
+
+void dataTransferTest(dataTransferTest_t test, ARMAssemblerInterface *a64asm,
+                      uint32_t Rd = R_v1, uint32_t Rn = R_t0,uint32_t Rm = R_t1)
+{
+    int64_t regs[NUM_REGS] = {0};
+    int64_t savedRegs[NUM_REGS] = {0};
+    int32_t flags[NUM_FLAGS] = {0};
+    uint32_t i;
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        regs[i] = i;
+    }
+
+    uint32_t op2;
+
+    regs[Rd] = test.RdValue;
+    regs[Rn] = (uint64_t)(&dataMem[test.RnValue]);
+    regs[Rm] = test.RmValue;
+    flags[test.preFlag] = 1;
+
+    if(test.setMem == true)
+    {
+        unsigned char *mem = (unsigned char *)&dataMem[test.memOffset];
+        uint64_t value = test.memValue;
+        for(int j = 0; j < 8; ++j)
+        {
+            mem[j] = value & 0x00FF;
+            value >>= 8;
+        }
+    }
+    a64asm->reset();
+    a64asm->prolog();
+    if(test.offsetType == REG_SCALE_OFFSET)
+    {
+        op2 = a64asm->reg_scale_pre(Rm);
+    }
+    else if(test.offsetType == REG_OFFSET)
+    {
+        op2 = a64asm->reg_pre(Rm);
+    }
+    else if(test.offsetType == IMM12_OFFSET && test.preIndex == true)
+    {
+        op2 = a64asm->immed12_pre(test.immValue, test.writeBack);
+    }
+    else if(test.offsetType == IMM12_OFFSET && test.postIndex == true)
+    {
+        op2 = a64asm->immed12_post(test.immValue);
+    }
+    else if(test.offsetType == IMM8_OFFSET && test.preIndex == true)
+    {
+        op2 = a64asm->immed8_pre(test.immValue, test.writeBack);
+    }
+    else if(test.offsetType == IMM8_OFFSET && test.postIndex == true)
+    {
+        op2 = a64asm->immed8_post(test.immValue);
+    }
+    else if(test.offsetType == NO_OFFSET)
+    {
+        op2 = a64asm->__immed12_pre(0);
+    }
+    else
+    {
+        printf("Error - Unknown offset\n"); return;
+    }
+
+    switch(test.op)
+    {
+    case INSTR_LDR:  a64asm->LDR(test.cond, Rd,Rn,op2); break;
+    case INSTR_LDRB: a64asm->LDRB(test.cond, Rd,Rn,op2); break;
+    case INSTR_LDRH: a64asm->LDRH(test.cond, Rd,Rn,op2); break;
+    case INSTR_ADDR_LDR: a64asm->ADDR_LDR(test.cond, Rd,Rn,op2); break;
+    case INSTR_STR:  a64asm->STR(test.cond, Rd,Rn,op2); break;
+    case INSTR_STRB: a64asm->STRB(test.cond, Rd,Rn,op2); break;
+    case INSTR_STRH: a64asm->STRH(test.cond, Rd,Rn,op2); break;
+    case INSTR_ADDR_STR: a64asm->ADDR_STR(test.cond, Rd,Rn,op2); break;
+    default: printf("Error"); return;
+    }
+    a64asm->epilog(0);
+    flushcache();
+
+    asm_function_t asm_function = (asm_function_t)(instrMem);
+
+    for(i = 0; i < NUM_REGS; ++i)
+        savedRegs[i] = regs[i];
+
+    asm_mips_test_jacket(asm_function, regs, flags);
+
+    /* Check if all regs except Rd/Rn are same */
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        if(i == Rd || i == Rn || i == R_v0) continue;
+
+        if(regs[i] != savedRegs[i])
+        {
+            printf("Test %x failed Reg(%d) tampered"
+                   " Expected(0x%" PRIx64 "), Actual(0x%" PRIx64 ") t\n",
+                   test.id, i, savedRegs[i], regs[i]);
+            return;
+        }
+    }
+
+    if((uint64_t)regs[Rd] != test.postRdValue)
+    {
+        printf("Test %x failed, "
+               "Expected in Rd(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+               test.id, test.postRdValue, regs[Rd]);
+    }
+    else if((uint64_t)regs[Rn] != (uint64_t)(&dataMem[test.postRnValue]))
+    {
+        printf("Test %x failed, "
+               "Expected in Rn(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+               test.id, test.postRnValue, regs[Rn] - (uint64_t)dataMem);
+    }
+    else if(test.checkMem == true)
+    {
+        unsigned char *addr = (unsigned char *)&dataMem[test.postMemOffset];
+        uint64_t value;
+        value = 0;
+        for(uint32_t j = 0; j < test.postMemLength; ++j)
+            value = (value << 8) | addr[test.postMemLength-j-1];
+        if(value != test.postMemValue)
+        {
+            printf("Test %x failed, "
+                   "Expected in Mem(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+                   test.id, test.postMemValue, value);
+        }
+        else
+        {
+            printf("Test %x passed\n", test.id);
+        }
+    }
+    else
+    {
+        printf("Test %x passed\n", test.id);
+    }
+}
+
+int main(void)
+{
+    uint32_t i;
+
+    /* Allocate memory to store instructions generated by ArmToArm64Assembler */
+    {
+        int fd = ashmem_create_region("code cache", instrMemSize);
+        if(fd < 0) {
+            printf("IF < 0\n");
+            printf("Creating code cache, ashmem_create_region "
+                                "failed with error '%s'", strerror(errno));
+        }
+        instrMem = mmap(NULL, instrMemSize,
+                                    PROT_READ | PROT_WRITE | PROT_EXEC,
+                                MAP_PRIVATE, fd, 0);
+    }
+
+    ArmToMips64Assembler a64asm(instrMem);
+
+    if(TESTS_DATAOP_ENABLE)
+    {
+        printf("Running data processing tests\n");
+        for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i) {
+            dataOpTest(dataOpTests[i], &a64asm);
+        }
+    }
+
+    if(TESTS_DATATRANSFER_ENABLE)
+    {
+        printf("Running data transfer tests\n");
+        for(i = 0; i < sizeof(dataTransferTests)/sizeof(dataTransferTest_t); ++i)
+            dataTransferTest(dataTransferTests[i], &a64asm);
+    }
+
+    return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
new file mode 100644 (file)
index 0000000..7d4177e
--- /dev/null
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    col32cb16blend_test.c \
+    ../../../arch-mips64/col32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips64-col32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c
new file mode 100644 (file)
index 0000000..066eab6
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+
+#define ARGB_8888_MAX   0xFFFFFFFF
+#define ARGB_8888_MIN   0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+    char name[256];
+    uint32_t src_color;
+    uint16_t dst_color;
+    size_t count;
+};
+
+struct test_t tests[] =
+{
+    {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+    {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+    {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+    {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+    {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+    {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+    {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+    {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+    {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+    {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+};
+
+void scanline_col32cb16blend_mips64(uint16_t *dst, uint32_t src, size_t count);
+void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count)
+{
+    uint32_t srcAlpha = (src>>24);
+    uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7));
+
+    while (count--)
+    {
+        uint16_t d = *dst;
+        int dstR = (d>>11)&0x1f;
+        int dstG = (d>>5)&0x3f;
+        int dstB = (d)&0x1f;
+        int srcR = (src >> (   3))&0x1F;
+        int srcG = (src >> ( 8+2))&0x3F;
+        int srcB = (src >> (16+3))&0x1F;
+        srcR += (f*dstR)>>8;
+        srcG += (f*dstG)>>8;
+        srcB += (f*dstB)>>8;
+        *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+    }
+}
+
+void scanline_col32cb16blend_test()
+{
+    uint16_t dst_c[16], dst_asm[16];
+    uint32_t i, j;
+
+    for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+    {
+        struct test_t test = tests[i];
+
+        printf("Testing - %s:",test.name);
+
+        memset(dst_c, 0, sizeof(dst_c));
+        memset(dst_asm, 0, sizeof(dst_asm));
+
+        for(j = 0; j < test.count; ++j)
+        {
+            dst_c[j]   = test.dst_color;
+            dst_asm[j] = test.dst_color;
+        }
+
+
+        scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
+        scanline_col32cb16blend_mips64(dst_asm, test.src_color, test.count);
+
+        if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+            printf("Passed\n");
+        else
+            printf("Failed\n");
+
+        for(j = 0; j < test.count; ++j)
+        {
+            printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+        }
+    }
+}
+
+int main()
+{
+    scanline_col32cb16blend_test();
+    return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.mk b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
new file mode 100644 (file)
index 0000000..4e72b57
--- /dev/null
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    mips64_disassembler_test.cpp \
+    ../../../codeflinger/mips64_disassem.c
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips64-disassembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp b/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp
new file mode 100644 (file)
index 0000000..41f6f3e
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+#include "../../../codeflinger/mips64_disassem.h"
+
+//typedef uint64_t db_addr_t;
+//db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_format);
+
+struct test_table_entry_t
+{
+     uint32_t code;
+     const char *instr;
+};
+
+static test_table_entry_t test_table [] =
+{
+    { 0x00011020, "add\tv0,zero,at"     },
+    { 0x00832820, "add\ta1,a0,v1"       },
+    { 0x00c74020, "add\ta4,a2,a3"       },
+    { 0x012a5820, "add\ta7,a5,a6"       },
+    { 0x258dffff, "addiu\tt1,t0,-1"     },
+    { 0x25cf0004, "addiu\tt3,t2,4"      },
+    { 0x02119021, "addu\ts2,s0,s1"      },
+    { 0x0274a821, "addu\ts5,s3,s4"      },
+    { 0x02d7c024, "and\tt8,s6,s7"       },
+    { 0x333aff00, "andi\tk0,t9,0xff00"  },
+    { 0x3f7cffff, "aui\tgp,k1,-1"       },
+    { 0x3c1dffff, "lui\tsp,0xffff"      },
+    { 0x00e04051, "clo\ta4,a3"          },
+    { 0x01205050, "clz\ta6,a5"          },
+    { 0x016c682c, "dadd\tt1,a7,t0"      },
+    { 0x65cf0008, "daddiu\tt3,t2,8"     },
+    { 0x0211902d, "daddu\ts2,s0,s1"     },
+    { 0x7e741403, "dext\ts4,s3,16,3"    },
+    { 0x7eb6f801, "dextm\ts6,s5,0,64"   },
+    { 0x7ef87c02, "dextu\tt8,s7,48,16"  },
+    { 0x7f3a8207, "dins\tk0,t9,8,9"     },
+    { 0x7f7c0005, "dinsm\tgp,k1,0,33"   },
+    { 0x7fbe0806, "dinsu\ts8,sp,32,2"   },
+    { 0x03e1102e, "dsub\tv0,ra,at"      },
+    { 0x0064282f, "dsubu\ta1,v1,a0"     },
+    { 0x7cc77a00, "ext\ta3,a2,8,16"     },
+    { 0x7d09fc04, "ins\ta5,a4,16,16"    },
+    { 0x00200009, "jr\tat"              },
+    { 0x00201009, "jalr\tv0,at"         },
+    { 0x0020f809, "jalr\tat"            },
+    { 0x8082fff0, "lb\tv0,-16(a0)"      },
+    { 0x916c0008, "lbu\tt0,8(a7)"       },
+    { 0xdfa3ffe8, "ld\tv1,-24(sp)"      },
+    { 0x84850080, "lh\ta1,128(a0)"      },
+    { 0x94c7ff80, "lhu\ta3,-128(a2)"    },
+    { 0x8d09000c, "lw\ta5,12(a4)"       },
+    { 0x9d4bfff4, "lwu\ta7,-12(a6)"     },
+    { 0x00620898, "mul\tat,v1,v0"       },
+    { 0x006208d8, "muh\tat,v1,v0"       },
+    { 0x00620899, "mulu\tat,v1,v0"      },
+    { 0x006208d9, "muhu\tat,v1,v0"      },
+    { 0x00000000, "nop"                 },
+    { 0x02329827, "nor\ts3,s1,s2"       },
+    { 0x0295b025, "or\ts6,s4,s5"        },
+    { 0x36f0ff00, "ori\ts0,s7,0xff00"   },
+    { 0x7c03103b, "rdhwr\tv0,v1"        },
+    { 0x00242a02, "rotr\ta1,a0,8"       },
+    { 0x00c74046, "rotrv\ta4,a3,a2"     },
+    { 0xa12afff0, "sb\ta6,-16(a5)"      },
+    { 0xfd6c0100, "sd\tt0,256(a7)"      },
+    { 0x7c0d7420, "seb\tt2,t1"          },
+    { 0x7c0f8620, "seh\ts0,t3"          },
+    { 0x02329835, "seleqz\ts3,s1,s2"    },
+    { 0x0295b037, "selnez\ts6,s4,s5"    },
+    { 0xa6f84000, "sh\tt8,16384(s7)"    },
+    { 0x0019d100, "sll\tk0,t9,4"        },
+    { 0x037ce804, "sllv\tsp,gp,k1"      },
+    { 0x03df082a, "slt\tat,s8,ra"       },
+    { 0x28430007, "slti\tv1,v0,7"       },
+    { 0x2c850020, "sltiu\ta1,a0,32"     },
+    { 0x00c7402b, "sltu\ta4,a2,a3"      },
+    { 0x00095103, "sra\ta6,a5,4"        },
+    { 0x016c6807, "srav\tt1,t0,a7"      },
+    { 0x000e7a02, "srl\tt3,t2,8"        },
+    { 0x02119006, "srlv\ts2,s1,s0"      },
+    { 0x0274a822, "sub\ts5,s3,s4"       },
+    { 0x02d7c023, "subu\tt8,s6,s7"      },
+    { 0xaf3afffc, "sw\tk0,-4(t9)"       },
+    { 0x7c1be0a0, "wsbh\tgp,k1"         },
+    { 0x03bef826, "xor\tra,sp,s8"       },
+    { 0x3801ffff, "li\tat,0xffff"       },
+    { 0x3843ffff, "xori\tv1,v0,0xffff"  },
+};
+
+struct test_branches_table_entry_t
+{
+     uint32_t code;
+     const char *instr;
+     int16_t offset;
+};
+
+static test_branches_table_entry_t test_branches_table [] = {
+    { 0x1000ffff, "b\t", 0xffff             },
+    { 0x13df0008, "beq\ts8,ra,", 0x8        },
+    { 0x042100ff, "bgez\tat,", 0xff         },
+    { 0x1c40ff00, "bgtz\tv0,", 0xff00       },
+    { 0x18605555, "blez\tv1,", 0x5555       },
+    { 0x0480aaaa, "bltz\ta0,", 0xaaaa       },
+    { 0x14a68888, "bne\ta1,a2,", 0x8888     },
+};
+
+struct test_jump_table_entry_t
+{
+     uint32_t code;
+     const char *instr;
+     int32_t offset;
+};
+
+static test_jump_table_entry_t test_jump_table [] = {
+    { 0x0956ae66, "j\t", 0x156ae66          },
+    { 0x0d56ae66, "jal\t", 0x156ae66        },
+};
+
+int main()
+{
+    char instr[256];
+    uint32_t failed = 0;
+
+    for(uint32_t i = 0; i < sizeof(test_table)/sizeof(test_table_entry_t); ++i)
+    {
+        test_table_entry_t *test;
+        test = &test_table[i];
+        mips_disassem(&test->code, instr, 0);
+        if(strcmp(instr, test->instr) != 0)
+        {
+            printf("Test Failed \n"
+                   "Code     : 0x%0x\n"
+                   "Expected : %s\n"
+                   "Actual   : %s\n", test->code, test->instr, instr);
+            failed++;
+        }
+    }
+    for(uint32_t i = 0; i < sizeof(test_branches_table)/sizeof(test_branches_table_entry_t); ++i)
+    {
+        test_branches_table_entry_t *test;
+        test = &test_branches_table[i];
+        mips_disassem(&test->code, instr, 0);
+        //printf("DBG code address: %lx\n", (uint64_t)(&test->code));
+        uint64_t loc = (uint64_t)test + 4 + (test->offset << 2);
+        //printf("DBG loc: %lx\n", loc);
+        char temp[256], address[16];
+        strcpy(temp, test->instr);
+        sprintf(address, "0x%lx", loc);
+        strcat(temp, address);
+        if(strcmp(instr, temp) != 0)
+        {
+            printf("Test Failed \n"
+                   "Code     : 0x%0x\n"
+                   "Expected : %s\n"
+                   "Actual   : %s\n", test->code, temp, instr);
+            failed++;
+        }
+    }
+    for(uint32_t i = 0; i < sizeof(test_jump_table)/sizeof(test_jump_table_entry_t); ++i)
+    {
+        test_jump_table_entry_t *test;
+        test = &test_jump_table[i];
+        mips_disassem(&test->code, instr, 0);
+        //printf("DBG code address: %lx\n", (uint64_t)(&test->code));
+        uint64_t loc = ((uint64_t)test & 0xfffffffff0000000) | (test->offset << 2);
+        //printf("DBG loc: %lx\n", loc);
+        char temp[256], address[16];
+        strcpy(temp, test->instr);
+        sprintf(address, "0x%08lx", loc);
+        strcat(temp, address);
+        if(strcmp(instr, temp) != 0)
+        {
+            printf("Test Failed \n"
+                   "Code     : 0x%0x\n"
+                   "Expected : '%s'\n"
+                   "Actual   : '%s'\n", test->code, temp, instr);
+            failed++;
+        }
+    }
+    if(failed == 0)
+    {
+        printf("All tests PASSED\n");
+        return 0;
+    }
+    else
+    {
+        printf("%d tests FAILED\n", failed);
+        return -1;
+    }
+}
index 148b6f4..efa6d87 100644 (file)
 #include "codeflinger/ARMAssembler.h"
 #if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
 #include "codeflinger/MIPSAssembler.h"
+#elif defined(__mips__) && defined(__LP64__) && __mips_isa_rev == 6
+#include "codeflinger/MIPS64Assembler.h"
 #endif
 #include "codeflinger/Arm64Assembler.h"
 
-#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)
+#if defined(__arm__) || (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || (defined(__LP64__) && __mips_isa_rev == 6))) || defined(__aarch64__)
 #   define ANDROID_ARM_CODEGEN  1
 #else
 #   define ANDROID_ARM_CODEGEN  0
 #endif
 
-#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || (defined(__LP64__) && __mips_isa_rev == 6))
 #define ASSEMBLY_SCRATCH_SIZE   4096
 #elif defined(__aarch64__)
 #define ASSEMBLY_SCRATCH_SIZE   8192
@@ -58,6 +60,10 @@ static void ggl_test_codegen(uint32_t n, uint32_t p, uint32_t t0, uint32_t t1)
     GGLAssembler assembler( new ArmToMipsAssembler(a) );
 #endif
 
+#if defined(__mips__) && defined(__LP64__) && __mips_isa_rev == 6
+    GGLAssembler assembler( new ArmToMips64Assembler(a) );
+#endif
+
 #if defined(__aarch64__)
     GGLAssembler assembler( new ArmToArm64Assembler(a) );
 #endif
index 80efeff..ea53625 100644 (file)
@@ -563,10 +563,10 @@ void trianglex_small(void* con,
     
     c->init_y(c, miny);
     for (int32_t y = miny; y < maxy; y++) {
-        register int32_t ex0 = ey0;
-        register int32_t ex1 = ey1;
-        register int32_t ex2 = ey2;    
-        register int32_t xl, xr;
+        int32_t ex0 = ey0;
+        int32_t ex1 = ey1;
+        int32_t ex2 = ey2;    
+        int32_t xl, xr;
         for (xl=minx ; xl<maxx ; xl++) {
             if (ex0>0 && ex1>0 && ex2>0)
                 break; // all strictly positive
index 925b98b..c77eba9 100644 (file)
@@ -18,6 +18,7 @@ LOCAL_MODULE := libsparse_host
 LOCAL_STATIC_LIBRARIES := libz
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_CFLAGS := -Werror
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 
index 3e72b57..794cd6b 100644 (file)
@@ -221,7 +221,8 @@ static int merge_bb(struct backed_block_list *bbl,
                }
                break;
        case BACKED_BLOCK_FILE:
-               if (a->file.filename != b->file.filename ||
+               /* Already make sure b->type is BACKED_BLOCK_FILE */
+               if (strcmp(a->file.filename, b->file.filename) ||
                                a->file.offset + a->len != b->file.offset) {
                        return -EINVAL;
                }
@@ -279,7 +280,10 @@ static int queue_bb(struct backed_block_list *bbl, struct backed_block *new_bb)
        }
 
        merge_bb(bbl, new_bb, new_bb->next);
-       merge_bb(bbl, bb, new_bb);
+       if (!merge_bb(bbl, bb, new_bb)) {
+               /* new_bb destroyed, point to retained as last_used */
+               bbl->last_used = bb;
+       }
 
        return 0;
 }
index 95e9b5b..b9b438e 100644 (file)
@@ -40,7 +40,6 @@ int main(int argc, char *argv[])
        int in;
        int out;
        int i;
-       int ret;
        struct sparse_file *s;
 
        if (argc < 3) {
@@ -71,10 +70,12 @@ int main(int argc, char *argv[])
                        exit(-1);
                }
 
-               lseek(out, SEEK_SET, 0);
+               if (lseek(out, 0, SEEK_SET) == -1) {
+                       perror("lseek failed");
+                       exit(EXIT_FAILURE);
+               }
 
-               ret = sparse_file_write(s, out, false, false, false);
-               if (ret < 0) {
+               if (sparse_file_write(s, out, false, false, false) < 0) {
                        fprintf(stderr, "Cannot write output file\n");
                        exit(-1);
                }
index 6ece31d..c70d45f 100755 (executable)
@@ -135,7 +135,7 @@ def main():
           break;
         else:
           crc_bin = FH.read(4)
-          crc = struct.unpack("<I", crc)
+          crc = struct.unpack("<I", crc_bin)
           print("Unverified CRC32 0x%08X" % (crc))
       else:
           print("Unknown chunk type 0x%04X" % (chunk_type), end="")
index cad8a86..50cd9e9 100644 (file)
  * limitations under the License.
  */
 
+#ifndef _LIBSPARSE_SPARSE_CRC32_H_
+#define _LIBSPARSE_SPARSE_CRC32_H_
+
 #include <stdint.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
 
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 9b10293..ec63850 100644 (file)
@@ -18,6 +18,7 @@
 #define _FILE_OFFSET_BITS 64
 #define _LARGEFILE64_SOURCE 1
 
+#include <inttypes.h>
 #include <fcntl.h>
 #include <stdarg.h>
 #include <stdbool.h>
@@ -233,7 +234,7 @@ static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
                        ret = process_raw_chunk(s, chunk_data_size, fd, offset,
                                        chunk_header->chunk_sz, cur_block, crc_ptr);
                        if (ret < 0) {
-                               verbose_error(s->verbose, ret, "data block at %lld", offset);
+                               verbose_error(s->verbose, ret, "data block at %" PRId64, offset);
                                return ret;
                        }
                        return chunk_header->chunk_sz;
@@ -241,7 +242,7 @@ static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
                        ret = process_fill_chunk(s, chunk_data_size, fd,
                                        chunk_header->chunk_sz, cur_block, crc_ptr);
                        if (ret < 0) {
-                               verbose_error(s->verbose, ret, "fill block at %lld", offset);
+                               verbose_error(s->verbose, ret, "fill block at %" PRId64, offset);
                                return ret;
                        }
                        return chunk_header->chunk_sz;
@@ -250,7 +251,7 @@ static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
                                        chunk_header->chunk_sz, cur_block, crc_ptr);
                        if (chunk_data_size != 0) {
                                if (ret < 0) {
-                                       verbose_error(s->verbose, ret, "skip block at %lld", offset);
+                                       verbose_error(s->verbose, ret, "skip block at %" PRId64, offset);
                                        return ret;
                                }
                        }
@@ -258,13 +259,13 @@ static int process_chunk(struct sparse_file *s, int fd, off64_t offset,
                case CHUNK_TYPE_CRC32:
                        ret = process_crc32_chunk(fd, chunk_data_size, *crc_ptr);
                        if (ret < 0) {
-                               verbose_error(s->verbose, -EINVAL, "crc block at %lld",
+                               verbose_error(s->verbose, -EINVAL, "crc block at %" PRId64,
                                                offset);
                                return ret;
                        }
                        return 0;
                default:
-                       verbose_error(s->verbose, -EINVAL, "unknown block %04X at %lld",
+                       verbose_error(s->verbose, -EINVAL, "unknown block %04X at %" PRId64,
                                        chunk_header->chunk_type, offset);
        }
 
index 55cd687..2c409dc 100644 (file)
@@ -50,7 +50,7 @@ public:
     bool isValid() const {
         if (m_fdInitialized) {
             int status = fcntl(m_fd, F_GETFD, 0);
-            if (status == 0)
+            if (status >= 0)
                 return true;
             else
                 return false;
@@ -92,7 +92,7 @@ public:
     bool isValid() const {
         if (m_fdInitialized) {
             int status = fcntl(m_fd, F_GETFD, 0);
-            if (status == 0)
+            if (status >= 0)
                 return true;
             else
                 return false;
index 23a5c59..8c4fd15 100644 (file)
@@ -15,7 +15,6 @@
 LOCAL_PATH:= $(call my-dir)
 
 commonSources:= \
-       BasicHashtable.cpp \
        CallStack.cpp \
        FileMap.cpp \
        JenkinsHash.cpp \
@@ -41,27 +40,19 @@ commonSources:= \
 
 host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -Werror
 
-ifeq ($(HOST_OS),windows)
-ifeq ($(strip $(USE_CYGWIN),),)
-# Under MinGW, ctype.h doesn't need multi-byte support
-host_commonCflags += -DMB_CUR_MAX=1
-endif
-endif
-
 # For the host
 # =====================================================
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES:= $(commonSources)
-ifeq ($(HOST_OS), linux)
-LOCAL_SRC_FILES += Looper.cpp
-endif
-ifeq ($(HOST_OS),darwin)
-LOCAL_CFLAGS += -Wno-unused-parameter
-endif
+LOCAL_SRC_FILES_linux := Looper.cpp
+LOCAL_CFLAGS_darwin := -Wno-unused-parameter
 LOCAL_MODULE:= libutils
 LOCAL_STATIC_LIBRARIES := liblog
 LOCAL_CFLAGS += $(host_commonCflags)
+# Under MinGW, ctype.h doesn't need multi-byte support
+LOCAL_CFLAGS_windows := -DMB_CUR_MAX=1
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_C_INCLUDES += external/safe-iop/include
 include $(BUILD_HOST_STATIC_LIBRARY)
 
@@ -92,7 +83,9 @@ LOCAL_SHARED_LIBRARIES := \
         liblog \
         libdl
 
-LOCAL_MODULE:= libutils
+LOCAL_MODULE := libutils
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
 LOCAL_C_INCLUDES += external/safe-iop/include
 include $(BUILD_STATIC_LIBRARY)
 
@@ -109,21 +102,20 @@ LOCAL_SHARED_LIBRARIES := \
 LOCAL_CFLAGS := -Werror
 LOCAL_C_INCLUDES += external/safe-iop/include
 
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
 include $(BUILD_SHARED_LIBRARY)
 
-# Include subdirectory makefiles
-# ============================================================
-
 include $(CLEAR_VARS)
 LOCAL_MODULE := SharedBufferTest
-LOCAL_STATIC_LIBRARIES := libutils libcutils
+LOCAL_STATIC_LIBRARIES := libutils
 LOCAL_SHARED_LIBRARIES := liblog
 LOCAL_SRC_FILES := SharedBufferTest.cpp
 include $(BUILD_NATIVE_TEST)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := SharedBufferTest
-LOCAL_STATIC_LIBRARIES := libutils libcutils
+LOCAL_STATIC_LIBRARIES := libutils
 LOCAL_SHARED_LIBRARIES := liblog
 LOCAL_SRC_FILES := SharedBufferTest.cpp
 include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libutils/BasicHashtable.cpp b/libutils/BasicHashtable.cpp
deleted file mode 100644 (file)
index 491d9e9..0000000
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "BasicHashtable"
-
-#include <math.h>
-
-#include <utils/Log.h>
-#include <utils/BasicHashtable.h>
-#include <utils/misc.h>
-
-namespace android {
-
-BasicHashtableImpl::BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor,
-        size_t minimumInitialCapacity, float loadFactor) :
-        mBucketSize(entrySize + sizeof(Bucket)), mHasTrivialDestructor(hasTrivialDestructor),
-        mLoadFactor(loadFactor), mSize(0),
-        mFilledBuckets(0), mBuckets(NULL) {
-    determineCapacity(minimumInitialCapacity, mLoadFactor, &mBucketCount, &mCapacity);
-}
-
-BasicHashtableImpl::BasicHashtableImpl(const BasicHashtableImpl& other) :
-        mBucketSize(other.mBucketSize), mHasTrivialDestructor(other.mHasTrivialDestructor),
-        mCapacity(other.mCapacity), mLoadFactor(other.mLoadFactor),
-        mSize(other.mSize), mFilledBuckets(other.mFilledBuckets),
-        mBucketCount(other.mBucketCount), mBuckets(other.mBuckets) {
-    if (mBuckets) {
-        SharedBuffer::bufferFromData(mBuckets)->acquire();
-    }
-}
-
-BasicHashtableImpl::~BasicHashtableImpl()
-{
-}
-
-void BasicHashtableImpl::dispose() {
-    if (mBuckets) {
-        releaseBuckets(mBuckets, mBucketCount);
-    }
-}
-
-void BasicHashtableImpl::clone() {
-    if (mBuckets) {
-        void* newBuckets = allocateBuckets(mBucketCount);
-        copyBuckets(mBuckets, newBuckets, mBucketCount);
-        releaseBuckets(mBuckets, mBucketCount);
-        mBuckets = newBuckets;
-    }
-}
-
-void BasicHashtableImpl::setTo(const BasicHashtableImpl& other) {
-    if (mBuckets) {
-        releaseBuckets(mBuckets, mBucketCount);
-    }
-
-    mCapacity = other.mCapacity;
-    mLoadFactor = other.mLoadFactor;
-    mSize = other.mSize;
-    mFilledBuckets = other.mFilledBuckets;
-    mBucketCount = other.mBucketCount;
-    mBuckets = other.mBuckets;
-
-    if (mBuckets) {
-        SharedBuffer::bufferFromData(mBuckets)->acquire();
-    }
-}
-
-void BasicHashtableImpl::clear() {
-    if (mBuckets) {
-        if (mFilledBuckets) {
-            SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets);
-            if (sb->onlyOwner()) {
-                destroyBuckets(mBuckets, mBucketCount);
-                for (size_t i = 0; i < mBucketCount; i++) {
-                    Bucket& bucket = bucketAt(mBuckets, i);
-                    bucket.cookie = 0;
-                }
-            } else {
-                releaseBuckets(mBuckets, mBucketCount);
-                mBuckets = NULL;
-            }
-            mFilledBuckets = 0;
-        }
-        mSize = 0;
-    }
-}
-
-ssize_t BasicHashtableImpl::next(ssize_t index) const {
-    if (mSize) {
-        while (size_t(++index) < mBucketCount) {
-            const Bucket& bucket = bucketAt(mBuckets, index);
-            if (bucket.cookie & Bucket::PRESENT) {
-                return index;
-            }
-        }
-    }
-    return -1;
-}
-
-ssize_t BasicHashtableImpl::find(ssize_t index, hash_t hash,
-        const void* __restrict__ key) const {
-    if (!mSize) {
-        return -1;
-    }
-
-    hash = trimHash(hash);
-    if (index < 0) {
-        index = chainStart(hash, mBucketCount);
-
-        const Bucket& bucket = bucketAt(mBuckets, size_t(index));
-        if (bucket.cookie & Bucket::PRESENT) {
-            if (compareBucketKey(bucket, key)) {
-                return index;
-            }
-        } else {
-            if (!(bucket.cookie & Bucket::COLLISION)) {
-                return -1;
-            }
-        }
-    }
-
-    size_t inc = chainIncrement(hash, mBucketCount);
-    for (;;) {
-        index = chainSeek(index, inc, mBucketCount);
-
-        const Bucket& bucket = bucketAt(mBuckets, size_t(index));
-        if (bucket.cookie & Bucket::PRESENT) {
-            if ((bucket.cookie & Bucket::HASH_MASK) == hash
-                    && compareBucketKey(bucket, key)) {
-                return index;
-            }
-        }
-        if (!(bucket.cookie & Bucket::COLLISION)) {
-            return -1;
-        }
-    }
-}
-
-size_t BasicHashtableImpl::add(hash_t hash, const void* entry) {
-    if (!mBuckets) {
-        mBuckets = allocateBuckets(mBucketCount);
-    } else {
-        edit();
-    }
-
-    hash = trimHash(hash);
-    for (;;) {
-        size_t index = chainStart(hash, mBucketCount);
-        Bucket* bucket = &bucketAt(mBuckets, size_t(index));
-        if (bucket->cookie & Bucket::PRESENT) {
-            size_t inc = chainIncrement(hash, mBucketCount);
-            do {
-                bucket->cookie |= Bucket::COLLISION;
-                index = chainSeek(index, inc, mBucketCount);
-                bucket = &bucketAt(mBuckets, size_t(index));
-            } while (bucket->cookie & Bucket::PRESENT);
-        }
-
-        uint32_t collision = bucket->cookie & Bucket::COLLISION;
-        if (!collision) {
-            if (mFilledBuckets >= mCapacity) {
-                rehash(mCapacity * 2, mLoadFactor);
-                continue;
-            }
-            mFilledBuckets += 1;
-        }
-
-        bucket->cookie = collision | Bucket::PRESENT | hash;
-        mSize += 1;
-        initializeBucketEntry(*bucket, entry);
-        return index;
-    }
-}
-
-void BasicHashtableImpl::removeAt(size_t index) {
-    edit();
-
-    Bucket& bucket = bucketAt(mBuckets, index);
-    bucket.cookie &= ~Bucket::PRESENT;
-    if (!(bucket.cookie & Bucket::COLLISION)) {
-        mFilledBuckets -= 1;
-    }
-    mSize -= 1;
-    if (!mHasTrivialDestructor) {
-        destroyBucketEntry(bucket);
-    }
-}
-
-void BasicHashtableImpl::rehash(size_t minimumCapacity, float loadFactor) {
-    if (minimumCapacity < mSize) {
-        minimumCapacity = mSize;
-    }
-    size_t newBucketCount, newCapacity;
-    determineCapacity(minimumCapacity, loadFactor, &newBucketCount, &newCapacity);
-
-    if (newBucketCount != mBucketCount || newCapacity != mCapacity) {
-        if (mBuckets) {
-            void* newBuckets;
-            if (mSize) {
-                newBuckets = allocateBuckets(newBucketCount);
-                for (size_t i = 0; i < mBucketCount; i++) {
-                    const Bucket& fromBucket = bucketAt(mBuckets, i);
-                    if (fromBucket.cookie & Bucket::PRESENT) {
-                        hash_t hash = fromBucket.cookie & Bucket::HASH_MASK;
-                        size_t index = chainStart(hash, newBucketCount);
-                        Bucket* toBucket = &bucketAt(newBuckets, size_t(index));
-                        if (toBucket->cookie & Bucket::PRESENT) {
-                            size_t inc = chainIncrement(hash, newBucketCount);
-                            do {
-                                toBucket->cookie |= Bucket::COLLISION;
-                                index = chainSeek(index, inc, newBucketCount);
-                                toBucket = &bucketAt(newBuckets, size_t(index));
-                            } while (toBucket->cookie & Bucket::PRESENT);
-                        }
-                        toBucket->cookie = Bucket::PRESENT | hash;
-                        initializeBucketEntry(*toBucket, fromBucket.entry);
-                    }
-                }
-            } else {
-                newBuckets = NULL;
-            }
-            releaseBuckets(mBuckets, mBucketCount);
-            mBuckets = newBuckets;
-            mFilledBuckets = mSize;
-        }
-        mBucketCount = newBucketCount;
-        mCapacity = newCapacity;
-    }
-    mLoadFactor = loadFactor;
-}
-
-void* BasicHashtableImpl::allocateBuckets(size_t count) const {
-    size_t bytes = count * mBucketSize;
-    SharedBuffer* sb = SharedBuffer::alloc(bytes);
-    LOG_ALWAYS_FATAL_IF(!sb, "Could not allocate %u bytes for hashtable with %u buckets.",
-            uint32_t(bytes), uint32_t(count));
-    void* buckets = sb->data();
-    for (size_t i = 0; i < count; i++) {
-        Bucket& bucket = bucketAt(buckets, i);
-        bucket.cookie = 0;
-    }
-    return buckets;
-}
-
-void BasicHashtableImpl::releaseBuckets(void* __restrict__ buckets, size_t count) const {
-    SharedBuffer* sb = SharedBuffer::bufferFromData(buckets);
-    if (sb->release(SharedBuffer::eKeepStorage) == 1) {
-        destroyBuckets(buckets, count);
-        SharedBuffer::dealloc(sb);
-    }
-}
-
-void BasicHashtableImpl::destroyBuckets(void* __restrict__ buckets, size_t count) const {
-    if (!mHasTrivialDestructor) {
-        for (size_t i = 0; i < count; i++) {
-            Bucket& bucket = bucketAt(buckets, i);
-            if (bucket.cookie & Bucket::PRESENT) {
-                destroyBucketEntry(bucket);
-            }
-        }
-    }
-}
-
-void BasicHashtableImpl::copyBuckets(const void* __restrict__ fromBuckets,
-        void* __restrict__ toBuckets, size_t count) const {
-    for (size_t i = 0; i < count; i++) {
-        const Bucket& fromBucket = bucketAt(fromBuckets, i);
-        Bucket& toBucket = bucketAt(toBuckets, i);
-        toBucket.cookie = fromBucket.cookie;
-        if (fromBucket.cookie & Bucket::PRESENT) {
-            initializeBucketEntry(toBucket, fromBucket.entry);
-        }
-    }
-}
-
-// Table of 31-bit primes where each prime is no less than twice as large
-// as the previous one.  Generated by "primes.py".
-static size_t PRIMES[] = {
-    5,
-    11,
-    23,
-    47,
-    97,
-    197,
-    397,
-    797,
-    1597,
-    3203,
-    6421,
-    12853,
-    25717,
-    51437,
-    102877,
-    205759,
-    411527,
-    823117,
-    1646237,
-    3292489,
-    6584983,
-    13169977,
-    26339969,
-    52679969,
-    105359939,
-    210719881,
-    421439783,
-    842879579,
-    1685759167,
-    0,
-};
-
-void BasicHashtableImpl::determineCapacity(size_t minimumCapacity, float loadFactor,
-        size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity) {
-    LOG_ALWAYS_FATAL_IF(loadFactor <= 0.0f || loadFactor > 1.0f,
-            "Invalid load factor %0.3f.  Must be in the range (0, 1].", loadFactor);
-
-    size_t count = ceilf(minimumCapacity / loadFactor) + 1;
-    size_t i = 0;
-    while (count > PRIMES[i] && i < NELEM(PRIMES)) {
-        i++;
-    }
-    count = PRIMES[i];
-    LOG_ALWAYS_FATAL_IF(!count, "Could not determine required number of buckets for "
-            "hashtable with minimum capacity %u and load factor %0.3f.",
-            uint32_t(minimumCapacity), loadFactor);
-    *outBucketCount = count;
-    *outCapacity = ceilf((count - 1) * loadFactor);
-}
-
-}; // namespace android
index 0bfb520..699da74 100644 (file)
 
 #define LOG_TAG "CallStack"
 
+#include <memory>
+
 #include <utils/CallStack.h>
 #include <utils/Printer.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
-#include <UniquePtr.h>
 
 #include <backtrace/Backtrace.h>
 
@@ -40,7 +41,7 @@ CallStack::~CallStack() {
 void CallStack::update(int32_t ignoreDepth, pid_t tid) {
     mFrameLines.clear();
 
-    UniquePtr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
+    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
     if (!backtrace->Unwind(ignoreDepth)) {
         ALOGW("%s: Failed to unwind callstack.", __FUNCTION__);
     }
index 91e45d8..4f4b889 100644 (file)
@@ -53,6 +53,43 @@ FileMap::FileMap(void)
 {
 }
 
+// Move Constructor.
+FileMap::FileMap(FileMap&& other)
+    : mFileName(other.mFileName), mBasePtr(other.mBasePtr), mBaseLength(other.mBaseLength),
+      mDataOffset(other.mDataOffset), mDataPtr(other.mDataPtr), mDataLength(other.mDataLength)
+#if defined(__MINGW32__)
+      , mFileHandle(other.mFileHandle), mFileMapping(other.mFileMapping)
+#endif
+{
+    other.mFileName = NULL;
+    other.mBasePtr = NULL;
+    other.mDataPtr = NULL;
+#if defined(__MINGW32__)
+    other.mFileHandle = 0;
+    other.mFileMapping = 0;
+#endif
+}
+
+// Move assign operator.
+FileMap& FileMap::operator=(FileMap&& other) {
+    mFileName = other.mFileName;
+    mBasePtr = other.mBasePtr;
+    mBaseLength = other.mBaseLength;
+    mDataOffset = other.mDataOffset;
+    mDataPtr = other.mDataPtr;
+    mDataLength = other.mDataLength;
+    other.mFileName = NULL;
+    other.mBasePtr = NULL;
+    other.mDataPtr = NULL;
+#if defined(__MINGW32__)
+    mFileHandle = other.mFileHandle;
+    mFileMapping = other.mFileMapping;
+    other.mFileHandle = 0;
+    other.mFileMapping = 0;
+#endif
+    return *this;
+}
+
 // Destructor.
 FileMap::~FileMap(void)
 {
index 52c9bb7..ff5d252 100644 (file)
  * should still be quite good.
  **/
 
+#include <stdlib.h>
 #include <utils/JenkinsHash.h>
 
 namespace android {
 
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
 hash_t JenkinsHashWhiten(uint32_t hash) {
     hash += (hash << 3);
     hash ^= (hash >> 11);
@@ -31,6 +35,9 @@ hash_t JenkinsHashWhiten(uint32_t hash) {
 }
 
 uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) {
+    if (size > UINT32_MAX) {
+        abort();
+    }
     hash = JenkinsHashMix(hash, (uint32_t)size);
     size_t i;
     for (i = 0; i < (size & -4); i += 4) {
@@ -47,6 +54,9 @@ uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) {
 }
 
 uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size) {
+    if (size > UINT32_MAX) {
+        abort();
+    }
     hash = JenkinsHashMix(hash, (uint32_t)size);
     size_t i;
     for (i = 0; i < (size & -2); i += 2) {
index b7d28d4..138ce8b 100644 (file)
 
 #include <utils/LinearTransform.h>
 
+// disable sanitize as these functions may intentionally overflow (see comments below).
+// the ifdef can be removed when host builds use clang.
+#if defined(__clang__)
+#define ATTRIBUTE_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define ATTRIBUTE_NO_SANITIZE_INTEGER
+#endif
+
 namespace android {
 
-template<class T> static inline T ABS(T x) { return (x < 0) ? -x : x; }
+// sanitize failure with T = int32_t and x = 0x80000000
+template<class T>
+ATTRIBUTE_NO_SANITIZE_INTEGER
+static inline T ABS(T x) { return (x < 0) ? -x : x; }
 
 // Static math methods involving linear transformations
+// remote sanitize failure on overflow case.
+ATTRIBUTE_NO_SANITIZE_INTEGER
 static bool scale_u64_to_u64(
         uint64_t val,
         uint32_t N,
@@ -109,6 +122,8 @@ static bool scale_u64_to_u64(
     return true;
 }
 
+// at least one known sanitize failure (see comment below)
+ATTRIBUTE_NO_SANITIZE_INTEGER
 static bool linear_transform_s64_to_s64(
         int64_t  val,
         int64_t  basis1,
@@ -172,7 +187,7 @@ static bool linear_transform_s64_to_s64(
         // (scaled_signbit XOR res_signbit)
 
         if (is_neg)
-            scaled = -scaled;
+            scaled = -scaled; // known sanitize failure
         res = scaled + basis2;
 
         if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN)
@@ -250,6 +265,8 @@ template <class T> void LinearTransform::reduce(T* N, T* D) {
 template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D);
 template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D);
 
+// sanitize failure if *N = 0x80000000
+ATTRIBUTE_NO_SANITIZE_INTEGER
 void LinearTransform::reduce(int32_t* N, uint32_t* D) {
     if (N && D && *D) {
         if (*N < 0) {
index 5b0ff3a..b14884b 100644 (file)
 #include <utils/Looper.h>
 #include <utils/Timers.h>
 
-#include <unistd.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <inttypes.h>
+#include <string.h>
 #include <sys/eventfd.h>
+#include <unistd.h>
 
 
 namespace android {
@@ -73,7 +75,8 @@ Looper::Looper(bool allowNonCallbacks) :
         mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
         mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
     mWakeEventFd = eventfd(0, EFD_NONBLOCK);
-    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd.  errno=%d", errno);
+    LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
+                        strerror(errno));
 
     AutoMutex _l(mLock);
     rebuildEpollLocked();
@@ -148,15 +151,15 @@ void Looper::rebuildEpollLocked() {
 
     // Allocate the new epoll instance and register the wake pipe.
     mEpollFd = epoll_create(EPOLL_SIZE_HINT);
-    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
+    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
     struct epoll_event eventItem;
     memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
     eventItem.events = EPOLLIN;
     eventItem.data.fd = mWakeEventFd;
     int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);
-    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance.  errno=%d",
-            errno);
+    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
+                        strerror(errno));
 
     for (size_t i = 0; i < mRequests.size(); i++) {
         const Request& request = mRequests.valueAt(i);
@@ -165,8 +168,8 @@ void Looper::rebuildEpollLocked() {
 
         int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);
         if (epollResult < 0) {
-            ALOGE("Error adding epoll events for fd %d while rebuilding epoll set, errno=%d",
-                    request.fd, errno);
+            ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
+                  request.fd, strerror(errno));
         }
     }
 }
@@ -265,7 +268,7 @@ int Looper::pollInner(int timeoutMillis) {
         if (errno == EINTR) {
             goto Done;
         }
-        ALOGW("Poll failed with an unexpected error, errno=%d", errno);
+        ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
         result = POLL_ERROR;
         goto Done;
     }
@@ -410,7 +413,7 @@ void Looper::wake() {
     ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
     if (nWrite != sizeof(uint64_t)) {
         if (errno != EAGAIN) {
-            ALOGW("Could not write wake signal, errno=%d", errno);
+            ALOGW("Could not write wake signal: %s", strerror(errno));
         }
     }
 }
@@ -474,7 +477,7 @@ int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callb
         if (requestIndex < 0) {
             int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
             if (epollResult < 0) {
-                ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
+                ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
                 return -1;
             }
             mRequests.add(fd, request);
@@ -497,18 +500,18 @@ int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callb
                     // call instead, but that approach carries others disadvantages.
 #if DEBUG_CALLBACKS
                     ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
-                            "being recycled, falling back on EPOLL_CTL_ADD, errno=%d",
-                            this, errno);
+                            "being recycled, falling back on EPOLL_CTL_ADD: %s",
+                            this, strerror(errno));
 #endif
                     epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
                     if (epollResult < 0) {
-                        ALOGE("Error modifying or adding epoll events for fd %d, errno=%d",
-                                fd, errno);
+                        ALOGE("Error modifying or adding epoll events for fd %d: %s",
+                                fd, strerror(errno));
                         return -1;
                     }
                     scheduleEpollRebuildLocked();
                 } else {
-                    ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
+                    ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno));
                     return -1;
                 }
             }
@@ -563,7 +566,7 @@ int Looper::removeFd(int fd, int seq) {
                 // call instead, but that approach carries others disadvantages.
 #if DEBUG_CALLBACKS
                 ALOGD("%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor "
-                        "being closed, errno=%d", this, errno);
+                        "being closed: %s", this, strerror(errno));
 #endif
                 scheduleEpollRebuildLocked();
             } else {
@@ -571,7 +574,7 @@ int Looper::removeFd(int fd, int seq) {
                 // our list of callbacks got out of sync with the epoll set somehow.
                 // We defensively rebuild the epoll set to avoid getting spurious
                 // notifications with nowhere to go.
-                ALOGE("Error removing epoll events for fd %d, errno=%d", fd, errno);
+                ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
                 scheduleEpollRebuildLocked();
                 return -1;
             }
index db07e56..011c302 100644 (file)
 #define LOG_TAG "ProcessCallStack"
 // #define LOG_NDEBUG 0
 
-#include <string.h>
-#include <stdio.h>
 #include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
 
 #include <utils/Log.h>
 #include <utils/Errors.h>
@@ -135,8 +136,8 @@ void ProcessCallStack::update() {
 
     dp = opendir(PATH_SELF_TASK);
     if (dp == NULL) {
-        ALOGE("%s: Failed to update the process's call stacks (errno = %d, '%s')",
-              __FUNCTION__, errno, strerror(errno));
+        ALOGE("%s: Failed to update the process's call stacks: %s",
+              __FUNCTION__, strerror(errno));
         return;
     }
 
@@ -172,8 +173,8 @@ void ProcessCallStack::update() {
 
         ssize_t idx = mThreadMap.add(tid, ThreadInfo());
         if (idx < 0) { // returns negative error value on error
-            ALOGE("%s: Failed to add new ThreadInfo (errno = %zd, '%s')",
-                  __FUNCTION__, idx, strerror(-idx));
+            ALOGE("%s: Failed to add new ThreadInfo: %s",
+                  __FUNCTION__, strerror(-idx));
             continue;
         }
 
@@ -195,8 +196,8 @@ void ProcessCallStack::update() {
               __FUNCTION__, tid, threadInfo.callStack.size());
     }
     if (code != 0) { // returns positive error value on error
-        ALOGE("%s: Failed to readdir from %s (errno = %d, '%s')",
-              __FUNCTION__, PATH_SELF_TASK, -code, strerror(code));
+        ALOGE("%s: Failed to readdir from %s: %s",
+              __FUNCTION__, PATH_SELF_TASK, strerror(code));
     }
 #endif
 
index 947551a..34d75ee 100644 (file)
  * limitations under the License.
  */
 
-#define __STDC_LIMIT_MACROS
-#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include <log/log.h>
-#include <utils/SharedBuffer.h>
 #include <utils/Atomic.h>
 
+#include "SharedBuffer.h"
+
 // ---------------------------------------------------------------------------
 
 namespace android {
index d88fbf3..33a4e0c 100644 (file)
 
 #define __STDC_LIMIT_MACROS
 
-#include <utils/SharedBuffer.h>
-
 #include <gtest/gtest.h>
 
 #include <memory>
 #include <stdint.h>
 
+#include "SharedBuffer.h"
+
 TEST(SharedBufferTest, TestAlloc) {
   EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX), "");
   EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer)), "");
@@ -31,10 +31,10 @@ TEST(SharedBufferTest, TestAlloc) {
   // Check that null is returned, as we are asking for the whole address space.
   android::SharedBuffer* buf =
       android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer) - 1);
-  ASSERT_TRUE(NULL == buf);
+  ASSERT_EQ(nullptr, buf);
 
   buf = android::SharedBuffer::alloc(0);
-  ASSERT_FALSE(NULL == buf);
+  ASSERT_NE(nullptr, buf);
   ASSERT_EQ(0U, buf->size());
   buf->release();
 }
@@ -49,7 +49,7 @@ TEST(SharedBufferTest, TestEditResize) {
   // Make sure we don't die here.
   // Check that null is returned, as we are asking for the whole address space.
   buf = buf->editResize(SIZE_MAX - sizeof(android::SharedBuffer) - 1);
-  ASSERT_TRUE(NULL == buf);
+  ASSERT_EQ(nullptr, buf);
 
   buf = android::SharedBuffer::alloc(10);
   buf = buf->editResize(0);
index 91efdaa..6a5273f 100644 (file)
@@ -25,6 +25,7 @@
 #include <stdio.h>
 #include <ctype.h>
 
+#include "SharedBuffer.h"
 
 namespace android {
 
@@ -165,6 +166,11 @@ String16::~String16()
     SharedBuffer::bufferFromData(mString)->release();
 }
 
+size_t String16::size() const
+{
+    return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1;
+}
+
 void String16::setTo(const String16& other)
 {
     SharedBuffer::bufferFromData(other.mString)->acquire();
index ad65fdb..771d312 100644 (file)
 
 #include <utils/String8.h>
 
+#include <utils/Compat.h>
 #include <utils/Log.h>
 #include <utils/Unicode.h>
-#include <utils/SharedBuffer.h>
 #include <utils/String16.h>
 #include <utils/threads.h>
 
 #include <ctype.h>
 
+#include "SharedBuffer.h"
+
 /*
  * Functions outside android is below the namespace android, since they use
  * functions and constants in android namespace.
@@ -213,6 +215,11 @@ String8::~String8()
     SharedBuffer::bufferFromData(mString)->release();
 }
 
+size_t String8::length() const
+{
+    return SharedBuffer::sizeFromData(mString)-1;
+}
+
 String8 String8::format(const char* fmt, ...)
 {
     va_list args;
index ac3dd98..1fca2b2 100644 (file)
@@ -19,7 +19,7 @@
  * System clock functions.
  */
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 #include <linux/ioctl.h>
 #include <linux/rtc.h>
 #include <utils/Atomic.h>
@@ -29,7 +29,6 @@
 #include <sys/time.h>
 #include <limits.h>
 #include <fcntl.h>
-#include <errno.h>
 #include <string.h>
 
 #include <utils/SystemClock.h>
@@ -108,7 +107,7 @@ static inline void checkTimeStamps(int64_t timestamp,
  */
 int64_t elapsedRealtimeNano()
 {
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     struct timespec ts;
     int result;
     int64_t timestamp;
index 1e014c6..6dda6b5 100644 (file)
@@ -22,6 +22,7 @@
 #include <memory.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #if !defined(_WIN32)
@@ -44,7 +45,7 @@
 
 #include <cutils/sched_policy.h>
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 # define __android_unused
 #else
 # define __android_unused __attribute__((__unused__))
@@ -131,7 +132,7 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
     pthread_attr_init(&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
-#ifdef HAVE_ANDROID_OS  /* valgrind is rejecting RT-priority create reqs */
+#if defined(__ANDROID__)  /* valgrind is rejecting RT-priority create reqs */
     if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {
         // Now that the pthread_t has a method to find the associated
         // android_thread_id_t (pid) from pthread_t, it would be possible to avoid
@@ -160,9 +161,9 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
                     (android_pthread_entry)entryFunction, userData);
     pthread_attr_destroy(&attr);
     if (result != 0) {
-        ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n"
+        ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, %s)\n"
              "(android threadPriority=%d)",
-            entryFunction, result, errno, threadPriority);
+            entryFunction, result, strerror(errno), threadPriority);
         return 0;
     }
 
@@ -175,7 +176,7 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction,
     return 1;
 }
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 static pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread)
 {
     return (pthread_t) thread;
@@ -301,12 +302,10 @@ void androidSetCreateThreadFunc(android_create_thread_fn func)
     gCreateThreadFn = func;
 }
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 int androidSetThreadPriority(pid_t tid, int pri)
 {
     int rc = 0;
-
-#if !defined(_WIN32)
     int lasterr = 0;
 
     if (pri >= ANDROID_PRIORITY_BACKGROUND) {
@@ -324,17 +323,12 @@ int androidSetThreadPriority(pid_t tid, int pri)
     } else {
         errno = lasterr;
     }
-#endif
 
     return rc;
 }
 
 int androidGetThreadPriority(pid_t tid) {
-#if !defined(_WIN32)
     return getpriority(PRIO_PROCESS, tid);
-#else
-    return ANDROID_PRIORITY_NORMAL;
-#endif
 }
 
 #endif
@@ -657,7 +651,7 @@ Thread::Thread(bool canCallJava)
         mLock("Thread::mLock"),
         mStatus(NO_ERROR),
         mExitPending(false), mRunning(false)
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
         , mTid(-1)
 #endif
 {
@@ -727,7 +721,7 @@ int Thread::_threadLoop(void* user)
     wp<Thread> weak(strong);
     self->mHoldSelf.clear();
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
     // this is very useful for debugging with gdb
     self->mTid = gettid();
 #endif
@@ -838,7 +832,7 @@ bool Thread::isRunning() const {
     return mRunning;
 }
 
-#ifdef HAVE_ANDROID_OS
+#if defined(__ANDROID__)
 pid_t Thread::getTid() const
 {
     // mTid is not defined until the child initializes it, and the caller may need it earlier
index fb70e15..201bc41 100644 (file)
@@ -23,7 +23,7 @@
 #include <sys/time.h>
 #include <time.h>
 
-#if defined(HAVE_ANDROID_OS)
+#if defined(__ANDROID__)
 nsecs_t systemTime(int clock)
 {
     static const clockid_t clocks[] = {
index 610002f..2d0e83d 100644 (file)
@@ -56,12 +56,12 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
     int fd = ::open(filename.string(), O_RDONLY);
     if (fd < 0) {
         result = -errno;
-        ALOGE("Error opening file '%s', %s.", filename.string(), strerror(errno));
+        ALOGE("Error opening file '%s': %s", filename.string(), strerror(errno));
     } else {
         struct stat stat;
         if (fstat(fd, &stat)) {
             result = -errno;
-            ALOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno));
+            ALOGE("Error getting size of file '%s': %s", filename.string(), strerror(errno));
         } else {
             size_t length = size_t(stat.st_size);
 
@@ -83,7 +83,7 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {
                 ssize_t nrd = read(fd, buffer, length);
                 if (nrd < 0) {
                     result = -errno;
-                    ALOGE("Error reading file '%s', %s.", filename.string(), strerror(errno));
+                    ALOGE("Error reading file '%s': %s", filename.string(), strerror(errno));
                     delete[] buffer;
                     buffer = NULL;
                 } else {
index fb876c9..6f4b721 100644 (file)
@@ -18,7 +18,7 @@
 
 #include <stddef.h>
 
-#ifdef HAVE_WINSOCK
+#if defined(_WIN32)
 # undef  nhtol
 # undef  htonl
 # undef  nhtos
index de65a6c..e8d40ed 100644 (file)
 #include <safe_iop.h>
 
 #include <utils/Errors.h>
-#include <utils/SharedBuffer.h>
 #include <utils/VectorImpl.h>
 
+#include "SharedBuffer.h"
+
 /*****************************************************************************/
 
 
@@ -204,7 +205,10 @@ status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state)
                     _do_copy(next, curr, 1);
                     next = curr;
                     --j;
-                    curr = reinterpret_cast<char*>(array) + mItemSize*(j);                    
+                    curr = NULL;
+                    if (j >= 0) {
+                        curr = reinterpret_cast<char*>(array) + mItemSize*(j);
+                    }
                 } while (j>=0 && (cmp(curr, temp, state) > 0));
 
                 _do_destroy(next, 1);
@@ -587,6 +591,10 @@ size_t SortedVectorImpl::orderOf(const void* item) const
 
 ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
 {
+    if (order) *order = 0;
+    if (isEmpty()) {
+        return NAME_NOT_FOUND;
+    }
     // binary search
     ssize_t err = NAME_NOT_FOUND;
     ssize_t l = 0;
index ed1ba23..216dc14 100644 (file)
@@ -24,7 +24,6 @@
 
 #include <sys/stat.h>
 #include <string.h>
-#include <errno.h>
 #include <stdio.h>
 
 #if !defined(_WIN32)
index d4a45fd..8f07f1a 100644 (file)
@@ -22,12 +22,12 @@ include $(CLEAR_VARS)
 LOCAL_MODULE := libutils_tests
 
 LOCAL_SRC_FILES := \
-    BasicHashtable_test.cpp \
     BlobCache_test.cpp \
     BitSet_test.cpp \
     Looper_test.cpp \
     LruCache_test.cpp \
     String8_test.cpp \
+    StrongPointer_test.cpp \
     Unicode_test.cpp \
     Vector_test.cpp \
 
diff --git a/libutils/tests/BasicHashtable_test.cpp b/libutils/tests/BasicHashtable_test.cpp
deleted file mode 100644 (file)
index 4b3a717..0000000
+++ /dev/null
@@ -1,582 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "BasicHashtable_test"
-
-#include <utils/BasicHashtable.h>
-#include <cutils/log.h>
-#include <gtest/gtest.h>
-#include <unistd.h>
-
-namespace {
-
-typedef int SimpleKey;
-typedef int SimpleValue;
-typedef android::key_value_pair_t<SimpleKey, SimpleValue> SimpleEntry;
-typedef android::BasicHashtable<SimpleKey, SimpleEntry> SimpleHashtable;
-
-struct ComplexKey {
-    int k;
-
-    explicit ComplexKey(int k) : k(k) {
-        instanceCount += 1;
-    }
-
-    ComplexKey(const ComplexKey& other) : k(other.k) {
-        instanceCount += 1;
-    }
-
-    ~ComplexKey() {
-        instanceCount -= 1;
-    }
-
-    bool operator ==(const ComplexKey& other) const {
-        return k == other.k;
-    }
-
-    bool operator !=(const ComplexKey& other) const {
-        return k != other.k;
-    }
-
-    static ssize_t instanceCount;
-};
-
-ssize_t ComplexKey::instanceCount = 0;
-
-struct ComplexValue {
-    int v;
-
-    explicit ComplexValue(int v) : v(v) {
-        instanceCount += 1;
-    }
-
-    ComplexValue(const ComplexValue& other) : v(other.v) {
-        instanceCount += 1;
-    }
-
-    ~ComplexValue() {
-        instanceCount -= 1;
-    }
-
-    static ssize_t instanceCount;
-};
-
-ssize_t ComplexValue::instanceCount = 0;
-
-} // namespace
-
-
-namespace android {
-
-typedef key_value_pair_t<ComplexKey, ComplexValue> ComplexEntry;
-typedef BasicHashtable<ComplexKey, ComplexEntry> ComplexHashtable;
-
-template<> inline hash_t hash_type(const ComplexKey& value) {
-    return hash_type(value.k);
-}
-
-class BasicHashtableTest : public testing::Test {
-protected:
-    virtual void SetUp() {
-        ComplexKey::instanceCount = 0;
-        ComplexValue::instanceCount = 0;
-    }
-
-    virtual void TearDown() {
-        ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-    }
-
-    void assertInstanceCount(ssize_t keys, ssize_t values) {
-        if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) {
-            FAIL() << "Expected " << keys << " keys and " << values << " values "
-                    "but there were actually " << ComplexKey::instanceCount << " keys and "
-                    << ComplexValue::instanceCount << " values";
-        }
-    }
-
-public:
-    template <typename TKey, typename TEntry>
-    static void cookieAt(const BasicHashtable<TKey, TEntry>& h, size_t index,
-            bool* collision, bool* present, hash_t* hash) {
-        uint32_t cookie = h.cookieAt(index);
-        *collision = cookie & BasicHashtable<TKey, TEntry>::Bucket::COLLISION;
-        *present = cookie & BasicHashtable<TKey, TEntry>::Bucket::PRESENT;
-        *hash = cookie & BasicHashtable<TKey, TEntry>::Bucket::HASH_MASK;
-    }
-
-    template <typename TKey, typename TEntry>
-    static const void* getBuckets(const BasicHashtable<TKey, TEntry>& h) {
-        return h.mBuckets;
-    }
-};
-
-template <typename TKey, typename TValue>
-static size_t add(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
-        const TKey& key, const TValue& value) {
-    return h.add(hash_type(key), key_value_pair_t<TKey, TValue>(key, value));
-}
-
-template <typename TKey, typename TValue>
-static ssize_t find(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
-        ssize_t index, const TKey& key) {
-    return h.find(index, hash_type(key), key);
-}
-
-template <typename TKey, typename TValue>
-static bool remove(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h,
-        const TKey& key) {
-    ssize_t index = find(h, -1, key);
-    if (index >= 0) {
-        h.removeAt(index);
-        return true;
-    }
-    return false;
-}
-
-template <typename TEntry>
-static void getKeyValue(const TEntry& entry, int* key, int* value);
-
-template <> void getKeyValue(const SimpleEntry& entry, int* key, int* value) {
-    *key = entry.key;
-    *value = entry.value;
-}
-
-template <> void getKeyValue(const ComplexEntry& entry, int* key, int* value) {
-    *key = entry.key.k;
-    *value = entry.value.v;
-}
-
-template <typename TKey, typename TValue>
-static void dump(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h) {
-    ALOGD("hashtable %p, size=%u, capacity=%u, bucketCount=%u",
-            &h, h.size(), h.capacity(), h.bucketCount());
-    for (size_t i = 0; i < h.bucketCount(); i++) {
-        bool collision, present;
-        hash_t hash;
-        BasicHashtableTest::cookieAt(h, i, &collision, &present, &hash);
-        if (present) {
-            int key, value;
-            getKeyValue(h.entryAt(i), &key, &value);
-            ALOGD("  [%3u] = collision=%d, present=%d, hash=0x%08x, key=%3d, value=%3d, "
-                    "hash_type(key)=0x%08x",
-                    i, collision, present, hash, key, value, hash_type(key));
-        } else {
-            ALOGD("  [%3u] = collision=%d, present=%d",
-                    i, collision, present);
-        }
-    }
-}
-
-TEST_F(BasicHashtableTest, DefaultConstructor_WithDefaultProperties) {
-    SimpleHashtable h;
-
-    EXPECT_EQ(0U, h.size());
-    EXPECT_EQ(3U, h.capacity());
-    EXPECT_EQ(5U, h.bucketCount());
-    EXPECT_EQ(0.75f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Constructor_WithNonUnityLoadFactor) {
-    SimpleHashtable h(52, 0.8f);
-
-    EXPECT_EQ(0U, h.size());
-    EXPECT_EQ(77U, h.capacity());
-    EXPECT_EQ(97U, h.bucketCount());
-    EXPECT_EQ(0.8f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndExactCapacity) {
-    SimpleHashtable h(46, 1.0f);
-
-    EXPECT_EQ(0U, h.size());
-    EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f
-    EXPECT_EQ(47U, h.bucketCount());
-    EXPECT_EQ(1.0f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndInexactCapacity) {
-    SimpleHashtable h(42, 1.0f);
-
-    EXPECT_EQ(0U, h.size());
-    EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f
-    EXPECT_EQ(47U, h.bucketCount());
-    EXPECT_EQ(1.0f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, FindAddFindRemoveFind_OneEntry) {
-    SimpleHashtable h;
-    ssize_t index = find(h, -1, 8);
-    ASSERT_EQ(-1, index);
-
-    index = add(h, 8, 1);
-    ASSERT_EQ(1U, h.size());
-
-    ASSERT_EQ(index, find(h, -1, 8));
-    ASSERT_EQ(8, h.entryAt(index).key);
-    ASSERT_EQ(1, h.entryAt(index).value);
-
-    index = find(h, index, 8);
-    ASSERT_EQ(-1, index);
-
-    ASSERT_TRUE(remove(h, 8));
-    ASSERT_EQ(0U, h.size());
-
-    index = find(h, -1, 8);
-    ASSERT_EQ(-1, index);
-}
-
-TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithUniqueKey) {
-    const size_t N = 11;
-
-    SimpleHashtable h;
-    for (size_t i = 0; i < N; i++) {
-        ssize_t index = find(h, -1, int(i));
-        ASSERT_EQ(-1, index);
-
-        index = add(h, int(i), int(i * 10));
-        ASSERT_EQ(i + 1, h.size());
-
-        ASSERT_EQ(index, find(h, -1, int(i)));
-        ASSERT_EQ(int(i), h.entryAt(index).key);
-        ASSERT_EQ(int(i * 10), h.entryAt(index).value);
-
-        index = find(h, index, int(i));
-        ASSERT_EQ(-1, index);
-    }
-
-    for (size_t i = N; --i > 0; ) {
-        ASSERT_TRUE(remove(h, int(i))) << "i = " << i;
-        ASSERT_EQ(i, h.size());
-
-        ssize_t index = find(h, -1, int(i));
-        ASSERT_EQ(-1, index);
-    }
-}
-
-TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithDuplicateKey) {
-    const size_t N = 11;
-    const int K = 1;
-
-    SimpleHashtable h;
-    for (size_t i = 0; i < N; i++) {
-        ssize_t index = find(h, -1, K);
-        if (i == 0) {
-            ASSERT_EQ(-1, index);
-        } else {
-            ASSERT_NE(-1, index);
-        }
-
-        add(h, K, int(i));
-        ASSERT_EQ(i + 1, h.size());
-
-        index = -1;
-        int values = 0;
-        for (size_t j = 0; j <= i; j++) {
-            index = find(h, index, K);
-            ASSERT_GE(index, 0);
-            ASSERT_EQ(K, h.entryAt(index).key);
-            values |= 1 << h.entryAt(index).value;
-        }
-        ASSERT_EQ(values, (1 << (i + 1)) - 1);
-
-        index = find(h, index, K);
-        ASSERT_EQ(-1, index);
-    }
-
-    for (size_t i = N; --i > 0; ) {
-        ASSERT_TRUE(remove(h, K)) << "i = " << i;
-        ASSERT_EQ(i, h.size());
-
-        ssize_t index = -1;
-        for (size_t j = 0; j < i; j++) {
-            index = find(h, index, K);
-            ASSERT_GE(index, 0);
-            ASSERT_EQ(K, h.entryAt(index).key);
-        }
-
-        index = find(h, index, K);
-        ASSERT_EQ(-1, index);
-    }
-}
-
-TEST_F(BasicHashtableTest, Clear_WhenAlreadyEmpty_DoesNothing) {
-    SimpleHashtable h;
-    h.clear();
-
-    EXPECT_EQ(0U, h.size());
-    EXPECT_EQ(3U, h.capacity());
-    EXPECT_EQ(5U, h.bucketCount());
-    EXPECT_EQ(0.75f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_RemovesThem) {
-    SimpleHashtable h;
-    add(h, 0, 0);
-    add(h, 1, 0);
-    h.clear();
-
-    EXPECT_EQ(0U, h.size());
-    EXPECT_EQ(3U, h.capacity());
-    EXPECT_EQ(5U, h.bucketCount());
-    EXPECT_EQ(0.75f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_DestroysThem) {
-    ComplexHashtable h;
-    add(h, ComplexKey(0), ComplexValue(0));
-    add(h, ComplexKey(1), ComplexValue(0));
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-
-    h.clear();
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-
-    EXPECT_EQ(0U, h.size());
-    EXPECT_EQ(3U, h.capacity());
-    EXPECT_EQ(5U, h.bucketCount());
-    EXPECT_EQ(0.75f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Remove_AfterElementsAdded_DestroysThem) {
-    ComplexHashtable h;
-    add(h, ComplexKey(0), ComplexValue(0));
-    add(h, ComplexKey(1), ComplexValue(0));
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-
-    ASSERT_TRUE(remove(h, ComplexKey(0)));
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
-
-    ASSERT_TRUE(remove(h, ComplexKey(1)));
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-
-    EXPECT_EQ(0U, h.size());
-    EXPECT_EQ(3U, h.capacity());
-    EXPECT_EQ(5U, h.bucketCount());
-    EXPECT_EQ(0.75f, h.loadFactor());
-}
-
-TEST_F(BasicHashtableTest, Destructor_AfterElementsAdded_DestroysThem) {
-    {
-        ComplexHashtable h;
-        add(h, ComplexKey(0), ComplexValue(0));
-        add(h, ComplexKey(1), ComplexValue(0));
-        ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-    } // h is destroyed here
-
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-}
-
-TEST_F(BasicHashtableTest, Next_WhenEmpty_ReturnsMinusOne) {
-    SimpleHashtable h;
-
-    ASSERT_EQ(-1, h.next(-1));
-}
-
-TEST_F(BasicHashtableTest, Next_WhenNonEmpty_IteratesOverAllEntries) {
-    const int N = 88;
-
-    SimpleHashtable h;
-    for (int i = 0; i < N; i++) {
-        add(h, i, i * 10);
-    }
-
-    bool set[N];
-    memset(set, 0, sizeof(bool) * N);
-    int count = 0;
-    for (ssize_t index = -1; (index = h.next(index)) != -1; ) {
-        ASSERT_GE(index, 0);
-        ASSERT_LT(size_t(index), h.bucketCount());
-
-        const SimpleEntry& entry = h.entryAt(index);
-        ASSERT_GE(entry.key, 0);
-        ASSERT_LT(entry.key, N);
-        ASSERT_FALSE(set[entry.key]);
-        ASSERT_EQ(entry.key * 10, entry.value);
-
-        set[entry.key] = true;
-        count += 1;
-    }
-    ASSERT_EQ(N, count);
-}
-
-TEST_F(BasicHashtableTest, Add_RehashesOnDemand) {
-    SimpleHashtable h;
-    size_t initialCapacity = h.capacity();
-    size_t initialBucketCount = h.bucketCount();
-
-    for (size_t i = 0; i < initialCapacity; i++) {
-        add(h, int(i), 0);
-    }
-
-    EXPECT_EQ(initialCapacity, h.size());
-    EXPECT_EQ(initialCapacity, h.capacity());
-    EXPECT_EQ(initialBucketCount, h.bucketCount());
-
-    add(h, -1, -1);
-
-    EXPECT_EQ(initialCapacity + 1, h.size());
-    EXPECT_GT(h.capacity(), initialCapacity);
-    EXPECT_GT(h.bucketCount(), initialBucketCount);
-    EXPECT_GT(h.bucketCount(), h.capacity());
-}
-
-TEST_F(BasicHashtableTest, Rehash_WhenCapacityAndBucketCountUnchanged_DoesNothing) {
-    ComplexHashtable h;
-    add(h, ComplexKey(0), ComplexValue(0));
-    const void* oldBuckets = getBuckets(h);
-    ASSERT_NE((void*)NULL, oldBuckets);
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
-
-    h.rehash(h.capacity(), h.loadFactor());
-
-    ASSERT_EQ(oldBuckets, getBuckets(h));
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1));
-}
-
-TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasNoBuckets_ButDoesNotAllocateBuckets) {
-    ComplexHashtable h;
-    ASSERT_EQ((void*)NULL, getBuckets(h));
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-
-    h.rehash(9, 1.0f);
-
-    EXPECT_EQ(0U, h.size());
-    EXPECT_EQ(10U, h.capacity());
-    EXPECT_EQ(11U, h.bucketCount());
-    EXPECT_EQ(1.0f, h.loadFactor());
-    EXPECT_EQ((void*)NULL, getBuckets(h));
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-}
-
-TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasBuckets_ReleasesBucketsAndSetsCapacity) {
-    ComplexHashtable h(10);
-    add(h, ComplexKey(0), ComplexValue(0));
-    ASSERT_TRUE(remove(h, ComplexKey(0)));
-    ASSERT_NE((void*)NULL, getBuckets(h));
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-
-    h.rehash(0, 0.75f);
-
-    EXPECT_EQ(0U, h.size());
-    EXPECT_EQ(3U, h.capacity());
-    EXPECT_EQ(5U, h.bucketCount());
-    EXPECT_EQ(0.75f, h.loadFactor());
-    EXPECT_EQ((void*)NULL, getBuckets(h));
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));
-}
-
-TEST_F(BasicHashtableTest, Rehash_WhenLessThanCurrentCapacity_ShrinksBuckets) {
-    ComplexHashtable h(10);
-    add(h, ComplexKey(0), ComplexValue(0));
-    add(h, ComplexKey(1), ComplexValue(1));
-    const void* oldBuckets = getBuckets(h);
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-
-    h.rehash(0, 0.75f);
-
-    EXPECT_EQ(2U, h.size());
-    EXPECT_EQ(3U, h.capacity());
-    EXPECT_EQ(5U, h.bucketCount());
-    EXPECT_EQ(0.75f, h.loadFactor());
-    EXPECT_NE(oldBuckets, getBuckets(h));
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-}
-
-TEST_F(BasicHashtableTest, CopyOnWrite) {
-    ComplexHashtable h1;
-    add(h1, ComplexKey(0), ComplexValue(0));
-    add(h1, ComplexKey(1), ComplexValue(1));
-    const void* originalBuckets = getBuckets(h1);
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-    ssize_t index0 = find(h1, -1, ComplexKey(0));
-    EXPECT_GE(index0, 0);
-
-    // copy constructor acquires shared reference
-    ComplexHashtable h2(h1);
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-    ASSERT_EQ(originalBuckets, getBuckets(h2));
-    EXPECT_EQ(h1.size(), h2.size());
-    EXPECT_EQ(h1.capacity(), h2.capacity());
-    EXPECT_EQ(h1.bucketCount(), h2.bucketCount());
-    EXPECT_EQ(h1.loadFactor(), h2.loadFactor());
-    EXPECT_EQ(index0, find(h2, -1, ComplexKey(0)));
-
-    // operator= acquires shared reference
-    ComplexHashtable h3;
-    h3 = h2;
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-    ASSERT_EQ(originalBuckets, getBuckets(h3));
-    EXPECT_EQ(h1.size(), h3.size());
-    EXPECT_EQ(h1.capacity(), h3.capacity());
-    EXPECT_EQ(h1.bucketCount(), h3.bucketCount());
-    EXPECT_EQ(h1.loadFactor(), h3.loadFactor());
-    EXPECT_EQ(index0, find(h3, -1, ComplexKey(0)));
-
-    // editEntryAt copies shared contents
-    h1.editEntryAt(index0).value.v = 42;
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
-    ASSERT_NE(originalBuckets, getBuckets(h1));
-    EXPECT_EQ(42, h1.entryAt(index0).value.v);
-    EXPECT_EQ(0, h2.entryAt(index0).value.v);
-    EXPECT_EQ(0, h3.entryAt(index0).value.v);
-
-    // clear releases reference to shared contents
-    h2.clear();
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
-    EXPECT_EQ(0U, h2.size());
-    ASSERT_NE(originalBuckets, getBuckets(h2));
-
-    // operator= acquires shared reference, destroys unshared contents
-    h1 = h3;
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-    ASSERT_EQ(originalBuckets, getBuckets(h1));
-    EXPECT_EQ(h3.size(), h1.size());
-    EXPECT_EQ(h3.capacity(), h1.capacity());
-    EXPECT_EQ(h3.bucketCount(), h1.bucketCount());
-    EXPECT_EQ(h3.loadFactor(), h1.loadFactor());
-    EXPECT_EQ(index0, find(h1, -1, ComplexKey(0)));
-
-    // add copies shared contents
-    add(h1, ComplexKey(2), ComplexValue(2));
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(5, 5));
-    ASSERT_NE(originalBuckets, getBuckets(h1));
-    EXPECT_EQ(3U, h1.size());
-    EXPECT_EQ(0U, h2.size());
-    EXPECT_EQ(2U, h3.size());
-
-    // remove copies shared contents
-    h1 = h3;
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-    ASSERT_EQ(originalBuckets, getBuckets(h1));
-    h1.removeAt(index0);
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(3, 3));
-    ASSERT_NE(originalBuckets, getBuckets(h1));
-    EXPECT_EQ(1U, h1.size());
-    EXPECT_EQ(0U, h2.size());
-    EXPECT_EQ(2U, h3.size());
-
-    // rehash copies shared contents
-    h1 = h3;
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2));
-    ASSERT_EQ(originalBuckets, getBuckets(h1));
-    h1.rehash(10, 1.0f);
-    ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4));
-    ASSERT_NE(originalBuckets, getBuckets(h1));
-    EXPECT_EQ(2U, h1.size());
-    EXPECT_EQ(0U, h2.size());
-    EXPECT_EQ(2U, h3.size());
-}
-
-} // namespace android
index 38b668a..59d913e 100644 (file)
@@ -138,11 +138,11 @@ TEST_F(BitSet32Test, FillAndClear) {
 TEST_F(BitSet32Test, GetIndexOfBit) {
     b1.markBit(1);
     b1.markBit(4);
-    EXPECT_EQ(b1.getIndexOfBit(1), 0);
-    EXPECT_EQ(b1.getIndexOfBit(4), 1);
+    EXPECT_EQ(0U, b1.getIndexOfBit(1));
+    EXPECT_EQ(1U, b1.getIndexOfBit(4));
     b1.markFirstUnmarkedBit();
-    EXPECT_EQ(b1.getIndexOfBit(1), 1);
-    EXPECT_EQ(b1.getIndexOfBit(4), 2);
+    EXPECT_EQ(1U, b1.getIndexOfBit(1));
+    EXPECT_EQ(2U, b1.getIndexOfBit(4));
 }
 
 class BitSet64Test : public testing::Test {
@@ -260,11 +260,11 @@ TEST_F(BitSet64Test, FillAndClear) {
 TEST_F(BitSet64Test, GetIndexOfBit) {
     b1.markBit(10);
     b1.markBit(40);
-    EXPECT_EQ(b1.getIndexOfBit(10), 0);
-    EXPECT_EQ(b1.getIndexOfBit(40), 1);
+    EXPECT_EQ(0U, b1.getIndexOfBit(10));
+    EXPECT_EQ(1U, b1.getIndexOfBit(40));
     b1.markFirstUnmarkedBit();
-    EXPECT_EQ(b1.getIndexOfBit(10), 1);
-    EXPECT_EQ(b1.getIndexOfBit(40), 2);
+    EXPECT_EQ(1U, b1.getIndexOfBit(10));
+    EXPECT_EQ(2U, b1.getIndexOfBit(40));
 }
 
 } // namespace android
index 6534211..580b980 100644 (file)
@@ -220,8 +220,8 @@ TEST_F(LruCacheTest, NoLeak) {
 
     cache.put(ComplexKey(0), ComplexValue(0));
     cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2, cache.size());
-    assertInstanceCount(2, 3);  // the null value counts as an instance
+    EXPECT_EQ(2U, cache.size());
+    assertInstanceCount(2, 3);  // the member mNullValue counts as an instance
 }
 
 TEST_F(LruCacheTest, Clear) {
@@ -229,7 +229,7 @@ TEST_F(LruCacheTest, Clear) {
 
     cache.put(ComplexKey(0), ComplexValue(0));
     cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2, cache.size());
+    EXPECT_EQ(2U, cache.size());
     assertInstanceCount(2, 3);
     cache.clear();
     assertInstanceCount(0, 1);
@@ -241,7 +241,7 @@ TEST_F(LruCacheTest, ClearNoDoubleFree) {
 
         cache.put(ComplexKey(0), ComplexValue(0));
         cache.put(ComplexKey(1), ComplexValue(1));
-        EXPECT_EQ(2, cache.size());
+        EXPECT_EQ(2U, cache.size());
         assertInstanceCount(2, 3);
         cache.removeOldest();
         cache.clear();
@@ -255,13 +255,13 @@ TEST_F(LruCacheTest, ClearReuseOk) {
 
     cache.put(ComplexKey(0), ComplexValue(0));
     cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2, cache.size());
+    EXPECT_EQ(2U, cache.size());
     assertInstanceCount(2, 3);
     cache.clear();
     assertInstanceCount(0, 1);
     cache.put(ComplexKey(0), ComplexValue(0));
     cache.put(ComplexKey(1), ComplexValue(1));
-    EXPECT_EQ(2, cache.size());
+    EXPECT_EQ(2U, cache.size());
     assertInstanceCount(2, 3);
 }
 
@@ -273,7 +273,7 @@ TEST_F(LruCacheTest, Callback) {
     cache.put(1, "one");
     cache.put(2, "two");
     cache.put(3, "three");
-    EXPECT_EQ(3, cache.size());
+    EXPECT_EQ(3U, cache.size());
     cache.removeOldest();
     EXPECT_EQ(1, callback.callbackCount);
     EXPECT_EQ(1, callback.lastKey);
@@ -288,9 +288,115 @@ TEST_F(LruCacheTest, CallbackOnClear) {
     cache.put(1, "one");
     cache.put(2, "two");
     cache.put(3, "three");
-    EXPECT_EQ(3, cache.size());
+    EXPECT_EQ(3U, cache.size());
     cache.clear();
     EXPECT_EQ(3, callback.callbackCount);
 }
 
+TEST_F(LruCacheTest, IteratorCheck) {
+    LruCache<int, int> cache(100);
+
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+    EXPECT_EQ(3U, cache.size());
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        int v = it.value();
+        // Check we haven't seen the value before.
+        EXPECT_TRUE(returnedValues.find(v) == returnedValues.end());
+        returnedValues.insert(v);
+    }
+    EXPECT_EQ(std::unordered_set<int>({4, 5, 6}), returnedValues);
+}
+
+TEST_F(LruCacheTest, EmptyCacheIterator) {
+    // Check that nothing crashes...
+    LruCache<int, int> cache(100);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>(), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheIterator) {
+    // Check that nothing crashes...
+    LruCache<int, int> cache(100);
+    cache.put(1, 2);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 2 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheRemove) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 2);
+
+    cache.remove(1);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ }), returnedValues);
+}
+
+TEST_F(LruCacheTest, Remove) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+
+    cache.remove(2);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 4, 6 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveYoungest) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+
+    cache.remove(3);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 4, 5 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveNonMember) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+
+    cache.remove(7);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 4, 5, 6 }), returnedValues);
+}
+
 }
index c42c68d..01e64f6 100644 (file)
@@ -72,4 +72,9 @@ TEST_F(String8Test, OperatorPlusEquals) {
     EXPECT_STREQ(src3, " Verify me.");
 }
 
+TEST_F(String8Test, SetToSizeMaxReturnsNoMemory) {
+    const char *in = "some string";
+    EXPECT_EQ(NO_MEMORY, String8("").setTo(in, SIZE_MAX));
+}
+
 }
diff --git a/libutils/tests/StrongPointer_test.cpp b/libutils/tests/StrongPointer_test.cpp
new file mode 100644 (file)
index 0000000..f46d6d1
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+
+using namespace android;
+
+class Foo : public LightRefBase<Foo> {
+public:
+    Foo(bool* deleted_check) : mDeleted(deleted_check) {
+        *mDeleted = false;
+    }
+
+    ~Foo() {
+        *mDeleted = true;
+    }
+private:
+    bool* mDeleted;
+};
+
+TEST(StrongPointer, move) {
+    bool isDeleted;
+    Foo* foo = new Foo(&isDeleted);
+    ASSERT_EQ(0, foo->getStrongCount());
+    ASSERT_FALSE(isDeleted) << "Already deleted...?";
+    sp<Foo> sp1(foo);
+    ASSERT_EQ(1, foo->getStrongCount());
+    {
+        sp<Foo> sp2 = std::move(sp1);
+        ASSERT_EQ(1, foo->getStrongCount()) << "std::move failed, incremented refcnt";
+        ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
+        // The strong count isn't increasing, let's double check the old object
+        // is properly reset and doesn't early delete
+        sp1 = std::move(sp2);
+    }
+    ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
+    {
+        // Now let's double check it deletes on time
+        sp<Foo> sp2 = std::move(sp1);
+    }
+    ASSERT_TRUE(isDeleted) << "foo was leaked!";
+}
index 96d3168..d9b32f9 100644 (file)
@@ -47,26 +47,26 @@ TEST_F(VectorTest, CopyOnWrite_CopyAndAddElements) {
     vector.add(2);
     vector.add(3);
 
-    EXPECT_EQ(vector.size(), 3);
+    EXPECT_EQ(3U, vector.size());
 
     // copy the vector
     other = vector;
 
-    EXPECT_EQ(other.size(), 3);
+    EXPECT_EQ(3U, other.size());
 
     // add an element to the first vector
     vector.add(4);
 
     // make sure the sizes are correct
-    EXPECT_EQ(vector.size(), 4);
-    EXPECT_EQ(other.size(), 3);
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(3U, other.size());
 
     // add an element to the copy
     other.add(5);
 
     // make sure the sizes are correct
-    EXPECT_EQ(vector.size(), 4);
-    EXPECT_EQ(other.size(), 4);
+    EXPECT_EQ(4U, vector.size());
+    EXPECT_EQ(4U, other.size());
 
     // make sure the content of both vectors are correct
     EXPECT_EQ(vector[3], 4);
index a3087ee..8a4921f 100644 (file)
 
 LOCAL_PATH := $(call my-dir)
 
-source_files := zip_archive.cc
+source_files := zip_archive.cc zip_writer.cc
+test_files := zip_archive_test.cc zip_writer_test.cc entry_name_utils_test.cc
+
+# ZLIB_CONST turns on const for input buffers, which is pretty standard.
+common_c_flags := -Werror -Wall -DZLIB_CONST
+
+# Incorrectly warns when C++11 empty brace {} initializer is used.
+# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
+common_cpp_flags := -Wold-style-cast -Wno-missing-field-initializers
 
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := .cc
@@ -23,8 +31,8 @@ LOCAL_SRC_FILES := ${source_files}
 LOCAL_STATIC_LIBRARIES := libz
 LOCAL_SHARED_LIBRARIES := libutils libbase
 LOCAL_MODULE:= libziparchive
-LOCAL_CFLAGS := -Werror -Wall
-LOCAL_CPPFLAGS := -Wold-style-cast
+LOCAL_CFLAGS := $(common_c_flags)
+LOCAL_CPPFLAGS := $(common_cpp_flags)
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -32,20 +40,22 @@ LOCAL_CPP_EXTENSION := .cc
 LOCAL_SRC_FILES := ${source_files}
 LOCAL_STATIC_LIBRARIES := libz libutils libbase
 LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
-ifneq ($(strip $(USE_MINGW)),)
-       LOCAL_CFLAGS += -mno-ms-bitfields
-endif
+LOCAL_CFLAGS := $(common_c_flags)
+LOCAL_CFLAGS_windows := -mno-ms-bitfields
+LOCAL_CPPFLAGS := $(common_cpp_flags)
+
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := .cc
 LOCAL_SRC_FILES := ${source_files}
-LOCAL_STATIC_LIBRARIES := libz libutils
-LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_STATIC_LIBRARIES := libutils
+LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
 LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
+LOCAL_CPPFLAGS := $(common_cpp_flags)
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_SHARED_LIBRARY)
 
@@ -53,8 +63,9 @@ include $(BUILD_HOST_SHARED_LIBRARY)
 include $(CLEAR_VARS)
 LOCAL_MODULE := ziparchive-tests
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := -Werror
-LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
+LOCAL_CFLAGS := $(common_c_flags)
+LOCAL_CPPFLAGS := $(common_cpp_flags)
+LOCAL_SRC_FILES := $(test_files)
 LOCAL_SHARED_LIBRARIES := liblog libbase
 LOCAL_STATIC_LIBRARIES := libziparchive libz libutils
 include $(BUILD_NATIVE_TEST)
@@ -62,10 +73,9 @@ include $(BUILD_NATIVE_TEST)
 include $(CLEAR_VARS)
 LOCAL_MODULE := ziparchive-tests-host
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS += \
-    -Werror \
-    -Wno-unnamed-type-template-args
-LOCAL_SRC_FILES := zip_archive_test.cc entry_name_utils_test.cc
+LOCAL_CFLAGS := $(common_c_flags)
+LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(common_cpp_flags)
+LOCAL_SRC_FILES := $(test_files)
 LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
 LOCAL_STATIC_LIBRARIES := \
     libz \
index cc39aa5..3d18f7c 100644 (file)
@@ -39,6 +39,7 @@
 #include "zlib.h"
 
 #include "entry_name_utils-inl.h"
+#include "zip_archive_common.h"
 #include "ziparchive/zip_archive.h"
 
 using android::base::get_unaligned;
@@ -49,161 +50,6 @@ using android::base::get_unaligned;
 #define O_BINARY 0
 #endif
 
-// The "end of central directory" (EOCD) record. Each archive
-// contains exactly once such record which appears at the end of
-// the archive. It contains archive wide information like the
-// number of entries in the archive and the offset to the central
-// directory of the offset.
-struct EocdRecord {
-  static const uint32_t kSignature = 0x06054b50;
-
-  // End of central directory signature, should always be
-  // |kSignature|.
-  uint32_t eocd_signature;
-  // The number of the current "disk", i.e, the "disk" that this
-  // central directory is on.
-  //
-  // This implementation assumes that each archive spans a single
-  // disk only. i.e, that disk_num == 1.
-  uint16_t disk_num;
-  // The disk where the central directory starts.
-  //
-  // This implementation assumes that each archive spans a single
-  // disk only. i.e, that cd_start_disk == 1.
-  uint16_t cd_start_disk;
-  // The number of central directory records on this disk.
-  //
-  // This implementation assumes that each archive spans a single
-  // disk only. i.e, that num_records_on_disk == num_records.
-  uint16_t num_records_on_disk;
-  // The total number of central directory records.
-  uint16_t num_records;
-  // The size of the central directory (in bytes).
-  uint32_t cd_size;
-  // The offset of the start of the central directory, relative
-  // to the start of the file.
-  uint32_t cd_start_offset;
-  // Length of the central directory comment.
-  uint16_t comment_length;
- private:
-  EocdRecord() = default;
-  DISALLOW_COPY_AND_ASSIGN(EocdRecord);
-} __attribute__((packed));
-
-// A structure representing the fixed length fields for a single
-// record in the central directory of the archive. In addition to
-// the fixed length fields listed here, each central directory
-// record contains a variable length "file_name" and "extra_field"
-// whose lengths are given by |file_name_length| and |extra_field_length|
-// respectively.
-struct CentralDirectoryRecord {
-  static const uint32_t kSignature = 0x02014b50;
-
-  // The start of record signature. Must be |kSignature|.
-  uint32_t record_signature;
-  // Tool version. Ignored by this implementation.
-  uint16_t version_made_by;
-  // Tool version. Ignored by this implementation.
-  uint16_t version_needed;
-  // The "general purpose bit flags" for this entry. The only
-  // flag value that we currently check for is the "data descriptor"
-  // flag.
-  uint16_t gpb_flags;
-  // The compression method for this entry, one of |kCompressStored|
-  // and |kCompressDeflated|.
-  uint16_t compression_method;
-  // The file modification time and date for this entry.
-  uint16_t last_mod_time;
-  uint16_t last_mod_date;
-  // The CRC-32 checksum for this entry.
-  uint32_t crc32;
-  // The compressed size (in bytes) of this entry.
-  uint32_t compressed_size;
-  // The uncompressed size (in bytes) of this entry.
-  uint32_t uncompressed_size;
-  // The length of the entry file name in bytes. The file name
-  // will appear immediately after this record.
-  uint16_t file_name_length;
-  // The length of the extra field info (in bytes). This data
-  // will appear immediately after the entry file name.
-  uint16_t extra_field_length;
-  // The length of the entry comment (in bytes). This data will
-  // appear immediately after the extra field.
-  uint16_t comment_length;
-  // The start disk for this entry. Ignored by this implementation).
-  uint16_t file_start_disk;
-  // File attributes. Ignored by this implementation.
-  uint16_t internal_file_attributes;
-  // File attributes. Ignored by this implementation.
-  uint32_t external_file_attributes;
-  // The offset to the local file header for this entry, from the
-  // beginning of this archive.
-  uint32_t local_file_header_offset;
- private:
-  CentralDirectoryRecord() = default;
-  DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
-} __attribute__((packed));
-
-// The local file header for a given entry. This duplicates information
-// present in the central directory of the archive. It is an error for
-// the information here to be different from the central directory
-// information for a given entry.
-struct LocalFileHeader {
-  static const uint32_t kSignature = 0x04034b50;
-
-  // The local file header signature, must be |kSignature|.
-  uint32_t lfh_signature;
-  // Tool version. Ignored by this implementation.
-  uint16_t version_needed;
-  // The "general purpose bit flags" for this entry. The only
-  // flag value that we currently check for is the "data descriptor"
-  // flag.
-  uint16_t gpb_flags;
-  // The compression method for this entry, one of |kCompressStored|
-  // and |kCompressDeflated|.
-  uint16_t compression_method;
-  // The file modification time and date for this entry.
-  uint16_t last_mod_time;
-  uint16_t last_mod_date;
-  // The CRC-32 checksum for this entry.
-  uint32_t crc32;
-  // The compressed size (in bytes) of this entry.
-  uint32_t compressed_size;
-  // The uncompressed size (in bytes) of this entry.
-  uint32_t uncompressed_size;
-  // The length of the entry file name in bytes. The file name
-  // will appear immediately after this record.
-  uint16_t file_name_length;
-  // The length of the extra field info (in bytes). This data
-  // will appear immediately after the entry file name.
-  uint16_t extra_field_length;
- private:
-  LocalFileHeader() = default;
-  DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
-} __attribute__((packed));
-
-struct DataDescriptor {
-  // The *optional* data descriptor start signature.
-  static const uint32_t kOptSignature = 0x08074b50;
-
-  // CRC-32 checksum of the entry.
-  uint32_t crc32;
-  // Compressed size of the entry.
-  uint32_t compressed_size;
-  // Uncompressed size of the entry.
-  uint32_t uncompressed_size;
- private:
-  DataDescriptor() = default;
-  DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
-} __attribute__((packed));
-
-
-static const uint32_t kGPBDDFlagMask = 0x0008;         // mask value that signifies that the entry has a DD
-
-// The maximum size of a central directory or a file
-// comment in bytes.
-static const uint32_t kMaxCommentLen = 65535;
-
 // The maximum number of bytes to scan backwards for the EOCD start.
 static const uint32_t kMaxEOCDSearch = kMaxCommentLen + sizeof(EocdRecord);
 
@@ -307,7 +153,7 @@ struct ZipArchive {
    * ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
    */
   uint32_t hash_table_size;
-  ZipEntryName* hash_table;
+  ZipString* hash_table;
 
   ZipArchive(const int fd, bool assume_ownership) :
       fd(fd),
@@ -343,7 +189,7 @@ static uint32_t RoundUpPower2(uint32_t val) {
   return val;
 }
 
-static uint32_t ComputeHash(const ZipEntryName& name) {
+static uint32_t ComputeHash(const ZipString& name) {
   uint32_t hash = 0;
   uint16_t len = name.name_length;
   const uint8_t* str = name.name;
@@ -359,16 +205,15 @@ static uint32_t ComputeHash(const ZipEntryName& name) {
  * Convert a ZipEntry to a hash table index, verifying that it's in a
  * valid range.
  */
-static int64_t EntryToIndex(const ZipEntryName* hash_table,
+static int64_t EntryToIndex(const ZipString* hash_table,
                             const uint32_t hash_table_size,
-                            const ZipEntryName& name) {
+                            const ZipString& name) {
   const uint32_t hash = ComputeHash(name);
 
   // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
   uint32_t ent = hash & (hash_table_size - 1);
   while (hash_table[ent].name != NULL) {
-    if (hash_table[ent].name_length == name.name_length &&
-        memcmp(hash_table[ent].name, name.name, name.name_length) == 0) {
+    if (hash_table[ent] == name) {
       return ent;
     }
 
@@ -382,8 +227,8 @@ static int64_t EntryToIndex(const ZipEntryName* hash_table,
 /*
  * Add a new entry to the hash table.
  */
-static int32_t AddToHash(ZipEntryName *hash_table, const uint64_t hash_table_size,
-                         const ZipEntryName& name) {
+static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
+                         const ZipString& name) {
   const uint64_t hash = ComputeHash(name);
   uint32_t ent = hash & (hash_table_size - 1);
 
@@ -392,8 +237,7 @@ static int32_t AddToHash(ZipEntryName *hash_table, const uint64_t hash_table_siz
    * Further, we guarantee that the hashtable size is not 0.
    */
   while (hash_table[ent].name != NULL) {
-    if (hash_table[ent].name_length == name.name_length &&
-        memcmp(hash_table[ent].name, name.name, name.name_length) == 0) {
+    if (hash_table[ent] == name) {
       // We've found a duplicate entry. We don't accept it
       ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
       return kDuplicateEntry;
@@ -473,7 +317,7 @@ static int32_t MapCentralDirectory0(int fd, const char* debug_file_name,
     return kEmptyArchive;
   }
 
-  ALOGV("+++ num_entries=%" PRIu32 "dir_size=%" PRIu32 " dir_offset=%" PRIu32,
+  ALOGV("+++ num_entries=%" PRIu32 " dir_size=%" PRIu32 " dir_offset=%" PRIu32,
         eocd->num_records, eocd->cd_size, eocd->cd_start_offset);
 
   /*
@@ -565,8 +409,8 @@ static int32_t ParseZipArchive(ZipArchive* archive) {
    * least one unused entry to avoid an infinite loop during creation.
    */
   archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
-  archive->hash_table = reinterpret_cast<ZipEntryName*>(calloc(archive->hash_table_size,
-      sizeof(ZipEntryName)));
+  archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
+      sizeof(ZipString)));
 
   /*
    * Walk through the central directory, adding entries to the hash
@@ -605,7 +449,7 @@ static int32_t ParseZipArchive(ZipArchive* archive) {
     }
 
     /* add the CDE filename to the hash table */
-    ZipEntryName entry_name;
+    ZipString entry_name;
     entry_name.name = file_name;
     entry_name.name_length = file_name_length;
     const int add_result = AddToHash(archive->hash_table,
@@ -744,7 +588,7 @@ static int32_t FindEntry(const ZipArchive* archive, const int ent,
   // and other interesting attributes from the central directory. These
   // will later be compared against values from the local file header.
   data->method = cdr->compression_method;
-  data->mod_time = cdr->last_mod_time;
+  data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
   data->crc32 = cdr->crc32;
   data->compressed_length = cdr->compressed_size;
   data->uncompressed_length = cdr->uncompressed_size;
@@ -851,39 +695,41 @@ struct IterationHandle {
   uint32_t position;
   // We're not using vector here because this code is used in the Windows SDK
   // where the STL is not available.
-  const uint8_t* prefix;
-  const uint16_t prefix_len;
-  const uint8_t* suffix;
-  const uint16_t suffix_len;
+  ZipString prefix;
+  ZipString suffix;
   ZipArchive* archive;
 
-  IterationHandle(const ZipEntryName* prefix_name,
-                  const ZipEntryName* suffix_name)
-    : prefix(NULL),
-      prefix_len(prefix_name ? prefix_name->name_length : 0),
-      suffix(NULL),
-      suffix_len(suffix_name ? suffix_name->name_length : 0) {
-    if (prefix_name) {
-      uint8_t* prefix_copy = new uint8_t[prefix_len];
-      memcpy(prefix_copy, prefix_name->name, prefix_len);
-      prefix = prefix_copy;
+  IterationHandle(const ZipString* in_prefix,
+                  const ZipString* in_suffix) {
+    if (in_prefix) {
+      uint8_t* name_copy = new uint8_t[in_prefix->name_length];
+      memcpy(name_copy, in_prefix->name, in_prefix->name_length);
+      prefix.name = name_copy;
+      prefix.name_length = in_prefix->name_length;
+    } else {
+      prefix.name = NULL;
+      prefix.name_length = 0;
     }
-    if (suffix_name) {
-      uint8_t* suffix_copy = new uint8_t[suffix_len];
-      memcpy(suffix_copy, suffix_name->name, suffix_len);
-      suffix = suffix_copy;
+    if (in_suffix) {
+      uint8_t* name_copy = new uint8_t[in_suffix->name_length];
+      memcpy(name_copy, in_suffix->name, in_suffix->name_length);
+      suffix.name = name_copy;
+      suffix.name_length = in_suffix->name_length;
+    } else {
+      suffix.name = NULL;
+      suffix.name_length = 0;
     }
   }
 
   ~IterationHandle() {
-    delete[] prefix;
-    delete[] suffix;
+    delete[] prefix.name;
+    delete[] suffix.name;
   }
 };
 
 int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
-                       const ZipEntryName* optional_prefix,
-                       const ZipEntryName* optional_suffix) {
+                       const ZipString* optional_prefix,
+                       const ZipString* optional_suffix) {
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
 
   if (archive == NULL || archive->hash_table == NULL) {
@@ -903,7 +749,7 @@ void EndIteration(void* cookie) {
   delete reinterpret_cast<IterationHandle*>(cookie);
 }
 
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipEntryName& entryName,
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
                   ZipEntry* data) {
   const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
   if (entryName.name_length == 0) {
@@ -922,7 +768,7 @@ int32_t FindEntry(const ZipArchiveHandle handle, const ZipEntryName& entryName,
   return FindEntry(archive, ent, data);
 }
 
-int32_t Next(void* cookie, ZipEntry* data, ZipEntryName* name) {
+int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
   IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
   if (handle == NULL) {
     return kInvalidHandle;
@@ -936,18 +782,14 @@ int32_t Next(void* cookie, ZipEntry* data, ZipEntryName* name) {
 
   const uint32_t currentOffset = handle->position;
   const uint32_t hash_table_length = archive->hash_table_size;
-  const ZipEntryName *hash_table = archive->hash_table;
+  const ZipString* hash_table = archive->hash_table;
 
   for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
     if (hash_table[i].name != NULL &&
-        (handle->prefix_len == 0 ||
-         (hash_table[i].name_length >= handle->prefix_len &&
-          memcmp(handle->prefix, hash_table[i].name, handle->prefix_len) == 0)) &&
-        (handle->suffix_len == 0 ||
-         (hash_table[i].name_length >= handle->suffix_len &&
-          memcmp(handle->suffix,
-                 hash_table[i].name + hash_table[i].name_length - handle->suffix_len,
-                 handle->suffix_len) == 0))) {
+        (handle->prefix.name_length == 0 ||
+         hash_table[i].StartsWith(handle->prefix)) &&
+        (handle->suffix.name_length == 0 ||
+         hash_table[i].EndsWith(handle->suffix))) {
       handle->position = (i + 1);
       const int error = FindEntry(archive, i, data);
       if (!error) {
diff --git a/libziparchive/zip_archive_common.h b/libziparchive/zip_archive_common.h
new file mode 100644 (file)
index 0000000..7f20d51
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
+#define LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_
+
+#include "base/macros.h"
+
+#include <inttypes.h>
+
+// The "end of central directory" (EOCD) record. Each archive
+// contains exactly once such record which appears at the end of
+// the archive. It contains archive wide information like the
+// number of entries in the archive and the offset to the central
+// directory of the offset.
+struct EocdRecord {
+  static const uint32_t kSignature = 0x06054b50;
+
+  // End of central directory signature, should always be
+  // |kSignature|.
+  uint32_t eocd_signature;
+  // The number of the current "disk", i.e, the "disk" that this
+  // central directory is on.
+  //
+  // This implementation assumes that each archive spans a single
+  // disk only. i.e, that disk_num == 1.
+  uint16_t disk_num;
+  // The disk where the central directory starts.
+  //
+  // This implementation assumes that each archive spans a single
+  // disk only. i.e, that cd_start_disk == 1.
+  uint16_t cd_start_disk;
+  // The number of central directory records on this disk.
+  //
+  // This implementation assumes that each archive spans a single
+  // disk only. i.e, that num_records_on_disk == num_records.
+  uint16_t num_records_on_disk;
+  // The total number of central directory records.
+  uint16_t num_records;
+  // The size of the central directory (in bytes).
+  uint32_t cd_size;
+  // The offset of the start of the central directory, relative
+  // to the start of the file.
+  uint32_t cd_start_offset;
+  // Length of the central directory comment.
+  uint16_t comment_length;
+ private:
+  EocdRecord() = default;
+  DISALLOW_COPY_AND_ASSIGN(EocdRecord);
+} __attribute__((packed));
+
+// A structure representing the fixed length fields for a single
+// record in the central directory of the archive. In addition to
+// the fixed length fields listed here, each central directory
+// record contains a variable length "file_name" and "extra_field"
+// whose lengths are given by |file_name_length| and |extra_field_length|
+// respectively.
+struct CentralDirectoryRecord {
+  static const uint32_t kSignature = 0x02014b50;
+
+  // The start of record signature. Must be |kSignature|.
+  uint32_t record_signature;
+  // Tool version. Ignored by this implementation.
+  uint16_t version_made_by;
+  // Tool version. Ignored by this implementation.
+  uint16_t version_needed;
+  // The "general purpose bit flags" for this entry. The only
+  // flag value that we currently check for is the "data descriptor"
+  // flag.
+  uint16_t gpb_flags;
+  // The compression method for this entry, one of |kCompressStored|
+  // and |kCompressDeflated|.
+  uint16_t compression_method;
+  // The file modification time and date for this entry.
+  uint16_t last_mod_time;
+  uint16_t last_mod_date;
+  // The CRC-32 checksum for this entry.
+  uint32_t crc32;
+  // The compressed size (in bytes) of this entry.
+  uint32_t compressed_size;
+  // The uncompressed size (in bytes) of this entry.
+  uint32_t uncompressed_size;
+  // The length of the entry file name in bytes. The file name
+  // will appear immediately after this record.
+  uint16_t file_name_length;
+  // The length of the extra field info (in bytes). This data
+  // will appear immediately after the entry file name.
+  uint16_t extra_field_length;
+  // The length of the entry comment (in bytes). This data will
+  // appear immediately after the extra field.
+  uint16_t comment_length;
+  // The start disk for this entry. Ignored by this implementation).
+  uint16_t file_start_disk;
+  // File attributes. Ignored by this implementation.
+  uint16_t internal_file_attributes;
+  // File attributes. Ignored by this implementation.
+  uint32_t external_file_attributes;
+  // The offset to the local file header for this entry, from the
+  // beginning of this archive.
+  uint32_t local_file_header_offset;
+ private:
+  CentralDirectoryRecord() = default;
+  DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
+} __attribute__((packed));
+
+// The local file header for a given entry. This duplicates information
+// present in the central directory of the archive. It is an error for
+// the information here to be different from the central directory
+// information for a given entry.
+struct LocalFileHeader {
+  static const uint32_t kSignature = 0x04034b50;
+
+  // The local file header signature, must be |kSignature|.
+  uint32_t lfh_signature;
+  // Tool version. Ignored by this implementation.
+  uint16_t version_needed;
+  // The "general purpose bit flags" for this entry. The only
+  // flag value that we currently check for is the "data descriptor"
+  // flag.
+  uint16_t gpb_flags;
+  // The compression method for this entry, one of |kCompressStored|
+  // and |kCompressDeflated|.
+  uint16_t compression_method;
+  // The file modification time and date for this entry.
+  uint16_t last_mod_time;
+  uint16_t last_mod_date;
+  // The CRC-32 checksum for this entry.
+  uint32_t crc32;
+  // The compressed size (in bytes) of this entry.
+  uint32_t compressed_size;
+  // The uncompressed size (in bytes) of this entry.
+  uint32_t uncompressed_size;
+  // The length of the entry file name in bytes. The file name
+  // will appear immediately after this record.
+  uint16_t file_name_length;
+  // The length of the extra field info (in bytes). This data
+  // will appear immediately after the entry file name.
+  uint16_t extra_field_length;
+ private:
+  LocalFileHeader() = default;
+  DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
+} __attribute__((packed));
+
+struct DataDescriptor {
+  // The *optional* data descriptor start signature.
+  static const uint32_t kOptSignature = 0x08074b50;
+
+  // CRC-32 checksum of the entry.
+  uint32_t crc32;
+  // Compressed size of the entry.
+  uint32_t compressed_size;
+  // Uncompressed size of the entry.
+  uint32_t uncompressed_size;
+ private:
+  DataDescriptor() = default;
+  DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
+} __attribute__((packed));
+
+// mask value that signifies that the entry has a DD
+static const uint32_t kGPBDDFlagMask = 0x0008;
+
+// The maximum size of a central directory or a file
+// comment in bytes.
+static const uint32_t kMaxCommentLen = 65535;
+
+#endif /* LIBZIPARCHIVE_ZIPARCHIVECOMMON_H_ */
index c799869..32b1a38 100644 (file)
@@ -70,7 +70,7 @@ static int32_t OpenArchiveWrapper(const std::string& name,
 }
 
 static void AssertNameEquals(const std::string& name_str,
-                             const ZipEntryName& name) {
+                             const ZipString& name) {
   ASSERT_EQ(name_str.size(), name.name_length);
   ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
 }
@@ -118,7 +118,7 @@ TEST(ziparchive, Iteration) {
   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
 
   // b/c.txt
   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
@@ -151,11 +151,11 @@ TEST(ziparchive, IterationWithPrefix) {
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ZipEntryName prefix("b/");
+  ZipString prefix("b/");
   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, NULL));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
 
   // b/c.txt
   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
@@ -180,11 +180,11 @@ TEST(ziparchive, IterationWithSuffix) {
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ZipEntryName suffix(".txt");
+  ZipString suffix(".txt");
   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, &suffix));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
 
   // b/c.txt
   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
@@ -213,12 +213,12 @@ TEST(ziparchive, IterationWithPrefixAndSuffix) {
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ZipEntryName prefix("b");
-  ZipEntryName suffix(".txt");
+  ZipString prefix("b");
+  ZipString suffix(".txt");
   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
 
   // b/c.txt
   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
@@ -243,12 +243,12 @@ TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ZipEntryName prefix("x");
-  ZipEntryName suffix("y");
+  ZipString prefix("x");
+  ZipString suffix("y");
   ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
 
   // End of iteration.
   ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
@@ -261,7 +261,7 @@ TEST(ziparchive, FindEntry) {
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
   name.name = kATxtName;
   name.name_length = kATxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, name, &data));
@@ -272,9 +272,10 @@ TEST(ziparchive, FindEntry) {
   ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
   ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
   ASSERT_EQ(0x950821c5, data.crc32);
+  ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
 
   // An entry that doesn't exist. Should be a negative return code.
-  ZipEntryName absent_name;
+  ZipString absent_name;
   absent_name.name = kNonexistentTxtName;
   absent_name.name_length = kNonexistentTxtNameLength;
   ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
@@ -287,9 +288,9 @@ TEST(ziparchive, TestInvalidDeclaredLength) {
   ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
 
   void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
 
-  ZipEntryName name;
+  ZipString name;
   ZipEntry data;
 
   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
@@ -304,7 +305,7 @@ TEST(ziparchive, ExtractToMemory) {
 
   // An entry that's deflated.
   ZipEntry data;
-  ZipEntryName a_name;
+  ZipString a_name;
   a_name.name = kATxtName;
   a_name.name_length = kATxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, a_name, &data));
@@ -316,7 +317,7 @@ TEST(ziparchive, ExtractToMemory) {
   delete[] buffer;
 
   // An entry that's stored.
-  ZipEntryName b_name;
+  ZipString b_name;
   b_name.name = kBTxtName;
   b_name.name_length = kBTxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, b_name, &data));
@@ -403,7 +404,7 @@ TEST(ziparchive, EmptyEntries) {
   ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
 
   ZipEntry entry;
-  ZipEntryName empty_name;
+  ZipString empty_name;
   empty_name.name = kEmptyTxtName;
   empty_name.name_length = kEmptyTxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
@@ -434,7 +435,7 @@ TEST(ziparchive, EntryLargerThan32K) {
   ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
 
   ZipEntry entry;
-  ZipEntryName ab_name;
+  ZipString ab_name;
   ab_name.name = kAbTxtName;
   ab_name.name_length = kAbTxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
@@ -502,7 +503,7 @@ TEST(ziparchive, ExtractToFile) {
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry entry;
-  ZipEntryName name;
+  ZipString name;
   name.name = kATxtName;
   name.name_length = kATxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, name, &entry));
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
new file mode 100644 (file)
index 0000000..f117cc5
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "entry_name_utils-inl.h"
+#include "zip_archive_common.h"
+#include "ziparchive/zip_writer.h"
+
+#include <utils/Log.h>
+
+#include <cassert>
+#include <cstdio>
+#include <memory>
+#include <zlib.h>
+#define DEF_MEM_LEVEL 8                // normally in zutil.h?
+
+/* Zip compression methods we support */
+enum {
+  kCompressStored     = 0,        // no compression
+  kCompressDeflated   = 8,        // standard deflate
+};
+
+// Size of the output buffer used for compression.
+static const size_t kBufSize = 32768u;
+
+// No error, operation completed successfully.
+static const int32_t kNoError = 0;
+
+// The ZipWriter is in a bad state.
+static const int32_t kInvalidState = -1;
+
+// There was an IO error while writing to disk.
+static const int32_t kIoError = -2;
+
+// The zip entry name was invalid.
+static const int32_t kInvalidEntryName = -3;
+
+// An error occurred in zlib.
+static const int32_t kZlibError = -4;
+
+static const char* sErrorCodes[] = {
+    "Invalid state",
+    "IO error",
+    "Invalid entry name",
+    "Zlib error",
+};
+
+const char* ZipWriter::ErrorCodeString(int32_t error_code) {
+  if (error_code < 0 && (-error_code) < static_cast<int32_t>(arraysize(sErrorCodes))) {
+    return sErrorCodes[-error_code];
+  }
+  return nullptr;
+}
+
+static void DeleteZStream(z_stream* stream) {
+  deflateEnd(stream);
+  delete stream;
+}
+
+ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
+                                z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
+}
+
+ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
+                                           current_offset_(writer.current_offset_),
+                                           state_(writer.state_),
+                                           files_(std::move(writer.files_)),
+                                           z_stream_(std::move(writer.z_stream_)),
+                                           buffer_(std::move(writer.buffer_)){
+  writer.file_ = nullptr;
+  writer.state_ = State::kError;
+}
+
+ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
+  file_ = writer.file_;
+  current_offset_ = writer.current_offset_;
+  state_ = writer.state_;
+  files_ = std::move(writer.files_);
+  z_stream_ = std::move(writer.z_stream_);
+  buffer_ = std::move(writer.buffer_);
+  writer.file_ = nullptr;
+  writer.state_ = State::kError;
+  return *this;
+}
+
+int32_t ZipWriter::HandleError(int32_t error_code) {
+  state_ = State::kError;
+  z_stream_.reset();
+  return error_code;
+}
+
+int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
+  return StartEntryWithTime(path, flags, time_t());
+}
+
+static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
+  /* round up to an even number of seconds */
+  when = static_cast<time_t>((static_cast<unsigned long>(when) + 1) & (~1));
+
+  struct tm* ptm;
+#if !defined(_WIN32)
+    struct tm tm_result;
+    ptm = localtime_r(&when, &tm_result);
+#else
+    ptm = localtime(&when);
+#endif
+
+  int year = ptm->tm_year;
+  if (year < 80) {
+    year = 80;
+  }
+
+  *out_date = (year - 80) << 9 | (ptm->tm_mon + 1) << 5 | ptm->tm_mday;
+  *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+}
+
+int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
+  if (state_ != State::kWritingZip) {
+    return kInvalidState;
+  }
+
+  FileInfo fileInfo = {};
+  fileInfo.path = std::string(path);
+  fileInfo.local_file_header_offset = current_offset_;
+
+  if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(fileInfo.path.data()),
+                       fileInfo.path.size())) {
+    return kInvalidEntryName;
+  }
+
+  LocalFileHeader header = {};
+  header.lfh_signature = LocalFileHeader::kSignature;
+
+  // Set this flag to denote that a DataDescriptor struct will appear after the data,
+  // containing the crc and size fields.
+  header.gpb_flags |= kGPBDDFlagMask;
+
+  if (flags & ZipWriter::kCompress) {
+    fileInfo.compression_method = kCompressDeflated;
+
+    int32_t result = PrepareDeflate();
+    if (result != kNoError) {
+      return result;
+    }
+  } else {
+    fileInfo.compression_method = kCompressStored;
+  }
+  header.compression_method = fileInfo.compression_method;
+
+  ExtractTimeAndDate(time, &fileInfo.last_mod_time, &fileInfo.last_mod_date);
+  header.last_mod_time = fileInfo.last_mod_time;
+  header.last_mod_date = fileInfo.last_mod_date;
+
+  header.file_name_length = fileInfo.path.size();
+
+  off64_t offset = current_offset_ + sizeof(header) + fileInfo.path.size();
+  if ((flags & ZipWriter::kAlign32) && (offset & 0x03)) {
+    // Pad the extra field so the data will be aligned.
+    uint16_t padding = 4 - (offset % 4);
+    header.extra_field_length = padding;
+    offset += padding;
+  }
+
+  if (fwrite(&header, sizeof(header), 1, file_) != 1) {
+    return HandleError(kIoError);
+  }
+
+  if (fwrite(path, sizeof(*path), fileInfo.path.size(), file_) != fileInfo.path.size()) {
+    return HandleError(kIoError);
+  }
+
+  if (fwrite("\0\0\0", 1, header.extra_field_length, file_) != header.extra_field_length) {
+    return HandleError(kIoError);
+  }
+
+  files_.emplace_back(std::move(fileInfo));
+
+  current_offset_ = offset;
+  state_ = State::kWritingEntry;
+  return kNoError;
+}
+
+int32_t ZipWriter::PrepareDeflate() {
+  assert(state_ == State::kWritingZip);
+
+  // Initialize the z_stream for compression.
+  z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
+
+  int zerr = deflateInit2(z_stream_.get(), Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
+                          DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+  if (zerr != Z_OK) {
+    if (zerr == Z_VERSION_ERROR) {
+      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+      return HandleError(kZlibError);
+    } else {
+      ALOGE("deflateInit2 failed (zerr=%d)", zerr);
+      return HandleError(kZlibError);
+    }
+  }
+
+  z_stream_->next_out = buffer_.data();
+  z_stream_->avail_out = buffer_.size();
+  return kNoError;
+}
+
+int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
+  if (state_ != State::kWritingEntry) {
+    return HandleError(kInvalidState);
+  }
+
+  FileInfo& currentFile = files_.back();
+  int32_t result = kNoError;
+  if (currentFile.compression_method & kCompressDeflated) {
+    result = CompressBytes(&currentFile, data, len);
+  } else {
+    result = StoreBytes(&currentFile, data, len);
+  }
+
+  if (result != kNoError) {
+    return result;
+  }
+
+  currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
+  currentFile.uncompressed_size += len;
+  return kNoError;
+}
+
+int32_t ZipWriter::StoreBytes(FileInfo* file, const void* data, size_t len) {
+  assert(state_ == State::kWritingEntry);
+
+  if (fwrite(data, 1, len, file_) != len) {
+    return HandleError(kIoError);
+  }
+  file->compressed_size += len;
+  current_offset_ += len;
+  return kNoError;
+}
+
+int32_t ZipWriter::CompressBytes(FileInfo* file, const void* data, size_t len) {
+  assert(state_ == State::kWritingEntry);
+  assert(z_stream_);
+  assert(z_stream_->next_out != nullptr);
+  assert(z_stream_->avail_out != 0);
+
+  // Prepare the input.
+  z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
+  z_stream_->avail_in = len;
+
+  while (z_stream_->avail_in > 0) {
+    // We have more data to compress.
+    int zerr = deflate(z_stream_.get(), Z_NO_FLUSH);
+    if (zerr != Z_OK) {
+      return HandleError(kZlibError);
+    }
+
+    if (z_stream_->avail_out == 0) {
+      // The output is full, let's write it to disk.
+      size_t write_bytes = z_stream_->next_out - buffer_.data();
+      if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+        return HandleError(kIoError);
+      }
+      file->compressed_size += write_bytes;
+      current_offset_ += write_bytes;
+
+      // Reset the output buffer for the next input.
+      z_stream_->next_out = buffer_.data();
+      z_stream_->avail_out = buffer_.size();
+    }
+  }
+  return kNoError;
+}
+
+int32_t ZipWriter::FlushCompressedBytes(FileInfo* file) {
+  assert(state_ == State::kWritingEntry);
+  assert(z_stream_);
+  assert(z_stream_->next_out != nullptr);
+  assert(z_stream_->avail_out != 0);
+
+  // Keep deflating while there isn't enough space in the buffer to
+  // to complete the compress.
+  int zerr;
+  while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) {
+    assert(z_stream_->avail_out == 0);
+    size_t write_bytes = z_stream_->next_out - buffer_.data();
+    if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+      return HandleError(kIoError);
+    }
+    file->compressed_size += write_bytes;
+    current_offset_ += write_bytes;
+
+    z_stream_->next_out = buffer_.data();
+    z_stream_->avail_out = buffer_.size();
+  }
+  if (zerr != Z_STREAM_END) {
+    return HandleError(kZlibError);
+  }
+
+  size_t write_bytes = z_stream_->next_out - buffer_.data();
+  if (write_bytes != 0) {
+    if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+      return HandleError(kIoError);
+    }
+    file->compressed_size += write_bytes;
+    current_offset_ += write_bytes;
+  }
+  z_stream_.reset();
+  return kNoError;
+}
+
+int32_t ZipWriter::FinishEntry() {
+  if (state_ != State::kWritingEntry) {
+    return kInvalidState;
+  }
+
+  FileInfo& currentFile = files_.back();
+  if (currentFile.compression_method & kCompressDeflated) {
+    int32_t result = FlushCompressedBytes(&currentFile);
+    if (result != kNoError) {
+      return result;
+    }
+  }
+
+  const uint32_t sig = DataDescriptor::kOptSignature;
+  if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
+    state_ = State::kError;
+    return kIoError;
+  }
+
+  DataDescriptor dd = {};
+  dd.crc32 = currentFile.crc32;
+  dd.compressed_size = currentFile.compressed_size;
+  dd.uncompressed_size = currentFile.uncompressed_size;
+  if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
+    return HandleError(kIoError);
+  }
+
+  current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
+  state_ = State::kWritingZip;
+  return kNoError;
+}
+
+int32_t ZipWriter::Finish() {
+  if (state_ != State::kWritingZip) {
+    return kInvalidState;
+  }
+
+  off64_t startOfCdr = current_offset_;
+  for (FileInfo& file : files_) {
+    CentralDirectoryRecord cdr = {};
+    cdr.record_signature = CentralDirectoryRecord::kSignature;
+    cdr.gpb_flags |= kGPBDDFlagMask;
+    cdr.compression_method = file.compression_method;
+    cdr.last_mod_time = file.last_mod_time;
+    cdr.last_mod_date = file.last_mod_date;
+    cdr.crc32 = file.crc32;
+    cdr.compressed_size = file.compressed_size;
+    cdr.uncompressed_size = file.uncompressed_size;
+    cdr.file_name_length = file.path.size();
+    cdr.local_file_header_offset = file.local_file_header_offset;
+    if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
+      return HandleError(kIoError);
+    }
+
+    if (fwrite(file.path.data(), 1, file.path.size(), file_) != file.path.size()) {
+      return HandleError(kIoError);
+    }
+
+    current_offset_ += sizeof(cdr) + file.path.size();
+  }
+
+  EocdRecord er = {};
+  er.eocd_signature = EocdRecord::kSignature;
+  er.disk_num = 0;
+  er.cd_start_disk = 0;
+  er.num_records_on_disk = files_.size();
+  er.num_records = files_.size();
+  er.cd_size = current_offset_ - startOfCdr;
+  er.cd_start_offset = startOfCdr;
+
+  if (fwrite(&er, sizeof(er), 1, file_) != 1) {
+    return HandleError(kIoError);
+  }
+
+  if (fflush(file_) != 0) {
+    return HandleError(kIoError);
+  }
+
+  current_offset_ += sizeof(er);
+  state_ = State::kDone;
+  return kNoError;
+}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
new file mode 100644 (file)
index 0000000..f752b7e
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ziparchive/zip_archive.h"
+#include "ziparchive/zip_writer.h"
+
+#include <base/test_utils.h>
+#include <gtest/gtest.h>
+#include <memory>
+#include <vector>
+
+struct zipwriter : public ::testing::Test {
+  TemporaryFile* temp_file_;
+  int fd_;
+  FILE* file_;
+
+  void SetUp() override {
+    temp_file_ = new TemporaryFile();
+    fd_ = temp_file_->fd;
+    file_ = fdopen(fd_, "w");
+    ASSERT_NE(file_, nullptr);
+  }
+
+  void TearDown() override {
+    fclose(file_);
+    delete temp_file_;
+  }
+};
+
+TEST_F(zipwriter, WriteUncompressedZipWithOneFile) {
+  ZipWriter writer(file_);
+
+  const char* expected = "hello";
+
+  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(strlen(expected), data.compressed_length);
+  EXPECT_EQ(strlen(expected), data.uncompressed_length);
+  EXPECT_EQ(kCompressStored, data.method);
+
+  char buffer[6];
+  EXPECT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)));
+  buffer[5] = 0;
+
+  EXPECT_STREQ(expected, buffer);
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
+  ZipWriter writer(file_);
+
+  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.FinishEntry());
+
+  ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+  ASSERT_EQ(0, writer.FinishEntry());
+
+  ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
+  ASSERT_EQ(0, writer.FinishEntry());
+
+  ASSERT_EQ(0, writer.Finish());
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  char buffer[4];
+  ZipEntry data;
+
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(2u, data.compressed_length);
+  EXPECT_EQ(2u, data.uncompressed_length);
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
+  buffer[2] = 0;
+  EXPECT_STREQ("he", buffer);
+
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(3u, data.compressed_length);
+  EXPECT_EQ(3u, data.uncompressed_length);
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
+  buffer[3] = 0;
+  EXPECT_STREQ("llo", buffer);
+
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(0u, data.compressed_length);
+  EXPECT_EQ(0u, data.uncompressed_length);
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipWithAlignedFile) {
+  ZipWriter writer(file_);
+
+  ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  EXPECT_EQ(0, data.offset & 0x03);
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
+  ZipWriter writer(file_);
+
+  ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+  ASSERT_EQ(0, writer.WriteBytes("helo", 4));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(kCompressDeflated, data.method);
+  EXPECT_EQ(4u, data.uncompressed_length);
+
+  char buffer[5];
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
+  buffer[4] = 0;
+
+  EXPECT_STREQ("helo", buffer);
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteCompressedZipFlushFull) {
+  // This exact data will cause the Finish() to require multiple calls
+  // to deflate() because the ZipWriter buffer isn't big enough to hold
+  // the entire compressed data buffer.
+  constexpr size_t kBufSize = 10000000;
+  std::vector<uint8_t> buffer(kBufSize);
+  size_t prev = 1;
+  for (size_t i = 0; i < kBufSize; i++) {
+    buffer[i] = i + prev;
+    prev = i;
+  }
+
+  ZipWriter writer(file_);
+  ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+  ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(kCompressDeflated, data.method);
+  EXPECT_EQ(kBufSize, data.uncompressed_length);
+
+  std::vector<uint8_t> decompress(kBufSize);
+  memset(decompress.data(), 0, kBufSize);
+  ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size()));
+  EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
+      << "Input buffer and output buffer are different.";
+
+  CloseArchive(handle);
+}
index 39081d6..8c88661 100644 (file)
@@ -7,4 +7,6 @@ LOCAL_CFLAGS := -Werror
 
 LOCAL_MODULE := lmkd
 
+LOCAL_INIT_RC := lmkd.rc
+
 include $(BUILD_EXECUTABLE)
index 7bbc811..cb9598e 100644 (file)
@@ -77,12 +77,7 @@ static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
 static int epollfd;
 static int maxevents;
 
-#define OOM_DISABLE (-17)
-/* inclusive */
-#define OOM_ADJUST_MIN (-16)
-#define OOM_ADJUST_MAX 15
-
-/* kernel OOM score values */
+/* OOM score values used by both kernel and framework */
 #define OOM_SCORE_ADJ_MIN       (-1000)
 #define OOM_SCORE_ADJ_MAX       1000
 
@@ -114,8 +109,8 @@ struct proc {
 static struct proc *pidhash[PIDHASH_SZ];
 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
 
-#define ADJTOSLOT(adj) (adj + -OOM_ADJUST_MIN)
-static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_ADJUST_MAX) + 1];
+#define ADJTOSLOT(adj) (adj + -OOM_SCORE_ADJ_MIN)
+static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
 
 /*
  * Wait 1-2 seconds for the death report of a killed process prior to
@@ -148,14 +143,6 @@ static ssize_t read_all(int fd, char *buf, size_t max_len)
     return ret;
 }
 
-static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
-{
-    if (oom_adj == OOM_ADJUST_MAX)
-        return OOM_SCORE_ADJ_MAX;
-    else
-        return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
-}
-
 static struct proc *pid_lookup(int pid) {
     struct proc *procp;
 
@@ -254,13 +241,13 @@ static void cmd_procprio(int pid, int uid, int oomadj) {
     char path[80];
     char val[20];
 
-    if (oomadj < OOM_DISABLE || oomadj > OOM_ADJUST_MAX) {
+    if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
         ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
         return;
     }
 
     snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
-    snprintf(val, sizeof(val), "%d", lowmem_oom_adj_to_oom_score_adj(oomadj));
+    snprintf(val, sizeof(val), "%d", oomadj);
     writefilestring(path, val);
 
     if (use_inkernel_interface)
@@ -607,7 +594,7 @@ static int kill_one_process(struct proc *procp, int other_free, int other_file,
 static int find_and_kill_process(int other_free, int other_file, bool first)
 {
     int i;
-    int min_score_adj = OOM_ADJUST_MAX + 1;
+    int min_score_adj = OOM_SCORE_ADJ_MAX + 1;
     int minfree = 0;
     int killed_size = 0;
 
@@ -619,10 +606,10 @@ static int find_and_kill_process(int other_free, int other_file, bool first)
         }
     }
 
-    if (min_score_adj == OOM_ADJUST_MAX + 1)
+    if (min_score_adj == OOM_SCORE_ADJ_MAX + 1)
         return 0;
 
-    for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) {
+    for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
         struct proc *procp;
 
 retry:
@@ -783,7 +770,7 @@ static int init(void) {
             ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
     }
 
-    for (i = 0; i <= ADJTOSLOT(OOM_ADJUST_MAX); i++) {
+    for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
         procadjslot_list[i].next = &procadjslot_list[i];
         procadjslot_list[i].prev = &procadjslot_list[i];
     }
diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc
new file mode 100644 (file)
index 0000000..3bb84ab
--- /dev/null
@@ -0,0 +1,6 @@
+service lmkd /system/bin/lmkd
+    class core
+    group root readproc
+    critical
+    socket lmkd seqpacket 0660 system system
+    writepid /dev/cpuset/system-background/tasks
index 7115f9b..c4a9550 100644 (file)
@@ -11,6 +11,19 @@ LOCAL_MODULE := logcat
 
 LOCAL_CFLAGS := -Werror
 
+LOCAL_INIT_RC := logcatd.rc
+
 include $(BUILD_EXECUTABLE)
 
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := logpersist.start
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_PATH := $(bin_dir)
+LOCAL_SRC_FILES := logpersist
+ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
+LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);)
+include $(BUILD_PREBUILT)
+
 include $(call first-makefiles-under,$(LOCAL_PATH))
index 54cbc20..4d7adf1 100644 (file)
@@ -259,8 +259,8 @@ static void show_help(const char *cmd)
                     "  -r <kbytes>     Rotate log every kbytes. Requires -f\n"
                     "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
                     "  -v <format>     Sets the log print format, where <format> is:\n\n"
-                    "                      brief color long printable process raw tag thread\n"
-                    "                      threadtime time usec UTC year zone\n\n"
+                    "                      brief color epoch long monotonic printable process raw\n"
+                    "                      tag thread threadtime time usec UTC year zone\n\n"
                     "  -D              print dividers between each log buffer\n"
                     "  -c              clear (flush) the entire log and exit\n"
                     "  -d              dump the log and then exit (don't block)\n"
@@ -269,7 +269,7 @@ static void show_help(const char *cmd)
                     "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
                     "  -T '<time>'     print most recent lines since specified time (not imply -d)\n"
                     "                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
-                    "                  or 'YYYY-MM-DD hh:mm:ss.mmm...' format\n"
+                    "                  'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
                     "  -g              get the size of the log's ring buffer and exit\n"
                     "  -L              dump logs from prior to last reboot\n"
                     "  -b <buffer>     Request alternate ring buffer, 'main', 'system', 'radio',\n"
@@ -384,7 +384,11 @@ static char *parseTime(log_time &t, const char *cp) {
     if (ep) {
         return ep;
     }
-    return t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
+    ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
+    if (ep) {
+        return ep;
+    }
+    return t.strptime(cp, "%s.%q");
 }
 
 // Find last logged line in gestalt of all matching existing output files
@@ -395,6 +399,10 @@ static log_time lastLogTime(char *outputFileName) {
     }
 
     log_time now(CLOCK_REALTIME);
+    bool monotonic = android_log_timestamp() == 'm';
+    if (monotonic) {
+        now = log_time(CLOCK_MONOTONIC);
+    }
 
     std::string directory;
     char *file = strrchr(outputFileName, '/');
@@ -413,7 +421,11 @@ static log_time lastLogTime(char *outputFileName) {
     struct dirent *dp;
     while ((dp = readdir(dir.get())) != NULL) {
         if ((dp->d_type != DT_REG)
-                || strncmp(dp->d_name, file, len)
+                // If we are using realtime, check all files that match the
+                // basename for latest time. If we are using monotonic time
+                // then only check the main file because time cycles on
+                // every reboot.
+                || strncmp(dp->d_name, file, len + monotonic)
                 || (dp->d_name[len]
                     && ((dp->d_name[len] != '.')
                         || !isdigit(dp->d_name[len+1])))) {
@@ -832,49 +844,66 @@ int main(int argc, char **argv)
     } else {
         logger_list = android_logger_list_alloc(mode, tail_lines, 0);
     }
+    const char *openDeviceFail = NULL;
+    const char *clearFail = NULL;
+    const char *setSizeFail = NULL;
+    const char *getSizeFail = NULL;
+    // We have three orthogonal actions below to clear, set log size and
+    // get log size. All sharing the same iteration loop.
     while (dev) {
         dev->logger_list = logger_list;
         dev->logger = android_logger_open(logger_list,
                                           android_name_to_log_id(dev->device));
         if (!dev->logger) {
-            logcat_panic(false, "Unable to open log device '%s'\n",
-                         dev->device);
+            openDeviceFail = openDeviceFail ?: dev->device;
+            dev = dev->next;
+            continue;
         }
 
         if (clearLog) {
-            int ret;
-            ret = android_logger_clear(dev->logger);
-            if (ret) {
-                logcat_panic(false, "failed to clear the log");
+            if (android_logger_clear(dev->logger)) {
+                clearFail = clearFail ?: dev->device;
             }
         }
 
-        if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {
-            logcat_panic(false, "failed to set the log size");
+        if (setLogSize) {
+            if (android_logger_set_log_size(dev->logger, setLogSize)) {
+                setSizeFail = setSizeFail ?: dev->device;
+            }
         }
 
         if (getLogSize) {
-            long size, readable;
-
-            size = android_logger_get_log_size(dev->logger);
-            if (size < 0) {
-                logcat_panic(false, "failed to get the log size");
-            }
+            long size = android_logger_get_log_size(dev->logger);
+            long readable = android_logger_get_log_readable_size(dev->logger);
 
-            readable = android_logger_get_log_readable_size(dev->logger);
-            if (readable < 0) {
-                logcat_panic(false, "failed to get the readable log size");
+            if ((size < 0) || (readable < 0)) {
+                getSizeFail = getSizeFail ?: dev->device;
+            } else {
+                printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
+                       "max entry is %db, max payload is %db\n", dev->device,
+                       value_of_size(size), multiplier_of_size(size),
+                       value_of_size(readable), multiplier_of_size(readable),
+                       (int) LOGGER_ENTRY_MAX_LEN,
+                       (int) LOGGER_ENTRY_MAX_PAYLOAD);
             }
-
-            printf("%s: ring buffer is %ld%sb (%ld%sb consumed), "
-                   "max entry is %db, max payload is %db\n", dev->device,
-                   value_of_size(size), multiplier_of_size(size),
-                   value_of_size(readable), multiplier_of_size(readable),
-                   (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD);
         }
 
         dev = dev->next;
     }
+    // report any errors in the above loop and exit
+    if (openDeviceFail) {
+        logcat_panic(false, "Unable to open log device '%s'\n", openDeviceFail);
+    }
+    if (clearFail) {
+        logcat_panic(false, "failed to clear the '%s' log\n", clearFail);
+    }
+    if (setSizeFail) {
+        logcat_panic(false, "failed to set the '%s' log size\n", setSizeFail);
+    }
+    if (getSizeFail) {
+        logcat_panic(false, "failed to get the readable '%s' log size",
+                     getSizeFail);
+    }
 
     if (setPruneList) {
         size_t len = strlen(setPruneList);
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
new file mode 100644 (file)
index 0000000..cf0e0d2
--- /dev/null
@@ -0,0 +1,14 @@
+on property:persist.logd.logpersistd=logcatd
+    # all exec/services are called with umask(077), so no gain beyond 0700
+    mkdir /data/misc/logd 0700 logd log
+    # logd for write to /data/misc/logd, log group for read from pstore (-L)
+    exec - logd log -- /system/bin/logcat -L -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n 256
+    start logcatd
+
+service logcatd /system/bin/logcat -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n 256
+    class late_start
+    disabled
+    # logd for write to /data/misc/logd, log group for read from log daemon
+    user logd
+    group log
+    writepid /dev/cpuset/system-background/tasks
similarity index 88%
rename from logd/logpersist
rename to logcat/logpersist
index 215e1e2..6f666f6 100755 (executable)
@@ -1,9 +1,15 @@
 #! /system/bin/sh
 # logpersist cat start and stop handlers
+progname="${0##*/}"
+case `getprop ro.build.type` in
+userdebug|eng) ;;
+*) echo "${progname} - Permission denied"
+   exit 1
+   ;;
+esac
 data=/data/misc/logd
 property=persist.logd.logpersistd
 service=logcatd
-progname="${0##*/}"
 if [ X"${1}" = "-h" -o X"${1}" = X"--help" ]; then
   echo "${progname%.*}.cat            - dump current ${service%d} logs"
   echo "${progname%.*}.start          - start ${service} service"
index 610a6ec..153a3fd 100644 (file)
@@ -75,6 +75,12 @@ TEST(logcat, buckets) {
 }
 
 TEST(logcat, year) {
+
+    if (android_log_timestamp() == 'm') {
+        fprintf(stderr, "Skipping test, logd is monotonic time\n");
+        return;
+    }
+
     FILE *fp;
 
     char needle[32];
@@ -108,7 +114,44 @@ TEST(logcat, year) {
     ASSERT_EQ(3, count);
 }
 
+// Return a pointer to each null terminated -v long time field.
+char *fgetLongTime(char *buffer, size_t buflen, FILE *fp) {
+    while (fgets(buffer, buflen, fp)) {
+        char *cp = buffer;
+        if (*cp != '[') {
+            continue;
+        }
+        while (*++cp == ' ') {
+            ;
+        }
+        char *ep = cp;
+        while (isdigit(*ep)) {
+            ++ep;
+        }
+        if ((*ep != '-') && (*ep != '.')) {
+           continue;
+        }
+        // Find PID field
+        while (((ep = strchr(ep, ':'))) && (*++ep != ' ')) {
+            ;
+        }
+        if (!ep) {
+            continue;
+        }
+        ep -= 7;
+        *ep = '\0';
+        return cp;
+    }
+    return NULL;
+}
+
 TEST(logcat, tz) {
+
+    if (android_log_timestamp() == 'm') {
+        fprintf(stderr, "Skipping test, logd is monotonic time\n");
+        return;
+    }
+
     FILE *fp;
 
     ASSERT_TRUE(NULL != (fp = popen(
@@ -119,11 +162,8 @@ TEST(logcat, tz) {
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')
-         && (strstr(buffer, " -0700 ") || strstr(buffer, " -0800 "))) {
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) {
             ++count;
         }
     }
@@ -144,11 +184,8 @@ TEST(logcat, ntz) {
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')
-         && (strstr(buffer, " -0700 ") || strstr(buffer, " -0800 "))) {
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) {
             ++count;
         }
     }
@@ -169,12 +206,8 @@ TEST(logcat, tail_3) {
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')) {
-            ++count;
-        }
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        ++count;
     }
 
     pclose(fp);
@@ -193,12 +226,8 @@ TEST(logcat, tail_10) {
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')) {
-            ++count;
-        }
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        ++count;
     }
 
     pclose(fp);
@@ -217,12 +246,8 @@ TEST(logcat, tail_100) {
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')) {
-            ++count;
-        }
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        ++count;
     }
 
     pclose(fp);
@@ -241,12 +266,8 @@ TEST(logcat, tail_1000) {
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')) {
-            ++count;
-        }
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        ++count;
     }
 
     pclose(fp);
@@ -263,21 +284,15 @@ TEST(logcat, tail_time) {
     char *last_timestamp = NULL;
     char *first_timestamp = NULL;
     int count = 0;
-    const unsigned int time_length = 18;
-    const unsigned int time_offset = 2;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
-         && (buffer[time_offset + 2] == '-')) {
-            ++count;
-            buffer[time_length + time_offset] = '\0';
-            if (!first_timestamp) {
-                first_timestamp = strdup(buffer + time_offset);
-            }
-            free(last_timestamp);
-            last_timestamp = strdup(buffer + time_offset);
+    char *cp;
+    while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+        ++count;
+        if (!first_timestamp) {
+            first_timestamp = strdup(cp);
         }
+        free(last_timestamp);
+        last_timestamp = strdup(cp);
     }
     pclose(fp);
 
@@ -292,28 +307,24 @@ TEST(logcat, tail_time) {
     int second_count = 0;
     int last_timestamp_count = -1;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
-         && (buffer[time_offset + 2] == '-')) {
-            ++second_count;
-            buffer[time_length + time_offset] = '\0';
-            if (first_timestamp) {
-                // we can get a transitory *extremely* rare failure if hidden
-                // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
-                EXPECT_STREQ(buffer + time_offset, first_timestamp);
-                free(first_timestamp);
-                first_timestamp = NULL;
-            }
-            if (!strcmp(buffer + time_offset, last_timestamp)) {
-                last_timestamp_count = second_count;
-            }
+    while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+        ++second_count;
+        if (first_timestamp) {
+            // we can get a transitory *extremely* rare failure if hidden
+            // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
+            EXPECT_STREQ(cp, first_timestamp);
+            free(first_timestamp);
+            first_timestamp = NULL;
+        }
+        if (!strcmp(cp, last_timestamp)) {
+            last_timestamp_count = second_count;
         }
     }
     pclose(fp);
 
     free(last_timestamp);
     last_timestamp = NULL;
+    free(first_timestamp);
 
     EXPECT_TRUE(first_timestamp == NULL);
     EXPECT_LE(count, second_count);
@@ -601,6 +612,9 @@ TEST(logcat, logrotate) {
                 }
             }
             pclose(fp);
+            if ((count != 7) && (count != 8)) {
+                fprintf(stderr, "count=%d\n", count);
+            }
             EXPECT_TRUE(count == 7 || count == 8);
         }
     }
index 75df484..feca8d5 100644 (file)
@@ -4,6 +4,8 @@ include $(CLEAR_VARS)
 
 LOCAL_MODULE:= logd
 
+LOCAL_INIT_RC := logd.rc
+
 LOCAL_SRC_FILES := \
     main.cpp \
     LogCommand.cpp \
@@ -25,7 +27,8 @@ LOCAL_SHARED_LIBRARIES := \
     libsysutils \
     liblog \
     libcutils \
-    libbase
+    libbase \
+    libpackagelistparser
 
 # This is what we want to do:
 #  event_logtags = $(shell \
@@ -41,15 +44,4 @@ LOCAL_CFLAGS := -Werror $(event_flag)
 
 include $(BUILD_EXECUTABLE)
 
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := logpersist.start
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE_PATH := $(bin_dir)
-LOCAL_SRC_FILES := logpersist
-ALL_TOOLS := logpersist.start logpersist.stop logpersist.cat
-LOCAL_POST_INSTALL_CMD := $(hide) $(foreach t,$(filter-out $(LOCAL_MODULE),$(ALL_TOOLS)),ln -sf $(LOCAL_MODULE) $(TARGET_OUT)/bin/$(t);)
-include $(BUILD_PREBUILT)
-
 include $(call first-makefiles-under,$(LOCAL_PATH))
index 031c740..eafa28f 100644 (file)
@@ -96,8 +96,7 @@ int CommandListener::ClearCmd::runCommand(SocketClient *cli,
         return 0;
     }
 
-    mBuf.clear((log_id_t) id, uid);
-    cli->sendMsg("success");
+    cli->sendMsg(mBuf.clear((log_id_t) id, uid) ? "busy" : "success");
     return 0;
 }
 
index 808d17a..143fb04 100644 (file)
@@ -123,17 +123,19 @@ int LogAudit::logPrint(const char *fmt, ...) {
             && (*cp == ':')) {
         memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
         memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
-        //
-        // We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to
-        // differentiate without prejudice, we use 1980 to delineate, earlier
-        // is monotonic, later is real.
-        //
-#       define EPOCH_PLUS_10_YEARS (10 * 1461 / 4 * 24 * 60 * 60)
-        if (now.tv_sec < EPOCH_PLUS_10_YEARS) {
-            LogKlog::convertMonotonicToReal(now);
+        if (!isMonotonic()) {
+            if (android::isMonotonic(now)) {
+                LogKlog::convertMonotonicToReal(now);
+            }
+        } else {
+            if (!android::isMonotonic(now)) {
+                LogKlog::convertRealToMonotonic(now);
+            }
         }
+    } else if (isMonotonic()) {
+        now = log_time(CLOCK_MONOTONIC);
     } else {
-        now.strptime("", ""); // side effect of setting CLOCK_REALTIME
+        now = log_time(CLOCK_REALTIME);
     }
 
     static const char pid_str[] = " pid=";
@@ -183,7 +185,7 @@ int LogAudit::logPrint(const char *fmt, ...) {
     static const char comm_str[] = " comm=\"";
     const char *comm = strstr(str, comm_str);
     const char *estr = str + strlen(str);
-    char *commfree = NULL;
+    const char *commfree = NULL;
     if (comm) {
         estr = comm;
         comm += sizeof(comm_str) - 1;
@@ -231,7 +233,7 @@ int LogAudit::logPrint(const char *fmt, ...) {
         // end scope for main buffer
     }
 
-    free(commfree);
+    free(const_cast<char *>(commfree));
     free(str);
 
     if (notify) {
index 2342822..8a82630 100644 (file)
@@ -29,6 +29,7 @@ class LogAudit : public SocketListener {
 public:
     LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
     int log(char *buf, size_t len);
+    bool isMonotonic() { return logbuf->isMonotonic(); }
 
 protected:
     virtual bool onDataAvailable(SocketClient *cli);
index 3f7dbbe..fd5c066 100644 (file)
@@ -28,6 +28,7 @@
 #include <log/logger.h>
 
 #include "LogBuffer.h"
+#include "LogKlog.h"
 #include "LogReader.h"
 
 // Default
@@ -126,9 +127,48 @@ void LogBuffer::init() {
             setSize(i, LOG_BUFFER_MIN_SIZE);
         }
     }
+    bool lastMonotonic = monotonic;
+    monotonic = android_log_timestamp() == 'm';
+    if (lastMonotonic == monotonic) {
+        return;
+    }
+
+    //
+    // Fixup all timestamps, may not be 100% accurate, but better than
+    // throwing what we have away when we get 'surprised' by a change.
+    // In-place element fixup so no need to check reader-lock. Entries
+    // should already be in timestamp order, but we could end up with a
+    // few out-of-order entries if new monotonics come in before we
+    // are notified of the reinit change in status. A Typical example would
+    // be:
+    //  --------- beginning of system
+    //      10.494082   184   201 D Cryptfs : Just triggered post_fs_data
+    //  --------- beginning of kernel
+    //       0.000000     0     0 I         : Initializing cgroup subsys cpuacct
+    // as the act of mounting /data would trigger persist.logd.timestamp to
+    // be corrected. 1/30 corner case YMMV.
+    //
+    pthread_mutex_lock(&mLogElementsLock);
+    LogBufferElementCollection::iterator it = mLogElements.begin();
+    while((it != mLogElements.end())) {
+        LogBufferElement *e = *it;
+        if (monotonic) {
+            if (!android::isMonotonic(e->mRealTime)) {
+                LogKlog::convertRealToMonotonic(e->mRealTime);
+            }
+        } else {
+            if (android::isMonotonic(e->mRealTime)) {
+                LogKlog::convertMonotonicToReal(e->mRealTime);
+            }
+        }
+        ++it;
+    }
+    pthread_mutex_unlock(&mLogElementsLock);
 }
 
-LogBuffer::LogBuffer(LastLogTimes *times) : mTimes(*times) {
+LogBuffer::LogBuffer(LastLogTimes *times):
+        monotonic(android_log_timestamp() == 'm'),
+        mTimes(*times) {
     pthread_mutex_init(&mLogElementsLock, NULL);
 
     init();
@@ -217,7 +257,7 @@ int LogBuffer::log(log_id_t log_id, log_time realtime,
     return len;
 }
 
-// Prune at most 10% of the log entries or 256, whichever is less.
+// Prune at most 10% of the log entries or maxPrune, whichever is less.
 //
 // mLogElementsLock must be held when this function is called.
 void LogBuffer::maybePrune(log_id_t id) {
@@ -225,21 +265,24 @@ void LogBuffer::maybePrune(log_id_t id) {
     unsigned long maxSize = log_buffer_size(id);
     if (sizes > maxSize) {
         size_t sizeOver = sizes - ((maxSize * 9) / 10);
-        size_t elements = stats.elements(id);
-        size_t minElements = elements / 10;
+        size_t elements = stats.realElements(id);
+        size_t minElements = elements / 100;
+        if (minElements < minPrune) {
+            minElements = minPrune;
+        }
         unsigned long pruneRows = elements * sizeOver / sizes;
-        if (pruneRows <= minElements) {
+        if (pruneRows < minElements) {
             pruneRows = minElements;
         }
-        if (pruneRows > 256) {
-            pruneRows = 256;
+        if (pruneRows > maxPrune) {
+            pruneRows = maxPrune;
         }
         prune(id, pruneRows);
     }
 }
 
 LogBufferElementCollection::iterator LogBuffer::erase(
-        LogBufferElementCollection::iterator it, bool engageStats) {
+        LogBufferElementCollection::iterator it, bool coalesce) {
     LogBufferElement *e = *it;
     log_id_t id = e->getLogId();
 
@@ -248,10 +291,10 @@ LogBufferElementCollection::iterator LogBuffer::erase(
         mLastWorstUid[id].erase(f);
     }
     it = mLogElements.erase(it);
-    if (engageStats) {
-        stats.subtract(e);
-    } else {
+    if (coalesce) {
         stats.erase(e);
+    } else {
+        stats.subtract(e);
     }
     delete e;
 
@@ -286,7 +329,7 @@ class LogBufferElementLast {
 
 public:
 
-    bool merge(LogBufferElement *e, unsigned short dropped) {
+    bool coalesce(LogBufferElement *e, unsigned short dropped) {
         LogBufferElementKey key(e->getUid(), e->getPid(), e->getTid());
         LogBufferElementMap::iterator it = map.find(key.getKey());
         if (it != map.end()) {
@@ -374,8 +417,10 @@ public:
 //
 // mLogElementsLock must be held when this function is called.
 //
-void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
+bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
     LogTimeEntry *oldest = NULL;
+    bool busy = false;
+    bool clearAll = pruneRows == ULONG_MAX;
 
     LogTimeEntry::lock();
 
@@ -393,35 +438,31 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
     LogBufferElementCollection::iterator it;
 
     if (caller_uid != AID_ROOT) {
+        // Only here if clearAll condition (pruneRows == ULONG_MAX)
         for(it = mLogElements.begin(); it != mLogElements.end();) {
             LogBufferElement *e = *it;
 
-            if (oldest && (oldest->mStart <= e->getSequence())) {
-                break;
-            }
-
-            if (e->getLogId() != id) {
+            if ((e->getLogId() != id) || (e->getUid() != caller_uid)) {
                 ++it;
                 continue;
             }
 
-            if (e->getUid() == caller_uid) {
-                it = erase(it);
-                pruneRows--;
-                if (pruneRows == 0) {
-                    break;
-                }
-            } else {
-                ++it;
+            if (oldest && (oldest->mStart <= e->getSequence())) {
+                oldest->triggerSkip_Locked(id, pruneRows);
+                busy = true;
+                break;
             }
+
+            it = erase(it);
+            pruneRows--;
         }
         LogTimeEntry::unlock();
-        return;
+        return busy;
     }
 
     // prune by worst offender by uid
     bool hasBlacklist = mPrune.naughty();
-    while (pruneRows > 0) {
+    while (!clearAll && (pruneRows > 0)) {
         // recalculate the worst offender on every batched pass
         uid_t worst = (uid_t) -1;
         size_t worst_sizes = 0;
@@ -459,7 +500,7 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
         it = mLogElements.begin();
         // Perform at least one mandatory garbage collection cycle in following
         // - clear leading chatty tags
-        // - merge chatty tags
+        // - coalesce chatty tags
         // - check age-out of preserved logs
         bool gc = pruneRows <= 1;
         if (!gc && (worst != (uid_t) -1)) {
@@ -481,6 +522,7 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
             LogBufferElement *e = *it;
 
             if (oldest && (oldest->mStart <= e->getSequence())) {
+                busy = true;
                 break;
             }
 
@@ -497,9 +539,8 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
                 continue;
             }
 
-            // merge any drops
-            if (dropped && last.merge(e, dropped)) {
-                it = erase(it, false);
+            if (dropped && last.coalesce(e, dropped)) {
+                it = erase(it, true);
                 continue;
             }
 
@@ -530,7 +571,6 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
                 break;
             }
 
-            // unmerged drop message
             if (dropped) {
                 last.add(e);
                 if ((!gc && (e->getUid() == worst))
@@ -564,8 +604,8 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
             } else {
                 stats.drop(e);
                 e->setDropped(1);
-                if (last.merge(e, 1)) {
-                    it = erase(it, false);
+                if (last.coalesce(e, 1)) {
+                    it = erase(it, true);
                 } else {
                     last.add(e);
                     if (!gc || (mLastWorstUid[id].find(worst)
@@ -588,7 +628,7 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
     }
 
     bool whitelist = false;
-    bool hasWhitelist = mPrune.nice();
+    bool hasWhitelist = mPrune.nice() && !clearAll;
     it = mLogElements.begin();
     while((pruneRows > 0) && (it != mLogElements.end())) {
         LogBufferElement *e = *it;
@@ -599,6 +639,8 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
         }
 
         if (oldest && (oldest->mStart <= e->getSequence())) {
+            busy = true;
+
             if (whitelist) {
                 break;
             }
@@ -634,6 +676,7 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
             }
 
             if (oldest && (oldest->mStart <= e->getSequence())) {
+                busy = true;
                 if (stats.sizes(id) > (2 * log_buffer_size(id))) {
                     // kick a misbehaving log reader client off the island
                     oldest->release_Locked();
@@ -649,13 +692,50 @@ void LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
     }
 
     LogTimeEntry::unlock();
+
+    return (pruneRows > 0) && busy;
 }
 
 // clear all rows of type "id" from the buffer.
-void LogBuffer::clear(log_id_t id, uid_t uid) {
-    pthread_mutex_lock(&mLogElementsLock);
-    prune(id, ULONG_MAX, uid);
-    pthread_mutex_unlock(&mLogElementsLock);
+bool LogBuffer::clear(log_id_t id, uid_t uid) {
+    bool busy = true;
+    // If it takes more than 4 tries (seconds) to clear, then kill reader(s)
+    for (int retry = 4;;) {
+        if (retry == 1) { // last pass
+            // Check if it is still busy after the sleep, we say prune
+            // one entry, not another clear run, so we are looking for
+            // the quick side effect of the return value to tell us if
+            // we have a _blocked_ reader.
+            pthread_mutex_lock(&mLogElementsLock);
+            busy = prune(id, 1, uid);
+            pthread_mutex_unlock(&mLogElementsLock);
+            // It is still busy, blocked reader(s), lets kill them all!
+            // otherwise, lets be a good citizen and preserve the slow
+            // readers and let the clear run (below) deal with determining
+            // if we are still blocked and return an error code to caller.
+            if (busy) {
+                LogTimeEntry::lock();
+                LastLogTimes::iterator times = mTimes.begin();
+                while (times != mTimes.end()) {
+                    LogTimeEntry *entry = (*times);
+                    // Killer punch
+                    if (entry->owned_Locked() && entry->isWatching(id)) {
+                        entry->release_Locked();
+                    }
+                    times++;
+                }
+                LogTimeEntry::unlock();
+            }
+        }
+        pthread_mutex_lock(&mLogElementsLock);
+        busy = prune(id, ULONG_MAX, uid);
+        pthread_mutex_unlock(&mLogElementsLock);
+        if (!busy || !--retry) {
+            break;
+        }
+        sleep (1); // Let reader(s) catch up after notification
+    }
+    return busy;
 }
 
 // get the used space associated with "id".
index de76693..c1fec73 100644 (file)
 #include "LogStatistics.h"
 #include "LogWhiteBlackList.h"
 
+//
+// We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to
+// differentiate without prejudice, we use 1980 to delineate, earlier
+// is monotonic, later is real.
+//
+namespace android {
+
+static bool isMonotonic(const log_time &mono) {
+    static const uint32_t EPOCH_PLUS_10_YEARS = 10 * 1461 / 4 * 24 * 60 * 60;
+
+    return mono.tv_sec < EPOCH_PLUS_10_YEARS;
+}
+
+}
+
 typedef std::list<LogBufferElement *> LogBufferElementCollection;
 
 class LogBuffer {
@@ -49,11 +64,14 @@ class LogBuffer {
 
     unsigned long mMaxSize[LOG_ID_MAX];
 
+    bool monotonic;
+
 public:
     LastLogTimes &mTimes;
 
     LogBuffer(LastLogTimes *times);
     void init();
+    bool isMonotonic() { return monotonic; }
 
     int log(log_id_t log_id, log_time realtime,
             uid_t uid, pid_t pid, pid_t tid,
@@ -63,7 +81,7 @@ public:
                      int (*filter)(const LogBufferElement *element, void *arg) = NULL,
                      void *arg = NULL);
 
-    void clear(log_id_t id, uid_t uid = AID_ROOT);
+    bool clear(log_id_t id, uid_t uid = AID_ROOT);
     unsigned long getSize(log_id_t id);
     int setSize(log_id_t id, unsigned long size);
     unsigned long getSizeUsed(log_id_t id);
@@ -78,17 +96,21 @@ public:
     std::string formatPrune() { return mPrune.format(); }
 
     // helper must be protected directly or implicitly by lock()/unlock()
-    char *pidToName(pid_t pid) { return stats.pidToName(pid); }
+    const char *pidToName(pid_t pid) { return stats.pidToName(pid); }
     uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
-    char *uidToName(uid_t uid) { return stats.uidToName(uid); }
+    const char *uidToName(uid_t uid) { return stats.uidToName(uid); }
     void lock() { pthread_mutex_lock(&mLogElementsLock); }
     void unlock() { pthread_mutex_unlock(&mLogElementsLock); }
 
 private:
+
+    static constexpr size_t minPrune = 4;
+    static constexpr size_t maxPrune = 256;
+
     void maybePrune(log_id_t id);
-    void prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
+    bool prune(log_id_t id, unsigned long pruneRows, uid_t uid = AID_ROOT);
     LogBufferElementCollection::iterator erase(
-        LogBufferElementCollection::iterator it, bool engageStats = true);
+        LogBufferElementCollection::iterator it, bool coalesce = false);
 };
 
 #endif // _LOGD_LOG_BUFFER_H__
index 150ce22..c4c302b 100644 (file)
@@ -113,9 +113,9 @@ size_t LogBufferElement::populateDroppedMessage(char *&buffer,
 
     static const char format_uid[] = "uid=%u%s%s expire %u line%s";
     parent->lock();
-    char *name = parent->uidToName(mUid);
+    const char *name = parent->uidToName(mUid);
     parent->unlock();
-    char *commName = android::tidToName(mTid);
+    const char *commName = android::tidToName(mTid);
     if (!commName && (mTid != mPid)) {
         commName = android::tidToName(mPid);
     }
@@ -128,28 +128,28 @@ size_t LogBufferElement::populateDroppedMessage(char *&buffer,
         size_t len = strlen(name + 1);
         if (!strncmp(name + 1, commName + 1, len)) {
             if (commName[len + 1] == '\0') {
-                free(commName);
+                free(const_cast<char *>(commName));
                 commName = NULL;
             } else {
-                free(name);
+                free(const_cast<char *>(name));
                 name = NULL;
             }
         }
     }
     if (name) {
-        char *p = NULL;
-        asprintf(&p, "(%s)", name);
-        if (p) {
-            free(name);
-            name = p;
+        char *buf = NULL;
+        asprintf(&buf, "(%s)", name);
+        if (buf) {
+            free(const_cast<char *>(name));
+            name = buf;
         }
     }
     if (commName) {
-        char *p = NULL;
-        asprintf(&p, " %s", commName);
-        if (p) {
-            free(commName);
-            commName = p;
+        char *buf = NULL;
+        asprintf(&buf, " %s", commName);
+        if (buf) {
+            free(const_cast<char *>(commName));
+            commName = buf;
         }
     }
     // identical to below to calculate the buffer size required
@@ -166,18 +166,19 @@ size_t LogBufferElement::populateDroppedMessage(char *&buffer,
 
     buffer = static_cast<char *>(calloc(1, hdrLen + len + 1));
     if (!buffer) {
-        free(name);
-        free(commName);
+        free(const_cast<char *>(name));
+        free(const_cast<char *>(commName));
         return 0;
     }
 
     size_t retval = hdrLen + len;
     if (mLogId == LOG_ID_EVENTS) {
-        android_log_event_string_t *e = reinterpret_cast<android_log_event_string_t *>(buffer);
+        android_log_event_string_t *event =
+            reinterpret_cast<android_log_event_string_t *>(buffer);
 
-        e->header.tag = htole32(LOGD_LOG_TAG);
-        e->type = EVENT_TYPE_STRING;
-        e->length = htole32(len);
+        event->header.tag = htole32(LOGD_LOG_TAG);
+        event->type = EVENT_TYPE_STRING;
+        event->length = htole32(len);
     } else {
         ++retval;
         buffer[0] = ANDROID_LOG_INFO;
@@ -187,8 +188,8 @@ size_t LogBufferElement::populateDroppedMessage(char *&buffer,
     snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
              commName ? commName : "",
              mDropped, (mDropped > 1) ? "s" : "");
-    free(name);
-    free(commName);
+    free(const_cast<char *>(name));
+    free(const_cast<char *>(commName));
 
     return retval;
 }
index 30e43c6..09987ea 100644 (file)
@@ -34,6 +34,9 @@ class LogBuffer;
 #define EXPIRE_RATELIMIT 10      // maximum rate in seconds to report expiration
 
 class LogBufferElement {
+
+    friend LogBuffer;
+
     const log_id_t mLogId;
     const uid_t mUid;
     const pid_t mPid;
@@ -44,7 +47,7 @@ class LogBufferElement {
         unsigned short mDropped;      // mMsg == NULL
     };
     const uint64_t mSequence;
-    const log_time mRealTime;
+    log_time mRealTime;
     static atomic_int_fast64_t sequence;
 
     // assumption: mMsg == NULL
index 06d865c..6d0e92e 100644 (file)
@@ -42,7 +42,6 @@ LogCommand::LogCommand(const char *cmd) : FrameworkCommand(cmd) {
 static bool groupIsLog(char *buf) {
     char *ptr;
     static const char ws[] = " \n";
-    bool ret = false;
 
     for (buf = strtok_r(buf, ws, &ptr); buf; buf = strtok_r(NULL, ws, &ptr)) {
         errno = 0;
@@ -51,10 +50,10 @@ static bool groupIsLog(char *buf) {
             return false;
         }
         if (Gid == AID_LOG) {
-            ret = true;
+            return true;
         }
     }
-    return ret;
+    return false;
 }
 
 bool clientHasLogCredentials(SocketClient * cli) {
@@ -69,61 +68,79 @@ bool clientHasLogCredentials(SocketClient * cli) {
     }
 
     // FYI We will typically be here for 'adb logcat'
-    bool ret = false;
-
-    char filename[1024];
-    snprintf(filename, sizeof(filename), "/proc/%d/status", cli->getPid());
-
-    FILE *file = fopen(filename, "r");
-    if (!file) {
-        return ret;
-    }
+    char filename[256];
+    snprintf(filename, sizeof(filename), "/proc/%u/status", cli->getPid());
 
+    bool ret;
+    bool foundLog = false;
     bool foundGid = false;
     bool foundUid = false;
 
-    char line[1024];
-    while (fgets(line, sizeof(line), file)) {
-        static const char groups_string[] = "Groups:\t";
-        static const char uid_string[] = "Uid:\t";
-        static const char gid_string[] = "Gid:\t";
-
-        if (strncmp(groups_string, line, strlen(groups_string)) == 0) {
-            ret = groupIsLog(line + strlen(groups_string));
-            if (!ret) {
-                break;
-            }
-        } else if (strncmp(uid_string, line, strlen(uid_string)) == 0) {
-            uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
-
-            sscanf(line + strlen(uid_string), "%u\t%u\t%u\t%u",
-                   &u[0], &u[1], &u[2], &u[3]);
-
-            // Protect against PID reuse by checking that the UID is the same
-            if ((uid != u[0]) || (uid != u[1]) || (uid != u[2]) || (uid != u[3])) {
-                ret = false;
-                break;
-            }
-            foundUid = true;
-        } else if (strncmp(gid_string, line, strlen(gid_string)) == 0) {
-            gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
-
-            sscanf(line + strlen(gid_string), "%u\t%u\t%u\t%u",
-                   &g[0], &g[1], &g[2], &g[3]);
+    //
+    // Reading /proc/<pid>/status is rife with race conditions. All of /proc
+    // suffers from this and its use should be minimized. However, we have no
+    // choice.
+    //
+    // Notably the content from one 4KB page to the next 4KB page can be from a
+    // change in shape even if we are gracious enough to attempt to read
+    // atomically. getline can not even guarantee a page read is not split up
+    // and in effect can read from different vintages of the content.
+    //
+    // We are finding out in the field that a 'logcat -c' via adb occasionally
+    // is returned with permission denied when we did only one pass and thus
+    // breaking scripts. For security we still err on denying access if in
+    // doubt, but we expect the falses  should be reduced significantly as
+    // three times is a charm.
+    //
+    for (int retry = 3;
+            !(ret = foundGid && foundUid && foundLog) && retry;
+            --retry) {
+        FILE *file = fopen(filename, "r");
+        if (!file) {
+            continue;
+        }
 
-            // Protect against PID reuse by checking that the GID is the same
-            if ((gid != g[0]) || (gid != g[1]) || (gid != g[2]) || (gid != g[3])) {
-                ret = false;
-                break;
+        char *line = NULL;
+        size_t len = 0;
+        while (getline(&line, &len, file) > 0) {
+            static const char groups_string[] = "Groups:\t";
+            static const char uid_string[] = "Uid:\t";
+            static const char gid_string[] = "Gid:\t";
+
+            if (strncmp(groups_string, line, sizeof(groups_string) - 1) == 0) {
+                if (groupIsLog(line + sizeof(groups_string) - 1)) {
+                    foundLog = true;
+                }
+            } else if (strncmp(uid_string, line, sizeof(uid_string) - 1) == 0) {
+                uid_t u[4] = { (uid_t) -1, (uid_t) -1, (uid_t) -1, (uid_t) -1};
+
+                sscanf(line + sizeof(uid_string) - 1, "%u\t%u\t%u\t%u",
+                       &u[0], &u[1], &u[2], &u[3]);
+
+                // Protect against PID reuse by checking that UID is the same
+                if ((uid == u[0])
+                        && (uid == u[1])
+                        && (uid == u[2])
+                        && (uid == u[3])) {
+                    foundUid = true;
+                }
+            } else if (strncmp(gid_string, line, sizeof(gid_string) - 1) == 0) {
+                gid_t g[4] = { (gid_t) -1, (gid_t) -1, (gid_t) -1, (gid_t) -1};
+
+                sscanf(line + sizeof(gid_string) - 1, "%u\t%u\t%u\t%u",
+                       &g[0], &g[1], &g[2], &g[3]);
+
+                // Protect against PID reuse by checking that GID is the same
+                if ((gid == g[0])
+                        && (gid == g[1])
+                        && (gid == g[2])
+                        && (gid == g[3])) {
+                    foundGid = true;
+                }
             }
-            foundGid = true;
         }
-    }
-
-    fclose(file);
-
-    if (!foundGid || !foundUid) {
-        ret = false;
+        free(line);
+        fclose(file);
     }
 
     return ret;
index d28161e..2a3f52f 100644 (file)
@@ -330,6 +330,10 @@ void LogKlog::sniffTime(log_time &now,
         }
         *buf = cp;
 
+        if (isMonotonic()) {
+            return;
+        }
+
         const char *b;
         if (((b = strnstr(cp, len, suspendStr)))
                 && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
@@ -376,7 +380,11 @@ void LogKlog::sniffTime(log_time &now,
 
         convertMonotonicToReal(now);
     } else {
-        now = log_time(CLOCK_REALTIME);
+        if (isMonotonic()) {
+            now = log_time(CLOCK_MONOTONIC);
+        } else {
+            now = log_time(CLOCK_REALTIME);
+        }
     }
 }
 
@@ -574,7 +582,7 @@ int LogKlog::log(const char *buf, size_t len) {
     // Some may view the following as an ugly heuristic, the desire is to
     // beautify the kernel logs into an Android Logging format; the goal is
     // admirable but costly.
-    while ((isspace(*p) || !*p) && (p < &buf[len])) {
+    while ((p < &buf[len]) && (isspace(*p) || !*p)) {
         ++p;
     }
     if (p >= &buf[len]) { // timestamp, no content
@@ -588,7 +596,7 @@ int LogKlog::log(const char *buf, size_t len) {
         const char *bt, *et, *cp;
 
         bt = p;
-        if (!fast<strncmp>(p, "[INFO]", 6)) {
+        if ((taglen >= 6) && !fast<strncmp>(p, "[INFO]", 6)) {
             // <PRI>[<TIME>] "[INFO]"<tag> ":" message
             bt = p + 6;
             taglen -= 6;
@@ -612,7 +620,9 @@ int LogKlog::log(const char *buf, size_t len) {
             p = cp + 1;
         } else if (taglen) {
             size = et - bt;
-            if ((*bt == *cp) && fast<strncmp>(bt + 1, cp + 1, size - 1)) {
+            if ((taglen > size) &&   // enough space for match plus trailing :
+                    (*bt == *cp) &&  // ubber fast<strncmp> pair
+                    fast<strncmp>(bt + 1, cp + 1, size - 1)) {
                 // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
                 if (!fast<strncmp>(bt + size - 5, "_host", 5)
                         && !fast<strncmp>(bt + 1, cp + 1, size - 6)) {
@@ -686,7 +696,7 @@ int LogKlog::log(const char *buf, size_t len) {
                     p = cp + 1;
                 }
             }
-        }
+        } /* else no tag */
         size = etag - tag;
         if ((size <= 1)
             // register names like x9
@@ -713,8 +723,12 @@ int LogKlog::log(const char *buf, size_t len) {
             taglen = mp - tag;
         }
     }
+    // Deal with sloppy and simplistic harmless p = cp + 1 etc above.
+    if (len < (size_t)(p - buf)) {
+        p = &buf[len];
+    }
     // skip leading space
-    while ((isspace(*p) || !*p) && (p < &buf[len])) {
+    while ((p < &buf[len]) && (isspace(*p) || !*p)) {
         ++p;
     }
     // truncate trailing space or nuls
@@ -727,16 +741,26 @@ int LogKlog::log(const char *buf, size_t len) {
         p = " ";
         b = 1;
     }
+    // paranoid sanity check, can not happen ...
     if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
         b = LOGGER_ENTRY_MAX_PAYLOAD;
     }
+    if (taglen > LOGGER_ENTRY_MAX_PAYLOAD) {
+        taglen = LOGGER_ENTRY_MAX_PAYLOAD;
+    }
+    // calculate buffer copy requirements
     size_t n = 1 + taglen + 1 + b + 1;
-    int rc = n;
-    if ((taglen > n) || (b > n)) { // Can not happen ...
-        rc = -EINVAL;
-        return rc;
+    // paranoid sanity check, first two just can not happen ...
+    if ((taglen > n) || (b > n) || (n > USHRT_MAX)) {
+        return -EINVAL;
     }
 
+    // Careful.
+    // We are using the stack to house the log buffer for speed reasons.
+    // If we malloc'd this buffer, we could get away without n's USHRT_MAX
+    // test above, but we would then required a max(n, USHRT_MAX) as
+    // truncating length argument to logbuf->log() below. Gain is protection
+    // of stack sanity and speedup, loss is truncated long-line content.
     char newstr[n];
     char *np = newstr;
 
@@ -755,8 +779,8 @@ int LogKlog::log(const char *buf, size_t len) {
     np[b] = '\0';
 
     // Log message
-    rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
-                     (n <= USHRT_MAX) ? (unsigned short) n : USHRT_MAX);
+    int rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
+                         (unsigned short) n);
 
     // notify readers
     if (!rc) {
index 469affd..3c8cc87 100644 (file)
@@ -43,7 +43,9 @@ public:
     int log(const char *buf, size_t len);
     void synchronize(const char *buf, size_t len);
 
+    bool isMonotonic() { return logbuf->isMonotonic(); }
     static void convertMonotonicToReal(log_time &real) { real += correction; }
+    static void convertRealToMonotonic(log_time &real) { real -= correction; }
 
 protected:
     void sniffTime(log_time &now, const char **buf, size_t len, bool reverse);
index 3833843..06135dd 100644 (file)
@@ -114,15 +114,17 @@ bool LogReader::onDataAvailable(SocketClient *cli) {
             log_time &start;
             uint64_t &sequence;
             uint64_t last;
+            bool isMonotonic;
 
         public:
-            LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) :
+            LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence, bool isMonotonic) :
                     mPid(pid),
                     mLogMask(logMask),
                     startTimeSet(false),
                     start(start),
                     sequence(sequence),
-                    last(sequence) {
+                    last(sequence),
+                    isMonotonic(isMonotonic) {
             }
 
             static int callback(const LogBufferElement *element, void *obj) {
@@ -133,20 +135,24 @@ bool LogReader::onDataAvailable(SocketClient *cli) {
                         me->sequence = element->getSequence();
                         me->startTimeSet = true;
                         return -1;
-                    } else {
+                    } else if (!me->isMonotonic ||
+                            android::isMonotonic(element->getRealTime())) {
                         if (me->start < element->getRealTime()) {
                             me->sequence = me->last;
                             me->startTimeSet = true;
                             return -1;
                         }
                         me->last = element->getSequence();
+                    } else {
+                        me->last = element->getSequence();
                     }
                 }
                 return false;
             }
 
             bool found() { return startTimeSet; }
-        } logFindStart(logMask, pid, start, sequence);
+        } logFindStart(logMask, pid, start, sequence,
+                       logbuf().isMonotonic() && android::isMonotonic(start));
 
         logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli),
                          logFindStart.callback, &logFindStart);
index a9c3086..416edd8 100644 (file)
  * limitations under the License.
  */
 
-#include <algorithm> // std::max
 #include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <base/stringprintf.h>
 #include <log/logger.h>
-#include <private/android_filesystem_config.h>
 
 #include "LogStatistics.h"
 
@@ -30,6 +27,7 @@ LogStatistics::LogStatistics() : enable(false) {
     log_id_for_each(id) {
         mSizes[id] = 0;
         mElements[id] = 0;
+        mDroppedElements[id] = 0;
         mSizesTotal[id] = 0;
         mElementsTotal[id] = 0;
     }
@@ -63,9 +61,9 @@ char *pidToName(pid_t pid) {
 
 }
 
-void LogStatistics::add(LogBufferElement *e) {
-    log_id_t log_id = e->getLogId();
-    unsigned short size = e->getMsgLen();
+void LogStatistics::add(LogBufferElement *element) {
+    log_id_t log_id = element->getLogId();
+    unsigned short size = element->getMsgLen();
     mSizes[log_id] += size;
     ++mElements[log_id];
 
@@ -76,65 +74,69 @@ void LogStatistics::add(LogBufferElement *e) {
         return;
     }
 
-    uidTable[log_id].add(e->getUid(), e);
+    uidTable[log_id].add(element->getUid(), element);
 
     if (!enable) {
         return;
     }
 
-    pidTable.add(e->getPid(), e);
-    tidTable.add(e->getTid(), e);
+    pidTable.add(element->getPid(), element);
+    tidTable.add(element->getTid(), element);
 
-    uint32_t tag = e->getTag();
+    uint32_t tag = element->getTag();
     if (tag) {
-        tagTable.add(tag, e);
+        tagTable.add(tag, element);
     }
 }
 
-void LogStatistics::subtract(LogBufferElement *e) {
-    log_id_t log_id = e->getLogId();
-    unsigned short size = e->getMsgLen();
+void LogStatistics::subtract(LogBufferElement *element) {
+    log_id_t log_id = element->getLogId();
+    unsigned short size = element->getMsgLen();
     mSizes[log_id] -= size;
     --mElements[log_id];
+    if (element->getDropped()) {
+        --mDroppedElements[log_id];
+    }
 
     if (log_id == LOG_ID_KERNEL) {
         return;
     }
 
-    uidTable[log_id].subtract(e->getUid(), e);
+    uidTable[log_id].subtract(element->getUid(), element);
 
     if (!enable) {
         return;
     }
 
-    pidTable.subtract(e->getPid(), e);
-    tidTable.subtract(e->getTid(), e);
+    pidTable.subtract(element->getPid(), element);
+    tidTable.subtract(element->getTid(), element);
 
-    uint32_t tag = e->getTag();
+    uint32_t tag = element->getTag();
     if (tag) {
-        tagTable.subtract(tag, e);
+        tagTable.subtract(tag, element);
     }
 }
 
 // Atomically set an entry to drop
 // entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::drop(LogBufferElement *e) {
-    log_id_t log_id = e->getLogId();
-    unsigned short size = e->getMsgLen();
+void LogStatistics::drop(LogBufferElement *element) {
+    log_id_t log_id = element->getLogId();
+    unsigned short size = element->getMsgLen();
     mSizes[log_id] -= size;
+    ++mDroppedElements[log_id];
 
-    uidTable[log_id].drop(e->getUid(), e);
+    uidTable[log_id].drop(element->getUid(), element);
 
     if (!enable) {
         return;
     }
 
-    pidTable.drop(e->getPid(), e);
-    tidTable.drop(e->getTid(), e);
+    pidTable.drop(element->getPid(), element);
+    tidTable.drop(element->getTid(), element);
 }
 
 // caller must own and free character string
-char *LogStatistics::uidToName(uid_t uid) {
+const char *LogStatistics::uidToName(uid_t uid) const {
     // Local hard coded favourites
     if (uid == AID_LOGD) {
         return strdup("auditd");
@@ -152,7 +154,7 @@ char *LogStatistics::uidToName(uid_t uid) {
 
     // Parse /data/system/packages.list
     uid_t userId = uid % AID_USER;
-    char *name = android::uidToName(userId);
+    const char *name = android::uidToName(userId);
     if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
         name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
     }
@@ -161,17 +163,17 @@ char *LogStatistics::uidToName(uid_t uid) {
     }
 
     // report uid -> pid(s) -> pidToName if unique
-    for(pidTable_t::iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
+    for(pidTable_t::const_iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
         const PidEntry &entry = it->second;
 
         if (entry.getUid() == uid) {
-            const char *n = entry.getName();
+            const char *nameTmp = entry.getName();
 
-            if (n) {
+            if (nameTmp) {
                 if (!name) {
-                    name = strdup(n);
-                } else if (fast<strcmp>(name, n)) {
-                    free(name);
+                    name = strdup(nameTmp);
+                } else if (fast<strcmp>(name, nameTmp)) {
+                    free(const_cast<char *>(name));
                     name = NULL;
                     break;
                 }
@@ -183,28 +185,202 @@ char *LogStatistics::uidToName(uid_t uid) {
     return name;
 }
 
-static std::string format_line(
-        const std::string &name,
-        const std::string &size,
-        const std::string &pruned) {
-    static const size_t pruned_len = 6;
-    static const size_t total_len = 70 + pruned_len;
-
-    ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
-    ssize_t size_len = std::max(size.length() + 1,
-                                total_len - name.length() - drop_len - 1);
-
-    if (pruned.length()) {
-        return android::base::StringPrintf("%s%*s%*s\n", name.c_str(),
-                                           (int)size_len, size.c_str(),
-                                           (int)drop_len, pruned.c_str());
+std::string UidEntry::formatHeader(const std::string &name, log_id_t id) const {
+    bool isprune = worstUidEnabledForLogid(id);
+    return formatLine(android::base::StringPrintf(
+                          name.c_str(), android_log_id_to_name(id)),
+                      std::string("Size"),
+                      std::string(isprune ? "+/-  Pruned" : ""))
+         + formatLine(std::string("UID   PACKAGE"),
+                      std::string("BYTES"),
+                      std::string(isprune ? "NUM" : ""));
+}
+
+std::string UidEntry::format(const LogStatistics &stat, log_id_t id) const {
+    uid_t uid = getKey();
+    std::string name = android::base::StringPrintf("%u", uid);
+    const char *nameTmp = stat.uidToName(uid);
+    if (nameTmp) {
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(6 - name.length(), (size_t)1),
+            "", nameTmp);
+        free(const_cast<char *>(nameTmp));
+    }
+
+    std::string size = android::base::StringPrintf("%zu", getSizes());
+
+    std::string pruned = "";
+    if (worstUidEnabledForLogid(id)) {
+        size_t totalDropped = 0;
+        for (LogStatistics::uidTable_t::const_iterator it = stat.uidTable[id].begin();
+                it != stat.uidTable[id].end(); ++it) {
+            totalDropped += it->second.getDropped();
+        }
+        size_t sizes = stat.sizes(id);
+        size_t totalSize = stat.sizesTotal(id);
+        size_t totalElements = stat.elementsTotal(id);
+        float totalVirtualSize = (float)sizes + (float)totalDropped * totalSize
+                                / totalElements;
+        size_t entrySize = getSizes();
+        float virtualEntrySize = entrySize;
+        int realPermille = virtualEntrySize * 1000.0 / sizes;
+        size_t dropped = getDropped();
+        if (dropped) {
+            pruned = android::base::StringPrintf("%zu", dropped);
+            virtualEntrySize += (float)dropped * totalSize / totalElements;
+        }
+        int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize;
+        int permille = (realPermille - virtualPermille) * 1000L
+                     / (virtualPermille ?: 1);
+        if ((permille < -1) || (1 < permille)) {
+            std::string change;
+            const char *units = "%";
+            const char *prefix = (permille > 0) ? "+" : "";
+
+            if (permille > 999) {
+                permille = (permille + 1000) / 100; // Now tenths fold
+                units = "X";
+                prefix = "";
+            }
+            if ((-99 < permille) && (permille < 99)) {
+                change = android::base::StringPrintf("%s%d.%u%s",
+                    prefix,
+                    permille / 10,
+                    ((permille < 0) ? (-permille % 10) : (permille % 10)),
+                    units);
+            } else {
+                change = android::base::StringPrintf("%s%d%s",
+                    prefix,
+                    (permille + 5) / 10, units);
+            }
+            ssize_t spaces = EntryBaseConstants::pruned_len
+                           - 2 - pruned.length() - change.length();
+            if ((spaces <= 0) && pruned.length()) {
+                spaces = 1;
+            }
+            if (spaces > 0) {
+                change += android::base::StringPrintf("%*s", (int)spaces, "");
+            }
+            pruned = change + pruned;
+        }
+    }
+
+    return formatLine(name, size, pruned);
+}
+
+std::string PidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
+    return formatLine(name,
+                      std::string("Size"),
+                      std::string("Pruned"))
+         + formatLine(std::string("  PID/UID   COMMAND LINE"),
+                      std::string("BYTES"),
+                      std::string("NUM"));
+}
+
+std::string PidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+    uid_t uid = getUid();
+    std::string name = android::base::StringPrintf("%5u/%u",
+                                                   getKey(), uid);
+    const char *nameTmp = getName();
+    if (nameTmp) {
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+            "", nameTmp);
+    } else if ((nameTmp = stat.uidToName(uid))) {
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+            "", nameTmp);
+        free(const_cast<char *>(nameTmp));
+    }
+
+    std::string size = android::base::StringPrintf("%zu",
+                                                   getSizes());
+
+    std::string pruned = "";
+    size_t dropped = getDropped();
+    if (dropped) {
+        pruned = android::base::StringPrintf("%zu", dropped);
+    }
+
+    return formatLine(name, size, pruned);
+}
+
+std::string TidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
+    return formatLine(name,
+                      std::string("Size"),
+                      std::string("Pruned"))
+         + formatLine(std::string("  TID/UID   COMM"),
+                      std::string("BYTES"),
+                      std::string("NUM"));
+}
+
+std::string TidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+    uid_t uid = getUid();
+    std::string name = android::base::StringPrintf("%5u/%u",
+                                                   getKey(), uid);
+    const char *nameTmp = getName();
+    if (nameTmp) {
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+            "", nameTmp);
+    } else if ((nameTmp = stat.uidToName(uid))) {
+        // if we do not have a PID name, lets punt to try UID name?
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+            "", nameTmp);
+        free(const_cast<char *>(nameTmp));
+        // We tried, better to not have a name at all, we still
+        // have TID/UID by number to report in any case.
+    }
+
+    std::string size = android::base::StringPrintf("%zu",
+                                                   getSizes());
+
+    std::string pruned = "";
+    size_t dropped = getDropped();
+    if (dropped) {
+        pruned = android::base::StringPrintf("%zu", dropped);
+    }
+
+    return formatLine(name, size, pruned);
+}
+
+std::string TagEntry::formatHeader(const std::string &name, log_id_t id) const {
+    bool isprune = worstUidEnabledForLogid(id);
+    return formatLine(name,
+                      std::string("Size"),
+                      std::string(isprune ? "Prune" : ""))
+         + formatLine(std::string("    TAG/UID   TAGNAME"),
+                      std::string("BYTES"),
+                      std::string(isprune ? "NUM" : ""));
+}
+
+std::string TagEntry::format(const LogStatistics & /* stat */, log_id_t /* id */) const {
+    std::string name;
+    uid_t uid = getUid();
+    if (uid == (uid_t)-1) {
+        name = android::base::StringPrintf("%7u",
+                                           getKey());
     } else {
-        return android::base::StringPrintf("%s%*s\n", name.c_str(),
-                                           (int)size_len, size.c_str());
+        name = android::base::StringPrintf("%7u/%u",
+                                           getKey(), uid);
+    }
+    const char *nameTmp = getName();
+    if (nameTmp) {
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
+            "", nameTmp);
     }
+
+    std::string size = android::base::StringPrintf("%zu",
+                                                   getSizes());
+
+    std::string pruned = "";
+
+    return formatLine(name, size, pruned);
 }
 
-std::string LogStatistics::format(uid_t uid, unsigned int logMask) {
+std::string LogStatistics::format(uid_t uid, unsigned int logMask) const {
     static const unsigned short spaces_total = 19;
 
     // Report on total logging, current and for all time
@@ -227,7 +403,7 @@ std::string LogStatistics::format(uid_t uid, unsigned int logMask) {
     }
 
     spaces = 4;
-    output += android::base::StringPrintf("\nTotal");
+    output += "\nTotal";
 
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) {
@@ -244,7 +420,7 @@ std::string LogStatistics::format(uid_t uid, unsigned int logMask) {
     }
 
     spaces = 6;
-    output += android::base::StringPrintf("\nNow");
+    output += "\nNow";
 
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) {
@@ -266,245 +442,30 @@ std::string LogStatistics::format(uid_t uid, unsigned int logMask) {
 
     // Report on Chattiest
 
+    std::string name;
+
     // Chattiest by application (UID)
-    static const size_t maximum_sorted_entries = 32;
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) {
             continue;
         }
 
-        bool headerPrinted = false;
-        std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
-        ssize_t index = -1;
-        while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
-            const UidEntry *entry = sorted[index];
-            uid_t u = entry->getKey();
-            if ((uid != AID_ROOT) && (u != uid)) {
-                continue;
-            }
-
-            if (!headerPrinted) {
-                output += android::base::StringPrintf("\n\n");
-                std::string name;
-                if (uid == AID_ROOT) {
-                    name = android::base::StringPrintf(
-                        "Chattiest UIDs in %s log buffer:",
-                        android_log_id_to_name(id));
-                } else {
-                    name = android::base::StringPrintf(
-                        "Logging for your UID in %s log buffer:",
-                        android_log_id_to_name(id));
-                }
-                std::string size = "Size";
-                std::string pruned = "Pruned";
-                if (!worstUidEnabledForLogid(id)) {
-                    pruned = "";
-                }
-                output += format_line(name, size, pruned);
-
-                name = "UID   PACKAGE";
-                size = "BYTES";
-                pruned = "LINES";
-                if (!worstUidEnabledForLogid(id)) {
-                    pruned = "";
-                }
-                output += format_line(name, size, pruned);
-
-                headerPrinted = true;
-            }
-
-            std::string name = android::base::StringPrintf("%u", u);
-            char *n = uidToName(u);
-            if (n) {
-                name += android::base::StringPrintf(
-                    "%*s%s", (int)std::max(6 - name.length(), (size_t)1),
-                    "", n);
-                free(n);
-            }
-
-            std::string size = android::base::StringPrintf("%zu",
-                                                           entry->getSizes());
-
-            std::string pruned = "";
-            size_t dropped = entry->getDropped();
-            if (dropped) {
-                pruned = android::base::StringPrintf("%zu", dropped);
-            }
-
-            output += format_line(name, size, pruned);
-        }
+        name = (uid == AID_ROOT)
+            ? "Chattiest UIDs in %s log buffer:"
+            : "Logging for your UID in %s log buffer:";
+        output += uidTable[id].format(*this, uid, name, id);
     }
 
     if (enable) {
-        // Pid table
-        bool headerPrinted = false;
-        std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
-        ssize_t index = -1;
-        while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
-            const PidEntry *entry = sorted[index];
-            uid_t u = entry->getUid();
-            if ((uid != AID_ROOT) && (u != uid)) {
-                continue;
-            }
-
-            if (!headerPrinted) {
-                output += android::base::StringPrintf("\n\n");
-                std::string name;
-                if (uid == AID_ROOT) {
-                    name = android::base::StringPrintf("Chattiest PIDs:");
-                } else {
-                    name = android::base::StringPrintf("Logging for this PID:");
-                }
-                std::string size = "Size";
-                std::string pruned = "Pruned";
-                output += format_line(name, size, pruned);
-
-                name = "  PID/UID   COMMAND LINE";
-                size = "BYTES";
-                pruned = "LINES";
-                output += format_line(name, size, pruned);
-
-                headerPrinted = true;
-            }
-
-            std::string name = android::base::StringPrintf("%5u/%u",
-                                                           entry->getKey(), u);
-            const char *n = entry->getName();
-            if (n) {
-                name += android::base::StringPrintf(
-                    "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
-                    "", n);
-            } else {
-                char *un = uidToName(u);
-                if (un) {
-                    name += android::base::StringPrintf(
-                        "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
-                        "", un);
-                    free(un);
-                }
-            }
-
-            std::string size = android::base::StringPrintf("%zu",
-                                                           entry->getSizes());
-
-            std::string pruned = "";
-            size_t dropped = entry->getDropped();
-            if (dropped) {
-                pruned = android::base::StringPrintf("%zu", dropped);
-            }
-
-            output += format_line(name, size, pruned);
-        }
-    }
-
-    if (enable) {
-        // Tid table
-        bool headerPrinted = false;
-        // sort() returns list of references, unique_ptr makes sure self-delete
-        std::unique_ptr<const TidEntry *[]> sorted = tidTable.sort(maximum_sorted_entries);
-        ssize_t index = -1;
-        while ((index = tidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
-            const TidEntry *entry = sorted[index];
-            uid_t u = entry->getUid();
-            if ((uid != AID_ROOT) && (u != uid)) {
-                continue;
-            }
-
-            if (!headerPrinted) { // Only print header if we have table to print
-                output += android::base::StringPrintf("\n\n");
-                std::string name = "Chattiest TIDs:";
-                std::string size = "Size";
-                std::string pruned = "Pruned";
-                output += format_line(name, size, pruned);
-
-                name = "  TID/UID   COMM";
-                size = "BYTES";
-                pruned = "LINES";
-                output += format_line(name, size, pruned);
-
-                headerPrinted = true;
-            }
-
-            std::string name = android::base::StringPrintf("%5u/%u",
-                                                           entry->getKey(), u);
-            const char *n = entry->getName();
-            if (n) {
-                name += android::base::StringPrintf(
-                    "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
-                    "", n);
-            } else {
-                // if we do not have a PID name, lets punt to try UID name?
-                char *un = uidToName(u);
-                if (un) {
-                    name += android::base::StringPrintf(
-                        "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
-                        "", un);
-                    free(un);
-                }
-                // We tried, better to not have a name at all, we still
-                // have TID/UID by number to report in any case.
-            }
-
-            std::string size = android::base::StringPrintf("%zu",
-                                                           entry->getSizes());
-
-            std::string pruned = "";
-            size_t dropped = entry->getDropped();
-            if (dropped) {
-                pruned = android::base::StringPrintf("%zu", dropped);
-            }
-
-            output += format_line(name, size, pruned);
-        }
+        name = (uid == AID_ROOT) ? "Chattiest PIDs:" : "Logging for this PID:";
+        output += pidTable.format(*this, uid, name);
+        name = "Chattiest TIDs:";
+        output += tidTable.format(*this, uid, name);
     }
 
     if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
-        // Tag table
-        bool headerPrinted = false;
-        std::unique_ptr<const TagEntry *[]> sorted = tagTable.sort(maximum_sorted_entries);
-        ssize_t index = -1;
-        while ((index = tagTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
-            const TagEntry *entry = sorted[index];
-            uid_t u = entry->getUid();
-            if ((uid != AID_ROOT) && (u != uid)) {
-                continue;
-            }
-
-            std::string pruned = "";
-
-            if (!headerPrinted) {
-                output += android::base::StringPrintf("\n\n");
-                std::string name = "Chattiest events log buffer TAGs:";
-                std::string size = "Size";
-                output += format_line(name, size, pruned);
-
-                name = "    TAG/UID   TAGNAME";
-                size = "BYTES";
-                output += format_line(name, size, pruned);
-
-                headerPrinted = true;
-            }
-
-            std::string name;
-            if (u == (uid_t)-1) {
-                name = android::base::StringPrintf("%7u",
-                                                   entry->getKey());
-            } else {
-                name = android::base::StringPrintf("%7u/%u",
-                                                   entry->getKey(), u);
-            }
-            const char *n = entry->getName();
-            if (n) {
-                name += android::base::StringPrintf(
-                    "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
-                    "", n);
-            }
-
-            std::string size = android::base::StringPrintf("%zu",
-                                                           entry->getSizes());
-
-            output += format_line(name, size, pruned);
-        }
+        name = "Chattiest events log buffer TAGs:";
+        output += tagTable.format(*this, uid, name, LOG_ID_EVENTS);
     }
 
     return output;
@@ -536,8 +497,10 @@ uid_t LogStatistics::pidToUid(pid_t pid) {
 }
 
 // caller must free character string
-char *LogStatistics::pidToName(pid_t pid) {
-    const char *name = pidTable.add(pid)->second.getName();
+const char *LogStatistics::pidToName(pid_t pid) const {
+    // An inconvenient truth ... getName() can alter the object
+    pidTable_t &writablePidTable = const_cast<pidTable_t &>(pidTable);
+    const char *name = writablePidTable.add(pid)->second.getName();
     if (!name) {
         return NULL;
     }
index 5a44d59..41f8b95 100644 (file)
 #include <stdlib.h>
 #include <sys/types.h>
 
+#include <algorithm> // std::max
+#include <string>    // std::string
 #include <unordered_map>
 
+#include <base/stringprintf.h>
 #include <log/log.h>
+#include <private/android_filesystem_config.h>
 
 #include "LogBufferElement.h"
 #include "LogUtils.h"
@@ -31,6 +35,8 @@
 #define log_id_for_each(i) \
     for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1))
 
+class LogStatistics;
+
 template <typename TKey, typename TEntry>
 class LogHashtable {
 
@@ -39,50 +45,43 @@ class LogHashtable {
 public:
 
     typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
+    typedef typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
 
-    std::unique_ptr<const TEntry *[]> sort(size_t n) {
-        if (!n) {
+    std::unique_ptr<const TEntry *[]> sort(size_t len) const {
+        if (!len) {
             std::unique_ptr<const TEntry *[]> sorted(NULL);
             return sorted;
         }
 
-        const TEntry **retval = new const TEntry* [n];
-        memset(retval, 0, sizeof(*retval) * n);
+        const TEntry **retval = new const TEntry* [len];
+        memset(retval, 0, sizeof(*retval) * len);
 
-        for(iterator it = map.begin(); it != map.end(); ++it) {
+        for(const_iterator it = map.begin(); it != map.end(); ++it) {
             const TEntry &entry = it->second;
-            size_t s = entry.getSizes();
-            ssize_t i = n - 1;
-            while ((!retval[i] || (s > retval[i]->getSizes())) && (--i >= 0))
+            size_t sizes = entry.getSizes();
+            ssize_t index = len - 1;
+            while ((!retval[index] || (sizes > retval[index]->getSizes()))
+                    && (--index >= 0))
                 ;
-            if (++i < (ssize_t)n) {
-                size_t b = n - i - 1;
-                if (b) {
-                    memmove(&retval[i+1], &retval[i], b * sizeof(retval[0]));
+            if (++index < (ssize_t)len) {
+                size_t num = len - index - 1;
+                if (num) {
+                    memmove(&retval[index + 1], &retval[index],
+                            num * sizeof(retval[0]));
                 }
-                retval[i] = &entry;
+                retval[index] = &entry;
             }
         }
         std::unique_ptr<const TEntry *[]> sorted(retval);
         return sorted;
     }
 
-    // Iteration handler for the sort method output
-    static ssize_t next(ssize_t index, std::unique_ptr<const TEntry *[]> &sorted, size_t n) {
-        ++index;
-        if (!sorted.get() || (index < 0) || (n <= (size_t)index) || !sorted[index]
-         || (sorted[index]->getSizes() <= (sorted[0]->getSizes() / 100))) {
-            return -1;
-        }
-        return index;
-    }
-
-    inline iterator add(TKey key, LogBufferElement *e) {
+    inline iterator add(TKey key, LogBufferElement *element) {
         iterator it = map.find(key);
         if (it == map.end()) {
-            it = map.insert(std::make_pair(key, TEntry(e))).first;
+            it = map.insert(std::make_pair(key, TEntry(element))).first;
         } else {
-            it->second.add(e);
+            it->second.add(element);
         }
         return it;
     }
@@ -97,65 +96,138 @@ public:
         return it;
     }
 
-    void subtract(TKey key, LogBufferElement *e) {
+    void subtract(TKey key, LogBufferElement *element) {
         iterator it = map.find(key);
-        if ((it != map.end()) && it->second.subtract(e)) {
+        if ((it != map.end()) && it->second.subtract(element)) {
             map.erase(it);
         }
     }
 
-    inline void drop(TKey key, LogBufferElement *e) {
+    inline void drop(TKey key, LogBufferElement *element) {
         iterator it = map.find(key);
         if (it != map.end()) {
-            it->second.drop(e);
+            it->second.drop(element);
         }
     }
 
     inline iterator begin() { return map.begin(); }
+    inline const_iterator begin() const { return map.begin(); }
     inline iterator end() { return map.end(); }
-
+    inline const_iterator end() const { return map.end(); }
+
+    std::string format(
+            const LogStatistics &stat,
+            uid_t uid,
+            const std::string &name = std::string(""),
+            log_id_t id = LOG_ID_MAX) const {
+        static const size_t maximum_sorted_entries = 32;
+        std::string output;
+        std::unique_ptr<const TEntry *[]> sorted = sort(maximum_sorted_entries);
+
+        if (!sorted.get()) {
+            return output;
+        }
+        bool headerPrinted = false;
+        for (size_t index = 0; index < maximum_sorted_entries; ++index) {
+            const TEntry *entry = sorted[index];
+            if (!entry) {
+                break;
+            }
+            if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
+                break;
+            }
+            if ((uid != AID_ROOT) && (uid != entry->getUid())) {
+                continue;
+            }
+            if (!headerPrinted) {
+                output += "\n\n";
+                output += entry->formatHeader(name, id);
+                headerPrinted = true;
+            }
+            output += entry->format(stat, id);
+        }
+        return output;
+    }
 };
 
+namespace EntryBaseConstants {
+    static constexpr size_t pruned_len = 14;
+    static constexpr size_t total_len = 80;
+}
+
 struct EntryBase {
     size_t size;
 
     EntryBase():size(0) { }
-    EntryBase(LogBufferElement *e):size(e->getMsgLen()) { }
+    EntryBase(LogBufferElement *element):size(element->getMsgLen()) { }
 
     size_t getSizes() const { return size; }
 
-    inline void add(LogBufferElement *e) { size += e->getMsgLen(); }
-    inline bool subtract(LogBufferElement *e) { size -= e->getMsgLen(); return !size; }
+    inline void add(LogBufferElement *element) { size += element->getMsgLen(); }
+    inline bool subtract(LogBufferElement *element) {
+        size -= element->getMsgLen();
+        return !size;
+    }
+
+    static std::string formatLine(
+            const std::string &name,
+            const std::string &size,
+            const std::string &pruned) {
+        ssize_t drop_len = std::max(pruned.length() + 1,
+                                    EntryBaseConstants::pruned_len);
+        ssize_t size_len = std::max(size.length() + 1,
+                                    EntryBaseConstants::total_len
+                                        - name.length() - drop_len - 1);
+
+        if (pruned.length()) {
+            return android::base::StringPrintf("%s%*s%*s\n", name.c_str(),
+                                               (int)size_len, size.c_str(),
+                                               (int)drop_len, pruned.c_str());
+        } else {
+            return android::base::StringPrintf("%s%*s\n", name.c_str(),
+                                               (int)size_len, size.c_str());
+        }
+    }
 };
 
 struct EntryBaseDropped : public EntryBase {
     size_t dropped;
 
     EntryBaseDropped():dropped(0) { }
-    EntryBaseDropped(LogBufferElement *e):EntryBase(e),dropped(e->getDropped()){ }
+    EntryBaseDropped(LogBufferElement *element):
+            EntryBase(element),
+            dropped(element->getDropped()){
+    }
 
     size_t getDropped() const { return dropped; }
 
-    inline void add(LogBufferElement *e) {
-        dropped += e->getDropped();
-        EntryBase::add(e);
+    inline void add(LogBufferElement *element) {
+        dropped += element->getDropped();
+        EntryBase::add(element);
     }
-    inline bool subtract(LogBufferElement *e) {
-        dropped -= e->getDropped();
-        return EntryBase::subtract(e) && !dropped;
+    inline bool subtract(LogBufferElement *element) {
+        dropped -= element->getDropped();
+        return EntryBase::subtract(element) && !dropped;
     }
-    inline void drop(LogBufferElement *e) {
+    inline void drop(LogBufferElement *element) {
         dropped += 1;
-        EntryBase::subtract(e);
+        EntryBase::subtract(element);
     }
 };
 
 struct UidEntry : public EntryBaseDropped {
     const uid_t uid;
 
-    UidEntry(LogBufferElement *e):EntryBaseDropped(e),uid(e->getUid()) { }
+    UidEntry(LogBufferElement *element):
+            EntryBaseDropped(element),
+            uid(element->getUid()) {
+    }
 
     inline const uid_t&getKey() const { return uid; }
+    inline const uid_t&getUid() const { return uid; }
+
+    std::string formatHeader(const std::string &name, log_id_t id) const;
+    std::string format(const LogStatistics &stat, log_id_t id) const;
 };
 
 namespace android {
@@ -167,51 +239,54 @@ struct PidEntry : public EntryBaseDropped {
     uid_t uid;
     char *name;
 
-    PidEntry(pid_t p):
-        EntryBaseDropped(),
-        pid(p),
-        uid(android::pidToUid(p)),
-        name(android::pidToName(pid)) { }
-    PidEntry(LogBufferElement *e):
-        EntryBaseDropped(e),
-        pid(e->getPid()),
-        uid(e->getUid()),
-        name(android::pidToName(e->getPid())) { }
-    PidEntry(const PidEntry &c):
-        EntryBaseDropped(c),
-        pid(c.pid),
-        uid(c.uid),
-        name(c.name ? strdup(c.name) : NULL) { }
+    PidEntry(pid_t pid):
+            EntryBaseDropped(),
+            pid(pid),
+            uid(android::pidToUid(pid)),
+            name(android::pidToName(pid)) {
+    }
+    PidEntry(LogBufferElement *element):
+            EntryBaseDropped(element),
+            pid(element->getPid()),
+            uid(element->getUid()),
+            name(android::pidToName(pid)) {
+    }
+    PidEntry(const PidEntry &element):
+            EntryBaseDropped(element),
+            pid(element.pid),
+            uid(element.uid),
+            name(element.name ? strdup(element.name) : NULL) {
+    }
     ~PidEntry() { free(name); }
 
     const pid_t&getKey() const { return pid; }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return name; }
 
-    inline void add(pid_t p) {
+    inline void add(pid_t newPid) {
         if (name && !fast<strncmp>(name, "zygote", 6)) {
             free(name);
             name = NULL;
         }
         if (!name) {
-            char *n = android::pidToName(p);
-            if (n) {
-                name = n;
-            }
+            name = android::pidToName(newPid);
         }
     }
 
-    inline void add(LogBufferElement *e) {
-        uid_t u = e->getUid();
-        if (getUid() != u) {
-            uid = u;
+    inline void add(LogBufferElement *element) {
+        uid_t incomingUid = element->getUid();
+        if (getUid() != incomingUid) {
+            uid = incomingUid;
             free(name);
-            name = android::pidToName(e->getPid());
+            name = android::pidToName(element->getPid());
         } else {
-            add(e->getPid());
+            add(element->getPid());
         }
-        EntryBaseDropped::add(e);
+        EntryBaseDropped::add(element);
     }
+
+    std::string formatHeader(const std::string &name, log_id_t id) const;
+    std::string format(const LogStatistics &stat, log_id_t id) const;
 };
 
 struct TidEntry : public EntryBaseDropped {
@@ -219,79 +294,89 @@ struct TidEntry : public EntryBaseDropped {
     uid_t uid;
     char *name;
 
-    TidEntry(pid_t t):
-        EntryBaseDropped(),
-        tid(t),
-        uid(android::pidToUid(t)),
-        name(android::tidToName(tid)) { }
-    TidEntry(LogBufferElement *e):
-        EntryBaseDropped(e),
-        tid(e->getTid()),
-        uid(e->getUid()),
-        name(android::tidToName(e->getTid())) { }
-    TidEntry(const TidEntry &c):
-        EntryBaseDropped(c),
-        tid(c.tid),
-        uid(c.uid),
-        name(c.name ? strdup(c.name) : NULL) { }
+    TidEntry(pid_t tid):
+            EntryBaseDropped(),
+            tid(tid),
+            uid(android::pidToUid(tid)),
+            name(android::tidToName(tid)) {
+    }
+    TidEntry(LogBufferElement *element):
+            EntryBaseDropped(element),
+            tid(element->getTid()),
+            uid(element->getUid()),
+            name(android::tidToName(tid)) {
+    }
+    TidEntry(const TidEntry &element):
+            EntryBaseDropped(element),
+            tid(element.tid),
+            uid(element.uid),
+            name(element.name ? strdup(element.name) : NULL) {
+    }
     ~TidEntry() { free(name); }
 
     const pid_t&getKey() const { return tid; }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return name; }
 
-    inline void add(pid_t t) {
+    inline void add(pid_t incomingTid) {
         if (name && !fast<strncmp>(name, "zygote", 6)) {
             free(name);
             name = NULL;
         }
         if (!name) {
-            char *n = android::tidToName(t);
-            if (n) {
-                name = n;
-            }
+            name = android::tidToName(incomingTid);
         }
     }
 
-    inline void add(LogBufferElement *e) {
-        uid_t u = e->getUid();
-        if (getUid() != u) {
-            uid = u;
+    inline void add(LogBufferElement *element) {
+        uid_t incomingUid = element->getUid();
+        if (getUid() != incomingUid) {
+            uid = incomingUid;
             free(name);
-            name = android::tidToName(e->getTid());
+            name = android::tidToName(element->getTid());
         } else {
-            add(e->getTid());
+            add(element->getTid());
         }
-        EntryBaseDropped::add(e);
+        EntryBaseDropped::add(element);
     }
+
+    std::string formatHeader(const std::string &name, log_id_t id) const;
+    std::string format(const LogStatistics &stat, log_id_t id) const;
 };
 
 struct TagEntry : public EntryBase {
     const uint32_t tag;
     uid_t uid;
 
-    TagEntry(LogBufferElement *e):
-        EntryBase(e),
-        tag(e->getTag()),
-        uid(e->getUid()) { }
+    TagEntry(LogBufferElement *element):
+            EntryBase(element),
+            tag(element->getTag()),
+            uid(element->getUid()) {
+    }
 
     const uint32_t&getKey() const { return tag; }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return android::tagToName(tag); }
 
-    inline void add(LogBufferElement *e) {
-        uid_t u = e->getUid();
-        if (uid != u) {
+    inline void add(LogBufferElement *element) {
+        uid_t incomingUid = element->getUid();
+        if (uid != incomingUid) {
             uid = -1;
         }
-        EntryBase::add(e);
+        EntryBase::add(element);
     }
+
+    std::string formatHeader(const std::string &name, log_id_t id) const;
+    std::string format(const LogStatistics &stat, log_id_t id) const;
 };
 
 // Log Statistics
 class LogStatistics {
+    friend UidEntry;
+
     size_t mSizes[LOG_ID_MAX];
     size_t mElements[LOG_ID_MAX];
+    size_t mDroppedElements[LOG_ID_MAX];
     size_t mSizesTotal[LOG_ID_MAX];
     size_t mElementsTotal[LOG_ID_MAX];
     bool enable;
@@ -321,23 +406,32 @@ public:
     void subtract(LogBufferElement *entry);
     // entry->setDropped(1) must follow this call
     void drop(LogBufferElement *entry);
-    // Correct for merging two entries referencing dropped content
-    void erase(LogBufferElement *e) { --mElements[e->getLogId()]; }
+    // Correct for coalescing two entries referencing dropped content
+    void erase(LogBufferElement *element) {
+        log_id_t log_id = element->getLogId();
+        --mElements[log_id];
+        --mDroppedElements[log_id];
+    }
 
-    std::unique_ptr<const UidEntry *[]> sort(size_t n, log_id i) { return uidTable[i].sort(n); }
+    std::unique_ptr<const UidEntry *[]> sort(size_t len, log_id id) {
+        return uidTable[id].sort(len);
+    }
 
     // fast track current value by id only
     size_t sizes(log_id_t id) const { return mSizes[id]; }
     size_t elements(log_id_t id) const { return mElements[id]; }
+    size_t realElements(log_id_t id) const {
+        return mElements[id] - mDroppedElements[id];
+    }
     size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
     size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
 
-    std::string format(uid_t uid, unsigned int logMask);
+    std::string format(uid_t uid, unsigned int logMask) const;
 
     // helper (must be locked directly or implicitly by mLogElementsLock)
-    char *pidToName(pid_t pid);
+    const char *pidToName(pid_t pid) const;
     uid_t pidToUid(pid_t pid);
-    char *uidToName(uid_t uid);
+    const char *uidToName(uid_t uid) const;
 };
 
 #endif // _LOGD_LOG_STATISTICS_H__
index 68a0680..229be3c 100644 (file)
@@ -128,9 +128,9 @@ void *LogTimeEntry::threadStart(void *obj) {
 
     lock();
 
-    while (me->threadRunning && !me->isError_Locked()) {
-        uint64_t start = me->mStart;
+    uint64_t start = me->mStart;
 
+    while (me->threadRunning && !me->isError_Locked()) {
         unlock();
 
         if (me->mTail) {
@@ -143,8 +143,11 @@ void *LogTimeEntry::threadStart(void *obj) {
 
         if (start == LogBufferElement::FLUSH_ERROR) {
             me->error_Locked();
+            break;
         }
 
+        me->mStart = start + 1;
+
         if (me->mNonBlock || !me->threadRunning || me->isError_Locked()) {
             break;
         }
index ad005ec..c71beb5 100644 (file)
@@ -17,6 +17,7 @@
 #include <ctype.h>
 
 #include <base/stringprintf.h>
+#include <cutils/properties.h>
 
 #include "LogWhiteBlackList.h"
 
@@ -49,7 +50,8 @@ std::string Prune::format() {
     return std::string("/");
 }
 
-PruneList::PruneList() : mWorstUidEnabled(true) {
+PruneList::PruneList() {
+    init(NULL);
 }
 
 PruneList::~PruneList() {
@@ -72,13 +74,44 @@ int PruneList::init(const char *str) {
         it = mNaughty.erase(it);
     }
 
-    if (!str) {
-        return 0;
+    static const char _default[] = "default";
+    // default here means take ro.logd.filter, persist.logd.filter then
+    // internal default in that order.
+    if (str && !strcmp(str, _default)) {
+        str = NULL;
+    }
+    static const char _disable[] = "disable";
+    if (str && !strcmp(str, _disable)) {
+        str = "";
+    }
+
+    std::string filter;
+
+    if (str) {
+        filter = str;
+    } else {
+        char property[PROPERTY_VALUE_MAX];
+        property_get("ro.logd.filter", property, _default);
+        filter = property;
+        property_get("persist.logd.filter", property, filter.c_str());
+        // default here means take ro.logd.filter
+        if (strcmp(property, _default)) {
+            filter = property;
+        }
+    }
+
+    // default here means take internal default.
+    if (filter == _default) {
+        // See README.property for description of filter format
+        filter = "~!";
+    }
+    if (filter == _disable) {
+        filter = "";
     }
 
     mWorstUidEnabled = false;
 
-    for(; *str; ++str) {
+    for(str = filter.c_str(); *str; ++str) {
         if (isspace(*str)) {
             continue;
         }
index a472efd..e4b23a9 100644 (file)
@@ -12,16 +12,28 @@ ro.build.type               string       if user, logd.statistics & logd.klogd
                                          default false
 persist.logd.logpersistd    string       Enable logpersist daemon, "logcatd"
                                          turns on logcat -f in logd context
-persist.logd.size          number 256K   default size of the buffer for all
-                                         log ids at initial startup, at runtime
-                                         use: logcat -b all -G <value>
+persist.logd.size          number 256K   Global default size of the buffer for
+                                         all log ids at initial startup, at
+                                         runtime use: logcat -b all -G <value>
 persist.logd.size.main     number 256K   Size of the buffer for the main log
 persist.logd.size.system   number 256K   Size of the buffer for the system log
 persist.logd.size.radio    number 256K   Size of the buffer for the radio log
 persist.logd.size.event    number 256K   Size of the buffer for the event log
 persist.logd.size.crash    number 256K   Size of the buffer for the crash log
+persist.logd.filter         string       Pruning filter to optimize content,
+                                         default is ro.logd.filter or
+                                         "~!" which means to prune the oldest
+                                         entries of chattiest UID. At runtime
+                                         use: logcat -P "<string>"
+persist.logd.timestamp      string       The recording timestamp source. Default
+                                         is ro.logd.timestamp. "m[onotonic]" is
+                                         the only supported key character,
+                                         otherwise assumes realtime.
 
 NB:
-- number support multipliers (K or M) for convenience. Range is limited
-  to between 64K and 256M for log buffer sizes. Individual logs override the
-  global default.
+- Number support multipliers (K or M) for convenience. Range is limited
+  to between 64K and 256M for log buffer sizes. Individual log buffer ids
+  such as main, system, ... override global default.
+- Pruning filter is of form of a space-separated list of [~][UID][/PID]
+  references, where '~' prefix means to blacklist otherwise whitelist. For
+  blacklisting, UID may be a '!' to instead reference the chattiest client.
diff --git a/logd/logd.rc b/logd/logd.rc
new file mode 100644 (file)
index 0000000..10f3553
--- /dev/null
@@ -0,0 +1,12 @@
+service logd /system/bin/logd
+    class core
+    socket logd stream 0666 logd logd
+    socket logdr seqpacket 0666 logd logd
+    socket logdw dgram 0222 logd logd
+    group root system readproc
+    writepid /dev/cpuset/system-background/tasks
+
+service logd-reinit /system/bin/logd --reinit
+    oneshot
+    disabled
+    writepid /dev/cpuset/system-background/tasks
index 2ed52be..8e75b37 100644 (file)
 #include <syslog.h>
 #include <unistd.h>
 
+#include <cstdbool>
 #include <memory>
 
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
+#include <packagelistparser/packagelistparser.h>
 #include <private/android_filesystem_config.h>
 #include <utils/threads.h>
 
@@ -104,7 +106,9 @@ static int drop_privs() {
         return -1;
     }
 
-    if (setgroups(0, NULL) == -1) {
+    gid_t groups[] = { AID_READPROC };
+
+    if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) == -1) {
         return -1;
     }
 
@@ -166,6 +170,19 @@ static sem_t reinit;
 static bool reinit_running = false;
 static LogBuffer *logBuf = NULL;
 
+static bool package_list_parser_cb(pkg_info *info, void * /* userdata */) {
+
+    bool rc = true;
+    if (info->uid == uid) {
+        name = strdup(info->name);
+        // false to stop processing
+        rc = false;
+    }
+
+    packagelist_free(info);
+    return rc;
+}
+
 static void *reinit_thread_start(void * /*obj*/) {
     prctl(PR_SET_NAME, "logd.daemon");
     set_sched_policy(0, SP_BACKGROUND);
@@ -180,31 +197,8 @@ static void *reinit_thread_start(void * /*obj*/) {
         if (uid) {
             name = NULL;
 
-            FILE *fp = fopen("/data/system/packages.list", "r");
-            if (fp) {
-                // This simple parser is sensitive to format changes in
-                // frameworks/base/services/core/java/com/android/server/pm/Settings.java
-                // A dependency note has been added to that file to correct
-                // this parser.
-
-                char *buffer = NULL;
-                size_t len;
-                while (getline(&buffer, &len, fp) > 0) {
-                    char *userId = strchr(buffer, ' ');
-                    if (!userId) {
-                        continue;
-                    }
-                    *userId = '\0';
-                    unsigned long value = strtoul(userId + 1, NULL, 10);
-                    if (value != uid) {
-                        continue;
-                    }
-                    name = strdup(buffer);
-                    break;
-                }
-                free(buffer);
-                fclose(fp);
-            }
+            packagelist_parse(package_list_parser_cb, NULL);
+
             uid = 0;
             sem_post(&uidName);
             continue;
@@ -220,6 +214,7 @@ static void *reinit_thread_start(void * /*obj*/) {
         // Anything that reads persist.<property>
         if (logBuf) {
             logBuf->init();
+            logBuf->initPrune(NULL);
         }
     }
 
@@ -305,7 +300,7 @@ static void readDmesg(LogAudit *al, LogKlog *kl) {
     }
     buf[--len] = '\0';
 
-    if (kl) {
+    if (kl && kl->isMonotonic()) {
         kl->synchronize(buf.get(), len);
     }
 
@@ -428,7 +423,7 @@ int main(int argc, char *argv[]) {
 
     LogListener *swl = new LogListener(logBuf, reader);
     // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
-    if (swl->startListener(300)) {
+    if (swl->startListener(600)) {
         exit(1);
     }
 
index 85ca4ac..a7c6b53 100644 (file)
@@ -34,10 +34,6 @@ test_c_flags := \
     -Werror \
     -fno-builtin \
 
-ifneq ($(TARGET_USES_LOGD),false)
-test_c_flags += -DTARGET_USES_LOGD=1
-endif
-
 test_src_files := \
     logd_test.cpp
 
index 3266360..44fa95c 100644 (file)
@@ -137,13 +137,7 @@ TEST(logd, statistics) {
 
     alloc_statistics(&buf, &len);
 
-#ifdef TARGET_USES_LOGD
     ASSERT_TRUE(NULL != buf);
-#else
-    if (!buf) {
-        return;
-    }
-#endif
 
     // remove trailing FF
     char *cp = buf + len - 1;
@@ -167,7 +161,6 @@ TEST(logd, statistics) {
 
     EXPECT_EQ(0, truncated);
 
-#ifdef TARGET_USES_LOGD
     char *main_logs = strstr(cp, "\nChattiest UIDs in main ");
     EXPECT_TRUE(NULL != main_logs);
 
@@ -179,7 +172,6 @@ TEST(logd, statistics) {
 
     char *events_logs = strstr(cp, "\nChattiest UIDs in events ");
     EXPECT_TRUE(NULL != events_logs);
-#endif
 
     delete [] buf;
 }
@@ -419,37 +411,17 @@ TEST(logd, benchmark) {
         return;
     }
 
-#ifdef TARGET_USES_LOGD
     EXPECT_GE(200000UL, ns[log_maximum_retry]); // 104734 user
-#else
-    EXPECT_GE(10000UL, ns[log_maximum_retry]); // 5636 kernel
-#endif
 
-#ifdef TARGET_USES_LOGD
     EXPECT_GE(90000UL, ns[log_maximum]); // 46913 user
-#else
-    EXPECT_GE(10000UL, ns[log_maximum]); // 5637 kernel
-#endif
 
     EXPECT_GE(4096UL, ns[clock_overhead]); // 4095
 
-#ifdef TARGET_USES_LOGD
     EXPECT_GE(250000UL, ns[log_overhead]); // 126886 user
-#else
-    EXPECT_GE(100000UL, ns[log_overhead]); // 50945 kernel
-#endif
 
-#ifdef TARGET_USES_LOGD
     EXPECT_GE(10000UL, ns[log_latency]); // 5669 user space
-#else
-    EXPECT_GE(500000UL, ns[log_latency]); // 254200 kernel
-#endif
 
-#ifdef TARGET_USES_LOGD
     EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user
-#else
-    EXPECT_GE(55000UL, ns[log_delay]); // 27341 kernel
-#endif
 
     for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
         EXPECT_NE(0UL, ns[i]);
@@ -457,14 +429,8 @@ TEST(logd, benchmark) {
 
     alloc_statistics(&buf, &len);
 
-#ifdef TARGET_USES_LOGD
     bool collected_statistics = !!buf;
     EXPECT_EQ(true, collected_statistics);
-#else
-    if (!buf) {
-        return;
-    }
-#endif
 
     ASSERT_TRUE(NULL != buf);
 
index 61b4659..ad45b2c 100644 (file)
@@ -11,7 +11,7 @@ LOCAL_SRC_FILES := logwrap.c
 LOCAL_SHARED_LIBRARIES := libcutils liblog
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := -Werror -std=gnu99
 include $(BUILD_STATIC_LIBRARY)
 
 # ========================================================
@@ -23,7 +23,7 @@ LOCAL_SHARED_LIBRARIES := libcutils liblog
 LOCAL_WHOLE_STATIC_LIBRARIES := liblogwrap
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := -Werror -std=gnu99
 include $(BUILD_SHARED_LIBRARY)
 
 # ========================================================
@@ -33,5 +33,5 @@ include $(CLEAR_VARS)
 LOCAL_SRC_FILES:= logwrapper.c
 LOCAL_MODULE := logwrapper
 LOCAL_STATIC_LIBRARIES := liblog liblogwrap libcutils
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := -Werror -std=gnu99
 include $(BUILD_EXECUTABLE)
index 4307a30..89a8fdd 100644 (file)
@@ -19,6 +19,7 @@
 #define __LIBS_LOGWRAP_H
 
 #include <stdbool.h>
+#include <stdint.h>
 
 __BEGIN_DECLS
 
@@ -53,6 +54,9 @@ __BEGIN_DECLS
  *           the specified log until the child has exited.
  *   file_path: if log_target has the LOG_FILE bit set, then this parameter
  *           must be set to the pathname of the file to log to.
+ *   opts: set to non-NULL if you want to use one or more of the
+ *           FORK_EXECVP_OPTION_* features.
+ *   opts_len: the length of the opts array. When opts is NULL, pass 0.
  *
  * Return value:
  *   0 when logwrap successfully run the child process and captured its status
@@ -68,8 +72,30 @@ __BEGIN_DECLS
 #define LOG_KLOG        2
 #define LOG_FILE        4
 
+/* Write data to child's stdin. */
+#define FORK_EXECVP_OPTION_INPUT             0
+/* Capture data from child's stdout and stderr. */
+#define FORK_EXECVP_OPTION_CAPTURE_OUTPUT    1
+
+struct AndroidForkExecvpOption {
+    int opt_type;
+    union {
+        struct {
+            const uint8_t* input;
+            size_t input_len;
+        } opt_input;
+        struct {
+            void (*on_output)(const uint8_t* /*output*/,
+                              size_t /*output_len*/,
+                              void* /* user_pointer */);
+            void* user_pointer;
+        } opt_capture_output;
+    };
+};
+
 int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
-        int log_target, bool abbreviated, char *file_path);
+        int log_target, bool abbreviated, char *file_path,
+        const struct AndroidForkExecvpOption* opts, size_t opts_len);
 
 /* Similar to above, except abbreviated logging is not available, and if logwrap
  * is true, logging is to the Android system log, and if false, there is no
@@ -79,7 +105,8 @@ static inline int android_fork_execvp(int argc, char* argv[], int *status,
                                      bool ignore_int_quit, bool logwrap)
 {
     return android_fork_execvp_ext(argc, argv, status, ignore_int_quit,
-                                   (logwrap ? LOG_ALOG : LOG_NONE), false, NULL);
+                                   (logwrap ? LOG_ALOG : LOG_NONE), false, NULL,
+                                   NULL, 0);
 }
 
 __END_DECLS
index 83576fb..28d6de7 100644 (file)
@@ -291,7 +291,8 @@ static void print_abbr_buf(struct log_info *log_info) {
 }
 
 static int parent(const char *tag, int parent_read, pid_t pid,
-        int *chld_sts, int log_target, bool abbreviated, char *file_path) {
+        int *chld_sts, int log_target, bool abbreviated, char *file_path,
+        const struct AndroidForkExecvpOption* opts, size_t opts_len) {
     int status = 0;
     char buffer[4096];
     struct pollfd poll_fds[] = {
@@ -355,7 +356,15 @@ static int parent(const char *tag, int parent_read, pid_t pid,
         }
 
         if (poll_fds[0].revents & POLLIN) {
-            sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b);
+            sz = TEMP_FAILURE_RETRY(
+                read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
+
+            for (size_t i = 0; sz > 0 && i < opts_len; ++i) {
+                if (opts[i].opt_type == FORK_EXECVP_OPTION_CAPTURE_OUTPUT) {
+                  opts[i].opt_capture_output.on_output(
+                      (uint8_t*)&buffer[b], sz, opts[i].opt_capture_output.user_pointer);
+                }
+            }
 
             sz += b;
             // Log one line at a time
@@ -473,7 +482,8 @@ static void child(int argc, char* argv[]) {
 }
 
 int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
-        int log_target, bool abbreviated, char *file_path) {
+        int log_target, bool abbreviated, char *file_path,
+        const struct AndroidForkExecvpOption* opts, size_t opts_len) {
     pid_t pid;
     int parent_ptty;
     int child_ptty;
@@ -490,7 +500,7 @@ int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int
     }
 
     /* Use ptty instead of socketpair so that STDOUT is not buffered */
-    parent_ptty = open("/dev/ptmx", O_RDWR);
+    parent_ptty = TEMP_FAILURE_RETRY(open("/dev/ptmx", O_RDWR));
     if (parent_ptty < 0) {
         ERROR("Cannot create parent ptty\n");
         rc = -1;
@@ -505,7 +515,7 @@ int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int
         goto err_ptty;
     }
 
-    child_ptty = open(child_devname, O_RDWR);
+    child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR));
     if (child_ptty < 0) {
         ERROR("Cannot open child_ptty\n");
         rc = -1;
@@ -528,7 +538,13 @@ int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int
         pthread_sigmask(SIG_SETMASK, &oldset, NULL);
         close(parent_ptty);
 
-        // redirect stdout and stderr
+        // redirect stdin, stdout and stderr
+        for (size_t i = 0; i < opts_len; ++i) {
+            if (opts[i].opt_type == FORK_EXECVP_OPTION_INPUT) {
+                dup2(child_ptty, 0);
+                break;
+            }
+        }
         dup2(child_ptty, 1);
         dup2(child_ptty, 2);
         close(child_ptty);
@@ -545,8 +561,24 @@ int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int
             sigaction(SIGQUIT, &ignact, &quitact);
         }
 
+        for (size_t i = 0; i < opts_len; ++i) {
+            if (opts[i].opt_type == FORK_EXECVP_OPTION_INPUT) {
+                size_t left = opts[i].opt_input.input_len;
+                const uint8_t* input = opts[i].opt_input.input;
+                while (left > 0) {
+                    ssize_t res =
+                        TEMP_FAILURE_RETRY(write(parent_ptty, input, left));
+                    if (res < 0) {
+                        break;
+                    }
+                    left -= res;
+                    input += res;
+                }
+            }
+        }
+
         rc = parent(argv[0], parent_ptty, pid, status, log_target,
-                    abbreviated, file_path);
+                    abbreviated, file_path, opts, opts_len);
     }
 
     if (ignore_int_quit) {
index 9e0385d..55b71c7 100644 (file)
@@ -81,7 +81,7 @@ int main(int argc, char* argv[]) {
     }
 
     rc = android_fork_execvp_ext(argc, &argv[0], &status, true,
-                                 log_target, abbreviated, NULL);
+                                 log_target, abbreviated, NULL, NULL, 0);
     if (!rc) {
         if (WIFEXITED(status))
             rc = WEXITSTATUS(status);
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
new file mode 100644 (file)
index 0000000..291c983
--- /dev/null
@@ -0,0 +1,171 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+metrics_cpp_extension := .cc
+libmetrics_sources := \
+  c_metrics_library.cc \
+  metrics_library.cc \
+  serialization/metric_sample.cc \
+  serialization/serialization_utils.cc \
+  timer.cc
+
+metrics_client_sources := \
+  metrics_client.cc
+
+metrics_daemon_common := \
+  collectors/averaged_statistics_collector.cc \
+  collectors/cpu_usage_collector.cc \
+  collectors/disk_usage_collector.cc \
+  metrics_daemon.cc \
+  persistent_integer.cc \
+  serialization/metric_sample.cc \
+  serialization/serialization_utils.cc \
+  uploader/metrics_hashes.cc \
+  uploader/metrics_log_base.cc \
+  uploader/metrics_log.cc \
+  uploader/sender_http.cc \
+  uploader/system_profile_cache.cc \
+  uploader/upload_service.cc \
+
+metrics_tests_sources := \
+  collectors/averaged_statistics_collector_test.cc \
+  collectors/cpu_usage_collector_test.cc \
+  metrics_daemon_test.cc \
+  metrics_library_test.cc \
+  persistent_integer_test.cc \
+  serialization/serialization_utils_unittest.cc \
+  timer_test.cc \
+  uploader/metrics_hashes_unittest.cc \
+  uploader/metrics_log_base_unittest.cc \
+  uploader/mock/sender_mock.cc \
+  uploader/upload_service_test.cc \
+
+metrics_CFLAGS := -Wall \
+  -Wno-char-subscripts \
+  -Wno-missing-field-initializers \
+  -Wno-unused-function \
+  -Wno-unused-parameter \
+  -Werror \
+  -fvisibility=default
+metrics_CPPFLAGS := -Wno-non-virtual-dtor \
+  -Wno-sign-promo \
+  -Wno-strict-aliasing \
+  -fvisibility=default
+metrics_includes := external/gtest/include \
+  $(LOCAL_PATH)/include
+libmetrics_shared_libraries := libchrome libbrillo
+metrics_daemon_shared_libraries := $(libmetrics_shared_libraries) \
+  libbrillo-http \
+  libbrillo-dbus \
+  libchrome-dbus \
+  libdbus \
+  libmetrics \
+  libprotobuf-cpp-lite \
+  librootdev \
+  libupdate_engine_client \
+  libweaved \
+
+# Shared library for metrics.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libmetrics
+LOCAL_C_INCLUDES := $(metrics_includes)
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := $(libmetrics_shared_libraries)
+LOCAL_SRC_FILES := $(libmetrics_sources)
+include $(BUILD_SHARED_LIBRARY)
+
+# CLI client for metrics.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_client
+LOCAL_C_INCLUDES := $(metrics_includes)
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
+LOCAL_SHARED_LIBRARIES := $(libmetrics_shared_libraries) \
+  libmetrics
+LOCAL_SRC_FILES := $(metrics_client_sources)
+include $(BUILD_EXECUTABLE)
+
+# Protobuf library for metrics_daemon.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_daemon_protos
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+generated_sources_dir := $(call local-generated-sources-dir)
+LOCAL_EXPORT_C_INCLUDE_DIRS += \
+    $(generated_sources_dir)/proto/system/core/metricsd
+LOCAL_SRC_FILES :=  $(call all-proto-files-under,uploader/proto)
+include $(BUILD_STATIC_LIBRARY)
+
+# metrics daemon.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_daemon
+LOCAL_C_INCLUDES := $(metrics_includes)
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
+LOCAL_INIT_RC := metrics_daemon.rc
+LOCAL_REQUIRED_MODULES := \
+  metrics.json \
+  metrics.schema.json \
+
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := $(metrics_daemon_shared_libraries)
+LOCAL_CLANG := true
+LOCAL_SRC_FILES := $(metrics_daemon_common) \
+  metrics_daemon_main.cc
+LOCAL_STATIC_LIBRARIES := metrics_daemon_protos
+include $(BUILD_EXECUTABLE)
+
+# Unit tests for metrics.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_tests
+LOCAL_CLANG := true
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := $(metrics_daemon_shared_libraries)
+LOCAL_SRC_FILES := $(metrics_tests_sources) $(metrics_daemon_common)
+LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metrics_daemon_protos
+
+include $(BUILD_NATIVE_TEST)
+
+# Weave schema files
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics.json
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/weaved/commands
+LOCAL_SRC_FILES := etc/weaved/commands/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics.schema.json
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/weaved/states
+LOCAL_SRC_FILES := etc/weaved/states/$(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/metricsd/OWNERS b/metricsd/OWNERS
new file mode 100644 (file)
index 0000000..7f5e50d
--- /dev/null
@@ -0,0 +1,3 @@
+semenzato@chromium.org
+derat@chromium.org
+bsimonnet@chromium.org
diff --git a/metricsd/README b/metricsd/README
new file mode 100644 (file)
index 0000000..d4c9a0e
--- /dev/null
@@ -0,0 +1,150 @@
+Copyright (C) 2015 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+================================================================================
+
+The Chrome OS "metrics" package contains utilities for client-side user metric
+collection.
+When Chrome is installed, Chrome will take care of aggregating and uploading the
+metrics to the UMA server.
+When Chrome is not installed (embedded build) and the metrics_uploader USE flag
+is set, metrics_daemon will aggregate and upload the metrics itself.
+
+
+================================================================================
+The Metrics Library: libmetrics
+================================================================================
+
+libmetrics is a small library that implements the basic C and C++ API for
+metrics collection. All metrics collection is funneled through this library. The
+easiest and recommended way for a client-side module to collect user metrics is
+to link libmetrics and use its APIs to send metrics to Chrome for transport to
+UMA. In order to use the library in a module, you need to do the following:
+
+- Add a dependence (DEPEND and RDEPEND) on chromeos-base/metrics to the module's
+  ebuild.
+
+- Link the module with libmetrics (for example, by passing -lmetrics to the
+  module's link command). Both libmetrics.so and libmetrics.a are built and
+  installed under $SYSROOT/usr/lib/. Note that by default -lmetrics will link
+  against libmetrics.so, which is preferred.
+
+- To access the metrics library API in the module, include the
+  <metrics/metrics_library.h> header file. The file is installed in
+  $SYSROOT/usr/include/ when the metrics library is built and installed.
+
+- The API is documented in metrics_library.h under src/platform/metrics/. Before
+  using the API methods, a MetricsLibrary object needs to be constructed and
+  initialized through its Init method.
+
+  For more information on the C API see c_metrics_library.h.
+
+- Samples are sent to Chrome only if the "/home/chronos/Consent To Send Stats"
+  file exists or the metrics are declared enabled in the policy file (see the
+  AreMetricsEnabled API method).
+
+- On the target platform, shortly after the sample is sent, it should be visible
+  in Chrome through "about:histograms".
+
+
+================================================================================
+Histogram Naming Convention
+================================================================================
+
+Use TrackerArea.MetricName. For example:
+
+Platform.DailyUseTime
+Network.TimeToDrop
+
+
+================================================================================
+Server Side
+================================================================================
+
+If the histogram data is visible in about:histograms, it will be sent by an
+official Chrome build to UMA, assuming the user has opted into metrics
+collection. To make the histogram visible on "chromedashboard", the histogram
+description XML file needs to be updated (steps 2 and 3 after following the
+"Details on how to add your own histograms" link under the Histograms tab).
+Include the string "Chrome OS" in the histogram description so that it's easier
+to distinguish Chrome OS specific metrics from general Chrome histograms.
+
+The UMA server logs and keeps the collected field data even if the metric's name
+is not added to the histogram XML. However, the dashboard histogram for that
+metric will show field data as of the histogram XML update date; it will not
+include data for older dates. If past data needs to be displayed, manual
+server-side intervention is required. In other words, one should assume that
+field data collection starts only after the histogram XML has been updated.
+
+
+================================================================================
+The Metrics Client: metrics_client
+================================================================================
+
+metrics_client is a simple shell command-line utility for sending histogram
+samples and user actions. It's installed under /usr/bin on the target platform
+and uses libmetrics to send the data to Chrome. The utility is useful for
+generating metrics from shell scripts.
+
+For usage information and command-line options, run "metrics_client" on the
+target platform or look for "Usage:" in metrics_client.cc.
+
+
+================================================================================
+The Metrics Daemon: metrics_daemon
+================================================================================
+
+metrics_daemon is a daemon that runs in the background on the target platform
+and is intended for passive or ongoing metrics collection, or metrics collection
+requiring feedback from multiple modules. For example, it listens to D-Bus
+signals related to the user session and screen saver states to determine if the
+user is actively using the device or not and generates the corresponding
+data. The metrics daemon uses libmetrics to send the data to Chrome.
+
+The recommended way to generate metrics data from a module is to link and use
+libmetrics directly. However, the module could instead send signals to or
+communicate in some alternative way with the metrics daemon. Then the metrics
+daemon needs to monitor for the relevant events and take appropriate action --
+for example, aggregate data and send the histogram samples.
+
+
+================================================================================
+FAQ
+================================================================================
+
+Q. What should my histogram's |min| and |max| values be set at?
+
+A. You should set the values to a range that covers the vast majority of samples
+   that would appear in the field. Note that samples below the |min| will still
+   be collected in the underflow bucket and samples above the |max| will end up
+   in the overflow bucket. Also, the reported mean of the data will be correct
+   regardless of the range.
+
+Q. How many buckets should I use in my histogram?
+
+A. You should allocate as many buckets as necessary to perform proper analysis
+   on the collected data. Note, however, that the memory allocated in Chrome for
+   each histogram is proportional to the number of buckets. Therefore, it is
+   strongly recommended to keep this number low (e.g., 50 is normal, while 100
+   is probably high).
+
+Q. When should I use an enumeration (linear) histogram vs. a regular
+   (exponential) histogram?
+
+A. Enumeration histograms should really be used only for sampling enumerated
+   events and, in some cases, percentages. Normally, you should use a regular
+   histogram with exponential bucket layout that provides higher resolution at
+   the low end of the range and lower resolution at the high end. Regular
+   histograms are generally used for collecting performance data (e.g., timing,
+   memory usage, power) as well as aggregated event counts.
diff --git a/metricsd/WATCHLISTS b/metricsd/WATCHLISTS
new file mode 100644 (file)
index 0000000..a051f35
--- /dev/null
@@ -0,0 +1,16 @@
+# See http://dev.chromium.org/developers/contributing-code/watchlists for
+# a description of this file's format.
+# Please keep these keys in alphabetical order.
+
+{
+  'WATCHLIST_DEFINITIONS': {
+    'all': {
+      'filepath': '.',
+    },
+  },
+  'WATCHLISTS': {
+    'all': ['petkov@chromium.org',
+                'semenzato@chromium.org',
+                'sosa@chromium.org']
+  },
+}
diff --git a/metricsd/c_metrics_library.cc b/metricsd/c_metrics_library.cc
new file mode 100644 (file)
index 0000000..0503876
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// C wrapper to libmetrics
+//
+
+#include "metrics/c_metrics_library.h"
+
+#include <string>
+
+#include "metrics/metrics_library.h"
+
+extern "C" CMetricsLibrary CMetricsLibraryNew(void) {
+  MetricsLibrary* lib = new MetricsLibrary;
+  return reinterpret_cast<CMetricsLibrary>(lib);
+}
+
+extern "C" void CMetricsLibraryDelete(CMetricsLibrary handle) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  delete lib;
+}
+
+extern "C" void CMetricsLibraryInit(CMetricsLibrary handle) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib != NULL)
+    lib->Init();
+}
+
+extern "C" int CMetricsLibrarySendToUMA(CMetricsLibrary handle,
+                                        const char* name, int sample,
+                                        int min, int max, int nbuckets) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->SendToUMA(std::string(name), sample, min, max, nbuckets);
+}
+
+extern "C" int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle,
+                                            const char* name, int sample,
+                                            int max) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->SendEnumToUMA(std::string(name), sample, max);
+}
+
+extern "C" int CMetricsLibrarySendSparseToUMA(CMetricsLibrary handle,
+                                              const char* name, int sample) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->SendSparseToUMA(std::string(name), sample);
+}
+
+extern "C" int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle,
+                                                  const char* action) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->SendUserActionToUMA(std::string(action));
+}
+
+extern "C" int CMetricsLibrarySendCrashToUMA(CMetricsLibrary handle,
+                                            const char* crash_kind) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->SendCrashToUMA(crash_kind);
+}
+
+extern "C" int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle) {
+  MetricsLibrary* lib = reinterpret_cast<MetricsLibrary*>(handle);
+  if (lib == NULL)
+    return 0;
+  return lib->AreMetricsEnabled();
+}
diff --git a/metricsd/collectors/averaged_statistics_collector.cc b/metricsd/collectors/averaged_statistics_collector.cc
new file mode 100644 (file)
index 0000000..0931e7b
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "averaged_statistics_collector.h"
+
+#include <base/files/file_util.h>
+#include <base/files/file_path.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+
+#include "metrics_daemon.h"
+
+namespace {
+
+// disk stats metrics
+
+// The {Read,Write}Sectors numbers are in sectors/second.
+// A sector is usually 512 bytes.
+const char kReadSectorsHistogramName[] = "Platform.ReadSectors";
+const char kWriteSectorsHistogramName[] = "Platform.WriteSectors";
+const int kDiskMetricsStatItemCount = 11;
+
+// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
+// sectors.
+const int kSectorsIOMax = 500000;  // sectors/second
+const int kSectorsBuckets = 50;    // buckets
+
+// Page size is 4k, sector size is 0.5k.  We're not interested in page fault
+// rates that the disk cannot sustain.
+const int kPageFaultsMax = kSectorsIOMax / 8;  // Page faults/second
+const int kPageFaultsBuckets = 50;
+
+// Major page faults, i.e. the ones that require data to be read from disk.
+const char kPageFaultsHistogramName[] = "Platform.PageFaults";
+
+// Swap in and Swap out
+const char kSwapInHistogramName[] = "Platform.SwapIn";
+const char kSwapOutHistogramName[] = "Platform.SwapOut";
+
+const int kIntervalBetweenCollection = 60;  // seconds
+const int kCollectionDuration = 1;  // seconds
+
+}  // namespace
+
+AveragedStatisticsCollector::AveragedStatisticsCollector(
+    MetricsLibraryInterface* metrics_library,
+    const std::string& diskstats_path,
+    const std::string& vmstats_path) :
+  metrics_lib_(metrics_library),
+  diskstats_path_(diskstats_path),
+  vmstats_path_(vmstats_path) {
+}
+
+void AveragedStatisticsCollector::ScheduleWait() {
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&AveragedStatisticsCollector::WaitCallback,
+                 base::Unretained(this)),
+      base::TimeDelta::FromSeconds(
+          kIntervalBetweenCollection - kCollectionDuration));
+}
+
+void AveragedStatisticsCollector::ScheduleCollect() {
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&AveragedStatisticsCollector::CollectCallback,
+                 base::Unretained(this)),
+      base::TimeDelta::FromSeconds(kCollectionDuration));
+}
+
+void AveragedStatisticsCollector::WaitCallback() {
+  ReadInitialValues();
+  ScheduleCollect();
+}
+
+void AveragedStatisticsCollector::CollectCallback() {
+  Collect();
+  ScheduleWait();
+}
+
+void AveragedStatisticsCollector::ReadInitialValues() {
+  stats_start_time_ = MetricsDaemon::GetActiveTime();
+  DiskStatsReadStats(&read_sectors_, &write_sectors_);
+  VmStatsReadStats(&vmstats_);
+}
+
+bool AveragedStatisticsCollector::DiskStatsReadStats(
+    uint64_t* read_sectors, uint64_t* write_sectors) {
+  CHECK(read_sectors);
+  CHECK(write_sectors);
+  std::string line;
+  if (diskstats_path_.empty()) {
+    return false;
+  }
+
+  if (!base::ReadFileToString(base::FilePath(diskstats_path_), &line)) {
+    PLOG(WARNING) << "Could not read disk stats from "
+                  << diskstats_path_.value();
+    return false;
+  }
+
+  std::vector<std::string> parts = base::SplitString(
+      line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+  if (parts.size() != kDiskMetricsStatItemCount) {
+    LOG(ERROR) << "Could not parse disk stat correctly. Expected "
+               << kDiskMetricsStatItemCount << " elements but got "
+               << parts.size();
+    return false;
+  }
+  if (!base::StringToUint64(parts[2], read_sectors)) {
+    LOG(ERROR) << "Couldn't convert read sectors " << parts[2] << " to uint64";
+    return false;
+  }
+  if (!base::StringToUint64(parts[6], write_sectors)) {
+    LOG(ERROR) << "Couldn't convert write sectors " << parts[6] << " to uint64";
+    return false;
+  }
+
+  return true;
+}
+
+bool AveragedStatisticsCollector::VmStatsParseStats(
+    const char* stats, struct VmstatRecord* record) {
+  CHECK(stats);
+  CHECK(record);
+  base::StringPairs pairs;
+  base::SplitStringIntoKeyValuePairs(stats, ' ', '\n', &pairs);
+
+  for (base::StringPairs::iterator it = pairs.begin();
+       it != pairs.end(); ++it) {
+    if (it->first == "pgmajfault" &&
+        !base::StringToUint64(it->second, &record->page_faults)) {
+      return false;
+    }
+    if (it->first == "pswpin" &&
+        !base::StringToUint64(it->second, &record->swap_in)) {
+      return false;
+    }
+    if (it->first == "pswpout" &&
+        !base::StringToUint64(it->second, &record->swap_out)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool AveragedStatisticsCollector::VmStatsReadStats(struct VmstatRecord* stats) {
+  CHECK(stats);
+  std::string value_string;
+  if (!base::ReadFileToString(vmstats_path_, &value_string)) {
+    LOG(WARNING) << "cannot read " << vmstats_path_.value();
+    return false;
+  }
+  return VmStatsParseStats(value_string.c_str(), stats);
+}
+
+void AveragedStatisticsCollector::Collect() {
+  uint64_t read_sectors_now, write_sectors_now;
+  struct VmstatRecord vmstats_now;
+  double time_now = MetricsDaemon::GetActiveTime();
+  double delta_time = time_now - stats_start_time_;
+  bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
+                                              &write_sectors_now);
+
+  int delta_read = read_sectors_now - read_sectors_;
+  int delta_write = write_sectors_now - write_sectors_;
+  int read_sectors_per_second = delta_read / delta_time;
+  int write_sectors_per_second = delta_write / delta_time;
+  bool vmstats_success = VmStatsReadStats(&vmstats_now);
+  uint64_t delta_faults = vmstats_now.page_faults - vmstats_.page_faults;
+  uint64_t delta_swap_in = vmstats_now.swap_in - vmstats_.swap_in;
+  uint64_t delta_swap_out = vmstats_now.swap_out - vmstats_.swap_out;
+  uint64_t page_faults_per_second = delta_faults / delta_time;
+  uint64_t swap_in_per_second = delta_swap_in / delta_time;
+  uint64_t swap_out_per_second = delta_swap_out / delta_time;
+  if (diskstats_success) {
+    metrics_lib_->SendToUMA(kReadSectorsHistogramName,
+                            read_sectors_per_second,
+                            1,
+                            kSectorsIOMax,
+                            kSectorsBuckets);
+    metrics_lib_->SendToUMA(kWriteSectorsHistogramName,
+                            write_sectors_per_second,
+                            1,
+                            kSectorsIOMax,
+                            kSectorsBuckets);
+  }
+  if (vmstats_success) {
+    metrics_lib_->SendToUMA(kPageFaultsHistogramName,
+                            page_faults_per_second,
+                            1,
+                            kPageFaultsMax,
+                            kPageFaultsBuckets);
+    metrics_lib_->SendToUMA(kSwapInHistogramName,
+                            swap_in_per_second,
+                            1,
+                            kPageFaultsMax,
+                            kPageFaultsBuckets);
+    metrics_lib_->SendToUMA(kSwapOutHistogramName,
+                            swap_out_per_second,
+                            1,
+                            kPageFaultsMax,
+                            kPageFaultsBuckets);
+  }
+}
diff --git a/metricsd/collectors/averaged_statistics_collector.h b/metricsd/collectors/averaged_statistics_collector.h
new file mode 100644 (file)
index 0000000..753f70c
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICSD_COLLECTORS_AVERAGED_STATISTICS_COLLECTOR_H_
+#define METRICSD_COLLECTORS_AVERAGED_STATISTICS_COLLECTOR_H_
+
+#include "metrics/metrics_library.h"
+
+class AveragedStatisticsCollector {
+ public:
+  AveragedStatisticsCollector(MetricsLibraryInterface* metrics_library,
+                              const std::string& diskstats_path,
+                              const std::string& vmstat_path);
+
+  // Schedule a wait period.
+  void ScheduleWait();
+
+  // Schedule a collection period.
+  void ScheduleCollect();
+
+  // Callback used by the main loop.
+  void CollectCallback();
+
+  // Callback used by the main loop.
+  void WaitCallback();
+
+  // Read and store the initial values at the beginning of a collection cycle.
+  void ReadInitialValues();
+
+  // Collect the disk usage statistics and report them.
+  void Collect();
+
+ private:
+  friend class AveragedStatisticsTest;
+  FRIEND_TEST(AveragedStatisticsTest, ParseDiskStats);
+  FRIEND_TEST(AveragedStatisticsTest, ParseVmStats);
+
+  // Record for retrieving and reporting values from /proc/vmstat
+  struct VmstatRecord {
+    uint64_t page_faults;    // major faults
+    uint64_t swap_in;        // pages swapped in
+    uint64_t swap_out;       // pages swapped out
+  };
+
+  // Read the disk read/write statistics for the main disk.
+  bool DiskStatsReadStats(uint64_t* read_sectors, uint64_t* write_sectors);
+
+  // Parse the content of the vmstats file into |record|.
+  bool VmStatsParseStats(const char* stats, struct VmstatRecord* record);
+
+  // Read the vmstats into |stats|.
+  bool VmStatsReadStats(struct VmstatRecord* stats);
+
+  MetricsLibraryInterface* metrics_lib_;
+  base::FilePath diskstats_path_;
+  base::FilePath vmstats_path_;
+
+  // Values observed at the beginning of the collection period.
+  uint64_t read_sectors_;
+  uint64_t write_sectors_;
+  struct VmstatRecord vmstats_;
+
+  double stats_start_time_;
+};
+
+#endif  // METRICSD_COLLECTORS_AVERAGED_STATISTICS_COLLECTOR_H_
diff --git a/metricsd/collectors/averaged_statistics_collector_test.cc b/metricsd/collectors/averaged_statistics_collector_test.cc
new file mode 100644 (file)
index 0000000..9c97f00
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "averaged_statistics_collector.h"
+
+#include <inttypes.h>
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/strings/stringprintf.h>
+#include <gtest/gtest.h>
+
+
+static const char kFakeDiskStatsFormat[] =
+    "    1793     1788    %" PRIu64 "   105580    "
+    "    196      175     %" PRIu64 "    30290    "
+    "    0    44060   135850\n";
+static const uint64_t kFakeReadSectors[] = {80000, 100000};
+static const uint64_t kFakeWriteSectors[] = {3000, 4000};
+
+
+class AveragedStatisticsTest : public testing::Test {
+ protected:
+  std::string kFakeDiskStats0;
+  std::string kFakeDiskStats1;
+
+  virtual void SetUp() {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    disk_stats_path_ = temp_dir_.path().Append("disk_stats");
+    collector_.reset(new AveragedStatisticsCollector(
+        &metrics_lib_, disk_stats_path_.value(), ""));
+
+    kFakeDiskStats0 = base::StringPrintf(kFakeDiskStatsFormat,
+                                         kFakeReadSectors[0],
+                                         kFakeWriteSectors[0]);
+    kFakeDiskStats1 = base::StringPrintf(kFakeDiskStatsFormat,
+                                         kFakeReadSectors[1],
+                                         kFakeWriteSectors[1]);
+
+    CreateFakeDiskStatsFile(kFakeDiskStats0);
+  }
+
+  // Creates or overwrites an input file containing fake disk stats.
+  void CreateFakeDiskStatsFile(const std::string& fake_stats) {
+    EXPECT_EQ(base::WriteFile(disk_stats_path_,
+                              fake_stats.data(), fake_stats.size()),
+              fake_stats.size());
+  }
+
+  // Collector used for tests.
+  scoped_ptr<AveragedStatisticsCollector> collector_;
+
+  // Temporary directory used for tests.
+  base::ScopedTempDir temp_dir_;
+
+  // Path for the fake files.
+  base::FilePath disk_stats_path_;
+
+  MetricsLibrary metrics_lib_;
+};
+
+TEST_F(AveragedStatisticsTest, ParseDiskStats) {
+  uint64_t read_sectors_now, write_sectors_now;
+  CreateFakeDiskStatsFile(kFakeDiskStats0);
+  ASSERT_TRUE(collector_->DiskStatsReadStats(&read_sectors_now,
+                                             &write_sectors_now));
+  EXPECT_EQ(read_sectors_now, kFakeReadSectors[0]);
+  EXPECT_EQ(write_sectors_now, kFakeWriteSectors[0]);
+
+  CreateFakeDiskStatsFile(kFakeDiskStats1);
+  ASSERT_TRUE(collector_->DiskStatsReadStats(&read_sectors_now,
+                                             &write_sectors_now));
+  EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]);
+  EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]);
+}
+
+TEST_F(AveragedStatisticsTest, ParseVmStats) {
+  static char kVmStats[] = "pswpin 1345\npswpout 8896\n"
+    "foo 100\nbar 200\npgmajfault 42\netcetc 300\n";
+  struct AveragedStatisticsCollector::VmstatRecord stats;
+  EXPECT_TRUE(collector_->VmStatsParseStats(kVmStats, &stats));
+  EXPECT_EQ(stats.page_faults, 42);
+  EXPECT_EQ(stats.swap_in, 1345);
+  EXPECT_EQ(stats.swap_out, 8896);
+}
diff --git a/metricsd/collectors/cpu_usage_collector.cc b/metricsd/collectors/cpu_usage_collector.cc
new file mode 100644 (file)
index 0000000..05934b4
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "collectors/cpu_usage_collector.h"
+
+#include <base/bind.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/sys_info.h>
+
+#include "metrics/metrics_library.h"
+
+namespace {
+
+const char kCpuUsagePercent[] = "Platform.CpuUsage.Percent";
+const char kMetricsProcStatFileName[] = "/proc/stat";
+const int kMetricsProcStatFirstLineItemsCount = 11;
+
+// Collect every minute.
+const int kCollectionIntervalSecs = 60;
+
+}  // namespace
+
+using base::TimeDelta;
+
+CpuUsageCollector::CpuUsageCollector(MetricsLibraryInterface* metrics_library) {
+  CHECK(metrics_library);
+  metrics_lib_ = metrics_library;
+  collect_interval_ = TimeDelta::FromSeconds(kCollectionIntervalSecs);
+}
+
+void CpuUsageCollector::Init() {
+  num_cpu_ = base::SysInfo::NumberOfProcessors();
+
+  // Get ticks per second (HZ) on this system.
+  // Sysconf cannot fail, so no sanity checks are needed.
+  ticks_per_second_ = sysconf(_SC_CLK_TCK);
+  CHECK_GT(ticks_per_second_, uint64_t(0))
+      << "Number of ticks per seconds should be positive.";
+
+  latest_cpu_use_ = GetCumulativeCpuUse();
+}
+
+void CpuUsageCollector::CollectCallback() {
+  Collect();
+  Schedule();
+}
+
+void CpuUsageCollector::Schedule() {
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&CpuUsageCollector::CollectCallback, base::Unretained(this)),
+      collect_interval_);
+}
+
+void CpuUsageCollector::Collect() {
+  TimeDelta cpu_use = GetCumulativeCpuUse();
+  TimeDelta diff_per_cpu = (cpu_use - latest_cpu_use_) / num_cpu_;
+  latest_cpu_use_ = cpu_use;
+
+  // Report the cpu usage as a percentage of the total cpu usage possible.
+  int percent_use = diff_per_cpu.InMilliseconds() * 100 /
+      (kCollectionIntervalSecs * 1000);
+
+  metrics_lib_->SendEnumToUMA(kCpuUsagePercent, percent_use, 101);
+}
+
+TimeDelta CpuUsageCollector::GetCumulativeCpuUse() {
+  base::FilePath proc_stat_path(kMetricsProcStatFileName);
+  std::string proc_stat_string;
+  if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) {
+    LOG(WARNING) << "cannot open " << kMetricsProcStatFileName;
+    return TimeDelta();
+  }
+
+  uint64_t user_ticks, user_nice_ticks, system_ticks;
+  if (!ParseProcStat(proc_stat_string, &user_ticks, &user_nice_ticks,
+                     &system_ticks)) {
+    return TimeDelta();
+  }
+
+  uint64_t total = user_ticks + user_nice_ticks + system_ticks;
+  return TimeDelta::FromMicroseconds(
+      total * 1000 * 1000 / ticks_per_second_);
+}
+
+bool CpuUsageCollector::ParseProcStat(const std::string& stat_content,
+                                      uint64_t *user_ticks,
+                                      uint64_t *user_nice_ticks,
+                                      uint64_t *system_ticks) {
+  std::vector<std::string> proc_stat_lines;
+  base::SplitString(stat_content, '\n', &proc_stat_lines);
+  if (proc_stat_lines.empty()) {
+    LOG(WARNING) << "No lines found in " << kMetricsProcStatFileName;
+    return false;
+  }
+  std::vector<std::string> proc_stat_totals;
+  base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
+
+  if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
+      proc_stat_totals[0] != "cpu" ||
+      !base::StringToUint64(proc_stat_totals[1], user_ticks) ||
+      !base::StringToUint64(proc_stat_totals[2], user_nice_ticks) ||
+      !base::StringToUint64(proc_stat_totals[3], system_ticks)) {
+    LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0];
+    return false;
+  }
+  return true;
+}
diff --git a/metricsd/collectors/cpu_usage_collector.h b/metricsd/collectors/cpu_usage_collector.h
new file mode 100644 (file)
index 0000000..f81dfcb
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_
+#define METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_
+
+#include <base/time/time.h>
+
+#include "metrics/metrics_library.h"
+
+class CpuUsageCollector {
+ public:
+  CpuUsageCollector(MetricsLibraryInterface* metrics_library);
+
+  // Initialize this collector's state.
+  void Init();
+
+  // Schedule a collection interval.
+  void Schedule();
+
+  // Callback called at the end of the collection interval.
+  void CollectCallback();
+
+  // Measure the cpu use and report it.
+  void Collect();
+
+  // Gets the current cumulated Cpu usage.
+  base::TimeDelta GetCumulativeCpuUse();
+
+ private:
+  FRIEND_TEST(CpuUsageTest, ParseProcStat);
+  bool ParseProcStat(const std::string& stat_content,
+                     uint64_t *user_ticks,
+                     uint64_t *user_nice_ticks,
+                     uint64_t *system_ticks);
+
+  int num_cpu_;
+  uint32_t ticks_per_second_;
+
+  base::TimeDelta collect_interval_;
+  base::TimeDelta latest_cpu_use_;
+
+  MetricsLibraryInterface* metrics_lib_;
+};
+
+#endif  // METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_
diff --git a/metricsd/collectors/cpu_usage_collector_test.cc b/metricsd/collectors/cpu_usage_collector_test.cc
new file mode 100644 (file)
index 0000000..ee5c92b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "collectors/cpu_usage_collector.h"
+#include "metrics/metrics_library_mock.h"
+
+
+TEST(CpuUsageTest, ParseProcStat) {
+  MetricsLibraryMock metrics_lib_mock;
+  CpuUsageCollector collector(&metrics_lib_mock);
+  std::vector<std::string> invalid_contents = {
+    "",
+    // First line does not start with cpu.
+    "spu  17191 11 36579 151118 289 0 2 0 0 0\n"
+    "cpu0 1564 2 866 48650 68 0 2 0 0 0\n"
+    "cpu1 14299 0 35116 1844 81 0 0 0 0 0\n",
+    // One of the field is not a number.
+    "cpu  a17191 11 36579 151118 289 0 2 0 0 0",
+    // To many numbers in the first line.
+    "cpu  17191 11 36579 151118 289 0 2 0 0 0 102"
+  };
+
+  uint64_t user, nice, system;
+  for (int i = 0; i < invalid_contents.size(); i++) {
+    ASSERT_FALSE(collector.ParseProcStat(invalid_contents[i], &user, &nice,
+                                         &system));
+  }
+
+  ASSERT_TRUE(collector.ParseProcStat(
+      std::string("cpu  17191 11 36579 151118 289 0 2 0 0 0"),
+      &user, &nice, &system));
+  ASSERT_EQ(17191, user);
+  ASSERT_EQ(11, nice);
+  ASSERT_EQ(36579, system);
+}
diff --git a/metricsd/collectors/disk_usage_collector.cc b/metricsd/collectors/disk_usage_collector.cc
new file mode 100644 (file)
index 0000000..5ab51fb
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "collectors/disk_usage_collector.h"
+
+#include <base/bind.h>
+#include <base/bind_helpers.h>
+#include <base/message_loop/message_loop.h>
+#include <sys/statvfs.h>
+
+#include "metrics/metrics_library.h"
+
+namespace {
+
+const char kDiskUsageMB[] = "Platform.DataPartitionUsed.MB";
+const char kDiskUsagePercent[] = "Platform.DataPartitionUsed.Percent";
+const char kDataPartitionPath[] = "/data";
+
+// Collect every 15 minutes.
+const int kDiskUsageCollectorIntervalSeconds = 900;
+
+}  // namespace
+
+DiskUsageCollector::DiskUsageCollector(
+    MetricsLibraryInterface* metrics_library) {
+  collect_interval_ = base::TimeDelta::FromSeconds(
+      kDiskUsageCollectorIntervalSeconds);
+  CHECK(metrics_library);
+  metrics_lib_ = metrics_library;
+}
+
+void DiskUsageCollector::Collect() {
+  struct statvfs buf;
+  int result = statvfs(kDataPartitionPath, &buf);
+  if (result != 0) {
+    PLOG(ERROR) << "Failed to check the available space in "
+                << kDataPartitionPath;
+    return;
+  }
+
+  unsigned long total_space = buf.f_blocks * buf.f_bsize;
+  unsigned long used_space = (buf.f_blocks - buf.f_bfree) * buf.f_bsize;
+  int percent_used = (used_space * 100) / total_space;
+
+  metrics_lib_->SendToUMA(kDiskUsageMB,
+                          used_space / (1024 * 1024),
+                          0,
+                          1024, // up to 1 GB.
+                          100);
+  metrics_lib_->SendEnumToUMA(kDiskUsagePercent, percent_used, 101);
+}
+
+void DiskUsageCollector::CollectCallback() {
+  Collect();
+  Schedule();
+}
+
+void DiskUsageCollector::Schedule() {
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&DiskUsageCollector::CollectCallback, base::Unretained(this)),
+      collect_interval_);
+}
diff --git a/metricsd/collectors/disk_usage_collector.h b/metricsd/collectors/disk_usage_collector.h
new file mode 100644 (file)
index 0000000..c1d4546
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICSD_COLLECTORS_DISK_USAGE_COLLECTOR_H_
+#define METRICSD_COLLECTORS_DISK_USAGE_COLLECTOR_H_
+
+#include <base/time/time.h>
+
+#include "metrics/metrics_library.h"
+
+class DiskUsageCollector {
+ public:
+  DiskUsageCollector(MetricsLibraryInterface* metrics_library);
+
+  // Schedule the next collection.
+  void Schedule();
+
+  // Callback used by the main loop.
+  void CollectCallback();
+
+  // Collect the disk usage statistics and report them.
+  void Collect();
+
+ private:
+  base::TimeDelta collect_interval_;
+  MetricsLibraryInterface* metrics_lib_;
+};
+
+#endif  // METRICSD_COLLECTORS_DISK_USAGE_COLLECTOR_H_
diff --git a/metricsd/constants.h b/metricsd/constants.h
new file mode 100644 (file)
index 0000000..3a7569b
--- /dev/null
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef METRICS_CONSTANTS_H_
+#define METRICS_CONSTANTS_H_
+
+namespace metrics {
+static const char kMetricsDirectory[] = "/data/misc/metrics/";
+static const char kMetricsEventsFileName[] = "uma-events";
+static const char kMetricsGUIDFileName[] = "Sysinfo.GUID";
+static const char kMetricsServer[] = "https://clients4.google.com/uma/v2";
+static const char kConsentFileName[] = "enabled";
+static const char kStagedLogName[] = "staged_log";
+static const char kFailedUploadCountName[] = "failed_upload_count";
+static const char kDefaultVersion[] = "0.0.0.0";
+
+// Build time properties name.
+static const char kProductId[] = "product_id";
+static const char kProductVersion[] = "product_version";
+
+// Weave configuration.
+static const char kWeaveConfigurationFile[] = "/system/etc/weaved/weaved.conf";
+static const char kModelManifestId[] = "model_id";
+}  // namespace metrics
+
+#endif  // METRICS_CONSTANTS_H_
diff --git a/metricsd/etc/weaved/commands/metrics.json b/metricsd/etc/weaved/commands/metrics.json
new file mode 100644 (file)
index 0000000..b7f32d5
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "_metrics": {
+    "_enableAnalyticsReporting": {
+      "minimalRole": "manager"
+    },
+    "_disableAnalyticsReporting": {
+      "minimalRole": "manager"
+    }
+  }
+}
diff --git a/metricsd/etc/weaved/states/metrics.schema.json b/metricsd/etc/weaved/states/metrics.schema.json
new file mode 100644 (file)
index 0000000..130ac46
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "_metrics": {
+    "_AnalyticsReportingState": {
+      "enum": ["enabled", "disabled"],
+      "default": "disabled"
+    }
+  }
+}
diff --git a/metricsd/include/metrics/c_metrics_library.h b/metricsd/include/metrics/c_metrics_library.h
new file mode 100644 (file)
index 0000000..4e7e666
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_C_METRICS_LIBRARY_H_
+#define METRICS_C_METRICS_LIBRARY_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+typedef struct CMetricsLibraryOpaque* CMetricsLibrary;
+
+// C wrapper for MetricsLibrary::MetricsLibrary.
+CMetricsLibrary CMetricsLibraryNew(void);
+
+// C wrapper for MetricsLibrary::~MetricsLibrary.
+void CMetricsLibraryDelete(CMetricsLibrary handle);
+
+// C wrapper for MetricsLibrary::Init.
+void CMetricsLibraryInit(CMetricsLibrary handle);
+
+// C wrapper for MetricsLibrary::SendToUMA.
+int CMetricsLibrarySendToUMA(CMetricsLibrary handle,
+                             const char* name, int sample,
+                             int min, int max, int nbuckets);
+
+// C wrapper for MetricsLibrary::SendEnumToUMA.
+int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle,
+                                 const char* name, int sample, int max);
+
+// C wrapper for MetricsLibrary::SendSparseToUMA.
+int CMetricsLibrarySendSparseToUMA(CMetricsLibrary handle,
+                                   const char* name, int sample);
+
+// C wrapper for MetricsLibrary::SendUserActionToUMA.
+int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle,
+                                       const char* action);
+
+// C wrapper for MetricsLibrary::SendCrashToUMA.
+int CMetricsLibrarySendCrashToUMA(CMetricsLibrary handle,
+                                  const char* crash_kind);
+
+// C wrapper for MetricsLibrary::AreMetricsEnabled.
+int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle);
+
+#if defined(__cplusplus)
+}
+#endif
+#endif  // METRICS_C_METRICS_LIBRARY_H_
diff --git a/metricsd/include/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h
new file mode 100644 (file)
index 0000000..d2e98c8
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_METRICS_LIBRARY_H_
+#define METRICS_METRICS_LIBRARY_H_
+
+#include <sys/types.h>
+#include <string>
+#include <unistd.h>
+
+#include <base/compiler_specific.h>
+#include <base/files/file_path.h>
+#include <base/macros.h>
+#include <base/memory/scoped_ptr.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+class MetricsLibraryInterface {
+ public:
+  virtual void Init() = 0;
+  virtual bool AreMetricsEnabled() = 0;
+  virtual bool SendToUMA(const std::string& name, int sample,
+                         int min, int max, int nbuckets) = 0;
+  virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0;
+  virtual bool SendBoolToUMA(const std::string& name, bool sample) = 0;
+  virtual bool SendSparseToUMA(const std::string& name, int sample) = 0;
+  virtual bool SendUserActionToUMA(const std::string& action) = 0;
+  virtual ~MetricsLibraryInterface() {}
+};
+
+// Library used to send metrics to Chrome/UMA.
+class MetricsLibrary : public MetricsLibraryInterface {
+ public:
+  MetricsLibrary();
+  virtual ~MetricsLibrary();
+
+  // Initializes the library.
+  void Init() override;
+
+  // Initializes the library and disables the cache of whether or not the
+  // metrics collection is enabled.
+  // By disabling this, we may have to check for the metrics status more often
+  // but the result will never be stale.
+  void InitWithNoCaching();
+
+  // Returns whether or not the machine is running in guest mode.
+  bool IsGuestMode();
+
+  // Returns whether or not metrics collection is enabled.
+  bool AreMetricsEnabled() override;
+
+  // Sends histogram data to Chrome for transport to UMA and returns
+  // true on success. This method results in the equivalent of an
+  // asynchronous non-blocking RPC to UMA_HISTOGRAM_CUSTOM_COUNTS
+  // inside Chrome (see base/histogram.h).
+  //
+  // |sample| is the sample value to be recorded (|min| <= |sample| < |max|).
+  // |min| is the minimum value of the histogram samples (|min| > 0).
+  // |max| is the maximum value of the histogram samples.
+  // |nbuckets| is the number of histogram buckets.
+  // [0,min) is the implicit underflow bucket.
+  // [|max|,infinity) is the implicit overflow bucket.
+  //
+  // Note that the memory allocated in Chrome for each histogram is
+  // proportional to the number of buckets. Therefore, it is strongly
+  // recommended to keep this number low (e.g., 50 is normal, while
+  // 100 is high).
+  bool SendToUMA(const std::string& name, int sample,
+                 int min, int max, int nbuckets) override;
+
+  // Sends linear histogram data to Chrome for transport to UMA and
+  // returns true on success. This method results in the equivalent of
+  // an asynchronous non-blocking RPC to UMA_HISTOGRAM_ENUMERATION
+  // inside Chrome (see base/histogram.h).
+  //
+  // |sample| is the sample value to be recorded (1 <= |sample| < |max|).
+  // |max| is the maximum value of the histogram samples.
+  // 0 is the implicit underflow bucket.
+  // [|max|,infinity) is the implicit overflow bucket.
+  //
+  // An enumeration histogram requires |max| + 1 number of
+  // buckets. Note that the memory allocated in Chrome for each
+  // histogram is proportional to the number of buckets. Therefore, it
+  // is strongly recommended to keep this number low (e.g., 50 is
+  // normal, while 100 is high).
+  bool SendEnumToUMA(const std::string& name, int sample, int max) override;
+
+  // Specialization of SendEnumToUMA for boolean values.
+  bool SendBoolToUMA(const std::string& name, bool sample) override;
+
+  // Sends sparse histogram sample to Chrome for transport to UMA.  Returns
+  // true on success.
+  //
+  // |sample| is the 32-bit integer value to be recorded.
+  bool SendSparseToUMA(const std::string& name, int sample) override;
+
+  // Sends a user action to Chrome for transport to UMA and returns true on
+  // success. This method results in the equivalent of an asynchronous
+  // non-blocking RPC to UserMetrics::RecordAction.  The new metric must be
+  // added to chrome/tools/extract_actions.py in the Chromium repository, which
+  // should then be run to generate a hash for the new action.
+  //
+  // Until http://crosbug.com/11125 is fixed, the metric must also be added to
+  // chrome/browser/chromeos/external_metrics.cc.
+  //
+  // |action| is the user-generated event (e.g., "MuteKeyPressed").
+  bool SendUserActionToUMA(const std::string& action) override;
+
+  // Sends a signal to UMA that a crash of the given |crash_kind|
+  // has occurred.  Used by UMA to generate stability statistics.
+  bool SendCrashToUMA(const char *crash_kind);
+
+  // Sends a "generic Chrome OS event" to UMA.  This is an event name
+  // that is translated into an enumerated histogram entry.  Event names
+  // are added to metrics_library.cc.  Optionally, they can be added
+  // to histograms.xml---but part of the reason for this is to simplify
+  // the addition of events (at the cost of having to look them up by
+  // number in the histograms dashboard).
+  bool SendCrosEventToUMA(const std::string& event);
+
+ private:
+  friend class CMetricsLibraryTest;
+  friend class MetricsLibraryTest;
+  friend class UploadServiceTest;
+  FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabled);
+  FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabledNoCaching);
+  FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage);
+  FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong);
+  FRIEND_TEST(MetricsLibraryTest, IsDeviceMounted);
+  FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome);
+  FRIEND_TEST(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation);
+
+  void InitForTest(const base::FilePath& metrics_directory);
+
+  // Sets |*result| to whether or not the |mounts_file| indicates that
+  // the |device_name| is currently mounted.  Uses |buffer| of
+  // |buffer_size| to read the file.  Returns false if any error.
+  bool IsDeviceMounted(const char* device_name,
+                       const char* mounts_file,
+                       char* buffer, int buffer_size,
+                       bool* result);
+
+  // Time at which we last checked if metrics were enabled.
+  time_t cached_enabled_time_;
+
+  // Cached state of whether or not metrics were enabled.
+  bool cached_enabled_;
+
+  // True iff we should cache the enabled/disabled status.
+  bool use_caching_;
+
+  base::FilePath uma_events_file_;
+  base::FilePath consent_file_;
+
+  DISALLOW_COPY_AND_ASSIGN(MetricsLibrary);
+};
+
+#endif  // METRICS_METRICS_LIBRARY_H_
diff --git a/metricsd/include/metrics/metrics_library_mock.h b/metricsd/include/metrics/metrics_library_mock.h
new file mode 100644 (file)
index 0000000..db56f9e
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_METRICS_LIBRARY_MOCK_H_
+#define METRICS_METRICS_LIBRARY_MOCK_H_
+
+#include <string>
+
+#include "metrics/metrics_library.h"
+
+#include <gmock/gmock.h>
+
+class MetricsLibraryMock : public MetricsLibraryInterface {
+ public:
+  bool metrics_enabled_ = true;
+
+  MOCK_METHOD0(Init, void());
+  MOCK_METHOD5(SendToUMA, bool(const std::string& name, int sample,
+                               int min, int max, int nbuckets));
+  MOCK_METHOD3(SendEnumToUMA, bool(const std::string& name, int sample,
+                                   int max));
+  MOCK_METHOD2(SendBoolToUMA, bool(const std::string& name, bool sample));
+  MOCK_METHOD2(SendSparseToUMA, bool(const std::string& name, int sample));
+  MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action));
+
+  bool AreMetricsEnabled() override {return metrics_enabled_;};
+};
+
+#endif  // METRICS_METRICS_LIBRARY_MOCK_H_
diff --git a/metricsd/include/metrics/timer.h b/metricsd/include/metrics/timer.h
new file mode 100644 (file)
index 0000000..b36ffff
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Timer - class that provides timer tracking.
+
+#ifndef METRICS_TIMER_H_
+#define METRICS_TIMER_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/time/time.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+class MetricsLibraryInterface;
+
+namespace chromeos_metrics {
+
+class TimerInterface {
+ public:
+  virtual ~TimerInterface() {}
+
+  virtual bool Start() = 0;
+  virtual bool Stop() = 0;
+  virtual bool Reset() = 0;
+  virtual bool HasStarted() const = 0;
+};
+
+// Wrapper for calls to the system clock.
+class ClockWrapper {
+ public:
+  ClockWrapper() {}
+  virtual ~ClockWrapper() {}
+
+  // Returns the current time from the system.
+  virtual base::TimeTicks GetCurrentTime() const;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ClockWrapper);
+};
+
+// Implements a Timer.
+class Timer : public TimerInterface {
+ public:
+  Timer();
+  virtual ~Timer() {}
+
+  // Starts the timer. If a timer is already running, also resets current
+  // timer. Always returns true.
+  virtual bool Start();
+
+  // Stops the timer and calculates the total time elapsed between now and when
+  // Start() was called. Note that this method needs a prior call to Start().
+  // Otherwise, it fails (returns false).
+  virtual bool Stop();
+
+  // Pauses a timer.  If the timer is stopped, this call starts the timer in
+  // the paused state. Fails (returns false) if the timer is already paused.
+  virtual bool Pause();
+
+  // Restarts a paused timer (or starts a stopped timer). This method fails
+  // (returns false) if the timer is already running; otherwise, returns true.
+  virtual bool Resume();
+
+  // Resets the timer, erasing the current duration being tracked. Always
+  // returns true.
+  virtual bool Reset();
+
+  // Returns whether the timer has started or not.
+  virtual bool HasStarted() const;
+
+  // Stores the current elapsed time in |elapsed_time|. If timer is stopped,
+  // stores the elapsed time from when Stop() was last called. Otherwise,
+  // calculates and stores the elapsed time since the last Start().
+  // Returns false if the timer was never Start()'ed or if called with a null
+  // pointer argument.
+  virtual bool GetElapsedTime(base::TimeDelta* elapsed_time) const;
+
+ private:
+  enum TimerState { kTimerStopped, kTimerRunning, kTimerPaused };
+  friend class TimerTest;
+  friend class TimerReporterTest;
+  FRIEND_TEST(TimerReporterTest, StartStopReport);
+  FRIEND_TEST(TimerTest, InvalidElapsedTime);
+  FRIEND_TEST(TimerTest, InvalidStop);
+  FRIEND_TEST(TimerTest, PauseResumeStop);
+  FRIEND_TEST(TimerTest, PauseStartStopResume);
+  FRIEND_TEST(TimerTest, PauseStop);
+  FRIEND_TEST(TimerTest, Reset);
+  FRIEND_TEST(TimerTest, ReStart);
+  FRIEND_TEST(TimerTest, ResumeStartStopPause);
+  FRIEND_TEST(TimerTest, SeparatedTimers);
+  FRIEND_TEST(TimerTest, StartPauseResumePauseResumeStop);
+  FRIEND_TEST(TimerTest, StartPauseResumePauseStop);
+  FRIEND_TEST(TimerTest, StartPauseResumeStop);
+  FRIEND_TEST(TimerTest, StartPauseStop);
+  FRIEND_TEST(TimerTest, StartResumeStop);
+  FRIEND_TEST(TimerTest, StartStop);
+
+  // Elapsed time of the last use of the timer.
+  base::TimeDelta elapsed_time_;
+
+  // Starting time value.
+  base::TimeTicks start_time_;
+
+  // Whether the timer is running, stopped, or paused.
+  TimerState timer_state_;
+
+  // Wrapper for the calls to the system clock.
+  scoped_ptr<ClockWrapper> clock_wrapper_;
+
+  DISALLOW_COPY_AND_ASSIGN(Timer);
+};
+
+// Extends the Timer class to report the elapsed time in milliseconds through
+// the UMA metrics library.
+class TimerReporter : public Timer {
+ public:
+  // Initializes the timer by providing a |histogram_name| to report to with
+  // |min|, |max| and |num_buckets| attributes for the histogram.
+  TimerReporter(const std::string& histogram_name, int min, int max,
+                int num_buckets);
+  virtual ~TimerReporter() {}
+
+  // Sets the metrics library used by all instances of this class.
+  static void set_metrics_lib(MetricsLibraryInterface* metrics_lib) {
+    metrics_lib_ = metrics_lib;
+  }
+
+  // Reports the current duration to UMA, in milliseconds. Returns false if
+  // there is nothing to report, e.g. a metrics library is not set.
+  virtual bool ReportMilliseconds() const;
+
+  // Accessor methods.
+  const std::string& histogram_name() const { return histogram_name_; }
+  int min() const { return min_; }
+  int max() const { return max_; }
+  int num_buckets() const { return num_buckets_; }
+
+ private:
+  friend class TimerReporterTest;
+  FRIEND_TEST(TimerReporterTest, StartStopReport);
+  FRIEND_TEST(TimerReporterTest, InvalidReport);
+
+  static MetricsLibraryInterface* metrics_lib_;
+  std::string histogram_name_;
+  int min_;
+  int max_;
+  int num_buckets_;
+
+  DISALLOW_COPY_AND_ASSIGN(TimerReporter);
+};
+
+}  // namespace chromeos_metrics
+
+#endif  // METRICS_TIMER_H_
diff --git a/metricsd/include/metrics/timer_mock.h b/metricsd/include/metrics/timer_mock.h
new file mode 100644 (file)
index 0000000..200ee9a
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_TIMER_MOCK_H_
+#define METRICS_TIMER_MOCK_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "metrics/timer.h"
+
+namespace chromeos_metrics {
+
+class TimerMock : public Timer {
+ public:
+  MOCK_METHOD0(Start, bool());
+  MOCK_METHOD0(Stop, bool());
+  MOCK_METHOD0(Reset, bool());
+  MOCK_CONST_METHOD0(HasStarted, bool());
+  MOCK_CONST_METHOD1(GetElapsedTime, bool(base::TimeDelta* elapsed_time));
+};
+
+class TimerReporterMock : public TimerReporter {
+ public:
+  TimerReporterMock() : TimerReporter("", 0, 0, 0) {}
+  MOCK_METHOD0(Start, bool());
+  MOCK_METHOD0(Stop, bool());
+  MOCK_METHOD0(Reset, bool());
+  MOCK_CONST_METHOD0(HasStarted, bool());
+  MOCK_CONST_METHOD1(GetElapsedTime, bool(base::TimeDelta* elapsed_time));
+  MOCK_CONST_METHOD0(ReportMilliseconds, bool());
+  MOCK_CONST_METHOD0(histogram_name, std::string&());
+  MOCK_CONST_METHOD0(min, int());
+  MOCK_CONST_METHOD0(max, int());
+  MOCK_CONST_METHOD0(num_buckets, int());
+};
+
+class ClockWrapperMock : public ClockWrapper {
+ public:
+  MOCK_CONST_METHOD0(GetCurrentTime, base::TimeTicks());
+};
+
+}  // namespace chromeos_metrics
+
+#endif  // METRICS_TIMER_MOCK_H_
diff --git a/metricsd/libmetrics-334380.gyp b/metricsd/libmetrics-334380.gyp
new file mode 100644 (file)
index 0000000..9771821
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  'variables': {
+    'libbase_ver': 334380,
+  },
+  'includes': [
+    'libmetrics.gypi',
+  ],
+}
diff --git a/metricsd/libmetrics.gypi b/metricsd/libmetrics.gypi
new file mode 100644 (file)
index 0000000..3c8fc7c
--- /dev/null
@@ -0,0 +1,33 @@
+{
+  'target_defaults': {
+    'variables': {
+      'deps': [
+        'libbrillo-<(libbase_ver)',
+        'libchrome-<(libbase_ver)',
+      ]
+    },
+    'cflags_cc': [
+      '-fno-exceptions',
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'libmetrics-<(libbase_ver)',
+      'type': 'shared_library',
+      'cflags': [
+        '-fvisibility=default',
+      ],
+      'libraries+': [
+        '-lpolicy-<(libbase_ver)',
+      ],
+      'sources': [
+        'c_metrics_library.cc',
+        'metrics_library.cc',
+        'serialization/metric_sample.cc',
+        'serialization/serialization_utils.cc',
+        'timer.cc',
+      ],
+      'include_dirs': ['.'],
+    },
+  ],
+}
diff --git a/metricsd/libmetrics.pc.in b/metricsd/libmetrics.pc.in
new file mode 100644 (file)
index 0000000..233f318
--- /dev/null
@@ -0,0 +1,7 @@
+bslot=@BSLOT@
+
+Name: libmetrics
+Description: Chrome OS metrics library
+Version: ${bslot}
+Requires.private: libchrome-${bslot}
+Libs: -lmetrics-${bslot}
diff --git a/metricsd/metrics.gyp b/metricsd/metrics.gyp
new file mode 100644 (file)
index 0000000..c9c02d7
--- /dev/null
@@ -0,0 +1,184 @@
+{
+  'target_defaults': {
+    'variables': {
+      'deps': [
+        'dbus-1',
+        'libbrillo-<(libbase_ver)',
+        'libchrome-<(libbase_ver)',
+      ]
+    },
+    'cflags_cc': [
+      '-fno-exceptions',
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'libmetrics_daemon',
+      'type': 'static_library',
+      'dependencies': [
+        '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
+        'libupload_service',
+        'metrics_proto',
+      ],
+      'link_settings': {
+        'libraries': [
+          '-lrootdev',
+        ],
+      },
+      'sources': [
+        'persistent_integer.cc',
+        'metrics_daemon.cc',
+        'metrics_daemon_main.cc',
+      ],
+      'include_dirs': ['.'],
+    },
+    {
+      'target_name': 'metrics_client',
+      'type': 'executable',
+      'dependencies': [
+        '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
+      ],
+      'sources': [
+        'metrics_client.cc',
+      ]
+    },
+    {
+      'target_name': 'libupload_service',
+      'type': 'static_library',
+      'dependencies': [
+        'metrics_proto',
+        '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
+      ],
+      'link_settings': {
+        'libraries': [
+          '-lvboot_host',
+        ],
+      },
+      'variables': {
+        'exported_deps': [
+          'protobuf-lite',
+        ],
+        'deps': [
+          '<@(exported_deps)',
+        ],
+      },
+      'all_dependent_settings': {
+        'variables': {
+          'deps+': [
+            '<@(exported_deps)',
+          ],
+        },
+      },
+      'sources': [
+        'uploader/upload_service.cc',
+        'uploader/metrics_hashes.cc',
+        'uploader/metrics_log.cc',
+        'uploader/metrics_log_base.cc',
+        'uploader/system_profile_cache.cc',
+        'uploader/sender_http.cc',
+      ],
+      'include_dirs': ['.']
+    },
+    {
+      'target_name': 'metrics_proto',
+      'type': 'static_library',
+      'variables': {
+        'proto_in_dir': 'uploader/proto',
+        'proto_out_dir': 'include/metrics/uploader/proto',
+      },
+      'sources': [
+        '<(proto_in_dir)/chrome_user_metrics_extension.proto',
+        '<(proto_in_dir)/histogram_event.proto',
+        '<(proto_in_dir)/system_profile.proto',
+        '<(proto_in_dir)/user_action_event.proto',
+      ],
+      'includes': [
+        '../common-mk/protoc.gypi'
+      ],
+    },
+  ],
+  'conditions': [
+    ['USE_passive_metrics == 1', {
+      'targets': [
+        {
+          'target_name': 'metrics_daemon',
+          'type': 'executable',
+          'dependencies': ['libmetrics_daemon'],
+        },
+      ],
+    }],
+    ['USE_test == 1', {
+      'targets': [
+        {
+          'target_name': 'persistent_integer_test',
+          'type': 'executable',
+          'includes': ['../common-mk/common_test.gypi'],
+          'sources': [
+            'persistent_integer.cc',
+            'persistent_integer_test.cc',
+          ]
+        },
+        {
+          'target_name': 'metrics_library_test',
+          'type': 'executable',
+          'dependencies': [
+            '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
+          ],
+          'includes': ['../common-mk/common_test.gypi'],
+          'sources': [
+            'metrics_library_test.cc',
+            'serialization/serialization_utils_unittest.cc',
+          ],
+          'link_settings': {
+            'libraries': [
+              '-lpolicy-<(libbase_ver)',
+            ]
+          }
+        },
+        {
+          'target_name': 'timer_test',
+          'type': 'executable',
+          'includes': ['../common-mk/common_test.gypi'],
+          'sources': [
+            'timer.cc',
+            'timer_test.cc',
+          ]
+        },
+        {
+          'target_name': 'upload_service_test',
+          'type': 'executable',
+          'sources': [
+            'persistent_integer.cc',
+            'uploader/metrics_hashes_unittest.cc',
+            'uploader/metrics_log_base_unittest.cc',
+            'uploader/mock/sender_mock.cc',
+            'uploader/upload_service_test.cc',
+          ],
+          'dependencies': [
+            'libupload_service',
+          ],
+          'includes':[
+            '../common-mk/common_test.gypi',
+          ],
+          'include_dirs': ['.']
+        },
+      ],
+    }],
+    ['USE_passive_metrics == 1 and USE_test == 1', {
+      'targets': [
+        {
+          'target_name': 'metrics_daemon_test',
+          'type': 'executable',
+          'dependencies': [
+            'libmetrics_daemon',
+          ],
+          'includes': ['../common-mk/common_test.gypi'],
+          'sources': [
+            'metrics_daemon_test.cc',
+          ],
+          'include_dirs': ['.'],
+        },
+      ],
+    }],
+  ]
+}
diff --git a/metricsd/metrics_client.cc b/metricsd/metrics_client.cc
new file mode 100644 (file)
index 0000000..78174ef
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio>
+#include <cstdlib>
+
+#include <base/memory/scoped_vector.h>
+
+#include "constants.h"
+#include "metrics/metrics_library.h"
+#include "serialization/metric_sample.h"
+#include "serialization/serialization_utils.h"
+
+enum Mode {
+    kModeDumpLogs,
+    kModeSendSample,
+    kModeSendEnumSample,
+    kModeSendSparseSample,
+    kModeSendUserAction,
+    kModeSendCrosEvent,
+    kModeHasConsent,
+    kModeIsGuestMode,
+};
+
+void ShowUsage() {
+  fprintf(stderr,
+          "Usage:  metrics_client [-t] name sample min max nbuckets\n"
+          "        metrics_client -e   name sample max\n"
+          "        metrics_client -s   name sample\n"
+          "        metrics_client -v   event\n"
+          "        metrics_client -u action\n"
+          "        metrics_client [-cdg]\n"
+          "\n"
+          "  default: send metric with integer values \n"
+          "           |min| > 0, |min| <= sample < |max|\n"
+          "  -c: return exit status 0 if user consents to stats, 1 otherwise,\n"
+          "      in guest mode always return 1\n"
+          "  -d: dump cached logs to the console\n"
+          "  -e: send linear/enumeration histogram data\n"
+          "  -g: return exit status 0 if machine in guest mode, 1 otherwise\n"
+          "  -s: send a sparse histogram sample\n"
+          "  -t: convert sample from double seconds to int milliseconds\n"
+          "  -u: send a user action to Chrome\n"
+          "  -v: send a Platform.CrOSEvent enum histogram sample\n");
+  exit(1);
+}
+
+static int ParseInt(const char *arg) {
+  char *endptr;
+  int value = strtol(arg, &endptr, 0);
+  if (*endptr != '\0') {
+    fprintf(stderr, "metrics client: bad integer \"%s\"\n", arg);
+    ShowUsage();
+  }
+  return value;
+}
+
+static double ParseDouble(const char *arg) {
+  char *endptr;
+  double value = strtod(arg, &endptr);
+  if (*endptr != '\0') {
+    fprintf(stderr, "metrics client: bad double \"%s\"\n", arg);
+    ShowUsage();
+  }
+  return value;
+}
+
+static int SendStats(char* argv[],
+                     int name_index,
+                     enum Mode mode,
+                     bool secs_to_msecs) {
+  const char* name = argv[name_index];
+  int sample;
+  if (secs_to_msecs) {
+    sample = static_cast<int>(ParseDouble(argv[name_index + 1]) * 1000.0);
+  } else {
+    sample = ParseInt(argv[name_index + 1]);
+  }
+
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  if (mode == kModeSendSparseSample) {
+    metrics_lib.SendSparseToUMA(name, sample);
+  } else if (mode == kModeSendEnumSample) {
+    int max = ParseInt(argv[name_index + 2]);
+    metrics_lib.SendEnumToUMA(name, sample, max);
+  } else {
+    int min = ParseInt(argv[name_index + 2]);
+    int max = ParseInt(argv[name_index + 3]);
+    int nbuckets = ParseInt(argv[name_index + 4]);
+    metrics_lib.SendToUMA(name, sample, min, max, nbuckets);
+  }
+  return 0;
+}
+
+static int SendUserAction(char* argv[], int action_index) {
+  const char* action = argv[action_index];
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  metrics_lib.SendUserActionToUMA(action);
+  return 0;
+}
+
+static int SendCrosEvent(char* argv[], int action_index) {
+  const char* event = argv[action_index];
+  bool result;
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  result = metrics_lib.SendCrosEventToUMA(event);
+  if (!result) {
+    fprintf(stderr, "metrics_client: could not send event %s\n", event);
+    return 1;
+  }
+  return 0;
+}
+
+static int HasConsent() {
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  return metrics_lib.AreMetricsEnabled() ? 0 : 1;
+}
+
+static int IsGuestMode() {
+  MetricsLibrary metrics_lib;
+  metrics_lib.Init();
+  return metrics_lib.IsGuestMode() ? 0 : 1;
+}
+
+static int DumpLogs() {
+  base::FilePath events_file = base::FilePath(
+      metrics::kMetricsDirectory).Append(metrics::kMetricsEventsFileName);
+  printf("Metrics from %s\n\n", events_file.value().data());
+
+  ScopedVector<metrics::MetricSample> metrics;
+  metrics::SerializationUtils::ReadMetricsFromFile(events_file.value(),
+                                                   &metrics);
+
+  for (ScopedVector<metrics::MetricSample>::const_iterator i = metrics.begin();
+       i != metrics.end(); ++i) {
+    const metrics::MetricSample* sample = *i;
+    printf("name: %s\t", sample->name().c_str());
+    printf("type: ");
+
+    switch (sample->type()) {
+      case metrics::MetricSample::CRASH:
+        printf("CRASH");
+        break;
+      case metrics::MetricSample::HISTOGRAM:
+        printf("HISTOGRAM");
+        break;
+      case metrics::MetricSample::LINEAR_HISTOGRAM:
+        printf("LINEAR_HISTOGRAM");
+        break;
+      case metrics::MetricSample::SPARSE_HISTOGRAM:
+        printf("SPARSE_HISTOGRAM");
+        break;
+      case metrics::MetricSample::USER_ACTION:
+        printf("USER_ACTION");
+        break;
+    }
+
+    printf("\n");
+  }
+
+  return 0;
+}
+
+int main(int argc, char** argv) {
+  enum Mode mode = kModeSendSample;
+  bool secs_to_msecs = false;
+
+  // Parse arguments
+  int flag;
+  while ((flag = getopt(argc, argv, "abcdegstuv")) != -1) {
+    switch (flag) {
+      case 'c':
+        mode = kModeHasConsent;
+        break;
+      case 'd':
+        mode = kModeDumpLogs;
+        break;
+      case 'e':
+        mode = kModeSendEnumSample;
+        break;
+      case 'g':
+        mode = kModeIsGuestMode;
+        break;
+      case 's':
+        mode = kModeSendSparseSample;
+        break;
+      case 't':
+        secs_to_msecs = true;
+        break;
+      case 'u':
+        mode = kModeSendUserAction;
+        break;
+      case 'v':
+        mode = kModeSendCrosEvent;
+        break;
+      default:
+        ShowUsage();
+        break;
+    }
+  }
+  int arg_index = optind;
+
+  int expected_args = 0;
+  if (mode == kModeSendSample)
+    expected_args = 5;
+  else if (mode == kModeSendEnumSample)
+    expected_args = 3;
+  else if (mode == kModeSendSparseSample)
+    expected_args = 2;
+  else if (mode == kModeSendUserAction)
+    expected_args = 1;
+  else if (mode == kModeSendCrosEvent)
+    expected_args = 1;
+
+  if ((arg_index + expected_args) != argc) {
+    ShowUsage();
+  }
+
+  switch (mode) {
+    case kModeSendSample:
+    case kModeSendEnumSample:
+    case kModeSendSparseSample:
+      if ((mode != kModeSendSample) && secs_to_msecs) {
+        ShowUsage();
+      }
+      return SendStats(argv,
+                       arg_index,
+                       mode,
+                       secs_to_msecs);
+    case kModeSendUserAction:
+      return SendUserAction(argv, arg_index);
+    case kModeSendCrosEvent:
+      return SendCrosEvent(argv, arg_index);
+    case kModeHasConsent:
+      return HasConsent();
+    case kModeIsGuestMode:
+      return IsGuestMode();
+    case kModeDumpLogs:
+      return DumpLogs();
+    default:
+      ShowUsage();
+      return 0;
+  }
+}
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
new file mode 100644 (file)
index 0000000..b606fd0
--- /dev/null
@@ -0,0 +1,898 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "metrics_daemon.h"
+
+#include <sysexits.h>
+#include <time.h>
+
+#include <base/bind.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/hash.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <brillo/osrelease_reader.h>
+#include <dbus/dbus.h>
+#include <dbus/message.h>
+
+#include "constants.h"
+#include "uploader/upload_service.h"
+
+using base::FilePath;
+using base::StringPrintf;
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+using chromeos_metrics::PersistentInteger;
+using com::android::Weave::CommandProxy;
+using com::android::Weave::ManagerProxy;
+using std::map;
+using std::string;
+using std::vector;
+
+namespace {
+
+const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
+const char kCrashReporterUserCrashSignal[] = "UserCrash";
+const char kCrashReporterMatchRule[] =
+    "type='signal',interface='%s',path='/',member='%s'";
+
+const int kSecondsPerMinute = 60;
+const int kMinutesPerHour = 60;
+const int kHoursPerDay = 24;
+const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour;
+const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay;
+const int kDaysPerWeek = 7;
+const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek;
+
+// Interval between calls to UpdateStats().
+const uint32_t kUpdateStatsIntervalMs = 300000;
+
+const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected";
+const char kUncleanShutdownDetectedFile[] =
+    "/var/run/unclean-shutdown-detected";
+
+const int kMetricMeminfoInterval = 30;    // seconds
+
+const char kMeminfoFileName[] = "/proc/meminfo";
+const char kVmStatFileName[] = "/proc/vmstat";
+
+// Thermal CPU throttling.
+
+const char kMetricScaledCpuFrequencyName[] =
+    "Platform.CpuFrequencyThermalScaling";
+
+}  // namespace
+
+// Zram sysfs entries.
+
+const char MetricsDaemon::kComprDataSizeName[] = "compr_data_size";
+const char MetricsDaemon::kOrigDataSizeName[] = "orig_data_size";
+const char MetricsDaemon::kZeroPagesName[] = "zero_pages";
+
+// Memory use stats collection intervals.  We collect some memory use interval
+// at these intervals after boot, and we stop collecting after the last one,
+// with the assumption that in most cases the memory use won't change much
+// after that.
+static const int kMemuseIntervals[] = {
+  1 * kSecondsPerMinute,    // 1 minute mark
+  4 * kSecondsPerMinute,    // 5 minute mark
+  25 * kSecondsPerMinute,   // 0.5 hour mark
+  120 * kSecondsPerMinute,  // 2.5 hour mark
+  600 * kSecondsPerMinute,  // 12.5 hour mark
+};
+
+MetricsDaemon::MetricsDaemon()
+    : memuse_final_time_(0),
+      memuse_interval_index_(0) {}
+
+MetricsDaemon::~MetricsDaemon() {
+}
+
+// static
+double MetricsDaemon::GetActiveTime() {
+  struct timespec ts;
+  int r = clock_gettime(CLOCK_MONOTONIC, &ts);
+  if (r < 0) {
+    PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed";
+    return 0;
+  } else {
+    return ts.tv_sec + static_cast<double>(ts.tv_nsec) / (1000 * 1000 * 1000);
+  }
+}
+
+int MetricsDaemon::Run() {
+  if (CheckSystemCrash(kKernelCrashDetectedFile)) {
+    ProcessKernelCrash();
+  }
+
+  if (CheckSystemCrash(kUncleanShutdownDetectedFile)) {
+    ProcessUncleanShutdown();
+  }
+
+  // On OS version change, clear version stats (which are reported daily).
+  int32_t version = GetOsVersionHash();
+  if (version_cycle_->Get() != version) {
+    version_cycle_->Set(version);
+    kernel_crashes_version_count_->Set(0);
+    version_cumulative_active_use_->Set(0);
+    version_cumulative_cpu_use_->Set(0);
+  }
+
+  return brillo::DBusDaemon::Run();
+}
+
+void MetricsDaemon::RunUploaderTest() {
+  upload_service_.reset(new UploadService(
+      new SystemProfileCache(true, metrics_directory_),
+      metrics_lib_,
+      server_));
+  upload_service_->Init(upload_interval_, metrics_directory_);
+  upload_service_->UploadEvent();
+}
+
+uint32_t MetricsDaemon::GetOsVersionHash() {
+  brillo::OsReleaseReader reader;
+  reader.Load();
+  string version;
+  if (!reader.GetString(metrics::kProductVersion, &version)) {
+    LOG(ERROR) << "failed to read the product version.";
+    version = metrics::kDefaultVersion;
+  }
+
+  uint32_t version_hash = base::Hash(version);
+  if (testing_) {
+    version_hash = 42;  // return any plausible value for the hash
+  }
+  return version_hash;
+}
+
+void MetricsDaemon::Init(bool testing,
+                         bool uploader_active,
+                         bool dbus_enabled,
+                         MetricsLibraryInterface* metrics_lib,
+                         const string& diskstats_path,
+                         const string& scaling_max_freq_path,
+                         const string& cpuinfo_max_freq_path,
+                         const base::TimeDelta& upload_interval,
+                         const string& server,
+                         const base::FilePath& metrics_directory) {
+  CHECK(metrics_lib);
+  testing_ = testing;
+  uploader_active_ = uploader_active;
+  dbus_enabled_ = dbus_enabled;
+  metrics_directory_ = metrics_directory;
+  metrics_lib_ = metrics_lib;
+
+  upload_interval_ = upload_interval;
+  server_ = server;
+
+  daily_active_use_.reset(
+      new PersistentInteger("Platform.UseTime.PerDay"));
+  version_cumulative_active_use_.reset(
+      new PersistentInteger("Platform.CumulativeUseTime"));
+  version_cumulative_cpu_use_.reset(
+      new PersistentInteger("Platform.CumulativeCpuTime"));
+
+  kernel_crash_interval_.reset(
+      new PersistentInteger("Platform.KernelCrashInterval"));
+  unclean_shutdown_interval_.reset(
+      new PersistentInteger("Platform.UncleanShutdownInterval"));
+  user_crash_interval_.reset(
+      new PersistentInteger("Platform.UserCrashInterval"));
+
+  any_crashes_daily_count_.reset(
+      new PersistentInteger("Platform.AnyCrashes.PerDay"));
+  any_crashes_weekly_count_.reset(
+      new PersistentInteger("Platform.AnyCrashes.PerWeek"));
+  user_crashes_daily_count_.reset(
+      new PersistentInteger("Platform.UserCrashes.PerDay"));
+  user_crashes_weekly_count_.reset(
+      new PersistentInteger("Platform.UserCrashes.PerWeek"));
+  kernel_crashes_daily_count_.reset(
+      new PersistentInteger("Platform.KernelCrashes.PerDay"));
+  kernel_crashes_weekly_count_.reset(
+      new PersistentInteger("Platform.KernelCrashes.PerWeek"));
+  kernel_crashes_version_count_.reset(
+      new PersistentInteger("Platform.KernelCrashesSinceUpdate"));
+  unclean_shutdowns_daily_count_.reset(
+      new PersistentInteger("Platform.UncleanShutdown.PerDay"));
+  unclean_shutdowns_weekly_count_.reset(
+      new PersistentInteger("Platform.UncleanShutdowns.PerWeek"));
+
+  daily_cycle_.reset(new PersistentInteger("daily.cycle"));
+  weekly_cycle_.reset(new PersistentInteger("weekly.cycle"));
+  version_cycle_.reset(new PersistentInteger("version.cycle"));
+
+  scaling_max_freq_path_ = scaling_max_freq_path;
+  cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
+  disk_usage_collector_.reset(new DiskUsageCollector(metrics_lib_));
+  averaged_stats_collector_.reset(
+      new AveragedStatisticsCollector(metrics_lib_, diskstats_path,
+                                      kVmStatFileName));
+  cpu_usage_collector_.reset(new CpuUsageCollector(metrics_lib_));
+}
+
+int MetricsDaemon::OnInit() {
+  int return_code = dbus_enabled_ ? brillo::DBusDaemon::OnInit() :
+      brillo::Daemon::OnInit();
+  if (return_code != EX_OK)
+    return return_code;
+
+  StatsReporterInit();
+
+  // Start collecting meminfo stats.
+  ScheduleMeminfoCallback(kMetricMeminfoInterval);
+  memuse_final_time_ = GetActiveTime() + kMemuseIntervals[0];
+  ScheduleMemuseCallback(kMemuseIntervals[0]);
+
+  if (testing_)
+    return EX_OK;
+
+  if (dbus_enabled_) {
+    bus_->AssertOnDBusThread();
+    CHECK(bus_->SetUpAsyncOperations());
+
+    if (bus_->is_connected()) {
+      const std::string match_rule =
+          base::StringPrintf(kCrashReporterMatchRule,
+                             kCrashReporterInterface,
+                             kCrashReporterUserCrashSignal);
+
+      bus_->AddFilterFunction(&MetricsDaemon::MessageFilter, this);
+
+      DBusError error;
+      dbus_error_init(&error);
+      bus_->AddMatch(match_rule, &error);
+
+      if (dbus_error_is_set(&error)) {
+        LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
+            << error.name << ": " << error.message;
+        return EX_SOFTWARE;
+      }
+    } else {
+      LOG(ERROR) << "DBus isn't connected.";
+      return EX_UNAVAILABLE;
+    }
+
+    device_ = weaved::Device::CreateInstance(
+        bus_,
+        base::Bind(&MetricsDaemon::UpdateWeaveState, base::Unretained(this)));
+    device_->AddCommandHandler(
+        "_metrics._enableAnalyticsReporting",
+        base::Bind(&MetricsDaemon::OnEnableMetrics, base::Unretained(this)));
+    device_->AddCommandHandler(
+        "_metrics._disableAnalyticsReporting",
+        base::Bind(&MetricsDaemon::OnDisableMetrics, base::Unretained(this)));
+  }
+
+  latest_cpu_use_microseconds_ = cpu_usage_collector_->GetCumulativeCpuUse();
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
+                 base::Unretained(this)),
+      base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
+
+  if (uploader_active_) {
+    upload_service_.reset(
+        new UploadService(new SystemProfileCache(), metrics_lib_, server_));
+    upload_service_->Init(upload_interval_, metrics_directory_);
+  }
+
+  return EX_OK;
+}
+
+void MetricsDaemon::OnShutdown(int* return_code) {
+  if (!testing_ && dbus_enabled_ && bus_->is_connected()) {
+    const std::string match_rule =
+        base::StringPrintf(kCrashReporterMatchRule,
+                           kCrashReporterInterface,
+                           kCrashReporterUserCrashSignal);
+
+    bus_->RemoveFilterFunction(&MetricsDaemon::MessageFilter, this);
+
+    DBusError error;
+    dbus_error_init(&error);
+    bus_->RemoveMatch(match_rule, &error);
+
+    if (dbus_error_is_set(&error)) {
+      LOG(ERROR) << "Failed to remove match rule \"" << match_rule << "\". Got "
+          << error.name << ": " << error.message;
+    }
+  }
+  brillo::DBusDaemon::OnShutdown(return_code);
+}
+
+void MetricsDaemon::OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd) {
+  auto command = cmd.lock();
+  if (!command)
+    return;
+
+  if (base::WriteFile(metrics_directory_.Append(metrics::kConsentFileName),
+                      "", 0) != 0) {
+    PLOG(ERROR) << "Could not create the consent file.";
+    command->Abort("metrics_error", "Could not create the consent file",
+                   nullptr);
+    return;
+  }
+
+  UpdateWeaveState();
+  command->Complete({}, nullptr);
+}
+
+void MetricsDaemon::OnDisableMetrics(
+    const std::weak_ptr<weaved::Command>& cmd) {
+  auto command = cmd.lock();
+  if (!command)
+    return;
+
+  if (!base::DeleteFile(metrics_directory_.Append(metrics::kConsentFileName),
+                        false)) {
+    PLOG(ERROR) << "Could not delete the consent file.";
+    command->Abort("metrics_error", "Could not delete the consent file",
+                   nullptr);
+    return;
+  }
+
+  UpdateWeaveState();
+  command->Complete({}, nullptr);
+}
+
+void MetricsDaemon::UpdateWeaveState() {
+  if (!device_)
+    return;
+
+  brillo::VariantDictionary state_change{
+    { "_metrics._AnalyticsReportingState",
+      metrics_lib_->AreMetricsEnabled() ? "enabled" : "disabled" }
+  };
+
+  if (!device_->SetStateProperties(state_change, nullptr)) {
+    LOG(ERROR) << "failed to update weave's state";
+  }
+}
+
+// static
+DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
+                                               DBusMessage* message,
+                                               void* user_data) {
+  int message_type = dbus_message_get_type(message);
+  if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
+    DLOG(WARNING) << "unexpected message type " << message_type;
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  }
+
+  // Signal messages always have interfaces.
+  const std::string interface(dbus_message_get_interface(message));
+  const std::string member(dbus_message_get_member(message));
+  DLOG(INFO) << "Got " << interface << "." << member << " D-Bus signal";
+
+  MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
+
+  DBusMessageIter iter;
+  dbus_message_iter_init(message, &iter);
+  if (interface == kCrashReporterInterface) {
+    CHECK_EQ(member, kCrashReporterUserCrashSignal);
+    daemon->ProcessUserCrash();
+  } else {
+    // Ignore messages from the bus itself.
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+  }
+
+  return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+void MetricsDaemon::ProcessUserCrash() {
+  // Counts the active time up to now.
+  UpdateStats(TimeTicks::Now(), Time::Now());
+
+  // Reports the active use time since the last crash and resets it.
+  SendAndResetCrashIntervalSample(user_crash_interval_);
+
+  any_crashes_daily_count_->Add(1);
+  any_crashes_weekly_count_->Add(1);
+  user_crashes_daily_count_->Add(1);
+  user_crashes_weekly_count_->Add(1);
+}
+
+void MetricsDaemon::ProcessKernelCrash() {
+  // Counts the active time up to now.
+  UpdateStats(TimeTicks::Now(), Time::Now());
+
+  // Reports the active use time since the last crash and resets it.
+  SendAndResetCrashIntervalSample(kernel_crash_interval_);
+
+  any_crashes_daily_count_->Add(1);
+  any_crashes_weekly_count_->Add(1);
+  kernel_crashes_daily_count_->Add(1);
+  kernel_crashes_weekly_count_->Add(1);
+
+  kernel_crashes_version_count_->Add(1);
+}
+
+void MetricsDaemon::ProcessUncleanShutdown() {
+  // Counts the active time up to now.
+  UpdateStats(TimeTicks::Now(), Time::Now());
+
+  // Reports the active use time since the last crash and resets it.
+  SendAndResetCrashIntervalSample(unclean_shutdown_interval_);
+
+  unclean_shutdowns_daily_count_->Add(1);
+  unclean_shutdowns_weekly_count_->Add(1);
+  any_crashes_daily_count_->Add(1);
+  any_crashes_weekly_count_->Add(1);
+}
+
+bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
+  FilePath crash_detected(crash_file);
+  if (!base::PathExists(crash_detected))
+    return false;
+
+  // Deletes the crash-detected file so that the daemon doesn't report
+  // another kernel crash in case it's restarted.
+  base::DeleteFile(crash_detected, false);  // not recursive
+  return true;
+}
+
+void MetricsDaemon::StatsReporterInit() {
+  disk_usage_collector_->Schedule();
+
+  cpu_usage_collector_->Init();
+  cpu_usage_collector_->Schedule();
+
+  // Don't start a collection cycle during the first run to avoid delaying the
+  // boot.
+  averaged_stats_collector_->ScheduleWait();
+}
+
+
+bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) {
+  const FilePath sysfs_path(sysfs_file_name);
+  string value_string;
+  if (!base::ReadFileToString(sysfs_path, &value_string)) {
+    LOG(WARNING) << "cannot read " << sysfs_path.value().c_str();
+    return false;
+  }
+  if (!base::RemoveChars(value_string, "\n", &value_string)) {
+    LOG(WARNING) << "no newline in " << value_string;
+    // Continue even though the lack of newline is suspicious.
+  }
+  if (!base::StringToInt(value_string, value)) {
+    LOG(WARNING) << "cannot convert " << value_string << " to int";
+    return false;
+  }
+  return true;
+}
+
+void MetricsDaemon::SendCpuThrottleMetrics() {
+  // |max_freq| is 0 only the first time through.
+  static int max_freq = 0;
+  if (max_freq == -1)
+    // Give up, as sysfs did not report max_freq correctly.
+    return;
+  if (max_freq == 0 || testing_) {
+    // One-time initialization of max_freq.  (Every time when testing.)
+    if (!ReadFreqToInt(cpuinfo_max_freq_path_, &max_freq)) {
+      max_freq = -1;
+      return;
+    }
+    if (max_freq == 0) {
+      LOG(WARNING) << "sysfs reports 0 max CPU frequency\n";
+      max_freq = -1;
+      return;
+    }
+    if (max_freq % 10000 == 1000) {
+      // Special case: system has turbo mode, and max non-turbo frequency is
+      // max_freq - 1000.  This relies on "normal" (non-turbo) frequencies
+      // being multiples of (at least) 10 MHz.  Although there is no guarantee
+      // of this, it seems a fairly reasonable assumption.  Otherwise we should
+      // read scaling_available_frequencies, sort the frequencies, compare the
+      // two highest ones, and check if they differ by 1000 (kHz) (and that's a
+      // hack too, no telling when it will change).
+      max_freq -= 1000;
+    }
+  }
+  int scaled_freq = 0;
+  if (!ReadFreqToInt(scaling_max_freq_path_, &scaled_freq))
+    return;
+  // Frequencies are in kHz.  If scaled_freq > max_freq, turbo is on, but
+  // scaled_freq is not the actual turbo frequency.  We indicate this situation
+  // with a 101% value.
+  int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100);
+  SendLinearSample(kMetricScaledCpuFrequencyName, percent, 101, 102);
+}
+
+void MetricsDaemon::ScheduleMeminfoCallback(int wait) {
+  if (testing_) {
+    return;
+  }
+  base::TimeDelta waitDelta = base::TimeDelta::FromSeconds(wait);
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
+                 waitDelta),
+      waitDelta);
+}
+
+void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) {
+  string meminfo_raw;
+  const FilePath meminfo_path(kMeminfoFileName);
+  if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
+    LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
+    return;
+  }
+  // Make both calls even if the first one fails.
+  if (ProcessMeminfo(meminfo_raw)) {
+    base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+        base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this),
+                   wait),
+        wait);
+  }
+}
+
+// static
+bool MetricsDaemon::ReadFileToUint64(const base::FilePath& path,
+                                     uint64_t* value) {
+  std::string content;
+  if (!base::ReadFileToString(path, &content)) {
+    PLOG(WARNING) << "cannot read " << path.MaybeAsASCII();
+    return false;
+  }
+  // Remove final newline.
+  base::TrimWhitespaceASCII(content, base::TRIM_TRAILING, &content);
+  if (!base::StringToUint64(content, value)) {
+    LOG(WARNING) << "invalid integer: " << content;
+    return false;
+  }
+  return true;
+}
+
+bool MetricsDaemon::ReportZram(const base::FilePath& zram_dir) {
+  // Data sizes are in bytes.  |zero_pages| is in number of pages.
+  uint64_t compr_data_size, orig_data_size, zero_pages;
+  const size_t page_size = 4096;
+
+  if (!ReadFileToUint64(zram_dir.Append(kComprDataSizeName),
+                        &compr_data_size) ||
+      !ReadFileToUint64(zram_dir.Append(kOrigDataSizeName), &orig_data_size) ||
+      !ReadFileToUint64(zram_dir.Append(kZeroPagesName), &zero_pages)) {
+    return false;
+  }
+
+  // |orig_data_size| does not include zero-filled pages.
+  orig_data_size += zero_pages * page_size;
+
+  const int compr_data_size_mb = compr_data_size >> 20;
+  const int savings_mb = (orig_data_size - compr_data_size) >> 20;
+  const int zero_ratio_percent = zero_pages * page_size * 100 / orig_data_size;
+
+  // Report compressed size in megabytes.  100 MB or less has little impact.
+  SendSample("Platform.ZramCompressedSize", compr_data_size_mb, 100, 4000, 50);
+  SendSample("Platform.ZramSavings", savings_mb, 100, 4000, 50);
+  // The compression ratio is multiplied by 100 for better resolution.  The
+  // ratios of interest are between 1 and 6 (100% and 600% as reported).  We
+  // don't want samples when very little memory is being compressed.
+  if (compr_data_size_mb >= 1) {
+    SendSample("Platform.ZramCompressionRatioPercent",
+               orig_data_size * 100 / compr_data_size, 100, 600, 50);
+  }
+  // The values of interest for zero_pages are between 1MB and 1GB.  The units
+  // are number of pages.
+  SendSample("Platform.ZramZeroPages", zero_pages, 256, 256 * 1024, 50);
+  SendSample("Platform.ZramZeroRatioPercent", zero_ratio_percent, 1, 50, 50);
+
+  return true;
+}
+
+bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
+  static const MeminfoRecord fields_array[] = {
+    { "MemTotal", "MemTotal" },  // SPECIAL CASE: total system memory
+    { "MemFree", "MemFree" },
+    { "Buffers", "Buffers" },
+    { "Cached", "Cached" },
+    // { "SwapCached", "SwapCached" },
+    { "Active", "Active" },
+    { "Inactive", "Inactive" },
+    { "ActiveAnon", "Active(anon)" },
+    { "InactiveAnon", "Inactive(anon)" },
+    { "ActiveFile" , "Active(file)" },
+    { "InactiveFile", "Inactive(file)" },
+    { "Unevictable", "Unevictable", kMeminfoOp_HistLog },
+    // { "Mlocked", "Mlocked" },
+    { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal },
+    { "SwapFree", "SwapFree", kMeminfoOp_SwapFree },
+    // { "Dirty", "Dirty" },
+    // { "Writeback", "Writeback" },
+    { "AnonPages", "AnonPages" },
+    { "Mapped", "Mapped" },
+    { "Shmem", "Shmem", kMeminfoOp_HistLog },
+    { "Slab", "Slab", kMeminfoOp_HistLog },
+    // { "SReclaimable", "SReclaimable" },
+    // { "SUnreclaim", "SUnreclaim" },
+  };
+  vector<MeminfoRecord> fields(fields_array,
+                               fields_array + arraysize(fields_array));
+  if (!FillMeminfo(meminfo_raw, &fields)) {
+    return false;
+  }
+  int total_memory = fields[0].value;
+  if (total_memory == 0) {
+    // this "cannot happen"
+    LOG(WARNING) << "borked meminfo parser";
+    return false;
+  }
+  int swap_total = 0;
+  int swap_free = 0;
+  // Send all fields retrieved, except total memory.
+  for (unsigned int i = 1; i < fields.size(); i++) {
+    string metrics_name = base::StringPrintf("Platform.Meminfo%s",
+                                             fields[i].name);
+    int percent;
+    switch (fields[i].op) {
+      case kMeminfoOp_HistPercent:
+        // report value as percent of total memory
+        percent = fields[i].value * 100 / total_memory;
+        SendLinearSample(metrics_name, percent, 100, 101);
+        break;
+      case kMeminfoOp_HistLog:
+        // report value in kbytes, log scale, 4Gb max
+        SendSample(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100);
+        break;
+      case kMeminfoOp_SwapTotal:
+        swap_total = fields[i].value;
+      case kMeminfoOp_SwapFree:
+        swap_free = fields[i].value;
+        break;
+    }
+  }
+  if (swap_total > 0) {
+    int swap_used = swap_total - swap_free;
+    int swap_used_percent = swap_used * 100 / swap_total;
+    SendSample("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100);
+    SendLinearSample("Platform.MeminfoSwapUsed.Percent", swap_used_percent,
+                     100, 101);
+  }
+  return true;
+}
+
+bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
+                                vector<MeminfoRecord>* fields) {
+  vector<string> lines;
+  unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
+
+  // Scan meminfo output and collect field values.  Each field name has to
+  // match a meminfo entry (case insensitive) after removing non-alpha
+  // characters from the entry.
+  unsigned int ifield = 0;
+  for (unsigned int iline = 0;
+       iline < nlines && ifield < fields->size();
+       iline++) {
+    vector<string> tokens;
+    Tokenize(lines[iline], ": ", &tokens);
+    if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
+      // Name matches. Parse value and save.
+      if (!base::StringToInt(tokens[1], &(*fields)[ifield].value)) {
+        LOG(WARNING) << "Cound not convert " << tokens[1] << " to int";
+        return false;
+      }
+      ifield++;
+    }
+  }
+  if (ifield < fields->size()) {
+    // End of input reached while scanning.
+    LOG(WARNING) << "cannot find field " << (*fields)[ifield].match
+                 << " and following";
+    return false;
+  }
+  return true;
+}
+
+void MetricsDaemon::ScheduleMemuseCallback(double interval) {
+  if (testing_) {
+    return;
+  }
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&MetricsDaemon::MemuseCallback, base::Unretained(this)),
+      base::TimeDelta::FromSeconds(interval));
+}
+
+void MetricsDaemon::MemuseCallback() {
+  // Since we only care about active time (i.e. uptime minus sleep time) but
+  // the callbacks are driven by real time (uptime), we check if we should
+  // reschedule this callback due to intervening sleep periods.
+  double now = GetActiveTime();
+  // Avoid intervals of less than one second.
+  double remaining_time = ceil(memuse_final_time_ - now);
+  if (remaining_time > 0) {
+    ScheduleMemuseCallback(remaining_time);
+  } else {
+    // Report stats and advance the measurement interval unless there are
+    // errors or we've completed the last interval.
+    if (MemuseCallbackWork() &&
+        memuse_interval_index_ < arraysize(kMemuseIntervals)) {
+      double interval = kMemuseIntervals[memuse_interval_index_++];
+      memuse_final_time_ = now + interval;
+      ScheduleMemuseCallback(interval);
+    }
+  }
+}
+
+bool MetricsDaemon::MemuseCallbackWork() {
+  string meminfo_raw;
+  const FilePath meminfo_path(kMeminfoFileName);
+  if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
+    LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
+    return false;
+  }
+  return ProcessMemuse(meminfo_raw);
+}
+
+bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
+  static const MeminfoRecord fields_array[] = {
+    { "MemTotal", "MemTotal" },  // SPECIAL CASE: total system memory
+    { "ActiveAnon", "Active(anon)" },
+    { "InactiveAnon", "Inactive(anon)" },
+  };
+  vector<MeminfoRecord> fields(fields_array,
+                               fields_array + arraysize(fields_array));
+  if (!FillMeminfo(meminfo_raw, &fields)) {
+    return false;
+  }
+  int total = fields[0].value;
+  int active_anon = fields[1].value;
+  int inactive_anon = fields[2].value;
+  if (total == 0) {
+    // this "cannot happen"
+    LOG(WARNING) << "borked meminfo parser";
+    return false;
+  }
+  string metrics_name = base::StringPrintf("Platform.MemuseAnon%d",
+                                           memuse_interval_index_);
+  SendLinearSample(metrics_name, (active_anon + inactive_anon) * 100 / total,
+                   100, 101);
+  return true;
+}
+
+void MetricsDaemon::SendSample(const string& name, int sample,
+                               int min, int max, int nbuckets) {
+  metrics_lib_->SendToUMA(name, sample, min, max, nbuckets);
+}
+
+void MetricsDaemon::SendKernelCrashesCumulativeCountStats() {
+  // Report the number of crashes for this OS version, but don't clear the
+  // counter.  It is cleared elsewhere on version change.
+  int64_t crashes_count = kernel_crashes_version_count_->Get();
+  SendSample(kernel_crashes_version_count_->Name(),
+             crashes_count,
+             1,                         // value of first bucket
+             500,                       // value of last bucket
+             100);                      // number of buckets
+
+
+  int64_t cpu_use_ms = version_cumulative_cpu_use_->Get();
+  SendSample(version_cumulative_cpu_use_->Name(),
+             cpu_use_ms / 1000,         // stat is in seconds
+             1,                         // device may be used very little...
+             8 * 1000 * 1000,           // ... or a lot (a little over 90 days)
+             100);
+
+  // On the first run after an autoupdate, cpu_use_ms and active_use_seconds
+  // can be zero.  Avoid division by zero.
+  if (cpu_use_ms > 0) {
+    // Send the crash frequency since update in number of crashes per CPU year.
+    SendSample("Logging.KernelCrashesPerCpuYear",
+               crashes_count * kSecondsPerDay * 365 * 1000 / cpu_use_ms,
+               1,
+               1000 * 1000,     // about one crash every 30s of CPU time
+               100);
+  }
+
+  int64_t active_use_seconds = version_cumulative_active_use_->Get();
+  if (active_use_seconds > 0) {
+    SendSample(version_cumulative_active_use_->Name(),
+               active_use_seconds,
+               1,                          // device may be used very little...
+               8 * 1000 * 1000,            // ... or a lot (about 90 days)
+               100);
+    // Same as above, but per year of active time.
+    SendSample("Logging.KernelCrashesPerActiveYear",
+               crashes_count * kSecondsPerDay * 365 / active_use_seconds,
+               1,
+               1000 * 1000,     // about one crash every 30s of active time
+               100);
+  }
+}
+
+void MetricsDaemon::SendAndResetDailyUseSample(
+    const scoped_ptr<PersistentInteger>& use) {
+  SendSample(use->Name(),
+             use->GetAndClear(),
+             1,                        // value of first bucket
+             kSecondsPerDay,           // value of last bucket
+             50);                      // number of buckets
+}
+
+void MetricsDaemon::SendAndResetCrashIntervalSample(
+    const scoped_ptr<PersistentInteger>& interval) {
+  SendSample(interval->Name(),
+             interval->GetAndClear(),
+             1,                        // value of first bucket
+             4 * kSecondsPerWeek,      // value of last bucket
+             50);                      // number of buckets
+}
+
+void MetricsDaemon::SendAndResetCrashFrequencySample(
+    const scoped_ptr<PersistentInteger>& frequency) {
+  SendSample(frequency->Name(),
+             frequency->GetAndClear(),
+             1,                        // value of first bucket
+             100,                      // value of last bucket
+             50);                      // number of buckets
+}
+
+void MetricsDaemon::SendLinearSample(const string& name, int sample,
+                                     int max, int nbuckets) {
+  // TODO(semenzato): add a proper linear histogram to the Chrome external
+  // metrics API.
+  LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale";
+  metrics_lib_->SendEnumToUMA(name, sample, max);
+}
+
+void MetricsDaemon::UpdateStats(TimeTicks now_ticks,
+                                Time now_wall_time) {
+  const int elapsed_seconds = (now_ticks - last_update_stats_time_).InSeconds();
+  daily_active_use_->Add(elapsed_seconds);
+  version_cumulative_active_use_->Add(elapsed_seconds);
+  user_crash_interval_->Add(elapsed_seconds);
+  kernel_crash_interval_->Add(elapsed_seconds);
+  TimeDelta cpu_use = cpu_usage_collector_->GetCumulativeCpuUse();
+  version_cumulative_cpu_use_->Add(
+      (cpu_use - latest_cpu_use_microseconds_).InMilliseconds());
+  latest_cpu_use_microseconds_ = cpu_use;
+  last_update_stats_time_ = now_ticks;
+
+  const TimeDelta since_epoch = now_wall_time - Time::UnixEpoch();
+  const int day = since_epoch.InDays();
+  const int week = day / 7;
+
+  if (daily_cycle_->Get() != day) {
+    daily_cycle_->Set(day);
+    SendAndResetDailyUseSample(daily_active_use_);
+    SendAndResetCrashFrequencySample(any_crashes_daily_count_);
+    SendAndResetCrashFrequencySample(user_crashes_daily_count_);
+    SendAndResetCrashFrequencySample(kernel_crashes_daily_count_);
+    SendAndResetCrashFrequencySample(unclean_shutdowns_daily_count_);
+    SendKernelCrashesCumulativeCountStats();
+  }
+
+  if (weekly_cycle_->Get() != week) {
+    weekly_cycle_->Set(week);
+    SendAndResetCrashFrequencySample(any_crashes_weekly_count_);
+    SendAndResetCrashFrequencySample(user_crashes_weekly_count_);
+    SendAndResetCrashFrequencySample(kernel_crashes_weekly_count_);
+    SendAndResetCrashFrequencySample(unclean_shutdowns_weekly_count_);
+  }
+}
+
+void MetricsDaemon::HandleUpdateStatsTimeout() {
+  UpdateStats(TimeTicks::Now(), Time::Now());
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
+                 base::Unretained(this)),
+      base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs));
+}
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
new file mode 100644 (file)
index 0000000..f12b02e
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_METRICS_DAEMON_H_
+#define METRICS_METRICS_DAEMON_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/files/file_path.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/time/time.h>
+#include <brillo/daemons/dbus_daemon.h>
+#include <libweaved/command.h>
+#include <libweaved/device.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "collectors/averaged_statistics_collector.h"
+#include "collectors/cpu_usage_collector.h"
+#include "collectors/disk_usage_collector.h"
+#include "metrics/metrics_library.h"
+#include "persistent_integer.h"
+#include "uploader/upload_service.h"
+
+using chromeos_metrics::PersistentInteger;
+
+class MetricsDaemon : public brillo::DBusDaemon {
+ public:
+  MetricsDaemon();
+  ~MetricsDaemon();
+
+  // Initializes metrics class variables.
+  void Init(bool testing,
+            bool uploader_active,
+            bool dbus_enabled,
+            MetricsLibraryInterface* metrics_lib,
+            const std::string& diskstats_path,
+            const std::string& cpuinfo_max_freq_path,
+            const std::string& scaling_max_freq_path,
+            const base::TimeDelta& upload_interval,
+            const std::string& server,
+            const base::FilePath& metrics_directory);
+
+  // Initializes DBus and MessageLoop variables before running the MessageLoop.
+  int OnInit() override;
+
+  // Clean up data set up in OnInit before shutting down message loop.
+  void OnShutdown(int* return_code) override;
+
+  // Does all the work.
+  int Run() override;
+
+  // Triggers an upload event and exit. (Used to test UploadService)
+  void RunUploaderTest();
+
+  // Returns the active time since boot (uptime minus sleep time) in seconds.
+  static double GetActiveTime();
+
+ protected:
+  // Used also by the unit tests.
+  static const char kComprDataSizeName[];
+  static const char kOrigDataSizeName[];
+  static const char kZeroPagesName[];
+
+ private:
+  friend class MetricsDaemonTest;
+  FRIEND_TEST(MetricsDaemonTest, CheckSystemCrash);
+  FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoCurrent);
+  FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoLast);
+  FRIEND_TEST(MetricsDaemonTest, GetHistogramPath);
+  FRIEND_TEST(MetricsDaemonTest, IsNewEpoch);
+  FRIEND_TEST(MetricsDaemonTest, MessageFilter);
+  FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash);
+  FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo);
+  FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo2);
+  FRIEND_TEST(MetricsDaemonTest, ProcessUncleanShutdown);
+  FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash);
+  FRIEND_TEST(MetricsDaemonTest, ReportCrashesDailyFrequency);
+  FRIEND_TEST(MetricsDaemonTest, ReadFreqToInt);
+  FRIEND_TEST(MetricsDaemonTest, ReportKernelCrashInterval);
+  FRIEND_TEST(MetricsDaemonTest, ReportUncleanShutdownInterval);
+  FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval);
+  FRIEND_TEST(MetricsDaemonTest, SendSample);
+  FRIEND_TEST(MetricsDaemonTest, SendCpuThrottleMetrics);
+  FRIEND_TEST(MetricsDaemonTest, SendZramMetrics);
+
+  // Type of scale to use for meminfo histograms.  For most of them we use
+  // percent of total RAM, but for some we use absolute numbers, usually in
+  // megabytes, on a log scale from 0 to 4000, and 0 to 8000 for compressed
+  // swap (since it can be larger than total RAM).
+  enum MeminfoOp {
+    kMeminfoOp_HistPercent = 0,
+    kMeminfoOp_HistLog,
+    kMeminfoOp_SwapTotal,
+    kMeminfoOp_SwapFree,
+  };
+
+  // Record for retrieving and reporting values from /proc/meminfo.
+  struct MeminfoRecord {
+    const char* name;        // print name
+    const char* match;       // string to match in output of /proc/meminfo
+    MeminfoOp op;            // histogram scale selector, or other operator
+    int value;               // value from /proc/meminfo
+  };
+
+  // D-Bus filter callback.
+  static DBusHandlerResult MessageFilter(DBusConnection* connection,
+                                         DBusMessage* message,
+                                         void* user_data);
+
+  // Enables metrics reporting.
+  void OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd);
+
+  // Disables metrics reporting.
+  void OnDisableMetrics(const std::weak_ptr<weaved::Command>& cmd);
+
+  // Updates the weave device state.
+  void UpdateWeaveState();
+
+  // Updates the active use time and logs time between user-space
+  // process crashes.
+  void ProcessUserCrash();
+
+  // Updates the active use time and logs time between kernel crashes.
+  void ProcessKernelCrash();
+
+  // Updates the active use time and logs time between unclean shutdowns.
+  void ProcessUncleanShutdown();
+
+  // Checks if a kernel crash has been detected and returns true if
+  // so.  The method assumes that a kernel crash has happened if
+  // |crash_file| exists.  It removes the file immediately if it
+  // exists, so it must not be called more than once.
+  bool CheckSystemCrash(const std::string& crash_file);
+
+  // Sends a regular (exponential) histogram sample to Chrome for
+  // transport to UMA. See MetricsLibrary::SendToUMA in
+  // metrics_library.h for a description of the arguments.
+  void SendSample(const std::string& name, int sample,
+                  int min, int max, int nbuckets);
+
+  // Sends a linear histogram sample to Chrome for transport to UMA. See
+  // MetricsLibrary::SendToUMA in metrics_library.h for a description of the
+  // arguments.
+  void SendLinearSample(const std::string& name, int sample,
+                        int max, int nbuckets);
+
+  // Sends various cumulative kernel crash-related stats, for instance the
+  // total number of kernel crashes since the last version update.
+  void SendKernelCrashesCumulativeCountStats();
+
+  // Sends a sample representing the number of seconds of active use
+  // for a 24-hour period and reset |use|.
+  void SendAndResetDailyUseSample(
+      const scoped_ptr<PersistentInteger>& use);
+
+  // Sends a sample representing a time interval between two crashes of the
+  // same type and reset |interval|.
+  void SendAndResetCrashIntervalSample(
+      const scoped_ptr<PersistentInteger>& interval);
+
+  // Sends a sample representing a frequency of crashes of some type and reset
+  // |frequency|.
+  void SendAndResetCrashFrequencySample(
+      const scoped_ptr<PersistentInteger>& frequency);
+
+  // Initializes vm and disk stats reporting.
+  void StatsReporterInit();
+
+  // Schedules meminfo collection callback.
+  void ScheduleMeminfoCallback(int wait);
+
+  // Reports memory statistics.  Reschedules callback on success.
+  void MeminfoCallback(base::TimeDelta wait);
+
+  // Parses content of /proc/meminfo and sends fields of interest to UMA.
+  // Returns false on errors.  |meminfo_raw| contains the content of
+  // /proc/meminfo.
+  bool ProcessMeminfo(const std::string& meminfo_raw);
+
+  // Parses meminfo data from |meminfo_raw|.  |fields| is a vector containing
+  // the fields of interest.  The order of the fields must be the same in which
+  // /proc/meminfo prints them.  The result of parsing fields[i] is placed in
+  // fields[i].value.
+  bool FillMeminfo(const std::string& meminfo_raw,
+                   std::vector<MeminfoRecord>* fields);
+
+  // Schedule a memory use callback in |interval| seconds.
+  void ScheduleMemuseCallback(double interval);
+
+  // Calls MemuseCallbackWork, and possibly schedules next callback, if enough
+  // active time has passed.  Otherwise reschedules itself to simulate active
+  // time callbacks (i.e. wall clock time minus sleep time).
+  void MemuseCallback();
+
+  // Reads /proc/meminfo and sends total anonymous memory usage to UMA.
+  bool MemuseCallbackWork();
+
+  // Parses meminfo data and sends it to UMA.
+  bool ProcessMemuse(const std::string& meminfo_raw);
+
+  // Sends stats for thermal CPU throttling.
+  void SendCpuThrottleMetrics();
+
+  // Reads an integer CPU frequency value from sysfs.
+  bool ReadFreqToInt(const std::string& sysfs_file_name, int* value);
+
+  // Reads the current OS version from /etc/lsb-release and hashes it
+  // to a unsigned 32-bit int.
+  uint32_t GetOsVersionHash();
+
+  // Updates stats, additionally sending them to UMA if enough time has elapsed
+  // since the last report.
+  void UpdateStats(base::TimeTicks now_ticks, base::Time now_wall_time);
+
+  // Invoked periodically by |update_stats_timeout_id_| to call UpdateStats().
+  void HandleUpdateStatsTimeout();
+
+  // Reports zram statistics.
+  bool ReportZram(const base::FilePath& zram_dir);
+
+  // Reads a string from a file and converts it to uint64_t.
+  static bool ReadFileToUint64(const base::FilePath& path, uint64_t* value);
+
+  // VARIABLES
+
+  // Test mode.
+  bool testing_;
+
+  // Whether the uploader is enabled or disabled.
+  bool uploader_active_;
+
+  // Whether or not dbus should be used.
+  // If disabled, we will not collect the frequency of crashes.
+  bool dbus_enabled_;
+
+  // Root of the configuration files to use.
+  base::FilePath metrics_directory_;
+
+  // The metrics library handle.
+  MetricsLibraryInterface* metrics_lib_;
+
+  // The last time that UpdateStats() was called.
+  base::TimeTicks last_update_stats_time_;
+
+  // End time of current memuse stat collection interval.
+  double memuse_final_time_;
+
+  // Selects the wait time for the next memory use callback.
+  unsigned int memuse_interval_index_;
+
+  // Used internally by GetIncrementalCpuUse() to return the CPU utilization
+  // between calls.
+  base::TimeDelta latest_cpu_use_microseconds_;
+
+  // Persistent values and accumulators for crash statistics.
+  scoped_ptr<PersistentInteger> daily_cycle_;
+  scoped_ptr<PersistentInteger> weekly_cycle_;
+  scoped_ptr<PersistentInteger> version_cycle_;
+
+  // Active use accumulated in a day.
+  scoped_ptr<PersistentInteger> daily_active_use_;
+  // Active use accumulated since the latest version update.
+  scoped_ptr<PersistentInteger> version_cumulative_active_use_;
+
+  // The CPU time accumulator.  This contains the CPU time, in milliseconds,
+  // used by the system since the most recent OS version update.
+  scoped_ptr<PersistentInteger> version_cumulative_cpu_use_;
+
+  scoped_ptr<PersistentInteger> user_crash_interval_;
+  scoped_ptr<PersistentInteger> kernel_crash_interval_;
+  scoped_ptr<PersistentInteger> unclean_shutdown_interval_;
+
+  scoped_ptr<PersistentInteger> any_crashes_daily_count_;
+  scoped_ptr<PersistentInteger> any_crashes_weekly_count_;
+  scoped_ptr<PersistentInteger> user_crashes_daily_count_;
+  scoped_ptr<PersistentInteger> user_crashes_weekly_count_;
+  scoped_ptr<PersistentInteger> kernel_crashes_daily_count_;
+  scoped_ptr<PersistentInteger> kernel_crashes_weekly_count_;
+  scoped_ptr<PersistentInteger> kernel_crashes_version_count_;
+  scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
+  scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
+
+  scoped_ptr<CpuUsageCollector> cpu_usage_collector_;
+  scoped_ptr<DiskUsageCollector> disk_usage_collector_;
+  scoped_ptr<AveragedStatisticsCollector> averaged_stats_collector_;
+
+  std::string scaling_max_freq_path_;
+  std::string cpuinfo_max_freq_path_;
+
+  base::TimeDelta upload_interval_;
+  std::string server_;
+
+  scoped_ptr<UploadService> upload_service_;
+  std::unique_ptr<weaved::Device> device_;
+};
+
+#endif  // METRICS_METRICS_DAEMON_H_
diff --git a/metricsd/metrics_daemon.rc b/metricsd/metrics_daemon.rc
new file mode 100644 (file)
index 0000000..8b24749
--- /dev/null
@@ -0,0 +1,7 @@
+on post-fs-data
+    mkdir /data/misc/metrics 0770 system system
+
+service metrics_daemon /system/bin/metrics_daemon --uploader --foreground --logtosyslog
+    class late_start
+    user system
+    group system dbus inet
diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc
new file mode 100644 (file)
index 0000000..8573f68
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <base/at_exit.h>
+#include <base/command_line.h>
+#include <base/logging.h>
+#include <base/strings/string_util.h>
+#include <brillo/flag_helper.h>
+#include <brillo/syslog_logging.h>
+#include <rootdev.h>
+
+#include "constants.h"
+#include "metrics_daemon.h"
+
+const char kScalingMaxFreqPath[] =
+    "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq";
+const char kCpuinfoMaxFreqPath[] =
+    "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq";
+
+// Returns the path to the disk stats in the sysfs.  Returns the null string if
+// it cannot find the disk stats file.
+static
+const std::string MetricsMainDiskStatsPath() {
+  char dev_path_cstr[PATH_MAX];
+  std::string dev_prefix = "/dev/block/";
+  std::string dev_path;
+
+  int ret = rootdev(dev_path_cstr, sizeof(dev_path_cstr), true, true);
+  if (ret != 0) {
+    LOG(WARNING) << "error " << ret << " determining root device";
+    return "";
+  }
+  dev_path = dev_path_cstr;
+  // Check that rootdev begins with "/dev/block/".
+  if (!base::StartsWithASCII(dev_path, dev_prefix, false)) {
+    LOG(WARNING) << "unexpected root device " << dev_path;
+    return "";
+  }
+  return "/sys/class/block/" + dev_path.substr(dev_prefix.length()) + "/stat";
+}
+
+int main(int argc, char** argv) {
+  DEFINE_bool(foreground, false, "Don't daemonize");
+
+  // The uploader is disabled by default on ChromeOS as Chrome is responsible
+  // for sending the metrics.
+  DEFINE_bool(uploader, false, "activate the uploader");
+
+  // Upload the metrics once and exit. (used for testing)
+  DEFINE_bool(uploader_test,
+              false,
+              "run the uploader once and exit");
+
+  // Enable dbus.
+  DEFINE_bool(withdbus, true, "Enable dbus");
+
+  // Upload Service flags.
+  DEFINE_int32(upload_interval_secs,
+               1800,
+               "Interval at which metrics_daemon sends the metrics. (needs "
+               "-uploader)");
+  DEFINE_string(server,
+                metrics::kMetricsServer,
+                "Server to upload the metrics to. (needs -uploader)");
+  DEFINE_string(metrics_directory,
+                metrics::kMetricsDirectory,
+                "Root of the configuration files (testing only)");
+
+  DEFINE_bool(logtostderr, false, "Log to standard error");
+  DEFINE_bool(logtosyslog, false, "Log to syslog");
+
+  brillo::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon");
+
+  int logging_location = (FLAGS_foreground ? brillo::kLogToStderr
+                          : brillo::kLogToSyslog);
+  if (FLAGS_logtosyslog)
+    logging_location = brillo::kLogToSyslog;
+
+  if (FLAGS_logtostderr)
+    logging_location = brillo::kLogToStderr;
+
+  // Also log to stderr when not running as daemon.
+  brillo::InitLog(logging_location | brillo::kLogHeader);
+
+  if (FLAGS_logtostderr && FLAGS_logtosyslog) {
+    LOG(ERROR) << "only one of --logtosyslog and --logtostderr can be set";
+    return 1;
+  }
+
+  if (!FLAGS_foreground && daemon(0, 0) != 0) {
+    return errno;
+  }
+
+  MetricsLibrary metrics_lib;
+  metrics_lib.InitWithNoCaching();
+  MetricsDaemon daemon;
+  daemon.Init(FLAGS_uploader_test,
+              FLAGS_uploader | FLAGS_uploader_test,
+              FLAGS_withdbus,
+              &metrics_lib,
+              MetricsMainDiskStatsPath(),
+              kScalingMaxFreqPath,
+              kCpuinfoMaxFreqPath,
+              base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs),
+              FLAGS_server,
+              base::FilePath(FLAGS_metrics_directory));
+
+  if (FLAGS_uploader_test) {
+    daemon.RunUploaderTest();
+    return 0;
+  }
+
+  daemon.Run();
+}
diff --git a/metricsd/metrics_daemon_test.cc b/metricsd/metrics_daemon_test.cc
new file mode 100644 (file)
index 0000000..d3c9a23
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <base/at_exit.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/strings/string_number_conversions.h>
+#include <brillo/flag_helper.h>
+#include <gtest/gtest.h>
+
+#include "constants.h"
+#include "metrics_daemon.h"
+#include "metrics/metrics_library_mock.h"
+#include "persistent_integer_mock.h"
+
+using base::FilePath;
+using base::TimeDelta;
+using std::string;
+using std::vector;
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::AtLeast;
+using ::testing::Return;
+using ::testing::StrictMock;
+using chromeos_metrics::PersistentIntegerMock;
+
+
+class MetricsDaemonTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    brillo::FlagHelper::Init(0, nullptr, "");
+    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+    scaling_max_freq_path_ = temp_dir_.path().Append("scaling_max");
+    cpu_max_freq_path_ = temp_dir_.path().Append("cpu_freq_max");
+
+    CreateUint64ValueFile(cpu_max_freq_path_, 10000000);
+    CreateUint64ValueFile(scaling_max_freq_path_, 10000000);
+
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        temp_dir_.path().value());
+    daemon_.Init(true,
+                 false,
+                 true,
+                 &metrics_lib_,
+                 "",
+                 scaling_max_freq_path_.value(),
+                 cpu_max_freq_path_.value(),
+                 base::TimeDelta::FromMinutes(30),
+                 metrics::kMetricsServer,
+                 temp_dir_.path());
+  }
+
+  // Adds a metrics library mock expectation that the specified metric
+  // will be generated.
+  void ExpectSample(const std::string& name, int sample) {
+    EXPECT_CALL(metrics_lib_, SendToUMA(name, sample, _, _, _))
+        .Times(1)
+        .WillOnce(Return(true))
+        .RetiresOnSaturation();
+  }
+
+  // Creates a new DBus signal message with zero or more string arguments.
+  // The message can be deallocated through DeleteDBusMessage.
+  //
+  // |path| is the object emitting the signal.
+  // |interface| is the interface the signal is emitted from.
+  // |name| is the name of the signal.
+  // |arg_values| contains the values of the string arguments.
+  DBusMessage* NewDBusSignalString(const string& path,
+                                   const string& interface,
+                                   const string& name,
+                                   const vector<string>& arg_values) {
+    DBusMessage* msg = dbus_message_new_signal(path.c_str(),
+                                               interface.c_str(),
+                                               name.c_str());
+    DBusMessageIter iter;
+    dbus_message_iter_init_append(msg, &iter);
+    for (vector<string>::const_iterator it = arg_values.begin();
+         it != arg_values.end(); ++it) {
+      const char* str_value = it->c_str();
+      dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &str_value);
+    }
+    return msg;
+  }
+
+  // Deallocates the DBus message |msg| previously allocated through
+  // dbus_message_new*.
+  void DeleteDBusMessage(DBusMessage* msg) {
+    dbus_message_unref(msg);
+  }
+
+
+  // Creates or overwrites the file in |path| so that it contains the printable
+  // representation of |value|.
+  void CreateUint64ValueFile(const base::FilePath& path, uint64_t value) {
+    std::string value_string = base::Uint64ToString(value);
+    ASSERT_EQ(value_string.length(),
+              base::WriteFile(path, value_string.c_str(),
+                              value_string.length()));
+  }
+
+  // The MetricsDaemon under test.
+  MetricsDaemon daemon_;
+
+  // Temporary directory used for tests.
+  base::ScopedTempDir temp_dir_;
+
+  // Path for the fake files.
+  base::FilePath scaling_max_freq_path_;
+  base::FilePath cpu_max_freq_path_;
+
+  // Mocks. They are strict mock so that all unexpected
+  // calls are marked as failures.
+  StrictMock<MetricsLibraryMock> metrics_lib_;
+};
+
+TEST_F(MetricsDaemonTest, MessageFilter) {
+  // Ignore calls to SendToUMA.
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AnyNumber());
+
+  DBusMessage* msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+  DBusHandlerResult res =
+      MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
+  EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res);
+  DeleteDBusMessage(msg);
+
+  vector<string> signal_args;
+  msg = NewDBusSignalString("/",
+                            "org.chromium.CrashReporter",
+                            "UserCrash",
+                            signal_args);
+  res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
+  EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res);
+  DeleteDBusMessage(msg);
+
+  signal_args.clear();
+  signal_args.push_back("randomstate");
+  signal_args.push_back("bob");  // arbitrary username
+  msg = NewDBusSignalString("/",
+                            "org.chromium.UnknownService.Manager",
+                            "StateChanged",
+                            signal_args);
+  res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_);
+  EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res);
+  DeleteDBusMessage(msg);
+}
+
+TEST_F(MetricsDaemonTest, SendSample) {
+  ExpectSample("Dummy.Metric", 3);
+  daemon_.SendSample("Dummy.Metric", /* sample */ 3,
+                     /* min */ 1, /* max */ 100, /* buckets */ 50);
+}
+
+TEST_F(MetricsDaemonTest, ProcessMeminfo) {
+  string meminfo =
+      "MemTotal:        2000000 kB\nMemFree:          500000 kB\n"
+      "Buffers:         1000000 kB\nCached:           213652 kB\n"
+      "SwapCached:            0 kB\nActive:           133400 kB\n"
+      "Inactive:         183396 kB\nActive(anon):      92984 kB\n"
+      "Inactive(anon):    58860 kB\nActive(file):      40416 kB\n"
+      "Inactive(file):   124536 kB\nUnevictable:           0 kB\n"
+      "Mlocked:               0 kB\nSwapTotal:             0 kB\n"
+      "SwapFree:              0 kB\nDirty:                40 kB\n"
+      "Writeback:             0 kB\nAnonPages:         92652 kB\n"
+      "Mapped:            59716 kB\nShmem:             59196 kB\n"
+      "Slab:              16656 kB\nSReclaimable:       6132 kB\n"
+      "SUnreclaim:        10524 kB\nKernelStack:        1648 kB\n"
+      "PageTables:         2780 kB\nNFS_Unstable:          0 kB\n"
+      "Bounce:                0 kB\nWritebackTmp:          0 kB\n"
+      "CommitLimit:      970656 kB\nCommitted_AS:    1260528 kB\n"
+      "VmallocTotal:     122880 kB\nVmallocUsed:       12144 kB\n"
+      "VmallocChunk:     103824 kB\nDirectMap4k:        9636 kB\n"
+      "DirectMap2M:     1955840 kB\n";
+
+  // All enum calls must report percents.
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, 100)).Times(AtLeast(1));
+  // Check that MemFree is correctly computed at 25%.
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA("Platform.MeminfoMemFree", 25, 100))
+      .Times(AtLeast(1));
+  // Check that we call SendToUma at least once (log histogram).
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _))
+      .Times(AtLeast(1));
+  // Make sure we don't report fields not in the list.
+  EXPECT_CALL(metrics_lib_, SendToUMA("Platform.MeminfoMlocked", _, _, _, _))
+      .Times(0);
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA("Platform.MeminfoMlocked", _, _))
+      .Times(0);
+  EXPECT_TRUE(daemon_.ProcessMeminfo(meminfo));
+}
+
+TEST_F(MetricsDaemonTest, ProcessMeminfo2) {
+  string meminfo = "MemTotal:        2000000 kB\nMemFree:         1000000 kB\n";
+  // Not enough fields.
+  EXPECT_FALSE(daemon_.ProcessMeminfo(meminfo));
+}
+
+TEST_F(MetricsDaemonTest, ReadFreqToInt) {
+  const int fake_scaled_freq = 1666999;
+  const int fake_max_freq = 2000000;
+  int scaled_freq = 0;
+  int max_freq = 0;
+  CreateUint64ValueFile(scaling_max_freq_path_, fake_scaled_freq);
+  CreateUint64ValueFile(cpu_max_freq_path_, fake_max_freq);
+  EXPECT_TRUE(daemon_.testing_);
+  EXPECT_TRUE(daemon_.ReadFreqToInt(scaling_max_freq_path_.value(),
+                                    &scaled_freq));
+  EXPECT_TRUE(daemon_.ReadFreqToInt(cpu_max_freq_path_.value(), &max_freq));
+  EXPECT_EQ(fake_scaled_freq, scaled_freq);
+  EXPECT_EQ(fake_max_freq, max_freq);
+}
+
+TEST_F(MetricsDaemonTest, SendCpuThrottleMetrics) {
+  CreateUint64ValueFile(cpu_max_freq_path_, 2001000);
+  // Test the 101% and 100% cases.
+  CreateUint64ValueFile(scaling_max_freq_path_, 2001000);
+  EXPECT_TRUE(daemon_.testing_);
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 101, 101));
+  daemon_.SendCpuThrottleMetrics();
+  CreateUint64ValueFile(scaling_max_freq_path_, 2000000);
+  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 100, 101));
+  daemon_.SendCpuThrottleMetrics();
+}
+
+TEST_F(MetricsDaemonTest, SendZramMetrics) {
+  EXPECT_TRUE(daemon_.testing_);
+
+  // |compr_data_size| is the size in bytes of compressed data.
+  const uint64_t compr_data_size = 50 * 1000 * 1000;
+  // The constant '3' is a realistic but random choice.
+  // |orig_data_size| does not include zero pages.
+  const uint64_t orig_data_size = compr_data_size * 3;
+  const uint64_t page_size = 4096;
+  const uint64_t zero_pages = 10 * 1000 * 1000 / page_size;
+
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kComprDataSizeName),
+      compr_data_size);
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kOrigDataSizeName),
+      orig_data_size);
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kZeroPagesName), zero_pages);
+
+  const uint64_t real_orig_size = orig_data_size + zero_pages * page_size;
+  const uint64_t zero_ratio_percent =
+      zero_pages * page_size * 100 / real_orig_size;
+  // Ratio samples are in percents.
+  const uint64_t actual_ratio_sample = real_orig_size * 100 / compr_data_size;
+
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, compr_data_size >> 20, _, _, _));
+  EXPECT_CALL(metrics_lib_,
+              SendToUMA(_, (real_orig_size - compr_data_size) >> 20, _, _, _));
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, actual_ratio_sample, _, _, _));
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_pages, _, _, _));
+  EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_ratio_percent, _, _, _));
+
+  EXPECT_TRUE(daemon_.ReportZram(temp_dir_.path()));
+}
diff --git a/metricsd/metrics_library.cc b/metricsd/metrics_library.cc
new file mode 100644 (file)
index 0000000..735d39f
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "metrics/metrics_library.h"
+
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <errno.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <cstring>
+
+#include "constants.h"
+#include "serialization/metric_sample.h"
+#include "serialization/serialization_utils.h"
+
+static const char kCrosEventHistogramName[] = "Platform.CrOSEvent";
+static const int kCrosEventHistogramMax = 100;
+
+/* Add new cros events here.
+ *
+ * The index of the event is sent in the message, so please do not
+ * reorder the names.
+ */
+static const char *kCrosEventNames[] = {
+  "ModemManagerCommandSendFailure",  // 0
+  "HwWatchdogReboot",  // 1
+  "Cras.NoCodecsFoundAtBoot",  // 2
+  "Chaps.DatabaseCorrupted",  // 3
+  "Chaps.DatabaseRepairFailure",  // 4
+  "Chaps.DatabaseCreateFailure",  // 5
+  "Attestation.OriginSpecificExhausted",  // 6
+  "SpringPowerSupply.Original.High",  // 7
+  "SpringPowerSupply.Other.High",  // 8
+  "SpringPowerSupply.Original.Low",  // 9
+  "SpringPowerSupply.ChargerIdle",  // 10
+  "TPM.NonZeroDictionaryAttackCounter",  // 11
+  "TPM.EarlyResetDuringCommand",  // 12
+};
+
+MetricsLibrary::MetricsLibrary() {}
+MetricsLibrary::~MetricsLibrary() {}
+
+// We take buffer and buffer_size as parameters in order to simplify testing
+// of various alignments of the |device_name| with |buffer_size|.
+bool MetricsLibrary::IsDeviceMounted(const char* device_name,
+                                     const char* mounts_file,
+                                     char* buffer,
+                                     int buffer_size,
+                                     bool* result) {
+  if (buffer == nullptr || buffer_size < 1)
+    return false;
+  int mounts_fd = open(mounts_file, O_RDONLY);
+  if (mounts_fd < 0)
+    return false;
+  // match_offset describes:
+  //   -1 -- not beginning of line
+  //   0..strlen(device_name)-1 -- this offset in device_name is next to match
+  //   strlen(device_name) -- matched full name, just need a space.
+  int match_offset = 0;
+  bool match = false;
+  while (!match) {
+    int read_size = read(mounts_fd, buffer, buffer_size);
+    if (read_size <= 0) {
+      if (errno == -EINTR)
+        continue;
+      break;
+    }
+    for (int i = 0; i < read_size; ++i) {
+      if (buffer[i] == '\n') {
+        match_offset = 0;
+        continue;
+      }
+      if (match_offset < 0) {
+        continue;
+      }
+      if (device_name[match_offset] == '\0') {
+        if (buffer[i] == ' ') {
+          match = true;
+          break;
+        }
+        match_offset = -1;
+        continue;
+      }
+
+      if (buffer[i] == device_name[match_offset]) {
+        ++match_offset;
+      } else {
+        match_offset = -1;
+      }
+    }
+  }
+  close(mounts_fd);
+  *result = match;
+  return true;
+}
+
+bool MetricsLibrary::IsGuestMode() {
+  char buffer[256];
+  bool result = false;
+  if (!IsDeviceMounted("guestfs",
+                       "/proc/mounts",
+                       buffer,
+                       sizeof(buffer),
+                       &result)) {
+    return false;
+  }
+  return result && (access("/var/run/state/logged-in", F_OK) == 0);
+}
+
+bool MetricsLibrary::AreMetricsEnabled() {
+  static struct stat stat_buffer;
+  time_t this_check_time = time(nullptr);
+  if (!use_caching_ || this_check_time != cached_enabled_time_) {
+    cached_enabled_time_ = this_check_time;
+    cached_enabled_ = stat(consent_file_.value().data(), &stat_buffer) >= 0;
+  }
+  return cached_enabled_;
+}
+
+void MetricsLibrary::Init() {
+  base::FilePath dir = base::FilePath(metrics::kMetricsDirectory);
+  uma_events_file_ = dir.Append(metrics::kMetricsEventsFileName);
+  consent_file_ = dir.Append(metrics::kConsentFileName);
+  cached_enabled_ = false;
+  cached_enabled_time_ = 0;
+  use_caching_ = true;
+}
+
+void MetricsLibrary::InitWithNoCaching() {
+  Init();
+  use_caching_ = false;
+}
+
+void MetricsLibrary::InitForTest(const base::FilePath& metrics_directory) {
+  uma_events_file_ = metrics_directory.Append(metrics::kMetricsEventsFileName);
+  consent_file_ = metrics_directory.Append(metrics::kConsentFileName);
+  cached_enabled_ = false;
+  cached_enabled_time_ = 0;
+  use_caching_ = true;
+}
+
+bool MetricsLibrary::SendToUMA(const std::string& name,
+                               int sample,
+                               int min,
+                               int max,
+                               int nbuckets) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::HistogramSample(name, sample, min, max, nbuckets)
+           .get(),
+      uma_events_file_.value());
+}
+
+bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample,
+                                   int max) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::LinearHistogramSample(name, sample, max).get(),
+      uma_events_file_.value());
+}
+
+bool MetricsLibrary::SendBoolToUMA(const std::string& name, bool sample) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::LinearHistogramSample(name,
+                                                    sample ? 1 : 0, 2).get(),
+      uma_events_file_.value());
+}
+
+bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::SparseHistogramSample(name, sample).get(),
+      uma_events_file_.value());
+}
+
+bool MetricsLibrary::SendUserActionToUMA(const std::string& action) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::UserActionSample(action).get(),
+      uma_events_file_.value());
+}
+
+bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::CrashSample(crash_kind).get(),
+      uma_events_file_.value());
+}
+
+bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) {
+  for (size_t i = 0; i < arraysize(kCrosEventNames); i++) {
+    if (strcmp(event.c_str(), kCrosEventNames[i]) == 0) {
+      return SendEnumToUMA(kCrosEventHistogramName, i, kCrosEventHistogramMax);
+    }
+  }
+  return false;
+}
diff --git a/metricsd/metrics_library_test.cc b/metricsd/metrics_library_test.cc
new file mode 100644 (file)
index 0000000..be8a4bb
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "metrics/c_metrics_library.h"
+#include "metrics/metrics_library.h"
+
+
+class MetricsLibraryTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    lib_.InitForTest(temp_dir_.path());
+    EXPECT_EQ(0, WriteFile(lib_.uma_events_file_, "", 0));
+    // Defeat metrics enabled caching between tests.
+    lib_.cached_enabled_time_ = 0;
+  }
+
+  void SetMetricsConsent(bool enabled) {
+    if (enabled) {
+      ASSERT_EQ(base::WriteFile(lib_.consent_file_, "", 0), 0);
+    } else {
+      ASSERT_TRUE(base::DeleteFile(lib_.consent_file_, false));
+    }
+  }
+
+  void VerifyEnabledCacheHit(bool to_value);
+  void VerifyEnabledCacheEviction(bool to_value);
+
+  MetricsLibrary lib_;
+  base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) {
+  SetMetricsConsent(false);
+  EXPECT_FALSE(lib_.AreMetricsEnabled());
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledTrue) {
+  SetMetricsConsent(true);
+  EXPECT_TRUE(lib_.AreMetricsEnabled());
+}
+
+void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) {
+  // We might step from one second to the next one time, but not 100
+  // times in a row.
+  for (int i = 0; i < 100; ++i) {
+    lib_.cached_enabled_time_ = 0;
+    SetMetricsConsent(to_value);
+    lib_.AreMetricsEnabled();
+    // If we check the metrics status twice in a row, we use the cached value
+    // the second time.
+    SetMetricsConsent(!to_value);
+    if (lib_.AreMetricsEnabled() == to_value)
+      return;
+  }
+  ADD_FAILURE() << "Did not see evidence of caching";
+}
+
+void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) {
+  lib_.cached_enabled_time_ = 0;
+  SetMetricsConsent(!to_value);
+  ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
+
+  SetMetricsConsent(to_value);
+  // Sleep one second (or cheat to be faster) and check that we are not using
+  // the cached value.
+  --lib_.cached_enabled_time_;
+  ASSERT_EQ(to_value, lib_.AreMetricsEnabled());
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledCaching) {
+  VerifyEnabledCacheHit(false);
+  VerifyEnabledCacheHit(true);
+  VerifyEnabledCacheEviction(false);
+  VerifyEnabledCacheEviction(true);
+}
+
+TEST_F(MetricsLibraryTest, AreMetricsEnabledNoCaching) {
+  // disable caching.
+  lib_.use_caching_ = false;
+
+  // Checking the consent repeatedly should return the right result.
+  for (int i=0; i<100; ++i) {
+    SetMetricsConsent(true);
+    ASSERT_EQ(true, lib_.AreMetricsEnabled());
+    SetMetricsConsent(false);
+    ASSERT_EQ(false, lib_.AreMetricsEnabled());
+  }
+}
diff --git a/metricsd/persistent_integer.cc b/metricsd/persistent_integer.cc
new file mode 100644 (file)
index 0000000..e849f00
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "persistent_integer.h"
+
+#include <fcntl.h>
+
+#include <base/logging.h>
+#include <base/posix/eintr_wrapper.h>
+
+#include "constants.h"
+#include "metrics/metrics_library.h"
+
+
+namespace chromeos_metrics {
+
+// Static class member instantiation.
+std::string PersistentInteger::metrics_directory_ = metrics::kMetricsDirectory;
+
+PersistentInteger::PersistentInteger(const std::string& name) :
+      value_(0),
+      version_(kVersion),
+      name_(name),
+      synced_(false) {
+  backing_file_name_ = metrics_directory_ + name_;
+}
+
+PersistentInteger::~PersistentInteger() {}
+
+void PersistentInteger::Set(int64_t value) {
+  value_ = value;
+  Write();
+}
+
+int64_t PersistentInteger::Get() {
+  // If not synced, then read.  If the read fails, it's a good idea to write.
+  if (!synced_ && !Read())
+    Write();
+  return value_;
+}
+
+int64_t PersistentInteger::GetAndClear() {
+  int64_t v = Get();
+  Set(0);
+  return v;
+}
+
+void PersistentInteger::Add(int64_t x) {
+  Set(Get() + x);
+}
+
+void PersistentInteger::Write() {
+  int fd = HANDLE_EINTR(open(backing_file_name_.c_str(),
+                             O_WRONLY | O_CREAT | O_TRUNC,
+                             S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH));
+  PCHECK(fd >= 0) << "cannot open " << backing_file_name_ << " for writing";
+  PCHECK((HANDLE_EINTR(write(fd, &version_, sizeof(version_))) ==
+          sizeof(version_)) &&
+         (HANDLE_EINTR(write(fd, &value_, sizeof(value_))) ==
+          sizeof(value_)))
+      << "cannot write to " << backing_file_name_;
+  close(fd);
+  synced_ = true;
+}
+
+bool PersistentInteger::Read() {
+  int fd = HANDLE_EINTR(open(backing_file_name_.c_str(), O_RDONLY));
+  if (fd < 0) {
+    PLOG(WARNING) << "cannot open " << backing_file_name_ << " for reading";
+    return false;
+  }
+  int32_t version;
+  int64_t value;
+  bool read_succeeded = false;
+  if (HANDLE_EINTR(read(fd, &version, sizeof(version))) == sizeof(version) &&
+      version == version_ &&
+      HANDLE_EINTR(read(fd, &value, sizeof(value))) == sizeof(value)) {
+    value_ = value;
+    read_succeeded = true;
+    synced_ = true;
+  }
+  close(fd);
+  return read_succeeded;
+}
+
+void PersistentInteger::SetMetricsDirectory(const std::string& directory) {
+  metrics_directory_ = directory;
+}
+
+
+}  // namespace chromeos_metrics
diff --git a/metricsd/persistent_integer.h b/metricsd/persistent_integer.h
new file mode 100644 (file)
index 0000000..ecef3d1
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_PERSISTENT_INTEGER_H_
+#define METRICS_PERSISTENT_INTEGER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+namespace chromeos_metrics {
+
+// PersistentIntegers is a named 64-bit integer value backed by a file.
+// The in-memory value acts as a write-through cache of the file value.
+// If the backing file doesn't exist or has bad content, the value is 0.
+
+class PersistentInteger {
+ public:
+  explicit PersistentInteger(const std::string& name);
+
+  // Virtual only because of mock.
+  virtual ~PersistentInteger();
+
+  // Sets the value.  This writes through to the backing file.
+  void Set(int64_t v);
+
+  // Gets the value.  May sync from backing file first.
+  int64_t Get();
+
+  // Returns the name of the object.
+  std::string Name() { return name_; }
+
+  // Convenience function for Get() followed by Set(0).
+  int64_t GetAndClear();
+
+  // Convenience function for v = Get, Set(v + x).
+  // Virtual only because of mock.
+  virtual void Add(int64_t x);
+
+  // Sets the directory path for all persistent integers.
+  // This is used in unittests to change where the counters are stored.
+  static void SetMetricsDirectory(const std::string& directory);
+
+ private:
+  static const int kVersion = 1001;
+
+  // Writes |value_| to the backing file, creating it if necessary.
+  void Write();
+
+  // Reads the value from the backing file, stores it in |value_|, and returns
+  // true if the backing file is valid.  Returns false otherwise, and creates
+  // a valid backing file as a side effect.
+  bool Read();
+
+  int64_t value_;
+  int32_t version_;
+  std::string name_;
+  std::string backing_file_name_;
+  static std::string metrics_directory_;
+  bool synced_;
+};
+
+}  // namespace chromeos_metrics
+
+#endif  // METRICS_PERSISTENT_INTEGER_H_
diff --git a/metricsd/persistent_integer_mock.h b/metricsd/persistent_integer_mock.h
new file mode 100644 (file)
index 0000000..acc5389
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_PERSISTENT_INTEGER_MOCK_H_
+#define METRICS_PERSISTENT_INTEGER_MOCK_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "persistent_integer.h"
+
+namespace chromeos_metrics {
+
+class PersistentIntegerMock : public PersistentInteger {
+ public:
+  explicit PersistentIntegerMock(const std::string& name)
+      : PersistentInteger(name) {}
+    MOCK_METHOD1(Add, void(int64_t count));
+};
+
+}  // namespace chromeos_metrics
+
+#endif  // METRICS_PERSISTENT_INTEGER_MOCK_H_
diff --git a/metricsd/persistent_integer_test.cc b/metricsd/persistent_integer_test.cc
new file mode 100644 (file)
index 0000000..5e2067f
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <base/compiler_specific.h>
+#include <base/files/file_enumerator.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+
+#include "persistent_integer.h"
+
+const char kBackingFileName[] = "1.pibakf";
+const char kBackingFilePattern[] = "*.pibakf";
+
+using chromeos_metrics::PersistentInteger;
+
+class PersistentIntegerTest : public testing::Test {
+  void SetUp() override {
+    // Set testing mode.
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        temp_dir_.path().value());
+  }
+
+  void TearDown() override {
+    // Remove backing files.  The convention is that they all end in ".pibakf".
+    base::FileEnumerator f_enum(base::FilePath("."),
+                                false,
+                                base::FileEnumerator::FILES,
+                                FILE_PATH_LITERAL(kBackingFilePattern));
+    for (base::FilePath name = f_enum.Next();
+         !name.empty();
+         name = f_enum.Next()) {
+      base::DeleteFile(name, false);
+    }
+  }
+
+  base::ScopedTempDir temp_dir_;
+};
+
+TEST_F(PersistentIntegerTest, BasicChecks) {
+  scoped_ptr<PersistentInteger> pi(new PersistentInteger(kBackingFileName));
+
+  // Test initialization.
+  EXPECT_EQ(0, pi->Get());
+  EXPECT_EQ(kBackingFileName, pi->Name());  // boring
+
+  // Test set and add.
+  pi->Set(2);
+  pi->Add(3);
+  EXPECT_EQ(5, pi->Get());
+
+  // Test persistence.
+  pi.reset(new PersistentInteger(kBackingFileName));
+  EXPECT_EQ(5, pi->Get());
+
+  // Test GetAndClear.
+  EXPECT_EQ(5, pi->GetAndClear());
+  EXPECT_EQ(pi->Get(), 0);
+
+  // Another persistence test.
+  pi.reset(new PersistentInteger(kBackingFileName));
+  EXPECT_EQ(0, pi->Get());
+}
diff --git a/metricsd/serialization/metric_sample.cc b/metricsd/serialization/metric_sample.cc
new file mode 100644 (file)
index 0000000..76a47c0
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "serialization/metric_sample.h"
+
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+
+namespace metrics {
+
+MetricSample::MetricSample(MetricSample::SampleType sample_type,
+                           const std::string& metric_name,
+                           int sample,
+                           int min,
+                           int max,
+                           int bucket_count)
+    : type_(sample_type),
+      name_(metric_name),
+      sample_(sample),
+      min_(min),
+      max_(max),
+      bucket_count_(bucket_count) {
+}
+
+MetricSample::~MetricSample() {
+}
+
+bool MetricSample::IsValid() const {
+  return name().find(' ') == std::string::npos &&
+         name().find('\0') == std::string::npos && !name().empty();
+}
+
+std::string MetricSample::ToString() const {
+  if (type_ == CRASH) {
+    return base::StringPrintf("crash%c%s%c",
+                              '\0',
+                              name().c_str(),
+                              '\0');
+  } else if (type_ == SPARSE_HISTOGRAM) {
+    return base::StringPrintf("sparsehistogram%c%s %d%c",
+                              '\0',
+                              name().c_str(),
+                              sample_,
+                              '\0');
+  } else if (type_ == LINEAR_HISTOGRAM) {
+    return base::StringPrintf("linearhistogram%c%s %d %d%c",
+                              '\0',
+                              name().c_str(),
+                              sample_,
+                              max_,
+                              '\0');
+  } else if (type_ == HISTOGRAM) {
+    return base::StringPrintf("histogram%c%s %d %d %d %d%c",
+                              '\0',
+                              name().c_str(),
+                              sample_,
+                              min_,
+                              max_,
+                              bucket_count_,
+                              '\0');
+  } else {
+    // The type can only be USER_ACTION.
+    CHECK_EQ(type_, USER_ACTION);
+    return base::StringPrintf("useraction%c%s%c",
+                              '\0',
+                              name().c_str(),
+                              '\0');
+  }
+}
+
+int MetricSample::sample() const {
+  CHECK_NE(type_, USER_ACTION);
+  CHECK_NE(type_, CRASH);
+  return sample_;
+}
+
+int MetricSample::min() const {
+  CHECK_EQ(type_, HISTOGRAM);
+  return min_;
+}
+
+int MetricSample::max() const {
+  CHECK_NE(type_, CRASH);
+  CHECK_NE(type_, USER_ACTION);
+  CHECK_NE(type_, SPARSE_HISTOGRAM);
+  return max_;
+}
+
+int MetricSample::bucket_count() const {
+  CHECK_EQ(type_, HISTOGRAM);
+  return bucket_count_;
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::CrashSample(
+    const std::string& crash_name) {
+  return scoped_ptr<MetricSample>(
+      new MetricSample(CRASH, crash_name, 0, 0, 0, 0));
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::HistogramSample(
+    const std::string& histogram_name,
+    int sample,
+    int min,
+    int max,
+    int bucket_count) {
+  return scoped_ptr<MetricSample>(new MetricSample(
+      HISTOGRAM, histogram_name, sample, min, max, bucket_count));
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::ParseHistogram(
+    const std::string& serialized_histogram) {
+  std::vector<std::string> parts;
+  base::SplitString(serialized_histogram, ' ', &parts);
+
+  if (parts.size() != 5)
+    return scoped_ptr<MetricSample>();
+  int sample, min, max, bucket_count;
+  if (parts[0].empty() || !base::StringToInt(parts[1], &sample) ||
+      !base::StringToInt(parts[2], &min) ||
+      !base::StringToInt(parts[3], &max) ||
+      !base::StringToInt(parts[4], &bucket_count)) {
+    return scoped_ptr<MetricSample>();
+  }
+
+  return HistogramSample(parts[0], sample, min, max, bucket_count);
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::SparseHistogramSample(
+    const std::string& histogram_name,
+    int sample) {
+  return scoped_ptr<MetricSample>(
+      new MetricSample(SPARSE_HISTOGRAM, histogram_name, sample, 0, 0, 0));
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::ParseSparseHistogram(
+    const std::string& serialized_histogram) {
+  std::vector<std::string> parts;
+  base::SplitString(serialized_histogram, ' ', &parts);
+  if (parts.size() != 2)
+    return scoped_ptr<MetricSample>();
+  int sample;
+  if (parts[0].empty() || !base::StringToInt(parts[1], &sample))
+    return scoped_ptr<MetricSample>();
+
+  return SparseHistogramSample(parts[0], sample);
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::LinearHistogramSample(
+    const std::string& histogram_name,
+    int sample,
+    int max) {
+  return scoped_ptr<MetricSample>(
+      new MetricSample(LINEAR_HISTOGRAM, histogram_name, sample, 0, max, 0));
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::ParseLinearHistogram(
+    const std::string& serialized_histogram) {
+  std::vector<std::string> parts;
+  int sample, max;
+  base::SplitString(serialized_histogram, ' ', &parts);
+  if (parts.size() != 3)
+    return scoped_ptr<MetricSample>();
+  if (parts[0].empty() || !base::StringToInt(parts[1], &sample) ||
+      !base::StringToInt(parts[2], &max)) {
+    return scoped_ptr<MetricSample>();
+  }
+
+  return LinearHistogramSample(parts[0], sample, max);
+}
+
+// static
+scoped_ptr<MetricSample> MetricSample::UserActionSample(
+    const std::string& action_name) {
+  return scoped_ptr<MetricSample>(
+      new MetricSample(USER_ACTION, action_name, 0, 0, 0, 0));
+}
+
+bool MetricSample::IsEqual(const MetricSample& metric) {
+  return type_ == metric.type_ && name_ == metric.name_ &&
+         sample_ == metric.sample_ && min_ == metric.min_ &&
+         max_ == metric.max_ && bucket_count_ == metric.bucket_count_;
+}
+
+}  // namespace metrics
diff --git a/metricsd/serialization/metric_sample.h b/metricsd/serialization/metric_sample.h
new file mode 100644 (file)
index 0000000..5a4e4ae
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_SERIALIZATION_METRIC_SAMPLE_H_
+#define METRICS_SERIALIZATION_METRIC_SAMPLE_H_
+
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace metrics {
+
+// This class is used by libmetrics (ChromeOS) to serialize
+// and deserialize measurements to send them to a metrics sending service.
+// It is meant to be a simple container with serialization functions.
+class MetricSample {
+ public:
+  // Types of metric sample used.
+  enum SampleType {
+    CRASH,
+    HISTOGRAM,
+    LINEAR_HISTOGRAM,
+    SPARSE_HISTOGRAM,
+    USER_ACTION
+  };
+
+  ~MetricSample();
+
+  // Returns true if the sample is valid (can be serialized without ambiguity).
+  //
+  // This function should be used to filter bad samples before serializing them.
+  bool IsValid() const;
+
+  // Getters for type and name. All types of metrics have these so we do not
+  // need to check the type.
+  SampleType type() const { return type_; }
+  const std::string& name() const { return name_; }
+
+  // Getters for sample, min, max, bucket_count.
+  // Check the metric type to make sure the request make sense. (ex: a crash
+  // sample does not have a bucket_count so we crash if we call bucket_count()
+  // on it.)
+  int sample() const;
+  int min() const;
+  int max() const;
+  int bucket_count() const;
+
+  // Returns a serialized version of the sample.
+  //
+  // The serialized message for each type is:
+  // crash: crash\0|name_|\0
+  // user action: useraction\0|name_|\0
+  // histogram: histogram\0|name_| |sample_| |min_| |max_| |bucket_count_|\0
+  // sparsehistogram: sparsehistogram\0|name_| |sample_|\0
+  // linearhistogram: linearhistogram\0|name_| |sample_| |max_|\0
+  std::string ToString() const;
+
+  // Builds a crash sample.
+  static scoped_ptr<MetricSample> CrashSample(const std::string& crash_name);
+
+  // Builds a histogram sample.
+  static scoped_ptr<MetricSample> HistogramSample(
+      const std::string& histogram_name,
+      int sample,
+      int min,
+      int max,
+      int bucket_count);
+  // Deserializes a histogram sample.
+  static scoped_ptr<MetricSample> ParseHistogram(const std::string& serialized);
+
+  // Builds a sparse histogram sample.
+  static scoped_ptr<MetricSample> SparseHistogramSample(
+      const std::string& histogram_name,
+      int sample);
+  // Deserializes a sparse histogram sample.
+  static scoped_ptr<MetricSample> ParseSparseHistogram(
+      const std::string& serialized);
+
+  // Builds a linear histogram sample.
+  static scoped_ptr<MetricSample> LinearHistogramSample(
+      const std::string& histogram_name,
+      int sample,
+      int max);
+  // Deserializes a linear histogram sample.
+  static scoped_ptr<MetricSample> ParseLinearHistogram(
+      const std::string& serialized);
+
+  // Builds a user action sample.
+  static scoped_ptr<MetricSample> UserActionSample(
+      const std::string& action_name);
+
+  // Returns true if sample and this object represent the same sample (type,
+  // name, sample, min, max, bucket_count match).
+  bool IsEqual(const MetricSample& sample);
+
+ private:
+  MetricSample(SampleType sample_type,
+               const std::string& metric_name,
+               const int sample,
+               const int min,
+               const int max,
+               const int bucket_count);
+
+  const SampleType type_;
+  const std::string name_;
+  const int sample_;
+  const int min_;
+  const int max_;
+  const int bucket_count_;
+
+  DISALLOW_COPY_AND_ASSIGN(MetricSample);
+};
+
+}  // namespace metrics
+
+#endif  // METRICS_SERIALIZATION_METRIC_SAMPLE_H_
diff --git a/metricsd/serialization/serialization_utils.cc b/metricsd/serialization/serialization_utils.cc
new file mode 100644 (file)
index 0000000..102c940
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "serialization/serialization_utils.h"
+
+#include <sys/file.h>
+
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "serialization/metric_sample.h"
+
+#define READ_WRITE_ALL_FILE_FLAGS \
+  (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
+
+namespace metrics {
+namespace {
+
+// Reads the next message from |file_descriptor| into |message|.
+//
+// |message| will be set to the empty string if no message could be read (EOF)
+// or the message was badly constructed.
+//
+// Returns false if no message can be read from this file anymore (EOF or
+// unrecoverable error).
+bool ReadMessage(int fd, std::string* message) {
+  CHECK(message);
+
+  int result;
+  int32_t message_size;
+  const int32_t message_hdr_size = sizeof(message_size);
+  // The file containing the metrics do not leave the device so the writer and
+  // the reader will always have the same endianness.
+  result = HANDLE_EINTR(read(fd, &message_size, sizeof(message_size)));
+  if (result < 0) {
+    DPLOG(ERROR) << "reading metrics message header";
+    return false;
+  }
+  if (result == 0) {
+    // This indicates a normal EOF.
+    return false;
+  }
+  if (result < message_hdr_size) {
+    DLOG(ERROR) << "bad read size " << result << ", expecting "
+                << sizeof(message_size);
+    return false;
+  }
+
+  // kMessageMaxLength applies to the entire message: the 4-byte
+  // length field and the content.
+  if (message_size > SerializationUtils::kMessageMaxLength) {
+    DLOG(ERROR) << "message too long : " << message_size;
+    if (HANDLE_EINTR(lseek(fd, message_size - 4, SEEK_CUR)) == -1) {
+      DLOG(ERROR) << "error while skipping message. abort";
+      return false;
+    }
+    // Badly formatted message was skipped. Treat the badly formatted sample as
+    // an empty sample.
+    message->clear();
+    return true;
+  }
+
+  if (message_size < message_hdr_size) {
+    DLOG(ERROR) << "message too short : " << message_size;
+    return false;
+  }
+
+  message_size -= message_hdr_size;  // The message size includes itself.
+  char buffer[SerializationUtils::kMessageMaxLength];
+  if (!base::ReadFromFD(fd, buffer, message_size)) {
+    DPLOG(ERROR) << "reading metrics message body";
+    return false;
+  }
+  *message = std::string(buffer, message_size);
+  return true;
+}
+
+
+// Opens the metrics log file at |filename| in the given |mode|.
+//
+// Returns the file descriptor wrapped in a valid ScopedFD on success.
+base::ScopedFD OpenMetricsFile(const std::string& filename, mode_t mode) {
+  struct stat stat_buf;
+  int result;
+
+  result = stat(filename.c_str(), &stat_buf);
+  if (result < 0) {
+    if (errno != ENOENT)
+      DPLOG(ERROR) << filename << ": bad metrics file stat";
+
+    // Nothing to collect---try later.
+    return base::ScopedFD();
+  }
+  if (stat_buf.st_size == 0) {
+    // Also nothing to collect.
+    return base::ScopedFD();
+  }
+  base::ScopedFD fd(open(filename.c_str(), mode));
+  if (fd.get() < 0) {
+    DPLOG(ERROR) << filename << ": cannot open";
+    return base::ScopedFD();
+  }
+
+  return fd.Pass();
+}
+
+
+// Parses the contents of the metrics log file descriptor |fd| into |metrics|.
+void ReadAllMetricsFromFd(int fd, ScopedVector<MetricSample>* metrics) {
+  for (;;) {
+    std::string message;
+
+    if (!ReadMessage(fd, &message))
+      break;
+
+    scoped_ptr<MetricSample> sample = SerializationUtils::ParseSample(message);
+    if (sample)
+      metrics->push_back(sample.release());
+  }
+}
+
+}  // namespace
+
+scoped_ptr<MetricSample> SerializationUtils::ParseSample(
+    const std::string& sample) {
+  if (sample.empty())
+    return scoped_ptr<MetricSample>();
+
+  std::vector<std::string> parts;
+  base::SplitString(sample, '\0', &parts);
+  // We should have two null terminated strings so split should produce
+  // three chunks.
+  if (parts.size() != 3) {
+    DLOG(ERROR) << "splitting message on \\0 produced " << parts.size()
+                << " parts (expected 3)";
+    return scoped_ptr<MetricSample>();
+  }
+  const std::string& name = parts[0];
+  const std::string& value = parts[1];
+
+  if (base::LowerCaseEqualsASCII(name, "crash")) {
+    return MetricSample::CrashSample(value);
+  } else if (base::LowerCaseEqualsASCII(name, "histogram")) {
+    return MetricSample::ParseHistogram(value);
+  } else if (base::LowerCaseEqualsASCII(name, "linearhistogram")) {
+    return MetricSample::ParseLinearHistogram(value);
+  } else if (base::LowerCaseEqualsASCII(name, "sparsehistogram")) {
+    return MetricSample::ParseSparseHistogram(value);
+  } else if (base::LowerCaseEqualsASCII(name, "useraction")) {
+    return MetricSample::UserActionSample(value);
+  } else {
+    DLOG(ERROR) << "invalid event type: " << name << ", value: " << value;
+  }
+  return scoped_ptr<MetricSample>();
+}
+
+void SerializationUtils::ReadMetricsFromFile(
+    const std::string& filename,
+    ScopedVector<MetricSample>* metrics) {
+  base::ScopedFD fd(OpenMetricsFile(filename, O_RDONLY));
+  if (!fd.is_valid()) {
+    return;
+  }
+
+  // This processes all messages in the log.
+  ReadAllMetricsFromFd(fd.get(), metrics);
+}
+
+void SerializationUtils::ReadAndTruncateMetricsFromFile(
+    const std::string& filename,
+    ScopedVector<MetricSample>* metrics) {
+  base::ScopedFD fd(OpenMetricsFile(filename, O_RDWR));
+  if (!fd.is_valid()) {
+    return;
+  }
+
+  int result = flock(fd.get(), LOCK_EX);
+  if (result < 0) {
+    DPLOG(ERROR) << filename << ": cannot lock";
+    return;
+  }
+
+  // This processes all messages in the log. When all messages are
+  // read and processed, or an error occurs, truncate the file to zero size.
+  ReadAllMetricsFromFd(fd.get(), metrics);
+
+  result = ftruncate(fd.get(), 0);
+  if (result < 0)
+    DPLOG(ERROR) << "truncate metrics log";
+
+  result = flock(fd.get(), LOCK_UN);
+  if (result < 0)
+    DPLOG(ERROR) << "unlock metrics log";
+}
+
+bool SerializationUtils::WriteMetricToFile(const MetricSample& sample,
+                                           const std::string& filename) {
+  if (!sample.IsValid())
+    return false;
+
+  base::ScopedFD file_descriptor(open(filename.c_str(),
+                                      O_WRONLY | O_APPEND | O_CREAT,
+                                      READ_WRITE_ALL_FILE_FLAGS));
+
+  if (file_descriptor.get() < 0) {
+    DPLOG(ERROR) << filename << ": cannot open";
+    return false;
+  }
+
+  fchmod(file_descriptor.get(), READ_WRITE_ALL_FILE_FLAGS);
+  // Grab a lock to avoid chrome truncating the file
+  // underneath us. Keep the file locked as briefly as possible.
+  // Freeing file_descriptor will close the file and and remove the lock.
+  if (HANDLE_EINTR(flock(file_descriptor.get(), LOCK_EX)) < 0) {
+    DPLOG(ERROR) << filename << ": cannot lock";
+    return false;
+  }
+
+  std::string msg = sample.ToString();
+  int32 size = msg.length() + sizeof(int32);
+  if (size > kMessageMaxLength) {
+    DLOG(ERROR) << "cannot write message: too long";
+    return false;
+  }
+
+  // The file containing the metrics samples will only be read by programs on
+  // the same device so we do not check endianness.
+  if (!base::WriteFileDescriptor(file_descriptor.get(),
+                                 reinterpret_cast<char*>(&size),
+                                 sizeof(size))) {
+    DPLOG(ERROR) << "error writing message length";
+    return false;
+  }
+
+  if (!base::WriteFileDescriptor(
+          file_descriptor.get(), msg.c_str(), msg.size())) {
+    DPLOG(ERROR) << "error writing message";
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace metrics
diff --git a/metricsd/serialization/serialization_utils.h b/metricsd/serialization/serialization_utils.h
new file mode 100644 (file)
index 0000000..655652d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
+#define METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+
+namespace metrics {
+
+class MetricSample;
+
+// Metrics helpers to serialize and deserialize metrics collected by
+// ChromeOS.
+namespace SerializationUtils {
+
+// Deserializes a sample passed as a string and return a sample.
+// The return value will either be a scoped_ptr to a Metric sample (if the
+// deserialization was successful) or a NULL scoped_ptr.
+scoped_ptr<MetricSample> ParseSample(const std::string& sample);
+
+// Reads all samples from a file. The file contents remain unchanged.
+void ReadMetricsFromFile(const std::string& filename,
+                         ScopedVector<MetricSample>* metrics);
+
+// Reads all samples from a file and truncates the file when done.
+void ReadAndTruncateMetricsFromFile(const std::string& filename,
+                                    ScopedVector<MetricSample>* metrics);
+
+// Serializes a sample and write it to filename.
+// The format for the message is:
+//  message_size, serialized_message
+// where
+//  * message_size is the total length of the message (message_size +
+//    serialized_message) on 4 bytes
+//  * serialized_message is the serialized version of sample (using ToString)
+//
+//  NB: the file will never leave the device so message_size will be written
+//  with the architecture's endianness.
+bool WriteMetricToFile(const MetricSample& sample, const std::string& filename);
+
+// Maximum length of a serialized message
+static const int kMessageMaxLength = 1024;
+
+}  // namespace SerializationUtils
+}  // namespace metrics
+
+#endif  // METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_
diff --git a/metricsd/serialization/serialization_utils_unittest.cc b/metricsd/serialization/serialization_utils_unittest.cc
new file mode 100644 (file)
index 0000000..7a572de
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "serialization/serialization_utils.h"
+
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/logging.h>
+#include <base/strings/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "serialization/metric_sample.h"
+
+namespace metrics {
+namespace {
+
+class SerializationUtilsTest : public testing::Test {
+ protected:
+  SerializationUtilsTest() {
+    bool success = temporary_dir.CreateUniqueTempDir();
+    if (success) {
+      base::FilePath dir_path = temporary_dir.path();
+      filename = dir_path.value() + "chromeossampletest";
+      filepath = base::FilePath(filename);
+    }
+  }
+
+  void SetUp() override { base::DeleteFile(filepath, false); }
+
+  void TestSerialization(MetricSample* sample) {
+    std::string serialized(sample->ToString());
+    ASSERT_EQ('\0', serialized[serialized.length() - 1]);
+    scoped_ptr<MetricSample> deserialized =
+        SerializationUtils::ParseSample(serialized);
+    ASSERT_TRUE(deserialized);
+    EXPECT_TRUE(sample->IsEqual(*deserialized.get()));
+  }
+
+  std::string filename;
+  base::ScopedTempDir temporary_dir;
+  base::FilePath filepath;
+};
+
+TEST_F(SerializationUtilsTest, CrashSerializeTest) {
+  TestSerialization(MetricSample::CrashSample("test").get());
+}
+
+TEST_F(SerializationUtilsTest, HistogramSerializeTest) {
+  TestSerialization(
+      MetricSample::HistogramSample("myhist", 13, 1, 100, 10).get());
+}
+
+TEST_F(SerializationUtilsTest, LinearSerializeTest) {
+  TestSerialization(
+      MetricSample::LinearHistogramSample("linearhist", 12, 30).get());
+}
+
+TEST_F(SerializationUtilsTest, SparseSerializeTest) {
+  TestSerialization(MetricSample::SparseHistogramSample("mysparse", 30).get());
+}
+
+TEST_F(SerializationUtilsTest, UserActionSerializeTest) {
+  TestSerialization(MetricSample::UserActionSample("myaction").get());
+}
+
+TEST_F(SerializationUtilsTest, IllegalNameAreFilteredTest) {
+  scoped_ptr<MetricSample> sample1 =
+      MetricSample::SparseHistogramSample("no space", 10);
+  scoped_ptr<MetricSample> sample2 = MetricSample::LinearHistogramSample(
+      base::StringPrintf("here%cbhe", '\0'), 1, 3);
+
+  EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample1.get(), filename));
+  EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample2.get(), filename));
+  int64 size = 0;
+
+  ASSERT_TRUE(!PathExists(filepath) || base::GetFileSize(filepath, &size));
+
+  EXPECT_EQ(0, size);
+}
+
+TEST_F(SerializationUtilsTest, BadInputIsCaughtTest) {
+  std::string input(
+      base::StringPrintf("sparsehistogram%cname foo%c", '\0', '\0'));
+  EXPECT_EQ(NULL, MetricSample::ParseSparseHistogram(input).get());
+}
+
+TEST_F(SerializationUtilsTest, MessageSeparatedByZero) {
+  scoped_ptr<MetricSample> crash = MetricSample::CrashSample("mycrash");
+
+  SerializationUtils::WriteMetricToFile(*crash.get(), filename);
+  int64 size = 0;
+  ASSERT_TRUE(base::GetFileSize(filepath, &size));
+  // 4 bytes for the size
+  // 5 bytes for crash
+  // 7 bytes for mycrash
+  // 2 bytes for the \0
+  // -> total of 18
+  EXPECT_EQ(size, 18);
+}
+
+TEST_F(SerializationUtilsTest, MessagesTooLongAreDiscardedTest) {
+  // Creates a message that is bigger than the maximum allowed size.
+  // As we are adding extra character (crash, \0s, etc), if the name is
+  // kMessageMaxLength long, it will be too long.
+  std::string name(SerializationUtils::kMessageMaxLength, 'c');
+
+  scoped_ptr<MetricSample> crash = MetricSample::CrashSample(name);
+  EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*crash.get(), filename));
+  int64 size = 0;
+  ASSERT_TRUE(base::GetFileSize(filepath, &size));
+  EXPECT_EQ(0, size);
+}
+
+TEST_F(SerializationUtilsTest, ReadLongMessageTest) {
+  base::File test_file(filepath,
+                       base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
+  std::string message(SerializationUtils::kMessageMaxLength + 1, 'c');
+
+  int32 message_size = message.length() + sizeof(int32);
+  test_file.WriteAtCurrentPos(reinterpret_cast<const char*>(&message_size),
+                              sizeof(message_size));
+  test_file.WriteAtCurrentPos(message.c_str(), message.length());
+  test_file.Close();
+
+  scoped_ptr<MetricSample> crash = MetricSample::CrashSample("test");
+  SerializationUtils::WriteMetricToFile(*crash.get(), filename);
+
+  ScopedVector<MetricSample> samples;
+  SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &samples);
+  ASSERT_EQ(size_t(1), samples.size());
+  ASSERT_TRUE(samples[0] != NULL);
+  EXPECT_TRUE(crash->IsEqual(*samples[0]));
+}
+
+TEST_F(SerializationUtilsTest, WriteReadTest) {
+  scoped_ptr<MetricSample> hist =
+      MetricSample::HistogramSample("myhist", 1, 2, 3, 4);
+  scoped_ptr<MetricSample> crash = MetricSample::CrashSample("mycrash");
+  scoped_ptr<MetricSample> lhist =
+      MetricSample::LinearHistogramSample("linear", 1, 10);
+  scoped_ptr<MetricSample> shist =
+      MetricSample::SparseHistogramSample("mysparse", 30);
+  scoped_ptr<MetricSample> action = MetricSample::UserActionSample("myaction");
+
+  SerializationUtils::WriteMetricToFile(*hist.get(), filename);
+  SerializationUtils::WriteMetricToFile(*crash.get(), filename);
+  SerializationUtils::WriteMetricToFile(*lhist.get(), filename);
+  SerializationUtils::WriteMetricToFile(*shist.get(), filename);
+  SerializationUtils::WriteMetricToFile(*action.get(), filename);
+  ScopedVector<MetricSample> vect;
+  SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &vect);
+  ASSERT_EQ(vect.size(), size_t(5));
+  for (int i = 0; i < 5; i++) {
+    ASSERT_TRUE(vect[0] != NULL);
+  }
+  EXPECT_TRUE(hist->IsEqual(*vect[0]));
+  EXPECT_TRUE(crash->IsEqual(*vect[1]));
+  EXPECT_TRUE(lhist->IsEqual(*vect[2]));
+  EXPECT_TRUE(shist->IsEqual(*vect[3]));
+  EXPECT_TRUE(action->IsEqual(*vect[4]));
+
+  int64 size = 0;
+  ASSERT_TRUE(base::GetFileSize(filepath, &size));
+  ASSERT_EQ(0, size);
+}
+
+}  // namespace
+}  // namespace metrics
diff --git a/metricsd/timer.cc b/metricsd/timer.cc
new file mode 100644 (file)
index 0000000..0c2c119
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "metrics/timer.h"
+
+#include <string>
+
+#include <base/memory/scoped_ptr.h>
+
+#include "metrics/metrics_library.h"
+
+namespace chromeos_metrics {
+
+base::TimeTicks ClockWrapper::GetCurrentTime() const {
+  return base::TimeTicks::Now();
+}
+
+Timer::Timer()
+    : timer_state_(kTimerStopped),
+      clock_wrapper_(new ClockWrapper()) {}
+
+bool Timer::Start() {
+  elapsed_time_ = base::TimeDelta();  // Sets elapsed_time_ to zero.
+  start_time_ = clock_wrapper_->GetCurrentTime();
+  timer_state_ = kTimerRunning;
+  return true;
+}
+
+bool Timer::Stop() {
+  if (timer_state_ == kTimerStopped)
+    return false;
+  if (timer_state_ == kTimerRunning)
+    elapsed_time_ += clock_wrapper_->GetCurrentTime() - start_time_;
+  timer_state_ = kTimerStopped;
+  return true;
+}
+
+bool Timer::Pause() {
+  switch (timer_state_) {
+    case kTimerStopped:
+      if (!Start())
+        return false;
+      timer_state_ = kTimerPaused;
+      return true;
+    case kTimerRunning:
+      timer_state_ = kTimerPaused;
+      elapsed_time_ += clock_wrapper_->GetCurrentTime() - start_time_;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool Timer::Resume() {
+  switch (timer_state_) {
+    case kTimerStopped:
+      return Start();
+    case kTimerPaused:
+      start_time_ = clock_wrapper_->GetCurrentTime();
+      timer_state_ = kTimerRunning;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool Timer::Reset() {
+  elapsed_time_ = base::TimeDelta();  // Sets elapsed_time_ to zero.
+  timer_state_ = kTimerStopped;
+  return true;
+}
+
+bool Timer::HasStarted() const {
+  return timer_state_ != kTimerStopped;
+}
+
+bool Timer::GetElapsedTime(base::TimeDelta* elapsed_time) const {
+  if (start_time_.is_null() || !elapsed_time)
+    return false;
+  *elapsed_time = elapsed_time_;
+  if (timer_state_ == kTimerRunning) {
+    *elapsed_time += clock_wrapper_->GetCurrentTime() - start_time_;
+  }
+  return true;
+}
+
+// static
+MetricsLibraryInterface* TimerReporter::metrics_lib_ = nullptr;
+
+TimerReporter::TimerReporter(const std::string& histogram_name, int min,
+                             int max, int num_buckets)
+    : histogram_name_(histogram_name),
+      min_(min),
+      max_(max),
+      num_buckets_(num_buckets) {}
+
+bool TimerReporter::ReportMilliseconds() const {
+  base::TimeDelta elapsed_time;
+  if (!metrics_lib_ || !GetElapsedTime(&elapsed_time)) return false;
+  return metrics_lib_->SendToUMA(histogram_name_,
+                                 elapsed_time.InMilliseconds(),
+                                 min_,
+                                 max_,
+                                 num_buckets_);
+}
+
+}  // namespace chromeos_metrics
diff --git a/metricsd/timer_test.cc b/metricsd/timer_test.cc
new file mode 100644 (file)
index 0000000..bc7a2a1
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <base/memory/scoped_ptr.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "metrics/metrics_library_mock.h"
+#include "metrics/timer.h"
+#include "metrics/timer_mock.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace chromeos_metrics {
+
+namespace {
+const int64_t kStime1MSec = 1400;
+const int64_t kEtime1MSec = 3000;
+const int64_t kDelta1MSec = 1600;
+
+const int64_t kStime2MSec = 4200;
+const int64_t kEtime2MSec = 5000;
+const int64_t kDelta2MSec = 800;
+
+const int64_t kStime3MSec = 6600;
+const int64_t kEtime3MSec = 6800;
+const int64_t kDelta3MSec = 200;
+}  // namespace
+
+class TimerTest : public testing::Test {
+ public:
+  TimerTest() : clock_wrapper_mock_(new ClockWrapperMock()) {}
+
+ protected:
+  virtual void SetUp() {
+    EXPECT_EQ(Timer::kTimerStopped, timer_.timer_state_);
+    stime += base::TimeDelta::FromMilliseconds(kStime1MSec);
+    etime += base::TimeDelta::FromMilliseconds(kEtime1MSec);
+    stime2 += base::TimeDelta::FromMilliseconds(kStime2MSec);
+    etime2 += base::TimeDelta::FromMilliseconds(kEtime2MSec);
+    stime3 += base::TimeDelta::FromMilliseconds(kStime3MSec);
+    etime3 += base::TimeDelta::FromMilliseconds(kEtime3MSec);
+  }
+
+  virtual void TearDown() {}
+
+  Timer timer_;
+  scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
+  base::TimeTicks stime, etime, stime2, etime2, stime3, etime3;
+};
+
+TEST_F(TimerTest, StartStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_FALSE(timer_.HasStarted());
+}
+
+TEST_F(TimerTest, ReStart) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  timer_.Start();
+  base::TimeTicks buffer = timer_.start_time_;
+  timer_.Start();
+  ASSERT_FALSE(timer_.start_time_ == buffer);
+}
+
+TEST_F(TimerTest, Reset) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  timer_.Start();
+  ASSERT_TRUE(timer_.Reset());
+  ASSERT_FALSE(timer_.HasStarted());
+}
+
+TEST_F(TimerTest, SeparatedTimers) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime2);
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, InvalidStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_FALSE(timer_.Stop());
+  // Now we try it again, but after a valid start/stop.
+  timer_.Start();
+  timer_.Stop();
+  base::TimeDelta elapsed_time = timer_.elapsed_time_;
+  ASSERT_FALSE(timer_.Stop());
+  ASSERT_TRUE(elapsed_time == timer_.elapsed_time_);
+}
+
+TEST_F(TimerTest, InvalidElapsedTime) {
+  base::TimeDelta elapsed_time;
+  ASSERT_FALSE(timer_.GetElapsedTime(&elapsed_time));
+}
+
+TEST_F(TimerTest, PauseStartStopResume) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2))
+      .WillOnce(Return(stime3))
+      .WillOnce(Return(etime3));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Pause());  // Starts timer paused.
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Start());  // Restarts timer.
+  ASSERT_TRUE(timer_.start_time_ == stime2);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(kDelta3MSec, elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, ResumeStartStopPause) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2))
+      .WillOnce(Return(stime3));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime2);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(0, elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, StartResumeStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_FALSE(timer_.Resume());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, StartPauseStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, StartPauseResumeStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec + kDelta2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, PauseStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 0);
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 0);
+  ASSERT_FALSE(timer_.HasStarted());
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, PauseResumeStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, StartPauseResumePauseStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(stime3))
+      .WillOnce(Return(etime3));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+  // Make sure GetElapsedTime works while we're running.
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(kDelta1MSec + kStime3MSec - kStime2MSec,
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            kDelta1MSec + kEtime3MSec - kStime2MSec);
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            kDelta1MSec + kEtime3MSec - kStime2MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+TEST_F(TimerTest, StartPauseResumePauseResumeStop) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime))
+      .WillOnce(Return(stime2))
+      .WillOnce(Return(etime2))
+      .WillOnce(Return(stime3))
+      .WillOnce(Return(etime3));
+  timer_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  ASSERT_TRUE(timer_.Start());
+  ASSERT_TRUE(timer_.start_time_ == stime);
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec);
+  base::TimeDelta elapsed_time;
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Pause());
+  ASSERT_TRUE(timer_.HasStarted());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec + kDelta2MSec);
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+
+  ASSERT_TRUE(timer_.Resume());
+  ASSERT_TRUE(timer_.HasStarted());
+
+  ASSERT_TRUE(timer_.Stop());
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            kDelta1MSec + kDelta2MSec + kDelta3MSec);
+  ASSERT_FALSE(timer_.HasStarted());
+  ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time));
+  ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(),
+            elapsed_time.InMilliseconds());
+}
+
+static const char kMetricName[] = "test-timer";
+static const int kMinSample = 0;
+static const int kMaxSample = 120 * 1E6;
+static const int kNumBuckets = 50;
+
+class TimerReporterTest : public testing::Test {
+ public:
+  TimerReporterTest() : timer_reporter_(kMetricName, kMinSample, kMaxSample,
+                                        kNumBuckets),
+                        clock_wrapper_mock_(new ClockWrapperMock()) {}
+
+ protected:
+  virtual void SetUp() {
+    timer_reporter_.set_metrics_lib(&lib_);
+    EXPECT_EQ(timer_reporter_.histogram_name_, kMetricName);
+    EXPECT_EQ(timer_reporter_.min_, kMinSample);
+    EXPECT_EQ(timer_reporter_.max_, kMaxSample);
+    EXPECT_EQ(timer_reporter_.num_buckets_, kNumBuckets);
+    stime += base::TimeDelta::FromMilliseconds(kStime1MSec);
+    etime += base::TimeDelta::FromMilliseconds(kEtime1MSec);
+  }
+
+  virtual void TearDown() {
+    timer_reporter_.set_metrics_lib(nullptr);
+  }
+
+  TimerReporter timer_reporter_;
+  MetricsLibraryMock lib_;
+  scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
+  base::TimeTicks stime, etime;
+};
+
+TEST_F(TimerReporterTest, StartStopReport) {
+  EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime())
+      .WillOnce(Return(stime))
+      .WillOnce(Return(etime));
+  timer_reporter_.clock_wrapper_.reset(clock_wrapper_mock_.release());
+  EXPECT_CALL(lib_, SendToUMA(kMetricName, kDelta1MSec, kMinSample, kMaxSample,
+                              kNumBuckets)).WillOnce(Return(true));
+  ASSERT_TRUE(timer_reporter_.Start());
+  ASSERT_TRUE(timer_reporter_.Stop());
+  ASSERT_TRUE(timer_reporter_.ReportMilliseconds());
+}
+
+TEST_F(TimerReporterTest, InvalidReport) {
+  ASSERT_FALSE(timer_reporter_.ReportMilliseconds());
+}
+
+}  // namespace chromeos_metrics
+
+int main(int argc, char **argv) {
+  testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/metricsd/uploader/metrics_hashes.cc b/metricsd/uploader/metrics_hashes.cc
new file mode 100644 (file)
index 0000000..208c560
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/metrics_hashes.h"
+
+#include "base/logging.h"
+#include "base/md5.h"
+#include "base/sys_byteorder.h"
+
+namespace metrics {
+
+namespace {
+
+// Converts the 8-byte prefix of an MD5 hash into a uint64 value.
+inline uint64_t HashToUInt64(const std::string& hash) {
+  uint64_t value;
+  DCHECK_GE(hash.size(), sizeof(value));
+  memcpy(&value, hash.data(), sizeof(value));
+  return base::HostToNet64(value);
+}
+
+}  // namespace
+
+uint64_t HashMetricName(const std::string& name) {
+  // Create an MD5 hash of the given |name|, represented as a byte buffer
+  // encoded as an std::string.
+  base::MD5Context context;
+  base::MD5Init(&context);
+  base::MD5Update(&context, name);
+
+  base::MD5Digest digest;
+  base::MD5Final(&digest, &context);
+
+  std::string hash_str(reinterpret_cast<char*>(digest.a), arraysize(digest.a));
+  return HashToUInt64(hash_str);
+}
+
+}  // namespace metrics
diff --git a/metricsd/uploader/metrics_hashes.h b/metricsd/uploader/metrics_hashes.h
new file mode 100644 (file)
index 0000000..1082b42
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_UPLOADER_METRICS_HASHES_H_
+#define METRICS_UPLOADER_METRICS_HASHES_H_
+
+#include <string>
+
+namespace metrics {
+
+// Computes a uint64 hash of a given string based on its MD5 hash. Suitable for
+// metric names.
+uint64_t HashMetricName(const std::string& name);
+
+}  // namespace metrics
+
+#endif  // METRICS_UPLOADER_METRICS_HASHES_H_
diff --git a/metricsd/uploader/metrics_hashes_unittest.cc b/metricsd/uploader/metrics_hashes_unittest.cc
new file mode 100644 (file)
index 0000000..b8c2575
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/metrics_hashes.h"
+
+#include <base/format_macros.h>
+#include <base/macros.h>
+#include <base/strings/stringprintf.h>
+#include <gtest/gtest.h>
+
+namespace metrics {
+
+// Make sure our ID hashes are the same as what we see on the server side.
+TEST(MetricsUtilTest, HashMetricName) {
+  static const struct {
+    std::string input;
+    std::string output;
+  } cases[] = {
+    {"Back", "0x0557fa923dcee4d0"},
+    {"Forward", "0x67d2f6740a8eaebf"},
+    {"NewTab", "0x290eb683f96572f1"},
+  };
+
+  for (size_t i = 0; i < arraysize(cases); ++i) {
+    uint64_t hash = HashMetricName(cases[i].input);
+    std::string hash_hex = base::StringPrintf("0x%016" PRIx64, hash);
+    EXPECT_EQ(cases[i].output, hash_hex);
+  }
+}
+
+}  // namespace metrics
diff --git a/metricsd/uploader/metrics_log.cc b/metricsd/uploader/metrics_log.cc
new file mode 100644 (file)
index 0000000..1f16ca1
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/metrics_log.h"
+
+#include <string>
+
+#include "uploader/proto/system_profile.pb.h"
+#include "uploader/system_profile_setter.h"
+
+// We use default values for the MetricsLogBase constructor as the setter will
+// override them.
+MetricsLog::MetricsLog()
+    : MetricsLogBase("", 0, metrics::MetricsLogBase::ONGOING_LOG, "") {
+}
+
+void MetricsLog::IncrementUserCrashCount() {
+  metrics::SystemProfileProto::Stability* stability(
+      uma_proto()->mutable_system_profile()->mutable_stability());
+  int current = stability->other_user_crash_count();
+  stability->set_other_user_crash_count(current + 1);
+}
+
+void MetricsLog::IncrementKernelCrashCount() {
+  metrics::SystemProfileProto::Stability* stability(
+      uma_proto()->mutable_system_profile()->mutable_stability());
+  int current = stability->kernel_crash_count();
+  stability->set_kernel_crash_count(current + 1);
+}
+
+void MetricsLog::IncrementUncleanShutdownCount() {
+  metrics::SystemProfileProto::Stability* stability(
+      uma_proto()->mutable_system_profile()->mutable_stability());
+  int current = stability->unclean_system_shutdown_count();
+  stability->set_unclean_system_shutdown_count(current + 1);
+}
+
+bool MetricsLog::PopulateSystemProfile(SystemProfileSetter* profile_setter) {
+  return profile_setter->Populate(uma_proto());
+}
diff --git a/metricsd/uploader/metrics_log.h b/metricsd/uploader/metrics_log.h
new file mode 100644 (file)
index 0000000..5e09070
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_UPLOADER_METRICS_LOG_H_
+#define METRICS_UPLOADER_METRICS_LOG_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "uploader/metrics_log_base.h"
+
+// This file defines a set of user experience metrics data recorded by
+// the MetricsService. This is the unit of data that is sent to the server.
+class SystemProfileSetter;
+
+// This class provides base functionality for logging metrics data.
+class MetricsLog : public metrics::MetricsLogBase {
+ public:
+  // The constructor doesn't set any metadata. The metadata is only set by a
+  // SystemProfileSetter.
+  MetricsLog();
+
+  void IncrementUserCrashCount();
+  void IncrementKernelCrashCount();
+  void IncrementUncleanShutdownCount();
+
+  // Populate the system profile with system information using setter.
+  bool PopulateSystemProfile(SystemProfileSetter* setter);
+
+ private:
+  FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
+  FRIEND_TEST(UploadServiceTest, LogKernelCrash);
+  FRIEND_TEST(UploadServiceTest, LogUncleanShutdown);
+  FRIEND_TEST(UploadServiceTest, LogUserCrash);
+  FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored);
+
+  DISALLOW_COPY_AND_ASSIGN(MetricsLog);
+};
+
+#endif  // METRICS_UPLOADER_METRICS_LOG_H_
diff --git a/metricsd/uploader/metrics_log_base.cc b/metricsd/uploader/metrics_log_base.cc
new file mode 100644 (file)
index 0000000..1a60b4f
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/metrics_log_base.h"
+
+#include "base/build_time.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_samples.h"
+#include "uploader/metrics_hashes.h"
+#include "uploader/proto/histogram_event.pb.h"
+#include "uploader/proto/system_profile.pb.h"
+#include "uploader/proto/user_action_event.pb.h"
+
+using base::Histogram;
+using base::HistogramBase;
+using base::HistogramSamples;
+using base::SampleCountIterator;
+using base::Time;
+using base::TimeDelta;
+using metrics::HistogramEventProto;
+using metrics::SystemProfileProto;
+using metrics::UserActionEventProto;
+
+namespace metrics {
+namespace {
+
+// Any id less than 16 bytes is considered to be a testing id.
+bool IsTestingID(const std::string& id) {
+  return id.size() < 16;
+}
+
+}  // namespace
+
+MetricsLogBase::MetricsLogBase(const std::string& client_id,
+                               int session_id,
+                               LogType log_type,
+                               const std::string& version_string)
+    : num_events_(0),
+      locked_(false),
+      log_type_(log_type) {
+  DCHECK_NE(NO_LOG, log_type);
+  if (IsTestingID(client_id))
+    uma_proto_.set_client_id(0);
+  else
+    uma_proto_.set_client_id(Hash(client_id));
+
+  uma_proto_.set_session_id(session_id);
+  uma_proto_.mutable_system_profile()->set_build_timestamp(GetBuildTime());
+  uma_proto_.mutable_system_profile()->set_app_version(version_string);
+}
+
+MetricsLogBase::~MetricsLogBase() {}
+
+// static
+uint64_t MetricsLogBase::Hash(const std::string& value) {
+  uint64_t hash = metrics::HashMetricName(value);
+
+  // The following log is VERY helpful when folks add some named histogram into
+  // the code, but forgot to update the descriptive list of histograms.  When
+  // that happens, all we get to see (server side) is a hash of the histogram
+  // name.  We can then use this logging to find out what histogram name was
+  // being hashed to a given MD5 value by just running the version of Chromium
+  // in question with --enable-logging.
+  VLOG(1) << "Metrics: Hash numeric [" << value << "]=[" << hash << "]";
+
+  return hash;
+}
+
+// static
+int64_t MetricsLogBase::GetBuildTime() {
+  static int64_t integral_build_time = 0;
+  if (!integral_build_time) {
+    Time time = base::GetBuildTime();
+    integral_build_time = static_cast<int64_t>(time.ToTimeT());
+  }
+  return integral_build_time;
+}
+
+// static
+int64_t MetricsLogBase::GetCurrentTime() {
+  return (base::TimeTicks::Now() - base::TimeTicks()).InSeconds();
+}
+
+void MetricsLogBase::CloseLog() {
+  DCHECK(!locked_);
+  locked_ = true;
+}
+
+void MetricsLogBase::GetEncodedLog(std::string* encoded_log) {
+  DCHECK(locked_);
+  uma_proto_.SerializeToString(encoded_log);
+}
+
+void MetricsLogBase::RecordUserAction(const std::string& key) {
+  DCHECK(!locked_);
+
+  UserActionEventProto* user_action = uma_proto_.add_user_action_event();
+  user_action->set_name_hash(Hash(key));
+  user_action->set_time(GetCurrentTime());
+
+  ++num_events_;
+}
+
+void MetricsLogBase::RecordHistogramDelta(const std::string& histogram_name,
+                                          const HistogramSamples& snapshot) {
+  DCHECK(!locked_);
+  DCHECK_NE(0, snapshot.TotalCount());
+
+  // We will ignore the MAX_INT/infinite value in the last element of range[].
+
+  HistogramEventProto* histogram_proto = uma_proto_.add_histogram_event();
+  histogram_proto->set_name_hash(Hash(histogram_name));
+  histogram_proto->set_sum(snapshot.sum());
+
+  for (scoped_ptr<SampleCountIterator> it = snapshot.Iterator(); !it->Done();
+       it->Next()) {
+    HistogramBase::Sample min;
+    HistogramBase::Sample max;
+    HistogramBase::Count count;
+    it->Get(&min, &max, &count);
+    HistogramEventProto::Bucket* bucket = histogram_proto->add_bucket();
+    bucket->set_min(min);
+    bucket->set_max(max);
+    bucket->set_count(count);
+  }
+
+  // Omit fields to save space (see rules in histogram_event.proto comments).
+  for (int i = 0; i < histogram_proto->bucket_size(); ++i) {
+    HistogramEventProto::Bucket* bucket = histogram_proto->mutable_bucket(i);
+    if (i + 1 < histogram_proto->bucket_size() &&
+        bucket->max() == histogram_proto->bucket(i + 1).min()) {
+      bucket->clear_max();
+    } else if (bucket->max() == bucket->min() + 1) {
+      bucket->clear_min();
+    }
+  }
+}
+
+}  // namespace metrics
diff --git a/metricsd/uploader/metrics_log_base.h b/metricsd/uploader/metrics_log_base.h
new file mode 100644 (file)
index 0000000..f4e1995
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This file defines a set of user experience metrics data recorded by
+// the MetricsService.  This is the unit of data that is sent to the server.
+
+#ifndef METRICS_UPLOADER_METRICS_LOG_BASE_H_
+#define METRICS_UPLOADER_METRICS_LOG_BASE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+
+namespace base {
+class HistogramSamples;
+}  // namespace base
+
+namespace metrics {
+
+// This class provides base functionality for logging metrics data.
+class MetricsLogBase {
+ public:
+  // TODO(asvitkine): Remove the NO_LOG value.
+  enum LogType {
+    INITIAL_STABILITY_LOG,  // The initial log containing stability stats.
+    ONGOING_LOG,            // Subsequent logs in a session.
+    NO_LOG,                 // Placeholder value for when there is no log.
+  };
+
+  // Creates a new metrics log of the specified type.
+  // client_id is the identifier for this profile on this installation
+  // session_id is an integer that's incremented on each application launch
+  MetricsLogBase(const std::string& client_id,
+                 int session_id,
+                 LogType log_type,
+                 const std::string& version_string);
+  virtual ~MetricsLogBase();
+
+  // Computes the MD5 hash of the given string, and returns the first 8 bytes of
+  // the hash.
+  static uint64_t Hash(const std::string& value);
+
+  // Get the GMT buildtime for the current binary, expressed in seconds since
+  // January 1, 1970 GMT.
+  // The value is used to identify when a new build is run, so that previous
+  // reliability stats, from other builds, can be abandoned.
+  static int64_t GetBuildTime();
+
+  // Convenience function to return the current time at a resolution in seconds.
+  // This wraps base::TimeTicks, and hence provides an abstract time that is
+  // always incrementing for use in measuring time durations.
+  static int64_t GetCurrentTime();
+
+  // Records a user-initiated action.
+  void RecordUserAction(const std::string& key);
+
+  // Record any changes in a given histogram for transmission.
+  void RecordHistogramDelta(const std::string& histogram_name,
+                            const base::HistogramSamples& snapshot);
+
+  // Stop writing to this record and generate the encoded representation.
+  // None of the Record* methods can be called after this is called.
+  void CloseLog();
+
+  // Fills |encoded_log| with the serialized protobuf representation of the
+  // record.  Must only be called after CloseLog() has been called.
+  void GetEncodedLog(std::string* encoded_log);
+
+  int num_events() { return num_events_; }
+
+  void set_hardware_class(const std::string& hardware_class) {
+    uma_proto_.mutable_system_profile()->mutable_hardware()->set_hardware_class(
+        hardware_class);
+  }
+
+  LogType log_type() const { return log_type_; }
+
+ protected:
+  bool locked() const { return locked_; }
+
+  metrics::ChromeUserMetricsExtension* uma_proto() { return &uma_proto_; }
+  const metrics::ChromeUserMetricsExtension* uma_proto() const {
+    return &uma_proto_;
+  }
+
+  // TODO(isherman): Remove this once the XML pipeline is outta here.
+  int num_events_;  // the number of events recorded in this log
+
+ private:
+  // locked_ is true when record has been packed up for sending, and should
+  // no longer be written to.  It is only used for sanity checking and is
+  // not a real lock.
+  bool locked_;
+
+  // The type of the log, i.e. initial or ongoing.
+  const LogType log_type_;
+
+  // Stores the protocol buffer representation for this log.
+  metrics::ChromeUserMetricsExtension uma_proto_;
+
+  DISALLOW_COPY_AND_ASSIGN(MetricsLogBase);
+};
+
+}  // namespace metrics
+
+#endif  // METRICS_UPLOADER_METRICS_LOG_BASE_H_
diff --git a/metricsd/uploader/metrics_log_base_unittest.cc b/metricsd/uploader/metrics_log_base_unittest.cc
new file mode 100644 (file)
index 0000000..980afd5
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/metrics_log_base.h"
+
+#include <string>
+
+#include <base/metrics/bucket_ranges.h>
+#include <base/metrics/sample_vector.h>
+#include <gtest/gtest.h>
+
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+
+namespace metrics {
+
+namespace {
+
+class TestMetricsLogBase : public MetricsLogBase {
+ public:
+  TestMetricsLogBase()
+      : MetricsLogBase("client_id", 1, MetricsLogBase::ONGOING_LOG, "1.2.3.4") {
+  }
+  virtual ~TestMetricsLogBase() {}
+
+  using MetricsLogBase::uma_proto;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestMetricsLogBase);
+};
+
+}  // namespace
+
+TEST(MetricsLogBaseTest, LogType) {
+  MetricsLogBase log1("id", 0, MetricsLogBase::ONGOING_LOG, "1.2.3");
+  EXPECT_EQ(MetricsLogBase::ONGOING_LOG, log1.log_type());
+
+  MetricsLogBase log2("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "1.2.3");
+  EXPECT_EQ(MetricsLogBase::INITIAL_STABILITY_LOG, log2.log_type());
+}
+
+TEST(MetricsLogBaseTest, EmptyRecord) {
+  MetricsLogBase log("totally bogus client ID", 137,
+                     MetricsLogBase::ONGOING_LOG, "bogus version");
+  log.set_hardware_class("sample-class");
+  log.CloseLog();
+
+  std::string encoded;
+  log.GetEncodedLog(&encoded);
+
+  // A couple of fields are hard to mock, so these will be copied over directly
+  // for the expected output.
+  metrics::ChromeUserMetricsExtension parsed;
+  ASSERT_TRUE(parsed.ParseFromString(encoded));
+
+  metrics::ChromeUserMetricsExtension expected;
+  expected.set_client_id(5217101509553811875);  // Hashed bogus client ID
+  expected.set_session_id(137);
+  expected.mutable_system_profile()->set_build_timestamp(
+      parsed.system_profile().build_timestamp());
+  expected.mutable_system_profile()->set_app_version("bogus version");
+  expected.mutable_system_profile()->mutable_hardware()->set_hardware_class(
+      "sample-class");
+
+  EXPECT_EQ(expected.SerializeAsString(), encoded);
+}
+
+TEST(MetricsLogBaseTest, HistogramBucketFields) {
+  // Create buckets: 1-5, 5-7, 7-8, 8-9, 9-10, 10-11, 11-12.
+  base::BucketRanges ranges(8);
+  ranges.set_range(0, 1);
+  ranges.set_range(1, 5);
+  ranges.set_range(2, 7);
+  ranges.set_range(3, 8);
+  ranges.set_range(4, 9);
+  ranges.set_range(5, 10);
+  ranges.set_range(6, 11);
+  ranges.set_range(7, 12);
+
+  base::SampleVector samples(&ranges);
+  samples.Accumulate(3, 1);   // Bucket 1-5.
+  samples.Accumulate(6, 1);   // Bucket 5-7.
+  samples.Accumulate(8, 1);   // Bucket 8-9. (7-8 skipped)
+  samples.Accumulate(10, 1);  // Bucket 10-11. (9-10 skipped)
+  samples.Accumulate(11, 1);  // Bucket 11-12.
+
+  TestMetricsLogBase log;
+  log.RecordHistogramDelta("Test", samples);
+
+  const metrics::ChromeUserMetricsExtension* uma_proto = log.uma_proto();
+  const metrics::HistogramEventProto& histogram_proto =
+      uma_proto->histogram_event(uma_proto->histogram_event_size() - 1);
+
+  // Buckets with samples: 1-5, 5-7, 8-9, 10-11, 11-12.
+  // Should become: 1-/, 5-7, /-9, 10-/, /-12.
+  ASSERT_EQ(5, histogram_proto.bucket_size());
+
+  // 1-5 becomes 1-/ (max is same as next min).
+  EXPECT_TRUE(histogram_proto.bucket(0).has_min());
+  EXPECT_FALSE(histogram_proto.bucket(0).has_max());
+  EXPECT_EQ(1, histogram_proto.bucket(0).min());
+
+  // 5-7 stays 5-7 (no optimization possible).
+  EXPECT_TRUE(histogram_proto.bucket(1).has_min());
+  EXPECT_TRUE(histogram_proto.bucket(1).has_max());
+  EXPECT_EQ(5, histogram_proto.bucket(1).min());
+  EXPECT_EQ(7, histogram_proto.bucket(1).max());
+
+  // 8-9 becomes /-9 (min is same as max - 1).
+  EXPECT_FALSE(histogram_proto.bucket(2).has_min());
+  EXPECT_TRUE(histogram_proto.bucket(2).has_max());
+  EXPECT_EQ(9, histogram_proto.bucket(2).max());
+
+  // 10-11 becomes 10-/ (both optimizations apply, omit max is prioritized).
+  EXPECT_TRUE(histogram_proto.bucket(3).has_min());
+  EXPECT_FALSE(histogram_proto.bucket(3).has_max());
+  EXPECT_EQ(10, histogram_proto.bucket(3).min());
+
+  // 11-12 becomes /-12 (last record must keep max, min is same as max - 1).
+  EXPECT_FALSE(histogram_proto.bucket(4).has_min());
+  EXPECT_TRUE(histogram_proto.bucket(4).has_max());
+  EXPECT_EQ(12, histogram_proto.bucket(4).max());
+}
+
+}  // namespace metrics
diff --git a/metricsd/uploader/mock/mock_system_profile_setter.h b/metricsd/uploader/mock/mock_system_profile_setter.h
new file mode 100644 (file)
index 0000000..9b20291
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
+#define METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
+
+#include "uploader/system_profile_setter.h"
+
+namespace metrics {
+class ChromeUserMetricsExtension;
+}
+
+// Mock profile setter used for testing.
+class MockSystemProfileSetter : public SystemProfileSetter {
+ public:
+  bool Populate(metrics::ChromeUserMetricsExtension* profile_proto) override {
+    return true;
+  }
+};
+
+#endif  // METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
diff --git a/metricsd/uploader/mock/sender_mock.cc b/metricsd/uploader/mock/sender_mock.cc
new file mode 100644 (file)
index 0000000..bb4dc7d
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/mock/sender_mock.h"
+
+SenderMock::SenderMock() {
+  Reset();
+}
+
+bool SenderMock::Send(const std::string& content, const std::string& hash) {
+  send_call_count_ += 1;
+  last_message_ = content;
+  is_good_proto_ = last_message_proto_.ParseFromString(content);
+  return should_succeed_;
+}
+
+void SenderMock::Reset() {
+  send_call_count_ = 0;
+  last_message_ = "";
+  should_succeed_ = true;
+  last_message_proto_.Clear();
+  is_good_proto_ = false;
+}
diff --git a/metricsd/uploader/mock/sender_mock.h b/metricsd/uploader/mock/sender_mock.h
new file mode 100644 (file)
index 0000000..e79233f
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_UPLOADER_MOCK_SENDER_MOCK_H_
+#define METRICS_UPLOADER_MOCK_SENDER_MOCK_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+#include "uploader/sender.h"
+
+class SenderMock : public Sender {
+ public:
+  SenderMock();
+
+  bool Send(const std::string& content, const std::string& hash) override;
+  void Reset();
+
+  bool is_good_proto() { return is_good_proto_; }
+  int send_call_count() { return send_call_count_; }
+  const std::string last_message() { return last_message_; }
+  metrics::ChromeUserMetricsExtension last_message_proto() {
+    return last_message_proto_;
+  }
+  void set_should_succeed(bool succeed) { should_succeed_ = succeed; }
+
+ private:
+  // Is set to true if the proto was parsed successfully.
+  bool is_good_proto_;
+
+  // If set to true, the Send method will return true to simulate a successful
+  // send.
+  bool should_succeed_;
+
+  // Count of how many times Send was called since the last reset.
+  int send_call_count_;
+
+  // Last message received by Send.
+  std::string last_message_;
+
+  // If is_good_proto is true, last_message_proto is the deserialized
+  // representation of last_message.
+  metrics::ChromeUserMetricsExtension last_message_proto_;
+};
+
+#endif  // METRICS_UPLOADER_MOCK_SENDER_MOCK_H_
diff --git a/metricsd/uploader/proto/README b/metricsd/uploader/proto/README
new file mode 100644 (file)
index 0000000..4292a40
--- /dev/null
@@ -0,0 +1,37 @@
+Copyright (C) 2015 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+
+
+This directory contains the protocol buffers used by the standalone metrics
+uploader. Those protobuffers are copied from the chromium protobuffers from
+https://chromium.googlesource.com/chromium/src/+/master/components/metrics/proto/
+at 3bfe5f2b4c03d2cac718d137ed14cd2c6354bfed.
+
+Any change to this protobuf must first be made to the backend's protobuf and be
+compatible with the chromium protobuffers.
+
+
+Q: Why fork the chromium protobuffers ?
+A: The standalone metrics uploader needs chromium os fields that are not defined
+by the chromium protobufs. Instead of pushing chromium os specific changes to
+chromium, we can add them only to chromium os (and to the backend of course).
+
+
+Q: What's the difference between those protobuffers and chromium's protobuffers?
+A: When the protobuffers were copied, some chromium specific protobuffers were
+not imported:
+* omnibox related protobuffers.
+* performance profiling protobuffers (not used in chromium os).
diff --git a/metricsd/uploader/proto/chrome_user_metrics_extension.proto b/metricsd/uploader/proto/chrome_user_metrics_extension.proto
new file mode 100644 (file)
index 0000000..a07830f
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//
+// Protocol buffer for Chrome UMA (User Metrics Analysis).
+//
+// Note: this protobuf must be compatible with the one in chromium.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_outer_classname = "ChromeUserMetricsExtensionProtos";
+option java_package = "org.chromium.components.metrics";
+
+package metrics;
+
+import "system/core/metricsd/uploader/proto/histogram_event.proto";
+import "system/core/metricsd/uploader/proto/system_profile.proto";
+import "system/core/metricsd/uploader/proto/user_action_event.proto";
+
+// Next tag: 13
+message ChromeUserMetricsExtension {
+  // The product (i.e. end user application) for a given UMA log.
+  enum Product {
+    // Google Chrome product family.
+    CHROME = 0;
+  }
+  // The product corresponding to this log. The field type is int32 instead of
+  // Product so that downstream users of the Chromium metrics component can
+  // introduce products without needing to make changes to the Chromium code
+  // (though they still need to add the new product to the server-side enum).
+  // Note: The default value is Chrome, so Chrome products will not transmit
+  // this field.
+  optional int32 product = 10 [default = 0];
+
+  // The id of the client install that generated these events.
+  //
+  // For Chrome clients, this id is unique to a top-level (one level above the
+  // "Default" directory) Chrome user data directory [1], and so is shared among
+  // all Chrome user profiles contained in this user data directory.
+  // An id of 0 is reserved for test data (monitoring and internal testing) and
+  // should normally be ignored in analysis of the data.
+  // [1] http://www.chromium.org/user-experience/user-data-directory
+  optional fixed64 client_id = 1;
+
+  // The session id for this user.
+  // Values such as tab ids are only meaningful within a particular session.
+  // The client keeps track of the session id and sends it with each event.
+  // The session id is simply an integer that is incremented each time the user
+  // relaunches Chrome.
+  optional int32 session_id = 2;
+
+  // Information about the user's browser and system configuration.
+  optional SystemProfileProto system_profile = 3;
+
+  // This message will log one or more of the following event types:
+  repeated UserActionEventProto user_action_event = 4;
+  repeated HistogramEventProto histogram_event = 6;
+
+}
diff --git a/metricsd/uploader/proto/histogram_event.proto b/metricsd/uploader/proto/histogram_event.proto
new file mode 100644 (file)
index 0000000..3825063
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//
+// Histogram-collected metrics.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_outer_classname = "HistogramEventProtos";
+option java_package = "org.chromium.components.metrics";
+
+package metrics;
+
+// Next tag: 4
+message HistogramEventProto {
+  // The name of the histogram, hashed.
+  optional fixed64 name_hash = 1;
+
+  // The sum of all the sample values.
+  // Together with the total count of the sample values, this allows us to
+  // compute the average value.  The count of all sample values is just the sum
+  // of the counts of all the buckets.
+  optional int64 sum = 2;
+
+  // The per-bucket data.
+  message Bucket {
+    // Each bucket's range is bounded by min <= x < max.
+    // It is valid to omit one of these two fields in a bucket, but not both.
+    // If the min field is omitted, its value is assumed to be equal to max - 1.
+    // If the max field is omitted, its value is assumed to be equal to the next
+    // bucket's min value (possibly computed per above).  The last bucket in a
+    // histogram should always include the max field.
+    optional int64 min = 1;
+    optional int64 max = 2;
+
+    // The bucket's index in the list of buckets, sorted in ascending order.
+    // This field was intended to provide extra redundancy to detect corrupted
+    // records, but was never used.  As of M31, it is no longer sent by Chrome
+    // clients to reduce the UMA upload size.
+    optional int32 bucket_index = 3 [deprecated = true];
+
+    // The number of entries in this bucket.
+    optional int64 count = 4;
+  }
+  repeated Bucket bucket = 3;
+}
diff --git a/metricsd/uploader/proto/system_profile.proto b/metricsd/uploader/proto/system_profile.proto
new file mode 100644 (file)
index 0000000..bac828b
--- /dev/null
@@ -0,0 +1,759 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//
+// Stores information about the user's brower and system configuration.
+// The system configuration fields are recorded once per client session.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_outer_classname = "SystemProfileProtos";
+option java_package = "org.chromium.components.metrics";
+
+package metrics;
+
+// Next tag: 21
+message SystemProfileProto {
+  // The time when the client was compiled/linked, in seconds since the epoch.
+  optional int64 build_timestamp = 1;
+
+  // A version number string for the application.
+  // Most commonly this is the browser version number found in a user agent
+  // string, and is typically a 4-tuple of numbers separated by periods.  In
+  // cases where the user agent version might be ambiguous (example: Linux 64-
+  // bit build, rather than 32-bit build, or a Windows version used in some
+  // special context, such as ChromeFrame running in IE), then this may include
+  // some additional postfix to provide clarification not available in the UA
+  // string.
+  //
+  // An example of a browser version 4-tuple is "5.0.322.0".  Currently used
+  // postfixes are:
+  //
+  //   "-64": a 64-bit build
+  //   "-F": Chrome is running under control of ChromeFrame
+  //   "-devel": this is not an official build of Chrome
+  //
+  // A full version number string could look similar to:
+  // "5.0.322.0-F-devel".
+  //
+  // This value, when available, is more trustworthy than the UA string
+  // associated with the request; and including the postfix, may be more
+  // specific.
+  optional string app_version = 2;
+
+  // The brand code or distribution tag assigned to a partner, if available.
+  // Brand codes are only available on Windows.  Not every Windows install
+  // though will have a brand code.
+  optional string brand_code = 12;
+
+  // The possible channels for an installation, from least to most stable.
+  enum Channel {
+    CHANNEL_UNKNOWN = 0;  // Unknown channel -- perhaps an unofficial build?
+    CHANNEL_CANARY = 1;
+    CHANNEL_DEV = 2;
+    CHANNEL_BETA = 3;
+    CHANNEL_STABLE = 4;
+  }
+  optional Channel channel = 10;
+
+  // True if Chrome build is ASan-instrumented.
+  optional bool is_asan_build = 20 [default = false];
+
+  // The date the user enabled UMA, in seconds since the epoch.
+  // If the user has toggled the UMA enabled state multiple times, this will
+  // be the most recent date on which UMA was enabled.
+  // For privacy, this is rounded to the nearest hour.
+  optional int64 uma_enabled_date = 3;
+
+  // The time when the client was installed, in seconds since the epoch.
+  // For privacy, this is rounded to the nearest hour.
+  optional int64 install_date = 16;
+
+  // The user's selected application locale, i.e. the user interface language.
+  // The locale includes a language code and, possibly, also a country code,
+  // e.g. "en-US".
+  optional string application_locale = 4;
+
+  message BrilloDeviceData {
+    optional string product_id = 1;
+  }
+  optional BrilloDeviceData brillo = 21;
+
+  // Information on the user's operating system.
+  message OS {
+    // The user's operating system. This should be one of:
+    // - Android
+    // - Windows NT
+    // - Linux (includes ChromeOS)
+    // - iPhone OS
+    // - Mac OS X
+    optional string name = 1;
+
+    // The version of the OS.  The meaning of this field is OS-dependent.
+    optional string version = 2;
+
+    // The fingerprint of the build.  This field is used only on Android.
+    optional string fingerprint = 3;
+
+    // Whether the version of iOS appears to be "jailbroken". This field is
+    // used only on iOS. Chrome for iOS detects whether device contains a
+    // DynamicLibraries/ directory. It's a necessary but insufficient indicator
+    // of whether the operating system has been jailbroken.
+    optional bool is_jailbroken = 4;
+  }
+  optional OS os = 5;
+
+  // Next tag for Hardware: 18
+  // Information on the user's hardware.
+  message Hardware {
+    // The CPU architecture (x86, PowerPC, x86_64, ...)
+    optional string cpu_architecture = 1;
+
+    // The amount of RAM present on the system, in megabytes.
+    optional int64 system_ram_mb = 2;
+
+    // The base memory address that chrome.dll was loaded at.
+    // (Logged only on Windows.)
+    optional int64 dll_base = 3;
+
+    // The Chrome OS device hardware class ID is a unique string associated with
+    // each Chrome OS device product revision generally assigned at hardware
+    // qualification time.  The hardware class effectively identifies the
+    // configured system components such as CPU, WiFi adapter, etc.
+    //
+    // An example of such a hardware class is "IEC MARIO PONY 6101".  An
+    // internal database associates this hardware class with the qualified
+    // device specifications including OEM information, schematics, hardware
+    // qualification reports, test device tags, etc.
+    optional string hardware_class = 4;
+
+    // The number of physical screens.
+    optional int32 screen_count = 5;
+
+    // The screen dimensions of the primary screen, in pixels.
+    optional int32 primary_screen_width = 6;
+    optional int32 primary_screen_height = 7;
+
+    // The device scale factor of the primary screen.
+    optional float primary_screen_scale_factor = 12;
+
+    // Max DPI for any attached screen. (Windows only)
+    optional float max_dpi_x = 9;
+    optional float max_dpi_y = 10;
+
+    // Information on the CPU obtained by CPUID.
+    message CPU {
+      // A 12 character string naming the vendor, e.g. "GeniuneIntel".
+      optional string vendor_name = 1;
+
+      // The signature reported by CPUID (from EAX).
+      optional uint32 signature = 2;
+
+      // Number of logical processors/cores on the current machine.
+      optional uint32 num_cores = 3;
+    }
+    optional CPU cpu = 13;
+
+    // Information on the GPU
+    message Graphics {
+      // The GPU manufacturer's vendor id.
+      optional uint32 vendor_id = 1;
+
+      // The GPU manufacturer's device id for the chip set.
+      optional uint32 device_id = 2;
+
+      // The driver version on the GPU.
+      optional string driver_version = 3;
+
+      // The driver date on the GPU.
+      optional string driver_date = 4;
+
+      // The GL_VENDOR string. An example of a gl_vendor string is
+      // "Imagination Technologies". "" if we are not using OpenGL.
+      optional string gl_vendor = 6;
+
+      // The GL_RENDERER string. An example of a gl_renderer string is
+      // "PowerVR SGX 540". "" if we are not using OpenGL.
+      optional string gl_renderer = 7;
+    }
+    optional Graphics gpu = 8;
+
+    // Information about Bluetooth devices paired with the system.
+    message Bluetooth {
+      // Whether Bluetooth is present on this system.
+      optional bool is_present = 1;
+
+      // Whether Bluetooth is enabled on this system.
+      optional bool is_enabled = 2;
+
+      // Describes a paired device.
+      message PairedDevice {
+        // Assigned class of the device. This is a bitfield according to the
+        // Bluetooth specification available at the following URL:
+        // https://www.bluetooth.org/en-us/specification/assigned-numbers-overview/baseband
+        optional uint32 bluetooth_class = 1;
+
+        // Decoded device type.
+        enum Type {
+          DEVICE_UNKNOWN = 0;
+          DEVICE_COMPUTER = 1;
+          DEVICE_PHONE = 2;
+          DEVICE_MODEM = 3;
+          DEVICE_AUDIO = 4;
+          DEVICE_CAR_AUDIO = 5;
+          DEVICE_VIDEO = 6;
+          DEVICE_PERIPHERAL = 7;
+          DEVICE_JOYSTICK = 8;
+          DEVICE_GAMEPAD = 9;
+          DEVICE_KEYBOARD = 10;
+          DEVICE_MOUSE = 11;
+          DEVICE_TABLET = 12;
+          DEVICE_KEYBOARD_MOUSE_COMBO = 13;
+        }
+        optional Type type = 2;
+
+        // Vendor prefix of the Bluetooth address, these are OUI registered by
+        // the IEEE and are encoded with the first byte in bits 16-23, the
+        // second byte in bits 8-15 and the third byte in bits 0-7.
+        //
+        // ie. Google's OUI (00:1A:11) is encoded as 0x00001A11
+        optional uint32 vendor_prefix = 4;
+
+        // The Vendor ID of a device, returned in vendor_id below, can be
+        // either allocated by the Bluetooth SIG or USB IF, providing two
+        // completely overlapping namespaces for identifiers.
+        //
+        // This field should be read along with vendor_id to correctly
+        // identify the vendor. For example Google is identified by either
+        // vendor_id_source = VENDOR_ID_BLUETOOTH, vendor_id = 0x00E0 or
+        // vendor_id_source = VENDOR_ID_USB, vendor_id = 0x18D1.
+        //
+        // If the device does not support the Device ID specification the
+        // unknown value will be set.
+        enum VendorIDSource {
+          VENDOR_ID_UNKNOWN = 0;
+          VENDOR_ID_BLUETOOTH = 1;
+          VENDOR_ID_USB = 2;
+        }
+        optional VendorIDSource vendor_id_source = 8;
+
+        // Vendor ID of the device, where available.
+        optional uint32 vendor_id = 5;
+
+        // Product ID of the device, where available.
+        optional uint32 product_id = 6;
+
+        // Device ID of the device, generally the release or version number in
+        // BCD format, where available.
+        optional uint32 device_id = 7;
+      }
+      repeated PairedDevice paired_device = 3;
+    }
+    optional Bluetooth bluetooth = 11;
+
+    // Whether the internal display produces touch events. Omitted if unknown.
+    // Logged on ChromeOS only.
+    optional bool internal_display_supports_touch = 14;
+
+    // Vendor ids and product ids of external touchscreens.
+    message TouchScreen {
+      // Touch screen vendor id.
+      optional uint32 vendor_id = 1;
+      // Touch screen product id.
+      optional uint32 product_id = 2;
+    }
+    // Lists vendor and product ids of external touchscreens.
+    // Logged on ChromeOS only.
+    repeated TouchScreen external_touchscreen = 15;
+
+    // Drive messages are currently logged on Windows 7+, iOS, and Android.
+    message Drive {
+      // Whether this drive incurs a time penalty when randomly accessed. This
+      // should be true for spinning disks but false for SSDs or other
+      // flash-based drives.
+      optional bool has_seek_penalty = 1;
+    }
+    // The drive that the application executable was loaded from.
+    optional Drive app_drive = 16;
+    // The drive that the current user data directory was loaded from.
+    optional Drive user_data_drive = 17;
+  }
+  optional Hardware hardware = 6;
+
+  // Information about the network connection.
+  message Network {
+    // Set to true if connection_type changed during the lifetime of the log.
+    optional bool connection_type_is_ambiguous = 1;
+
+    // See net::NetworkChangeNotifier::ConnectionType.
+    enum ConnectionType {
+      CONNECTION_UNKNOWN = 0;
+      CONNECTION_ETHERNET = 1;
+      CONNECTION_WIFI = 2;
+      CONNECTION_2G = 3;
+      CONNECTION_3G = 4;
+      CONNECTION_4G = 5;
+      CONNECTION_BLUETOOTH = 6;
+    }
+    // The connection type according to NetworkChangeNotifier.
+    optional ConnectionType connection_type = 2;
+
+    // Set to true if wifi_phy_layer_protocol changed during the lifetime of the log.
+    optional bool wifi_phy_layer_protocol_is_ambiguous = 3;
+
+    // See net::WifiPHYLayerProtocol.
+    enum WifiPHYLayerProtocol {
+      WIFI_PHY_LAYER_PROTOCOL_NONE = 0;
+      WIFI_PHY_LAYER_PROTOCOL_ANCIENT = 1;
+      WIFI_PHY_LAYER_PROTOCOL_A = 2;
+      WIFI_PHY_LAYER_PROTOCOL_B = 3;
+      WIFI_PHY_LAYER_PROTOCOL_G = 4;
+      WIFI_PHY_LAYER_PROTOCOL_N = 5;
+      WIFI_PHY_LAYER_PROTOCOL_UNKNOWN = 6;
+    }
+    // The physical layer mode of the associated wifi access point, if any.
+    optional WifiPHYLayerProtocol wifi_phy_layer_protocol = 4;
+
+    // Describe wifi access point information.
+    message WifiAccessPoint {
+      // Vendor prefix of the access point's BSSID, these are OUIs
+      // (Organizationally Unique Identifiers) registered by
+      // the IEEE and are encoded with the first byte in bits 16-23, the
+      // second byte in bits 8-15 and the third byte in bits 0-7.
+      optional uint32 vendor_prefix = 1;
+
+      // Access point seurity mode definitions.
+      enum SecurityMode {
+        SECURITY_UNKNOWN = 0;
+        SECURITY_WPA = 1;
+        SECURITY_WEP = 2;
+        SECURITY_RSN = 3;
+        SECURITY_802_1X = 4;
+        SECURITY_PSK = 5;
+        SECURITY_NONE = 6;
+      }
+      // The security mode of the access point.
+      optional SecurityMode security_mode = 2;
+
+      // Vendor specific information.
+      message VendorInformation {
+        // The model number, for example "0".
+        optional string model_number = 1;
+
+        // The model name (sometimes the same as the model_number),
+        // for example "WZR-HP-AG300H".
+        optional string model_name = 2;
+
+        // The device name (sometimes the same as the model_number),
+        // for example "Dummynet"
+        optional string device_name = 3;
+
+        // The list of vendor-specific OUIs (Organziationally Unqiue
+        // Identifiers). These are provided by the vendor through WPS
+        // (Wireless Provisioning Service) information elements, which
+        // identifies the content of the element.
+        repeated uint32 element_identifier = 4;
+      }
+      // The wireless access point vendor information.
+      optional VendorInformation vendor_info = 3;
+    }
+    // Information of the wireless AP that device is connected to.
+    optional WifiAccessPoint access_point_info = 5;
+  }
+  optional Network network = 13;
+
+  // Information on the Google Update install that is managing this client.
+  message GoogleUpdate {
+    // Whether the Google Update install is system-level or user-level.
+    optional bool is_system_install = 1;
+
+    // The date at which Google Update last started performing an automatic
+    // update check, in seconds since the Unix epoch.
+    optional int64 last_automatic_start_timestamp = 2;
+
+    // The date at which Google Update last successfully sent an update check
+    // and recieved an intact response from the server, in seconds since the
+    // Unix epoch. (The updates don't need to be successfully installed.)
+    optional int64 last_update_check_timestamp = 3;
+
+    // Describes a product being managed by Google Update. (This can also
+    // describe Google Update itself.)
+    message ProductInfo {
+      // The current version of the product that is installed.
+      optional string version = 1;
+
+      // The date at which Google Update successfully updated this product,
+      // stored in seconds since the Unix epoch.  This is updated when an update
+      // is successfully applied, or if the server reports that no update
+      // is available.
+      optional int64 last_update_success_timestamp = 2;
+
+      // The result reported by the product updater on its last run.
+      enum InstallResult {
+        INSTALL_RESULT_SUCCESS = 0;
+        INSTALL_RESULT_FAILED_CUSTOM_ERROR = 1;
+        INSTALL_RESULT_FAILED_MSI_ERROR = 2;
+        INSTALL_RESULT_FAILED_SYSTEM_ERROR = 3;
+        INSTALL_RESULT_EXIT_CODE = 4;
+      }
+      optional InstallResult last_result = 3;
+
+      // The error code reported by the product updater on its last run.  This
+      // will typically be a error code specific to the product installer.
+      optional int32 last_error = 4;
+
+      // The extra error code reported by the product updater on its last run.
+      // This will typically be a Win32 error code.
+      optional int32 last_extra_error = 5;
+    }
+    optional ProductInfo google_update_status = 4;
+    optional ProductInfo client_status = 5;
+  }
+  optional GoogleUpdate google_update = 11;
+
+  // Information on all installed plugins.
+  message Plugin {
+    // The plugin's self-reported name and filename (without path).
+    optional string name = 1;
+    optional string filename = 2;
+
+    // The plugin's version.
+    optional string version = 3;
+
+    // True if the plugin is disabled.
+    // If a client has multiple local Chrome user accounts, this is logged based
+    // on the first user account launched during the current session.
+    optional bool is_disabled = 4;
+
+    // True if the plugin is PPAPI.
+    optional bool is_pepper = 5;
+  }
+  repeated Plugin plugin = 7;
+
+  // Figures that can be used to generate application stability metrics.
+  // All values are counts of events since the last time that these
+  // values were reported.
+  // Next tag: 24
+  message Stability {
+    // Total amount of time that the program was running, in seconds,
+    // since the last time a log was recorded, as measured using a client-side
+    // clock implemented via TimeTicks, which guarantees that it is monotonic
+    // and does not jump if the user changes his/her clock.  The TimeTicks
+    // implementation also makes the clock not count time the computer is
+    // suspended.
+    optional int64 incremental_uptime_sec = 1;
+
+    // Total amount of time that the program was running, in seconds,
+    // since startup, as measured using a client-side clock implemented
+    // via TimeTicks, which guarantees that it is monotonic and does not
+    // jump if the user changes his/her clock.  The TimeTicks implementation
+    // also makes the clock not count time the computer is suspended.
+    // This field was added for M-35.
+    optional int64 uptime_sec = 23;
+
+    // Page loads along with renderer crashes and hangs, since page load count
+    // roughly corresponds to usage.
+    optional int32 page_load_count = 2;
+    optional int32 renderer_crash_count = 3;
+    optional int32 renderer_hang_count = 4;
+
+    // Number of renderer crashes that were for extensions. These crashes are
+    // not counted in renderer_crash_count.
+    optional int32 extension_renderer_crash_count = 5;
+
+    // Number of non-renderer child process crashes.
+    optional int32 child_process_crash_count = 6;
+
+    // Number of times the browser has crashed while logged in as the "other
+    // user" (guest) account.
+    // Logged on ChromeOS only.
+    optional int32 other_user_crash_count = 7;
+
+    // Number of times the kernel has crashed.
+    // Logged on ChromeOS only.
+    optional int32 kernel_crash_count = 8;
+
+    // Number of times the system has shut down uncleanly.
+    // Logged on ChromeOS only.
+    optional int32 unclean_system_shutdown_count = 9;
+
+    //
+    // All the remaining fields in the Stability are recorded at most once per
+    // client session.
+    //
+
+    // The number of times the program was launched.
+    // This will typically be equal to 1.  However, it is possible that Chrome
+    // was unable to upload stability metrics for previous launches (e.g. due to
+    // crashing early during startup), and hence this value might be greater
+    // than 1.
+    optional int32 launch_count = 15;
+    // The number of times that it didn't exit cleanly (which we assume to be
+    // mostly crashes).
+    optional int32 crash_count = 16;
+
+    // The number of times the program began, but did not complete, the shutdown
+    // process.  (For example, this may occur when Windows is shutting down, and
+    // it only gives the process a few seconds to clean up.)
+    optional int32 incomplete_shutdown_count = 17;
+
+    // The number of times the program was able register with breakpad crash
+    // services.
+    optional int32 breakpad_registration_success_count = 18;
+
+    // The number of times the program failed to register with breakpad crash
+    // services.  If crash registration fails then when the program crashes no
+    // crash report will be generated.
+    optional int32 breakpad_registration_failure_count = 19;
+
+    // The number of times the program has run under a debugger.  This should
+    // be an exceptional condition.  Running under a debugger prevents crash
+    // dumps from being generated.
+    optional int32 debugger_present_count = 20;
+
+    // The number of times the program has run without a debugger attached.
+    // This should be most common scenario and should be very close to
+    // |launch_count|.
+    optional int32 debugger_not_present_count = 21;
+
+    // Stability information for all installed plugins.
+    message PluginStability {
+      // The relevant plugin's information (name, etc.)
+      optional Plugin plugin = 1;
+
+      // The number of times this plugin's process was launched.
+      optional int32 launch_count = 2;
+
+      // The number of times this plugin was instantiated on a web page.
+      // This will be >= |launch_count|.
+      // (A page load with multiple sections drawn by this plugin will
+      // increase this count multiple times.)
+      optional int32 instance_count = 3;
+
+      // The number of times this plugin process crashed.
+      // This value will be <= |launch_count|.
+      optional int32 crash_count = 4;
+
+      // The number of times this plugin could not be loaded.
+      optional int32 loading_error_count = 5;
+    }
+    repeated PluginStability plugin_stability = 22;
+  }
+  optional Stability stability = 8;
+
+  // Description of a field trial or experiment that the user is currently
+  // enrolled in.
+  // All metrics reported in this upload can potentially be influenced by the
+  // field trial.
+  message FieldTrial {
+    // The name of the field trial, as a 32-bit identifier.
+    // Currently, the identifier is a hash of the field trial's name.
+    optional fixed32 name_id = 1;
+
+    // The user's group within the field trial, as a 32-bit identifier.
+    // Currently, the identifier is a hash of the group's name.
+    optional fixed32 group_id = 2;
+  }
+  repeated FieldTrial field_trial = 9;
+
+  // Information about the A/V output device(s) (typically just a TV).
+  // However, a configuration may have one or more intermediate A/V devices
+  // between the source device and the TV (e.g. an A/V receiver, video
+  // processor, etc.).
+  message ExternalAudioVideoDevice {
+    // The manufacturer name (possibly encoded as a 3-letter code, e.g. "YMH"
+    // for Yamaha).
+    optional string manufacturer_name = 1;
+
+    // The model name (e.g. "RX-V1900"). Some devices may report generic names
+    // like "receiver" or use the full manufacturer name (e.g "PHILIPS").
+    optional string model_name = 2;
+
+    // The product code (e.g. "0218").
+    optional string product_code = 3;
+
+    // The device types. A single device can have multiple types (e.g. a set-top
+    // box could be both a tuner and a player).  The same type may even be
+    // repeated (e.g a device that reports two tuners).
+    enum AVDeviceType {
+      AV_DEVICE_TYPE_UNKNOWN = 0;
+      AV_DEVICE_TYPE_TV = 1;
+      AV_DEVICE_TYPE_RECORDER = 2;
+      AV_DEVICE_TYPE_TUNER = 3;
+      AV_DEVICE_TYPE_PLAYER = 4;
+      AV_DEVICE_TYPE_AUDIO_SYSTEM = 5;
+    }
+    repeated AVDeviceType av_device_type = 4;
+
+    // The year of manufacture.
+    optional int32 manufacture_year = 5;
+
+    // The week of manufacture.
+    // Note: per the Wikipedia EDID article, numbering for this field may not
+    // be consistent between manufacturers.
+    optional int32 manufacture_week = 6;
+
+    // Max horizontal resolution in pixels.
+    optional int32 horizontal_resolution = 7;
+
+    // Max vertical resolution in pixels.
+    optional int32 vertical_resolution = 8;
+
+    // Audio capabilities of the device.
+    // Ref: http://en.wikipedia.org/wiki/Extended_display_identification_data
+    message AudioDescription {
+      // Audio format
+      enum AudioFormat {
+        AUDIO_FORMAT_UNKNOWN = 0;
+        AUDIO_FORMAT_LPCM = 1;
+        AUDIO_FORMAT_AC_3 = 2;
+        AUDIO_FORMAT_MPEG1 = 3;
+        AUDIO_FORMAT_MP3 = 4;
+        AUDIO_FORMAT_MPEG2 = 5;
+        AUDIO_FORMAT_AAC = 6;
+        AUDIO_FORMAT_DTS = 7;
+        AUDIO_FORMAT_ATRAC = 8;
+        AUDIO_FORMAT_ONE_BIT = 9;
+        AUDIO_FORMAT_DD_PLUS = 10;
+        AUDIO_FORMAT_DTS_HD = 11;
+        AUDIO_FORMAT_MLP_DOLBY_TRUEHD = 12;
+        AUDIO_FORMAT_DST_AUDIO = 13;
+        AUDIO_FORMAT_MICROSOFT_WMA_PRO = 14;
+      }
+      optional AudioFormat audio_format = 1;
+
+      // Number of channels (e.g. 1, 2, 8, etc.).
+      optional int32 num_channels = 2;
+
+      // Supported sample frequencies in Hz (e.g. 32000, 44100, etc.).
+      // Multiple frequencies may be specified.
+      repeated int32 sample_frequency_hz = 3;
+
+      // Maximum bit rate in bits/s.
+      optional int32 max_bit_rate_per_second = 4;
+
+      // Bit depth (e.g. 16, 20, 24, etc.).
+      optional int32 bit_depth = 5;
+    }
+    repeated AudioDescription audio_description = 9;
+
+    // The position in AV setup.
+    // A value of 0 means this device is the TV.
+    // A value of 1 means this device is directly connected to one of
+    // the TV's inputs.
+    // Values > 1 indicate there are 1 or more devices between this device
+    // and the TV.
+    optional int32 position_in_setup = 10;
+
+    // Whether this device is in the path to the TV.
+    optional bool is_in_path_to_tv = 11;
+
+    // The CEC version the device supports.
+    // CEC stands for Consumer Electronics Control, a part of the HDMI
+    // specification.  Not all HDMI devices support CEC.
+    // Only devices that support CEC will report a value here.
+    optional int32 cec_version = 12;
+
+    // This message reports CEC commands seen by a device.
+    // After each log is sent, this information is cleared and gathered again.
+    // By collecting CEC status information by opcode we can determine
+    // which CEC features can be supported.
+    message CECCommand {
+      // The CEC command opcode.  CEC supports up to 256 opcodes.
+      // We add only one CECCommand message per unique opcode.  Only opcodes
+      // seen by the device will be reported. The remainder of the message
+      // accumulates status for this opcode (and device).
+      optional int32 opcode = 1;
+
+      // The total number of commands received from the external device.
+      optional int32 num_received_direct = 2;
+
+      // The number of commands received from the external device as part of a
+      // broadcast message.
+      optional int32 num_received_broadcast = 3;
+
+      // The total number of commands sent to the external device.
+      optional int32 num_sent_direct = 4;
+
+      // The number of commands sent to the external device as part of a
+      // broadcast message.
+      optional int32 num_sent_broadcast = 5;
+
+      // The number of aborted commands for unknown reasons.
+      optional int32 num_aborted_unknown_reason = 6;
+
+      // The number of aborted commands because of an unrecognized opcode.
+      optional int32 num_aborted_unrecognized = 7;
+    }
+    repeated CECCommand cec_command = 13;
+  }
+  repeated ExternalAudioVideoDevice external_audio_video_device = 14;
+
+  // Information about the current wireless access point. Collected directly
+  // from the wireless access point via standard apis if the device is
+  // connected to the Internet wirelessly. Introduced for Chrome on TV devices
+  // but also can be collected by ChromeOS, Android or other clients.
+  message ExternalAccessPoint {
+    // The manufacturer name, for example "ASUSTeK Computer Inc.".
+    optional string manufacturer = 1;
+
+    // The model name, for example "Wi-Fi Protected Setup Router".
+    optional string model_name = 2;
+
+    // The model number, for example "RT-N16".
+    optional string model_number = 3;
+
+    // The device name (sometime same as model_number), for example "RT-N16".
+    optional string device_name = 4;
+  }
+  optional ExternalAccessPoint external_access_point = 15;
+
+  // Number of users currently signed into a multiprofile session.
+  // A zero value indicates that the user count changed while the log is open.
+  // Logged only on ChromeOS.
+  optional uint32 multi_profile_user_count = 17;
+
+  // Information about extensions that are installed, masked to provide better
+  // privacy.  Only extensions from a single profile are reported; this will
+  // generally be the profile used when the browser is started.  The profile
+  // reported on will remain consistent at least until the browser is
+  // relaunched (or the profile is deleted by the user).
+  //
+  // Each client first picks a value for client_key derived from its UMA
+  // client_id:
+  //   client_key = client_id % 4096
+  // Then, each installed extension is mapped into a hash bucket according to
+  //   bucket = CityHash64(StringPrintf("%d:%s",
+  //                                    client_key, extension_id)) % 1024
+  // The client reports the set of hash buckets occupied by all installed
+  // extensions.  If multiple extensions map to the same bucket, that bucket is
+  // still only reported once.
+  repeated int32 occupied_extension_bucket = 18;
+
+  // The state of loaded extensions for this system. The system can have either
+  // no applicable extensions, extensions only from the webstore and verified by
+  // the webstore, extensions only from the webstore but not verified, or
+  // extensions not from the store. If there is a single off-store extension,
+  // then HAS_OFFSTORE is reported. This should be kept in sync with the
+  // corresponding enum in chrome/browser/metrics/extensions_metrics_provider.cc
+  enum ExtensionsState {
+    NO_EXTENSIONS = 0;
+    NO_OFFSTORE_VERIFIED = 1;
+    NO_OFFSTORE_UNVERIFIED = 2;
+    HAS_OFFSTORE = 3;
+  }
+  optional ExtensionsState offstore_extensions_state = 19;
+}
diff --git a/metricsd/uploader/proto/user_action_event.proto b/metricsd/uploader/proto/user_action_event.proto
new file mode 100644 (file)
index 0000000..464f3c8
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+//
+// Stores information about an event that occurs in response to a user action,
+// e.g. an interaction with a browser UI element.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_outer_classname = "UserActionEventProtos";
+option java_package = "org.chromium.components.metrics";
+
+package metrics;
+
+// Next tag: 3
+message UserActionEventProto {
+  // The name of the action, hashed.
+  optional fixed64 name_hash = 1;
+
+  // The timestamp for the event, in seconds since the epoch.
+  optional int64 time = 2;
+}
diff --git a/metricsd/uploader/sender.h b/metricsd/uploader/sender.h
new file mode 100644 (file)
index 0000000..369c9c2
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_UPLOADER_SENDER_H_
+#define METRICS_UPLOADER_SENDER_H_
+
+#include <string>
+
+// Abstract class for a Sender that uploads a metrics message.
+class Sender {
+ public:
+  virtual ~Sender() {}
+  // Sends a message |content| with its sha1 hash |hash|
+  virtual bool Send(const std::string& content, const std::string& hash) = 0;
+};
+
+#endif  // METRICS_UPLOADER_SENDER_H_
diff --git a/metricsd/uploader/sender_http.cc b/metricsd/uploader/sender_http.cc
new file mode 100644 (file)
index 0000000..4b572a6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/sender_http.h"
+
+#include <string>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <brillo/http/http_utils.h>
+#include <brillo/mime_utils.h>
+
+HttpSender::HttpSender(const std::string server_url)
+    : server_url_(server_url) {}
+
+bool HttpSender::Send(const std::string& content,
+                      const std::string& content_hash) {
+  const std::string hash =
+      base::HexEncode(content_hash.data(), content_hash.size());
+
+  brillo::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}};
+  brillo::ErrorPtr error;
+  auto response = brillo::http::PostTextAndBlock(
+      server_url_,
+      content,
+      brillo::mime::application::kWwwFormUrlEncoded,
+      headers,
+      brillo::http::Transport::CreateDefault(),
+      &error);
+  if (!response || response->ExtractDataAsString() != "OK") {
+    if (error) {
+      DLOG(ERROR) << "Failed to send data: " << error->GetMessage();
+    }
+    return false;
+  }
+  return true;
+}
diff --git a/metricsd/uploader/sender_http.h b/metricsd/uploader/sender_http.h
new file mode 100644 (file)
index 0000000..4f1c08f
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_UPLOADER_SENDER_HTTP_H_
+#define METRICS_UPLOADER_SENDER_HTTP_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "uploader/sender.h"
+
+// Sender implemented using http_utils from libbrillo
+class HttpSender : public Sender {
+ public:
+  explicit HttpSender(std::string server_url);
+  ~HttpSender() override = default;
+  // Sends |content| whose SHA1 hash is |hash| to server_url with a synchronous
+  // POST request to server_url.
+  bool Send(const std::string& content, const std::string& hash) override;
+
+ private:
+  const std::string server_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(HttpSender);
+};
+
+#endif  // METRICS_UPLOADER_SENDER_HTTP_H_
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
new file mode 100644 (file)
index 0000000..637cf9e
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/system_profile_cache.h"
+
+#include <base/files/file_util.h>
+#include <base/guid.h>
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+#include <brillo/osrelease_reader.h>
+#include <string>
+#include <update_engine/client.h>
+#include <vector>
+
+#include "constants.h"
+#include "persistent_integer.h"
+#include "uploader/metrics_log_base.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+
+namespace {
+
+const char kPersistentSessionIdFilename[] = "Sysinfo.SessionId";
+
+}  // namespace
+
+std::string ChannelToString(
+    const metrics::SystemProfileProto_Channel& channel) {
+  switch (channel) {
+    case metrics::SystemProfileProto::CHANNEL_STABLE:
+    return "STABLE";
+  case metrics::SystemProfileProto::CHANNEL_DEV:
+    return "DEV";
+  case metrics::SystemProfileProto::CHANNEL_BETA:
+    return "BETA";
+  case metrics::SystemProfileProto::CHANNEL_CANARY:
+    return "CANARY";
+  default:
+    return "UNKNOWN";
+  }
+}
+
+SystemProfileCache::SystemProfileCache()
+    : initialized_(false),
+    testing_(false),
+    metrics_directory_(metrics::kMetricsDirectory),
+    session_id_(new chromeos_metrics::PersistentInteger(
+        kPersistentSessionIdFilename)) {
+}
+
+SystemProfileCache::SystemProfileCache(bool testing,
+                                       const base::FilePath& metrics_directory)
+    : initialized_(false),
+      testing_(testing),
+      metrics_directory_(metrics_directory),
+      session_id_(new chromeos_metrics::PersistentInteger(
+          kPersistentSessionIdFilename)) {
+}
+
+bool SystemProfileCache::Initialize() {
+  CHECK(!initialized_)
+      << "this should be called only once in the metrics_daemon lifetime.";
+
+  brillo::OsReleaseReader reader;
+  std::string channel;
+  if (testing_) {
+    reader.LoadTestingOnly(metrics_directory_);
+    channel = "unknown";
+  } else {
+    reader.Load();
+    auto client = update_engine::UpdateEngineClient::CreateInstance();
+    if (!client->GetChannel(&channel)) {
+      LOG(ERROR) << "failed to read the current channel from update engine.";
+    }
+  }
+
+  if (!reader.GetString(metrics::kProductId, &profile_.product_id)) {
+    LOG(ERROR) << "product_id is not set.";
+    return false;
+  }
+
+  if (!reader.GetString(metrics::kProductVersion, &profile_.version)) {
+    LOG(ERROR) << "failed to read the product version";
+  }
+
+  if (channel.empty() || profile_.version.empty()) {
+    // If the channel or version is missing, the image is not official.
+    // In this case, set the channel to unknown and the version to 0.0.0.0 to
+    // avoid polluting the production data.
+    channel = "";
+    profile_.version = metrics::kDefaultVersion;
+  }
+  std::string guid_path = metrics_directory_.Append(
+      metrics::kMetricsGUIDFileName).value();
+  profile_.client_id = testing_ ?
+      "client_id_test" :
+      GetPersistentGUID(guid_path);
+  profile_.model_manifest_id = "unknown";
+  if (!testing_) {
+    brillo::KeyValueStore weave_config;
+    if (!weave_config.Load(base::FilePath(metrics::kWeaveConfigurationFile))) {
+      LOG(ERROR) << "Failed to load the weave configuration file.";
+    } else if (!weave_config.GetString(metrics::kModelManifestId,
+                                       &profile_.model_manifest_id)) {
+      LOG(ERROR) << "The model manifest id (model_id) is undefined in "
+                 << metrics::kWeaveConfigurationFile;
+    }
+  }
+
+  profile_.channel = ProtoChannelFromString(channel);
+
+  // Increment the session_id everytime we initialize this. If metrics_daemon
+  // does not crash, this should correspond to the number of reboots of the
+  // system.
+  session_id_->Add(1);
+  profile_.session_id = static_cast<int32_t>(session_id_->Get());
+
+  initialized_ = true;
+  return initialized_;
+}
+
+bool SystemProfileCache::InitializeOrCheck() {
+  return initialized_ || Initialize();
+}
+
+bool SystemProfileCache::Populate(
+    metrics::ChromeUserMetricsExtension* metrics_proto) {
+  CHECK(metrics_proto);
+  if (not InitializeOrCheck()) {
+    return false;
+  }
+
+  // The client id is hashed before being sent.
+  metrics_proto->set_client_id(
+      metrics::MetricsLogBase::Hash(profile_.client_id));
+  metrics_proto->set_session_id(profile_.session_id);
+
+  // Sets the product id.
+  metrics_proto->set_product(9);
+
+  metrics::SystemProfileProto* profile_proto =
+      metrics_proto->mutable_system_profile();
+  profile_proto->mutable_hardware()->set_hardware_class(
+      profile_.model_manifest_id);
+  profile_proto->set_app_version(profile_.version);
+  profile_proto->set_channel(profile_.channel);
+  metrics::SystemProfileProto_BrilloDeviceData* device_data =
+      profile_proto->mutable_brillo();
+  device_data->set_product_id(profile_.product_id);
+
+  return true;
+}
+
+std::string SystemProfileCache::GetPersistentGUID(
+    const std::string& filename) {
+  std::string guid;
+  base::FilePath filepath(filename);
+  if (!base::ReadFileToString(filepath, &guid)) {
+    guid = base::GenerateGUID();
+    // If we can't read or write the file, the guid will not be preserved during
+    // the next reboot. Crash.
+    CHECK(base::WriteFile(filepath, guid.c_str(), guid.size()));
+  }
+  return guid;
+}
+
+metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
+    const std::string& channel) {
+  if (channel == "stable-channel") {
+    return metrics::SystemProfileProto::CHANNEL_STABLE;
+  } else if (channel == "dev-channel") {
+    return metrics::SystemProfileProto::CHANNEL_DEV;
+  } else if (channel == "beta-channel") {
+    return metrics::SystemProfileProto::CHANNEL_BETA;
+  } else if (channel == "canary-channel") {
+    return metrics::SystemProfileProto::CHANNEL_CANARY;
+  }
+
+  DLOG(INFO) << "unknown channel: " << channel;
+  return metrics::SystemProfileProto::CHANNEL_UNKNOWN;
+}
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
new file mode 100644 (file)
index 0000000..ae54a2a
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_
+#define METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "persistent_integer.h"
+#include "uploader/proto/system_profile.pb.h"
+#include "uploader/system_profile_setter.h"
+
+namespace metrics {
+class ChromeUserMetricsExtension;
+}
+
+struct SystemProfile {
+  std::string version;
+  std::string model_manifest_id;
+  std::string client_id;
+  int session_id;
+  metrics::SystemProfileProto::Channel channel;
+  std::string product_id;
+};
+
+// Retrieves general system informations needed by the protobuf for context and
+// remembers them to avoid expensive calls.
+//
+// The cache is populated lazily. The only method needed is Populate.
+class SystemProfileCache : public SystemProfileSetter {
+ public:
+  SystemProfileCache();
+
+  SystemProfileCache(bool testing, const base::FilePath& metrics_directory);
+
+  // Populates the ProfileSystem protobuf with system information.
+  bool Populate(metrics::ChromeUserMetricsExtension* metrics_proto) override;
+
+  // Converts a string representation of the channel to a
+  // SystemProfileProto_Channel
+  static metrics::SystemProfileProto_Channel ProtoChannelFromString(
+      const std::string& channel);
+
+  // Gets the persistent GUID and create it if it has not been created yet.
+  static std::string GetPersistentGUID(const std::string& filename);
+
+ private:
+  friend class UploadServiceTest;
+  FRIEND_TEST(UploadServiceTest, ExtractChannelFromDescription);
+  FRIEND_TEST(UploadServiceTest, ReadKeyValueFromFile);
+  FRIEND_TEST(UploadServiceTest, SessionIdIncrementedAtInitialization);
+  FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
+
+  // Fetches all informations and populates |profile_|
+  bool Initialize();
+
+  // Initializes |profile_| only if it has not been yet initialized.
+  bool InitializeOrCheck();
+
+  bool initialized_;
+  bool testing_;
+  base::FilePath metrics_directory_;
+  scoped_ptr<chromeos_metrics::PersistentInteger> session_id_;
+  SystemProfile profile_;
+};
+
+#endif  // METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_
diff --git a/metricsd/uploader/system_profile_setter.h b/metricsd/uploader/system_profile_setter.h
new file mode 100644 (file)
index 0000000..bd3ff42
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_
+#define METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_
+
+namespace metrics {
+class ChromeUserMetricsExtension;
+}
+
+// Abstract class used to delegate populating SystemProfileProto with system
+// information to simplify testing.
+class SystemProfileSetter {
+ public:
+  virtual ~SystemProfileSetter() {}
+  // Populates the protobuf with system informations.
+  virtual bool Populate(metrics::ChromeUserMetricsExtension* profile_proto) = 0;
+};
+
+#endif  // METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_
diff --git a/metricsd/uploader/upload_service.cc b/metricsd/uploader/upload_service.cc
new file mode 100644 (file)
index 0000000..b630cec
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "uploader/upload_service.h"
+
+#include <string>
+
+#include <base/bind.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/memory/scoped_vector.h>
+#include <base/message_loop/message_loop.h>
+#include <base/metrics/histogram.h>
+#include <base/metrics/histogram_base.h>
+#include <base/metrics/histogram_snapshot_manager.h>
+#include <base/metrics/sparse_histogram.h>
+#include <base/metrics/statistics_recorder.h>
+#include <base/sha1.h>
+
+#include "constants.h"
+#include "serialization/metric_sample.h"
+#include "serialization/serialization_utils.h"
+#include "uploader/metrics_log.h"
+#include "uploader/sender_http.h"
+#include "uploader/system_profile_setter.h"
+
+const int UploadService::kMaxFailedUpload = 10;
+
+UploadService::UploadService(SystemProfileSetter* setter,
+                             MetricsLibraryInterface* metrics_lib,
+                             const std::string& server)
+    : system_profile_setter_(setter),
+      metrics_lib_(metrics_lib),
+      histogram_snapshot_manager_(this),
+      sender_(new HttpSender(server)),
+      failed_upload_count_(metrics::kFailedUploadCountName),
+      testing_(false) {
+}
+
+UploadService::UploadService(SystemProfileSetter* setter,
+                             MetricsLibraryInterface* metrics_lib,
+                             const std::string& server,
+                             bool testing)
+    : UploadService(setter, metrics_lib, server) {
+  testing_ = testing;
+}
+
+void UploadService::Init(const base::TimeDelta& upload_interval,
+                         const base::FilePath& metrics_directory) {
+  base::StatisticsRecorder::Initialize();
+  metrics_file_ = metrics_directory.Append(metrics::kMetricsEventsFileName);
+  staged_log_path_ = metrics_directory.Append(metrics::kStagedLogName);
+
+  if (!testing_) {
+    base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+        base::Bind(&UploadService::UploadEventCallback,
+                   base::Unretained(this),
+                   upload_interval),
+        upload_interval);
+  }
+}
+
+void UploadService::StartNewLog() {
+  CHECK(!HasStagedLog()) << "the staged log should be discarded before "
+                         << "starting a new metrics log";
+  MetricsLog* log = new MetricsLog();
+  current_log_.reset(log);
+}
+
+void UploadService::UploadEventCallback(const base::TimeDelta& interval) {
+  UploadEvent();
+
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&UploadService::UploadEventCallback,
+                 base::Unretained(this),
+                 interval),
+      interval);
+}
+
+void UploadService::UploadEvent() {
+  // If the system shutdown or crashed while uploading a report, we may not have
+  // deleted an old log.
+  RemoveFailedLog();
+
+  if (HasStagedLog()) {
+    // Previous upload failed, retry sending the logs.
+    SendStagedLog();
+    return;
+  }
+
+  // Previous upload successful, reading metrics sample from the file.
+  ReadMetrics();
+  GatherHistograms();
+  StageCurrentLog();
+
+  // If a log is available for upload, upload it.
+  if (HasStagedLog()) {
+    SendStagedLog();
+  }
+}
+
+void UploadService::SendStagedLog() {
+  // If metrics are not enabled, discard the log and exit.
+  if (!metrics_lib_->AreMetricsEnabled()) {
+    LOG(INFO) << "Metrics disabled. Don't upload metrics samples.";
+    base::DeleteFile(staged_log_path_, false);
+    return;
+  }
+
+  std::string staged_log;
+  CHECK(base::ReadFileToString(staged_log_path_, &staged_log));
+
+  // Increase the failed count in case the daemon crashes while sending the log.
+  failed_upload_count_.Add(1);
+
+  if (!sender_->Send(staged_log, base::SHA1HashString(staged_log))) {
+    LOG(WARNING) << "log failed to upload";
+  } else {
+    VLOG(1) << "uploaded " << staged_log.length() << " bytes";
+    base::DeleteFile(staged_log_path_, false);
+  }
+
+  RemoveFailedLog();
+}
+
+void UploadService::Reset() {
+  base::DeleteFile(staged_log_path_, false);
+  current_log_.reset();
+  failed_upload_count_.Set(0);
+}
+
+void UploadService::ReadMetrics() {
+  CHECK(!HasStagedLog()) << "cannot read metrics until the old logs have been "
+                         << "discarded";
+
+  ScopedVector<metrics::MetricSample> vector;
+  metrics::SerializationUtils::ReadAndTruncateMetricsFromFile(
+      metrics_file_.value(), &vector);
+
+  int i = 0;
+  for (ScopedVector<metrics::MetricSample>::iterator it = vector.begin();
+       it != vector.end(); it++) {
+    metrics::MetricSample* sample = *it;
+    AddSample(*sample);
+    i++;
+  }
+  VLOG(1) << i << " samples read";
+}
+
+void UploadService::AddSample(const metrics::MetricSample& sample) {
+  base::HistogramBase* counter;
+  switch (sample.type()) {
+    case metrics::MetricSample::CRASH:
+      AddCrash(sample.name());
+      break;
+    case metrics::MetricSample::HISTOGRAM:
+      counter = base::Histogram::FactoryGet(
+          sample.name(), sample.min(), sample.max(), sample.bucket_count(),
+          base::Histogram::kUmaTargetedHistogramFlag);
+      counter->Add(sample.sample());
+      break;
+    case metrics::MetricSample::SPARSE_HISTOGRAM:
+      counter = base::SparseHistogram::FactoryGet(
+          sample.name(), base::HistogramBase::kUmaTargetedHistogramFlag);
+      counter->Add(sample.sample());
+      break;
+    case metrics::MetricSample::LINEAR_HISTOGRAM:
+      counter = base::LinearHistogram::FactoryGet(
+          sample.name(),
+          1,
+          sample.max(),
+          sample.max() + 1,
+          base::Histogram::kUmaTargetedHistogramFlag);
+      counter->Add(sample.sample());
+      break;
+    case metrics::MetricSample::USER_ACTION:
+      GetOrCreateCurrentLog()->RecordUserAction(sample.name());
+      break;
+    default:
+      break;
+  }
+}
+
+void UploadService::AddCrash(const std::string& crash_name) {
+  if (crash_name == "user") {
+    GetOrCreateCurrentLog()->IncrementUserCrashCount();
+  } else if (crash_name == "kernel") {
+    GetOrCreateCurrentLog()->IncrementKernelCrashCount();
+  } else if (crash_name == "uncleanshutdown") {
+    GetOrCreateCurrentLog()->IncrementUncleanShutdownCount();
+  } else {
+    DLOG(ERROR) << "crash name unknown" << crash_name;
+  }
+}
+
+void UploadService::GatherHistograms() {
+  base::StatisticsRecorder::Histograms histograms;
+  base::StatisticsRecorder::GetHistograms(&histograms);
+
+  histogram_snapshot_manager_.PrepareDeltas(
+      base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag);
+}
+
+void UploadService::RecordDelta(const base::HistogramBase& histogram,
+                                const base::HistogramSamples& snapshot) {
+  GetOrCreateCurrentLog()->RecordHistogramDelta(histogram.histogram_name(),
+                                                snapshot);
+}
+
+void UploadService::StageCurrentLog() {
+  // If we haven't logged anything since the last upload, don't upload an empty
+  // report.
+  if (!current_log_)
+    return;
+
+  scoped_ptr<MetricsLog> staged_log;
+  staged_log.swap(current_log_);
+  staged_log->CloseLog();
+  if (!staged_log->PopulateSystemProfile(system_profile_setter_.get())) {
+    LOG(WARNING) << "Error while adding metadata to the log. Discarding the "
+                 << "log.";
+    return;
+  }
+  std::string encoded_log;
+  staged_log->GetEncodedLog(&encoded_log);
+
+  failed_upload_count_.Set(0);
+  if (static_cast<int>(encoded_log.size()) != base::WriteFile(
+      staged_log_path_, encoded_log.data(), encoded_log.size())) {
+    LOG(ERROR) << "failed to persist to " << staged_log_path_.value();
+  }
+}
+
+MetricsLog* UploadService::GetOrCreateCurrentLog() {
+  if (!current_log_) {
+    StartNewLog();
+  }
+  return current_log_.get();
+}
+
+bool UploadService::HasStagedLog() {
+  return base::PathExists(staged_log_path_);
+}
+
+void UploadService::RemoveFailedLog() {
+  if (failed_upload_count_.Get() > kMaxFailedUpload) {
+    LOG(INFO) << "log failed more than " << kMaxFailedUpload << " times.";
+    CHECK(base::DeleteFile(staged_log_path_, false))
+        << "failed to delete staged log at " << staged_log_path_.value();
+    failed_upload_count_.Set(0);
+  }
+}
diff --git a/metricsd/uploader/upload_service.h b/metricsd/uploader/upload_service.h
new file mode 100644 (file)
index 0000000..77df74b
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef METRICS_UPLOADER_UPLOAD_SERVICE_H_
+#define METRICS_UPLOADER_UPLOAD_SERVICE_H_
+
+#include <string>
+
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_flattener.h"
+#include "base/metrics/histogram_snapshot_manager.h"
+
+#include "metrics/metrics_library.h"
+#include "persistent_integer.h"
+#include "uploader/metrics_log.h"
+#include "uploader/sender.h"
+#include "uploader/system_profile_cache.h"
+
+namespace metrics {
+class ChromeUserMetricsExtension;
+class CrashSample;
+class HistogramSample;
+class LinearHistogramSample;
+class MetricSample;
+class SparseHistogramSample;
+class UserActionSample;
+}
+
+class SystemProfileSetter;
+
+// Service responsible for uploading the metrics periodically to the server.
+// This service works as a simple 2-state state-machine.
+//
+// The two states are the presence or not of a staged log.
+// A staged log is a compressed protobuffer containing both the aggregated
+// metrics and event and information about the client. (product,
+// model_manifest_id, etc...).
+//
+// At regular intervals, the upload event will be triggered and the following
+// will happen:
+// * if a staged log is present:
+//    The previous upload may have failed for various reason. We then retry to
+//    upload the same log.
+//    - if the upload is successful, we discard the log (therefore
+//      transitioning back to no staged log)
+//    - if the upload fails, we keep the log to try again later.
+//    We do not try to read the metrics that are stored on
+//    the disk as we want to avoid storing the metrics in memory.
+//
+// * if no staged logs are present:
+//    Read all metrics from the disk, aggregate them and try to send them.
+//    - if the upload succeeds, we discard the staged log (transitioning back
+//      to the no staged log state)
+//    - if the upload fails, we keep the staged log in memory to retry
+//      uploading later.
+//
+class UploadService : public base::HistogramFlattener {
+ public:
+  explicit UploadService(SystemProfileSetter* setter,
+                         MetricsLibraryInterface* metrics_lib,
+                         const std::string& server);
+
+  void Init(const base::TimeDelta& upload_interval,
+            const base::FilePath& metrics_directory);
+
+  // Starts a new log. The log needs to be regenerated after each successful
+  // launch as it is destroyed when staging the log.
+  void StartNewLog();
+
+  // Event callback for handling MessageLoop events.
+  void UploadEventCallback(const base::TimeDelta& interval);
+
+  // Triggers an upload event.
+  void UploadEvent();
+
+  // Sends the staged log.
+  void SendStagedLog();
+
+  // Implements inconsistency detection to match HistogramFlattener's
+  // interface.
+  void InconsistencyDetected(
+      base::HistogramBase::Inconsistency problem) override {}
+  void UniqueInconsistencyDetected(
+      base::HistogramBase::Inconsistency problem) override {}
+  void InconsistencyDetectedInLoggedCount(int amount) override {}
+
+ private:
+  friend class UploadServiceTest;
+
+  FRIEND_TEST(UploadServiceTest, CanSendMultipleTimes);
+  FRIEND_TEST(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload);
+  FRIEND_TEST(UploadServiceTest, EmptyLogsAreNotSent);
+  FRIEND_TEST(UploadServiceTest, FailedSendAreRetried);
+  FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
+  FRIEND_TEST(UploadServiceTest, LogEmptyAfterUpload);
+  FRIEND_TEST(UploadServiceTest, LogEmptyByDefault);
+  FRIEND_TEST(UploadServiceTest, LogFromTheMetricsLibrary);
+  FRIEND_TEST(UploadServiceTest, LogKernelCrash);
+  FRIEND_TEST(UploadServiceTest, LogUncleanShutdown);
+  FRIEND_TEST(UploadServiceTest, LogUserCrash);
+  FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored);
+  FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
+
+  // Private constructor for use in unit testing.
+  UploadService(SystemProfileSetter* setter,
+                MetricsLibraryInterface* metrics_lib,
+                const std::string& server,
+                bool testing);
+
+  // If a staged log fails to upload more than kMaxFailedUpload times, it
+  // will be discarded.
+  static const int kMaxFailedUpload;
+
+  // Resets the internal state.
+  void Reset();
+
+  // Reads all the metrics from the disk.
+  void ReadMetrics();
+
+  // Adds a generic sample to the current log.
+  void AddSample(const metrics::MetricSample& sample);
+
+  // Adds a crash to the current log.
+  void AddCrash(const std::string& crash_name);
+
+  // Aggregates all histogram available in memory and store them in the current
+  // log.
+  void GatherHistograms();
+
+  // Callback for HistogramSnapshotManager to store the histograms.
+  void RecordDelta(const base::HistogramBase& histogram,
+                   const base::HistogramSamples& snapshot) override;
+
+  // Compiles all the samples received into a single protobuf and adds all
+  // system information.
+  void StageCurrentLog();
+
+  // Returns true iff a log is staged.
+  bool HasStagedLog();
+
+  // Remove the staged log iff the upload failed more than |kMaxFailedUpload|.
+  void RemoveFailedLog();
+
+  // Returns the current log. If there is no current log, creates it first.
+  MetricsLog* GetOrCreateCurrentLog();
+
+  scoped_ptr<SystemProfileSetter> system_profile_setter_;
+  MetricsLibraryInterface* metrics_lib_;
+  base::HistogramSnapshotManager histogram_snapshot_manager_;
+  scoped_ptr<Sender> sender_;
+  chromeos_metrics::PersistentInteger failed_upload_count_;
+  scoped_ptr<MetricsLog> current_log_;
+
+  base::FilePath metrics_file_;
+  base::FilePath staged_log_path_;
+
+  bool testing_;
+};
+
+#endif  // METRICS_UPLOADER_UPLOAD_SERVICE_H_
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
new file mode 100644 (file)
index 0000000..47e7b91
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <base/at_exit.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/logging.h>
+#include <base/sys_info.h>
+
+#include "constants.h"
+#include "metrics/metrics_library_mock.h"
+#include "persistent_integer.h"
+#include "serialization/metric_sample.h"
+#include "uploader/metrics_log.h"
+#include "uploader/mock/mock_system_profile_setter.h"
+#include "uploader/mock/sender_mock.h"
+#include "uploader/proto/chrome_user_metrics_extension.pb.h"
+#include "uploader/proto/histogram_event.pb.h"
+#include "uploader/proto/system_profile.pb.h"
+#include "uploader/system_profile_cache.h"
+#include "uploader/upload_service.h"
+
+class UploadServiceTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    CHECK(dir_.CreateUniqueTempDir());
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        dir_.path().value());
+    metrics_lib_.InitForTest(dir_.path());
+    ASSERT_EQ(0, base::WriteFile(
+        dir_.path().Append(metrics::kConsentFileName), "", 0));
+    upload_service_.reset(new UploadService(new MockSystemProfileSetter(),
+                                            &metrics_lib_, "", true));
+
+    upload_service_->sender_.reset(new SenderMock);
+    upload_service_->Init(base::TimeDelta::FromMinutes(30), dir_.path());
+    upload_service_->GatherHistograms();
+    upload_service_->Reset();
+  }
+
+  scoped_ptr<metrics::MetricSample> Crash(const std::string& name) {
+    return metrics::MetricSample::CrashSample(name);
+  }
+
+  void SetTestingProperty(const std::string& name, const std::string& value) {
+    base::FilePath filepath = dir_.path().Append("etc/os-release.d").Append(name);
+    ASSERT_TRUE(base::CreateDirectory(filepath.DirName()));
+    ASSERT_EQ(
+        value.size(),
+        base::WriteFile(filepath, value.data(), value.size()));
+  }
+
+  base::ScopedTempDir dir_;
+  scoped_ptr<UploadService> upload_service_;
+  MetricsLibrary metrics_lib_;
+
+  scoped_ptr<base::AtExitManager> exit_manager_;
+};
+
+// Tests that the right crash increments a values.
+TEST_F(UploadServiceTest, LogUserCrash) {
+  upload_service_->AddSample(*Crash("user").get());
+
+  MetricsLog* log = upload_service_->current_log_.get();
+  metrics::ChromeUserMetricsExtension* proto = log->uma_proto();
+
+  EXPECT_EQ(1, proto->system_profile().stability().other_user_crash_count());
+}
+
+TEST_F(UploadServiceTest, LogUncleanShutdown) {
+  upload_service_->AddSample(*Crash("uncleanshutdown"));
+
+  EXPECT_EQ(1, upload_service_->current_log_
+                   ->uma_proto()
+                   ->system_profile()
+                   .stability()
+                   .unclean_system_shutdown_count());
+}
+
+TEST_F(UploadServiceTest, LogKernelCrash) {
+  upload_service_->AddSample(*Crash("kernel"));
+
+  EXPECT_EQ(1, upload_service_->current_log_
+                   ->uma_proto()
+                   ->system_profile()
+                   .stability()
+                   .kernel_crash_count());
+}
+
+TEST_F(UploadServiceTest, UnknownCrashIgnored) {
+  upload_service_->AddSample(*Crash("foo"));
+
+  // The log should be empty.
+  EXPECT_FALSE(upload_service_->current_log_);
+}
+
+TEST_F(UploadServiceTest, FailedSendAreRetried) {
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+
+  sender->set_should_succeed(false);
+
+  upload_service_->AddSample(*Crash("user"));
+  upload_service_->UploadEvent();
+  EXPECT_EQ(1, sender->send_call_count());
+  std::string sent_string = sender->last_message();
+
+  upload_service_->UploadEvent();
+  EXPECT_EQ(2, sender->send_call_count());
+  EXPECT_EQ(sent_string, sender->last_message());
+}
+
+TEST_F(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload) {
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+
+  sender->set_should_succeed(false);
+
+  upload_service_->AddSample(*Crash("user"));
+
+  for (int i = 0; i < UploadService::kMaxFailedUpload; i++) {
+    upload_service_->UploadEvent();
+  }
+
+  EXPECT_TRUE(upload_service_->HasStagedLog());
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->HasStagedLog());
+
+  // Log a new sample. The failed upload counter should be reset.
+  upload_service_->AddSample(*Crash("user"));
+  for (int i = 0; i < UploadService::kMaxFailedUpload; i++) {
+    upload_service_->UploadEvent();
+  }
+  // The log is not discarded after multiple failed uploads.
+  EXPECT_TRUE(upload_service_->HasStagedLog());
+}
+
+TEST_F(UploadServiceTest, EmptyLogsAreNotSent) {
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->current_log_);
+  EXPECT_EQ(0, sender->send_call_count());
+}
+
+TEST_F(UploadServiceTest, LogEmptyByDefault) {
+  UploadService upload_service(new MockSystemProfileSetter(), &metrics_lib_,
+                               "");
+
+  // current_log_ should be initialized later as it needs AtExitManager to exit
+  // in order to gather system information from SysInfo.
+  EXPECT_FALSE(upload_service.current_log_);
+}
+
+TEST_F(UploadServiceTest, CanSendMultipleTimes) {
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+
+  upload_service_->AddSample(*Crash("user"));
+  upload_service_->UploadEvent();
+
+  std::string first_message = sender->last_message();
+
+  upload_service_->AddSample(*Crash("kernel"));
+  upload_service_->UploadEvent();
+
+  EXPECT_NE(first_message, sender->last_message());
+}
+
+TEST_F(UploadServiceTest, LogEmptyAfterUpload) {
+  upload_service_->AddSample(*Crash("user"));
+
+  EXPECT_TRUE(upload_service_->current_log_);
+
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->current_log_);
+}
+
+TEST_F(UploadServiceTest, LogContainsAggregatedValues) {
+  scoped_ptr<metrics::MetricSample> histogram =
+      metrics::MetricSample::HistogramSample("foo", 10, 0, 42, 10);
+  upload_service_->AddSample(*histogram.get());
+
+
+  scoped_ptr<metrics::MetricSample> histogram2 =
+      metrics::MetricSample::HistogramSample("foo", 11, 0, 42, 10);
+  upload_service_->AddSample(*histogram2.get());
+
+  upload_service_->GatherHistograms();
+  metrics::ChromeUserMetricsExtension* proto =
+      upload_service_->current_log_->uma_proto();
+  EXPECT_EQ(1, proto->histogram_event().size());
+}
+
+TEST_F(UploadServiceTest, ExtractChannelFromString) {
+  EXPECT_EQ(
+      SystemProfileCache::ProtoChannelFromString(
+          "developer-build"),
+      metrics::SystemProfileProto::CHANNEL_UNKNOWN);
+
+  EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_DEV,
+            SystemProfileCache::ProtoChannelFromString("dev-channel"));
+
+  EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_STABLE,
+            SystemProfileCache::ProtoChannelFromString("stable-channel"));
+
+  EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_UNKNOWN,
+            SystemProfileCache::ProtoChannelFromString("this is a test"));
+}
+
+TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) {
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+
+  SetTestingProperty(metrics::kProductId, "hello");
+  SetTestingProperty(metrics::kProductVersion, "1.2.3.4");
+
+  scoped_ptr<metrics::MetricSample> histogram =
+      metrics::MetricSample::SparseHistogramSample("myhistogram", 1);
+  // Reset to create the new log with the profile setter.
+  upload_service_->system_profile_setter_.reset(
+      new SystemProfileCache(true, dir_.path()));
+  upload_service_->Reset();
+  upload_service_->AddSample(*histogram.get());
+  upload_service_->UploadEvent();
+
+  EXPECT_EQ(1, sender->send_call_count());
+  EXPECT_TRUE(sender->is_good_proto());
+  EXPECT_EQ(1, sender->last_message_proto().histogram_event().size());
+
+  EXPECT_NE(0, sender->last_message_proto().client_id());
+  EXPECT_NE(0, sender->last_message_proto().system_profile().build_timestamp());
+  EXPECT_NE(0, sender->last_message_proto().session_id());
+}
+
+TEST_F(UploadServiceTest, PersistentGUID) {
+  std::string tmp_file = dir_.path().Append("tmpfile").value();
+
+  std::string first_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
+  std::string second_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
+
+  // The GUID are cached.
+  EXPECT_EQ(first_guid, second_guid);
+
+  base::DeleteFile(base::FilePath(tmp_file), false);
+
+  first_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
+  base::DeleteFile(base::FilePath(tmp_file), false);
+  second_guid = SystemProfileCache::GetPersistentGUID(tmp_file);
+
+  // Random GUIDs are generated (not all the same).
+  EXPECT_NE(first_guid, second_guid);
+}
+
+TEST_F(UploadServiceTest, SessionIdIncrementedAtInitialization) {
+  SetTestingProperty(metrics::kProductId, "hello");
+  SystemProfileCache cache(true, dir_.path());
+  cache.Initialize();
+  int session_id = cache.profile_.session_id;
+  cache.initialized_ = false;
+  cache.Initialize();
+  EXPECT_EQ(cache.profile_.session_id, session_id + 1);
+}
+
+// Test that we can log metrics from the metrics library and have the uploader
+// upload them.
+TEST_F(UploadServiceTest, LogFromTheMetricsLibrary) {
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+
+  upload_service_->UploadEvent();
+  EXPECT_EQ(0, sender->send_call_count());
+
+  metrics_lib_.SendEnumToUMA("testname", 2, 10);
+  upload_service_->UploadEvent();
+
+  EXPECT_EQ(1, sender->send_call_count());
+}
index 0c9b0c6..8661d7d 100644 (file)
@@ -2,12 +2,10 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := mkbootimg.c
-LOCAL_STATIC_LIBRARIES := libmincrypt
-LOCAL_CFLAGS := -Werror
+LOCAL_SRC_FILES := mkbootimg
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_IS_HOST_MODULE := true
 
 LOCAL_MODULE := mkbootimg
 
-include $(BUILD_HOST_EXECUTABLE)
-
-$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE))
+include $(BUILD_PREBUILT)
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
new file mode 100755 (executable)
index 0000000..aea2585
--- /dev/null
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+# Copyright 2015, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import print_function
+from sys import argv, exit, stderr
+from argparse import ArgumentParser, FileType, Action
+from os import fstat
+from struct import pack
+from hashlib import sha1
+
+def filesize(f):
+    if f is None:
+        return 0
+    try:
+        return fstat(f.fileno()).st_size
+    except OSError:
+        return 0
+
+
+def update_sha(sha, f):
+    if f:
+        sha.update(f.read())
+        f.seek(0)
+        sha.update(pack('I', filesize(f)))
+    else:
+        sha.update(pack('I', 0))
+
+
+def pad_file(f, padding):
+    pad = (padding - (f.tell() & (padding - 1))) & (padding - 1)
+    f.write(pack(str(pad) + 'x'))
+
+
+def write_header(args):
+    BOOT_MAGIC = 'ANDROID!'.encode()
+    args.output.write(pack('8s', BOOT_MAGIC))
+    args.output.write(pack('8I',
+        filesize(args.kernel),                          # size in bytes
+        args.base + args.kernel_offset,                 # physical load addr
+        filesize(args.ramdisk),                         # size in bytes
+        args.base + args.ramdisk_offset,                # physical load addr
+        filesize(args.second),                          # size in bytes
+        args.base + args.second_offset,                 # physical load addr
+        args.base + args.tags_offset,                   # physical addr for kernel tags
+        args.pagesize))                                 # flash page size we assume
+    args.output.write(pack('8x'))                       # future expansion: should be 0
+    args.output.write(pack('16s', args.board.encode())) # asciiz product name
+    args.output.write(pack('512s', args.cmdline[:512].encode()))
+
+    sha = sha1()
+    update_sha(sha, args.kernel)
+    update_sha(sha, args.ramdisk)
+    update_sha(sha, args.second)
+    img_id = pack('32s', sha.digest())
+
+    args.output.write(img_id)
+    args.output.write(pack('1024s', args.cmdline[512:].encode()))
+    pad_file(args.output, args.pagesize)
+    return img_id
+
+
+class ValidateStrLenAction(Action):
+    def __init__(self, option_strings, dest, nargs=None, **kwargs):
+        if 'maxlen' not in kwargs:
+            raise ValueError('maxlen must be set')
+        self.maxlen = int(kwargs['maxlen'])
+        del kwargs['maxlen']
+        super(ValidateStrLenAction, self).__init__(option_strings, dest, **kwargs)
+
+    def __call__(self, parser, namespace, values, option_string=None):
+        if len(values) > self.maxlen:
+            raise ValueError('String argument too long: max {0:d}, got {1:d}'.
+                format(self.maxlen, len(values)))
+        setattr(namespace, self.dest, values)
+
+
+def write_padded_file(f_out, f_in, padding):
+    if f_in is None:
+        return
+    f_out.write(f_in.read())
+    pad_file(f_out, padding)
+
+
+def parse_int(x):
+    return int(x, 0)
+
+
+def parse_cmdline():
+    parser = ArgumentParser()
+    parser.add_argument('--kernel', help='path to the kernel', type=FileType('rb'),
+                        required=True)
+    parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
+    parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
+    parser.add_argument('--cmdline', help='extra arguments to be passed on the '
+                        'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
+    parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
+    parser.add_argument('--kernel_offset', help='kernel offset', type=parse_int, default=0x00008000)
+    parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
+    parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
+                        default=0x00f00000)
+    parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
+    parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
+                        maxlen=16)
+    parser.add_argument('--pagesize', help='page size', type=parse_int,
+                        choices=[2**i for i in range(11,15)], default=2048)
+    parser.add_argument('--id', help='print the image ID on standard output',
+                        action='store_true')
+    parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'),
+                        required=True)
+    return parser.parse_args()
+
+
+def write_data(args):
+    write_padded_file(args.output, args.kernel, args.pagesize)
+    write_padded_file(args.output, args.ramdisk, args.pagesize)
+    write_padded_file(args.output, args.second, args.pagesize)
+
+
+def main():
+    args = parse_cmdline()
+    img_id = write_header(args)
+    write_data(args)
+    if args.id:
+        print('0x' + ''.join('{:02x}'.format(ord(c)) for c in img_id))
+
+
+if __name__ == '__main__':
+    main()
diff --git a/mkbootimg/mkbootimg.c b/mkbootimg/mkbootimg.c
deleted file mode 100644 (file)
index b6a2801..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-/* tools/mkbootimg/mkbootimg.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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdbool.h>
-
-#include "mincrypt/sha.h"
-#include "bootimg.h"
-
-static 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);
-    if(data == 0) goto oops;
-
-    if(read(fd, data, sz) != sz) goto oops;
-    close(fd);
-
-    if(_sz) *_sz = sz;
-    return data;
-
-oops:
-    close(fd);
-    if(data != 0) free(data);
-    return 0;
-}
-
-int usage(void)
-{
-    fprintf(stderr,"usage: mkbootimg\n"
-            "       --kernel <filename>\n"
-            "       [ --ramdisk <filename> ]\n"
-            "       [ --second <2ndbootloader-filename> ]\n"
-            "       [ --cmdline <kernel-commandline> ]\n"
-            "       [ --board <boardname> ]\n"
-            "       [ --base <address> ]\n"
-            "       [ --pagesize <pagesize> ]\n"
-            "       [ --id ]\n"
-            "       -o|--output <filename>\n"
-            );
-    return 1;
-}
-
-
-
-static unsigned char padding[16384] = { 0, };
-
-static void print_id(const uint8_t *id, size_t id_len) {
-    printf("0x");
-    for (unsigned i = 0; i < id_len; i++) {
-        printf("%02x", id[i]);
-    }
-    printf("\n");
-}
-
-int write_padding(int fd, unsigned pagesize, unsigned itemsize)
-{
-    unsigned pagemask = pagesize - 1;
-    ssize_t count;
-
-    if((itemsize & pagemask) == 0) {
-        return 0;
-    }
-
-    count = pagesize - (itemsize & pagemask);
-
-    if(write(fd, padding, count) != count) {
-        return -1;
-    } else {
-        return 0;
-    }
-}
-
-int main(int argc, char **argv)
-{
-    boot_img_hdr hdr;
-
-    char *kernel_fn = NULL;
-    void *kernel_data = NULL;
-    char *ramdisk_fn = NULL;
-    void *ramdisk_data = NULL;
-    char *second_fn = NULL;
-    void *second_data = NULL;
-    char *cmdline = "";
-    char *bootimg = NULL;
-    char *board = "";
-    uint32_t pagesize = 2048;
-    int fd;
-    SHA_CTX ctx;
-    const uint8_t* sha;
-    uint32_t base           = 0x10000000U;
-    uint32_t kernel_offset  = 0x00008000U;
-    uint32_t ramdisk_offset = 0x01000000U;
-    uint32_t second_offset  = 0x00f00000U;
-    uint32_t tags_offset    = 0x00000100U;
-    size_t cmdlen;
-
-    argc--;
-    argv++;
-
-    memset(&hdr, 0, sizeof(hdr));
-
-    bool get_id = false;
-    while(argc > 0){
-        char *arg = argv[0];
-        if (!strcmp(arg, "--id")) {
-            get_id = true;
-            argc -= 1;
-            argv += 1;
-        } else if(argc >= 2) {
-            char *val = argv[1];
-            argc -= 2;
-            argv += 2;
-            if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) {
-                bootimg = val;
-            } else if(!strcmp(arg, "--kernel")) {
-                kernel_fn = val;
-            } else if(!strcmp(arg, "--ramdisk")) {
-                ramdisk_fn = val;
-            } else if(!strcmp(arg, "--second")) {
-                second_fn = val;
-            } else if(!strcmp(arg, "--cmdline")) {
-                cmdline = val;
-            } else if(!strcmp(arg, "--base")) {
-                base = strtoul(val, 0, 16);
-            } else if(!strcmp(arg, "--kernel_offset")) {
-                kernel_offset = strtoul(val, 0, 16);
-            } else if(!strcmp(arg, "--ramdisk_offset")) {
-                ramdisk_offset = strtoul(val, 0, 16);
-            } else if(!strcmp(arg, "--second_offset")) {
-                second_offset = strtoul(val, 0, 16);
-            } else if(!strcmp(arg, "--tags_offset")) {
-                tags_offset = strtoul(val, 0, 16);
-            } else if(!strcmp(arg, "--board")) {
-                board = val;
-            } else if(!strcmp(arg,"--pagesize")) {
-                pagesize = strtoul(val, 0, 10);
-                if ((pagesize != 2048) && (pagesize != 4096)
-                    && (pagesize != 8192) && (pagesize != 16384)) {
-                    fprintf(stderr,"error: unsupported page size %d\n", pagesize);
-                    return -1;
-                }
-            } else {
-                return usage();
-            }
-        } else {
-            return usage();
-        }
-    }
-    hdr.page_size = pagesize;
-
-    hdr.kernel_addr =  base + kernel_offset;
-    hdr.ramdisk_addr = base + ramdisk_offset;
-    hdr.second_addr =  base + second_offset;
-    hdr.tags_addr =    base + tags_offset;
-
-    if(bootimg == 0) {
-        fprintf(stderr,"error: no output filename specified\n");
-        return usage();
-    }
-
-    if(kernel_fn == 0) {
-        fprintf(stderr,"error: no kernel image specified\n");
-        return usage();
-    }
-
-    if(strlen(board) >= BOOT_NAME_SIZE) {
-        fprintf(stderr,"error: board name too large\n");
-        return usage();
-    }
-
-    strcpy((char *) hdr.name, board);
-
-    memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);
-
-    cmdlen = strlen(cmdline);
-    if(cmdlen > (BOOT_ARGS_SIZE + BOOT_EXTRA_ARGS_SIZE - 2)) {
-        fprintf(stderr,"error: kernel commandline too large\n");
-        return 1;
-    }
-    /* Even if we need to use the supplemental field, ensure we
-     * are still NULL-terminated */
-    strncpy((char *)hdr.cmdline, cmdline, BOOT_ARGS_SIZE - 1);
-    hdr.cmdline[BOOT_ARGS_SIZE - 1] = '\0';
-    if (cmdlen >= (BOOT_ARGS_SIZE - 1)) {
-        cmdline += (BOOT_ARGS_SIZE - 1);
-        strncpy((char *)hdr.extra_cmdline, cmdline, BOOT_EXTRA_ARGS_SIZE);
-    }
-
-    kernel_data = load_file(kernel_fn, &hdr.kernel_size);
-    if(kernel_data == 0) {
-        fprintf(stderr,"error: could not load kernel '%s'\n", kernel_fn);
-        return 1;
-    }
-
-    if(ramdisk_fn == 0) {
-        ramdisk_data = 0;
-        hdr.ramdisk_size = 0;
-    } else {
-        ramdisk_data = load_file(ramdisk_fn, &hdr.ramdisk_size);
-        if(ramdisk_data == 0) {
-            fprintf(stderr,"error: could not load ramdisk '%s'\n", ramdisk_fn);
-            return 1;
-        }
-    }
-
-    if(second_fn) {
-        second_data = load_file(second_fn, &hdr.second_size);
-        if(second_data == 0) {
-            fprintf(stderr,"error: could not load secondstage '%s'\n", second_fn);
-            return 1;
-        }
-    }
-
-    /* put a hash of the contents in the header so boot images can be
-     * differentiated based on their first 2k.
-     */
-    SHA_init(&ctx);
-    SHA_update(&ctx, kernel_data, hdr.kernel_size);
-    SHA_update(&ctx, &hdr.kernel_size, sizeof(hdr.kernel_size));
-    SHA_update(&ctx, ramdisk_data, hdr.ramdisk_size);
-    SHA_update(&ctx, &hdr.ramdisk_size, sizeof(hdr.ramdisk_size));
-    SHA_update(&ctx, second_data, hdr.second_size);
-    SHA_update(&ctx, &hdr.second_size, sizeof(hdr.second_size));
-    sha = SHA_final(&ctx);
-    memcpy(hdr.id, sha,
-           SHA_DIGEST_SIZE > sizeof(hdr.id) ? sizeof(hdr.id) : SHA_DIGEST_SIZE);
-
-    fd = open(bootimg, O_CREAT | O_TRUNC | O_WRONLY, 0644);
-    if(fd < 0) {
-        fprintf(stderr,"error: could not create '%s'\n", bootimg);
-        return 1;
-    }
-
-    if(write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) goto fail;
-    if(write_padding(fd, pagesize, sizeof(hdr))) goto fail;
-
-    if(write(fd, kernel_data, hdr.kernel_size) != (ssize_t) hdr.kernel_size) goto fail;
-    if(write_padding(fd, pagesize, hdr.kernel_size)) goto fail;
-
-    if(write(fd, ramdisk_data, hdr.ramdisk_size) != (ssize_t) hdr.ramdisk_size) goto fail;
-    if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail;
-
-    if(second_data) {
-        if(write(fd, second_data, hdr.second_size) != (ssize_t) hdr.second_size) goto fail;
-        if(write_padding(fd, pagesize, hdr.second_size)) goto fail;
-    }
-
-    if (get_id) {
-        print_id((uint8_t *) hdr.id, sizeof(hdr.id));
-    }
-
-    return 0;
-
-fail:
-    unlink(bootimg);
-    close(fd);
-    fprintf(stderr,"error: failed writing '%s': %s\n", bootimg,
-            strerror(errno));
-    return 1;
-}
index ab3220e..b134f93 100644 (file)
@@ -2,8 +2,6 @@ LOCAL_PATH:= $(call my-dir)
 
 #######################################
 # init.rc
-# Only copy init.rc if the target doesn't have its own.
-ifneq ($(TARGET_PROVIDES_INIT_RC),true)
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := init.rc
@@ -12,7 +10,20 @@ LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
 
 include $(BUILD_PREBUILT)
+
+#######################################
+# asan.options
+ifneq ($(filter address,$(SANITIZE_TARGET)),)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := asan.options
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(TARGET_OUT)
+
+include $(BUILD_PREBUILT)
 endif
+
 #######################################
 # init.environ.rc
 
@@ -21,6 +32,11 @@ LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE := init.environ.rc
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
 
+EXPORT_GLOBAL_ASAN_OPTIONS :=
+ifneq ($(filter address,$(SANITIZE_TARGET)),)
+  EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS include=/system/asan.options
+  LOCAL_REQUIRED_MODULES := asan.options
+endif
 # Put it here instead of in init.rc module definition,
 # because init.rc is conditionally included.
 #
@@ -54,6 +70,7 @@ $(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in $(bcp_dep)
        @mkdir -p $(dir $@)
        $(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
        $(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
+       $(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
 
 bcp_md5 :=
 bcp_dep :=
diff --git a/rootdir/asan.options b/rootdir/asan.options
new file mode 100644 (file)
index 0000000..43896a1
--- /dev/null
@@ -0,0 +1,5 @@
+allow_user_segv_handler=1
+detect_odr_violation=0
+alloc_dealloc_mismatch=0
+allocator_may_return_null=1
+detect_container_overflow=0
index b34ea01..32817fa 100644 (file)
@@ -9,3 +9,4 @@ on init
     export ASEC_MOUNTPOINT /mnt/asec
     export BOOTCLASSPATH %BOOTCLASSPATH%
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
+    %EXPORT_GLOBAL_ASAN_OPTIONS%
index b8df647..60688a3 100644 (file)
@@ -9,15 +9,20 @@ import /init.usb.rc
 import /init.${ro.hardware}.rc
 import /init.usb.configfs.rc
 import /init.${ro.zygote}.rc
-import /init.trace.rc
 
 on early-init
     # Set init and its forked children's oom_adj.
     write /proc/1/oom_score_adj -1000
 
+    # Disable sysrq from keyboard
+    write /proc/sys/kernel/sysrq 0
+
     # Set the security context of /adb_keys if present.
     restorecon /adb_keys
 
+    # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
+    mkdir /mnt 0775 root system
+
     start ueventd
 
 on init
@@ -107,7 +112,7 @@ on init
     write /proc/sys/kernel/kptr_restrict 2
     write /proc/sys/vm/mmap_min_addr 32768
     write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
-    write /proc/sys/net/unix/max_dgram_qlen 300
+    write /proc/sys/net/unix/max_dgram_qlen 600
     write /proc/sys/kernel/sched_rt_runtime_us 950000
     write /proc/sys/kernel/sched_rt_period_us 1000000
 
@@ -294,14 +299,14 @@ on post-fs-data
     # We restorecon /data in case the userdata partition has been reset.
     restorecon /data
 
-    # Emulated internal storage area
-    mkdir /data/media 0770 media_rw media_rw
-
     # Make sure we have the device encryption key
     start logd
     start vold
     installkey /data
 
+    # Emulated internal storage area
+    mkdir /data/media 0770 media_rw media_rw
+
     # Start bootcharting as soon as possible after the data partition is
     # mounted to collect more data.
     mkdir /data/bootchart 0755 shell shell
@@ -312,7 +317,6 @@ on post-fs-data
 
     # create basic filesystem structure
     mkdir /data/misc 01771 system misc
-    mkdir /data/misc/adb 02750 system shell
     mkdir /data/misc/bluedroid 02770 bluetooth net_bt_stack
     # Fix the access permissions and group ownership for 'bt_config.conf'
     chmod 0660 /data/misc/bluedroid/bt_config.conf
@@ -340,6 +344,9 @@ on post-fs-data
     mkdir /data/local 0751 root root
     mkdir /data/misc/media 0700 media media
     mkdir /data/misc/vold 0700 root root
+    mkdir /data/misc/boottrace 0771 system shell
+    mkdir /data/misc/update_engine 0700 root root
+    mkdir /data/misc/trace 0700 root root
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
@@ -372,7 +379,7 @@ on post-fs-data
     # the following directory.
     mkdir /data/mediadrm 0770 mediadrm mediadrm
 
-    mkdir /data/adb 0700 root root
+    mkdir /data/anr 0775 system system
 
     # symlink to bugreport storage location
     symlink /data/data/com.android.shell/files/bugreports /data/bugreports
@@ -385,9 +392,13 @@ on post-fs-data
     mkdir /data/backup 0700 system system
     mkdir /data/media 0770 media_rw media_rw
     mkdir /data/ss 0700 system system
+
     mkdir /data/system 0775 system system
     mkdir /data/system/heapdump 0700 system system
+    mkdir /data/system_ce 0770 system system
+
     mkdir /data/user 0711 system system
+    mkdir /data/user_de 0711 system system
 
     setusercryptopolicies /data/user
 
@@ -558,19 +569,6 @@ service ueventd /sbin/ueventd
     critical
     seclabel u:r:ueventd:s0
 
-service logd /system/bin/logd
-    class core
-    socket logd stream 0666 logd logd
-    socket logdr seqpacket 0666 logd logd
-    socket logdw dgram 0222 logd logd
-    group root system
-     writepid /dev/cpuset/system-background/tasks
-
-service logd-reinit /system/bin/logd --reinit
-    oneshot
-    writepid /dev/cpuset/system-background/tasks
-    disabled
-
 service healthd /sbin/healthd
     class core
     critical
@@ -582,184 +580,15 @@ service console /system/bin/sh
     console
     disabled
     user shell
-    group shell log
+    group shell log readproc
     seclabel u:r:shell:s0
 
 on property:ro.debuggable=1
+    # Give writes to anyone for the trace folder on debug builds.
+    # The folder is used to store method traces.
+    chmod 0773 /data/misc/trace
     start console
 
-# adbd is controlled via property triggers in init.<platform>.usb.rc
-service adbd /sbin/adbd --root_seclabel=u:r:su:s0
-    class core
-    socket adbd stream 660 system system
-    disabled
-    seclabel u:r:adbd:s0
-
-# adbd on at boot in emulator
-on property:ro.kernel.qemu=1
-    start adbd
-
-service lmkd /system/bin/lmkd
-    class core
-    critical
-    socket lmkd seqpacket 0660 system system
-    writepid /dev/cpuset/system-background/tasks
-
-service servicemanager /system/bin/servicemanager
-    class core
-    user system
-    group system
-    critical
-    onrestart restart healthd
-    onrestart restart zygote
-    onrestart restart media
-    onrestart restart surfaceflinger
-    onrestart restart drm
-
-service vold /system/bin/vold \
-        --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \
-        --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0
-    class core
-    socket vold stream 0660 root mount
-    socket cryptd stream 0660 root mount
-    ioprio be 2
-
-service netd /system/bin/netd
-    class main
-    socket netd stream 0660 root system
-    socket dnsproxyd stream 0660 root inet
-    socket mdns stream 0660 root system
-    socket fwmarkd stream 0660 root inet
-
-service debuggerd /system/bin/debuggerd
-    class main
-    writepid /dev/cpuset/system-background/tasks
-
-service debuggerd64 /system/bin/debuggerd64
-    class main
-    writepid /dev/cpuset/system-background/tasks
-
-service ril-daemon /system/bin/rild
-    class main
-    socket rild stream 660 root radio
-    socket sap_uim_socket1 stream 660 bluetooth bluetooth
-    socket rild-debug stream 660 radio system
-    user root
-    group radio cache inet misc audio log
-
-service surfaceflinger /system/bin/surfaceflinger
-    class core
-    user system
-    group graphics drmrpc
-    onrestart restart zygote
-    writepid /sys/fs/cgroup/stune/foreground/tasks /dev/cpuset/system-background/tasks
-
-service drm /system/bin/drmserver
-    class main
-    user drm
-    group drm system inet drmrpc
-
-service media /system/bin/mediaserver
-    class main
-    user media
-    group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
-    ioprio rt 4
-
-# One shot invocation to deal with encrypted volume.
-service defaultcrypto /system/bin/vdc --wait cryptfs mountdefaultencrypted
-    disabled
-    oneshot
-    # vold will set vold.decrypt to trigger_restart_framework (default
-    # encryption) or trigger_restart_min_framework (other encryption)
-
-# One shot invocation to encrypt unencrypted volumes
-service encrypt /system/bin/vdc --wait cryptfs enablecrypto inplace default noui
-    disabled
-    oneshot
-    # vold will set vold.decrypt to trigger_restart_framework (default
-    # encryption)
-
-service bootanim /system/bin/bootanimation
-    class core
-    user graphics
-    group graphics audio
-    disabled
-    oneshot
-
-service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper
-    class late_start
-    user system
-
-service installd /system/bin/installd
-    class main
-    socket installd stream 600 system system
-
 service flash_recovery /system/bin/install-recovery.sh
     class main
     oneshot
-
-service racoon /system/bin/racoon
-    class main
-    socket racoon stream 600 system system
-    # IKE uses UDP port 500. Racoon will setuid to vpn after binding the port.
-    group vpn net_admin inet
-    disabled
-    oneshot
-
-service mtpd /system/bin/mtpd
-    class main
-    socket mtpd stream 600 system system
-    user vpn
-    group vpn net_admin inet net_raw
-    disabled
-    oneshot
-
-service keystore /system/bin/keystore /data/misc/keystore
-    class main
-    user keystore
-    group keystore drmrpc
-
-service dumpstate /system/bin/dumpstate -s
-    class main
-    socket dumpstate stream 0660 shell log
-    disabled
-    oneshot
-
-service mdnsd /system/bin/mdnsd
-    class main
-    user mdnsr
-    group inet net_raw
-    socket mdnsd stream 0660 mdnsr inet
-    disabled
-    oneshot
-
-service uncrypt /system/bin/uncrypt
-    class main
-    disabled
-    oneshot
-
-service pre-recovery /system/bin/uncrypt --reboot
-    class main
-    disabled
-    oneshot
-
-service perfprofd /system/xbin/perfprofd
-    class late_start
-    user root
-    oneshot
-    writepid /dev/cpuset/system-background/tasks
-
-on property:persist.logd.logpersistd=logcatd
-    # all exec/services are called with umask(077), so no gain beyond 0700
-    mkdir /data/misc/logd 0700 logd log
-    # logd for write to /data/misc/logd, log group for read from pstore (-L)
-    exec - logd log -- /system/bin/logcat -L -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
-    start logcatd
-
-service logcatd /system/bin/logcat -b all -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 64 -n 256
-    class late_start
-    disabled
-    # logd for write to /data/misc/logd, log group for read from log daemon
-    user logd
-    group log
-    writepid /dev/cpuset/system-background/tasks
diff --git a/rootdir/init.trace.rc b/rootdir/init.trace.rc
deleted file mode 100644 (file)
index 50944e6..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-## Permissions to allow system-wide tracing to the kernel trace buffer.
-##
-on boot
-
-# Allow writing to the kernel trace log.
-    chmod 0222 /sys/kernel/debug/tracing/trace_marker
-
-# Allow the shell group to enable (some) kernel tracing.
-    chown root shell /sys/kernel/debug/tracing/trace_clock
-    chown root shell /sys/kernel/debug/tracing/buffer_size_kb
-    chown root shell /sys/kernel/debug/tracing/options/overwrite
-    chown root shell /sys/kernel/debug/tracing/options/print-tgid
-    chown root shell /sys/kernel/debug/tracing/events/sched/sched_switch/enable
-    chown root shell /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
-    chown root shell /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
-    chown root shell /sys/kernel/debug/tracing/events/power/cpu_idle/enable
-    chown root shell /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
-    chown root shell /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
-    chown root shell /sys/kernel/debug/tracing/tracing_on
-
-    chmod 0664 /sys/kernel/debug/tracing/trace_clock
-    chmod 0664 /sys/kernel/debug/tracing/buffer_size_kb
-    chmod 0664 /sys/kernel/debug/tracing/options/overwrite
-    chmod 0664 /sys/kernel/debug/tracing/options/print-tgid
-    chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_switch/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/sched/sched_wakeup/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/power/cpu_idle/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/power/clock_set_rate/enable
-    chmod 0664 /sys/kernel/debug/tracing/events/cpufreq_interactive/enable
-    chmod 0664 /sys/kernel/debug/tracing/tracing_on
-
-# Allow only the shell group to read and truncate the kernel trace.
-    chown root shell /sys/kernel/debug/tracing/trace
-    chmod 0660 /sys/kernel/debug/tracing/trace
index a105afe..1fd1e2a 100644 (file)
@@ -8,6 +8,19 @@ on post-fs-data
     chmod 0660 /sys/class/android_usb/android0/f_mass_storage/lun/file
     chown system system /sys/class/android_usb/android0/f_rndis/ethaddr
     chmod 0660 /sys/class/android_usb/android0/f_rndis/ethaddr
+    mkdir /data/misc/adb 02750 system shell
+    mkdir /data/adb 0700 root root
+
+# adbd is controlled via property triggers in init.<platform>.usb.rc
+service adbd /sbin/adbd --root_seclabel=u:r:su:s0
+    class core
+    socket adbd stream 660 system system
+    disabled
+    seclabel u:r:adbd:s0
+
+# adbd on at boot in emulator
+on property:ro.kernel.qemu=1
+    start adbd
 
 on boot
     setprop sys.usb.configfs 0
index 3f32e7d..f0fd2fe 100644 (file)
@@ -20,6 +20,8 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <paths.h>
+#include <pwd.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -193,10 +195,21 @@ int main(int argc, char **argv)
         panic("Could not set SELinux security context: %s\n", strerror(errno));
     }
 
-    /* cd into the data directory */
+    // cd into the data directory, and set $HOME correspondingly.
     if (TEMP_FAILURE_RETRY(chdir(info.dataDir)) < 0) {
         panic("Could not cd to package's data directory: %s\n", strerror(errno));
     }
+    setenv("HOME", info.dataDir, 1);
+
+    // Reset parts of the environment, like su would.
+    setenv("PATH", _PATH_DEFPATH, 1);
+    unsetenv("IFS");
+
+    // Set the user-specific parts for this user.
+    struct passwd* pw = getpwuid(uid);
+    setenv("LOGNAME", pw->pw_name, 1);
+    setenv("SHELL", pw->pw_shell, 1);
+    setenv("USER", pw->pw_name, 1);
 
     /* User specified command for exec. */
     if ((argc >= commandArgvOfs + 1) &&
index cb3a8fb..c5f3d1d 100644 (file)
@@ -5,7 +5,6 @@ include $(CLEAR_VARS)
 LOCAL_SRC_FILES := sdcard.c
 LOCAL_MODULE := sdcard
 LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
-
-LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := libcutils libpackagelistparser
 
 include $(BUILD_EXECUTABLE)
index a79e2dd..b6bbe7e 100644 (file)
@@ -24,6 +24,7 @@
 #include <limits.h>
 #include <linux/fuse.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -34,6 +35,7 @@
 #include <sys/stat.h>
 #include <sys/statfs.h>
 #include <sys/time.h>
+#include <sys/types.h>
 #include <sys/uio.h>
 #include <unistd.h>
 
@@ -41,6 +43,7 @@
 #include <cutils/hashmap.h>
 #include <cutils/log.h>
 #include <cutils/multiuser.h>
+#include <packagelistparser/packagelistparser.h>
 
 #include <private/android_filesystem_config.h>
 
  * or that a reply has already been written. */
 #define NO_STATUS 1
 
-/* Path to system-provided mapping of package name to appIds */
-static const char* const kPackagesListFile = "/data/system/packages.list";
-
 /* Supplementary groups to execute with */
 static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
 
@@ -244,7 +244,7 @@ struct fuse_handler {
      * buffer at the same time.  This allows us to share the underlying storage. */
     union {
         __u8 request_buffer[MAX_REQUEST_SIZE];
-        __u8 read_buffer[MAX_READ + PAGESIZE];
+        __u8 read_buffer[MAX_READ + PAGE_SIZE];
     };
 };
 
@@ -1215,7 +1215,7 @@ static int handle_read(struct fuse* fuse, struct fuse_handler* handler,
     __u32 size = req->size;
     __u64 offset = req->offset;
     int res;
-    __u8 *read_buffer = (__u8 *) ((uintptr_t)(handler->read_buffer + PAGESIZE) & ~((uintptr_t)PAGESIZE-1));
+    __u8 *read_buffer = (__u8 *) ((uintptr_t)(handler->read_buffer + PAGE_SIZE) & ~((uintptr_t)PAGE_SIZE-1));
 
     /* Don't access any other fields of hdr or req beyond this point, the read buffer
      * overlaps the request buffer and will clobber data in the request.  This
@@ -1241,7 +1241,7 @@ static int handle_write(struct fuse* fuse, struct fuse_handler* handler,
     struct fuse_write_out out;
     struct handle *h = id_to_ptr(req->fh);
     int res;
-    __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGESIZE)));
+    __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGE_SIZE)));
 
     if (req->flags & O_DIRECT) {
         memcpy(aligned_buffer, buffer, req->size);
@@ -1636,35 +1636,27 @@ static bool remove_str_to_int(void *key, void *value, void *context) {
     return true;
 }
 
-static int read_package_list(struct fuse_global* global) {
-    pthread_mutex_lock(&global->lock);
-
-    hashmapForEach(global->package_to_appid, remove_str_to_int, global->package_to_appid);
+static bool package_parse_callback(pkg_info *info, void *userdata) {
+    struct fuse_global *global = (struct fuse_global *)userdata;
 
-    FILE* file = fopen(kPackagesListFile, "r");
-    if (!file) {
-        ERROR("failed to open package list: %s\n", strerror(errno));
-        pthread_mutex_unlock(&global->lock);
-        return -1;
-    }
+    char* name = strdup(info->name);
+    hashmapPut(global->package_to_appid, name, (void*) (uintptr_t) info->uid);
+    packagelist_free(info);
+    return true;
+}
 
-    char buf[512];
-    while (fgets(buf, sizeof(buf), file) != NULL) {
-        char package_name[512];
-        int appid;
-        char gids[512];
+static bool read_package_list(struct fuse_global* global) {
+    pthread_mutex_lock(&global->lock);
 
-        if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) {
-            char* package_name_dup = strdup(package_name);
-            hashmapPut(global->package_to_appid, package_name_dup, (void*) (uintptr_t) appid);
-        }
-    }
+    hashmapForEach(global->package_to_appid, remove_str_to_int, global->package_to_appid);
 
+    bool rc = packagelist_parse(package_parse_callback, global);
     TRACE("read_package_list: found %zu packages\n",
             hashmapSize(global->package_to_appid));
-    fclose(file);
+
     pthread_mutex_unlock(&global->lock);
-    return 0;
+
+    return rc;
 }
 
 static void watch_package_list(struct fuse_global* global) {
@@ -1680,11 +1672,11 @@ static void watch_package_list(struct fuse_global* global) {
     bool active = false;
     while (1) {
         if (!active) {
-            int res = inotify_add_watch(nfd, kPackagesListFile, IN_DELETE_SELF);
+            int res = inotify_add_watch(nfd, PACKAGES_LIST_FILE, IN_DELETE_SELF);
             if (res == -1) {
                 if (errno == ENOENT || errno == EACCES) {
                     /* Framework may not have created yet, sleep and retry */
-                    ERROR("missing packages.list; retrying\n");
+                    ERROR("missing \"%s\"; retrying\n", PACKAGES_LIST_FILE);
                     sleep(3);
                     continue;
                 } else {
@@ -1695,8 +1687,8 @@ static void watch_package_list(struct fuse_global* global) {
 
             /* Watch above will tell us about any future changes, so
              * read the current state. */
-            if (read_package_list(global) == -1) {
-                ERROR("read_package_list failed: %s\n", strerror(errno));
+            if (read_package_list(global) == false) {
+                ERROR("read_package_list failed\n");
                 return;
             }
             active = true;
index ad99a39..52716e9 100644 (file)
@@ -2,8 +2,7 @@ LOCAL_PATH:= $(call my-dir)
 
 
 common_cflags := \
-    -std=gnu99 \
-    -Werror -Wno-unused-parameter \
+    -Werror -Wno-unused-parameter -Wno-unused-const-variable \
     -I$(LOCAL_PATH)/upstream-netbsd/include/ \
     -include bsd-compatibility.h \
 
@@ -25,48 +24,35 @@ LOCAL_CFLAGS += $(common_cflags) -Dmain=dd_main -DNO_CONV
 LOCAL_MODULE := libtoolbox_dd
 include $(BUILD_STATIC_LIBRARY)
 
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := upstream-netbsd/usr.bin/du/du.c
-LOCAL_CFLAGS += $(common_cflags) -Dmain=du_main
-LOCAL_MODULE := libtoolbox_du
-include $(BUILD_STATIC_LIBRARY)
-
 
 include $(CLEAR_VARS)
 
 BSD_TOOLS := \
     dd \
-    du \
 
 OUR_TOOLS := \
-    df \
     getevent \
     iftop \
     ioctl \
-    ionice \
     log \
-    ls \
-    lsof \
-    mount \
     nandread \
     newfs_msdos \
     ps \
     prlimit \
-    renice \
     sendevent \
     start \
     stop \
     top \
-    uptime \
-    watchprops \
 
 ALL_TOOLS = $(BSD_TOOLS) $(OUR_TOOLS)
 
 LOCAL_SRC_FILES := \
+    start_stop.cpp \
     toolbox.c \
     $(patsubst %,%.c,$(OUR_TOOLS)) \
 
 LOCAL_CFLAGS += $(common_cflags)
+LOCAL_CONLYFLAGS += -std=gnu99
 
 LOCAL_SHARED_LIBRARIES := \
     libcutils \
diff --git a/toolbox/df.c b/toolbox/df.c
deleted file mode 100644 (file)
index 9cd0743..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/statfs.h>
-
-static int ok = EXIT_SUCCESS;
-
-static void printsize(long long n)
-{
-    char unit = 'K';
-    long long t;
-
-    n *= 10;
-
-    if (n > 1024*1024*10) {
-        n /= 1024;
-        unit = 'M';
-    }
-
-    if (n > 1024*1024*10) {
-        n /= 1024;
-        unit = 'G';
-    }
-
-    t = (n + 512) / 1024;
-    printf("%4lld.%1lld%c", t/10, t%10, unit);
-}
-
-static void df(char *s, int always) {
-    struct statfs st;
-
-    if (statfs(s, &st) < 0) {
-        fprintf(stderr, "%s: %s\n", s, strerror(errno));
-        ok = EXIT_FAILURE;
-    } else {
-        if (st.f_blocks == 0 && !always)
-            return;        
-        printf("%-20s  ", s);
-        printsize((long long)st.f_blocks * (long long)st.f_bsize);
-        printf("  ");
-        printsize((long long)(st.f_blocks - (long long)st.f_bfree) * st.f_bsize);
-        printf("  ");
-        printsize((long long)st.f_bfree * (long long)st.f_bsize);
-        printf("   %d\n", (int) st.f_bsize);
-    }
-}
-
-int df_main(int argc, char *argv[]) {
-    printf("Filesystem               Size     Used     Free   Blksize\n");
-    if (argc == 1) {
-        char s[2000];
-        FILE *f = fopen("/proc/mounts", "r");
-
-        while (fgets(s, 2000, f)) {
-            char *c, *e = s;
-
-            for (c = s; *c; c++) {
-                if (*c == ' ') {
-                    e = c + 1;
-                    break;
-                }
-            }
-
-            for (c = e; *c; c++) {
-                if (*c == ' ') {
-                    *c = '\0';
-                    break;
-                }
-            }
-
-            df(e, 0);
-        }
-
-        fclose(f);
-    } else {
-        int i;
-
-        for (i = 1; i < argc; i++) {
-            df(argv[i], 1);
-        }
-    }
-
-    exit(ok);
-}
diff --git a/toolbox/ionice.c b/toolbox/ionice.c
deleted file mode 100644 (file)
index 7abc261..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include <cutils/iosched_policy.h>
-
-static char *classes[] = {"none", "rt", "be", "idle", NULL};
-
-int ionice_main(int argc, char *argv[])
-{
-    IoSchedClass clazz = IoSchedClass_NONE;
-    int ioprio = 0;
-    int pid;
-
-    if(argc != 2 && argc != 4) {
-        fprintf(stderr, "usage: ionice <pid> [none|rt|be|idle] [prio]\n");
-        return 1;
-    }
-
-    if (!(pid = atoi(argv[1]))) {
-        fprintf(stderr, "Invalid pid specified\n");
-        return 1;
-    }
-
-    if (argc == 2) {
-        if (android_get_ioprio(pid, &clazz, &ioprio)) {
-            fprintf(stderr, "Failed to read priority (%s)\n", strerror(errno));
-            return 1;
-        }
-        fprintf(stdout, "Pid %d, class %s (%d), prio %d\n", pid, classes[clazz], clazz, ioprio);
-        return 0;
-    }
-
-    if (!strcmp(argv[2], "none")) {
-        clazz = IoSchedClass_NONE;
-    } else if (!strcmp(argv[2], "rt")) {
-        clazz = IoSchedClass_RT;
-    } else if (!strcmp(argv[2], "be")) {
-        clazz = IoSchedClass_BE;
-    } else if (!strcmp(argv[2], "idle")) {
-        clazz = IoSchedClass_IDLE;
-    } else {
-        fprintf(stderr, "Unsupported class '%s'\n", argv[2]);
-        return 1;
-    }
-
-    ioprio = atoi(argv[3]);
-
-    printf("Setting pid %d i/o class to %d, prio %d\n", pid, clazz, ioprio);
-    if (android_set_ioprio(pid, clazz, ioprio)) {
-        fprintf(stderr, "Failed to set priority (%s)\n", strerror(errno));
-        return 1;
-    }
-
-    return 0;
-}
diff --git a/toolbox/ls.c b/toolbox/ls.c
deleted file mode 100644 (file)
index 9a89dd4..0000000
+++ /dev/null
@@ -1,588 +0,0 @@
-#include <dirent.h>
-#include <errno.h>
-#include <grp.h>
-#include <limits.h>
-#include <pwd.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/sysmacros.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <selinux/selinux.h>
-
-// simple dynamic array of strings.
-typedef struct {
-    int count;
-    int capacity;
-    void** items;
-} strlist_t;
-
-#define STRLIST_INITIALIZER { 0, 0, NULL }
-
-/* Used to iterate over a strlist_t
- * _list   :: pointer to strlist_t object
- * _item   :: name of local variable name defined within the loop with
- *            type 'char*'
- * _stmnt  :: C statement executed in each iteration
- *
- * This macro is only intended for simple uses. Do not add or remove items
- * to/from the list during iteration.
- */
-#define  STRLIST_FOREACH(_list,_item,_stmnt) \
-    do { \
-        int _nn_##__LINE__ = 0; \
-        for (;_nn_##__LINE__ < (_list)->count; ++ _nn_##__LINE__) { \
-            char* _item = (char*)(_list)->items[_nn_##__LINE__]; \
-            _stmnt; \
-        } \
-    } while (0)
-
-static void dynarray_reserve_more( strlist_t *a, int count ) {
-    int old_cap = a->capacity;
-    int new_cap = old_cap;
-    const int max_cap = INT_MAX/sizeof(void*);
-    void** new_items;
-    int new_count = a->count + count;
-
-    if (count <= 0)
-        return;
-
-    if (count > max_cap - a->count)
-        abort();
-
-    new_count = a->count + count;
-
-    while (new_cap < new_count) {
-        old_cap = new_cap;
-        new_cap += (new_cap >> 2) + 4;
-        if (new_cap < old_cap || new_cap > max_cap) {
-            new_cap = max_cap;
-        }
-    }
-    new_items = realloc(a->items, new_cap*sizeof(void*));
-    if (new_items == NULL)
-        abort();
-
-    a->items = new_items;
-    a->capacity = new_cap;
-}
-
-void strlist_init( strlist_t *list ) {
-    list->count = list->capacity = 0;
-    list->items = NULL;
-}
-
-// append a new string made of the first 'slen' characters from 'str'
-// followed by a trailing zero.
-void strlist_append_b( strlist_t *list, const void* str, size_t  slen ) {
-    char *copy = malloc(slen+1);
-    memcpy(copy, str, slen);
-    copy[slen] = '\0';
-    if (list->count >= list->capacity)
-        dynarray_reserve_more(list, 1);
-    list->items[list->count++] = copy;
-}
-
-// append the copy of a given input string to a strlist_t.
-void strlist_append_dup( strlist_t *list, const char *str) {
-    strlist_append_b(list, str, strlen(str));
-}
-
-// note: strlist_done will free all the strings owned by the list.
-void strlist_done( strlist_t *list ) {
-    STRLIST_FOREACH(list, string, free(string));
-    free(list->items);
-    list->items = NULL;
-    list->count = list->capacity = 0;
-}
-
-static int strlist_compare_strings(const void* a, const void* b) {
-    const char *sa = *(const char **)a;
-    const char *sb = *(const char **)b;
-    return strcmp(sa, sb);
-}
-
-/* sort the strings in a given list (using strcmp) */
-void strlist_sort( strlist_t *list ) {
-    if (list->count > 0) {
-        qsort(list->items, (size_t)list->count, sizeof(void*), strlist_compare_strings);
-    }
-}
-
-
-// bits for flags argument
-#define LIST_LONG           (1 << 0)
-#define LIST_ALL            (1 << 1)
-#define LIST_RECURSIVE      (1 << 2)
-#define LIST_DIRECTORIES    (1 << 3)
-#define LIST_SIZE           (1 << 4)
-#define LIST_LONG_NUMERIC   (1 << 5)
-#define LIST_CLASSIFY       (1 << 6)
-#define LIST_MACLABEL       (1 << 7)
-#define LIST_INODE          (1 << 8)
-
-// fwd
-static int listpath(const char *name, int flags);
-
-static char mode2kind(mode_t mode)
-{
-    switch(mode & S_IFMT){
-    case S_IFSOCK: return 's';
-    case S_IFLNK: return 'l';
-    case S_IFREG: return '-';
-    case S_IFDIR: return 'd';
-    case S_IFBLK: return 'b';
-    case S_IFCHR: return 'c';
-    case S_IFIFO: return 'p';
-    default: return '?';
-    }
-}
-
-void strmode(mode_t mode, char *out)
-{
-    *out++ = mode2kind(mode);
-
-    *out++ = (mode & 0400) ? 'r' : '-';
-    *out++ = (mode & 0200) ? 'w' : '-';
-    if(mode & 04000) {
-        *out++ = (mode & 0100) ? 's' : 'S';
-    } else {
-        *out++ = (mode & 0100) ? 'x' : '-';
-    }
-    *out++ = (mode & 040) ? 'r' : '-';
-    *out++ = (mode & 020) ? 'w' : '-';
-    if(mode & 02000) {
-        *out++ = (mode & 010) ? 's' : 'S';
-    } else {
-        *out++ = (mode & 010) ? 'x' : '-';
-    }
-    *out++ = (mode & 04) ? 'r' : '-';
-    *out++ = (mode & 02) ? 'w' : '-';
-    if(mode & 01000) {
-        *out++ = (mode & 01) ? 't' : 'T';
-    } else {
-        *out++ = (mode & 01) ? 'x' : '-';
-    }
-    *out = 0;
-}
-
-static void user2str(uid_t uid, char *out, size_t out_size)
-{
-    struct passwd *pw = getpwuid(uid);
-    if(pw) {
-        strlcpy(out, pw->pw_name, out_size);
-    } else {
-        snprintf(out, out_size, "%d", uid);
-    }
-}
-
-static void group2str(gid_t gid, char *out, size_t out_size)
-{
-    struct group *gr = getgrgid(gid);
-    if(gr) {
-        strlcpy(out, gr->gr_name, out_size);
-    } else {
-        snprintf(out, out_size, "%d", gid);
-    }
-}
-
-static int show_total_size(const char *dirname, DIR *d, int flags)
-{
-    struct dirent *de;
-    char tmp[1024];
-    struct stat s;
-    int sum = 0;
-
-    /* run through the directory and sum up the file block sizes */
-    while ((de = readdir(d)) != 0) {
-        if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
-            continue;
-        if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
-            continue;
-
-        if (strcmp(dirname, "/") == 0)
-            snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
-        else
-            snprintf(tmp, sizeof(tmp), "%s/%s", dirname, de->d_name);
-
-        if (lstat(tmp, &s) < 0) {
-            fprintf(stderr, "stat failed on %s: %s\n", tmp, strerror(errno));
-            rewinddir(d);
-            return -1;
-        }
-
-        sum += s.st_blocks / 2;
-    }
-
-    printf("total %d\n", sum);
-    rewinddir(d);
-    return 0;
-}
-
-static int listfile_size(const char *path, const char *filename, struct stat *s,
-                         int flags)
-{
-    if(!s || !path) {
-        return -1;
-    }
-
-    /* blocks are 512 bytes, we want output to be KB */
-    if ((flags & LIST_SIZE) != 0) {
-        printf("%lld ", (long long)s->st_blocks / 2);
-    }
-
-    if ((flags & LIST_CLASSIFY) != 0) {
-        char filetype = mode2kind(s->st_mode);
-        if (filetype != 'l') {
-            printf("%c ", filetype);
-        } else {
-            struct stat link_dest;
-            if (!stat(path, &link_dest)) {
-                printf("l%c ", mode2kind(link_dest.st_mode));
-            } else {
-                fprintf(stderr, "stat '%s' failed: %s\n", path, strerror(errno));
-                printf("l? ");
-            }
-        }
-    }
-
-    printf("%s\n", filename);
-
-    return 0;
-}
-
-static int listfile_long(const char *path, struct stat *s, int flags)
-{
-    char date[32];
-    char mode[16];
-    char user[32];
-    char group[32];
-    const char *name;
-
-    if(!s || !path) {
-        return -1;
-    }
-
-    /* name is anything after the final '/', or the whole path if none*/
-    name = strrchr(path, '/');
-    if(name == 0) {
-        name = path;
-    } else {
-        name++;
-    }
-
-    strmode(s->st_mode, mode);
-    if (flags & LIST_LONG_NUMERIC) {
-        snprintf(user, sizeof(user), "%u", s->st_uid);
-        snprintf(group, sizeof(group), "%u", s->st_gid);
-    } else {
-        user2str(s->st_uid, user, sizeof(user));
-        group2str(s->st_gid, group, sizeof(group));
-    }
-
-    strftime(date, 32, "%Y-%m-%d %H:%M", localtime((const time_t*)&s->st_mtime));
-    date[31] = 0;
-
-// 12345678901234567890123456789012345678901234567890123456789012345678901234567890
-// MMMMMMMM UUUUUUUU GGGGGGGGG XXXXXXXX YYYY-MM-DD HH:MM NAME (->LINK)
-
-    switch(s->st_mode & S_IFMT) {
-    case S_IFBLK:
-    case S_IFCHR:
-        printf("%s %-8s %-8s %3d, %3d %s %s\n",
-               mode, user, group,
-               major(s->st_rdev), minor(s->st_rdev),
-               date, name);
-        break;
-    case S_IFREG:
-        printf("%s %-8s %-8s %8lld %s %s\n",
-               mode, user, group, (long long)s->st_size, date, name);
-        break;
-    case S_IFLNK: {
-        char linkto[256];
-        ssize_t len;
-
-        len = readlink(path, linkto, 256);
-        if(len < 0) return -1;
-
-        if(len > 255) {
-            linkto[252] = '.';
-            linkto[253] = '.';
-            linkto[254] = '.';
-            linkto[255] = 0;
-        } else {
-            linkto[len] = 0;
-        }
-
-        printf("%s %-8s %-8s          %s %s -> %s\n",
-               mode, user, group, date, name, linkto);
-        break;
-    }
-    default:
-        printf("%s %-8s %-8s          %s %s\n",
-               mode, user, group, date, name);
-
-    }
-    return 0;
-}
-
-static int listfile_maclabel(const char *path, struct stat *s)
-{
-    char mode[16];
-    char user[32];
-    char group[32];
-    char *maclabel = NULL;
-    const char *name;
-
-    if(!s || !path) {
-        return -1;
-    }
-
-    /* name is anything after the final '/', or the whole path if none*/
-    name = strrchr(path, '/');
-    if(name == 0) {
-        name = path;
-    } else {
-        name++;
-    }
-
-    lgetfilecon(path, &maclabel);
-    if (!maclabel) {
-        return -1;
-    }
-
-    strmode(s->st_mode, mode);
-    user2str(s->st_uid, user, sizeof(user));
-    group2str(s->st_gid, group, sizeof(group));
-
-    switch(s->st_mode & S_IFMT) {
-    case S_IFLNK: {
-        char linkto[256];
-        ssize_t len;
-
-        len = readlink(path, linkto, sizeof(linkto));
-        if(len < 0) return -1;
-
-        if((size_t)len > sizeof(linkto)-1) {
-            linkto[sizeof(linkto)-4] = '.';
-            linkto[sizeof(linkto)-3] = '.';
-            linkto[sizeof(linkto)-2] = '.';
-            linkto[sizeof(linkto)-1] = 0;
-        } else {
-            linkto[len] = 0;
-        }
-
-        printf("%s %-8s %-8s          %s %s -> %s\n",
-               mode, user, group, maclabel, name, linkto);
-        break;
-    }
-    default:
-        printf("%s %-8s %-8s          %s %s\n",
-               mode, user, group, maclabel, name);
-
-    }
-
-    free(maclabel);
-
-    return 0;
-}
-
-static int listfile(const char *dirname, const char *filename, int flags)
-{
-    struct stat s;
-
-    if ((flags & (LIST_LONG | LIST_SIZE | LIST_CLASSIFY | LIST_MACLABEL | LIST_INODE)) == 0) {
-        printf("%s\n", filename);
-        return 0;
-    }
-
-    char tmp[4096];
-    const char* pathname = filename;
-
-    if (dirname != NULL) {
-        snprintf(tmp, sizeof(tmp), "%s/%s", dirname, filename);
-        pathname = tmp;
-    } else {
-        pathname = filename;
-    }
-
-    if(lstat(pathname, &s) < 0) {
-        fprintf(stderr, "lstat '%s' failed: %s\n", pathname, strerror(errno));
-        return -1;
-    }
-
-    if(flags & LIST_INODE) {
-        printf("%8llu ", (unsigned long long)s.st_ino);
-    }
-
-    if ((flags & LIST_MACLABEL) != 0) {
-        return listfile_maclabel(pathname, &s);
-    } else if ((flags & LIST_LONG) != 0) {
-        return listfile_long(pathname, &s, flags);
-    } else /*((flags & LIST_SIZE) != 0)*/ {
-        return listfile_size(pathname, filename, &s, flags);
-    }
-}
-
-static int listdir(const char *name, int flags)
-{
-    char tmp[4096];
-    DIR *d;
-    struct dirent *de;
-    strlist_t  files = STRLIST_INITIALIZER;
-
-    d = opendir(name);
-    if(d == 0) {
-        fprintf(stderr, "opendir failed, %s\n", strerror(errno));
-        return -1;
-    }
-
-    if ((flags & LIST_SIZE) != 0) {
-        show_total_size(name, d, flags);
-    }
-
-    while((de = readdir(d)) != 0){
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue;
-        if(de->d_name[0] == '.' && (flags & LIST_ALL) == 0) continue;
-
-        strlist_append_dup(&files, de->d_name);
-    }
-
-    strlist_sort(&files);
-    STRLIST_FOREACH(&files, filename, listfile(name, filename, flags));
-    strlist_done(&files);
-
-    if (flags & LIST_RECURSIVE) {
-        strlist_t subdirs = STRLIST_INITIALIZER;
-
-        rewinddir(d);
-
-        while ((de = readdir(d)) != 0) {
-            struct stat s;
-            int err;
-
-            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-                continue;
-            if (de->d_name[0] == '.' && (flags & LIST_ALL) == 0)
-                continue;
-
-            if (!strcmp(name, "/"))
-                snprintf(tmp, sizeof(tmp), "/%s", de->d_name);
-            else
-                snprintf(tmp, sizeof(tmp), "%s/%s", name, de->d_name);
-
-            /*
-             * If the name ends in a '/', use stat() so we treat it like a
-             * directory even if it's a symlink.
-             */
-            if (tmp[strlen(tmp)-1] == '/')
-                err = stat(tmp, &s);
-            else
-                err = lstat(tmp, &s);
-
-            if (err < 0) {
-                perror(tmp);
-                closedir(d);
-                return -1;
-            }
-
-            if (S_ISDIR(s.st_mode)) {
-                strlist_append_dup(&subdirs, tmp);
-            }
-        }
-        strlist_sort(&subdirs);
-        STRLIST_FOREACH(&subdirs, path, {
-            printf("\n%s:\n", path);
-            listdir(path, flags);
-        });
-        strlist_done(&subdirs);
-    }
-
-    closedir(d);
-    return 0;
-}
-
-static int listpath(const char *name, int flags)
-{
-    struct stat s;
-    int err;
-
-    /*
-     * If the name ends in a '/', use stat() so we treat it like a
-     * directory even if it's a symlink.
-     */
-    if (name[strlen(name)-1] == '/')
-        err = stat(name, &s);
-    else
-        err = lstat(name, &s);
-
-    if (err < 0) {
-        perror(name);
-        return -1;
-    }
-
-    if ((flags & LIST_DIRECTORIES) == 0 && S_ISDIR(s.st_mode)) {
-        if (flags & LIST_RECURSIVE)
-            printf("\n%s:\n", name);
-        return listdir(name, flags);
-    } else {
-        /* yeah this calls stat() again*/
-        return listfile(NULL, name, flags);
-    }
-}
-
-int ls_main(int argc, char **argv)
-{
-    int flags = 0;
-
-    if(argc > 1) {
-        int i;
-        int err = 0;
-        strlist_t  files = STRLIST_INITIALIZER;
-
-        for (i = 1; i < argc; i++) {
-            if (argv[i][0] == '-') {
-                /* an option ? */
-                const char *arg = argv[i]+1;
-                while (arg[0]) {
-                    switch (arg[0]) {
-                    case 'l': flags |= LIST_LONG; break;
-                    case 'n': flags |= LIST_LONG | LIST_LONG_NUMERIC; break;
-                    case 's': flags |= LIST_SIZE; break;
-                    case 'R': flags |= LIST_RECURSIVE; break;
-                    case 'd': flags |= LIST_DIRECTORIES; break;
-                    case 'Z': flags |= LIST_MACLABEL; break;
-                    case 'a': flags |= LIST_ALL; break;
-                    case 'F': flags |= LIST_CLASSIFY; break;
-                    case 'i': flags |= LIST_INODE; break;
-                    default:
-                        fprintf(stderr, "%s: Unknown option '-%c'. Aborting.\n", "ls", arg[0]);
-                        exit(1);
-                    }
-                    arg++;
-                }
-            } else {
-                /* not an option ? */
-                strlist_append_dup(&files, argv[i]);
-            }
-        }
-
-        if (files.count > 0) {
-            STRLIST_FOREACH(&files, path, {
-                if (listpath(path, flags) != 0) {
-                    err = EXIT_FAILURE;
-                }
-            });
-            strlist_done(&files);
-            return err;
-        }
-    }
-
-    // list working directory if no files or directories were specified
-    return listpath(".", flags);
-}
diff --git a/toolbox/lsof.c b/toolbox/lsof.c
deleted file mode 100644 (file)
index 982f5aa..0000000
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (c) 2010, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name of Google, Inc. nor the names of its contributors
- *    may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <libgen.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <pwd.h>
-#include <sys/stat.h>
-
-#define BUF_MAX 1024
-#define CMD_DISPLAY_MAX (9 + 1)
-#define USER_DISPLAY_MAX (10 + 1)
-
-struct pid_info_t {
-    pid_t pid;
-    char user[USER_DISPLAY_MAX];
-
-    char cmdline[CMD_DISPLAY_MAX];
-
-    char path[PATH_MAX];
-    ssize_t parent_length;
-};
-
-static void print_header()
-{
-    printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n",
-            "COMMAND",
-            "PID",
-            "USER",
-            "FD",
-            "TYPE",
-            "DEVICE",
-            "SIZE/OFF",
-            "NODE",
-            "NAME");
-}
-
-static void print_type(char *type, struct pid_info_t* info)
-{
-    static ssize_t link_dest_size;
-    static char link_dest[PATH_MAX];
-
-    strlcat(info->path, type, sizeof(info->path));
-    if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) {
-        if (errno == ENOENT)
-            goto out;
-
-        snprintf(link_dest, sizeof(link_dest), "%s (readlink: %s)", info->path, strerror(errno));
-    } else {
-        link_dest[link_dest_size] = '\0';
-    }
-
-    // Things that are just the root filesystem are uninteresting (we already know)
-    if (!strcmp(link_dest, "/"))
-        goto out;
-
-    printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n",
-            info->cmdline, info->pid, info->user, type,
-            "???", "???", "???", "???", link_dest);
-
-out:
-    info->path[info->parent_length] = '\0';
-}
-
-// Prints out all file that have been memory mapped
-static void print_maps(struct pid_info_t* info)
-{
-    FILE *maps;
-    size_t offset;
-    char device[10];
-    long int inode;
-    char file[PATH_MAX];
-
-    strlcat(info->path, "maps", sizeof(info->path));
-
-    maps = fopen(info->path, "r");
-    if (!maps)
-        goto out;
-
-    while (fscanf(maps, "%*x-%*x %*s %zx %s %ld %s\n", &offset, device, &inode,
-            file) == 4) {
-        // We don't care about non-file maps
-        if (inode == 0 || !strcmp(device, "00:00"))
-            continue;
-
-        printf("%-9s %5d %10s %4s %9s %18s %9zd %10ld %s\n",
-                info->cmdline, info->pid, info->user, "mem",
-                "???", device, offset, inode, file);
-    }
-
-    fclose(maps);
-
-out:
-    info->path[info->parent_length] = '\0';
-}
-
-// Prints out all open file descriptors
-static void print_fds(struct pid_info_t* info)
-{
-    static char* fd_path = "fd/";
-    strlcat(info->path, fd_path, sizeof(info->path));
-
-    int previous_length = info->parent_length;
-    info->parent_length += strlen(fd_path);
-
-    DIR *dir = opendir(info->path);
-    if (dir == NULL) {
-        char msg[BUF_MAX];
-        snprintf(msg, sizeof(msg), "%s (opendir: %s)", info->path, strerror(errno));
-        printf("%-9s %5d %10s %4s %9s %18s %9s %10s %s\n",
-                info->cmdline, info->pid, info->user, "FDS",
-                "", "", "", "", msg);
-        goto out;
-    }
-
-    struct dirent* de;
-    while ((de = readdir(dir))) {
-        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-            continue;
-
-        print_type(de->d_name, info);
-    }
-    closedir(dir);
-
-out:
-    info->parent_length = previous_length;
-    info->path[info->parent_length] = '\0';
-}
-
-static void lsof_dumpinfo(pid_t pid)
-{
-    int fd;
-    struct pid_info_t info;
-    struct stat pidstat;
-    struct passwd *pw;
-
-    info.pid = pid;
-    snprintf(info.path, sizeof(info.path), "/proc/%d/", pid);
-    info.parent_length = strlen(info.path);
-
-    // Get the UID by calling stat on the proc/pid directory.
-    if (!stat(info.path, &pidstat)) {
-        pw = getpwuid(pidstat.st_uid);
-        if (pw) {
-            strlcpy(info.user, pw->pw_name, sizeof(info.user));
-        } else {
-            snprintf(info.user, USER_DISPLAY_MAX, "%d", (int)pidstat.st_uid);
-        }
-    } else {
-        strcpy(info.user, "???");
-    }
-
-    // Read the command line information; each argument is terminated with NULL.
-    strlcat(info.path, "cmdline", sizeof(info.path));
-    fd = open(info.path, O_RDONLY);
-    if (fd < 0) {
-        fprintf(stderr, "Couldn't read %s\n", info.path);
-        return;
-    }
-
-    char cmdline[PATH_MAX];
-    int numRead = read(fd, cmdline, sizeof(cmdline) - 1);
-    close(fd);
-
-    if (numRead < 0) {
-        fprintf(stderr, "Error reading cmdline: %s: %s\n", info.path, strerror(errno));
-        return;
-    }
-
-    cmdline[numRead] = '\0';
-
-    // We only want the basename of the cmdline
-    strlcpy(info.cmdline, basename(cmdline), sizeof(info.cmdline));
-
-    // Read each of these symlinks
-    print_type("cwd", &info);
-    print_type("exe", &info);
-    print_type("root", &info);
-
-    print_fds(&info);
-    print_maps(&info);
-}
-
-int lsof_main(int argc, char *argv[])
-{
-    long int pid = 0;
-    char* endptr;
-    if (argc == 2) {
-        pid = strtol(argv[1], &endptr, 10);
-    }
-
-    print_header();
-
-    if (pid) {
-        lsof_dumpinfo(pid);
-    } else {
-        DIR *dir = opendir("/proc");
-        if (dir == NULL) {
-            fprintf(stderr, "Couldn't open /proc\n");
-            return -1;
-        }
-
-        struct dirent* de;
-        while ((de = readdir(dir))) {
-            if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-                continue;
-
-            // Only inspect directories that are PID numbers
-            pid = strtol(de->d_name, &endptr, 10);
-            if (*endptr != '\0')
-                continue;
-
-            lsof_dumpinfo(pid);
-        }
-        closedir(dir);
-    }
-
-    return 0;
-}
diff --git a/toolbox/mount.c b/toolbox/mount.c
deleted file mode 100644 (file)
index 66ae8b1..0000000
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * mount.c, by rmk
- */
-
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <linux/loop.h>
-
-#define ARRAY_SIZE(x)  (sizeof(x) / sizeof(x[0]))
-
-#define DEFAULT_LOOP_DEVICE "/dev/block/loop0"
-#define LOOPDEV_MAXLEN 64
-
-struct mount_opts {
-       const char str[16];
-       unsigned long rwmask;
-       unsigned long rwset;
-       unsigned long rwnoset;
-};
-
-struct extra_opts {
-       char *str;
-       char *end;
-       int used_size;
-       int alloc_size;
-};
-
-/*
- * These options define the function of "mount(2)".
- */
-#define MS_TYPE        (MS_REMOUNT|MS_BIND|MS_MOVE)
-
-
-static const struct mount_opts options[] = {
-       /* name         mask            set             noset           */
-       { "async",      MS_SYNCHRONOUS, 0,              MS_SYNCHRONOUS  },
-       { "atime",      MS_NOATIME,     0,              MS_NOATIME      },
-       { "bind",       MS_TYPE,        MS_BIND,        0,              },
-       { "dev",        MS_NODEV,       0,              MS_NODEV        },
-       { "diratime",   MS_NODIRATIME,  0,              MS_NODIRATIME   },
-       { "dirsync",    MS_DIRSYNC,     MS_DIRSYNC,     0               },
-       { "exec",       MS_NOEXEC,      0,              MS_NOEXEC       },
-       { "move",       MS_TYPE,        MS_MOVE,        0               },
-       { "recurse",    MS_REC,         MS_REC,         0               },
-       { "rec",        MS_REC,         MS_REC,         0               },
-       { "remount",    MS_TYPE,        MS_REMOUNT,     0               },
-       { "ro",         MS_RDONLY,      MS_RDONLY,      0               },
-       { "rw",         MS_RDONLY,      0,              MS_RDONLY       },
-       { "suid",       MS_NOSUID,      0,              MS_NOSUID       },
-       { "sync",       MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0               },
-       { "verbose",    MS_VERBOSE,     MS_VERBOSE,     0               },
-       { "unbindable", MS_UNBINDABLE,  MS_UNBINDABLE,  0               },
-       { "private",    MS_PRIVATE,     MS_PRIVATE,     0               },
-       { "slave",      MS_SLAVE,       MS_SLAVE,       0               },
-       { "shared",     MS_SHARED,      MS_SHARED,      0               },
-};
-
-static void add_extra_option(struct extra_opts *extra, char *s)
-{
-       int len = strlen(s);
-       int newlen;
-
-       if (extra->str)
-              len++;                   /* +1 for ',' */
-       newlen = extra->used_size + len;
-
-       if (newlen >= extra->alloc_size) {
-               char *new;
-
-               new = realloc(extra->str, newlen + 1);  /* +1 for NUL */
-               if (!new)
-                       return;
-
-               extra->str = new;
-               extra->end = extra->str + extra->used_size;
-               extra->alloc_size = newlen + 1;
-       }
-
-       if (extra->used_size) {
-               *extra->end = ',';
-               extra->end++;
-       }
-       strcpy(extra->end, s);
-       extra->used_size += len;
-
-}
-
-static unsigned long
-parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, int* loop, char *loopdev)
-{
-       char *s;
-    
-    *loop = 0;
-       while ((s = strsep(&arg, ",")) != NULL) {
-               char *opt = s;
-               unsigned int i;
-               int res, no = s[0] == 'n' && s[1] == 'o';
-
-               if (no)
-                       s += 2;
-
-        if (strncmp(s, "loop=", 5) == 0) {
-            *loop = 1;
-            strlcpy(loopdev, s + 5, LOOPDEV_MAXLEN);
-            continue;
-        }
-
-        if (strcmp(s, "loop") == 0) {
-            *loop = 1;
-            strlcpy(loopdev, DEFAULT_LOOP_DEVICE, LOOPDEV_MAXLEN);
-            continue;
-        }
-               for (i = 0, res = 1; i < ARRAY_SIZE(options); i++) {
-                       res = strcmp(s, options[i].str);
-
-                       if (res == 0) {
-                               rwflag &= ~options[i].rwmask;
-                               if (no)
-                                       rwflag |= options[i].rwnoset;
-                               else
-                                       rwflag |= options[i].rwset;
-                       }
-                       if (res <= 0)
-                               break;
-               }
-
-               if (res != 0 && s[0])
-                       add_extra_option(extra, opt);
-       }
-
-       return rwflag;
-}
-
-/*
- * Mark the given block device as read-write, using the BLKROSET ioctl.
- */
-static void fs_set_blk_rw(const char *blockdev)
-{
-    int fd;
-    int OFF = 0;
-
-    fd = open(blockdev, O_RDONLY);
-    if (fd < 0) {
-        // should never happen
-        return;
-    }
-
-    ioctl(fd, BLKROSET, &OFF);
-    close(fd);
-}
-
-static char *progname;
-
-static struct extra_opts extra;
-static unsigned long rwflag;
-
-static int
-do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int loop,
-         char *loopdev)
-{
-       char *s;
-       int error = 0;
-
-    if (loop) {
-        int file_fd, device_fd;
-        int flags;
-
-        flags = (rwflag & MS_RDONLY) ? O_RDONLY : O_RDWR;
-        
-        file_fd = open(dev, flags);
-        if (file_fd < 0) {
-            perror("open backing file failed");
-            return 1;
-        }
-        device_fd = open(loopdev, flags);
-        if (device_fd < 0) {
-            perror("open loop device failed");
-            close(file_fd);
-            return 1;
-        }
-        if (ioctl(device_fd, LOOP_SET_FD, file_fd) < 0) {
-            perror("ioctl LOOP_SET_FD failed");
-            close(file_fd);
-            close(device_fd);
-            return 1;
-        }
-
-        close(file_fd);
-        close(device_fd);
-        dev = loopdev;
-    }
-
-    if ((rwflag & MS_RDONLY) == 0) {
-        fs_set_blk_rw(dev);
-    }
-
-       while ((s = strsep(&type, ",")) != NULL) {
-retry:
-               if (mount(dev, dir, s, rwflag, data) == -1) {
-                       error = errno;
-                       /*
-                        * If the filesystem is not found, or the
-                        * superblock is invalid, try the next.
-                        */
-                       if (error == ENODEV || error == EINVAL)
-                               continue;
-
-                       /*
-                        * If we get EACCESS, and we're trying to
-                        * mount readwrite and this isn't a remount,
-                        * try read only.
-                        */
-                       if (error == EACCES &&
-                           (rwflag & (MS_REMOUNT|MS_RDONLY)) == 0) {
-                               rwflag |= MS_RDONLY;
-                               goto retry;
-                       }
-                       break;
-               }
-       }
-
-       if (error) {
-               errno = error;
-               perror("mount");
-               return 255;
-       }
-
-       return 0;
-}
-
-static int print_mounts()
-{
-    FILE* f;
-    int length;
-    char buffer[100];
-    
-    f = fopen("/proc/mounts", "r");
-    if (!f) {
-        fprintf(stdout, "could not open /proc/mounts\n");
-        return -1;
-    }
-
-    do {
-        length = fread(buffer, 1, 100, f);
-        if (length > 0)
-            fwrite(buffer, 1, length, stdout);
-    } while (length > 0);
-
-    fclose(f);
-    return 0;
-}
-
-static int get_mounts_dev_dir(const char *arg, char **dev, char **dir)
-{
-       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;
-
-       f = fopen("/proc/mounts", "r");
-       if (!f) {
-               fprintf(stdout, "could not open /proc/mounts\n");
-               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 &&
-                       (strcmp(arg, mount_dev) == 0 ||
-                        strcmp(arg, mount_dir) == 0)) {
-                       *dev = strdup(mount_dev);
-                       *dir = strdup(mount_dir);
-                       fclose(f);
-                       return 0;
-               }
-       } while (match != EOF);
-
-       fclose(f);
-       return -1;
-}
-
-int mount_main(int argc, char *argv[])
-{
-       char *type = NULL;
-       char *dev = NULL;
-       char *dir = NULL;
-       int c;
-       int loop = 0;
-       char loopdev[LOOPDEV_MAXLEN];
-
-       progname = argv[0];
-       rwflag = MS_VERBOSE;
-       
-       // mount with no arguments is equivalent to "cat /proc/mounts"
-       if (argc == 1) return print_mounts();
-
-       do {
-               c = getopt(argc, argv, "o:rt:w");
-               if (c == EOF)
-                       break;
-               switch (c) {
-               case 'o':
-                       rwflag = parse_mount_options(optarg, rwflag, &extra, &loop, loopdev);
-                       break;
-               case 'r':
-                       rwflag |= MS_RDONLY;
-                       break;
-               case 't':
-                       type = optarg;
-                       break;
-               case 'w':
-                       rwflag &= ~MS_RDONLY;
-                       break;
-               case '?':
-                       fprintf(stderr, "%s: invalid option -%c\n",
-                               progname, optopt);
-                       exit(1);
-               }
-       } while (1);
-
-       /*
-        * If remount, bind or move was specified, then we don't
-        * have a "type" as such.  Use the dummy "none" type.
-        */
-       if (rwflag & MS_TYPE)
-               type = "none";
-
-       if (optind + 2 == argc) {
-               dev = argv[optind];
-               dir = argv[optind + 1];
-       } else if (optind + 1 == argc && rwflag & MS_REMOUNT) {
-               get_mounts_dev_dir(argv[optind], &dev, &dir);
-       }
-
-       if (dev == NULL || dir == NULL || type == NULL) {
-               fprintf(stderr, "Usage: %s [-r] [-w] [-o options] [-t type] "
-                       "device directory\n", progname);
-               exit(1);
-       }
-
-       return do_mount(dev, dir, type, rwflag, extra.str, loop, loopdev);
-       /* We leak dev and dir in some cases, but we're about to exit */
-}
index cf3f05a..3bc540d 100644 (file)
@@ -41,14 +41,14 @@ static int ppid_filter = 0;
 
 static void print_exe_abi(int pid);
 
-static int ps_line(int pid, int tid, char *namefilter)
+static int ps_line(int pid, int tid)
 {
     char statline[1024];
     char cmdline[1024];
     char macline[1024];
     char user[32];
     struct stat stats;
-    int fd, r;
+    int r;
     char *ptr, *name, *state;
     int ppid;
     unsigned rss, vss;
@@ -68,7 +68,7 @@ static int ps_line(int pid, int tid, char *namefilter)
         sprintf(statline, "/proc/%d/stat", pid);
         sprintf(cmdline, "/proc/%d/cmdline", pid);
         snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
-        fd = open(cmdline, O_RDONLY);
+        int fd = open(cmdline, O_RDONLY);
         if(fd == 0) {
             r = 0;
         } else {
@@ -79,7 +79,7 @@ static int ps_line(int pid, int tid, char *namefilter)
         cmdline[r] = 0;
     }
 
-    fd = open(statline, O_RDONLY);
+    int fd = open(statline, O_RDONLY);
     if(fd == 0) return -1;
     r = read(fd, statline, 1023);
     close(fd);
@@ -158,51 +158,48 @@ static int ps_line(int pid, int tid, char *namefilter)
         return 0;
     }
 
-    if(!namefilter || !strncmp(cmdline[0] ? cmdline : name, namefilter, strlen(namefilter))) {
-        if (display_flags & SHOW_MACLABEL) {
-            fd = open(macline, O_RDONLY);
-            strcpy(macline, "-");
-            if (fd >= 0) {
-                r = read(fd, macline, sizeof(macline)-1);
-                close(fd);
-                if (r > 0)
-                    macline[r] = 0;
-            }
-            printf("%-30s %-9s %-5d %-5d %s\n", macline, user, pid, ppid, cmdline[0] ? cmdline : name);
-            return 0;
-        }
-
-        printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
-        if (display_flags & SHOW_CPU)
-            printf(" %-2d", psr);
-        if (display_flags & SHOW_PRIO)
-            printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
-        if (display_flags & SHOW_POLICY) {
-            SchedPolicy p;
-            if (get_sched_policy(pid, &p) < 0)
-                printf(" un ");
-            else
-                printf(" %.2s ", get_sched_policy_name(p));
-        }
-        char path[PATH_MAX];
-        snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
-        char wchan[10];
-        int fd = open(path, O_RDONLY);
-        ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
-        if (wchan_len == -1) {
-            wchan[wchan_len = 0] = '\0';
-        }
-        close(fd);
-        printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
-        if (display_flags & SHOW_ABI) {
-            print_exe_abi(pid);
+    if (display_flags & SHOW_MACLABEL) {
+        fd = open(macline, O_RDONLY);
+        strcpy(macline, "-");
+        if (fd >= 0) {
+            r = read(fd, macline, sizeof(macline)-1);
+            close(fd);
+            if (r > 0)
+                macline[r] = 0;
         }
-        printf("%s", cmdline[0] ? cmdline : name);
-        if(display_flags&SHOW_TIME)
-            printf(" (u:%d, s:%d)", utime, stime);
+        printf("%-30s ", macline);
+    }
 
-        printf("\n");
+    printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
+    if (display_flags & SHOW_CPU)
+        printf(" %-2d", psr);
+    if (display_flags & SHOW_PRIO)
+        printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
+    if (display_flags & SHOW_POLICY) {
+        SchedPolicy p;
+        if (get_sched_policy(pid, &p) < 0)
+            printf(" un ");
+        else
+            printf(" %.2s ", get_sched_policy_name(p));
+    }
+    char path[PATH_MAX];
+    snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
+    char wchan[10];
+    fd = open(path, O_RDONLY);
+    ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
+    if (wchan_len == -1) {
+        wchan[wchan_len = 0] = '\0';
     }
+    close(fd);
+    printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
+    if (display_flags & SHOW_ABI) {
+        print_exe_abi(pid);
+    }
+    printf("%s", cmdline[0] ? cmdline : name);
+    if(display_flags&SHOW_TIME)
+        printf(" (u:%d, s:%d)", utime, stime);
+
+    printf("\n");
     return 0;
 }
 
@@ -240,7 +237,7 @@ static void print_exe_abi(int pid)
     }
 }
 
-void ps_threads(int pid, char *namefilter)
+void ps_threads(int pid)
 {
     char tmp[128];
     DIR *d;
@@ -254,7 +251,7 @@ void ps_threads(int pid, char *namefilter)
         if(isdigit(de->d_name[0])){
             int tid = atoi(de->d_name);
             if(tid == pid) continue;
-            ps_line(pid, tid, namefilter);
+            ps_line(pid, tid);
         }
     }
     closedir(d);
@@ -264,7 +261,6 @@ int ps_main(int argc, char **argv)
 {
     DIR *d;
     struct dirent *de;
-    char *namefilter = 0;
     int pidfilter = 0;
     int threads = 0;
 
@@ -290,33 +286,39 @@ int ps_main(int argc, char **argv)
             display_flags |= SHOW_ABI;
         } else if(!strcmp(argv[1],"--ppid")) {
             ppid_filter = atoi(argv[2]);
+            if (ppid_filter == 0) {
+                fprintf(stderr, "bad ppid '%s'\n", argv[2]);
+                return 1;
+            }
             argc--;
             argv++;
-        } else if(isdigit(argv[1][0])){
-            pidfilter = atoi(argv[1]);
         } else {
-            namefilter = argv[1];
+            pidfilter = atoi(argv[1]);
+            if (pidfilter == 0) {
+                fprintf(stderr, "bad pid '%s'\n", argv[1]);
+                return 1;
+            }
         }
         argc--;
         argv++;
     }
 
     if (display_flags & SHOW_MACLABEL) {
-        printf("LABEL                          USER      PID   PPID  NAME\n");
-    } else {
-        printf("USER      PID   PPID  VSIZE  RSS  %s%s %sWCHAN      %*s  %sNAME\n",
-               (display_flags&SHOW_CPU)?"CPU ":"",
-               (display_flags&SHOW_PRIO)?"PRIO  NICE  RTPRI SCHED ":"",
-               (display_flags&SHOW_POLICY)?"PCY " : "",
-               (int) PC_WIDTH, "PC",
-               (display_flags&SHOW_ABI)?"ABI " : "");
+        printf("LABEL                          ");
     }
+    printf("USER      PID   PPID  VSIZE  RSS  %s%s %sWCHAN      %*s  %sNAME\n",
+           (display_flags&SHOW_CPU)?"CPU ":"",
+           (display_flags&SHOW_PRIO)?"PRIO  NICE  RTPRI SCHED ":"",
+           (display_flags&SHOW_POLICY)?"PCY " : "",
+           (int) PC_WIDTH, "PC",
+           (display_flags&SHOW_ABI)?"ABI " : "");
+
     while((de = readdir(d)) != 0){
         if(isdigit(de->d_name[0])){
             int pid = atoi(de->d_name);
             if(!pidfilter || (pidfilter == pid)) {
-                ps_line(pid, 0, namefilter);
-                if(threads) ps_threads(pid, namefilter);
+                ps_line(pid, 0);
+                if(threads) ps_threads(pid);
             }
         }
     }
diff --git a/toolbox/renice.c b/toolbox/renice.c
deleted file mode 100644 (file)
index 99a06f4..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (c) 2008, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the 
- *    distribution.
- *  * Neither the name of Google, Inc. nor the names of its contributors
- *    may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sched.h>
-#include <getopt.h>
-
-static void
-usage(const char *s)
-{
-    fprintf(stderr, "USAGE: %s [[-r] [-t TYPE] priority pids ...] [-g pid]\n", s);
-    exit(EXIT_FAILURE);
-}
-
-void print_prio(pid_t pid)
-{
-    int sched;
-    struct sched_param sp;
-
-    printf("pid %d's priority: %d\n", pid, getpriority(PRIO_PROCESS, pid));
-
-    printf("scheduling class: ");
-    sched = sched_getscheduler(pid);
-    switch (sched) {
-    case SCHED_FIFO:
-        printf("FIFO\n");
-        break;
-    case SCHED_RR:
-        printf("RR\n");
-        break;
-    case SCHED_OTHER:
-        printf("Normal\n");
-        break;
-    case -1:
-        perror("sched_getscheduler");
-        break;
-    default:
-        printf("Unknown\n");
-    }
-
-    sched_getparam(pid, &sp);
-    printf("RT prio: %d (of %d to %d)\n", sp.sched_priority,
-           sched_get_priority_min(sched), sched_get_priority_max(sched));
-}
-
-int get_sched(char *str)
-{
-    if (strcasecmp(str, "RR") == 0)
-        return SCHED_RR;
-    else if (strcasecmp(str, "FIFO") == 0)
-        return SCHED_FIFO;
-    else if (strcasecmp(str, "NORMAL") == 0)
-        return SCHED_OTHER;
-    else if (strcasecmp(str, "OTHER") == 0)
-        return SCHED_OTHER;
-    return SCHED_RR;
-}
-
-int renice_main(int argc, char *argv[])
-{
-    int prio;
-    int realtime = 0;
-    int opt;
-    int sched = SCHED_RR;
-    char *cmd = argv[0];
-
-    do {
-        opt = getopt(argc, argv, "rt:g:");
-        if (opt == -1)
-            break;
-        switch (opt) {
-        case 'r':
-            // do realtime priority adjustment
-            realtime = 1;
-            break;
-        case 't':
-            sched = get_sched(optarg);
-            break;
-        case 'g':
-            print_prio(atoi(optarg));
-            return 0;
-        default:
-            usage(cmd);
-        }
-    } while (1);
-
-    argc -= optind;
-    argv += optind;
-
-    if (argc < 1)
-        usage(cmd);
-
-    prio = atoi(argv[0]);
-    argc--;
-    argv++;
-
-    if (argc < 1)
-        usage(cmd);
-
-    while(argc) {
-        pid_t pid;
-
-        pid = atoi(argv[0]);
-        argc--;
-        argv++;
-
-        if (realtime) {
-            struct sched_param sp = { .sched_priority = prio };
-            int ret;
-
-            ret = sched_setscheduler(pid, sched, &sp);
-            if (ret) {
-                perror("sched_set_scheduler");
-                exit(EXIT_FAILURE);
-            }
-        } else {
-            int ret;
-
-            ret = setpriority(PRIO_PROCESS, pid, prio);
-            if (ret) {
-                perror("setpriority");
-                exit(EXIT_FAILURE);
-            }
-        }
-    }
-
-    return 0;
-}
index 6c8a3f2..cca5fef 100644 (file)
@@ -1,21 +1 @@
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <cutils/properties.h>
-
-int start_main(int argc, char *argv[])
-{
-    if(argc > 1) {
-        property_set("ctl.start", argv[1]);
-    } else {
-        /* defaults to starting the common services stopped by stop.c */
-        property_set("ctl.start", "netd");
-        property_set("ctl.start", "surfaceflinger");
-        property_set("ctl.start", "zygote");
-        property_set("ctl.start", "zygote_secondary");
-    }
-
-    return 0;
-}
+/* Needed by Android.mk. Actual code in start_stop.cpp. */
diff --git a/toolbox/start_stop.cpp b/toolbox/start_stop.cpp
new file mode 100644 (file)
index 0000000..dc48c0c
--- /dev/null
@@ -0,0 +1,43 @@
+#include <error.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+
+static const char* services[] = {
+  "netd",
+  "surfaceflinger",
+  "zygote",
+  "zygote_secondary",
+};
+
+static int start_stop(bool start, int argc, char* argv[]) {
+  if (getuid() != 0) error(1, 0, "must be root");
+  const char* property = start ? "ctl.start" : "ctl.stop";
+  if (argc > 2) {
+    error(1, 0, "usage: %s [SERVICE]\n", argv[0]);
+  } else if (argc == 2) {
+    property_set(property, argv[1]);
+  } else {
+    if (start) {
+      for (size_t i = 0; i < sizeof(services)/sizeof(services[0]); ++i) {
+        property_set(property, services[i]);
+      }
+    } else {
+      for (int i = sizeof(services)/sizeof(services[0]) - 1; i >= 0; --i) {
+        property_set(property, services[i]);
+      }
+    }
+  }
+  return 0;
+}
+
+extern "C" int start_main(int argc, char* argv[]) {
+  return start_stop(true, argc, argv);
+}
+
+extern "C" int stop_main(int argc, char* argv[]) {
+  return start_stop(false, argc, argv);
+}
index 5e3ce3c..cca5fef 100644 (file)
@@ -1,19 +1 @@
-#include <stdio.h>
-#include <string.h>
-
-#include <cutils/properties.h>
-
-int stop_main(int argc, char *argv[])
-{
-    if(argc > 1) {
-        property_set("ctl.stop", argv[1]);
-    } else{
-        /* defaults to stopping the common services */
-        property_set("ctl.stop", "zygote_secondary");
-        property_set("ctl.stop", "zygote");
-        property_set("ctl.stop", "surfaceflinger");
-        property_set("ctl.stop", "netd");
-    }
-
-    return 0;
-}
+/* Needed by Android.mk. Actual code in start_stop.cpp. */
index 1e99d4c..0ea5a5e 100644 (file)
@@ -158,7 +158,7 @@ int top_main(int argc, char *argv[]) {
             fprintf(stderr, "Invalid argument \"%s\" for option -s.\n", argv[i]);
             exit(EXIT_FAILURE);
         }
-        if (!strcmp(argv[i], "-t")) { threads = 1; continue; }
+        if (!strcmp(argv[i], "-H") || !strcmp(argv[i], "-t")) { threads = 1; continue; }
         if (!strcmp(argv[i], "-h")) {
             usage(argv[0]);
             exit(EXIT_SUCCESS);
@@ -187,6 +187,7 @@ int top_main(int argc, char *argv[]) {
         read_procs();
         print_procs();
         free_old_procs();
+        fflush(stdout);
     }
 
     return 0;
@@ -566,7 +567,7 @@ static void usage(char *cmd) {
                     "    -n num  Updates to show before exiting.\n"
                     "    -d num  Seconds to wait between updates.\n"
                     "    -s col  Column to sort by (cpu,vss,rss,thr).\n"
-                    "    -t      Show threads instead of processes.\n"
+                    "    -H      Show threads instead of processes.\n"
                     "    -h      Display this help screen.\n",
         cmd);
 }
diff --git a/toolbox/upstream-netbsd/usr.bin/du/du.c b/toolbox/upstream-netbsd/usr.bin/du/du.c
deleted file mode 100644 (file)
index 086ac4a..0000000
+++ /dev/null
@@ -1,364 +0,0 @@
-/*     $NetBSD: du.c,v 1.36 2012/03/11 11:23:20 shattered Exp $        */
-
-/*
- * Copyright (c) 1989, 1993, 1994
- *     The Regents of the University of California.  All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Newcomb.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\
- The Regents of the University of California.  All rights reserved.");
-#endif /* not lint */
-
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)du.c       8.5 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: du.c,v 1.36 2012/03/11 11:23:20 shattered Exp $");
-#endif
-#endif /* not lint */
-
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <dirent.h>
-#include <err.h>
-#include <errno.h>
-#include <fts.h>
-#include <inttypes.h>
-#include <util.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-
-/* Count inodes or file size */
-#define        COUNT   (iflag ? 1 : p->fts_statp->st_blocks)
-
-static int     linkchk(dev_t, ino_t);
-static void    prstat(const char *, int64_t);
-__dead static void     usage(void);
-
-static int hflag, iflag;
-static long blocksize;
-
-int
-main(int argc, char *argv[])
-{
-       FTS *fts;
-       FTSENT *p;
-       int64_t totalblocks;
-       int ftsoptions, listfiles;
-       int depth;
-       int Hflag, Lflag, aflag, ch, cflag, dflag, gkmflag, nflag, rval, sflag;
-       const char *noargv[2];
-
-       Hflag = Lflag = aflag = cflag = dflag = gkmflag = nflag = sflag = 0;
-       totalblocks = 0;
-       ftsoptions = FTS_PHYSICAL;
-       depth = INT_MAX;
-       while ((ch = getopt(argc, argv, "HLPacd:ghikmnrsx")) != -1)
-               switch (ch) {
-               case 'H':
-                       Hflag = 1;
-                       Lflag = 0;
-                       break;
-               case 'L':
-                       Lflag = 1;
-                       Hflag = 0;
-                       break;
-               case 'P':
-                       Hflag = Lflag = 0;
-                       break;
-               case 'a':
-                       aflag = 1;
-                       break;
-               case 'c':
-                       cflag = 1;
-                       break;
-               case 'd':
-                       dflag = 1;
-                       depth = atoi(optarg);
-                       if (depth < 0 || depth > SHRT_MAX) {
-                               warnx("invalid argument to option d: %s", 
-                                       optarg);
-                               usage();
-                       }
-                       break;
-               case 'g':
-                       blocksize = 1024 * 1024 * 1024;
-                       gkmflag = 1;
-                       break;
-               case 'h':
-                       hflag = 1;
-                       break;
-               case 'i':
-                       iflag = 1;
-                       break;
-               case 'k':
-                       blocksize = 1024;
-                       gkmflag = 1;
-                       break;
-               case 'm':
-                       blocksize = 1024 * 1024;
-                       gkmflag = 1;
-                       break; 
-               case 'n':
-                       nflag = 1;
-                       break;
-               case 'r':
-                       break;
-               case 's':
-                       sflag = 1;
-                       break;
-               case 'x':
-                       ftsoptions |= FTS_XDEV;
-                       break;
-               case '?':
-               default:
-                       usage();
-               }
-       argc -= optind;
-       argv += optind;
-
-       /*
-        * XXX
-        * Because of the way that fts(3) works, logical walks will not count
-        * the blocks actually used by symbolic links.  We rationalize this by
-        * noting that users computing logical sizes are likely to do logical
-        * copies, so not counting the links is correct.  The real reason is
-        * that we'd have to re-implement the kernel's symbolic link traversing
-        * algorithm to get this right.  If, for example, you have relative
-        * symbolic links referencing other relative symbolic links, it gets
-        * very nasty, very fast.  The bottom line is that it's documented in
-        * the man page, so it's a feature.
-        */
-       if (Hflag)
-               ftsoptions |= FTS_COMFOLLOW;
-       if (Lflag) {
-               ftsoptions &= ~FTS_PHYSICAL;
-               ftsoptions |= FTS_LOGICAL;
-       }
-
-       listfiles = 0;
-       if (aflag) {
-               if (sflag || dflag)
-                       usage();
-               listfiles = 1;
-       } else if (sflag) {
-               if (dflag)
-                       usage();
-               depth = 0;
-       }
-
-       if (!*argv) {
-               noargv[0] = ".";
-               noargv[1] = NULL;
-               argv = __UNCONST(noargv);
-       }
-
-       if (!gkmflag)
-               (void)getbsize(NULL, &blocksize);
-       blocksize /= 512;
-
-       if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL)
-               err(1, "fts_open `%s'", *argv);
-
-       for (rval = 0; (p = fts_read(fts)) != NULL;) {
-#ifndef __ANDROID__
-               if (nflag) {
-                       switch (p->fts_info) {
-                       case FTS_NS:
-                       case FTS_SLNONE:
-                               /* nothing */
-                               break;
-                       default:
-                               if (p->fts_statp->st_flags & UF_NODUMP) {
-                                       fts_set(fts, p, FTS_SKIP);
-                                       continue;
-                               }
-                       }
-               }
-#endif
-               switch (p->fts_info) {
-               case FTS_D:                     /* Ignore. */
-                       break;
-               case FTS_DP:
-                       p->fts_parent->fts_number += 
-                           p->fts_number += COUNT;
-                       if (cflag)
-                               totalblocks += COUNT;
-                       /*
-                        * If listing each directory, or not listing files
-                        * or directories and this is post-order of the
-                        * root of a traversal, display the total.
-                        */
-                       if (p->fts_level <= depth
-                           || (!listfiles && !p->fts_level))
-                               prstat(p->fts_path, p->fts_number);
-                       break;
-               case FTS_DC:                    /* Ignore. */
-                       break;
-               case FTS_DNR:                   /* Warn, continue. */
-               case FTS_ERR:
-               case FTS_NS:
-                       warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
-                       rval = 1;
-                       break;
-               default:
-                       if (p->fts_statp->st_nlink > 1 &&
-                           linkchk(p->fts_statp->st_dev, p->fts_statp->st_ino))
-                               break;
-                       /*
-                        * If listing each file, or a non-directory file was
-                        * the root of a traversal, display the total.
-                        */
-                       if (listfiles || !p->fts_level)
-                               prstat(p->fts_path, COUNT);
-                       p->fts_parent->fts_number += COUNT;
-                       if (cflag)
-                               totalblocks += COUNT;
-               }
-       }
-       if (errno)
-               err(1, "fts_read");
-       if (cflag)
-               prstat("total", totalblocks);
-       exit(rval);
-}
-
-static void
-prstat(const char *fname, int64_t blocks)
-{
-       if (iflag) {
-               (void)printf("%" PRId64 "\t%s\n", blocks, fname);
-               return;
-       }
-
-       if (hflag) {
-               char buf[5];
-               int64_t sz = blocks * 512;
-
-               humanize_number(buf, sizeof(buf), sz, "", HN_AUTOSCALE,
-                   HN_B | HN_NOSPACE | HN_DECIMAL);
-
-               (void)printf("%s\t%s\n", buf, fname);
-       } else
-               (void)printf("%" PRId64 "\t%s\n",
-                   howmany(blocks, (int64_t)blocksize),
-                   fname);
-}
-
-static int
-linkchk(dev_t dev, ino_t ino)
-{
-       static struct entry {
-               dev_t   dev;
-               ino_t   ino;
-       } *htable;
-       static int htshift;  /* log(allocated size) */
-       static int htmask;   /* allocated size - 1 */
-       static int htused;   /* 2*number of insertions */
-       static int sawzero;  /* Whether zero is in table or not */
-       int h, h2;
-       uint64_t tmp;
-       /* this constant is (1<<64)/((1+sqrt(5))/2)
-        * aka (word size)/(golden ratio)
-        */
-       const uint64_t HTCONST = 11400714819323198485ULL;
-       const int HTBITS = CHAR_BIT * sizeof(tmp);
-
-       /* Never store zero in hashtable */
-       if (dev == 0 && ino == 0) {
-               h = sawzero;
-               sawzero = 1;
-               return h;
-       }
-
-       /* Extend hash table if necessary, keep load under 0.5 */
-       if (htused<<1 >= htmask) {
-               struct entry *ohtable;
-
-               if (!htable)
-                       htshift = 10;   /* starting hashtable size */
-               else
-                       htshift++;   /* exponential hashtable growth */
-
-               htmask  = (1 << htshift) - 1;
-               htused = 0;
-
-               ohtable = htable;
-               htable = calloc(htmask+1, sizeof(*htable));
-               if (!htable)
-                       err(1, "calloc");
-
-               /* populate newly allocated hashtable */
-               if (ohtable) {
-                       int i;
-                       for (i = 0; i <= htmask>>1; i++)
-                               if (ohtable[i].ino || ohtable[i].dev)
-                                       linkchk(ohtable[i].dev, ohtable[i].ino);
-                       free(ohtable);
-               }
-       }
-
-       /* multiplicative hashing */
-       tmp = dev;
-       tmp <<= HTBITS>>1;
-       tmp |=  ino;
-       tmp *= HTCONST;
-       h  = tmp >> (HTBITS - htshift);
-       h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */
-
-       /* open address hashtable search with double hash probing */
-       while (htable[h].ino || htable[h].dev) {
-               if ((htable[h].ino == ino) && (htable[h].dev == dev))
-                       return 1;
-               h = (h + h2) & htmask;
-       }
-
-       /* Insert the current entry into hashtable */
-       htable[h].dev = dev;
-       htable[h].ino = ino;
-       htused++;
-       return 0;
-}
-
-static void
-usage(void)
-{
-
-       (void)fprintf(stderr,
-               "usage: du [-H | -L | -P] [-a | -d depth | -s] [-cghikmnrx] [file ...]\n");
-       exit(1);
-}
diff --git a/toolbox/uptime.c b/toolbox/uptime.c
deleted file mode 100644 (file)
index ebfb15e..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2010, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name of Google, Inc. nor the names of its contributors
- *    may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-
-static void format_time(int time, char* buffer) {
-    int seconds = time % 60;
-    time /= 60;
-    int minutes = time % 60;
-    time /= 60;
-    int hours = time % 24;
-    int days = time / 24;
-
-    if (days > 0) {
-        sprintf(buffer, "%d day%s, %02d:%02d:%02d", days, (days == 1) ? "" : "s", hours, minutes, seconds);
-    } else {
-        sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
-    }
-}
-
-int uptime_main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) {
-    FILE* file = fopen("/proc/uptime", "r");
-    if (!file) {
-        fprintf(stderr, "Could not open /proc/uptime\n");
-        return -1;
-    }
-    float idle_time;
-    if (fscanf(file, "%*f %f", &idle_time) != 1) {
-        fprintf(stderr, "Could not parse /proc/uptime\n");
-        fclose(file);
-        return -1;
-    }
-    fclose(file);
-
-    struct timespec up_timespec;
-    if (clock_gettime(CLOCK_MONOTONIC, &up_timespec) == -1) {
-        fprintf(stderr, "Could not get monotonic time: %s\n", strerror(errno));
-       return -1;
-    }
-    float up_time = up_timespec.tv_sec + up_timespec.tv_nsec / 1e9;
-
-    struct timespec elapsed_timespec;
-    if (clock_gettime(CLOCK_BOOTTIME, &elapsed_timespec) == -1) {
-        fprintf(stderr, "Could not get boot time: %s\n", strerror(errno));
-        return -1;
-    }
-    int elapsed = elapsed_timespec.tv_sec;
-
-    char up_string[100], idle_string[100], sleep_string[100];
-    format_time(elapsed, up_string);
-    format_time((int)idle_time, idle_string);
-    format_time((int)(elapsed - up_time), sleep_string);
-    printf("up time: %s, idle time: %s, sleep time: %s\n", up_string, idle_string, sleep_string);
-
-    return 0;
-}
diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c
deleted file mode 100644 (file)
index cd62922..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <errno.h>
-
-#include <cutils/properties.h>
-#include <cutils/hashmap.h>
-
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
-
-static int str_hash(void *key)
-{
-    return hashmapHash(key, strlen(key));
-}
-
-static bool str_equals(void *keyA, void *keyB)
-{
-    return strcmp(keyA, keyB) == 0;
-}
-
-static void announce(char *name, char *value)
-{
-    unsigned char *x;
-    
-    for(x = (unsigned char *)value; *x; x++) {
-        if((*x < 32) || (*x > 127)) *x = '.';
-    }
-
-    fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value);
-}
-
-static void add_to_watchlist(Hashmap *watchlist, const char *name,
-        const prop_info *pi)
-{
-    char *key = strdup(name);
-    unsigned *value = malloc(sizeof(unsigned));
-    if (!key || !value)
-        exit(1);
-
-    *value = __system_property_serial(pi);
-    hashmapPut(watchlist, key, value);
-}
-
-static void populate_watchlist(const prop_info *pi, void *cookie)
-{
-    Hashmap *watchlist = cookie;
-    char name[PROP_NAME_MAX];
-    char value_unused[PROP_VALUE_MAX];
-
-    __system_property_read(pi, name, value_unused);
-    add_to_watchlist(watchlist, name, pi);
-}
-
-static void update_watchlist(const prop_info *pi, void *cookie)
-{
-    Hashmap *watchlist = cookie;
-    char name[PROP_NAME_MAX];
-    char value[PROP_VALUE_MAX];
-    unsigned *serial;
-
-    __system_property_read(pi, name, value);
-    serial = hashmapGet(watchlist, name);
-    if (!serial) {
-        add_to_watchlist(watchlist, name, pi);
-        announce(name, value);
-    } else {
-        unsigned tmp = __system_property_serial(pi);
-        if (*serial != tmp) {
-            *serial = tmp;
-            announce(name, value);
-        }
-    }
-}
-
-int watchprops_main(int argc, char *argv[])
-{
-    unsigned serial;
-    
-    Hashmap *watchlist = hashmapCreate(1024, str_hash, str_equals);
-    if (!watchlist)
-        exit(1);
-
-    __system_property_foreach(populate_watchlist, watchlist);
-
-    for(serial = 0;;) {
-        serial = __system_property_wait_any(serial);
-        __system_property_foreach(update_watchlist, watchlist);
-    }
-    return 0;
-}
diff --git a/trusty/gatekeeper/Android.mk b/trusty/gatekeeper/Android.mk
new file mode 100644 (file)
index 0000000..13e9a09
--- /dev/null
@@ -0,0 +1,50 @@
+#
+# Copyright (C) 2015 The Android Open-Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# WARNING: Everything listed here will be built on ALL platforms,
+# including x86, the emulator, and the SDK.  Modules must be uniquely
+# named (liblights.panda), and must build everywhere, or limit themselves
+# to only building on ARM if they include assembly. Individual makefiles
+# are responsible for having their own logic, for fine-grained control.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := gatekeeper.trusty
+
+LOCAL_MODULE_RELATIVE_PATH := hw
+
+LOCAL_SRC_FILES := \
+       module.cpp \
+       trusty_gatekeeper_ipc.c \
+       trusty_gatekeeper.cpp
+
+LOCAL_CLFAGS = -fvisibility=hidden -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES := \
+       libgatekeeper \
+       liblog \
+       libcutils \
+       libtrusty
+
+LOCAL_MODULE_TAGS := optional
+
+# Symlink gatekeeper.trusty.so -> gatekeeper.<device>.so so libhardware can find it.
+LOCAL_POST_INSTALL_CMD = \
+    $(hide) ln -sf $(notdir $(LOCAL_INSTALLED_MODULE)) $(dir $(LOCAL_INSTALLED_MODULE))gatekeeper.$(TARGET_DEVICE).so
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/gatekeeper/gatekeeper_ipc.h b/trusty/gatekeeper/gatekeeper_ipc.h
new file mode 100644 (file)
index 0000000..b05dcd8
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#define GATEKEEPER_PORT "com.android.trusty.gatekeeper"
+#define GATEKEEPER_MAX_BUFFER_LENGTH 1024
+
+enum gatekeeper_command {
+       GK_REQ_SHIFT = 1,
+       GK_RESP_BIT  = 1,
+
+       GK_ENROLL       = (0 << GK_REQ_SHIFT),
+       GK_VERIFY       = (1 << GK_REQ_SHIFT),
+};
+
+/**
+ * gatekeeper_message - Serial header for communicating with GK server
+ * @cmd: the command, one of ENROLL, VERIFY. Payload must be a serialized
+ *       buffer of the corresponding request object.
+ * @payload: start of the serialized command specific payload
+ */
+struct gatekeeper_message {
+    uint32_t cmd;
+    uint8_t payload[0];
+};
+
diff --git a/trusty/gatekeeper/module.cpp b/trusty/gatekeeper/module.cpp
new file mode 100644 (file)
index 0000000..0ee3c2f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hardware/hardware.h>
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "trusty_gatekeeper.h"
+
+using gatekeeper::TrustyGateKeeperDevice;
+
+static int trusty_gatekeeper_open(const hw_module_t *module, const char *name,
+        hw_device_t **device) {
+
+    if (strcmp(name, HARDWARE_GATEKEEPER) != 0) {
+        return -EINVAL;
+    }
+
+    TrustyGateKeeperDevice *gatekeeper = new TrustyGateKeeperDevice(module);
+    if (gatekeeper == NULL) return -ENOMEM;
+    *device = gatekeeper->hw_device();
+
+    return 0;
+}
+
+static struct hw_module_methods_t gatekeeper_module_methods = {
+    .open = trusty_gatekeeper_open,
+};
+
+struct gatekeeper_module HAL_MODULE_INFO_SYM __attribute__((visibility("default"))) = {
+    .common = {
+        .tag = HARDWARE_MODULE_TAG,
+        .module_api_version = GATEKEEPER_MODULE_API_VERSION_0_1,
+        .hal_api_version = HARDWARE_HAL_API_VERSION,
+        .id = GATEKEEPER_HARDWARE_MODULE_ID,
+        .name = "Trusty GateKeeper HAL",
+        .author = "The Android Open Source Project",
+        .methods = &gatekeeper_module_methods,
+        .dso = 0,
+        .reserved = {}
+    },
+};
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
new file mode 100644 (file)
index 0000000..d24f44f
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <assert.h>
+#include <type_traits>
+
+#include "trusty_gatekeeper.h"
+#include "trusty_gatekeeper_ipc.h"
+#include "gatekeeper_ipc.h"
+
+#define LOG_TAG "TrustyGateKeeper"
+#include <cutils/log.h>
+
+namespace gatekeeper {
+
+const uint32_t SEND_BUF_SIZE = 8192;
+const uint32_t RECV_BUF_SIZE = 8192;
+
+TrustyGateKeeperDevice::TrustyGateKeeperDevice(const hw_module_t *module) {
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+    static_assert(std::is_standard_layout<TrustyGateKeeperDevice>::value,
+                  "TrustyGateKeeperDevice must be standard layout");
+    static_assert(offsetof(TrustyGateKeeperDevice, device_) == 0,
+                  "device_ must be the first member of TrustyGateKeeperDevice");
+    static_assert(offsetof(TrustyGateKeeperDevice, device_.common) == 0,
+                  "common must be the first member of gatekeeper_device");
+#else
+    assert(reinterpret_cast<gatekeeper_device_t *>(this) == &device_);
+    assert(reinterpret_cast<hw_device_t *>(this) == &(device_.common));
+#endif
+
+    memset(&device_, 0, sizeof(device_));
+    device_.common.tag = HARDWARE_DEVICE_TAG;
+    device_.common.version = 1;
+    device_.common.module = const_cast<hw_module_t *>(module);
+    device_.common.close = close_device;
+
+    device_.enroll = enroll;
+    device_.verify = verify;
+    device_.delete_user = nullptr;
+    device_.delete_all_users = nullptr;
+
+    int rc = trusty_gatekeeper_connect();
+    if (rc < 0) {
+        ALOGE("Error initializing trusty session: %d", rc);
+    }
+
+    error_ = rc;
+
+}
+
+hw_device_t* TrustyGateKeeperDevice::hw_device() {
+    return &device_.common;
+}
+
+int TrustyGateKeeperDevice::close_device(hw_device_t* dev) {
+    delete reinterpret_cast<TrustyGateKeeperDevice *>(dev);
+    return 0;
+}
+
+TrustyGateKeeperDevice::~TrustyGateKeeperDevice() {
+    trusty_gatekeeper_disconnect();
+}
+
+int TrustyGateKeeperDevice::Enroll(uint32_t uid, const uint8_t *current_password_handle,
+        uint32_t current_password_handle_length, const uint8_t *current_password,
+        uint32_t current_password_length, const uint8_t *desired_password,
+        uint32_t desired_password_length, uint8_t **enrolled_password_handle,
+        uint32_t *enrolled_password_handle_length) {
+
+    if (error_ != 0) {
+        return error_;
+    }
+
+    SizedBuffer desired_password_buffer(desired_password_length);
+    memcpy(desired_password_buffer.buffer.get(), desired_password, desired_password_length);
+
+    SizedBuffer current_password_handle_buffer(current_password_handle_length);
+    if (current_password_handle) {
+        memcpy(current_password_handle_buffer.buffer.get(), current_password_handle,
+                current_password_handle_length);
+    }
+
+    SizedBuffer current_password_buffer(current_password_length);
+    if (current_password) {
+        memcpy(current_password_buffer.buffer.get(), current_password, current_password_length);
+    }
+
+    EnrollRequest request(uid, &current_password_handle_buffer, &desired_password_buffer,
+            &current_password_buffer);
+    EnrollResponse response;
+
+    gatekeeper_error_t error = Send(request, &response);
+
+    if (error == ERROR_RETRY) {
+        return response.retry_timeout;
+    } else if (error != ERROR_NONE) {
+        return -EINVAL;
+    }
+
+    *enrolled_password_handle = response.enrolled_password_handle.buffer.release();
+    *enrolled_password_handle_length = response.enrolled_password_handle.length;
+
+
+    return 0;
+}
+
+int TrustyGateKeeperDevice::Verify(uint32_t uid, uint64_t challenge,
+        const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+        const uint8_t *provided_password, uint32_t provided_password_length,
+        uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll) {
+    if (error_ != 0) {
+        return error_;
+    }
+
+    SizedBuffer password_handle_buffer(enrolled_password_handle_length);
+    memcpy(password_handle_buffer.buffer.get(), enrolled_password_handle,
+            enrolled_password_handle_length);
+    SizedBuffer provided_password_buffer(provided_password_length);
+    memcpy(provided_password_buffer.buffer.get(), provided_password, provided_password_length);
+
+    VerifyRequest request(uid, challenge, &password_handle_buffer, &provided_password_buffer);
+    VerifyResponse response;
+
+    gatekeeper_error_t error = Send(request, &response);
+
+    if (error == ERROR_RETRY) {
+        return response.retry_timeout;
+    } else if (error != ERROR_NONE) {
+        return -EINVAL;
+    }
+
+    if (auth_token != NULL && auth_token_length != NULL) {
+       *auth_token = response.auth_token.buffer.release();
+       *auth_token_length = response.auth_token.length;
+    }
+
+    if (request_reenroll != NULL) {
+        *request_reenroll = response.request_reenroll;
+    }
+
+    return 0;
+}
+
+gatekeeper_error_t TrustyGateKeeperDevice::Send(uint32_t command, const GateKeeperMessage& request,
+        GateKeeperMessage *response) {
+    uint32_t request_size = request.GetSerializedSize();
+    if (request_size > SEND_BUF_SIZE)
+        return ERROR_INVALID;
+    uint8_t send_buf[SEND_BUF_SIZE];
+    request.Serialize(send_buf, send_buf + request_size);
+
+    // Send it
+    uint8_t recv_buf[RECV_BUF_SIZE];
+    uint32_t response_size = RECV_BUF_SIZE;
+    int rc = trusty_gatekeeper_call(command, send_buf, request_size, recv_buf, &response_size);
+    if (rc < 0) {
+        ALOGE("error (%d) calling gatekeeper TA", rc);
+        return ERROR_INVALID;
+    }
+
+    const gatekeeper_message *msg = reinterpret_cast<gatekeeper_message *>(recv_buf);
+    const uint8_t *payload = msg->payload;
+
+    return response->Deserialize(payload, payload + response_size);
+}
+
+static inline TrustyGateKeeperDevice *convert_device(const gatekeeper_device *dev) {
+    return reinterpret_cast<TrustyGateKeeperDevice *>(const_cast<gatekeeper_device *>(dev));
+}
+
+/* static */
+int TrustyGateKeeperDevice::enroll(const struct gatekeeper_device *dev, uint32_t uid,
+            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+            const uint8_t *current_password, uint32_t current_password_length,
+            const uint8_t *desired_password, uint32_t desired_password_length,
+            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length) {
+
+    if (dev == NULL ||
+            enrolled_password_handle == NULL || enrolled_password_handle_length == NULL ||
+            desired_password == NULL || desired_password_length == 0)
+        return -EINVAL;
+
+    // Current password and current password handle go together
+    if (current_password_handle == NULL || current_password_handle_length == 0 ||
+            current_password == NULL || current_password_length == 0) {
+        current_password_handle = NULL;
+        current_password_handle_length = 0;
+        current_password = NULL;
+        current_password_length = 0;
+    }
+
+    return convert_device(dev)->Enroll(uid, current_password_handle, current_password_handle_length,
+            current_password, current_password_length, desired_password, desired_password_length,
+            enrolled_password_handle, enrolled_password_handle_length);
+
+}
+
+/* static */
+int TrustyGateKeeperDevice::verify(const struct gatekeeper_device *dev, uint32_t uid,
+        uint64_t challenge, const uint8_t *enrolled_password_handle,
+        uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
+        uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
+        bool *request_reenroll) {
+
+    if (dev == NULL || enrolled_password_handle == NULL ||
+            provided_password == NULL) {
+        return -EINVAL;
+    }
+
+    return convert_device(dev)->Verify(uid, challenge, enrolled_password_handle,
+            enrolled_password_handle_length, provided_password, provided_password_length,
+            auth_token, auth_token_length, request_reenroll);
+}
+};
diff --git a/trusty/gatekeeper/trusty_gatekeeper.h b/trusty/gatekeeper/trusty_gatekeeper.h
new file mode 100644 (file)
index 0000000..82108dc
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TRUSTY_GATEKEEPER_H
+#define TRUSTY_GATEKEEPER_H
+
+#include <hardware/gatekeeper.h>
+#include <gatekeeper/gatekeeper_messages.h>
+
+#include "gatekeeper_ipc.h"
+
+namespace gatekeeper {
+
+class TrustyGateKeeperDevice {
+    public:
+
+    TrustyGateKeeperDevice(const hw_module_t* module);
+    ~TrustyGateKeeperDevice();
+
+    hw_device_t* hw_device();
+
+    /**
+     * Enrolls password_payload, which should be derived from a user selected pin or password,
+     * with the authentication factor private key used only for enrolling authentication
+     * factor data.
+     *
+     * Returns: 0 on success or an error code less than 0 on error.
+     * On error, enrolled_password will not be allocated.
+     */
+    int Enroll(uint32_t uid, const uint8_t *current_password_handle,
+            uint32_t current_password_handle_length, const uint8_t *current_password,
+            uint32_t current_password_length, const uint8_t *desired_password,
+            uint32_t desired_password_length, uint8_t **enrolled_password_handle,
+            uint32_t *enrolled_password_handle_length);
+
+    /**
+     * Verifies provided_password matches expected_password after enrolling
+     * with the authentication factor private key.
+     *
+     * Implementations of this module may retain the result of this call
+     * to attest to the recency of authentication.
+     *
+     * On success, writes the address of a verification token to verification_token,
+     *
+     * Returns: 0 on success or an error code less than 0 on error
+     * On error, verification token will not be allocated
+     */
+    int Verify(uint32_t uid, uint64_t challenge, const uint8_t *enrolled_password_handle,
+            uint32_t enrolled_password_handle_length, const uint8_t *provided_password,
+            uint32_t provided_password_length, uint8_t **auth_token, uint32_t *auth_token_length,
+            bool *request_reenroll);
+
+    private:
+
+    gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,
+                           GateKeeperMessage* response);
+
+    gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse *response) {
+        return Send(GK_ENROLL, request, response);
+    }
+
+    gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse *response) {
+        return Send(GK_VERIFY, request, response);
+    }
+
+    // Static methods interfacing the HAL API with the TrustyGateKeeper device
+
+    /**
+     * Enrolls desired_password, which should be derived from a user selected pin or password,
+     * with the authentication factor private key used only for enrolling authentication
+     * factor data.
+     *
+     * If there was already a password enrolled, it should be provided in
+     * current_password_handle, along with the current password in current_password
+     * that should validate against current_password_handle.
+     *
+     * Returns: 0 on success or an error code less than 0 on error.
+     * On error, enrolled_password_handle will not be allocated.
+     */
+    static int enroll(const struct gatekeeper_device *dev, uint32_t uid,
+            const uint8_t *current_password_handle, uint32_t current_password_handle_length,
+            const uint8_t *current_password, uint32_t current_password_length,
+            const uint8_t *desired_password, uint32_t desired_password_length,
+            uint8_t **enrolled_password_handle, uint32_t *enrolled_password_handle_length);
+
+    /**
+     * Verifies provided_password matches enrolled_password_handle.
+     *
+     * Implementations of this module may retain the result of this call
+     * to attest to the recency of authentication.
+     *
+     * On success, writes the address of a verification token to auth_token,
+     * usable to attest password verification to other trusted services. Clients
+     * may pass NULL for this value.
+     *
+     * Returns: 0 on success or an error code less than 0 on error
+     * On error, verification token will not be allocated
+     */
+    static int verify(const struct gatekeeper_device *dev, uint32_t uid, uint64_t challenge,
+            const uint8_t *enrolled_password_handle, uint32_t enrolled_password_handle_length,
+            const uint8_t *provided_password, uint32_t provided_password_length,
+            uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
+
+    static int close_device(hw_device_t* dev);
+
+    gatekeeper_device device_;
+    int error_;
+
+};
+}
+
+#endif
+
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.c b/trusty/gatekeeper/trusty_gatekeeper_ipc.c
new file mode 100644 (file)
index 0000000..a1c319e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "TrustyGateKeeper"
+#include <cutils/log.h>
+#include <trusty/tipc.h>
+
+#include "trusty_gatekeeper_ipc.h"
+#include "gatekeeper_ipc.h"
+
+#define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
+
+static int handle_ = 0;
+
+int trusty_gatekeeper_connect() {
+    int rc = tipc_connect(TRUSTY_DEVICE_NAME, GATEKEEPER_PORT);
+    if (rc < 0) {
+        return rc;
+    }
+
+    handle_ = rc;
+    return 0;
+}
+
+int trusty_gatekeeper_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+                           uint32_t *out_size) {
+    if (handle_ == 0) {
+        ALOGE("not connected\n");
+        return -EINVAL;
+    }
+
+    size_t msg_size = in_size + sizeof(struct gatekeeper_message);
+    struct gatekeeper_message *msg = malloc(msg_size);
+    msg->cmd = cmd;
+    memcpy(msg->payload, in, in_size);
+
+    ssize_t rc = write(handle_, msg, msg_size);
+    free(msg);
+
+    if (rc < 0) {
+        ALOGE("failed to send cmd (%d) to %s: %s\n", cmd,
+                GATEKEEPER_PORT, strerror(errno));
+        return -errno;
+    }
+
+    rc = read(handle_, out, *out_size);
+    if (rc < 0) {
+        ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n",
+                cmd, GATEKEEPER_PORT, strerror(errno));
+        return -errno;
+    }
+
+    if ((size_t) rc < sizeof(struct gatekeeper_message)) {
+        ALOGE("invalid response size (%d)\n", (int) rc);
+        return -EINVAL;
+    }
+
+    msg = (struct gatekeeper_message *) out;
+
+    if ((cmd | GK_RESP_BIT) != msg->cmd) {
+        ALOGE("invalid command (%d)\n", msg->cmd);
+        return -EINVAL;
+    }
+
+    *out_size = ((size_t) rc) - sizeof(struct gatekeeper_message);
+    return rc;
+}
+
+void trusty_gatekeeper_disconnect() {
+    if (handle_ != 0) {
+        tipc_close(handle_);
+    }
+}
+
diff --git a/trusty/gatekeeper/trusty_gatekeeper_ipc.h b/trusty/gatekeeper/trusty_gatekeeper_ipc.h
new file mode 100644 (file)
index 0000000..f8de7f8
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+__BEGIN_DECLS
+
+int trusty_gatekeeper_connect();
+int trusty_gatekeeper_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,
+                           uint32_t *out_size);
+void trusty_gatekeeper_disconnect();
+
+__END_DECLS
diff --git a/trusty/libtrusty/Android.mk b/trusty/libtrusty/Android.mk
new file mode 100644 (file)
index 0000000..45fc079
--- /dev/null
@@ -0,0 +1,36 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# == libtrusty Static library ==
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtrusty
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := trusty.c
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+include $(BUILD_STATIC_LIBRARY)
+
+# ==  libtrusty shared library ==
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtrusty
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := trusty.c
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := liblog
+
+include $(BUILD_SHARED_LIBRARY)
similarity index 74%
rename from base/test_utils.h
rename to trusty/libtrusty/include/trusty/tipc.h
index 132d3a7..a3f2a3f 100644 (file)
  * limitations under the License.
  */
 
-#ifndef TEST_UTILS_H
-#define TEST_UTILS_H
+#ifndef _LIB_TIPC_H
+#define _LIB_TIPC_H
 
-class TemporaryFile {
- public:
-  TemporaryFile();
-  ~TemporaryFile();
+#ifdef __cplusplus
+extern "C" {
+#endif
 
-  int fd;
-  char filename[1024];
+int tipc_connect(const char *dev_name, const char *srv_name);
+int tipc_close(int fd);
 
- private:
-  void init(const char* tmp_dir);
-};
+#ifdef __cplusplus
+}
+#endif
 
-#endif // TEST_UTILS_H
+#endif
diff --git a/trusty/libtrusty/tipc-test/Android.mk b/trusty/libtrusty/tipc-test/Android.mk
new file mode 100644 (file)
index 0000000..80030fe
--- /dev/null
@@ -0,0 +1,29 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := tipc-test
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := tipc_test.c
+LOCAL_STATIC_LIBRARIES := libc libtrusty liblog
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+include $(BUILD_EXECUTABLE)
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
new file mode 100644 (file)
index 0000000..55d5ee6
--- /dev/null
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include <trusty/tipc.h>
+
+#define TIPC_DEFAULT_DEVNAME "/dev/trusty-ipc-dev0"
+
+static const char *dev_name = NULL;
+static const char *test_name = NULL;
+
+static const char *uuid_name = "com.android.ipc-unittest.srv.uuid";
+static const char *echo_name = "com.android.ipc-unittest.srv.echo";
+static const char *ta_only_name = "com.android.ipc-unittest.srv.ta_only";
+static const char *ns_only_name = "com.android.ipc-unittest.srv.ns_only";
+static const char *datasink_name = "com.android.ipc-unittest.srv.datasink";
+static const char *closer1_name = "com.android.ipc-unittest.srv.closer1";
+static const char *closer2_name = "com.android.ipc-unittest.srv.closer2";
+static const char *closer3_name = "com.android.ipc-unittest.srv.closer3";
+static const char *main_ctrl_name = "com.android.ipc-unittest.ctrl";
+
+static const char *_sopts = "hsvD:t:r:m:b:";
+static const struct option _lopts[] =  {
+       {"help",    no_argument,       0, 'h'},
+       {"silent",  no_argument,       0, 's'},
+       {"variable",no_argument,       0, 'v'},
+       {"dev",     required_argument, 0, 'D'},
+       {"repeat",  required_argument, 0, 'r'},
+       {"burst",   required_argument, 0, 'b'},
+       {"msgsize", required_argument, 0, 'm'},
+       {0, 0, 0, 0}
+};
+
+static const char *usage =
+"Usage: %s [options]\n"
+"\n"
+"options:\n"
+"  -h, --help            prints this message and exit\n"
+"  -D, --dev name        device name\n"
+"  -t, --test name       test to run\n"
+"  -r, --repeat cnt      repeat count\n"
+"  -m, --msgsize size    max message size\n"
+"  -v, --variable        variable message size\n"
+"  -s, --silent          silent\n"
+"\n"
+;
+
+static const char *usage_long =
+"\n"
+"The following tests are available:\n"
+"   connect      - connect to datasink service\n"
+"   connect_foo  - connect to non existing service\n"
+"   burst_write  - send messages to datasink service\n"
+"   echo         - send/receive messages to echo service\n"
+"   select       - test select call\n"
+"   blocked_read - test blocked read\n"
+"   closer1      - connection closed by remote (test1)\n"
+"   closer2      - connection closed by remote (test2)\n"
+"   closer3      - connection closed by remote (test3)\n"
+"   ta2ta-ipc    - execute TA to TA unittest\n"
+"   dev-uuid     - print device uuid\n"
+"   ta-access    - test ta-access flags\n"
+"\n"
+;
+
+static uint opt_repeat  = 1;
+static uint opt_msgsize = 32;
+static uint opt_msgburst = 32;
+static bool opt_variable = false;
+static bool opt_silent = false;
+
+static void print_usage_and_exit(const char *prog, int code, bool verbose)
+{
+       fprintf (stderr, usage, prog);
+       if (verbose)
+               fprintf (stderr, usage_long);
+       exit(code);
+}
+
+static void parse_options(int argc, char **argv)
+{
+       int c;
+       int oidx = 0;
+
+       while (1)
+       {
+               c = getopt_long (argc, argv, _sopts, _lopts, &oidx);
+               if (c == -1)
+                       break; /* done */
+
+               switch (c) {
+
+               case 'D':
+                       dev_name = strdup(optarg);
+               break;
+
+               case 't':
+                       test_name = strdup(optarg);
+               break;
+
+               case 'v':
+                       opt_variable = true;
+               break;
+
+               case 'r':
+                       opt_repeat = atoi(optarg);
+               break;
+
+               case 'm':
+                       opt_msgsize = atoi(optarg);
+               break;
+
+               case 'b':
+                       opt_msgburst = atoi(optarg);
+               break;
+
+               case 's':
+                       opt_silent = true;
+               break;
+
+               case 'h':
+                     print_usage_and_exit(argv[0], EXIT_SUCCESS, true);
+               break;
+
+               default:
+                     print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+               }
+       }
+}
+
+static int connect_test(uint repeat)
+{
+       uint i;
+       int  echo_fd;
+       int  dsink_fd;
+
+       if (!opt_silent) {
+               printf("%s: repeat = %u\n", __func__, repeat);
+       }
+
+       for (i = 0; i < repeat; i++) {
+               echo_fd = tipc_connect(dev_name, echo_name);
+               if (echo_fd < 0) {
+                       fprintf(stderr, "Failed to connect to '%s' service\n",
+                               "echo");
+               }
+               dsink_fd = tipc_connect(dev_name, datasink_name);
+               if (dsink_fd < 0) {
+                       fprintf(stderr, "Failed to connect to '%s' service\n",
+                               "datasink");
+               }
+
+               if (echo_fd >= 0) {
+                       tipc_close(echo_fd);
+               }
+               if (dsink_fd >= 0) {
+                       tipc_close(dsink_fd);
+               }
+       }
+
+       if (!opt_silent) {
+               printf("%s: done\n", __func__);
+       }
+
+       return 0;
+}
+
+static int connect_foo(uint repeat)
+{
+       uint i;
+       int  fd;
+
+       if (!opt_silent) {
+               printf("%s: repeat = %u\n", __func__, repeat);
+       }
+
+       for (i = 0; i < repeat; i++) {
+               fd = tipc_connect(dev_name, "foo");
+               if (fd >= 0) {
+                       fprintf(stderr, "succeeded to connect to '%s' service\n",
+                               "foo");
+                       tipc_close(fd);
+               }
+       }
+
+       if (!opt_silent) {
+               printf("%s: done\n", __func__);
+       }
+
+       return 0;
+}
+
+
+static int closer1_test(uint repeat)
+{
+       uint i;
+       int  fd;
+
+       if (!opt_silent) {
+               printf("%s: repeat = %u\n", __func__, repeat);
+       }
+
+       for (i = 0; i < repeat; i++) {
+               fd = tipc_connect(dev_name, closer1_name);
+               if (fd < 0) {
+                       fprintf(stderr, "Failed to connect to '%s' service\n",
+                               "closer1");
+                       continue;
+               }
+               if (!opt_silent) {
+                       printf("%s: connected\n", __func__);
+               }
+               tipc_close(fd);
+       }
+
+       if (!opt_silent) {
+               printf("%s: done\n", __func__);
+       }
+
+       return 0;
+}
+
+static int closer2_test(uint repeat)
+{
+       uint i;
+       int  fd;
+
+       if (!opt_silent) {
+               printf("%s: repeat = %u\n", __func__, repeat);
+       }
+
+       for (i = 0; i < repeat; i++) {
+               fd = tipc_connect(dev_name, closer2_name);
+               if (fd < 0) {
+                       if (!opt_silent) {
+                               printf("failed to connect to '%s' service\n", "closer2");
+                       }
+               } else {
+                       /* this should always fail */
+                       fprintf(stderr, "connected to '%s' service\n", "closer2");
+                       tipc_close(fd);
+               }
+       }
+
+       if (!opt_silent) {
+               printf("%s: done\n", __func__);
+       }
+
+       return 0;
+}
+
+static int closer3_test(uint repeat)
+{
+       uint i, j;
+       ssize_t rc;
+       int  fd[4];
+       char buf[64];
+
+       if (!opt_silent) {
+               printf("%s: repeat = %u\n", __func__, repeat);
+       }
+
+       for (i = 0; i < repeat; i++) {
+
+               /* open 4 connections to closer3 service */
+               for (j = 0; j < 4; j++) {
+                       fd[j] = tipc_connect(dev_name, closer3_name);
+                       if (fd[j] < 0) {
+                               fprintf(stderr, "fd[%d]: failed to connect to '%s' service\n", j, "closer3");
+                       } else {
+                               if (!opt_silent) {
+                                       printf("%s: fd[%d]=%d: connected\n", __func__, j, fd[j]);
+                               }
+                               memset(buf, i + j, sizeof(buf));
+                               rc = write(fd[j], buf, sizeof(buf));
+                               if (rc != sizeof(buf)) {
+                                       if (!opt_silent) {
+                                               printf("%s: fd[%d]=%d: write returned  = %zd\n",
+                                                       __func__, j, fd[j], rc);
+                                       }
+                                       perror("closer3_test: write");
+                               }
+                       }
+               }
+
+               /* sleep a bit */
+               sleep(1);
+
+               /* It is expected that they will be closed by remote */
+               for (j = 0; j < 4; j++) {
+                       if (fd[j] < 0)
+                               continue;
+                       rc = write(fd[j], buf, sizeof(buf));
+                       if (rc != sizeof(buf)) {
+                               if (!opt_silent) {
+                                       printf("%s: fd[%d]=%d: write returned = %zd\n",
+                                               __func__, j, fd[j], rc);
+                               }
+                               perror("closer3_test: write");
+                       }
+               }
+
+               /* then they have to be closed by remote */
+               for (j = 0; j < 4; j++) {
+                       if (fd[j] >= 0) {
+                               tipc_close(fd[j]);
+                       }
+               }
+       }
+
+       if (!opt_silent) {
+               printf("%s: done\n", __func__);
+       }
+
+       return 0;
+}
+
+
+static int echo_test(uint repeat, uint msgsz, bool var)
+{
+       uint i;
+       ssize_t rc;
+       size_t  msg_len;
+       int  echo_fd =-1;
+       char tx_buf[msgsz];
+       char rx_buf[msgsz];
+
+       if (!opt_silent) {
+               printf("%s: repeat %u: msgsz %u: variable %s\n",
+                       __func__, repeat, msgsz, var ? "true" : "false");
+       }
+
+       echo_fd = tipc_connect(dev_name, echo_name);
+       if (echo_fd < 0) {
+               fprintf(stderr, "Failed to connect to service\n");
+               return echo_fd;
+       }
+
+       for (i = 0; i < repeat; i++) {
+
+               msg_len = msgsz;
+               if (opt_variable && msgsz) {
+                       msg_len = rand() % msgsz;
+               }
+
+               memset(tx_buf, i + 1, msg_len);
+
+               rc = write(echo_fd, tx_buf, msg_len);
+               if ((size_t)rc != msg_len) {
+                       perror("echo_test: write");
+                       break;
+               }
+
+               rc = read(echo_fd, rx_buf, msg_len);
+               if (rc < 0) {
+                       perror("echo_test: read");
+                       break;
+               }
+
+               if ((size_t)rc != msg_len) {
+                       fprintf(stderr, "data truncated (%zu vs. %zu)\n",
+                                        rc, msg_len);
+                       continue;
+               }
+
+               if (memcmp(tx_buf, rx_buf, (size_t) rc)) {
+                       fprintf(stderr, "data mismatch\n");
+                       continue;
+               }
+       }
+
+       tipc_close(echo_fd);
+
+       if (!opt_silent) {
+               printf("%s: done\n",__func__);
+       }
+
+       return 0;
+}
+
+static int burst_write_test(uint repeat, uint msgburst, uint msgsz, bool var)
+{
+       int fd;
+       uint i, j;
+       ssize_t rc;
+       size_t  msg_len;
+       char tx_buf[msgsz];
+
+       if (!opt_silent) {
+               printf("%s: repeat %u: burst %u: msgsz %u: variable %s\n",
+                       __func__, repeat, msgburst, msgsz,
+                       var ? "true" : "false");
+       }
+
+       for (i = 0; i < repeat; i++) {
+
+               fd = tipc_connect(dev_name, datasink_name);
+               if (fd < 0) {
+                       fprintf(stderr, "Failed to connect to '%s' service\n",
+                               "datasink");
+                       break;
+               }
+
+               for (j = 0; j < msgburst; j++) {
+                       msg_len = msgsz;
+                       if (var && msgsz) {
+                               msg_len = rand() % msgsz;
+                       }
+
+                       memset(tx_buf, i + 1, msg_len);
+                       rc = write(fd, tx_buf, msg_len);
+                       if ((size_t)rc != msg_len) {
+                               perror("burst_test: write");
+                               break;
+                       }
+               }
+
+               tipc_close(fd);
+       }
+
+       if (!opt_silent) {
+               printf("%s: done\n",__func__);
+       }
+
+       return 0;
+}
+
+
+static int _wait_for_msg(int fd, uint msgsz, int timeout)
+{
+       int rc;
+       fd_set rfds;
+       uint msgcnt = 0;
+       char rx_buf[msgsz];
+       struct timeval tv;
+
+       if (!opt_silent) {
+               printf("waiting (%d) for msg\n", timeout);
+       }
+
+       FD_ZERO(&rfds);
+       FD_SET(fd, &rfds);
+
+       tv.tv_sec = timeout;
+       tv.tv_usec = 0;
+
+       for(;;) {
+               rc = select(fd+1, &rfds, NULL, NULL, &tv);
+
+               if (rc == 0) {
+                       if (!opt_silent) {
+                               printf("select timedout\n");
+                       }
+                       break;
+               }
+
+               if (rc == -1) {
+                       perror("select_test: select");
+                       return rc;
+               }
+
+               rc = read(fd, rx_buf, sizeof(rx_buf));
+               if (rc < 0) {
+                       perror("select_test: read");
+                       return rc;
+               } else {
+                       if (rc > 0) {
+                               msgcnt++;
+                       }
+               }
+       }
+
+       if (!opt_silent) {
+               printf("got %u messages\n", msgcnt);
+       }
+
+       return 0;
+}
+
+
+static int select_test(uint repeat, uint msgburst, uint msgsz)
+{
+       int fd;
+       uint i, j;
+       ssize_t rc;
+       char tx_buf[msgsz];
+
+       if (!opt_silent) {
+               printf("%s: repeat %u\n", __func__, repeat);
+       }
+
+       fd = tipc_connect(dev_name, echo_name);
+       if (fd < 0) {
+               fprintf(stderr, "Failed to connect to '%s' service\n",
+                       "echo");
+               return fd;
+       }
+
+       for (i = 0; i < repeat; i++) {
+
+               _wait_for_msg(fd, msgsz, 1);
+
+               if (!opt_silent) {
+                       printf("sending burst: %u msg\n", msgburst);
+               }
+
+               for (j = 0; j < msgburst; j++) {
+                       memset(tx_buf, i + j, msgsz);
+                       rc = write(fd, tx_buf, msgsz);
+                       if ((size_t)rc != msgsz) {
+                               perror("burst_test: write");
+                               break;
+                       }
+               }
+       }
+
+       tipc_close(fd);
+
+       if (!opt_silent) {
+               printf("%s: done\n",__func__);
+       }
+
+       return 0;
+}
+
+static int blocked_read_test(uint repeat)
+{
+       int fd;
+       uint i;
+       ssize_t rc;
+       char rx_buf[512];
+
+       if (!opt_silent) {
+               printf("%s: repeat %u\n", __func__, repeat);
+       }
+
+       fd = tipc_connect(dev_name, echo_name);
+       if (fd < 0) {
+               fprintf(stderr, "Failed to connect to '%s' service\n",
+                       "echo");
+               return fd;
+       }
+
+       for (i = 0; i < repeat; i++) {
+               rc = read(fd, rx_buf, sizeof(rx_buf));
+               if (rc < 0) {
+                       perror("select_test: read");
+                       break;
+               } else {
+                       if (!opt_silent) {
+                               printf("got %zd bytes\n", rc);
+                       }
+               }
+       }
+
+       tipc_close(fd);
+
+       if (!opt_silent) {
+               printf("%s: done\n",__func__);
+       }
+
+       return 0;
+}
+
+static int ta2ta_ipc_test(void)
+{
+       int fd;
+       char rx_buf[64];
+
+       if (!opt_silent) {
+               printf("%s:\n", __func__);
+       }
+
+       fd = tipc_connect(dev_name, main_ctrl_name);
+       if (fd < 0) {
+               fprintf(stderr, "Failed to connect to '%s' service\n",
+                       "main_ctrl");
+               return fd;
+       }
+
+       /* wait for test to complete */
+       (void) read(fd, rx_buf, sizeof(rx_buf));
+
+       tipc_close(fd);
+
+       return 0;
+}
+
+typedef struct uuid
+{
+       uint32_t time_low;
+       uint16_t time_mid;
+       uint16_t time_hi_and_version;
+       uint8_t clock_seq_and_node[8];
+} uuid_t;
+
+static void print_uuid(const char *dev, uuid_t *uuid)
+{
+       printf("%s:", dev);
+       printf("uuid: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+              uuid->time_low,
+              uuid->time_mid,
+              uuid->time_hi_and_version,
+              uuid->clock_seq_and_node[0],
+              uuid->clock_seq_and_node[1],
+              uuid->clock_seq_and_node[2],
+              uuid->clock_seq_and_node[3],
+              uuid->clock_seq_and_node[4],
+              uuid->clock_seq_and_node[5],
+              uuid->clock_seq_and_node[6],
+              uuid->clock_seq_and_node[7]
+              );
+}
+
+static int dev_uuid_test(void)
+{
+       int fd;
+       ssize_t rc;
+       uuid_t uuid;
+
+       fd = tipc_connect(dev_name, uuid_name);
+       if (fd < 0) {
+               fprintf(stderr, "Failed to connect to '%s' service\n",
+                       "uuid");
+               return fd;
+       }
+
+       /* wait for test to complete */
+       rc = read(fd, &uuid, sizeof(uuid));
+       if (rc < 0) {
+               perror("dev_uuid_test: read");
+       } else if (rc != sizeof(uuid)) {
+               fprintf(stderr, "unexpected uuid size (%d vs. %d)\n",
+                       (int)rc, (int)sizeof(uuid));
+       } else {
+               print_uuid(dev_name, &uuid);
+       }
+
+       tipc_close(fd);
+
+       return 0;
+}
+
+static int ta_access_test(void)
+{
+       int fd;
+
+       if (!opt_silent) {
+               printf("%s:\n", __func__);
+       }
+
+       fd = tipc_connect(dev_name, ta_only_name);
+       if (fd >= 0) {
+               fprintf(stderr, "Succeed to connect to '%s' service\n",
+                       "ta_only");
+               tipc_close(fd);
+       }
+
+       fd = tipc_connect(dev_name, ns_only_name);
+       if (fd < 0) {
+               fprintf(stderr, "Failed to connect to '%s' service\n",
+                       "ns_only");
+               return fd;
+       }
+       tipc_close(fd);
+
+       if (!opt_silent) {
+               printf("%s: done\n",__func__);
+       }
+
+       return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+       int rc = 0;
+
+       if (argc <= 1) {
+               print_usage_and_exit(argv[0], EXIT_FAILURE, false);
+       }
+
+       parse_options(argc, argv);
+
+       if (!dev_name) {
+               dev_name = TIPC_DEFAULT_DEVNAME;
+       }
+
+       if (!test_name) {
+               fprintf(stderr, "need a Test to run\n");
+               print_usage_and_exit(argv[0], EXIT_FAILURE, true);
+       }
+
+       if (strcmp(test_name, "connect") == 0) {
+               rc = connect_test(opt_repeat);
+       } else if (strcmp(test_name, "connect_foo") == 0) {
+               rc = connect_foo(opt_repeat);
+       } else if (strcmp(test_name, "burst_write") == 0) {
+               rc = burst_write_test(opt_repeat, opt_msgburst, opt_msgsize, opt_variable);
+       } else if (strcmp(test_name, "select") == 0) {
+               rc = select_test(opt_repeat, opt_msgburst,  opt_msgsize);
+       } else if (strcmp(test_name, "blocked_read") == 0) {
+               rc = blocked_read_test(opt_repeat);
+       } else if (strcmp(test_name, "closer1") == 0) {
+               rc = closer1_test(opt_repeat);
+       } else if (strcmp(test_name, "closer2") == 0) {
+               rc = closer2_test(opt_repeat);
+       } else if (strcmp(test_name, "closer3") == 0) {
+               rc = closer3_test(opt_repeat);
+       } else if (strcmp(test_name, "echo") == 0) {
+               rc = echo_test(opt_repeat, opt_msgsize, opt_variable);
+       } else if(strcmp(test_name, "ta2ta-ipc") == 0) {
+               rc = ta2ta_ipc_test();
+       } else if (strcmp(test_name, "dev-uuid") == 0) {
+               rc = dev_uuid_test();
+       } else if (strcmp(test_name, "ta-access") == 0) {
+               rc = ta_access_test();
+       } else {
+               fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
+               print_usage_and_exit(argv[0], EXIT_FAILURE, true);
+       }
+
+       return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
similarity index 68%
rename from adb/qemu_tracing.h
rename to trusty/libtrusty/tipc_ioctl.h
index ff42d4f..27da56a 100644 (file)
  * limitations under the License.
  */
 
-/*
- * Implements ADB tracing inside the emulator.
- */
+#ifndef _TIPC_IOCTL_H
+#define _TIPC_IOCTL_H
 
-#ifndef __QEMU_TRACING_H
-#define __QEMU_TRACING_H
+#include <linux/ioctl.h>
+#include <linux/types.h>
 
-/* Initializes connection with the adb-debug qemud service in the emulator. */
-int adb_qemu_trace_init(void);
-void adb_qemu_trace(const char* fmt, ...);
+#define TIPC_IOC_MAGIC                 'r'
+#define TIPC_IOC_CONNECT               _IOW(TIPC_IOC_MAGIC, 0x80, char *)
 
-#endif /* __QEMU_TRACING_H */
+#endif
diff --git a/trusty/libtrusty/trusty.c b/trusty/libtrusty/trusty.c
new file mode 100644 (file)
index 0000000..b6897ce
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "libtrusty"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cutils/log.h>
+
+#include "tipc_ioctl.h"
+
+int tipc_connect(const char *dev_name, const char *srv_name)
+{
+       int fd;
+       int rc;
+
+       fd = open(dev_name, O_RDWR);
+       if (fd < 0) {
+               rc = -errno;
+               ALOGE("%s: cannot open tipc device \"%s\": %s\n",
+                     __func__, dev_name, strerror(errno));
+               return rc < 0 ? rc : -1;
+       }
+
+       rc = ioctl(fd, TIPC_IOC_CONNECT, srv_name);
+       if (rc < 0) {
+               rc = -errno;
+               ALOGE("%s: can't connect to tipc service \"%s\" (err=%d)\n",
+                     __func__, srv_name, errno);
+               close(fd);
+               return rc < 0 ? rc : -1;
+       }
+
+       ALOGV("%s: connected to \"%s\" fd %d\n", __func__, srv_name, fd);
+       return fd;
+}
+
+void tipc_close(int fd)
+{
+       close(fd);
+}