summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin DeMartino <jjdemartino@google.com>2020-10-14 19:39:53 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2020-10-14 19:39:53 +0000
commit0d11af03e43f110b0bb160f7e20436d0043e3038 (patch)
tree48f8bcca856276ec73a86dd3fb26143d3ca64578
parent075666ebd0dee8d0c4a2efa54f7c324a3f67ee2a (diff)
parenta6c01e4e98d2b343dcecfc99611e2e6250c730db (diff)
Merge changes from topic "SP1A.200921.001" into s-keystone-qcom-dev
* changes: fs_mgr: adb-remount-test.sh: filter out more administrivia mounts. Merge SP1A.200921.001 Change-Id: I90b97c4e9fb10b1f45e74def404823eed5b1aaa8
-rw-r--r--TEST_MAPPING15
-rw-r--r--adb/Android.bp44
-rw-r--r--adb/README.md94
-rw-r--r--adb/adb_utils.h2
-rw-r--r--adb/client/file_sync_client.cpp4
-rw-r--r--adb/client/incremental.cpp6
-rw-r--r--adb/client/incremental_utils.cpp2
-rw-r--r--adb/crypto/Android.bp2
-rw-r--r--adb/crypto/rsa_2048_key.cpp28
-rw-r--r--adb/crypto/tests/Android.bp1
-rw-r--r--adb/daemon/main.cpp5
-rw-r--r--adb/fdevent/fdevent_test.h4
-rw-r--r--adb/sockets.cpp2
-rw-r--r--adb/sysdeps/env.cpp122
-rw-r--r--adb/sysdeps/env.h (renamed from trusty/libtrusty/tipc_ioctl.h)25
-rw-r--r--adb/sysdeps_win32.cpp49
-rwxr-xr-xadb/test_device.py25
-rwxr-xr-xbootstat/boot_reason_test.sh2
-rw-r--r--debuggerd/libdebuggerd/tombstone.cpp8
-rw-r--r--debuggerd/libdebuggerd/utility.cpp1
-rw-r--r--diagnose_usb/diagnose_usb.cpp2
-rw-r--r--fastboot/Android.bp3
-rw-r--r--fastboot/Android.mk1
-rw-r--r--fastboot/fastboot.cpp9
-rw-r--r--fs_mgr/TEST_MAPPING2
-rwxr-xr-xfs_mgr/fs_mgr.cpp89
-rw-r--r--fs_mgr/fs_mgr_fstab.cpp3
-rw-r--r--fs_mgr/include/fs_mgr.h14
-rw-r--r--fs_mgr/libdm/dm.cpp14
-rw-r--r--fs_mgr/libdm/include/libdm/dm.h7
-rw-r--r--fs_mgr/libfiemap/Android.bp27
-rw-r--r--fs_mgr/libfiemap/image_manager.cpp28
-rw-r--r--fs_mgr/libfiemap/image_test.cpp48
-rw-r--r--fs_mgr/libfiemap/include/libfiemap/image_manager.h2
-rw-r--r--fs_mgr/libfiemap/utility.cpp25
-rw-r--r--fs_mgr/libfiemap/utility.h4
-rw-r--r--fs_mgr/liblp/partition_opener.cpp4
-rw-r--r--fs_mgr/libsnapshot/Android.bp171
-rw-r--r--fs_mgr/libsnapshot/cow_api_test.cpp334
-rw-r--r--fs_mgr/libsnapshot/cow_decompress.cpp264
-rw-r--r--fs_mgr/libsnapshot/cow_decompress.h57
-rw-r--r--fs_mgr/libsnapshot/cow_reader.cpp256
-rw-r--r--fs_mgr/libsnapshot/cow_snapuserd_test.cpp351
-rw-r--r--fs_mgr/libsnapshot/cow_writer.cpp322
-rw-r--r--fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp432
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/cow_format.h107
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h109
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h101
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h99
-rw-r--r--fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h17
-rw-r--r--fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp690
-rw-r--r--fs_mgr/libsnapshot/partition_cow_creator_test.cpp16
-rw-r--r--fs_mgr/libsnapshot/snapshot_metadata_updater.cpp3
-rw-r--r--fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp13
-rw-r--r--fs_mgr/libsnapshot/snapshot_test.cpp97
-rw-r--r--fs_mgr/libsnapshot/snapuserd.cpp786
-rw-r--r--fs_mgr/libsnapshot/test_helpers.cpp5
-rwxr-xr-xfs_mgr/tests/adb-remount-test.sh8
-rw-r--r--fs_mgr/tools/dmctl.cpp22
-rw-r--r--healthd/Android.bp44
-rw-r--r--healthd/Android.mk36
-rw-r--r--healthd/AnimationParser.cpp4
-rw-r--r--healthd/AnimationParser.h4
-rw-r--r--healthd/AnimationParser_test.cpp (renamed from healthd/tests/AnimationParser_test.cpp)0
-rw-r--r--healthd/TEST_MAPPING7
-rw-r--r--healthd/animation.h20
-rw-r--r--healthd/healthd_mode_charger.cpp67
-rw-r--r--healthd/healthd_mode_charger.h5
-rw-r--r--healthd/healthd_mode_charger_test.cpp181
-rw-r--r--healthd/testdata/Android.bp20
-rw-r--r--healthd/testdata/empty/ensure_directory_creation.txt1
-rw-r--r--healthd/testdata/legacy/res/images/charger/battery_fail.png0
-rw-r--r--healthd/testdata/legacy/res/images/charger/battery_scale.png0
-rw-r--r--healthd/testdata/legacy/res/values/charger/animation.txt9
-rw-r--r--healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt9
-rw-r--r--healthd/testdata/product/product/etc/res/images/charger/battery_fail.png0
-rw-r--r--healthd/testdata/product/product/etc/res/images/charger/battery_scale.png0
-rw-r--r--healthd/testdata/product/product/etc/res/values/charger/animation.txt9
-rw-r--r--healthd/tests/Android.mk21
l---------include/private/android_filesystem_capability.h1
l---------include/private/android_logger.h1
l---------include/private/canned_fs_config.h1
-rw-r--r--include/private/fs_config.h4
l---------include/sysutils1
-rw-r--r--init/AndroidTest.xml3
-rw-r--r--init/builtins.cpp22
-rw-r--r--init/capabilities.cpp84
-rw-r--r--init/init.cpp8
-rw-r--r--init/mount_handler.cpp6
-rw-r--r--init/mount_namespace.cpp59
-rwxr-xr-xinit/perfboot.py6
-rw-r--r--init/property_service.cpp2
-rw-r--r--init/reboot.cpp18
-rw-r--r--init/service_utils.cpp11
-rw-r--r--init/subcontext.cpp9
-rw-r--r--init/sysprop/api/com.android.sysprop.init-latest.txt7
-rw-r--r--init/test_kill_services/init_kill_services_test.cpp2
-rw-r--r--libbacktrace/backtrace_test.cpp2
l---------libcrypto_utils/.clang-format1
-rw-r--r--libcrypto_utils/Android.bp2
-rw-r--r--libcrypto_utils/android_pubkey.cpp (renamed from libcrypto_utils/android_pubkey.c)83
-rw-r--r--libcutils/ashmem-dev.cpp26
-rw-r--r--libcutils/ashmem_test.cpp5
-rw-r--r--libcutils/include/cutils/trace.h3
-rw-r--r--libcutils/include/private/android_filesystem_config.h2
-rw-r--r--libcutils/qtaguid.cpp10
-rw-r--r--libcutils/trace-dev.cpp4
-rw-r--r--libcutils/uevent.cpp2
-rw-r--r--libkeyutils/keyutils_test.cpp2
-rw-r--r--liblog/Android.bp1
-rw-r--r--liblog/event_tag_map.cpp302
-rw-r--r--liblog/include/log/event_tag_map.h14
-rw-r--r--liblog/include/log/log_properties.h1
-rw-r--r--liblog/include/log/log_read.h7
-rw-r--r--liblog/include/private/android_logger.h15
-rw-r--r--liblog/liblog.map.txt3
-rw-r--r--liblog/logd_reader.cpp82
-rw-r--r--liblog/logd_writer.cpp58
-rw-r--r--liblog/properties.cpp243
-rw-r--r--liblog/tests/Android.bp1
-rw-r--r--liblog/tests/liblog_benchmark.cpp24
-rw-r--r--liblog/tests/liblog_global_state.cpp116
-rw-r--r--liblog/tests/liblog_test.cpp17
-rw-r--r--liblog/tests/log_read_test.cpp38
-rw-r--r--libmodprobe/libmodprobe.cpp1
-rw-r--r--libprocessgroup/profiles/cgroups.json24
-rw-r--r--libprocessgroup/profiles/cgroups.proto9
-rw-r--r--libprocessgroup/profiles/task_profiles.json14
-rw-r--r--libsparse/Android.bp1
-rw-r--r--libsparse/backed_block.cpp12
-rw-r--r--libsparse/backed_block.h10
-rw-r--r--libsparse/include/sparse/sparse.h16
-rw-r--r--libsparse/output_file.cpp39
-rw-r--r--libsparse/output_file.h10
-rw-r--r--libsparse/sparse.cpp8
-rw-r--r--libstats/pull/Android.bp30
-rw-r--r--libstats/pull/stats_pull_atom_callback.cpp4
-rw-r--r--libstats/socket/Android.bp1
-rw-r--r--libsysutils/src/NetlinkEvent.cpp2
-rw-r--r--libsysutils/src/SocketClient.cpp51
-rw-r--r--libunwindstack/DwarfSection.cpp24
-rw-r--r--libunwindstack/RegsX86_64.cpp5
-rw-r--r--libunwindstack/Unwinder.cpp28
-rw-r--r--libunwindstack/include/unwindstack/Error.h7
-rw-r--r--libunwindstack/include/unwindstack/Unwinder.h4
-rw-r--r--libunwindstack/tests/DwarfSectionImplTest.cpp35
-rw-r--r--libunwindstack/tests/RegsFake.h18
-rw-r--r--libunwindstack/tests/UnwinderTest.cpp345
-rw-r--r--libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp33
-rw-r--r--libutils/Android.bp56
-rw-r--r--libutils/CallStack_fuzz.cpp50
-rw-r--r--libutils/Looper_fuzz.cpp84
-rw-r--r--libutils/Looper_test.cpp44
-rw-r--r--libutils/Looper_test_pipe.h55
-rw-r--r--libutils/LruCache_fuzz.cpp74
-rwxr-xr-xlibutils/Printer_fuzz.cpp52
-rw-r--r--libutils/ProcessCallStack_fuzz.cpp77
-rw-r--r--libutils/PropertyMap.cpp214
-rwxr-xr-xlibutils/RWLock_fuzz.cpp50
-rwxr-xr-xlibutils/RefBase_fuzz.cpp103
-rw-r--r--libutils/StopWatch_fuzz.cpp45
-rw-r--r--libutils/String8.cpp8
-rw-r--r--libutils/SystemClock.cpp11
-rw-r--r--libutils/SystemClock_test.cpp20
-rw-r--r--libutils/Threads.cpp12
-rw-r--r--libutils/Unicode.cpp4
-rw-r--r--libutils/include/utils/AndroidThreads.h5
-rw-r--r--libutils/include/utils/Debug.h26
-rw-r--r--libutils/include/utils/Flattenable.h6
-rw-r--r--libutils/include/utils/PropertyMap.h106
-rw-r--r--libutils/include/utils/SystemClock.h1
-rw-r--r--libutils/include/utils/Vector.h8
-rw-r--r--logcat/logcat.cpp34
-rw-r--r--logcat/tests/logcat_test.cpp87
-rw-r--r--logd/Android.bp3
-rw-r--r--logd/ChattyLogBuffer.h1
-rw-r--r--logd/CommandListener.cpp112
-rw-r--r--logd/CommandListener.h1
-rw-r--r--logd/LogAudit.cpp8
-rw-r--r--logd/LogBuffer.h4
-rw-r--r--logd/LogBufferTest.cpp10
-rw-r--r--logd/LogBufferTest.h2
-rw-r--r--logd/LogKlog.cpp14
-rw-r--r--logd/LogReader.h2
-rw-r--r--logd/LogReaderThread.h1
-rw-r--r--logd/LogSize.cpp68
-rw-r--r--logd/LogSize.h34
-rw-r--r--logd/LogStatistics.h9
-rw-r--r--logd/LogTags.cpp1
-rw-r--r--logd/LogUtils.h3
-rw-r--r--logd/README.compression.md81
-rw-r--r--logd/README.property9
-rw-r--r--logd/ReplayMessages.cpp80
-rw-r--r--logd/SerializedLogBuffer.cpp144
-rw-r--r--logd/SerializedLogBuffer.h12
-rw-r--r--logd/SerializedLogChunk.cpp16
-rw-r--r--logd/SerializedLogChunkTest.cpp24
-rw-r--r--logd/SimpleLogBuffer.cpp15
-rw-r--r--logd/SimpleLogBuffer.h8
-rw-r--r--logd/doc_images/cpu_cuttlefish.pngbin0 -> 100511 bytes
-rw-r--r--logd/doc_images/cpu_walleye.pngbin0 -> 68462 bytes
-rw-r--r--logd/doc_images/memory_usage.pngbin0 -> 228594 bytes
-rw-r--r--logd/doc_images/total_log_count.pngbin0 -> 230463 bytes
-rw-r--r--logd/fuzz/log_buffer_log_fuzzer.cpp10
-rw-r--r--logd/logd_test.cpp150
-rw-r--r--logd/main.cpp38
-rw-r--r--property_service/libpropertyinfoserializer/property_info_serializer_test.cpp2
-rw-r--r--qemu_pipe/qemu_pipe.cpp1
-rw-r--r--rootdir/etc/public.libraries.android.txt1
-rw-r--r--rootdir/init.rc63
-rw-r--r--rootdir/ueventd.rc4
-rw-r--r--shell_and_utilities/README.md12
-rw-r--r--trusty/confirmationui/NotSoSecureInput.cpp2
-rw-r--r--trusty/gatekeeper/trusty_gatekeeper.cpp6
-rw-r--r--trusty/keymaster/Android.bp33
-rw-r--r--trusty/keymaster/TrustyKeymaster.cpp2
-rw-r--r--trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h13
-rw-r--r--trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml116
-rw-r--r--trusty/keymaster/set_attestation_key/set_attestation_key.cpp357
-rw-r--r--trusty/libtrusty/include/trusty/ipc.h77
-rw-r--r--trusty/libtrusty/include/trusty/tipc.h4
-rw-r--r--trusty/libtrusty/tipc-test/tipc_test.c115
-rw-r--r--trusty/libtrusty/trusty.c20
-rw-r--r--trusty/trusty-test.mk2
224 files changed, 7917 insertions, 2465 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 1abeb2e46..89bd66a57 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -16,13 +16,19 @@
"name": "adb_tls_connection_test"
},
{
+ "name": "CtsFsMgrTestCases"
+ },
+ {
"name": "CtsInitTestCases"
},
{
- "name": "debuggerd_test"
+ "name": "CtsLiblogTestCases"
},
{
- "name": "CtsFsMgrTestCases"
+ "name": "CtsLogdTestCases"
+ },
+ {
+ "name": "debuggerd_test"
},
{
"name": "fs_mgr_vendor_overlay_test"
@@ -61,5 +67,10 @@
{
"name": "propertyinfoserializer_tests"
}
+ ],
+ "imports": [
+ {
+ "path": "frameworks/base/tests/StagedInstallTest"
+ }
]
}
diff --git a/adb/Android.bp b/adb/Android.bp
index 8a3c75876..df2c0f965 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -117,6 +117,7 @@ cc_defaults {
static_libs: [
"libadb_crypto",
"libadb_pairing_connection",
+ "libadb_sysdeps",
"libadb_tls_connection",
"libadbd",
"libadbd_core",
@@ -167,6 +168,7 @@ libadb_srcs = [
"services.cpp",
"sockets.cpp",
"socket_spec.cpp",
+ "sysdeps/env.cpp",
"sysdeps/errno.cpp",
"transport.cpp",
"transport_fd.cpp",
@@ -261,6 +263,45 @@ cc_library_host_static {
],
}
+cc_library {
+ name: "libadb_sysdeps",
+ defaults: ["adb_defaults"],
+ recovery_available: true,
+ host_supported: true,
+ compile_multilib: "both",
+ min_sdk_version: "apex_inherit",
+ // This library doesn't use build::GetBuildNumber()
+ use_version_lib: false,
+
+ srcs: [
+ "sysdeps/env.cpp",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ target: {
+ windows: {
+ enabled: true,
+ ldflags: ["-municode"],
+ },
+ },
+
+ export_include_dirs: ["."],
+
+ visibility: [
+ "//system/core/adb:__subpackages__",
+ "//bootable/recovery/minadbd:__subpackages__",
+ ],
+
+ apex_available: [
+ "com.android.adbd",
+ "test_com.android.adbd",
+ ],
+}
+
cc_test_host {
name: "adb_test",
defaults: ["adb_defaults"],
@@ -274,6 +315,7 @@ cc_test_host {
"libadb_pairing_auth_static",
"libadb_pairing_connection_static",
"libadb_protos_static",
+ "libadb_sysdeps",
"libadb_tls_connection_static",
"libbase",
"libcutils",
@@ -330,6 +372,7 @@ cc_binary_host {
"libadb_pairing_auth",
"libadb_pairing_connection",
"libadb_protos",
+ "libadb_sysdeps",
"libadb_tls_connection",
"libandroidfw",
"libapp_processes_protos_full",
@@ -833,6 +876,7 @@ cc_test_host {
"libadb_pairing_auth_static",
"libadb_pairing_connection_static",
"libadb_protos_static",
+ "libadb_sysdeps",
"libadb_tls_connection_static",
"libandroidfw",
"libbase",
diff --git a/adb/README.md b/adb/README.md
new file mode 100644
index 000000000..224387c48
--- /dev/null
+++ b/adb/README.md
@@ -0,0 +1,94 @@
+# ADB Internals
+
+If you are new to adb source code, you should start by reading [OVERVIEW.TXT](OVERVIEW.TXT) which describes the three components of adb pipeline.
+
+This document is here to boost what can be achieved within a "window of naive interest". You will not find function or class documentation here but rather the "big picture" which should allow you to build a mental map to help navigate the code.
+
+## Three components of adb pipeline
+
+As outlined in the overview, this codebase generates three components (Client, Server (a.k.a Host), and Daemon (a.k.a adbd)). The central part is the Server which runs on the Host computer. On one side the Server exposes a "Smart Socket" to Clients such as adb or DDMLIB. On the other side, the Server continuously monitors for connecting Daemons (as USB devices or TCP emulator). Communication with a device is done with a Transport.
+
+```
++----------+ +------------------------+
+| ADB +----------+ | ADB SERVER | +----------+
+| CLIENT | | | | (USB)| ADBD |
++----------+ | | Transport+-------------+ (DEVICE) |
+ | | | +----------+
++----------- | | |
+| ADB | v + | +----------+
+| CLIENT +--------->SmartSocket | (USB)| ADBD |
++----------+ ^ | (TCP/IP) Transport+-------------+ (DEVICE) |
+ | | | +----------+
++----------+ | | |
+| DDMLIB | | | Transport+--+ +----------+
+| CLIENT +----------+ | | | (TCP/IP)| ADBD |
++----------+ +------------------------+ +----------|(EMULATOR)|
+ +----------+
+```
+
+The Client and the Server are contained in the same executable and both run on the Host machine. Code sections specific to the Host is enclosed within `ADB_HOST` guard. adbd runs on the Android Device. Daemon specific code is enclosed in `!ADB_HOST` but also sometimes with-in `__ANDROID__` guard.
+
+
+## "SMART SOCKET" and TRANSPORT
+
+A smart socket is a simple TCP socket with a smart protocol built on top of it. This is what Clients connect onto from the Host side. The Client must always initiate communication via a human readable request but the response format varies. The smart protocol is documented in [SERVICES.TXT](SERVICES.TXT).
+
+On the other side, the Server communicate with a device via a Transport. adb initially targeted devices connecting over USB, which is restricted to a fixed number of data streams. Therefore, adb multiplexes multiple byte streams over a single pipe via Transport. When devices connecting over other mechanisms (e.g. emulators over TCP) were introduced, the existing transport protocol was maintained.
+
+## THREADING MODEL and FDEVENT system
+
+At the heart of both the Server and Daemon is a main thread running an fdevent loop, which is an platform-independent abstraction over poll/epoll/WSAPoll monitoring file descriptors events. Requests and services are usually server from the main thread but some service requests result in new threads being spawned.
+
+To allow for operations to run on the Main thread, fdevent features a RunQueue combined with an interrupt fd to force polling to return.
+
+```
++------------+ +-------------------------^
+| RUNQUEUE | | |
++------------+ | POLLING (Main thread) |
+| Function<> | | |
++------------+ | |
+| Function<> | ^-^-------^-------^------^^
++------------+ | | | |
+| ... | | | | |
++------------+ | | | |
+| | | | | |
+|============| | | | |
+|Interrupt fd+------+ +----+ +----+ +----+
++------------+ fd Socket Pipe
+```
+
+## ASOCKET, APACKET, and AMESSAGE
+
+The asocket, apacket, and amessage constructs exist only to wrap data while it transits on a Transport. An asocket handles a stream of apackets. An apacket consists in a amessage header featuring a command (`A_SYNC`, `A_OPEN`, `A_CLSE`, `A_WRTE`, `A_OKAY`, ...) followed by a payload (find more documentation in [protocol.txt](protocol.txt). There is no `A_READ` command because an asocket is unidirectional. To model a bi-directional stream, asocket have a peer which go in the opposite direction.
+
+An asocket features a buffer where the elemental unit is an apacket. Is traffic is inbound, the buffer stores apacket until they are consumed. If the traffic is oubound, the buffer store apackets until they are sent down the wire (with `A_WRTE` commands).
+
+```
++---------------------ASocket------------------------+
+ | |
+ | +----------------APacket Queue------------------+ |
+ | | | |
+ | | APacket APacket APacket | |
+ | | +--------+ +--------+ +--------+ | |
+ | | |AMessage| |AMessage| |AMessage| | |
+ | | +--------+ +--------+ +--------+ | |
+ | | | | | | | | | |
+ | | ..... | | | | | | | |
+ | | | Data | | Data | | Data | | |
+ | | | | | | | | | |
+ | | | | | | | | | |
+ | | +--------+ +--------+ +--------+ | |
+ | | | |
+ | +-----------------------------------------------+ |
+ +---------------------------------------------------+
+```
+
+This system allows to multiplex data streams on an unique byte stream. Without entering too much into details, the amessage fields arg1 and arg2 are used alike in the TCP protocol where local and remote ports identify an unique stream. Note that unlike TCP which feature an "unacknowledged-send window", an apacket is sent only after the previous one has been confirmed to be received.
+
+The two types of asocket (Remote and Local) differentiate between outbound and inbound traffic.
+
+## adbd <-> APPPLICATION communication
+
+This pipeline is detailed in [services.cpp](services.cpp). The JDWP extension implemented by Dalvik/ART are documented in:
+- platform/dalvik/+/master/docs/debugmon.html
+- platform/dalvik/+/master/docs/debugger.html
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 66cba121b..e72d8b6f8 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -55,7 +55,7 @@ std::string perror_str(const char* msg);
bool set_file_block_mode(borrowed_fd fd, bool block);
-// Given forward/reverse targets, returns true if they look sane. If an error is found, fills
+// Given forward/reverse targets, returns true if they look valid. If an error is found, fills
// |error| and returns false.
// Currently this only checks "tcp:" targets. Additional checking could be added for other targets
// if needed.
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 8bbe2a878..2e8b97566 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -1053,7 +1053,7 @@ static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::s
if (!sc.SendSmallFile(rpath, mode, lpath, rpath, mtime, buf, data_length, dry_run)) {
return false;
}
- return sc.ReadAcknowledgements();
+ return sc.ReadAcknowledgements(sync);
#endif
}
@@ -1077,7 +1077,7 @@ static bool sync_send(SyncConnection& sc, const std::string& lpath, const std::s
return false;
}
}
- return sc.ReadAcknowledgements();
+ return sc.ReadAcknowledgements(sync);
}
static bool sync_recv_v1(SyncConnection& sc, const char* rpath, const char* lpath, const char* name,
diff --git a/adb/client/incremental.cpp b/adb/client/incremental.cpp
index 3033059d0..60735f8c5 100644
--- a/adb/client/incremental.cpp
+++ b/adb/client/incremental.cpp
@@ -55,9 +55,10 @@ static std::pair<unique_fd, std::vector<char>> read_signature(Size file_size,
return {};
}
- std::vector<char> invalid_signature;
+ auto [signature, tree_size] = read_id_sig_headers(fd);
- if (st.st_size > kMaxSignatureSize) {
+ std::vector<char> invalid_signature;
+ if (signature.size() > kMaxSignatureSize) {
if (!silent) {
fprintf(stderr, "Signature is too long. Max allowed is %d. Abort.\n",
kMaxSignatureSize);
@@ -65,7 +66,6 @@ static std::pair<unique_fd, std::vector<char>> read_signature(Size file_size,
return {std::move(fd), std::move(invalid_signature)};
}
- auto [signature, tree_size] = read_id_sig_headers(fd);
if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
if (!silent) {
fprintf(stderr,
diff --git a/adb/client/incremental_utils.cpp b/adb/client/incremental_utils.cpp
index dd117d228..d8b10e18b 100644
--- a/adb/client/incremental_utils.cpp
+++ b/adb/client/incremental_utils.cpp
@@ -325,7 +325,7 @@ static std::vector<int32_t> InstallationPriorityBlocks(borrowed_fd fd, Size file
}
std::vector<int32_t> installationPriorityBlocks;
- ZipEntry entry;
+ ZipEntry64 entry;
std::string_view entryName;
while (Next(cookie, &entry, &entryName) == 0) {
if (entryName == "classes.dex"sv) {
diff --git a/adb/crypto/Android.bp b/adb/crypto/Android.bp
index 9d14b030a..e2c27f1f3 100644
--- a/adb/crypto/Android.bp
+++ b/adb/crypto/Android.bp
@@ -48,6 +48,7 @@ cc_defaults {
shared_libs: [
"libadb_protos",
+ "libadb_sysdeps",
"libbase",
"liblog",
"libcrypto",
@@ -76,5 +77,6 @@ cc_library_static {
static_libs: [
"libadb_protos_static",
+ "libadb_sysdeps",
],
}
diff --git a/adb/crypto/rsa_2048_key.cpp b/adb/crypto/rsa_2048_key.cpp
index 7911af95f..6d9ee3054 100644
--- a/adb/crypto/rsa_2048_key.cpp
+++ b/adb/crypto/rsa_2048_key.cpp
@@ -20,32 +20,11 @@
#include <crypto_utils/android_pubkey.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
+#include <sysdeps/env.h>
namespace adb {
namespace crypto {
-namespace {
-std::string get_user_info() {
- std::string hostname;
- if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
-#if !defined(_WIN32)
- char buf[64];
- if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
-#endif
- if (hostname.empty()) hostname = "unknown";
-
- std::string username;
- if (getenv("LOGNAME")) username = getenv("LOGNAME");
-#if !defined(_WIN32)
- if (username.empty() && getlogin()) username = getlogin();
-#endif
- if (username.empty()) hostname = "unknown";
-
- return " " + username + "@" + hostname;
-}
-
-} // namespace
-
bool CalculatePublicKey(std::string* out, RSA* private_key) {
uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
@@ -63,7 +42,10 @@ bool CalculatePublicKey(std::string* out, RSA* private_key) {
size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
sizeof(binary_key_data));
out->resize(actual_length);
- out->append(get_user_info());
+ out->append(" ");
+ out->append(sysdeps::GetLoginNameUTF8());
+ out->append("@");
+ out->append(sysdeps::GetHostNameUTF8());
return true;
}
diff --git a/adb/crypto/tests/Android.bp b/adb/crypto/tests/Android.bp
index b32dcf731..b04105545 100644
--- a/adb/crypto/tests/Android.bp
+++ b/adb/crypto/tests/Android.bp
@@ -35,6 +35,7 @@ cc_test {
static_libs: [
"libadb_crypto_static",
"libadb_protos_static",
+ "libadb_sysdeps",
],
test_suites: ["device-tests"],
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index eb28668b9..8c41c5e2d 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -108,9 +108,12 @@ static void drop_privileges(int server_port) {
// AID_NET_BW_STATS to read out qtaguid statistics
// AID_READPROC for reading /proc entries across UID boundaries
// AID_UHID for using 'hid' command to read/write to /dev/uhid
+ // AID_EXT_DATA_RW for writing to /sdcard/Android/data (devices without sdcardfs)
+ // AID_EXT_OBB_RW for writing to /sdcard/Android/obb (devices without sdcardfs)
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, AID_UHID};
+ AID_NET_BW_STATS, AID_READPROC, AID_UHID, AID_EXT_DATA_RW,
+ AID_EXT_OBB_RW};
minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
// Don't listen on a port (default 5037) if running in secure mode.
diff --git a/adb/fdevent/fdevent_test.h b/adb/fdevent/fdevent_test.h
index ecda4da97..fcbf181dc 100644
--- a/adb/fdevent/fdevent_test.h
+++ b/adb/fdevent/fdevent_test.h
@@ -65,7 +65,7 @@ class FdeventTest : public ::testing::Test {
ASSERT_EQ(0u, fdevent_installed_count());
}
- // Register a dummy socket used to wake up the fdevent loop to tell it to die.
+ // Register a placeholder socket used to wake up the fdevent loop to tell it to die.
void PrepareThread() {
int dummy_fds[2];
if (adb_socketpair(dummy_fds) != 0) {
@@ -84,7 +84,7 @@ class FdeventTest : public ::testing::Test {
}
size_t GetAdditionalLocalSocketCount() {
- // dummy socket installed in PrepareThread()
+ // placeholder socket installed in PrepareThread()
return 1;
}
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 13a473776..33b952418 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -856,7 +856,7 @@ static int smart_socket_enqueue(asocket* s, apacket::payload_type data) {
s->peer->shutdown = nullptr;
s->peer->close = local_socket_close_notify;
s->peer->peer = nullptr;
- /* give him our transport and upref it */
+ /* give them our transport and upref it */
s->peer->transport = s->transport;
connect_to_remote(s->peer, std::string_view(s->smart_socket_data).substr(4));
diff --git a/adb/sysdeps/env.cpp b/adb/sysdeps/env.cpp
new file mode 100644
index 000000000..40587280c
--- /dev/null
+++ b/adb/sysdeps/env.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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/env.h"
+
+#ifdef _WIN32
+#include <lmcons.h>
+#include <windows.h>
+#endif // _WIN32
+
+#include <android-base/utf8.h>
+
+namespace adb {
+namespace sysdeps {
+
+std::optional<std::string> GetEnvironmentVariable(std::string_view var) {
+ if (var.empty()) {
+ return std::nullopt;
+ }
+
+#ifdef _WIN32
+ constexpr size_t kMaxEnvVarSize = 32767;
+ wchar_t wbuf[kMaxEnvVarSize];
+ std::wstring wvar;
+ if (!android::base::UTF8ToWide(var.data(), &wvar)) {
+ return std::nullopt;
+ }
+
+ auto sz = ::GetEnvironmentVariableW(wvar.data(), wbuf, sizeof(wbuf));
+ if (sz == 0) {
+ return std::nullopt;
+ }
+
+ std::string val;
+ if (!android::base::WideToUTF8(wbuf, &val)) {
+ return std::nullopt;
+ }
+
+ return std::make_optional(val);
+#else // !_WIN32
+ const char* val = getenv(var.data());
+ if (val == nullptr) {
+ return std::nullopt;
+ }
+
+ return std::make_optional(std::string(val));
+#endif
+}
+
+#ifdef _WIN32
+constexpr char kHostNameEnvVar[] = "COMPUTERNAME";
+constexpr char kUserNameEnvVar[] = "USERNAME";
+#else
+constexpr char kHostNameEnvVar[] = "HOSTNAME";
+constexpr char kUserNameEnvVar[] = "LOGNAME";
+#endif
+
+std::string GetHostNameUTF8() {
+ const auto hostName = GetEnvironmentVariable(kHostNameEnvVar);
+ if (hostName && !hostName->empty()) {
+ return *hostName;
+ }
+
+#ifdef _WIN32
+ wchar_t wbuf[MAX_COMPUTERNAME_LENGTH + 1];
+ DWORD size = sizeof(wbuf);
+ if (!GetComputerNameW(wbuf, &size) || size == 0) {
+ return "";
+ }
+
+ std::string name;
+ if (!android::base::WideToUTF8(wbuf, &name)) {
+ return "";
+ }
+
+ return name;
+#else // !_WIN32
+ char buf[256];
+ return (gethostname(buf, sizeof(buf)) == -1) ? "" : buf;
+#endif // _WIN32
+}
+
+std::string GetLoginNameUTF8() {
+ const auto userName = GetEnvironmentVariable(kUserNameEnvVar);
+ if (userName && !userName->empty()) {
+ return *userName;
+ }
+
+#ifdef _WIN32
+ wchar_t wbuf[UNLEN + 1];
+ DWORD size = sizeof(wbuf);
+ if (!GetUserNameW(wbuf, &size) || size == 0) {
+ return "";
+ }
+
+ std::string login;
+ if (!android::base::WideToUTF8(wbuf, &login)) {
+ return "";
+ }
+
+ return login;
+#else // !_WIN32
+ const char* login = getlogin();
+ return login ? login : "";
+#endif // _WIN32
+}
+
+} // namespace sysdeps
+} // namespace adb
diff --git a/trusty/libtrusty/tipc_ioctl.h b/adb/sysdeps/env.h
index 27da56a9e..b39b675cb 100644
--- a/trusty/libtrusty/tipc_ioctl.h
+++ b/adb/sysdeps/env.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,22 @@
* limitations under the License.
*/
-#ifndef _TIPC_IOCTL_H
-#define _TIPC_IOCTL_H
+#pragma once
-#include <linux/ioctl.h>
-#include <linux/types.h>
+#include <optional>
+#include <string>
-#define TIPC_IOC_MAGIC 'r'
-#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char *)
+namespace adb {
+namespace sysdeps {
-#endif
+// Attempts to retrieve the environment variable value for |var|. Returns std::nullopt
+// if unset.
+std::optional<std::string> GetEnvironmentVariableUTF8(std::string_view var);
+
+// Gets the host name of the system. Returns empty string on failure.
+std::string GetHostNameUTF8();
+// Gets the current login user. Returns empty string on failure.
+std::string GetLoginNameUTF8();
+
+} // namespace sysdeps
+} // namespace adb
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index be82bc0d1..217a6b7f8 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -1010,55 +1010,6 @@ int adb_register_socket(SOCKET s) {
return _fh_to_int(f);
}
-static bool isBlankStr(const char* str) {
- for (; *str != '\0'; ++str) {
- if (!isblank(*str)) {
- return false;
- }
- }
- return true;
-}
-
-int adb_gethostname(char* name, size_t len) {
- const char* computerName = adb_getenv("COMPUTERNAME");
- if (computerName && !isBlankStr(computerName)) {
- strncpy(name, computerName, len);
- name[len - 1] = '\0';
- return 0;
- }
-
- wchar_t buffer[MAX_COMPUTERNAME_LENGTH + 1];
- DWORD size = sizeof(buffer);
- if (!GetComputerNameW(buffer, &size)) {
- return -1;
- }
- std::string name_utf8;
- if (!android::base::WideToUTF8(buffer, &name_utf8)) {
- return -1;
- }
-
- strncpy(name, name_utf8.c_str(), len);
- name[len - 1] = '\0';
- return 0;
-}
-
-int adb_getlogin_r(char* buf, size_t bufsize) {
- wchar_t buffer[UNLEN + 1];
- DWORD len = sizeof(buffer);
- if (!GetUserNameW(buffer, &len)) {
- return -1;
- }
-
- std::string login;
- if (!android::base::WideToUTF8(buffer, &login)) {
- return -1;
- }
-
- strncpy(buf, login.c_str(), bufsize);
- buf[bufsize - 1] = '\0';
- return 0;
-}
-
#undef accept
int adb_socket_accept(borrowed_fd serverfd, struct sockaddr* addr, socklen_t* addrlen) {
FH serverfh = _fh_from_int(serverfd, __func__);
diff --git a/adb/test_device.py b/adb/test_device.py
index c1caafcf6..a92d4a711 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -1271,6 +1271,31 @@ class FileOperationsTest:
if temp_dir is not None:
shutil.rmtree(temp_dir)
+ def test_push_sync_multiple(self):
+ """Sync multiple host directories to a specific path."""
+
+ try:
+ temp_dir = tempfile.mkdtemp()
+ temp_files = make_random_host_files(in_dir=temp_dir, num_files=32)
+
+ device_dir = posixpath.join(self.DEVICE_TEMP_DIR, 'sync_src_dst')
+
+ # Clean up any stale files on the device.
+ device = adb.get_device() # pylint: disable=no-member
+ device.shell(['rm', '-rf', device_dir])
+ device.shell(['mkdir', '-p', device_dir])
+
+ host_paths = [os.path.join(temp_dir, x.base_name) for x in temp_files]
+ device.push(host_paths, device_dir, sync=True)
+
+ self.verify_sync(device, temp_files, device_dir)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if temp_dir is not None:
+ shutil.rmtree(temp_dir)
+
+
def test_push_dry_run_nonexistent_file(self):
"""Push with dry run."""
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 2f2919f2a..7cff7dc9f 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -1331,7 +1331,7 @@ if [ X"--macros" != X"${1}" ]; then
shift
fi
- # Check if all conditions for the script are sane
+ # Check if all conditions for the script are valid
if [ -z "${ANDROID_SERIAL}" ]; then
ndev=`(
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index face02bd7..7af99c94c 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -174,12 +174,8 @@ static void dump_signal_info(log_t* log, const ThreadInfo& thread_info,
}
static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
- // Blacklist logd, logd.reader, logd.writer, logd.auditd, logd.control ...
- // TODO: Why is this controlled by thread name?
- if (thread_info.thread_name == "logd" ||
- android::base::StartsWith(thread_info.thread_name, "logd.")) {
- log->should_retrieve_logcat = false;
- }
+ // Don't try to collect logs from the threads that implement the logging system itself.
+ if (thread_info.uid == AID_LOGD) log->should_retrieve_logcat = false;
_LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s >>> %s <<<\n", thread_info.pid,
thread_info.tid, thread_info.thread_name.c_str(), thread_info.process_name.c_str());
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index f43092c21..4e6df09c9 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -44,7 +44,6 @@
using android::base::unique_fd;
-// Whitelist output desired in the logcat output.
bool is_allowed_in_logcat(enum logtype ltype) {
if ((ltype == HEADER)
|| (ltype == REGISTERS)
diff --git a/diagnose_usb/diagnose_usb.cpp b/diagnose_usb/diagnose_usb.cpp
index 5695ecec5..35edb5e05 100644
--- a/diagnose_usb/diagnose_usb.cpp
+++ b/diagnose_usb/diagnose_usb.cpp
@@ -49,7 +49,7 @@ static std::string GetUdevProblem() {
// additionally just to be sure.
if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
// The user is in plugdev so the problem is likely with the udev rules.
- return "user in plugdev group; are your udev rules wrong?";
+ return "missing udev rules? user is in the plugdev group";
}
passwd* pwd = getpwuid(getuid());
return android::base::StringPrintf("user %s is not in the plugdev group",
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index bdb786c49..81ebf4392 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -251,7 +251,7 @@ cc_library_host_static {
darwin: {
srcs: ["usb_osx.cpp"],
},
- linux_glibc: {
+ linux: {
srcs: ["usb_linux.cpp"],
},
},
@@ -271,6 +271,7 @@ cc_binary_host {
required: [
"mke2fs",
"make_f2fs",
+ "make_f2fs_casefold",
],
dist: {
targets: [
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index fd009e768..0e918a35a 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -21,6 +21,7 @@ LOCAL_PATH:= $(call my-dir)
my_dist_files := $(SOONG_HOST_OUT_EXECUTABLES)/mke2fs
my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/e2fsdroid
my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs_casefold
my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/sload_f2fs
$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
my_dist_files :=
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index d33c98770..4bf791ecf 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -526,12 +526,15 @@ static std::vector<char> LoadBootableImage(const std::string& kernel, const std:
static bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,
std::vector<char>* out) {
- ZipEntry zip_entry;
+ ZipEntry64 zip_entry;
if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name.c_str());
return false;
}
+ if (zip_entry.uncompressed_length > std::numeric_limits<size_t>::max()) {
+ die("entry '%s' is too large: %" PRIu64, entry_name.c_str(), zip_entry.uncompressed_length);
+ }
out->resize(zip_entry.uncompressed_length);
fprintf(stderr, "extracting %s (%zu MB) to RAM...\n", entry_name.c_str(),
@@ -637,14 +640,14 @@ static void delete_fbemarker_tmpdir(const std::string& dir) {
static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
unique_fd fd(make_temporary_fd(entry_name));
- ZipEntry zip_entry;
+ ZipEntry64 zip_entry;
if (FindEntry(zip, entry_name, &zip_entry) != 0) {
fprintf(stderr, "archive does not contain '%s'\n", entry_name);
errno = ENOENT;
return -1;
}
- fprintf(stderr, "extracting %s (%" PRIu32 " MB) to disk...", entry_name,
+ fprintf(stderr, "extracting %s (%" PRIu64 " MB) to disk...", entry_name,
zip_entry.uncompressed_length / 1024 / 1024);
double start = now();
int error = ExtractEntryToFile(zip, &zip_entry, fd);
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 6cd043013..a34940824 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -7,7 +7,7 @@
"name": "liblp_test"
},
{
- "name": "fiemap_image_test_presubmit"
+ "name": "fiemap_image_test"
},
{
"name": "fiemap_writer_test"
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index a7087e7af..a8ba68c3c 100755
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -308,13 +308,10 @@ static bool is_ext4_superblock_valid(const struct ext4_super_block* es) {
return true;
}
-static bool needs_block_encryption(const FstabEntry& entry);
-static bool should_use_metadata_encryption(const FstabEntry& entry);
-
// Read the primary superblock from an ext4 filesystem. On failure return
// false. If it's not an ext4 filesystem, also set FS_STAT_INVALID_MAGIC.
-static bool read_ext4_superblock(const std::string& blk_device, const FstabEntry& entry,
- struct ext4_super_block* sb, int* fs_stat) {
+static bool read_ext4_superblock(const std::string& blk_device, struct ext4_super_block* sb,
+ int* fs_stat) {
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd < 0) {
@@ -331,29 +328,7 @@ static bool read_ext4_superblock(const std::string& blk_device, const FstabEntry
LINFO << "Invalid ext4 superblock on '" << blk_device << "'";
// not a valid fs, tune2fs, fsck, and mount will all fail.
*fs_stat |= FS_STAT_INVALID_MAGIC;
-
- bool encrypted = should_use_metadata_encryption(entry) || needs_block_encryption(entry);
- if (entry.mount_point == "/data" &&
- (!encrypted || android::base::StartsWith(blk_device, "/dev/block/dm-"))) {
- // try backup superblock, if main superblock is corrupted
- for (unsigned int blocksize = EXT4_MIN_BLOCK_SIZE; blocksize <= EXT4_MAX_BLOCK_SIZE;
- blocksize *= 2) {
- uint64_t superblock = blocksize * 8;
- if (blocksize == EXT4_MIN_BLOCK_SIZE) superblock++;
-
- if (TEMP_FAILURE_RETRY(pread(fd, sb, sizeof(*sb), superblock * blocksize)) !=
- sizeof(*sb)) {
- PERROR << "Can't read '" << blk_device << "' superblock";
- return false;
- }
- if (is_ext4_superblock_valid(sb) &&
- (1 << (10 + sb->s_log_block_size) == blocksize)) {
- *fs_stat &= ~FS_STAT_INVALID_MAGIC;
- break;
- }
- }
- }
- if (*fs_stat & FS_STAT_INVALID_MAGIC) return false;
+ return false;
}
*fs_stat |= FS_STAT_IS_EXT4;
LINFO << "superblock s_max_mnt_count:" << sb->s_max_mnt_count << "," << blk_device;
@@ -553,13 +528,13 @@ static void tune_verity(const std::string& blk_device, const FstabEntry& entry,
}
// Enable casefold if needed.
-static void tune_casefold(const std::string& blk_device, const struct ext4_super_block* sb,
- int* fs_stat) {
+static void tune_casefold(const std::string& blk_device, const FstabEntry& entry,
+ const struct ext4_super_block* sb, int* fs_stat) {
bool has_casefold = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_CASEFOLD)) != 0;
bool wants_casefold =
android::base::GetBoolProperty("external_storage.casefold.enabled", false);
- if (!wants_casefold || has_casefold) return;
+ if (entry.mount_point != "data" || !wants_casefold || has_casefold ) return;
std::string casefold_support;
if (!android::base::ReadFileToString(SYSFS_EXT4_CASEFOLD, &casefold_support)) {
@@ -614,7 +589,7 @@ static void tune_metadata_csum(const std::string& blk_device, const FstabEntry&
LINFO << "Enabling ext4 metadata_csum on " << blk_device;
- // requires to give last_fsck_time to current to avoid insane time.
+ // Must give `-T now` to prevent last_fsck_time from growing too large,
// otherwise, tune2fs won't enable metadata_csum.
const char* tune2fs_args[] = {TUNE2FS_BIN, "-O", "metadata_csum,64bit,extent",
"-T", "now", blk_device.c_str()};
@@ -694,7 +669,7 @@ static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry&
if (is_extfs(entry.fs_type)) {
struct ext4_super_block sb;
- if (read_ext4_superblock(blk_device, entry, &sb, &fs_stat)) {
+ if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||
(sb.s_state & EXT4_VALID_FS) == 0) {
LINFO << "Filesystem on " << blk_device << " was not cleanly shutdown; "
@@ -724,11 +699,11 @@ static int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry&
entry.fs_mgr_flags.fs_verity || entry.fs_mgr_flags.ext_meta_csum)) {
struct ext4_super_block sb;
- if (read_ext4_superblock(blk_device, entry, &sb, &fs_stat)) {
+ if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {
tune_reserved_size(blk_device, entry, &sb, &fs_stat);
tune_encrypt(blk_device, entry, &sb, &fs_stat);
tune_verity(blk_device, entry, &sb, &fs_stat);
- tune_casefold(blk_device, &sb, &fs_stat);
+ tune_casefold(blk_device, entry, &sb, &fs_stat);
tune_metadata_csum(blk_device, entry, &sb, &fs_stat);
}
}
@@ -1028,6 +1003,19 @@ static int handle_encryptable(const FstabEntry& entry) {
}
}
+static void set_type_property(int status) {
+ switch (status) {
+ case FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED:
+ SetProperty("ro.crypto.type", "block");
+ break;
+ case FS_MGR_MNTALL_DEV_FILE_ENCRYPTED:
+ case FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED:
+ case FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION:
+ SetProperty("ro.crypto.type", "file");
+ break;
+ }
+}
+
static bool call_vdc(const std::vector<std::string>& args, int* ret) {
std::vector<char const*> argv;
argv.emplace_back("/system/bin/vdc");
@@ -1165,10 +1153,9 @@ class CheckpointManager {
// metadata-encrypted device with smaller blocks, we must not change this for
// devices shipped with Q or earlier unless they explicitly selected dm-default-key
// v2
- constexpr unsigned int pre_gki_level = __ANDROID_API_Q__;
unsigned int options_format_version = android::base::GetUintProperty<unsigned int>(
"ro.crypto.dm_default_key.options_format.version",
- (android::fscrypt::GetFirstApiLevel() <= pre_gki_level ? 1 : 2));
+ (android::fscrypt::GetFirstApiLevel() <= __ANDROID_API_Q__ ? 1 : 2));
if (options_format_version > 1) {
bowTarget->SetBlockSize(4096);
}
@@ -1336,7 +1323,7 @@ static bool IsMountPointMounted(const std::string& mount_point) {
// When multiple fstab records share the same mount_point, it will try to mount each
// one in turn, and ignore any duplicates after a first successful mount.
// Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
-int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
+MountAllResult fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
int error_count = 0;
CheckpointManager checkpoint_manager;
@@ -1344,8 +1331,9 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
bool is_ffbm = false;
AvbUniquePtr avb_handle(nullptr);
+ bool userdata_mounted = false;
if (fstab->empty()) {
- return FS_MGR_MNTALL_FAIL;
+ return {FS_MGR_MNTALL_FAIL, userdata_mounted};
}
/**get boot mode*/
property_get("ro.bootmode", propbuf, "");
@@ -1393,7 +1381,7 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
}
// Terrible hack to make it possible to remount /data.
- // TODO: refact fs_mgr_mount_all and get rid of this.
+ // TODO: refactor fs_mgr_mount_all and get rid of this.
if (mount_mode == MOUNT_MODE_ONLY_USERDATA && current_entry.mount_point != "/data") {
continue;
}
@@ -1429,7 +1417,8 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
avb_handle = AvbHandle::Open();
if (!avb_handle) {
LERROR << "Failed to open AvbHandle";
- return FS_MGR_MNTALL_FAIL;
+ set_type_property(encryptable);
+ return {FS_MGR_MNTALL_FAIL, userdata_mounted};
}
}
if (avb_handle->SetUpAvbHashtree(&current_entry, true /* wait_for_verity_dev */) ==
@@ -1471,7 +1460,7 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
if (status == FS_MGR_MNTALL_FAIL) {
// Fatal error - no point continuing.
- return status;
+ return {status, userdata_mounted};
}
if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
@@ -1485,11 +1474,15 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
attempted_entry.mount_point},
nullptr)) {
LERROR << "Encryption failed";
- return FS_MGR_MNTALL_FAIL;
+ set_type_property(encryptable);
+ return {FS_MGR_MNTALL_FAIL, userdata_mounted};
}
}
}
+ if (current_entry.mount_point == "/data") {
+ userdata_mounted = true;
+ }
// Success! Go get the next one.
continue;
}
@@ -1588,14 +1581,16 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
}
}
+ set_type_property(encryptable);
+
#if ALLOW_ADBD_DISABLE_VERITY == 1 // "userdebug" build
fs_mgr_overlayfs_mount_all(fstab);
#endif
if (error_count) {
- return FS_MGR_MNTALL_FAIL;
+ return {FS_MGR_MNTALL_FAIL, userdata_mounted};
} else {
- return encryptable;
+ return {encryptable, userdata_mounted};
}
}
@@ -1817,8 +1812,8 @@ int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
}
LINFO << "Remounting /data";
// TODO(b/143970043): remove this hack after fs_mgr_mount_all is refactored.
- int result = fs_mgr_mount_all(fstab, MOUNT_MODE_ONLY_USERDATA);
- return result == FS_MGR_MNTALL_FAIL ? -1 : 0;
+ auto result = fs_mgr_mount_all(fstab, MOUNT_MODE_ONLY_USERDATA);
+ return result.code == FS_MGR_MNTALL_FAIL ? -1 : 0;
}
return 0;
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index c78552657..37b103229 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -636,13 +636,14 @@ void TransformFstabForDsu(Fstab* fstab, const std::vector<std::string>& dsu_part
entry.fs_mgr_flags.wait = true;
entry.fs_mgr_flags.logical = true;
entry.fs_mgr_flags.first_stage_mount = true;
+ fstab->emplace_back(entry);
} else {
// If the corresponding partition exists, transform all its Fstab
// by pointing .blk_device to the DSU partition.
for (auto&& entry : entries) {
entry->blk_device = partition;
// AVB keys for DSU should always be under kDsuKeysDir.
- entry->avb_keys += kDsuKeysDir;
+ entry->avb_keys = kDsuKeysDir;
}
// Make sure the ext4 is included to support GSI.
auto partition_ext4 =
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index beda2025e..2ba04176e 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -62,8 +62,20 @@ enum mount_mode {
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 1
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
#define FS_MGR_MNTALL_FAIL (-1)
+
+struct MountAllResult {
+ // One of the FS_MGR_MNTALL_* returned code defined above.
+ int code;
+ // Whether userdata was mounted as a result of |fs_mgr_mount_all| call.
+ bool userdata_mounted;
+};
+
// fs_mgr_mount_all() updates fstab entries that reference device-mapper.
-int fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
+// Returns a |MountAllResult|. The first element is one of the FS_MNG_MNTALL_* return codes
+// defined above, and the second element tells whether this call to fs_mgr_mount_all was responsible
+// for mounting userdata. Later is required for init to correctly enqueue fs-related events as part
+// of userdata remount during userspace reboot.
+MountAllResult fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);
#define FS_MGR_DOMNT_FAILED (-1)
#define FS_MGR_DOMNT_BUSY (-2)
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 791268849..bb7d8b311 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -427,6 +427,20 @@ bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* p
return true;
}
+// Accepts a device mapper device name (like system_a, vendor_b etc) and
+// returns its UUID.
+bool DeviceMapper::GetDmDeviceUuidByName(const std::string& name, std::string* uuid) {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
+ return false;
+ }
+
+ *uuid = std::string(io.uuid);
+ return true;
+}
+
bool DeviceMapper::GetDeviceNumber(const std::string& name, dev_t* dev) {
struct dm_ioctl io;
InitIo(&io, name);
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index abe9c4cda..5d6db467b 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -172,6 +172,13 @@ class DeviceMapper final {
// could race with ueventd.
bool GetDmDevicePathByName(const std::string& name, std::string* path);
+ // Returns the device mapper UUID for a given name. If the device does not
+ // exist, false is returned, and the path parameter is not set.
+ //
+ // WaitForFile() should not be used in conjunction with this call, since it
+ // could race with ueventd.
+ bool GetDmDeviceUuidByName(const std::string& name, std::string* path);
+
// Returns a device's unique path as generated by ueventd. This will return
// true as long as the device has been created, even if ueventd has not
// processed it yet.
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index cae43e6c2..a62211082 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -110,30 +110,3 @@ cc_test {
auto_gen_config: true,
require_root: true,
}
-
-/* BUG(148874852) temporary test */
-cc_test {
- name: "fiemap_image_test_presubmit",
- cppflags: [
- "-DSKIP_TEST_IN_PRESUBMIT",
- ],
- static_libs: [
- "libcrypto_utils",
- "libdm",
- "libext4_utils",
- "libfs_mgr",
- "liblp",
- ],
- shared_libs: [
- "libbase",
- "libcrypto",
- "libcutils",
- "liblog",
- ],
- srcs: [
- "image_test.cpp",
- ],
- test_suites: ["device-tests"],
- auto_gen_config: true,
- require_root: true,
-}
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 3ee742f5e..93fc1316f 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -136,13 +136,13 @@ bool ImageManager::BackingImageExists(const std::string& name) {
return !!FindPartition(*metadata.get(), name);
}
-static bool IsTestDir(const std::string& path) {
- return android::base::StartsWith(path, kTestImageMetadataDir) ||
- android::base::StartsWith(path, kOtaTestImageMetadataDir);
+bool ImageManager::MetadataDirIsTest() const {
+ return IsSubdir(metadata_dir_, kTestImageMetadataDir) ||
+ IsSubdir(metadata_dir_, kOtaTestImageMetadataDir);
}
-static bool IsUnreliablePinningAllowed(const std::string& path) {
- return android::base::StartsWith(path, "/data/gsi/dsu/") || IsTestDir(path);
+bool ImageManager::IsUnreliablePinningAllowed() const {
+ return IsSubdir(data_dir_, "/data/gsi/dsu/") || MetadataDirIsTest();
}
FiemapStatus ImageManager::CreateBackingImage(
@@ -159,7 +159,7 @@ FiemapStatus ImageManager::CreateBackingImage(
if (!FilesystemHasReliablePinning(data_path, &reliable_pinning)) {
return FiemapStatus::Error();
}
- if (!reliable_pinning && !IsUnreliablePinningAllowed(data_path)) {
+ if (!reliable_pinning && !IsUnreliablePinningAllowed()) {
// For historical reasons, we allow unreliable pinning for certain use
// cases (DSUs, testing) because the ultimate use case is either
// developer-oriented or ephemeral (the intent is to boot immediately
@@ -178,7 +178,7 @@ FiemapStatus ImageManager::CreateBackingImage(
// if device-mapper is stacked in some complex way not supported by
// FiemapWriter.
auto device_path = GetDevicePathForFile(fw.get());
- if (android::base::StartsWith(device_path, "/dev/block/dm-") && !IsTestDir(metadata_dir_)) {
+ if (android::base::StartsWith(device_path, "/dev/block/dm-") && !MetadataDirIsTest()) {
LOG(ERROR) << "Cannot persist images against device-mapper device: " << device_path;
fw = {};
@@ -640,16 +640,22 @@ bool ImageManager::Validate() {
return false;
}
+ bool ok = true;
for (const auto& partition : metadata->partitions) {
auto name = GetPartitionName(partition);
auto image_path = GetImageHeaderPath(name);
auto fiemap = SplitFiemap::Open(image_path);
- if (!fiemap || !fiemap->HasPinnedExtents()) {
- LOG(ERROR) << "Image is missing or was moved: " << image_path;
- return false;
+ if (fiemap == nullptr) {
+ LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
+ ok = false;
+ continue;
+ }
+ if (!fiemap->HasPinnedExtents()) {
+ LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
+ ok = false;
}
}
- return true;
+ return ok;
}
bool ImageManager::DisableImage(const std::string& name) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 66633916a..6d0975150 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -34,10 +34,13 @@
#include <libdm/dm.h>
#include <libfiemap/image_manager.h>
+#include "utility.h"
+
using namespace android::dm;
using namespace std::literals;
using android::base::unique_fd;
using android::fiemap::ImageManager;
+using android::fiemap::IsSubdir;
using android::fs_mgr::BlockDeviceInfo;
using android::fs_mgr::PartitionOpener;
using android::fs_mgr::WaitForFile;
@@ -131,6 +134,51 @@ TEST_F(NativeTest, GetMappedImageDevice) {
ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
}
+namespace {
+
+struct IsSubdirTestParam {
+ std::string child;
+ std::string parent;
+ bool result;
+};
+
+class IsSubdirTest : public ::testing::TestWithParam<IsSubdirTestParam> {};
+
+TEST_P(IsSubdirTest, Test) {
+ const auto& param = GetParam();
+ EXPECT_EQ(param.result, IsSubdir(param.child, param.parent))
+ << "IsSubdir(child=\"" << param.child << "\", parent=\"" << param.parent
+ << "\") != " << (param.result ? "true" : "false");
+}
+
+std::vector<IsSubdirTestParam> IsSubdirTestValues() {
+ // clang-format off
+ std::vector<IsSubdirTestParam> base_cases{
+ {"/foo/bar", "/foo", true},
+ {"/foo/bar/baz", "/foo", true},
+ {"/foo", "/foo", true},
+ {"/foo", "/", true},
+ {"/", "/", true},
+ {"/foo", "/foo/bar", false},
+ {"/foo", "/bar", false},
+ {"/foo-bar", "/foo", false},
+ {"/", "/foo", false},
+ };
+ // clang-format on
+ std::vector<IsSubdirTestParam> ret;
+ for (const auto& e : base_cases) {
+ ret.push_back(e);
+ ret.push_back({e.child + "/", e.parent, e.result});
+ ret.push_back({e.child, e.parent + "/", e.result});
+ ret.push_back({e.child + "/", e.parent + "/", e.result});
+ }
+ return ret;
+}
+
+INSTANTIATE_TEST_SUITE_P(IsSubdirTest, IsSubdirTest, ::testing::ValuesIn(IsSubdirTestValues()));
+
+} // namespace
+
bool Mkdir(const std::string& path) {
if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 60b98dce0..50f4f33c2 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -176,6 +176,8 @@ class ImageManager final : public IImageManager {
bool MapWithDmLinear(const IPartitionOpener& opener, const std::string& name,
const std::chrono::milliseconds& timeout_ms, std::string* path);
bool UnmapImageDevice(const std::string& name, bool force);
+ bool IsUnreliablePinningAllowed() const;
+ bool MetadataDirIsTest() const;
ImageManager(const ImageManager&) = delete;
ImageManager& operator=(const ImageManager&) = delete;
diff --git a/fs_mgr/libfiemap/utility.cpp b/fs_mgr/libfiemap/utility.cpp
index c1898556e..54cf1836b 100644
--- a/fs_mgr/libfiemap/utility.cpp
+++ b/fs_mgr/libfiemap/utility.cpp
@@ -167,5 +167,30 @@ bool FilesystemHasReliablePinning(const std::string& file, bool* supported) {
return F2fsPinBeforeAllocate(fd, supported);
}
+bool IsSubdir(const std::string& child, const std::string& parent) {
+ // Precondition: both are absolute paths.
+ CHECK(android::base::StartsWith(child, "/")) << "Not an absolute path: " << child;
+ CHECK(android::base::StartsWith(parent, "/")) << "Not an absolute path: " << parent;
+
+ // Remove extraneous "/" at the end.
+ std::string_view child_sv = child;
+ while (child_sv != "/" && android::base::ConsumeSuffix(&child_sv, "/"))
+ ;
+
+ std::string_view parent_sv = parent;
+ while (parent_sv != "/" && android::base::ConsumeSuffix(&parent_sv, "/"))
+ ;
+
+ // IsSubdir(anything, "/") => true
+ if (parent_sv == "/") return true;
+
+ // IsSubdir("/foo", "/foo") => true
+ if (parent_sv == child_sv) return true;
+
+ // IsSubdir("/foo/bar", "/foo") => true
+ // IsSubdir("/foo-bar", "/foo") => false
+ return android::base::StartsWith(child_sv, std::string(parent_sv) + "/");
+}
+
} // namespace fiemap
} // namespace android
diff --git a/fs_mgr/libfiemap/utility.h b/fs_mgr/libfiemap/utility.h
index 4c0bc2b1c..aa40f79d1 100644
--- a/fs_mgr/libfiemap/utility.h
+++ b/fs_mgr/libfiemap/utility.h
@@ -51,5 +51,9 @@ bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name);
// cases (such as snapshots or adb remount).
bool FilesystemHasReliablePinning(const std::string& file, bool* supported);
+// Crude implementation to check if |child| is a subdir of |parent|.
+// Assume both are absolute paths.
+bool IsSubdir(const std::string& child, const std::string& parent);
+
} // namespace fiemap
} // namespace android
diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp
index 3d3dde6f2..4696ff173 100644
--- a/fs_mgr/liblp/partition_opener.cpp
+++ b/fs_mgr/liblp/partition_opener.cpp
@@ -38,6 +38,9 @@ using android::base::unique_fd;
namespace {
std::string GetPartitionAbsolutePath(const std::string& path) {
+#if !defined(__ANDROID__)
+ return path;
+#else
if (android::base::StartsWith(path, "/")) {
return path;
}
@@ -56,6 +59,7 @@ std::string GetPartitionAbsolutePath(const std::string& path) {
}
}
return by_name;
+#endif
}
bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index eaef18037..0bb1b8719 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -122,6 +122,42 @@ cc_library_static {
],
}
+cc_defaults {
+ name: "libsnapshot_cow_defaults",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ export_include_dirs: ["include"],
+ srcs: [
+ "cow_decompress.cpp",
+ "cow_reader.cpp",
+ "cow_writer.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "libsnapshot_cow",
+ defaults: [
+ "libsnapshot_cow_defaults",
+ ],
+ host_supported: true,
+ recovery_available: true,
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libbrotli",
+ "libz",
+ ],
+ ramdisk_available: true,
+}
+
cc_library_static {
name: "libsnapshot_test_helpers",
defaults: ["libsnapshot_defaults"],
@@ -313,6 +349,9 @@ cc_test {
cc_defaults {
name: "snapuserd_defaults",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
srcs: [
"snapuserd.cpp",
],
@@ -324,8 +363,11 @@ cc_defaults {
static_libs: [
"libbase",
+ "libbrotli",
"liblog",
"libdm",
+ "libz",
+ "libsnapshot_cow",
],
}
@@ -341,5 +383,132 @@ cc_binary {
ramdisk: true,
static_executable: true,
- system_shared_libs: [],
+}
+
+cc_test {
+ name: "cow_api_test",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ srcs: [
+ "cow_api_test.cpp",
+ ],
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "liblog",
+ "libz",
+ ],
+ static_libs: [
+ "libbrotli",
+ "libgtest",
+ "libsnapshot_cow",
+ ],
+ test_min_api_level: 30,
+ auto_gen_config: true,
+ require_root: false,
+ host_supported: true,
+}
+
+cc_binary {
+ name: "make_cow_from_ab_ota",
+ host_supported: true,
+ device_supported: false,
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ static_libs: [
+ "libbase",
+ "libbspatch",
+ "libbrotli",
+ "libbz",
+ "libchrome",
+ "libcrypto",
+ "libgflags",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "libpuffpatch",
+ "libsnapshot_cow",
+ "libsparse",
+ "libxz",
+ "libz",
+ "libziparchive",
+ "update_metadata-protos",
+ ],
+ srcs: [
+ "make_cow_from_ab_ota.cpp",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_binary {
+ name: "estimate_cow_from_nonab_ota",
+ host_supported: true,
+ device_supported: false,
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ static_libs: [
+ "libbase",
+ "libbrotli",
+ "libbz",
+ "libcrypto",
+ "libgflags",
+ "liblog",
+ "libsnapshot_cow",
+ "libsparse",
+ "libz",
+ "libziparchive",
+ ],
+ srcs: [
+ "estimate_cow_from_nonab_ota.cpp",
+ ],
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
+cc_test {
+ name: "cow_snapuserd_test",
+ defaults: [
+ "fs_mgr_defaults",
+ ],
+ srcs: [
+ "cow_snapuserd_test.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ static_libs: [
+ "libbrotli",
+ "libgtest",
+ "libsnapshot_cow",
+ "libz",
+ ],
+ header_libs: [
+ "libstorage_literals_headers",
+ ],
+ test_min_api_level: 30,
+ auto_gen_config: true,
+ require_root: false,
}
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
new file mode 100644
index 000000000..40d5efbeb
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -0,0 +1,334 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <iostream>
+#include <memory>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+
+namespace android {
+namespace snapshot {
+
+class CowTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() override {
+ cow_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_->fd, 0) << strerror(errno);
+ }
+
+ virtual void TearDown() override { cow_ = nullptr; }
+
+ std::unique_ptr<TemporaryFile> cow_;
+};
+
+// Sink that always appends to the end of a string.
+class StringSink : public IByteSink {
+ public:
+ void* GetBuffer(size_t requested, size_t* actual) override {
+ size_t old_size = stream_.size();
+ stream_.resize(old_size + requested, '\0');
+ *actual = requested;
+ return stream_.data() + old_size;
+ }
+ bool ReturnData(void*, size_t) override { return true; }
+ void Reset() { stream_.clear(); }
+
+ std::string& stream() { return stream_; }
+
+ private:
+ std::string stream_;
+};
+
+TEST_F(CowTest, ReadWrite) {
+ CowOptions options;
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddCopy(10, 20));
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+ ASSERT_TRUE(writer.Flush());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ CowHeader header;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+ ASSERT_TRUE(reader.GetHeader(&header));
+ ASSERT_EQ(header.magic, kCowMagicNumber);
+ ASSERT_EQ(header.major_version, kCowVersionMajor);
+ ASSERT_EQ(header.minor_version, kCowVersionMinor);
+ ASSERT_EQ(header.block_size, options.block_size);
+ ASSERT_EQ(header.num_ops, 4);
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowCopyOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 10);
+ ASSERT_EQ(op->source, 20);
+
+ StringSink sink;
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 4096);
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_EQ(op->source, 106);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ // Note: the zero operation gets split into two blocks.
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 51);
+ ASSERT_EQ(op->source, 0);
+
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+
+ ASSERT_EQ(op->type, kCowZeroOp);
+ ASSERT_EQ(op->compression, kCowCompressNone);
+ ASSERT_EQ(op->data_length, 0);
+ ASSERT_EQ(op->new_block, 52);
+ ASSERT_EQ(op->source, 0);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, CompressGz) {
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.Flush());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+
+ StringSink sink;
+
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_EQ(op->data_length, 56); // compressed!
+ ASSERT_EQ(op->new_block, 50);
+ ASSERT_EQ(op->source, 106);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
+TEST_F(CowTest, CompressTwoBlocks) {
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size * 2, '\0');
+
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.Flush());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+ iter->Next();
+ ASSERT_FALSE(iter->Done());
+
+ StringSink sink;
+
+ auto op = &iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_EQ(op->compression, kCowCompressGz);
+ ASSERT_EQ(op->new_block, 51);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+}
+
+// Only return 1-byte buffers, to stress test the partial read logic in
+// CowReader.
+class HorribleStringSink : public StringSink {
+ public:
+ void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
+};
+
+class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};
+
+TEST_P(CompressionTest, HorribleSink) {
+ CowOptions options;
+ options.compression = GetParam();
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.Flush());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+ ASSERT_FALSE(iter->Done());
+
+ HorribleStringSink sink;
+ auto op = &iter->Get();
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+}
+
+INSTANTIATE_TEST_SUITE_P(CowApi, CompressionTest, testing::Values("none", "gz", "brotli"));
+
+TEST_F(CowTest, GetSize) {
+ CowOptions options;
+ CowWriter writer(options);
+ if (ftruncate(cow_->fd, 0) < 0) {
+ perror("Fails to set temp file size");
+ FAIL();
+ }
+ ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+
+ ASSERT_TRUE(writer.AddCopy(10, 20));
+ ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+ auto size_before = writer.GetCowSize();
+ ASSERT_TRUE(writer.Flush());
+ auto size_after = writer.GetCowSize();
+ ASSERT_EQ(size_before, size_after);
+ struct stat buf;
+
+ if (fstat(cow_->fd, &buf) < 0) {
+ perror("Fails to determine size of cow image written");
+ FAIL();
+ }
+ ASSERT_EQ(buf.st_size, writer.GetCowSize());
+}
+
+TEST_F(CowTest, Append) {
+ CowOptions options;
+ auto writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+ std::string data = "This is some data, believe it";
+ data.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+ ASSERT_TRUE(writer->Flush());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ writer = std::make_unique<CowWriter>(options);
+ ASSERT_TRUE(writer->Initialize(cow_->fd, CowWriter::OpenMode::APPEND));
+
+ std::string data2 = "More data!";
+ data2.resize(options.block_size, '\0');
+ ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
+ ASSERT_TRUE(writer->Flush());
+
+ ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+ struct stat buf;
+ ASSERT_EQ(fstat(cow_->fd, &buf), 0);
+ ASSERT_EQ(buf.st_size, writer->GetCowSize());
+
+ // Read back both operations.
+ CowReader reader;
+ ASSERT_TRUE(reader.Parse(cow_->fd));
+
+ StringSink sink;
+
+ auto iter = reader.GetOpIter();
+ ASSERT_NE(iter, nullptr);
+
+ ASSERT_FALSE(iter->Done());
+ auto op = &iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data);
+
+ iter->Next();
+ sink.Reset();
+
+ ASSERT_FALSE(iter->Done());
+ op = &iter->Get();
+ ASSERT_EQ(op->type, kCowReplaceOp);
+ ASSERT_TRUE(reader.ReadData(*op, &sink));
+ ASSERT_EQ(sink.stream(), data2);
+
+ iter->Next();
+ ASSERT_TRUE(iter->Done());
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libsnapshot/cow_decompress.cpp b/fs_mgr/libsnapshot/cow_decompress.cpp
new file mode 100644
index 000000000..faceafe17
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_decompress.cpp
@@ -0,0 +1,264 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "cow_decompress.h"
+
+#include <utility>
+
+#include <android-base/logging.h>
+#include <brotli/decode.h>
+#include <zlib.h>
+
+namespace android {
+namespace snapshot {
+
+class NoDecompressor final : public IDecompressor {
+ public:
+ bool Decompress(size_t) override;
+};
+
+bool NoDecompressor::Decompress(size_t) {
+ size_t stream_remaining = stream_->Size();
+ while (stream_remaining) {
+ size_t buffer_size = stream_remaining;
+ uint8_t* buffer = reinterpret_cast<uint8_t*>(sink_->GetBuffer(buffer_size, &buffer_size));
+ if (!buffer) {
+ LOG(ERROR) << "Could not acquire buffer from sink";
+ return false;
+ }
+
+ // Read until we can fill the buffer.
+ uint8_t* buffer_pos = buffer;
+ size_t bytes_to_read = std::min(buffer_size, stream_remaining);
+ while (bytes_to_read) {
+ size_t read;
+ if (!stream_->Read(buffer_pos, bytes_to_read, &read)) {
+ return false;
+ }
+ if (!read) {
+ LOG(ERROR) << "Stream ended prematurely";
+ return false;
+ }
+ if (!sink_->ReturnData(buffer_pos, read)) {
+ LOG(ERROR) << "Could not return buffer to sink";
+ return false;
+ }
+ buffer_pos += read;
+ bytes_to_read -= read;
+ stream_remaining -= read;
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<IDecompressor> IDecompressor::Uncompressed() {
+ return std::unique_ptr<IDecompressor>(new NoDecompressor());
+}
+
+// Read chunks of the COW and incrementally stream them to the decoder.
+class StreamDecompressor : public IDecompressor {
+ public:
+ bool Decompress(size_t output_bytes) override;
+
+ virtual bool Init() = 0;
+ virtual bool DecompressInput(const uint8_t* data, size_t length) = 0;
+ virtual bool Done() = 0;
+
+ protected:
+ bool GetFreshBuffer();
+
+ size_t output_bytes_;
+ size_t stream_remaining_;
+ uint8_t* output_buffer_ = nullptr;
+ size_t output_buffer_remaining_ = 0;
+};
+
+static constexpr size_t kChunkSize = 4096;
+
+bool StreamDecompressor::Decompress(size_t output_bytes) {
+ if (!Init()) {
+ return false;
+ }
+
+ stream_remaining_ = stream_->Size();
+ output_bytes_ = output_bytes;
+
+ uint8_t chunk[kChunkSize];
+ while (stream_remaining_) {
+ size_t read = std::min(stream_remaining_, sizeof(chunk));
+ if (!stream_->Read(chunk, read, &read)) {
+ return false;
+ }
+ if (!read) {
+ LOG(ERROR) << "Stream ended prematurely";
+ return false;
+ }
+ if (!DecompressInput(chunk, read)) {
+ return false;
+ }
+
+ stream_remaining_ -= read;
+
+ if (stream_remaining_ && Done()) {
+ LOG(ERROR) << "Decompressor terminated early";
+ return false;
+ }
+ }
+ if (!Done()) {
+ LOG(ERROR) << "Decompressor expected more bytes";
+ return false;
+ }
+ return true;
+}
+
+bool StreamDecompressor::GetFreshBuffer() {
+ size_t request_size = std::min(output_bytes_, kChunkSize);
+ output_buffer_ =
+ reinterpret_cast<uint8_t*>(sink_->GetBuffer(request_size, &output_buffer_remaining_));
+ if (!output_buffer_) {
+ LOG(ERROR) << "Could not acquire buffer from sink";
+ return false;
+ }
+ return true;
+}
+
+class GzDecompressor final : public StreamDecompressor {
+ public:
+ ~GzDecompressor();
+
+ bool Init() override;
+ bool DecompressInput(const uint8_t* data, size_t length) override;
+ bool Done() override { return ended_; }
+
+ private:
+ z_stream z_ = {};
+ bool ended_ = false;
+};
+
+bool GzDecompressor::Init() {
+ if (int rv = inflateInit(&z_); rv != Z_OK) {
+ LOG(ERROR) << "inflateInit returned error code " << rv;
+ return false;
+ }
+ return true;
+}
+
+GzDecompressor::~GzDecompressor() {
+ inflateEnd(&z_);
+}
+
+bool GzDecompressor::DecompressInput(const uint8_t* data, size_t length) {
+ z_.next_in = reinterpret_cast<Bytef*>(const_cast<uint8_t*>(data));
+ z_.avail_in = length;
+
+ while (z_.avail_in) {
+ // If no more output buffer, grab a new buffer.
+ if (z_.avail_out == 0) {
+ if (!GetFreshBuffer()) {
+ return false;
+ }
+ z_.next_out = reinterpret_cast<Bytef*>(output_buffer_);
+ z_.avail_out = output_buffer_remaining_;
+ }
+
+ // Remember the position of the output buffer so we can call ReturnData.
+ auto avail_out = z_.avail_out;
+
+ // Decompress.
+ int rv = inflate(&z_, Z_NO_FLUSH);
+ if (rv != Z_OK && rv != Z_STREAM_END) {
+ LOG(ERROR) << "inflate returned error code " << rv;
+ return false;
+ }
+
+ size_t returned = avail_out - z_.avail_out;
+ if (!sink_->ReturnData(output_buffer_, returned)) {
+ LOG(ERROR) << "Could not return buffer to sink";
+ return false;
+ }
+ output_buffer_ += returned;
+ output_buffer_remaining_ -= returned;
+
+ if (rv == Z_STREAM_END) {
+ if (z_.avail_in) {
+ LOG(ERROR) << "Gz stream ended prematurely";
+ return false;
+ }
+ ended_ = true;
+ return true;
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<IDecompressor> IDecompressor::Gz() {
+ return std::unique_ptr<IDecompressor>(new GzDecompressor());
+}
+
+class BrotliDecompressor final : public StreamDecompressor {
+ public:
+ ~BrotliDecompressor();
+
+ bool Init() override;
+ bool DecompressInput(const uint8_t* data, size_t length) override;
+ bool Done() override { return BrotliDecoderIsFinished(decoder_); }
+
+ private:
+ BrotliDecoderState* decoder_ = nullptr;
+};
+
+bool BrotliDecompressor::Init() {
+ decoder_ = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
+ return true;
+}
+
+BrotliDecompressor::~BrotliDecompressor() {
+ if (decoder_) {
+ BrotliDecoderDestroyInstance(decoder_);
+ }
+}
+
+bool BrotliDecompressor::DecompressInput(const uint8_t* data, size_t length) {
+ size_t available_in = length;
+ const uint8_t* next_in = data;
+
+ bool needs_more_output = false;
+ while (available_in || needs_more_output) {
+ if (!output_buffer_remaining_ && !GetFreshBuffer()) {
+ return false;
+ }
+
+ auto output_buffer = output_buffer_;
+ auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in,
+ &output_buffer_remaining_, &output_buffer_, nullptr);
+ if (r == BROTLI_DECODER_RESULT_ERROR) {
+ LOG(ERROR) << "brotli decode failed";
+ return false;
+ }
+ if (!sink_->ReturnData(output_buffer, output_buffer_ - output_buffer)) {
+ return false;
+ }
+ needs_more_output = (r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
+ }
+ return true;
+}
+
+std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
+ return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.h b/fs_mgr/libsnapshot/cow_decompress.h
new file mode 100644
index 000000000..f4852561f
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_decompress.h
@@ -0,0 +1,57 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <libsnapshot/cow_reader.h>
+
+namespace android {
+namespace snapshot {
+
+class IByteStream {
+ public:
+ virtual ~IByteStream() {}
+
+ // Read up to |length| bytes, storing the number of bytes read in the out-
+ // parameter. If the end of the stream is reached, 0 is returned.
+ virtual bool Read(void* buffer, size_t length, size_t* read) = 0;
+
+ // Size of the stream.
+ virtual size_t Size() const = 0;
+};
+
+class IDecompressor {
+ public:
+ virtual ~IDecompressor() {}
+
+ // Factory methods for decompression methods.
+ static std::unique_ptr<IDecompressor> Uncompressed();
+ static std::unique_ptr<IDecompressor> Gz();
+ static std::unique_ptr<IDecompressor> Brotli();
+
+ // |output_bytes| is the expected total number of bytes to sink.
+ virtual bool Decompress(size_t output_bytes) = 0;
+
+ void set_stream(IByteStream* stream) { stream_ = stream; }
+ void set_sink(IByteSink* sink) { sink_ = sink; }
+
+ protected:
+ IByteStream* stream_ = nullptr;
+ IByteSink* sink_ = nullptr;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
new file mode 100644
index 000000000..60093ab68
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -0,0 +1,256 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <limits>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <libsnapshot/cow_reader.h>
+#include <zlib.h>
+#include "cow_decompress.h"
+
+namespace android {
+namespace snapshot {
+
+CowReader::CowReader() : fd_(-1), header_(), fd_size_(0) {}
+
+static void SHA256(const void*, size_t, uint8_t[]) {
+#if 0
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+#endif
+}
+
+bool CowReader::Parse(android::base::unique_fd&& fd) {
+ owned_fd_ = std::move(fd);
+ return Parse(android::base::borrowed_fd{owned_fd_});
+}
+
+bool CowReader::Parse(android::base::borrowed_fd fd) {
+ fd_ = fd;
+
+ auto pos = lseek(fd_.get(), 0, SEEK_END);
+ if (pos < 0) {
+ PLOG(ERROR) << "lseek end failed";
+ return false;
+ }
+ fd_size_ = pos;
+
+ if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek header failed";
+ return false;
+ }
+ if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
+ PLOG(ERROR) << "read header failed";
+ return false;
+ }
+
+ // Validity check the ops range.
+ if (header_.ops_offset >= fd_size_) {
+ LOG(ERROR) << "ops offset " << header_.ops_offset << " larger than fd size " << fd_size_;
+ return false;
+ }
+ if (fd_size_ - header_.ops_offset < header_.ops_size) {
+ LOG(ERROR) << "ops size " << header_.ops_size << " is too large";
+ return false;
+ }
+
+ if (header_.magic != kCowMagicNumber) {
+ LOG(ERROR) << "Header Magic corrupted. Magic: " << header_.magic
+ << "Expected: " << kCowMagicNumber;
+ return false;
+ }
+ if (header_.header_size != sizeof(CowHeader)) {
+ LOG(ERROR) << "Header size unknown, read " << header_.header_size << ", expected "
+ << sizeof(CowHeader);
+ return false;
+ }
+
+ if ((header_.major_version != kCowVersionMajor) ||
+ (header_.minor_version != kCowVersionMinor)) {
+ LOG(ERROR) << "Header version mismatch";
+ LOG(ERROR) << "Major version: " << header_.major_version
+ << "Expected: " << kCowVersionMajor;
+ LOG(ERROR) << "Minor version: " << header_.minor_version
+ << "Expected: " << kCowVersionMinor;
+ return false;
+ }
+
+ uint8_t header_csum[32];
+ {
+ CowHeader tmp = header_;
+ memset(&tmp.header_checksum, 0, sizeof(tmp.header_checksum));
+ memset(header_csum, 0, sizeof(uint8_t) * 32);
+
+ SHA256(&tmp, sizeof(tmp), header_csum);
+ }
+ if (memcmp(header_csum, header_.header_checksum, sizeof(header_csum)) != 0) {
+ LOG(ERROR) << "header checksum is invalid";
+ return false;
+ }
+
+ return true;
+}
+
+bool CowReader::GetHeader(CowHeader* header) {
+ *header = header_;
+ return true;
+}
+
+class CowOpIter final : public ICowOpIter {
+ public:
+ CowOpIter(std::unique_ptr<uint8_t[]>&& ops, size_t len);
+
+ bool Done() override;
+ const CowOperation& Get() override;
+ void Next() override;
+
+ private:
+ bool HasNext();
+
+ std::unique_ptr<uint8_t[]> ops_;
+ const uint8_t* pos_;
+ const uint8_t* end_;
+ bool done_;
+};
+
+CowOpIter::CowOpIter(std::unique_ptr<uint8_t[]>&& ops, size_t len)
+ : ops_(std::move(ops)), pos_(ops_.get()), end_(pos_ + len), done_(!HasNext()) {}
+
+bool CowOpIter::Done() {
+ return done_;
+}
+
+bool CowOpIter::HasNext() {
+ return pos_ < end_ && size_t(end_ - pos_) >= sizeof(CowOperation);
+}
+
+void CowOpIter::Next() {
+ CHECK(!Done());
+
+ pos_ += sizeof(CowOperation);
+ if (!HasNext()) done_ = true;
+}
+
+const CowOperation& CowOpIter::Get() {
+ CHECK(!Done());
+ CHECK(HasNext());
+ return *reinterpret_cast<const CowOperation*>(pos_);
+}
+
+std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
+ if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek ops failed";
+ return nullptr;
+ }
+ auto ops_buffer = std::make_unique<uint8_t[]>(header_.ops_size);
+ if (!android::base::ReadFully(fd_, ops_buffer.get(), header_.ops_size)) {
+ PLOG(ERROR) << "read ops failed";
+ return nullptr;
+ }
+
+ uint8_t csum[32];
+ memset(csum, 0, sizeof(uint8_t) * 32);
+
+ SHA256(ops_buffer.get(), header_.ops_size, csum);
+ if (memcmp(csum, header_.ops_checksum, sizeof(csum)) != 0) {
+ LOG(ERROR) << "ops checksum does not match";
+ return nullptr;
+ }
+
+ return std::make_unique<CowOpIter>(std::move(ops_buffer), header_.ops_size);
+}
+
+bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
+ // Validate the offset, taking care to acknowledge possible overflow of offset+len.
+ if (offset < sizeof(header_) || offset >= header_.ops_offset || len >= fd_size_ ||
+ offset + len > header_.ops_offset) {
+ LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
+ return false;
+ }
+ if (lseek(fd_.get(), offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek to read raw bytes failed";
+ return false;
+ }
+ ssize_t rv = TEMP_FAILURE_RETRY(::read(fd_.get(), buffer, len));
+ if (rv < 0) {
+ PLOG(ERROR) << "read failed";
+ return false;
+ }
+ *read = rv;
+ return true;
+}
+
+class CowDataStream final : public IByteStream {
+ public:
+ CowDataStream(CowReader* reader, uint64_t offset, size_t data_length)
+ : reader_(reader), offset_(offset), data_length_(data_length) {
+ remaining_ = data_length_;
+ }
+
+ bool Read(void* buffer, size_t length, size_t* read) override {
+ size_t to_read = std::min(length, remaining_);
+ if (!to_read) {
+ *read = 0;
+ return true;
+ }
+ if (!reader_->GetRawBytes(offset_, buffer, to_read, read)) {
+ return false;
+ }
+ offset_ += *read;
+ remaining_ -= *read;
+ return true;
+ }
+
+ size_t Size() const override { return data_length_; }
+
+ private:
+ CowReader* reader_;
+ uint64_t offset_;
+ size_t data_length_;
+ size_t remaining_;
+};
+
+bool CowReader::ReadData(const CowOperation& op, IByteSink* sink) {
+ std::unique_ptr<IDecompressor> decompressor;
+ switch (op.compression) {
+ case kCowCompressNone:
+ decompressor = IDecompressor::Uncompressed();
+ break;
+ case kCowCompressGz:
+ decompressor = IDecompressor::Gz();
+ break;
+ case kCowCompressBrotli:
+ decompressor = IDecompressor::Brotli();
+ break;
+ default:
+ LOG(ERROR) << "Unknown compression type: " << op.compression;
+ return false;
+ }
+
+ CowDataStream stream(this, op.source, op.data_length);
+ decompressor->set_stream(&stream);
+ decompressor->set_sink(sink);
+ return decompressor->Decompress(header_.block_size);
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
new file mode 100644
index 000000000..80acb4ae1
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -0,0 +1,351 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <iostream>
+#include <memory>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <libsnapshot/cow_writer.h>
+#include <storage_literals/storage_literals.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace android::storage_literals;
+using android::base::unique_fd;
+
+class SnapuserdTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ cow_system_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_system_->fd, 0) << strerror(errno);
+
+ cow_product_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_product_->fd, 0) << strerror(errno);
+
+ size_ = 100_MiB;
+ }
+
+ void TearDown() override {
+ cow_system_ = nullptr;
+ cow_product_ = nullptr;
+ }
+
+ std::unique_ptr<TemporaryFile> cow_system_;
+ std::unique_ptr<TemporaryFile> cow_product_;
+
+ unique_fd sys_fd_;
+ unique_fd product_fd_;
+ size_t size_;
+
+ int system_blksize_;
+ int product_blksize_;
+ std::string system_device_name_;
+ std::string product_device_name_;
+
+ std::unique_ptr<uint8_t[]> random_buffer_1_;
+ std::unique_ptr<uint8_t[]> random_buffer_2_;
+ std::unique_ptr<uint8_t[]> zero_buffer_;
+ std::unique_ptr<uint8_t[]> system_buffer_;
+ std::unique_ptr<uint8_t[]> product_buffer_;
+
+ void Init();
+ void CreateCowDevice(std::unique_ptr<TemporaryFile>& cow);
+ void CreateSystemDmUser();
+ void CreateProductDmUser();
+ void StartSnapuserdDaemon();
+ void CreateSnapshotDevices();
+
+ void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf);
+};
+
+void SnapuserdTest::Init() {
+ unique_fd rnd_fd;
+ loff_t offset = 0;
+
+ rnd_fd.reset(open("/dev/random", O_RDONLY));
+ ASSERT_TRUE(rnd_fd > 0);
+
+ random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+ random_buffer_2_ = std::make_unique<uint8_t[]>(size_);
+ system_buffer_ = std::make_unique<uint8_t[]>(size_);
+ product_buffer_ = std::make_unique<uint8_t[]>(size_);
+ zero_buffer_ = std::make_unique<uint8_t[]>(size_);
+
+ // Fill random data
+ for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+ true);
+
+ ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_2_.get() + offset, 1_MiB, 0),
+ true);
+
+ offset += 1_MiB;
+ }
+
+ sys_fd_.reset(open("/dev/block/mapper/system_a", O_RDONLY));
+ ASSERT_TRUE(sys_fd_ > 0);
+
+ product_fd_.reset(open("/dev/block/mapper/product_a", O_RDONLY));
+ ASSERT_TRUE(product_fd_ > 0);
+
+ // Read from system partition from offset 0 of size 100MB
+ ASSERT_EQ(ReadFullyAtOffset(sys_fd_, system_buffer_.get(), size_, 0), true);
+
+ // Read from system partition from offset 0 of size 100MB
+ ASSERT_EQ(ReadFullyAtOffset(product_fd_, product_buffer_.get(), size_, 0), true);
+}
+
+void SnapuserdTest::CreateCowDevice(std::unique_ptr<TemporaryFile>& cow) {
+ //================Create a COW file with the following operations===========
+ //
+ // Create COW file which is gz compressed
+ //
+ // 0-100 MB of replace operation with random data
+ // 100-200 MB of copy operation
+ // 200-300 MB of zero operation
+ // 300-400 MB of replace operation with random data
+
+ CowOptions options;
+ options.compression = "gz";
+ CowWriter writer(options);
+
+ ASSERT_TRUE(writer.Initialize(cow->fd));
+
+ // Write 100MB random data to COW file which is gz compressed from block 0
+ ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), size_));
+
+ size_t num_blocks = size_ / options.block_size;
+ size_t blk_start_copy = num_blocks;
+ size_t blk_end_copy = blk_start_copy + num_blocks;
+ size_t source_blk = 0;
+
+ // Copy blocks - source_blk starts from 0 as snapuserd
+ // has to read from block 0 in system_a partition
+ //
+ // This initializes copy operation from block 0 of size 100 MB from
+ // /dev/block/mapper/system_a or product_a
+ for (size_t i = blk_start_copy; i < blk_end_copy; i++) {
+ ASSERT_TRUE(writer.AddCopy(i, source_blk));
+ source_blk += 1;
+ }
+
+ size_t blk_zero_copy_start = blk_end_copy;
+ size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
+
+ // 100 MB filled with zeroes
+ ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
+
+ // Final 100MB filled with random data which is gz compressed
+ size_t blk_random2_replace_start = blk_zero_copy_end;
+
+ ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_2_.get(), size_));
+
+ // Flush operations
+ ASSERT_TRUE(writer.Flush());
+
+ ASSERT_EQ(lseek(cow->fd, 0, SEEK_SET), 0);
+}
+
+void SnapuserdTest::CreateSystemDmUser() {
+ unique_fd system_a_fd;
+ std::string cmd;
+
+ // Create a COW device. Number of sectors is chosen random which can
+ // hold at least 400MB of data
+
+ system_a_fd.reset(open("/dev/block/mapper/system_a", O_RDONLY));
+ ASSERT_TRUE(system_a_fd > 0);
+
+ int err = ioctl(system_a_fd.get(), BLKGETSIZE, &system_blksize_);
+ ASSERT_GE(err, 0);
+
+ std::string str(cow_system_->path);
+ std::size_t found = str.find_last_of("/\\");
+ ASSERT_NE(found, std::string::npos);
+ system_device_name_ = str.substr(found + 1);
+ cmd = "dmctl create " + system_device_name_ + " user 0 " + std::to_string(system_blksize_);
+
+ system(cmd.c_str());
+}
+
+void SnapuserdTest::CreateProductDmUser() {
+ unique_fd product_a_fd;
+ std::string cmd;
+
+ // Create a COW device. Number of sectors is chosen random which can
+ // hold at least 400MB of data
+
+ product_a_fd.reset(open("/dev/block/mapper/product_a", O_RDONLY));
+ ASSERT_TRUE(product_a_fd > 0);
+
+ int err = ioctl(product_a_fd.get(), BLKGETSIZE, &product_blksize_);
+ ASSERT_GE(err, 0);
+
+ std::string str(cow_product_->path);
+ std::size_t found = str.find_last_of("/\\");
+ ASSERT_NE(found, std::string::npos);
+ product_device_name_ = str.substr(found + 1);
+ cmd = "dmctl create " + product_device_name_ + " user 0 " + std::to_string(product_blksize_);
+
+ system(cmd.c_str());
+}
+
+void SnapuserdTest::StartSnapuserdDaemon() {
+ // Start the snapuserd daemon
+ if (fork() == 0) {
+ const char* argv[] = {"/system/bin/snapuserd", cow_system_->path,
+ "/dev/block/mapper/system_a", cow_product_->path,
+ "/dev/block/mapper/product_a", nullptr};
+ if (execv(argv[0], const_cast<char**>(argv))) {
+ ASSERT_TRUE(0);
+ }
+ }
+}
+
+void SnapuserdTest::CreateSnapshotDevices() {
+ std::string cmd;
+
+ cmd = "dmctl create system-snapshot -ro snapshot 0 " + std::to_string(system_blksize_);
+ cmd += " /dev/block/mapper/system_a";
+ cmd += " /dev/block/mapper/" + system_device_name_;
+ cmd += " P 8";
+
+ system(cmd.c_str());
+
+ cmd.clear();
+
+ cmd = "dmctl create product-snapshot -ro snapshot 0 " + std::to_string(product_blksize_);
+ cmd += " /dev/block/mapper/product_a";
+ cmd += " /dev/block/mapper/" + product_device_name_;
+ cmd += " P 8";
+
+ system(cmd.c_str());
+}
+
+void SnapuserdTest::TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf) {
+ loff_t offset = 0;
+ std::unique_ptr<uint8_t[]> buffer = std::move(buf);
+
+ std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
+
+ //================Start IO operation on dm-snapshot device=================
+ // This will test the following paths:
+ //
+ // 1: IO path for all three operations and interleaving of operations.
+ // 2: Merging of blocks in kernel during metadata read
+ // 3: Bulk IO issued by kernel duing merge operation
+
+ // Read from snapshot device of size 100MB from offset 0. This tests the
+ // 1st replace operation.
+ //
+ // IO path:
+ //
+ // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
+ // op)->decompress_cow->return
+
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+
+ // Update the offset
+ offset += size_;
+
+ // Compare data with random_buffer_1_.
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1_.get(), size_), 0);
+
+ // Clear the buffer
+ memset(snapuserd_buffer.get(), 0, size_);
+
+ // Read from snapshot device of size 100MB from offset 100MB. This tests the
+ // copy operation.
+ //
+ // IO path:
+ //
+ // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_from_system_a_partition
+ // (copy op) -> return
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+
+ // Update the offset
+ offset += size_;
+
+ // Compare data with buffer.
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), buffer.get(), size_), 0);
+
+ // Read from snapshot device of size 100MB from offset 200MB. This tests the
+ // zero operation.
+ //
+ // IO path:
+ //
+ // dm-snap->dm-snap-persistent->dm-user->snapuserd->fill_memory_with_zero
+ // (zero op) -> return
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+
+ // Compare data with zero filled buffer
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), zero_buffer_.get(), size_), 0);
+
+ // Update the offset
+ offset += size_;
+
+ // Read from snapshot device of size 100MB from offset 300MB. This tests the
+ // final replace operation.
+ //
+ // IO path:
+ //
+ // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
+ // op)->decompress_cow->return
+ ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);
+
+ // Compare data with random_buffer_2_.
+ ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_2_.get(), size_), 0);
+}
+
+TEST_F(SnapuserdTest, ReadWrite) {
+ unique_fd snapshot_fd;
+
+ Init();
+
+ CreateCowDevice(cow_system_);
+ CreateCowDevice(cow_product_);
+
+ CreateSystemDmUser();
+ CreateProductDmUser();
+
+ StartSnapuserdDaemon();
+
+ CreateSnapshotDevices();
+
+ snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY));
+ ASSERT_TRUE(snapshot_fd > 0);
+ TestIO(snapshot_fd, std::move(system_buffer_));
+
+ snapshot_fd.reset(open("/dev/block/mapper/product-snapshot", O_RDONLY));
+ ASSERT_TRUE(snapshot_fd > 0);
+ TestIO(snapshot_fd, std::move(product_buffer_));
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
new file mode 100644
index 000000000..4cf2119a7
--- /dev/null
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -0,0 +1,322 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <limits>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <brotli/encode.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include <zlib.h>
+
+namespace android {
+namespace snapshot {
+
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
+CowWriter::CowWriter(const CowOptions& options) : ICowWriter(options), fd_(-1) {
+ SetupHeaders();
+}
+
+void CowWriter::SetupHeaders() {
+ header_ = {};
+ header_.magic = kCowMagicNumber;
+ header_.major_version = kCowVersionMajor;
+ header_.minor_version = kCowVersionMinor;
+ header_.header_size = sizeof(CowHeader);
+ header_.block_size = options_.block_size;
+}
+
+bool CowWriter::ParseOptions() {
+ if (options_.compression == "gz") {
+ compression_ = kCowCompressGz;
+ } else if (options_.compression == "brotli") {
+ compression_ = kCowCompressBrotli;
+ } else if (options_.compression == "none") {
+ compression_ = kCowCompressNone;
+ } else if (!options_.compression.empty()) {
+ LOG(ERROR) << "unrecognized compression: " << options_.compression;
+ return false;
+ }
+ return true;
+}
+
+bool CowWriter::Initialize(android::base::unique_fd&& fd, OpenMode mode) {
+ owned_fd_ = std::move(fd);
+ return Initialize(android::base::borrowed_fd{owned_fd_}, mode);
+}
+
+bool CowWriter::Initialize(android::base::borrowed_fd fd, OpenMode mode) {
+ fd_ = fd;
+
+ if (!ParseOptions()) {
+ return false;
+ }
+
+ switch (mode) {
+ case OpenMode::WRITE:
+ return OpenForWrite();
+ case OpenMode::APPEND:
+ return OpenForAppend();
+ default:
+ LOG(ERROR) << "Unknown open mode in CowWriter";
+ return false;
+ }
+}
+
+bool CowWriter::OpenForWrite() {
+ // This limitation is tied to the data field size in CowOperation.
+ if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
+ LOG(ERROR) << "Block size is too large";
+ return false;
+ }
+
+ if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+
+ // Headers are not complete, but this ensures the file is at the right
+ // position.
+ if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+ PLOG(ERROR) << "write failed";
+ return false;
+ }
+
+ header_.ops_offset = header_.header_size;
+ return true;
+}
+
+bool CowWriter::OpenForAppend() {
+ auto reader = std::make_unique<CowReader>();
+ if (!reader->Parse(fd_) || !reader->GetHeader(&header_)) {
+ return false;
+ }
+ options_.block_size = header_.block_size;
+
+ // Reset this, since we're going to reimport all operations.
+ header_.num_ops = 0;
+
+ auto iter = reader->GetOpIter();
+ while (!iter->Done()) {
+ auto& op = iter->Get();
+ AddOperation(op);
+
+ iter->Next();
+ }
+
+ // Free reader so we own the descriptor position again.
+ reader = nullptr;
+
+ // Seek to the end of the data section.
+ if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+ return true;
+}
+
+bool CowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
+ CowOperation op = {};
+ op.type = kCowCopyOp;
+ op.new_block = new_block;
+ op.source = old_block;
+ AddOperation(op);
+ return true;
+}
+
+bool CowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+ if (size % header_.block_size != 0) {
+ LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+ << header_.block_size;
+ return false;
+ }
+
+ uint64_t pos;
+ if (!GetDataPos(&pos)) {
+ return false;
+ }
+
+ const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
+ for (size_t i = 0; i < size / header_.block_size; i++) {
+ CowOperation op = {};
+ op.type = kCowReplaceOp;
+ op.new_block = new_block_start + i;
+ op.source = pos;
+
+ if (compression_) {
+ auto data = Compress(iter, header_.block_size);
+ if (data.empty()) {
+ PLOG(ERROR) << "AddRawBlocks: compression failed";
+ return false;
+ }
+ if (data.size() > std::numeric_limits<uint16_t>::max()) {
+ LOG(ERROR) << "Compressed block is too large: " << data.size() << " bytes";
+ return false;
+ }
+ if (!WriteRawData(data.data(), data.size())) {
+ PLOG(ERROR) << "AddRawBlocks: write failed";
+ return false;
+ }
+ op.compression = compression_;
+ op.data_length = static_cast<uint16_t>(data.size());
+ pos += data.size();
+ } else {
+ op.data_length = static_cast<uint16_t>(header_.block_size);
+ pos += header_.block_size;
+ }
+
+ AddOperation(op);
+ iter += header_.block_size;
+ }
+
+ if (!compression_ && !WriteRawData(data, size)) {
+ PLOG(ERROR) << "AddRawBlocks: write failed";
+ return false;
+ }
+ return true;
+}
+
+bool CowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+ for (uint64_t i = 0; i < num_blocks; i++) {
+ CowOperation op = {};
+ op.type = kCowZeroOp;
+ op.new_block = new_block_start + i;
+ op.source = 0;
+ AddOperation(op);
+ }
+ return true;
+}
+
+std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
+ switch (compression_) {
+ case kCowCompressGz: {
+ auto bound = compressBound(length);
+ auto buffer = std::make_unique<uint8_t[]>(bound);
+
+ uLongf dest_len = bound;
+ auto rv = compress2(buffer.get(), &dest_len, reinterpret_cast<const Bytef*>(data),
+ length, Z_BEST_COMPRESSION);
+ if (rv != Z_OK) {
+ LOG(ERROR) << "compress2 returned: " << rv;
+ return {};
+ }
+ return std::basic_string<uint8_t>(buffer.get(), dest_len);
+ }
+ case kCowCompressBrotli: {
+ auto bound = BrotliEncoderMaxCompressedSize(length);
+ if (!bound) {
+ LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
+ return {};
+ }
+ auto buffer = std::make_unique<uint8_t[]>(bound);
+
+ size_t encoded_size = bound;
+ auto rv = BrotliEncoderCompress(
+ BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
+ reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.get());
+ if (!rv) {
+ LOG(ERROR) << "BrotliEncoderCompress failed";
+ return {};
+ }
+ return std::basic_string<uint8_t>(buffer.get(), encoded_size);
+ }
+ default:
+ LOG(ERROR) << "unhandled compression type: " << compression_;
+ break;
+ }
+ return {};
+}
+
+// TODO: Fix compilation issues when linking libcrypto library
+// when snapuserd is compiled as part of ramdisk.
+static void SHA256(const void*, size_t, uint8_t[]) {
+#if 0
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, data, length);
+ SHA256_Final(out, &c);
+#endif
+}
+
+bool CowWriter::Flush() {
+ header_.ops_size = ops_.size();
+
+ memset(header_.ops_checksum, 0, sizeof(uint8_t) * 32);
+ memset(header_.header_checksum, 0, sizeof(uint8_t) * 32);
+
+ SHA256(ops_.data(), ops_.size(), header_.ops_checksum);
+ SHA256(&header_, sizeof(header_), header_.header_checksum);
+
+ if (lseek(fd_.get(), 0, SEEK_SET)) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+ if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+ PLOG(ERROR) << "write header failed";
+ return false;
+ }
+ if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek ops failed";
+ return false;
+ }
+ if (!WriteFully(fd_, ops_.data(), ops_.size())) {
+ PLOG(ERROR) << "write ops failed";
+ return false;
+ }
+
+ // Re-position for any subsequent writes.
+ if (lseek(fd_.get(), header_.ops_offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek ops failed";
+ return false;
+ }
+ return true;
+}
+
+size_t CowWriter::GetCowSize() {
+ return header_.ops_offset + header_.num_ops * sizeof(CowOperation);
+}
+
+bool CowWriter::GetDataPos(uint64_t* pos) {
+ off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
+ if (offs < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+ *pos = offs;
+ return true;
+}
+
+void CowWriter::AddOperation(const CowOperation& op) {
+ header_.num_ops++;
+ ops_.insert(ops_.size(), reinterpret_cast<const uint8_t*>(&op), sizeof(op));
+}
+
+bool CowWriter::WriteRawData(const void* data, size_t size) {
+ if (!android::base::WriteFully(fd_, data, size)) {
+ return false;
+ }
+ header_.ops_offset += size;
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp b/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp
new file mode 100644
index 000000000..2a0136b6d
--- /dev/null
+++ b/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp
@@ -0,0 +1,432 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <gflags/gflags.h>
+#include <libsnapshot/cow_writer.h>
+#include <openssl/sha.h>
+#include <sparse/sparse.h>
+#include <ziparchive/zip_archive.h>
+
+DEFINE_string(source_tf, "", "Source target files (dir or zip file)");
+DEFINE_string(ota_tf, "", "Target files of the build for an OTA");
+DEFINE_string(compression, "gz", "Compression (options: none, gz, brotli)");
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
+static constexpr size_t kBlockSize = 4096;
+
+void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
+ unsigned int, const char* message) {
+ if (severity == android::base::ERROR) {
+ fprintf(stderr, "%s\n", message);
+ } else {
+ fprintf(stdout, "%s\n", message);
+ }
+}
+
+class TargetFilesPackage final {
+ public:
+ explicit TargetFilesPackage(const std::string& path);
+
+ bool Open();
+ bool HasFile(const std::string& path);
+ std::unordered_set<std::string> GetDynamicPartitionNames();
+ unique_fd OpenFile(const std::string& path);
+ unique_fd OpenImage(const std::string& path);
+
+ private:
+ std::string path_;
+ unique_fd fd_;
+ std::unique_ptr<ZipArchive, decltype(&CloseArchive)> zip_;
+};
+
+TargetFilesPackage::TargetFilesPackage(const std::string& path)
+ : path_(path), zip_(nullptr, &CloseArchive) {}
+
+bool TargetFilesPackage::Open() {
+ fd_.reset(open(path_.c_str(), O_RDONLY));
+ if (fd_ < 0) {
+ PLOG(ERROR) << "open failed: " << path_;
+ return false;
+ }
+
+ struct stat s;
+ if (fstat(fd_.get(), &s) < 0) {
+ PLOG(ERROR) << "fstat failed: " << path_;
+ return false;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ return true;
+ }
+
+ // Otherwise, assume it's a zip file.
+ ZipArchiveHandle handle;
+ if (OpenArchiveFd(fd_.get(), path_.c_str(), &handle, false)) {
+ LOG(ERROR) << "Could not open " << path_ << " as a zip archive.";
+ return false;
+ }
+ zip_.reset(handle);
+ return true;
+}
+
+bool TargetFilesPackage::HasFile(const std::string& path) {
+ if (zip_) {
+ ZipEntry64 entry;
+ return !FindEntry(zip_.get(), path, &entry);
+ }
+
+ auto full_path = path_ + "/" + path;
+ return access(full_path.c_str(), F_OK) == 0;
+}
+
+unique_fd TargetFilesPackage::OpenFile(const std::string& path) {
+ if (!zip_) {
+ auto full_path = path_ + "/" + path;
+ unique_fd fd(open(full_path.c_str(), O_RDONLY));
+ if (fd < 0) {
+ PLOG(ERROR) << "open failed: " << full_path;
+ return {};
+ }
+ return fd;
+ }
+
+ ZipEntry64 entry;
+ if (FindEntry(zip_.get(), path, &entry)) {
+ LOG(ERROR) << path << " not found in archive: " << path_;
+ return {};
+ }
+
+ TemporaryFile temp;
+ if (temp.fd < 0) {
+ PLOG(ERROR) << "mkstemp failed";
+ return {};
+ }
+
+ LOG(INFO) << "Extracting " << path << " from " << path_ << " ...";
+ if (ExtractEntryToFile(zip_.get(), &entry, temp.fd)) {
+ LOG(ERROR) << "could not extract " << path << " from " << path_;
+ return {};
+ }
+ if (lseek(temp.fd, 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return {};
+ }
+ return unique_fd{temp.release()};
+}
+
+unique_fd TargetFilesPackage::OpenImage(const std::string& path) {
+ auto fd = OpenFile(path);
+ if (fd < 0) {
+ return {};
+ }
+
+ LOG(INFO) << "Unsparsing " << path << " ...";
+ std::unique_ptr<struct sparse_file, decltype(&sparse_file_destroy)> s(
+ sparse_file_import(fd.get(), false, false), &sparse_file_destroy);
+ if (!s) {
+ return fd;
+ }
+
+ TemporaryFile temp;
+ if (temp.fd < 0) {
+ PLOG(ERROR) << "mkstemp failed";
+ return {};
+ }
+ if (sparse_file_write(s.get(), temp.fd, false, false, false) < 0) {
+ LOG(ERROR) << "sparse_file_write failed";
+ return {};
+ }
+ if (lseek(temp.fd, 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return {};
+ }
+
+ fd.reset(temp.release());
+ return fd;
+}
+
+std::unordered_set<std::string> TargetFilesPackage::GetDynamicPartitionNames() {
+ auto fd = OpenFile("META/misc_info.txt");
+ if (fd < 0) {
+ return {};
+ }
+
+ std::string contents;
+ if (!android::base::ReadFdToString(fd, &contents)) {
+ PLOG(ERROR) << "read failed";
+ return {};
+ }
+
+ std::unordered_set<std::string> set;
+
+ auto lines = android::base::Split(contents, "\n");
+ for (const auto& line : lines) {
+ auto parts = android::base::Split(line, "=");
+ if (parts.size() == 2 && parts[0] == "dynamic_partition_list") {
+ auto partitions = android::base::Split(parts[1], " ");
+ for (const auto& name : partitions) {
+ if (!name.empty()) {
+ set.emplace(name);
+ }
+ }
+ break;
+ }
+ }
+ return set;
+}
+
+class NonAbEstimator final {
+ public:
+ NonAbEstimator(const std::string& ota_tf_path, const std::string& source_tf_path)
+ : ota_tf_path_(ota_tf_path), source_tf_path_(source_tf_path) {}
+
+ bool Run();
+
+ private:
+ bool OpenPackages();
+ bool AnalyzePartition(const std::string& partition_name);
+ std::unordered_map<std::string, uint64_t> GetBlockMap(borrowed_fd fd);
+
+ std::string ota_tf_path_;
+ std::string source_tf_path_;
+ std::unique_ptr<TargetFilesPackage> ota_tf_;
+ std::unique_ptr<TargetFilesPackage> source_tf_;
+ uint64_t size_ = 0;
+};
+
+bool NonAbEstimator::Run() {
+ if (!OpenPackages()) {
+ return false;
+ }
+
+ auto partitions = ota_tf_->GetDynamicPartitionNames();
+ if (partitions.empty()) {
+ LOG(ERROR) << "No dynamic partitions found in META/misc_info.txt";
+ return false;
+ }
+ for (const auto& partition : partitions) {
+ if (!AnalyzePartition(partition)) {
+ return false;
+ }
+ }
+
+ int64_t size_in_mb = int64_t(double(size_) / 1024.0 / 1024.0);
+
+ std::cout << "Estimated COW size: " << size_ << " (" << size_in_mb << "MiB)\n";
+ return true;
+}
+
+bool NonAbEstimator::OpenPackages() {
+ ota_tf_ = std::make_unique<TargetFilesPackage>(ota_tf_path_);
+ if (!ota_tf_->Open()) {
+ return false;
+ }
+ if (!source_tf_path_.empty()) {
+ source_tf_ = std::make_unique<TargetFilesPackage>(source_tf_path_);
+ if (!source_tf_->Open()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static std::string SHA256(const std::string& input) {
+ std::string hash(32, '\0');
+ SHA256_CTX c;
+ SHA256_Init(&c);
+ SHA256_Update(&c, input.data(), input.size());
+ SHA256_Final(reinterpret_cast<unsigned char*>(hash.data()), &c);
+ return hash;
+}
+
+bool NonAbEstimator::AnalyzePartition(const std::string& partition_name) {
+ auto path = "IMAGES/" + partition_name + ".img";
+ auto fd = ota_tf_->OpenImage(path);
+ if (fd < 0) {
+ return false;
+ }
+
+ unique_fd source_fd;
+ uint64_t source_size = 0;
+ std::unordered_map<std::string, uint64_t> source_blocks;
+ if (source_tf_) {
+ auto dap = source_tf_->GetDynamicPartitionNames();
+
+ source_fd = source_tf_->OpenImage(path);
+ if (source_fd >= 0) {
+ struct stat s;
+ if (fstat(source_fd.get(), &s)) {
+ PLOG(ERROR) << "fstat failed";
+ return false;
+ }
+ source_size = s.st_size;
+
+ std::cout << "Hashing blocks for " << partition_name << "...\n";
+ source_blocks = GetBlockMap(source_fd);
+ if (source_blocks.empty()) {
+ LOG(ERROR) << "Could not build a block map for source partition: "
+ << partition_name;
+ return false;
+ }
+ } else {
+ if (dap.count(partition_name)) {
+ return false;
+ }
+ LOG(ERROR) << "Warning: " << partition_name
+ << " has no incremental diff since it's not in the source image.";
+ }
+ }
+
+ TemporaryFile cow;
+ if (cow.fd < 0) {
+ PLOG(ERROR) << "mkstemp failed";
+ return false;
+ }
+
+ CowOptions options;
+ options.block_size = kBlockSize;
+ options.compression = FLAGS_compression;
+
+ auto writer = std::make_unique<CowWriter>(options);
+ if (!writer->Initialize(borrowed_fd{cow.fd})) {
+ LOG(ERROR) << "Could not initialize COW writer";
+ return false;
+ }
+
+ LOG(INFO) << "Analyzing " << partition_name << " ...";
+
+ std::string zeroes(kBlockSize, '\0');
+ std::string chunk(kBlockSize, '\0');
+ std::string src_chunk(kBlockSize, '\0');
+ uint64_t next_block_number = 0;
+ while (true) {
+ if (!android::base::ReadFully(fd, chunk.data(), chunk.size())) {
+ if (errno) {
+ PLOG(ERROR) << "read failed";
+ return false;
+ }
+ break;
+ }
+
+ uint64_t block_number = next_block_number++;
+ if (chunk == zeroes) {
+ if (!writer->AddZeroBlocks(block_number, 1)) {
+ LOG(ERROR) << "Could not add zero block";
+ return false;
+ }
+ continue;
+ }
+
+ uint64_t source_offset = block_number * kBlockSize;
+ if (source_fd >= 0 && source_offset <= source_size) {
+ off64_t offset = block_number * kBlockSize;
+ if (android::base::ReadFullyAtOffset(source_fd, src_chunk.data(), src_chunk.size(),
+ offset)) {
+ if (chunk == src_chunk) {
+ continue;
+ }
+ } else if (errno) {
+ PLOG(ERROR) << "pread failed";
+ return false;
+ }
+ }
+
+ auto hash = SHA256(chunk);
+ if (auto iter = source_blocks.find(hash); iter != source_blocks.end()) {
+ if (!writer->AddCopy(block_number, iter->second)) {
+ return false;
+ }
+ continue;
+ }
+
+ if (!writer->AddRawBlocks(block_number, chunk.data(), chunk.size())) {
+ return false;
+ }
+ }
+
+ if (!writer->Flush()) {
+ return false;
+ }
+
+ struct stat s;
+ if (fstat(cow.fd, &s) < 0) {
+ PLOG(ERROR) << "fstat failed";
+ return false;
+ }
+
+ size_ += s.st_size;
+ return true;
+}
+
+std::unordered_map<std::string, uint64_t> NonAbEstimator::GetBlockMap(borrowed_fd fd) {
+ std::string chunk(kBlockSize, '\0');
+
+ std::unordered_map<std::string, uint64_t> block_map;
+ uint64_t block_number = 0;
+ while (true) {
+ if (!android::base::ReadFully(fd, chunk.data(), chunk.size())) {
+ if (errno) {
+ PLOG(ERROR) << "read failed";
+ return {};
+ }
+ break;
+ }
+ auto hash = SHA256(chunk);
+ block_map[hash] = block_number;
+ block_number++;
+ }
+ return block_map;
+}
+
+} // namespace snapshot
+} // namespace android
+
+using namespace android::snapshot;
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, android::snapshot::MyLogger);
+ gflags::SetUsageMessage("Estimate VAB disk usage from Non A/B builds");
+ gflags::ParseCommandLineFlags(&argc, &argv, false);
+
+ if (FLAGS_ota_tf.empty()) {
+ std::cerr << "Must specify -ota_tf on the command-line." << std::endl;
+ return 1;
+ }
+
+ NonAbEstimator estimator(FLAGS_ota_tf, FLAGS_source_tf);
+ if (!estimator.Run()) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
new file mode 100644
index 000000000..4a6bd4ed0
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -0,0 +1,107 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+namespace android {
+namespace snapshot {
+
+static constexpr uint64_t kCowMagicNumber = 0x436f77634f572121ULL;
+static constexpr uint32_t kCowVersionMajor = 1;
+static constexpr uint32_t kCowVersionMinor = 0;
+
+// This header appears as the first sequence of bytes in the COW. All fields
+// in the layout are little-endian encoded. The on-disk layout is:
+//
+// +-----------------------+
+// | Header (fixed) |
+// +-----------------------+
+// | Raw Data (variable) |
+// +-----------------------+
+// | Operations (variable) |
+// +-----------------------+
+//
+// The "raw data" occurs immediately after the header, and the operation
+// sequence occurs after the raw data. This ordering is intentional. While
+// streaming an OTA, we can immediately write compressed data, but store the
+// metadata in memory. At the end, we can simply append the metadata and flush
+// the file. There is no need to create separate files to store the metadata
+// and block data.
+struct CowHeader {
+ uint64_t magic;
+ uint16_t major_version;
+ uint16_t minor_version;
+
+ // Size of this struct.
+ uint16_t header_size;
+
+ // Offset to the location of the operation sequence, and size of the
+ // operation sequence buffer. |ops_offset| is also the end of the
+ // raw data region.
+ uint64_t ops_offset;
+ uint64_t ops_size;
+ uint64_t num_ops;
+
+ // The size of block operations, in bytes.
+ uint32_t block_size;
+
+ // SHA256 checksums of this header, with this field set to 0.
+ uint8_t header_checksum[32];
+
+ // SHA256 of the operation sequence.
+ uint8_t ops_checksum[32];
+} __attribute__((packed));
+
+// Cow operations are currently fixed-size entries, but this may change if
+// needed.
+struct CowOperation {
+ // The operation code (see the constants and structures below).
+ uint8_t type;
+
+ // If this operation reads from the data section of the COW, this contains
+ // the compression type of that data (see constants below).
+ uint8_t compression;
+
+ // If this operation reads from the data section of the COW, this contains
+ // the length.
+ uint16_t data_length;
+
+ // The block of data in the new image that this operation modifies.
+ uint64_t new_block;
+
+ // The value of |source| depends on the operation code.
+ //
+ // For copy operations, this is a block location in the source image.
+ //
+ // For replace operations, this is a byte offset within the COW's data
+ // section (eg, not landing within the header or metadata). It is an
+ // absolute position within the image.
+ //
+ // For zero operations (replace with all zeroes), this is unused and must
+ // be zero.
+ uint64_t source;
+} __attribute__((packed));
+
+static constexpr uint8_t kCowCopyOp = 1;
+static constexpr uint8_t kCowReplaceOp = 2;
+static constexpr uint8_t kCowZeroOp = 3;
+
+static constexpr uint8_t kCowCompressNone = 0;
+static constexpr uint8_t kCowCompressGz = 1;
+static constexpr uint8_t kCowCompressBrotli = 2;
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
new file mode 100644
index 000000000..399877637
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -0,0 +1,109 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+class ICowOpIter;
+
+// A ByteSink object handles requests for a buffer of a specific size. It
+// always owns the underlying buffer. It's designed to minimize potential
+// copying as we parse or decompress the COW.
+class IByteSink {
+ public:
+ virtual ~IByteSink() {}
+
+ // Called when the reader has data. The size of the request is given. The
+ // sink must return a valid pointer (or null on failure), and return the
+ // maximum number of bytes that can be written to the returned buffer.
+ //
+ // The returned buffer is owned by IByteSink, but must remain valid until
+ // the ready operation has completed (or the entire buffer has been
+ // covered by calls to ReturnData).
+ //
+ // After calling GetBuffer(), all previous buffers returned are no longer
+ // valid.
+ virtual void* GetBuffer(size_t requested, size_t* actual) = 0;
+
+ // Called when a section returned by |GetBuffer| has been filled with data.
+ virtual bool ReturnData(void* buffer, size_t length) = 0;
+};
+
+// Interface for reading from a snapuserd COW.
+class ICowReader {
+ public:
+ virtual ~ICowReader() {}
+
+ // Return the file header.
+ virtual bool GetHeader(CowHeader* header) = 0;
+
+ // Return an iterator for retrieving CowOperation entries.
+ virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
+
+ // Get decoded bytes from the data section, handling any decompression.
+ // All retrieved data is passed to the sink.
+ virtual bool ReadData(const CowOperation& op, IByteSink* sink) = 0;
+};
+
+// Iterate over a sequence of COW operations.
+class ICowOpIter {
+ public:
+ virtual ~ICowOpIter() {}
+
+ // True if there are more items to read, false otherwise.
+ virtual bool Done() = 0;
+
+ // Read the current operation.
+ virtual const CowOperation& Get() = 0;
+
+ // Advance to the next item.
+ virtual void Next() = 0;
+};
+
+class CowReader : public ICowReader {
+ public:
+ CowReader();
+
+ bool Parse(android::base::unique_fd&& fd);
+ bool Parse(android::base::borrowed_fd fd);
+
+ bool GetHeader(CowHeader* header) override;
+
+ // Create a CowOpIter object which contains header_.num_ops
+ // CowOperation objects. Get() returns a unique CowOperation object
+ // whose lifeteime depends on the CowOpIter object
+ std::unique_ptr<ICowOpIter> GetOpIter() override;
+ bool ReadData(const CowOperation& op, IByteSink* sink) override;
+
+ bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
+
+ private:
+ android::base::unique_fd owned_fd_;
+ android::base::borrowed_fd fd_;
+ CowHeader header_;
+ uint64_t fd_size_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
new file mode 100644
index 000000000..8569161fb
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+struct CowOptions {
+ uint32_t block_size = 4096;
+ std::string compression;
+};
+
+// Interface for writing to a snapuserd COW. All operations are ordered; merges
+// will occur in the sequence they were added to the COW.
+class ICowWriter {
+ public:
+ explicit ICowWriter(const CowOptions& options) : options_(options) {}
+
+ virtual ~ICowWriter() {}
+
+ // Encode an operation that copies the contents of |old_block| to the
+ // location of |new_block|.
+ virtual bool AddCopy(uint64_t new_block, uint64_t old_block) = 0;
+
+ // Encode a sequence of raw blocks. |size| must be a multiple of the block size.
+ virtual bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
+
+ // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
+ virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
+
+ // Flush all pending writes. This must be called before closing the writer
+ // to ensure that the correct headers and footers are written.
+ virtual bool Flush() = 0;
+
+ // Return number of bytes the cow image occupies on disk.
+ virtual size_t GetCowSize() = 0;
+
+ protected:
+ CowOptions options_;
+};
+
+class CowWriter : public ICowWriter {
+ public:
+ enum class OpenMode { WRITE, APPEND };
+
+ explicit CowWriter(const CowOptions& options);
+
+ // Set up the writer.
+ bool Initialize(android::base::unique_fd&& fd, OpenMode mode = OpenMode::WRITE);
+ bool Initialize(android::base::borrowed_fd fd, OpenMode mode = OpenMode::WRITE);
+
+ bool AddCopy(uint64_t new_block, uint64_t old_block) override;
+ bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+ bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+
+ bool Flush() override;
+
+ size_t GetCowSize() override;
+
+ private:
+ void SetupHeaders();
+ bool ParseOptions();
+ bool OpenForWrite();
+ bool OpenForAppend();
+ bool GetDataPos(uint64_t* pos);
+ bool WriteRawData(const void* data, size_t size);
+ void AddOperation(const CowOperation& op);
+ std::basic_string<uint8_t> Compress(const void* data, size_t length);
+
+ private:
+ android::base::unique_fd owned_fd_;
+ android::base::borrowed_fd fd_;
+ CowHeader header_{};
+ int compression_ = 0;
+
+ // :TODO: this is not efficient, but stringstream ubsan aborts because some
+ // bytes overflow a signed char.
+ std::basic_string<uint8_t> ops_;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
new file mode 100644
index 000000000..e75757925
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
@@ -0,0 +1,99 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+namespace android {
+namespace snapshot {
+
+// Kernel COW header fields
+static constexpr uint32_t SNAP_MAGIC = 0x70416e53;
+
+static constexpr uint32_t SNAPSHOT_DISK_VERSION = 1;
+
+static constexpr uint32_t NUM_SNAPSHOT_HDR_CHUNKS = 1;
+
+static constexpr uint32_t SNAPSHOT_VALID = 1;
+
+/*
+ * The basic unit of block I/O is a sector. It is used in a number of contexts
+ * in Linux (blk, bio, genhd). The size of one sector is 512 = 2**9
+ * bytes. Variables of type sector_t represent an offset or size that is a
+ * multiple of 512 bytes. Hence these two constants.
+ */
+static constexpr uint32_t SECTOR_SHIFT = 9;
+
+typedef __u64 sector_t;
+typedef sector_t chunk_t;
+
+static constexpr uint32_t CHUNK_SIZE = 8;
+static constexpr uint32_t CHUNK_SHIFT = (__builtin_ffs(CHUNK_SIZE) - 1);
+
+static constexpr uint32_t BLOCK_SIZE = 4096;
+static constexpr uint32_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SIZE) - 1);
+
+// This structure represents the kernel COW header.
+// All the below fields should be in Little Endian format.
+struct disk_header {
+ uint32_t magic;
+
+ /*
+ * Is this snapshot valid. There is no way of recovering
+ * an invalid snapshot.
+ */
+ uint32_t valid;
+
+ /*
+ * Simple, incrementing version. no backward
+ * compatibility.
+ */
+ uint32_t version;
+
+ /* In sectors */
+ uint32_t chunk_size;
+} __packed;
+
+// A disk exception is a mapping of old_chunk to new_chunk
+// old_chunk is the chunk ID of a dm-snapshot device.
+// new_chunk is the chunk ID of the COW device.
+struct disk_exception {
+ uint64_t old_chunk;
+ uint64_t new_chunk;
+} __packed;
+
+// Control structures to communicate with dm-user
+// It comprises of header and a payload
+struct dm_user_header {
+ __u64 seq;
+ __u64 type;
+ __u64 flags;
+ __u64 sector;
+ __u64 len;
+ __u64 io_in_progress;
+} __attribute__((packed));
+
+struct dm_user_payload {
+ __u8 buf[];
+};
+
+// Message comprising both header and payload
+struct dm_user_message {
+ struct dm_user_header header;
+ struct dm_user_payload payload;
+};
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 98bf56af6..8e369b052 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -180,5 +180,22 @@ class LowSpaceUserdata {
uint64_t bsize_ = 0;
};
+bool IsVirtualAbEnabled();
+
+#define SKIP_IF_NON_VIRTUAL_AB() \
+ do { \
+ if (!IsVirtualAbEnabled()) GTEST_SKIP() << "Test for Virtual A/B devices only"; \
+ } while (0)
+
+#define RETURN_IF_NON_VIRTUAL_AB_MSG(msg) \
+ do { \
+ if (!IsVirtualAbEnabled()) { \
+ std::cerr << (msg); \
+ return; \
+ } \
+ } while (0)
+
+#define RETURN_IF_NON_VIRTUAL_AB() RETURN_IF_NON_VIRTUAL_AB_MSG("")
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
new file mode 100644
index 000000000..f76107709
--- /dev/null
+++ b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
@@ -0,0 +1,690 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <limits>
+#include <string>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <bsdiff/bspatch.h>
+#include <bzlib.h>
+#include <gflags/gflags.h>
+#include <libsnapshot/cow_writer.h>
+#include <puffin/puffpatch.h>
+#include <sparse/sparse.h>
+#include <update_engine/update_metadata.pb.h>
+#include <xz.h>
+#include <ziparchive/zip_archive.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using chromeos_update_engine::DeltaArchiveManifest;
+using chromeos_update_engine::Extent;
+using chromeos_update_engine::InstallOperation;
+using chromeos_update_engine::PartitionUpdate;
+
+static constexpr uint64_t kBlockSize = 4096;
+
+DEFINE_string(source_tf, "", "Source target files (dir or zip file) for incremental payloads");
+DEFINE_string(compression, "gz", "Compression type to use (none or gz)");
+
+void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
+ unsigned int, const char* message) {
+ if (severity == android::base::ERROR) {
+ fprintf(stderr, "%s\n", message);
+ } else {
+ fprintf(stdout, "%s\n", message);
+ }
+}
+
+uint64_t ToLittleEndian(uint64_t value) {
+ union {
+ uint64_t u64;
+ char bytes[8];
+ } packed;
+ packed.u64 = value;
+ std::swap(packed.bytes[0], packed.bytes[7]);
+ std::swap(packed.bytes[1], packed.bytes[6]);
+ std::swap(packed.bytes[2], packed.bytes[5]);
+ std::swap(packed.bytes[3], packed.bytes[4]);
+ return packed.u64;
+}
+
+class PayloadConverter final {
+ public:
+ PayloadConverter(const std::string& in_file, const std::string& out_dir)
+ : in_file_(in_file), out_dir_(out_dir), source_tf_zip_(nullptr, &CloseArchive) {}
+
+ bool Run();
+
+ private:
+ bool OpenPayload();
+ bool OpenSourceTargetFiles();
+ bool ProcessPartition(const PartitionUpdate& update);
+ bool ProcessOperation(const InstallOperation& op);
+ bool ProcessZero(const InstallOperation& op);
+ bool ProcessCopy(const InstallOperation& op);
+ bool ProcessReplace(const InstallOperation& op);
+ bool ProcessDiff(const InstallOperation& op);
+ borrowed_fd OpenSourceImage();
+
+ std::string in_file_;
+ std::string out_dir_;
+ unique_fd in_fd_;
+ uint64_t payload_offset_ = 0;
+ DeltaArchiveManifest manifest_;
+ std::unordered_set<std::string> dap_;
+ unique_fd source_tf_fd_;
+ std::unique_ptr<ZipArchive, decltype(&CloseArchive)> source_tf_zip_;
+
+ // Updated during ProcessPartition().
+ std::string partition_name_;
+ std::unique_ptr<CowWriter> writer_;
+ unique_fd source_image_;
+};
+
+bool PayloadConverter::Run() {
+ if (!OpenPayload()) {
+ return false;
+ }
+
+ if (manifest_.has_dynamic_partition_metadata()) {
+ const auto& dpm = manifest_.dynamic_partition_metadata();
+ for (const auto& group : dpm.groups()) {
+ for (const auto& partition : group.partition_names()) {
+ dap_.emplace(partition);
+ }
+ }
+ }
+
+ if (dap_.empty()) {
+ LOG(ERROR) << "No dynamic partitions found.";
+ return false;
+ }
+
+ if (!OpenSourceTargetFiles()) {
+ return false;
+ }
+
+ for (const auto& update : manifest_.partitions()) {
+ if (!ProcessPartition(update)) {
+ return false;
+ }
+ writer_ = nullptr;
+ source_image_.reset();
+ }
+ return true;
+}
+
+bool PayloadConverter::OpenSourceTargetFiles() {
+ if (FLAGS_source_tf.empty()) {
+ return true;
+ }
+
+ source_tf_fd_.reset(open(FLAGS_source_tf.c_str(), O_RDONLY));
+ if (source_tf_fd_ < 0) {
+ LOG(ERROR) << "open failed: " << FLAGS_source_tf;
+ return false;
+ }
+
+ struct stat s;
+ if (fstat(source_tf_fd_.get(), &s) < 0) {
+ LOG(ERROR) << "fstat failed: " << FLAGS_source_tf;
+ return false;
+ }
+ if (S_ISDIR(s.st_mode)) {
+ return true;
+ }
+
+ // Otherwise, assume it's a zip file.
+ ZipArchiveHandle handle;
+ if (OpenArchiveFd(source_tf_fd_.get(), FLAGS_source_tf.c_str(), &handle, false)) {
+ LOG(ERROR) << "Could not open " << FLAGS_source_tf << " as a zip archive.";
+ return false;
+ }
+ source_tf_zip_.reset(handle);
+ return true;
+}
+
+bool PayloadConverter::ProcessPartition(const PartitionUpdate& update) {
+ auto partition_name = update.partition_name();
+ if (dap_.find(partition_name) == dap_.end()) {
+ // Skip non-DAP partitions.
+ return true;
+ }
+
+ auto path = out_dir_ + "/" + partition_name + ".cow";
+ unique_fd fd(open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
+ if (fd < 0) {
+ PLOG(ERROR) << "open failed: " << path;
+ return false;
+ }
+
+ CowOptions options;
+ options.block_size = kBlockSize;
+ options.compression = FLAGS_compression;
+
+ writer_ = std::make_unique<CowWriter>(options);
+ if (!writer_->Initialize(std::move(fd))) {
+ LOG(ERROR) << "Unable to initialize COW writer";
+ return false;
+ }
+
+ partition_name_ = partition_name;
+
+ for (const auto& op : update.operations()) {
+ if (!ProcessOperation(op)) {
+ return false;
+ }
+ }
+
+ if (!writer_->Flush()) {
+ LOG(ERROR) << "Unable to finalize COW for " << partition_name;
+ return false;
+ }
+ return true;
+}
+
+bool PayloadConverter::ProcessOperation(const InstallOperation& op) {
+ switch (op.type()) {
+ case InstallOperation::SOURCE_COPY:
+ return ProcessCopy(op);
+ case InstallOperation::BROTLI_BSDIFF:
+ case InstallOperation::PUFFDIFF:
+ return ProcessDiff(op);
+ case InstallOperation::REPLACE:
+ case InstallOperation::REPLACE_XZ:
+ case InstallOperation::REPLACE_BZ:
+ return ProcessReplace(op);
+ case InstallOperation::ZERO:
+ return ProcessZero(op);
+ default:
+ LOG(ERROR) << "Unsupported op: " << (int)op.type();
+ return false;
+ }
+ return true;
+}
+
+bool PayloadConverter::ProcessZero(const InstallOperation& op) {
+ for (const auto& extent : op.dst_extents()) {
+ if (!writer_->AddZeroBlocks(extent.start_block(), extent.num_blocks())) {
+ LOG(ERROR) << "Could not add zero operation";
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename T>
+static uint64_t SizeOfAllExtents(const T& extents) {
+ uint64_t total = 0;
+ for (const auto& extent : extents) {
+ total += extent.num_blocks() * kBlockSize;
+ }
+ return total;
+}
+
+class PuffInputStream final : public puffin::StreamInterface {
+ public:
+ PuffInputStream(uint8_t* buffer, size_t length) : buffer_(buffer), length_(length), pos_(0) {}
+
+ bool GetSize(uint64_t* size) const override {
+ *size = length_;
+ return true;
+ }
+ bool GetOffset(uint64_t* offset) const override {
+ *offset = pos_;
+ return true;
+ }
+ bool Seek(uint64_t offset) override {
+ if (offset > length_) return false;
+ pos_ = offset;
+ return true;
+ }
+ bool Read(void* buffer, size_t length) override {
+ if (length_ - pos_ < length) return false;
+ memcpy(buffer, buffer_ + pos_, length);
+ pos_ += length;
+ return true;
+ }
+ bool Write(const void*, size_t) override { return false; }
+ bool Close() override { return true; }
+
+ private:
+ uint8_t* buffer_;
+ size_t length_;
+ size_t pos_;
+};
+
+class PuffOutputStream final : public puffin::StreamInterface {
+ public:
+ PuffOutputStream(std::vector<uint8_t>& stream) : stream_(stream), pos_(0) {}
+
+ bool GetSize(uint64_t* size) const override {
+ *size = stream_.size();
+ return true;
+ }
+ bool GetOffset(uint64_t* offset) const override {
+ *offset = pos_;
+ return true;
+ }
+ bool Seek(uint64_t offset) override {
+ if (offset > stream_.size()) {
+ return false;
+ }
+ pos_ = offset;
+ return true;
+ }
+ bool Read(void* buffer, size_t length) override {
+ if (stream_.size() - pos_ < length) {
+ return false;
+ }
+ memcpy(buffer, &stream_[0] + pos_, length);
+ pos_ += length;
+ return true;
+ }
+ bool Write(const void* buffer, size_t length) override {
+ auto remaining = stream_.size() - pos_;
+ if (remaining < length) {
+ stream_.resize(stream_.size() + (length - remaining));
+ }
+ memcpy(&stream_[0] + pos_, buffer, length);
+ pos_ += length;
+ return true;
+ }
+ bool Close() override { return true; }
+
+ private:
+ std::vector<uint8_t>& stream_;
+ size_t pos_;
+};
+
+bool PayloadConverter::ProcessDiff(const InstallOperation& op) {
+ auto source_image = OpenSourceImage();
+ if (source_image < 0) {
+ return false;
+ }
+
+ uint64_t src_length = SizeOfAllExtents(op.src_extents());
+ auto src = std::make_unique<uint8_t[]>(src_length);
+ size_t src_pos = 0;
+
+ // Read source bytes.
+ for (const auto& extent : op.src_extents()) {
+ uint64_t offset = extent.start_block() * kBlockSize;
+ if (lseek(source_image.get(), offset, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek source image failed";
+ return false;
+ }
+
+ uint64_t size = extent.num_blocks() * kBlockSize;
+ CHECK(src_length - src_pos >= size);
+ if (!android::base::ReadFully(source_image, src.get() + src_pos, size)) {
+ PLOG(ERROR) << "read source image failed";
+ return false;
+ }
+ src_pos += size;
+ }
+ CHECK(src_pos == src_length);
+
+ // Read patch bytes.
+ auto patch = std::make_unique<uint8_t[]>(op.data_length());
+ if (lseek(in_fd_.get(), payload_offset_ + op.data_offset(), SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek payload failed";
+ return false;
+ }
+ if (!android::base::ReadFully(in_fd_, patch.get(), op.data_length())) {
+ PLOG(ERROR) << "read payload failed";
+ return false;
+ }
+
+ std::vector<uint8_t> dest(SizeOfAllExtents(op.dst_extents()));
+
+ // Apply the diff.
+ if (op.type() == InstallOperation::BROTLI_BSDIFF) {
+ size_t dest_pos = 0;
+ auto sink = [&](const uint8_t* data, size_t length) -> size_t {
+ CHECK(dest.size() - dest_pos >= length);
+ memcpy(&dest[dest_pos], data, length);
+ dest_pos += length;
+ return length;
+ };
+ if (int rv = bsdiff::bspatch(src.get(), src_pos, patch.get(), op.data_length(), sink)) {
+ LOG(ERROR) << "bspatch failed, error code " << rv;
+ return false;
+ }
+ } else if (op.type() == InstallOperation::PUFFDIFF) {
+ auto src_stream = std::make_unique<PuffInputStream>(src.get(), src_length);
+ auto dest_stream = std::make_unique<PuffOutputStream>(dest);
+ bool ok = PuffPatch(std::move(src_stream), std::move(dest_stream), patch.get(),
+ op.data_length());
+ if (!ok) {
+ LOG(ERROR) << "puffdiff operation failed to apply";
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "unsupported diff operation: " << op.type();
+ return false;
+ }
+
+ // Write the final blocks to the COW.
+ size_t dest_pos = 0;
+ for (const auto& extent : op.dst_extents()) {
+ uint64_t size = extent.num_blocks() * kBlockSize;
+ CHECK(dest.size() - dest_pos >= size);
+
+ if (!writer_->AddRawBlocks(extent.start_block(), &dest[dest_pos], size)) {
+ return false;
+ }
+ dest_pos += size;
+ }
+ return true;
+}
+
+borrowed_fd PayloadConverter::OpenSourceImage() {
+ if (source_image_ >= 0) {
+ return source_image_;
+ }
+
+ unique_fd unzip_fd;
+
+ auto local_path = "IMAGES/" + partition_name_ + ".img";
+ if (source_tf_zip_) {
+ {
+ TemporaryFile tmp;
+ if (tmp.fd < 0) {
+ PLOG(ERROR) << "mkstemp failed";
+ return -1;
+ }
+ unzip_fd.reset(tmp.release());
+ }
+
+ ZipEntry64 entry;
+ if (FindEntry(source_tf_zip_.get(), local_path, &entry)) {
+ LOG(ERROR) << "not found in archive: " << local_path;
+ return -1;
+ }
+ if (ExtractEntryToFile(source_tf_zip_.get(), &entry, unzip_fd.get())) {
+ LOG(ERROR) << "could not extract " << local_path;
+ return -1;
+ }
+ if (lseek(unzip_fd.get(), 0, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return -1;
+ }
+ } else if (source_tf_fd_ >= 0) {
+ unzip_fd.reset(openat(source_tf_fd_.get(), local_path.c_str(), O_RDONLY));
+ if (unzip_fd < 0) {
+ PLOG(ERROR) << "open failed: " << FLAGS_source_tf << "/" << local_path;
+ return -1;
+ }
+ } else {
+ LOG(ERROR) << "No source target files package was specified; need -source_tf";
+ return -1;
+ }
+
+ std::unique_ptr<struct sparse_file, decltype(&sparse_file_destroy)> s(
+ sparse_file_import(unzip_fd.get(), false, false), &sparse_file_destroy);
+ if (s) {
+ TemporaryFile tmp;
+ if (tmp.fd < 0) {
+ PLOG(ERROR) << "mkstemp failed";
+ return -1;
+ }
+ if (sparse_file_write(s.get(), tmp.fd, false, false, false) < 0) {
+ LOG(ERROR) << "sparse_file_write failed";
+ return -1;
+ }
+ source_image_.reset(tmp.release());
+ } else {
+ source_image_ = std::move(unzip_fd);
+ }
+ return source_image_;
+}
+
+template <typename ContainerType>
+class ExtentIter final {
+ public:
+ ExtentIter(const ContainerType& container)
+ : iter_(container.cbegin()), end_(container.cend()), dst_index_(0) {}
+
+ bool GetNext(uint64_t* block) {
+ while (iter_ != end_) {
+ if (dst_index_ < iter_->num_blocks()) {
+ break;
+ }
+ iter_++;
+ dst_index_ = 0;
+ }
+ if (iter_ == end_) {
+ return false;
+ }
+ *block = iter_->start_block() + dst_index_;
+ dst_index_++;
+ return true;
+ }
+
+ private:
+ typename ContainerType::const_iterator iter_;
+ typename ContainerType::const_iterator end_;
+ uint64_t dst_index_;
+};
+
+bool PayloadConverter::ProcessCopy(const InstallOperation& op) {
+ ExtentIter dst_blocks(op.dst_extents());
+
+ for (const auto& extent : op.src_extents()) {
+ for (uint64_t i = 0; i < extent.num_blocks(); i++) {
+ uint64_t src_block = extent.start_block() + i;
+ uint64_t dst_block;
+ if (!dst_blocks.GetNext(&dst_block)) {
+ LOG(ERROR) << "SOURCE_COPY contained mismatching extents";
+ return false;
+ }
+ if (src_block == dst_block) continue;
+ if (!writer_->AddCopy(dst_block, src_block)) {
+ LOG(ERROR) << "Could not add copy operation";
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool PayloadConverter::ProcessReplace(const InstallOperation& op) {
+ auto buffer_size = op.data_length();
+ auto buffer = std::make_unique<char[]>(buffer_size);
+ uint64_t offs = payload_offset_ + op.data_offset();
+ if (lseek(in_fd_.get(), offs, SEEK_SET) < 0) {
+ PLOG(ERROR) << "lseek " << offs << " failed";
+ return false;
+ }
+ if (!android::base::ReadFully(in_fd_, buffer.get(), buffer_size)) {
+ PLOG(ERROR) << "read " << buffer_size << " bytes from offset " << offs << "failed";
+ return false;
+ }
+
+ uint64_t dst_size = 0;
+ for (const auto& extent : op.dst_extents()) {
+ dst_size += extent.num_blocks() * kBlockSize;
+ }
+
+ if (op.type() == InstallOperation::REPLACE_BZ) {
+ auto tmp = std::make_unique<char[]>(dst_size);
+
+ uint32_t actual_size;
+ if (dst_size > std::numeric_limits<typeof(actual_size)>::max()) {
+ LOG(ERROR) << "too many bytes to decompress: " << dst_size;
+ return false;
+ }
+ actual_size = static_cast<uint32_t>(dst_size);
+
+ auto rv = BZ2_bzBuffToBuffDecompress(tmp.get(), &actual_size, buffer.get(), buffer_size, 0,
+ 0);
+ if (rv) {
+ LOG(ERROR) << "bz2 decompress failed: " << rv;
+ return false;
+ }
+ if (actual_size != dst_size) {
+ LOG(ERROR) << "bz2 returned " << actual_size << " bytes, expected " << dst_size;
+ return false;
+ }
+ buffer = std::move(tmp);
+ buffer_size = dst_size;
+ } else if (op.type() == InstallOperation::REPLACE_XZ) {
+ constexpr uint32_t kXzMaxDictSize = 64 * 1024 * 1024;
+
+ if (dst_size > std::numeric_limits<size_t>::max()) {
+ LOG(ERROR) << "too many bytes to decompress: " << dst_size;
+ return false;
+ }
+
+ std::unique_ptr<struct xz_dec, decltype(&xz_dec_end)> s(
+ xz_dec_init(XZ_DYNALLOC, kXzMaxDictSize), xz_dec_end);
+ if (!s) {
+ LOG(ERROR) << "xz_dec_init failed";
+ return false;
+ }
+
+ auto tmp = std::make_unique<char[]>(dst_size);
+
+ struct xz_buf args;
+ args.in = reinterpret_cast<const uint8_t*>(buffer.get());
+ args.in_pos = 0;
+ args.in_size = buffer_size;
+ args.out = reinterpret_cast<uint8_t*>(tmp.get());
+ args.out_pos = 0;
+ args.out_size = dst_size;
+
+ auto rv = xz_dec_run(s.get(), &args);
+ if (rv != XZ_STREAM_END) {
+ LOG(ERROR) << "xz decompress failed: " << (int)rv;
+ return false;
+ }
+ buffer = std::move(tmp);
+ buffer_size = dst_size;
+ }
+
+ uint64_t buffer_pos = 0;
+ for (const auto& extent : op.dst_extents()) {
+ uint64_t extent_size = extent.num_blocks() * kBlockSize;
+ if (buffer_size - buffer_pos < extent_size) {
+ LOG(ERROR) << "replace op ran out of input buffer";
+ return false;
+ }
+ if (!writer_->AddRawBlocks(extent.start_block(), buffer.get() + buffer_pos, extent_size)) {
+ LOG(ERROR) << "failed to add raw blocks from replace op";
+ return false;
+ }
+ buffer_pos += extent_size;
+ }
+ return true;
+}
+
+bool PayloadConverter::OpenPayload() {
+ in_fd_.reset(open(in_file_.c_str(), O_RDONLY));
+ if (in_fd_ < 0) {
+ PLOG(ERROR) << "open " << in_file_;
+ return false;
+ }
+
+ char magic[4];
+ if (!android::base::ReadFully(in_fd_, magic, sizeof(magic))) {
+ PLOG(ERROR) << "read magic";
+ return false;
+ }
+ if (std::string(magic, sizeof(magic)) != "CrAU") {
+ LOG(ERROR) << "Invalid magic in " << in_file_;
+ return false;
+ }
+
+ uint64_t version;
+ uint64_t manifest_size;
+ uint32_t manifest_signature_size = 0;
+ if (!android::base::ReadFully(in_fd_, &version, sizeof(version))) {
+ PLOG(ERROR) << "read version";
+ return false;
+ }
+ version = ToLittleEndian(version);
+ if (version < 2) {
+ LOG(ERROR) << "Only payload version 2 or higher is supported.";
+ return false;
+ }
+
+ if (!android::base::ReadFully(in_fd_, &manifest_size, sizeof(manifest_size))) {
+ PLOG(ERROR) << "read manifest_size";
+ return false;
+ }
+ manifest_size = ToLittleEndian(manifest_size);
+ if (!android::base::ReadFully(in_fd_, &manifest_signature_size,
+ sizeof(manifest_signature_size))) {
+ PLOG(ERROR) << "read manifest_signature_size";
+ return false;
+ }
+ manifest_signature_size = ntohl(manifest_signature_size);
+
+ auto manifest = std::make_unique<uint8_t[]>(manifest_size);
+ if (!android::base::ReadFully(in_fd_, manifest.get(), manifest_size)) {
+ PLOG(ERROR) << "read manifest";
+ return false;
+ }
+
+ // Skip past manifest signature.
+ auto offs = lseek(in_fd_, manifest_signature_size, SEEK_CUR);
+ if (offs < 0) {
+ PLOG(ERROR) << "lseek failed";
+ return false;
+ }
+ payload_offset_ = offs;
+
+ if (!manifest_.ParseFromArray(manifest.get(), manifest_size)) {
+ LOG(ERROR) << "could not parse manifest";
+ return false;
+ }
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, android::snapshot::MyLogger);
+ gflags::SetUsageMessage("Convert OTA payload to a Virtual A/B COW");
+ int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, false);
+
+ xz_crc32_init();
+
+ if (argc - arg_start != 2) {
+ std::cerr << "Usage: [options] <payload.bin> <out-dir>\n";
+ return 1;
+ }
+
+ android::snapshot::PayloadConverter pc(argv[arg_start], argv[arg_start + 1]);
+ return pc.Run() ? 0 : 1;
+}
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index adfb97519..297022273 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -41,8 +41,14 @@ namespace snapshot {
class PartitionCowCreatorTest : public ::testing::Test {
public:
- void SetUp() override { SnapshotTestPropertyFetcher::SetUp(); }
- void TearDown() override { SnapshotTestPropertyFetcher::TearDown(); }
+ void SetUp() override {
+ SKIP_IF_NON_VIRTUAL_AB();
+ SnapshotTestPropertyFetcher::SetUp();
+ }
+ void TearDown() override {
+ RETURN_IF_NON_VIRTUAL_AB();
+ SnapshotTestPropertyFetcher::TearDown();
+ }
};
TEST_F(PartitionCowCreatorTest, IntersectSelf) {
@@ -223,6 +229,8 @@ TEST_F(PartitionCowCreatorTest, Zero) {
}
TEST(DmSnapshotInternals, CowSizeCalculator) {
+ SKIP_IF_NON_VIRTUAL_AB();
+
DmSnapCowSizeCalculator cc(512, 8);
unsigned long int b;
@@ -286,7 +294,9 @@ struct OptimizeOperationTestParam {
std::optional<InstallOperation> expected_output;
};
-class OptimizeOperationTest : public ::testing::TestWithParam<OptimizeOperationTestParam> {};
+class OptimizeOperationTest : public ::testing::TestWithParam<OptimizeOperationTestParam> {
+ void SetUp() override { SKIP_IF_NON_VIRTUAL_AB(); }
+};
TEST_P(OptimizeOperationTest, Test) {
InstallOperation actual_output;
EXPECT_EQ(GetParam().expected_output.has_value(),
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
index 12101a232..17a0c96a8 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater.cpp
@@ -39,6 +39,8 @@ namespace snapshot {
SnapshotMetadataUpdater::SnapshotMetadataUpdater(MetadataBuilder* builder, uint32_t target_slot,
const DeltaArchiveManifest& manifest)
: builder_(builder), target_suffix_(SlotSuffixForSlotNumber(target_slot)) {
+ partial_update_ = manifest.partial_update();
+
if (!manifest.has_dynamic_partition_metadata()) {
return;
}
@@ -63,7 +65,6 @@ SnapshotMetadataUpdater::SnapshotMetadataUpdater(MetadataBuilder* builder, uint3
}
}
- partial_update_ = manifest.partial_update();
}
bool SnapshotMetadataUpdater::ShrinkPartitions() const {
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
index 5530e59f0..0a16c03ce 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
@@ -43,11 +43,11 @@ namespace snapshot {
class SnapshotMetadataUpdaterTest : public ::testing::TestWithParam<uint32_t> {
public:
- SnapshotMetadataUpdaterTest() {
- is_virtual_ab_ = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
- }
+ SnapshotMetadataUpdaterTest() = default;
void SetUp() override {
+ SKIP_IF_NON_VIRTUAL_AB();
+
target_slot_ = GetParam();
target_suffix_ = SlotSuffixForSlotNumber(target_slot_);
SnapshotTestPropertyFetcher::SetUp(SlotSuffixForSlotNumber(1 - target_slot_));
@@ -68,7 +68,11 @@ class SnapshotMetadataUpdaterTest : public ::testing::TestWithParam<uint32_t> {
ASSERT_TRUE(FillFakeMetadata(builder_.get(), manifest_, target_suffix_));
}
- void TearDown() override { SnapshotTestPropertyFetcher::TearDown(); }
+ void TearDown() override {
+ RETURN_IF_NON_VIRTUAL_AB();
+
+ SnapshotTestPropertyFetcher::TearDown();
+ }
// Append suffix to name.
std::string T(std::string_view name) { return std::string(name) + target_suffix_; }
@@ -127,7 +131,6 @@ class SnapshotMetadataUpdaterTest : public ::testing::TestWithParam<uint32_t> {
<< ".";
}
- bool is_virtual_ab_;
std::unique_ptr<MetadataBuilder> builder_;
uint32_t target_slot_;
std::string target_suffix_;
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 2bd013599..6ff935bac 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -83,9 +83,7 @@ void MountMetadata();
class SnapshotTest : public ::testing::Test {
public:
- SnapshotTest() : dm_(DeviceMapper::Instance()) {
- is_virtual_ab_ = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
- }
+ SnapshotTest() : dm_(DeviceMapper::Instance()) {}
// This is exposed for main.
void Cleanup() {
@@ -95,7 +93,7 @@ class SnapshotTest : public ::testing::Test {
protected:
void SetUp() override {
- if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
+ SKIP_IF_NON_VIRTUAL_AB();
SnapshotTestPropertyFetcher::SetUp();
InitializeState();
@@ -106,7 +104,7 @@ class SnapshotTest : public ::testing::Test {
}
void TearDown() override {
- if (!is_virtual_ab_) return;
+ RETURN_IF_NON_VIRTUAL_AB();
lock_ = nullptr;
@@ -341,7 +339,6 @@ class SnapshotTest : public ::testing::Test {
}
static constexpr std::chrono::milliseconds snapshot_timeout_ = 5s;
- bool is_virtual_ab_;
DeviceMapper& dm_;
std::unique_ptr<SnapshotManager::LockedFile> lock_;
android::fiemap::IImageManager* image_manager_ = nullptr;
@@ -722,11 +719,13 @@ class LockTestConsumer {
class LockTest : public ::testing::Test {
public:
void SetUp() {
+ SKIP_IF_NON_VIRTUAL_AB();
first_consumer.StartHandleRequestsInBackground();
second_consumer.StartHandleRequestsInBackground();
}
void TearDown() {
+ RETURN_IF_NON_VIRTUAL_AB();
EXPECT_TRUE(first_consumer.MakeRequest(Request::EXIT));
EXPECT_TRUE(second_consumer.MakeRequest(Request::EXIT));
}
@@ -770,7 +769,7 @@ INSTANTIATE_TEST_SUITE_P(
class SnapshotUpdateTest : public SnapshotTest {
public:
void SetUp() override {
- if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
+ SKIP_IF_NON_VIRTUAL_AB();
SnapshotTest::SetUp();
Cleanup();
@@ -832,7 +831,7 @@ class SnapshotUpdateTest : public SnapshotTest {
}
}
void TearDown() override {
- if (!is_virtual_ab_) return;
+ RETURN_IF_NON_VIRTUAL_AB();
Cleanup();
SnapshotTest::TearDown();
@@ -1365,13 +1364,17 @@ TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
}
-class MetadataMountedTest : public SnapshotUpdateTest {
+class MetadataMountedTest : public ::testing::Test {
public:
+ // This is so main() can instantiate this to invoke Cleanup.
+ virtual void TestBody() override {}
void SetUp() override {
+ SKIP_IF_NON_VIRTUAL_AB();
metadata_dir_ = test_device->GetMetadataDir();
ASSERT_TRUE(ReadDefaultFstab(&fstab_));
}
void TearDown() override {
+ RETURN_IF_NON_VIRTUAL_AB();
SetUp();
// Remount /metadata
test_device->set_recovery(false);
@@ -1702,8 +1705,6 @@ class FlashAfterUpdateTest : public SnapshotUpdateTest,
};
TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
- if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
-
// OTA client blindly unmaps all partitions that are possibly mapped.
for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));
@@ -1803,14 +1804,13 @@ INSTANTIATE_TEST_SUITE_P(Snapshot, FlashAfterUpdateTest, Combine(Values(0, 1), B
class ImageManagerTest : public SnapshotTest, public WithParamInterface<uint64_t> {
protected:
void SetUp() override {
- if (!is_virtual_ab_) GTEST_SKIP() << "Test for Virtual A/B devices only";
-
+ SKIP_IF_NON_VIRTUAL_AB();
SnapshotTest::SetUp();
userdata_ = std::make_unique<LowSpaceUserdata>();
ASSERT_TRUE(userdata_->Init(GetParam()));
}
void TearDown() override {
- if (!is_virtual_ab_) return;
+ RETURN_IF_NON_VIRTUAL_AB();
return; // BUG(149738928)
EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
@@ -1852,11 +1852,6 @@ std::vector<uint64_t> ImageManagerTestParams() {
INSTANTIATE_TEST_SUITE_P(ImageManagerTest, ImageManagerTest, ValuesIn(ImageManagerTestParams()));
-} // namespace snapshot
-} // namespace android
-
-using namespace android::snapshot;
-
bool Mkdir(const std::string& path) {
if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
@@ -1865,8 +1860,21 @@ bool Mkdir(const std::string& path) {
return true;
}
-int main(int argc, char** argv) {
- ::testing::InitGoogleTest(&argc, argv);
+class SnapshotTestEnvironment : public ::testing::Environment {
+ public:
+ ~SnapshotTestEnvironment() override {}
+ void SetUp() override;
+ void TearDown() override;
+
+ private:
+ std::unique_ptr<IImageManager> super_images_;
+};
+
+void SnapshotTestEnvironment::SetUp() {
+ // b/163082876: GTEST_SKIP in Environment will make atest report incorrect results. Until
+ // that is fixed, don't call GTEST_SKIP here, but instead call GTEST_SKIP in individual test
+ // suites.
+ RETURN_IF_NON_VIRTUAL_AB_MSG("Virtual A/B is not enabled, skipping global setup.\n");
std::vector<std::string> paths = {
// clang-format off
@@ -1879,18 +1887,13 @@ int main(int argc, char** argv) {
// clang-format on
};
for (const auto& path : paths) {
- if (!Mkdir(path)) {
- return 1;
- }
+ ASSERT_TRUE(Mkdir(path));
}
// Create this once, otherwise, gsid will start/stop between each test.
test_device = new TestDeviceInfo();
sm = SnapshotManager::New(test_device);
- if (!sm) {
- std::cerr << "Could not create snapshot manager\n";
- return 1;
- }
+ ASSERT_NE(nullptr, sm) << "Could not create snapshot manager";
// Clean up previous run.
MetadataMountedTest().TearDown();
@@ -1898,31 +1901,35 @@ int main(int argc, char** argv) {
SnapshotTest().Cleanup();
// Use a separate image manager for our fake super partition.
- auto super_images = IImageManager::Open("ota/test/super", 10s);
- if (!super_images) {
- std::cerr << "Could not create image manager\n";
- return 1;
- }
+ super_images_ = IImageManager::Open("ota/test/super", 10s);
+ ASSERT_NE(nullptr, super_images_) << "Could not create image manager";
// Clean up any old copy.
- DeleteBackingImage(super_images.get(), "fake-super");
+ DeleteBackingImage(super_images_.get(), "fake-super");
// Create and map the fake super partition.
static constexpr int kImageFlags =
IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;
- if (!super_images->CreateBackingImage("fake-super", kSuperSize, kImageFlags)) {
- std::cerr << "Could not create fake super partition\n";
- return 1;
- }
- if (!super_images->MapImageDevice("fake-super", 10s, &fake_super)) {
- std::cerr << "Could not map fake super partition\n";
- return 1;
- }
+ ASSERT_TRUE(super_images_->CreateBackingImage("fake-super", kSuperSize, kImageFlags))
+ << "Could not create fake super partition";
+
+ ASSERT_TRUE(super_images_->MapImageDevice("fake-super", 10s, &fake_super))
+ << "Could not map fake super partition";
test_device->set_fake_super(fake_super);
+}
- auto result = RUN_ALL_TESTS();
+void SnapshotTestEnvironment::TearDown() {
+ RETURN_IF_NON_VIRTUAL_AB();
+ if (super_images_ != nullptr) {
+ DeleteBackingImage(super_images_.get(), "fake-super");
+ }
+}
- DeleteBackingImage(super_images.get(), "fake-super");
+} // namespace snapshot
+} // namespace android
- return result;
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::AddGlobalTestEnvironment(new ::android::snapshot::SnapshotTestEnvironment());
+ return RUN_ALL_TESTS();
}
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index a6ff4fd04..d3f4f70de 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -15,102 +15,735 @@
*/
#include <linux/types.h>
+#include <stdlib.h>
+
+#include <csignal>
+#include <cstring>
+#include <iostream>
+#include <limits>
+#include <string>
+#include <thread>
+#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <libdm/dm.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include <libsnapshot/snapuserd.h>
+
+namespace android {
+namespace snapshot {
+using namespace android;
+using namespace android::dm;
using android::base::unique_fd;
#define DM_USER_MAP_READ 0
#define DM_USER_MAP_WRITE 1
-struct dm_user_message {
- __u64 seq;
- __u64 type;
- __u64 flags;
- __u64 sector;
- __u64 len;
- __u8 buf[];
+static constexpr size_t PAYLOAD_SIZE = (1UL << 16);
+
+static_assert(PAYLOAD_SIZE >= BLOCK_SIZE);
+
+class Target {
+ public:
+ // Represents an already-created Target, which is referenced by UUID.
+ Target(std::string uuid) : uuid_(uuid) {}
+
+ const auto& uuid() { return uuid_; }
+ std::string control_path() { return std::string("/dev/dm-user-") + uuid(); }
+
+ private:
+ const std::string uuid_;
};
-using namespace android::dm;
+class Daemon {
+ // The Daemon class is a singleton to avoid
+ // instantiating more than once
+ public:
+ static Daemon& Instance() {
+ static Daemon instance;
+ return instance;
+ }
+
+ bool IsRunning();
+
+ private:
+ bool is_running_;
+
+ Daemon();
+ Daemon(Daemon const&) = delete;
+ void operator=(Daemon const&) = delete;
+
+ static void SignalHandler(int signal);
+};
+
+Daemon::Daemon() {
+ is_running_ = true;
+ signal(SIGINT, Daemon::SignalHandler);
+ signal(SIGTERM, Daemon::SignalHandler);
+}
+
+bool Daemon::IsRunning() {
+ return is_running_;
+}
+
+void Daemon::SignalHandler(int signal) {
+ LOG(DEBUG) << "Snapuserd received signal: " << signal;
+ switch (signal) {
+ case SIGINT:
+ case SIGTERM: {
+ Daemon::Instance().is_running_ = false;
+ break;
+ }
+ }
+}
+
+class BufferSink : public IByteSink {
+ public:
+ void Initialize(size_t size) {
+ buffer_size_ = size;
+ buffer_offset_ = 0;
+ buffer_ = std::make_unique<uint8_t[]>(size);
+ }
+
+ void* GetBufPtr() { return buffer_.get(); }
+
+ void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
+
+ void* GetPayloadBuffer(size_t size) {
+ if ((buffer_size_ - buffer_offset_) < size) return nullptr;
+
+ char* buffer = reinterpret_cast<char*>(GetBufPtr());
+ struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
+ return (char*)msg->payload.buf + buffer_offset_;
+ }
+
+ void* GetBuffer(size_t requested, size_t* actual) override {
+ void* buf = GetPayloadBuffer(requested);
+ if (!buf) {
+ *actual = 0;
+ return nullptr;
+ }
+ *actual = requested;
+ return buf;
+ }
+
+ void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
+
+ struct dm_user_header* GetHeaderPtr() {
+ CHECK(sizeof(struct dm_user_header) <= buffer_size_);
+ char* buf = reinterpret_cast<char*>(GetBufPtr());
+ struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
+ return header;
+ }
+
+ bool ReturnData(void*, size_t) override { return true; }
+ void ResetBufferOffset() { buffer_offset_ = 0; }
+
+ private:
+ std::unique_ptr<uint8_t[]> buffer_;
+ loff_t buffer_offset_;
+ size_t buffer_size_;
+};
+
+class Snapuserd final {
+ public:
+ Snapuserd(const std::string& in_cow_device, const std::string& in_backing_store_device)
+ : in_cow_device_(in_cow_device),
+ in_backing_store_device_(in_backing_store_device),
+ metadata_read_done_(false) {}
+
+ int Run();
+ int ReadDmUserHeader();
+ int WriteDmUserPayload(size_t size);
+ int ConstructKernelCowHeader();
+ int ReadMetadata();
+ int ZerofillDiskExceptions(size_t read_size);
+ int ReadDiskExceptions(chunk_t chunk, size_t size);
+ int ReadData(chunk_t chunk, size_t size);
+
+ private:
+ int ProcessReplaceOp(const CowOperation* cow_op);
+ int ProcessCopyOp(const CowOperation* cow_op);
+ int ProcessZeroOp();
+
+ std::string in_cow_device_;
+ std::string in_backing_store_device_;
+
+ unique_fd cow_fd_;
+ unique_fd backing_store_fd_;
+ unique_fd ctrl_fd_;
+
+ uint32_t exceptions_per_area_;
+
+ std::unique_ptr<ICowOpIter> cowop_iter_;
+ std::unique_ptr<CowReader> reader_;
+
+ // Vector of disk exception which is a
+ // mapping of old-chunk to new-chunk
+ std::vector<std::unique_ptr<uint8_t[]>> vec_;
+
+ // Index - Chunk ID
+ // Value - cow operation
+ std::vector<const CowOperation*> chunk_vec_;
+
+ bool metadata_read_done_;
+ BufferSink bufsink_;
+};
+
+// Construct kernel COW header in memory
+// This header will be in sector 0. The IO
+// request will always be 4k. After constructing
+// the header, zero out the remaining block.
+int Snapuserd::ConstructKernelCowHeader() {
+ void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SIZE);
+ CHECK(buffer != nullptr);
+
+ memset(buffer, 0, BLOCK_SIZE);
+
+ struct disk_header* dh = reinterpret_cast<struct disk_header*>(buffer);
+
+ dh->magic = SNAP_MAGIC;
+ dh->valid = SNAPSHOT_VALID;
+ dh->version = SNAPSHOT_DISK_VERSION;
+ dh->chunk_size = CHUNK_SIZE;
+
+ return BLOCK_SIZE;
+}
+
+// Start the replace operation. This will read the
+// internal COW format and if the block is compressed,
+// it will be de-compressed.
+int Snapuserd::ProcessReplaceOp(const CowOperation* cow_op) {
+ if (!reader_->ReadData(*cow_op, &bufsink_)) {
+ LOG(ERROR) << "ReadData failed for chunk: " << cow_op->new_block;
+ return -EIO;
+ }
+
+ return BLOCK_SIZE;
+}
+
+// Start the copy operation. This will read the backing
+// block device which is represented by cow_op->source.
+int Snapuserd::ProcessCopyOp(const CowOperation* cow_op) {
+ void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SIZE);
+ CHECK(buffer != nullptr);
+
+ // Issue a single 4K IO. However, this can be optimized
+ // if the successive blocks are contiguous.
+ if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SIZE,
+ cow_op->source * BLOCK_SIZE)) {
+ LOG(ERROR) << "Copy-op failed. Read from backing store at: " << cow_op->source;
+ return -1;
+ }
+
+ return BLOCK_SIZE;
+}
+
+int Snapuserd::ProcessZeroOp() {
+ // Zero out the entire block
+ void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SIZE);
+ CHECK(buffer != nullptr);
+
+ memset(buffer, 0, BLOCK_SIZE);
+ return BLOCK_SIZE;
+}
+
+/*
+ * Read the data of size bytes from a given chunk.
+ *
+ * Kernel can potentially merge the blocks if the
+ * successive chunks are contiguous. For chunk size of 8,
+ * there can be 256 disk exceptions; and if
+ * all 256 disk exceptions are contiguous, kernel can merge
+ * them into a single IO.
+ *
+ * Since each chunk in the disk exception
+ * mapping represents a 4k block, kernel can potentially
+ * issue 256*4k = 1M IO in one shot.
+ *
+ * Even though kernel assumes that the blocks are
+ * contiguous, we need to split the 1M IO into 4k chunks
+ * as each operation represents 4k and it can either be:
+ *
+ * 1: Replace operation
+ * 2: Copy operation
+ * 3: Zero operation
+ *
+ */
+int Snapuserd::ReadData(chunk_t chunk, size_t size) {
+ int ret = 0;
+
+ size_t read_size = size;
+
+ chunk_t chunk_key = chunk;
+ uint32_t stride;
+ lldiv_t divresult;
+
+ // Size should always be aligned
+ CHECK((read_size & (BLOCK_SIZE - 1)) == 0);
+
+ while (read_size > 0) {
+ const CowOperation* cow_op = chunk_vec_[chunk_key];
+ CHECK(cow_op != nullptr);
+ int result;
+
+ switch (cow_op->type) {
+ case kCowReplaceOp: {
+ result = ProcessReplaceOp(cow_op);
+ break;
+ }
+
+ case kCowZeroOp: {
+ result = ProcessZeroOp();
+ break;
+ }
+
+ case kCowCopyOp: {
+ result = ProcessCopyOp(cow_op);
+ break;
+ }
+
+ default: {
+ LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+ ret = -EIO;
+ goto done;
+ }
+ }
+
+ if (result < 0) {
+ ret = result;
+ goto done;
+ }
+
+ // Update the buffer offset
+ bufsink_.UpdateBufferOffset(BLOCK_SIZE);
+
+ read_size -= BLOCK_SIZE;
+ ret += BLOCK_SIZE;
+
+ // Start iterating the chunk incrementally; Since while
+ // constructing the metadata, we know that the chunk IDs
+ // are contiguous
+ chunk_key += 1;
+
+ // This is similar to the way when chunk IDs were assigned
+ // in ReadMetadata().
+ //
+ // Skip if the chunk id represents a metadata chunk.
+ stride = exceptions_per_area_ + 1;
+ divresult = lldiv(chunk_key, stride);
+ if (divresult.rem == NUM_SNAPSHOT_HDR_CHUNKS) {
+ // Crossing exception boundary. Kernel will never
+ // issue IO which is spanning between a data chunk
+ // and a metadata chunk. This should be perfectly aligned.
+ //
+ // Since the input read_size is 4k aligned, we will
+ // always end up reading all 256 data chunks in one area.
+ // Thus, every multiple of 4K IO represents 256 data chunks
+ CHECK(read_size == 0);
+ break;
+ }
+ }
+
+done:
+
+ // Reset the buffer offset
+ bufsink_.ResetBufferOffset();
+ return ret;
+}
+
+/*
+ * dm-snap does prefetch reads while reading disk-exceptions.
+ * By default, prefetch value is set to 12; this means that
+ * dm-snap will issue 12 areas wherein each area is a 4k page
+ * of disk-exceptions.
+ *
+ * If during prefetch, if the chunk-id seen is beyond the
+ * actual number of metadata page, fill the buffer with zero.
+ * When dm-snap starts parsing the buffer, it will stop
+ * reading metadata page once the buffer content is zero.
+ */
+int Snapuserd::ZerofillDiskExceptions(size_t read_size) {
+ size_t size = exceptions_per_area_ * sizeof(struct disk_exception);
+
+ if (read_size > size) return -EINVAL;
+
+ void* buffer = bufsink_.GetPayloadBuffer(size);
+ CHECK(buffer != nullptr);
+
+ memset(buffer, 0, size);
+ return size;
+}
+
+/*
+ * A disk exception is a simple mapping of old_chunk to new_chunk.
+ * When dm-snapshot device is created, kernel requests these mapping.
+ *
+ * Each disk exception is of size 16 bytes. Thus a single 4k page can
+ * have:
+ *
+ * exceptions_per_area_ = 4096/16 = 256. This entire 4k page
+ * is considered a metadata page and it is represented by chunk ID.
+ *
+ * Convert the chunk ID to index into the vector which gives us
+ * the metadata page.
+ */
+int Snapuserd::ReadDiskExceptions(chunk_t chunk, size_t read_size) {
+ uint32_t stride = exceptions_per_area_ + 1;
+ size_t size;
+
+ // ChunkID to vector index
+ lldiv_t divresult = lldiv(chunk, stride);
+
+ if (divresult.quot < vec_.size()) {
+ size = exceptions_per_area_ * sizeof(struct disk_exception);
+
+ if (read_size > size) return -EINVAL;
+
+ void* buffer = bufsink_.GetPayloadBuffer(size);
+ CHECK(buffer != nullptr);
+
+ memcpy(buffer, vec_[divresult.quot].get(), size);
+ } else {
+ size = ZerofillDiskExceptions(read_size);
+ }
+
+ return size;
+}
+
+/*
+ * Read the metadata from COW device and
+ * construct the metadata as required by the kernel.
+ *
+ * Please see design on kernel COW format
+ *
+ * 1: Read the metadata from internal COW device
+ * 2: There are 3 COW operations:
+ * a: Replace op
+ * b: Copy op
+ * c: Zero op
+ * 3: For each of the 3 operations, op->new_block
+ * represents the block number in the base device
+ * for which one of the 3 operations have to be applied.
+ * This represents the old_chunk in the kernel COW format
+ * 4: We need to assign new_chunk for a corresponding old_chunk
+ * 5: The algorithm is similar to how kernel assigns chunk number
+ * while creating exceptions.
+ * 6: Use a monotonically increasing chunk number to assign the
+ * new_chunk
+ * 7: Each chunk-id represents either a: Metadata page or b: Data page
+ * 8: Chunk-id representing a data page is stored in a vector. Index is the
+ * chunk-id and value is the pointer to the CowOperation
+ * 9: Chunk-id representing a metadata page is converted into a vector
+ * index. We store this in vector as kernel requests metadata during
+ * two stage:
+ * a: When initial dm-snapshot device is created, kernel requests
+ * all the metadata and stores it in its internal data-structures.
+ * b: During merge, kernel once again requests the same metadata
+ * once-again.
+ * In both these cases, a quick lookup based on chunk-id is done.
+ * 10: When chunk number is incremented, we need to make sure that
+ * if the chunk is representing a metadata page and skip.
+ * 11: Each 4k page will contain 256 disk exceptions. We call this
+ * exceptions_per_area_
+ * 12: Kernel will stop issuing metadata IO request when new-chunk ID is 0.
+ */
+int Snapuserd::ReadMetadata() {
+ reader_ = std::make_unique<CowReader>();
+ CowHeader header;
-static int daemon_main(const std::string& device) {
- unique_fd block_fd(open(device.c_str(), O_RDWR));
- if (block_fd < 0) {
- PLOG(ERROR) << "Unable to open " << device;
+ if (!reader_->Parse(cow_fd_)) {
+ LOG(ERROR) << "Failed to parse";
return 1;
}
- unique_fd ctrl_fd(open("/dev/dm-user", O_RDWR));
- if (ctrl_fd < 0) {
- PLOG(ERROR) << "Unable to open /dev/dm-user";
+ if (!reader_->GetHeader(&header)) {
+ LOG(ERROR) << "Failed to get header";
return 1;
}
- size_t buf_size = 1UL << 16;
- auto buf = std::make_unique<char>(buf_size);
+ CHECK(header.block_size == BLOCK_SIZE);
- /* Just keeps pumping messages between userspace and the kernel. We won't
- * actually be doing anything, but the sequence numbers line up so it'll at
- * least make forward progress. */
- while (true) {
- struct dm_user_message* msg = (struct dm_user_message*)buf.get();
+ LOG(DEBUG) << "Num-ops: " << std::hex << header.num_ops;
+ LOG(DEBUG) << "ops-offset: " << std::hex << header.ops_offset;
+ LOG(DEBUG) << "ops-size: " << std::hex << header.ops_size;
+
+ cowop_iter_ = reader_->GetOpIter();
+
+ if (cowop_iter_ == nullptr) {
+ LOG(ERROR) << "Failed to get cowop_iter";
+ return 1;
+ }
+
+ exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
+
+ // Start from chunk number 2. Chunk 0 represents header and chunk 1
+ // represents first metadata page.
+ chunk_t next_free = NUM_SNAPSHOT_HDR_CHUNKS + 1;
+ chunk_vec_.push_back(nullptr);
+ chunk_vec_.push_back(nullptr);
+
+ loff_t offset = 0;
+ std::unique_ptr<uint8_t[]> de_ptr =
+ std::make_unique<uint8_t[]>(exceptions_per_area_ * sizeof(struct disk_exception));
+
+ // This memset is important. Kernel will stop issuing IO when new-chunk ID
+ // is 0. When Area is not filled completely will all 256 exceptions,
+ // this memset will ensure that metadata read is completed.
+ memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
+ size_t num_ops = 0;
+
+ while (!cowop_iter_->Done()) {
+ const CowOperation* cow_op = &cowop_iter_->Get();
+ struct disk_exception* de =
+ reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
+
+ if (!(cow_op->type == kCowReplaceOp || cow_op->type == kCowZeroOp ||
+ cow_op->type == kCowCopyOp)) {
+ LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+ return 1;
+ }
+
+ // Construct the disk-exception
+ de->old_chunk = cow_op->new_block;
+ de->new_chunk = next_free;
- memset(buf.get(), 0, buf_size);
+ LOG(DEBUG) << "Old-chunk: " << de->old_chunk << "New-chunk: " << de->new_chunk;
- ssize_t readed = read(ctrl_fd.get(), buf.get(), buf_size);
- if (readed < 0) {
- PLOG(ERROR) << "Control read failed, trying with more space";
- buf_size *= 2;
- buf = std::make_unique<char>(buf_size);
- continue;
+ // Store operation pointer. Note, new-chunk ID is the index
+ chunk_vec_.push_back(cow_op);
+ CHECK(next_free == (chunk_vec_.size() - 1));
+
+ offset += sizeof(struct disk_exception);
+
+ cowop_iter_->Next();
+
+ // Find the next free chunk-id to be assigned. Check if the next free
+ // chunk-id represents a metadata page. If so, skip it.
+ next_free += 1;
+ uint32_t stride = exceptions_per_area_ + 1;
+ lldiv_t divresult = lldiv(next_free, stride);
+ num_ops += 1;
+
+ if (divresult.rem == NUM_SNAPSHOT_HDR_CHUNKS) {
+ CHECK(num_ops == exceptions_per_area_);
+ // Store it in vector at the right index. This maps the chunk-id to
+ // vector index.
+ vec_.push_back(std::move(de_ptr));
+ offset = 0;
+ num_ops = 0;
+
+ chunk_t metadata_chunk = (next_free - exceptions_per_area_ - NUM_SNAPSHOT_HDR_CHUNKS);
+
+ LOG(DEBUG) << "Area: " << vec_.size() - 1;
+ LOG(DEBUG) << "Metadata-chunk: " << metadata_chunk;
+ LOG(DEBUG) << "Sector number of Metadata-chunk: " << (metadata_chunk << CHUNK_SHIFT);
+
+ // Create buffer for next area
+ de_ptr = std::make_unique<uint8_t[]>(exceptions_per_area_ *
+ sizeof(struct disk_exception));
+ memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
+
+ // Since this is a metadata, store at this index
+ chunk_vec_.push_back(nullptr);
+
+ // Find the next free chunk-id
+ next_free += 1;
+ if (cowop_iter_->Done()) {
+ vec_.push_back(std::move(de_ptr));
+ }
}
+ }
- LOG(DEBUG) << android::base::StringPrintf("read() from dm-user returned %d bytes:",
- (int)readed);
- LOG(DEBUG) << android::base::StringPrintf(" msg->seq: 0x%016llx", msg->seq);
- LOG(DEBUG) << android::base::StringPrintf(" msg->type: 0x%016llx", msg->type);
- LOG(DEBUG) << android::base::StringPrintf(" msg->flags: 0x%016llx", msg->flags);
- LOG(DEBUG) << android::base::StringPrintf(" msg->sector: 0x%016llx", msg->sector);
- LOG(DEBUG) << android::base::StringPrintf(" msg->len: 0x%016llx", msg->len);
+ // Partially filled area
+ if (num_ops) {
+ LOG(DEBUG) << "Partially filled area num_ops: " << num_ops;
+ vec_.push_back(std::move(de_ptr));
+ }
- switch (msg->type) {
+ return 0;
+}
+
+void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
+ unsigned int, const char* message) {
+ if (severity == android::base::ERROR) {
+ fprintf(stderr, "%s\n", message);
+ } else {
+ fprintf(stdout, "%s\n", message);
+ }
+}
+
+// Read Header from dm-user misc device. This gives
+// us the sector number for which IO is issued by dm-snapshot device
+int Snapuserd::ReadDmUserHeader() {
+ if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
+ PLOG(ERROR) << "Control read failed";
+ return -1;
+ }
+
+ return sizeof(struct dm_user_header);
+}
+
+// Send the payload/data back to dm-user misc device.
+int Snapuserd::WriteDmUserPayload(size_t size) {
+ if (!android::base::WriteFully(ctrl_fd_, bufsink_.GetBufPtr(),
+ sizeof(struct dm_user_header) + size)) {
+ PLOG(ERROR) << "Write to dm-user failed";
+ return -1;
+ }
+
+ return sizeof(struct dm_user_header) + size;
+}
+
+// Start the daemon.
+// TODO: Handle signals
+int Snapuserd::Run() {
+ backing_store_fd_.reset(open(in_backing_store_device_.c_str(), O_RDONLY));
+ if (backing_store_fd_ < 0) {
+ LOG(ERROR) << "Open Failed: " << in_backing_store_device_;
+ return 1;
+ }
+
+ cow_fd_.reset(open(in_cow_device_.c_str(), O_RDWR));
+ if (cow_fd_ < 0) {
+ LOG(ERROR) << "Open Failed: " << in_cow_device_;
+ return 1;
+ }
+
+ std::string str(in_cow_device_);
+ std::size_t found = str.find_last_of("/\\");
+ CHECK(found != std::string::npos);
+ std::string device_name = str.substr(found + 1);
+
+ LOG(DEBUG) << "Fetching UUID for: " << device_name;
+
+ auto& dm = dm::DeviceMapper::Instance();
+ std::string uuid;
+ if (!dm.GetDmDeviceUuidByName(device_name, &uuid)) {
+ LOG(ERROR) << "Unable to find UUID for " << in_cow_device_;
+ return 1;
+ }
+
+ LOG(DEBUG) << "UUID: " << uuid;
+ Target t(uuid);
+
+ ctrl_fd_.reset(open(t.control_path().c_str(), O_RDWR));
+ if (ctrl_fd_ < 0) {
+ LOG(ERROR) << "Unable to open " << t.control_path();
+ return 1;
+ }
+
+ int ret = 0;
+
+ // Allocate the buffer which is used to communicate between
+ // daemon and dm-user. The buffer comprises of header and a fixed payload.
+ // If the dm-user requests a big IO, the IO will be broken into chunks
+ // of PAYLOAD_SIZE.
+ size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_SIZE;
+ bufsink_.Initialize(buf_size);
+
+ while (true) {
+ struct dm_user_header* header = bufsink_.GetHeaderPtr();
+
+ bufsink_.Clear();
+
+ ret = ReadDmUserHeader();
+ if (ret < 0) return ret;
+
+ LOG(DEBUG) << "dm-user returned " << ret << " bytes";
+
+ LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
+ LOG(DEBUG) << "msg->type: " << std::hex << header->type;
+ LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
+ LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
+ LOG(DEBUG) << "msg->len: " << std::hex << header->len;
+
+ switch (header->type) {
case DM_USER_MAP_READ: {
- LOG(DEBUG) << android::base::StringPrintf(
- "Responding to read of sector %lld with %lld bytes data", msg->sector,
- msg->len);
-
- if ((sizeof(*msg) + msg->len) > buf_size) {
- auto old_buf = std::move(buf);
- buf_size = sizeof(*msg) + msg->len;
- buf = std::make_unique<char>(buf_size);
- memcpy(buf.get(), old_buf.get(), sizeof(*msg));
- msg = (struct dm_user_message*)buf.get();
- }
-
- if (lseek(block_fd.get(), msg->sector * 512, SEEK_SET) < 0) {
- PLOG(ERROR) << "lseek failed: " << device;
- return 7;
- }
- if (!android::base::ReadFully(block_fd.get(), msg->buf, msg->len)) {
- PLOG(ERROR) << "read failed: " << device;
- return 7;
- }
-
- if (!android::base::WriteFully(ctrl_fd.get(), buf.get(), sizeof(*msg) + msg->len)) {
- PLOG(ERROR) << "write control failed";
- return 3;
- }
+ size_t remaining_size = header->len;
+ loff_t offset = 0;
+ header->io_in_progress = 0;
+ ret = 0;
+ do {
+ size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
+
+ // Request to sector 0 is always for kernel
+ // representation of COW header. This IO should be only
+ // once during dm-snapshot device creation. We should
+ // never see multiple IO requests. Additionally this IO
+ // will always be a single 4k.
+ if (header->sector == 0) {
+ // Read the metadata from internal COW device
+ // and build the in-memory data structures
+ // for all the operations in the internal COW.
+ if (!metadata_read_done_ && ReadMetadata()) {
+ LOG(ERROR) << "Metadata read failed";
+ return 1;
+ }
+ metadata_read_done_ = true;
+
+ CHECK(read_size == BLOCK_SIZE);
+ ret = ConstructKernelCowHeader();
+ if (ret < 0) return ret;
+ } else {
+ // Convert the sector number to a chunk ID.
+ //
+ // Check if the chunk ID represents a metadata
+ // page. If the chunk ID is not found in the
+ // vector, then it points to a metadata page.
+ chunk_t chunk = (header->sector >> CHUNK_SHIFT);
+
+ if (chunk >= chunk_vec_.size()) {
+ ret = ZerofillDiskExceptions(read_size);
+ if (ret < 0) {
+ LOG(ERROR) << "ZerofillDiskExceptions failed";
+ return ret;
+ }
+ } else if (chunk_vec_[chunk] == nullptr) {
+ ret = ReadDiskExceptions(chunk, read_size);
+ if (ret < 0) {
+ LOG(ERROR) << "ReadDiskExceptions failed";
+ return ret;
+ }
+ } else {
+ chunk_t num_chunks_read = (offset >> BLOCK_SHIFT);
+ ret = ReadData(chunk + num_chunks_read, read_size);
+ if (ret < 0) {
+ LOG(ERROR) << "ReadData failed";
+ return ret;
+ }
+ }
+ }
+
+ ssize_t written = WriteDmUserPayload(ret);
+ if (written < 0) return written;
+
+ remaining_size -= ret;
+ offset += ret;
+ if (remaining_size) {
+ LOG(DEBUG) << "Write done ret: " << ret
+ << " remaining size: " << remaining_size;
+ bufsink_.GetHeaderPtr()->io_in_progress = 1;
+ }
+ } while (remaining_size);
+
break;
}
- case DM_USER_MAP_WRITE:
+ case DM_USER_MAP_WRITE: {
+ // TODO: After merge operation is completed, kernel issues write
+ // to flush all the exception mappings where the merge is
+ // completed. If dm-user routes the WRITE IO, we need to clear
+ // in-memory data structures representing those exception
+ // mappings.
abort();
break;
+ }
}
LOG(DEBUG) << "read() finished, next message";
@@ -119,8 +752,33 @@ static int daemon_main(const std::string& device) {
return 0;
}
+} // namespace snapshot
+} // namespace android
+
+void run_thread(std::string cow_device, std::string backing_device) {
+ android::snapshot::Snapuserd snapd(cow_device, backing_device);
+ snapd.Run();
+}
+
int main([[maybe_unused]] int argc, char** argv) {
android::base::InitLogging(argv, &android::base::KernelLogger);
- daemon_main(argv[1]);
+
+ android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
+
+ while (daemon.IsRunning()) {
+ // TODO: This is hardcoded wherein:
+ // argv[1] = system_cow, argv[2] = /dev/block/mapper/system_a
+ // argv[3] = product_cow, argv[4] = /dev/block/mapper/product_a
+ //
+ // This should be fixed based on some kind of IPC or setup a
+ // command socket and spin up the thread based when a new
+ // partition is visible.
+ std::thread system_a(run_thread, argv[1], argv[2]);
+ std::thread product_a(run_thread, argv[3], argv[4]);
+
+ system_a.join();
+ product_a.join();
+ }
+
return 0;
}
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index de3d91246..b07bf9182 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -18,6 +18,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
@@ -241,5 +242,9 @@ uint64_t LowSpaceUserdata::bsize() const {
return bsize_;
}
+bool IsVirtualAbEnabled() {
+ return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index d56f7f25a..e9958887c 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -740,7 +740,7 @@ skip_administrative_mounts() {
grep -v \
-e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
-e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
- -e "^\(ramdumpfs\) " \
+ -e "^\(ramdumpfs\|binder\|/sys/kernel/debug\) " \
-e " functionfs " \
-e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
-e "^rootfs / rootfs rw," \
@@ -755,7 +755,7 @@ Filters out all apex and vendor override administrative overlay mounts
uninteresting to the test" ]
skip_unrelated_mounts() {
grep -v "^overlay.* /\(apex\|bionic\|system\|vendor\)/[^ ]" |
- grep -v "[%] /\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
+ grep -v "[%] /\(data_mirror\|apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
}
##
@@ -1301,9 +1301,9 @@ else
fi
check_ne "${BASE_SYSTEM_DEVT}" "${BASE_VENDOR_DEVT}" --warning system/vendor devt
[ -n "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
- die "system devt ${SYSTEM_DEVT} is major 0"
+ echo "${YELLOW}[ WARNING ]${NORMAL} system devt ${SYSTEM_DEVT} major 0" >&2
[ -n "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
- die "vendor devt ${SYSTEM_DEVT} is major 0"
+ echo "${YELLOW}[ WARNING ]${NORMAL} vendor devt ${VENDOR_DEVT} major 0" >&2
# Download libc.so, append some gargage, push back, and check if the file
# is updated.
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 7a3d9a930..9edcda705 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -38,6 +38,7 @@
#include <vector>
using namespace std::literals::string_literals;
+using namespace std::chrono_literals;
using namespace android::dm;
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
@@ -49,6 +50,7 @@ static int Usage(void) {
std::cerr << " delete <dm-name>" << std::endl;
std::cerr << " list <devices | targets> [-v]" << std::endl;
std::cerr << " getpath <dm-name>" << std::endl;
+ std::cerr << " getuuid <dm-name>" << std::endl;
std::cerr << " info <dm-name>" << std::endl;
std::cerr << " status <dm-name>" << std::endl;
std::cerr << " resume <dm-name>" << std::endl;
@@ -241,8 +243,9 @@ static int DmCreateCmdHandler(int argc, char** argv) {
return ret;
}
+ std::string ignore_path;
DeviceMapper& dm = DeviceMapper::Instance();
- if (!dm.CreateDevice(name, table)) {
+ if (!dm.CreateDevice(name, table, &ignore_path, 5s)) {
std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
return -EIO;
}
@@ -391,6 +394,22 @@ static int GetPathCmdHandler(int argc, char** argv) {
return 0;
}
+static int GetUuidCmdHandler(int argc, char** argv) {
+ if (argc != 1) {
+ std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
+ return -EINVAL;
+ }
+
+ DeviceMapper& dm = DeviceMapper::Instance();
+ std::string uuid;
+ if (!dm.GetDmDeviceUuidByName(argv[0], &uuid)) {
+ std::cerr << "Could not query uuid of device \"" << argv[0] << "\"." << std::endl;
+ return -EINVAL;
+ }
+ std::cout << uuid << std::endl;
+ return 0;
+}
+
static int InfoCmdHandler(int argc, char** argv) {
if (argc != 1) {
std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
@@ -504,6 +523,7 @@ static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
{"list", DmListCmdHandler},
{"help", HelpCmdHandler},
{"getpath", GetPathCmdHandler},
+ {"getuuid", GetUuidCmdHandler},
{"info", InfoCmdHandler},
{"table", TableCmdHandler},
{"status", StatusCmdHandler},
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 14d46b3d3..b3de9c4a8 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -240,3 +240,47 @@ cc_test {
defaults: ["charger_defaults"],
srcs: ["charger_test.cpp"],
}
+
+cc_test {
+ name: "libhealthd_charger_test",
+ defaults: ["charger_defaults"],
+ srcs: [
+ "AnimationParser_test.cpp",
+ "healthd_mode_charger_test.cpp"
+ ],
+ static_libs: [
+ "libgmock",
+ ],
+ test_suites: [
+ "general-tests",
+ "device-tests",
+ ],
+ data: [
+ ":libhealthd_charger_test_data",
+ ],
+ require_root: true,
+}
+
+// /system/etc/res/images/charger/battery_fail.png
+prebuilt_etc {
+ name: "system_core_charger_res_images_battery_fail.png",
+ src: "images/battery_fail.png",
+ relative_install_path: "res/images/charger",
+ filename: "battery_fail.png",
+}
+
+// /system/etc/res/images/charger/battery_scale.png
+prebuilt_etc {
+ name: "system_core_charger_res_images_battery_scale.png",
+ src: "images/battery_scale.png",
+ relative_install_path: "res/images/charger",
+ filename: "battery_scale.png",
+}
+
+phony {
+ name: "charger_res_images",
+ required: [
+ "system_core_charger_res_images_battery_fail.png",
+ "system_core_charger_res_images_battery_scale.png",
+ ],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
deleted file mode 100644
index 4b09cf826..000000000
--- a/healthd/Android.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-
-LOCAL_PATH := $(call my-dir)
-
-ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
-LOCAL_CHARGER_NO_UI := true
-endif
-
-### charger_res_images ###
-ifneq ($(strip $(LOCAL_CHARGER_NO_UI)),true)
-define _add-charger-image
-include $$(CLEAR_VARS)
-LOCAL_MODULE := system_core_charger_res_images_$(notdir $(1))
-LOCAL_MODULE_STEM := $(notdir $(1))
-_img_modules += $$(LOCAL_MODULE)
-LOCAL_SRC_FILES := $1
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $$(TARGET_ROOT_OUT)/res/images/charger
-include $$(BUILD_PREBUILT)
-endef
-
-_img_modules :=
-_images :=
-$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \
- $(eval $(call _add-charger-image,$(_img))))
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := charger_res_images
-LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := $(_img_modules)
-include $(BUILD_PHONY_PACKAGE)
-
-_add-charger-image :=
-_img_modules :=
-endif # LOCAL_CHARGER_NO_UI
diff --git a/healthd/AnimationParser.cpp b/healthd/AnimationParser.cpp
index fde3b95e0..6b08570d9 100644
--- a/healthd/AnimationParser.cpp
+++ b/healthd/AnimationParser.cpp
@@ -37,8 +37,8 @@ bool can_ignore_line(const char* str) {
return true;
}
-bool remove_prefix(const std::string& line, const char* prefix, const char** rest) {
- const char* str = line.c_str();
+bool remove_prefix(std::string_view line, const char* prefix, const char** rest) {
+ const char* str = line.data();
int start;
char c;
diff --git a/healthd/AnimationParser.h b/healthd/AnimationParser.h
index bc0084518..f55b5635e 100644
--- a/healthd/AnimationParser.h
+++ b/healthd/AnimationParser.h
@@ -17,6 +17,8 @@
#ifndef HEALTHD_ANIMATION_PARSER_H
#define HEALTHD_ANIMATION_PARSER_H
+#include <string_view>
+
#include "animation.h"
namespace android {
@@ -24,7 +26,7 @@ namespace android {
bool parse_animation_desc(const std::string& content, animation* anim);
bool can_ignore_line(const char* str);
-bool remove_prefix(const std::string& str, const char* prefix, const char** rest);
+bool remove_prefix(std::string_view str, const char* prefix, const char** rest);
bool parse_text_field(const char* in, animation::text_field* field);
} // namespace android
diff --git a/healthd/tests/AnimationParser_test.cpp b/healthd/AnimationParser_test.cpp
index 2fc3185e1..2fc3185e1 100644
--- a/healthd/tests/AnimationParser_test.cpp
+++ b/healthd/AnimationParser_test.cpp
diff --git a/healthd/TEST_MAPPING b/healthd/TEST_MAPPING
new file mode 100644
index 000000000..5893d10c7
--- /dev/null
+++ b/healthd/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libhealthd_charger_test"
+ }
+ ]
+}
diff --git a/healthd/animation.h b/healthd/animation.h
index d02d7a7ea..c2d5f1c04 100644
--- a/healthd/animation.h
+++ b/healthd/animation.h
@@ -18,6 +18,7 @@
#define HEALTHD_ANIMATION_H
#include <inttypes.h>
+
#include <string>
class GRSurface;
@@ -52,20 +53,11 @@ struct animation {
// - When treating paths as relative paths, it adds ".png" suffix.
// - When treating paths as absolute paths, it doesn't add the suffix. Hence, the suffix
// is added here.
- void set_resource_root(const std::string& root) {
- if (!animation_file.empty()) {
- animation_file = root + animation_file + ".png";
- }
- if (!fail_file.empty()) {
- fail_file = root + fail_file + ".png";
- }
- if (!text_clock.font_file.empty()) {
- text_clock.font_file = root + text_clock.font_file + ".png";
- }
- if (!text_percent.font_file.empty()) {
- text_percent.font_file = root + text_percent.font_file + ".png";
- }
- }
+ // If |backup_root| is provided, additionally check if file under |root| is accessbile or not.
+ // If not accessbile, use |backup_root| instead.
+ // Require that |root| starts and ends with "/". If |backup_root| is provided, require that
+ // |backup_root| starts and ends with "/".
+ void set_resource_root(const std::string& root, const std::string& backup_root = "");
std::string animation_file;
std::string fail_file;
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 2ae956c83..47cdea345 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -33,7 +33,9 @@
#include <optional>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <android-base/strings.h>
#include <linux/netlink.h>
#include <sys/socket.h>
@@ -58,6 +60,7 @@
#include <health2impl/Health.h>
#include <healthd/healthd.h>
+using std::string_literals::operator""s;
using namespace android;
using android::hardware::Return;
using android::hardware::health::GetHealthServiceOrDefault;
@@ -103,7 +106,13 @@ char* locale;
namespace android {
-// Resources in /product/etc/res overrides resources in /res.
+// Legacy animation resources are loaded from this directory.
+static constexpr const char* legacy_animation_root = "/res/images/";
+
+// Built-in animation resources are loaded from this directory.
+static constexpr const char* system_animation_root = "/system/etc/res/images/";
+
+// Resources in /product/etc/res overrides resources in /res and /system/etc/res.
// If the device is using the Generic System Image (GSI), resources may exist in
// both paths.
static constexpr const char* product_animation_desc_path =
@@ -628,6 +637,12 @@ void Charger::InitAnimation() {
batt_anim_.set_resource_root(product_animation_root);
} else if (base::ReadFileToString(animation_desc_path, &content)) {
parse_success = parse_animation_desc(content, &batt_anim_);
+ // Fallback resources always exist in system_animation_root. On legacy devices with an old
+ // ramdisk image, resources may be overridden under root. For example,
+ // /res/images/charger/battery_fail.png may not be the same as
+ // system/core/healthd/images/battery_fail.png in the source tree, but is a device-specific
+ // image. Hence, load from /res, and fall back to /system/etc/res.
+ batt_anim_.set_resource_root(legacy_animation_root, system_animation_root);
} else {
LOGW("Could not open animation description at %s\n", animation_desc_path);
parse_success = false;
@@ -636,13 +651,13 @@ void Charger::InitAnimation() {
if (!parse_success) {
LOGW("Could not parse animation description. Using default animation.\n");
batt_anim_ = BASE_ANIMATION;
- batt_anim_.animation_file.assign("charger/battery_scale");
+ batt_anim_.animation_file.assign(system_animation_root + "charger/battery_scale.png"s);
InitDefaultAnimationFrames();
batt_anim_.frames = owned_frames_.data();
batt_anim_.num_frames = owned_frames_.size();
}
if (batt_anim_.fail_file.empty()) {
- batt_anim_.fail_file.assign("charger/battery_fail");
+ batt_anim_.fail_file.assign(system_animation_root + "charger/battery_fail.png"s);
}
LOGV("Animation Description:\n");
@@ -681,10 +696,11 @@ void Charger::Init(struct healthd_config* config) {
InitAnimation();
- ret = res_create_display_surface(batt_anim_.fail_file.c_str(), &surf_unknown_);
+ ret = CreateDisplaySurface(batt_anim_.fail_file, &surf_unknown_);
if (ret < 0) {
LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);
- ret = res_create_display_surface("charger/battery_fail", &surf_unknown_);
+ ret = CreateDisplaySurface((system_animation_root + "charger/battery_fail.png"s).c_str(),
+ &surf_unknown_);
if (ret < 0) {
LOGE("Cannot load built in battery_fail image\n");
surf_unknown_ = NULL;
@@ -695,8 +711,8 @@ void Charger::Init(struct healthd_config* config) {
int scale_count;
int scale_fps; // Not in use (charger/battery_scale doesn't have FPS text
// chunk). We are using hard-coded frame.disp_time instead.
- ret = res_create_multi_display_surface(batt_anim_.animation_file.c_str(), &scale_count,
- &scale_fps, &scale_frames);
+ ret = CreateMultiDisplaySurface(batt_anim_.animation_file, &scale_count, &scale_fps,
+ &scale_frames);
if (ret < 0) {
LOGE("Cannot load battery_scale image\n");
batt_anim_.num_frames = 0;
@@ -725,6 +741,43 @@ void Charger::Init(struct healthd_config* config) {
boot_min_cap_ = config->boot_min_cap;
}
+int Charger::CreateDisplaySurface(const std::string& name, GRSurface** surface) {
+ return res_create_display_surface(name.c_str(), surface);
+}
+
+int Charger::CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,
+ GRSurface*** surface) {
+ return res_create_multi_display_surface(name.c_str(), frames, fps, surface);
+}
+
+void set_resource_root_for(const std::string& root, const std::string& backup_root,
+ std::string* value) {
+ if (value->empty()) {
+ return;
+ }
+
+ std::string new_value = root + *value + ".png";
+ // If |backup_root| is provided, additionally check whether the file under |root| is
+ // accessible or not. If not accessible, fallback to file under |backup_root|.
+ if (!backup_root.empty() && access(new_value.data(), F_OK) == -1) {
+ new_value = backup_root + *value + ".png";
+ }
+
+ *value = new_value;
+}
+
+void animation::set_resource_root(const std::string& root, const std::string& backup_root) {
+ CHECK(android::base::StartsWith(root, "/") && android::base::EndsWith(root, "/"))
+ << "animation root " << root << " must start and end with /";
+ CHECK(backup_root.empty() || (android::base::StartsWith(backup_root, "/") &&
+ android::base::EndsWith(backup_root, "/")))
+ << "animation backup root " << backup_root << " must start and end with /";
+ set_resource_root_for(root, backup_root, &animation_file);
+ set_resource_root_for(root, backup_root, &fail_file);
+ set_resource_root_for(root, backup_root, &text_clock.font_file);
+ set_resource_root_for(root, backup_root, &text_percent.font_file);
+}
+
} // namespace android
int healthd_charger_main(int argc, char** argv) {
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
index 6e569ee54..6f9ae8c0e 100644
--- a/healthd/healthd_mode_charger.h
+++ b/healthd/healthd_mode_charger.h
@@ -53,6 +53,11 @@ class Charger : public ::android::hardware::health::V2_1::implementation::HalHea
// HalHealthLoop overrides
void OnHealthInfoChanged(const HealthInfo_2_1& health_info) override;
+ // Allowed to be mocked for testing.
+ virtual int CreateDisplaySurface(const std::string& name, GRSurface** surface);
+ virtual int CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,
+ GRSurface*** surface);
+
private:
void InitDefaultAnimationFrames();
void UpdateScreenState(int64_t now);
diff --git a/healthd/healthd_mode_charger_test.cpp b/healthd/healthd_mode_charger_test.cpp
new file mode 100644
index 000000000..f444f66ad
--- /dev/null
+++ b/healthd/healthd_mode_charger_test.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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>
+
+#include <iostream>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <health/utils.h>
+
+#include "healthd_mode_charger.h"
+
+using android::hardware::Return;
+using android::hardware::health::InitHealthdConfig;
+using std::string_literals::operator""s;
+using testing::_;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::StrEq;
+using testing::Test;
+
+namespace android {
+
+// A replacement to ASSERT_* to be used in a forked process. When the condition is not met,
+// print a gtest message, then exit abnormally.
+class ChildAssertHelper : public std::stringstream {
+ public:
+ ChildAssertHelper(bool res, const char* expr, const char* file, int line) : res_(res) {
+ (*this) << file << ":" << line << ": `" << expr << "` evaluates to false\n";
+ }
+ ~ChildAssertHelper() {
+ EXPECT_TRUE(res_) << str();
+ if (!res_) exit(EX_SOFTWARE);
+ }
+
+ private:
+ bool res_;
+ DISALLOW_COPY_AND_ASSIGN(ChildAssertHelper);
+};
+#define CHILD_ASSERT_TRUE(expr) ChildAssertHelper(expr, #expr, __FILE__, __LINE__)
+
+// Run |test_body| in a chroot jail in a forked process. |subdir| is a sub-directory in testdata.
+// Within |test_body|,
+// - non-fatal errors may be reported using EXPECT_* macro as usual.
+// - fatal errors must be reported using CHILD_ASSERT_TRUE macro. ASSERT_* must not be used.
+void ForkTest(const std::string& subdir, const std::function<void(void)>& test_body) {
+ pid_t pid = fork();
+ ASSERT_GE(pid, 0) << "Fork fails: " << strerror(errno);
+ if (pid == 0) {
+ // child
+ CHILD_ASSERT_TRUE(
+ chroot((android::base::GetExecutableDirectory() + "/" + subdir).c_str()) != -1)
+ << "Failed to chroot to " << subdir << ": " << strerror(errno);
+ test_body();
+ // EXPECT_* macros may set the HasFailure bit without calling exit(). Set exit status
+ // accordingly.
+ exit(::testing::Test::HasFailure() ? EX_SOFTWARE : EX_OK);
+ }
+ // parent
+ int status;
+ ASSERT_NE(-1, waitpid(pid, &status, 0)) << "waitpid() fails: " << strerror(errno);
+ ASSERT_TRUE(WIFEXITED(status)) << "Test fails, waitpid() returns " << status;
+ ASSERT_EQ(EX_OK, WEXITSTATUS(status)) << "Test fails, child process returns " << status;
+}
+
+class MockHealth : public android::hardware::health::V2_1::IHealth {
+ MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, registerCallback,
+ (const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));
+ MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, unregisterCallback,
+ (const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));
+ MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, update, ());
+ MOCK_METHOD(Return<void>, getChargeCounter, (getChargeCounter_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getCurrentNow, (getCurrentNow_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getCurrentAverage, (getCurrentAverage_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getCapacity, (getCapacity_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getEnergyCounter, (getEnergyCounter_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getChargeStatus, (getChargeStatus_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getStorageInfo, (getStorageInfo_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getDiskStats, (getDiskStats_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getHealthInfo, (getHealthInfo_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getHealthConfig, (getHealthConfig_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, getHealthInfo_2_1, (getHealthInfo_2_1_cb _hidl_cb));
+ MOCK_METHOD(Return<void>, shouldKeepScreenOn, (shouldKeepScreenOn_cb _hidl_cb));
+};
+
+class TestCharger : public Charger {
+ public:
+ // Inherit constructor.
+ using Charger::Charger;
+ // Expose protected functions to be used in tests.
+ void Init(struct healthd_config* config) override { Charger::Init(config); }
+ MOCK_METHOD(int, CreateDisplaySurface, (const std::string& name, GRSurface** surface));
+ MOCK_METHOD(int, CreateMultiDisplaySurface,
+ (const std::string& name, int* frames, int* fps, GRSurface*** surface));
+};
+
+// Intentionally leak TestCharger instance to avoid calling ~HealthLoop() because ~HealthLoop()
+// should never be called. But still verify expected calls upon destruction.
+class VerifiedTestCharger {
+ public:
+ VerifiedTestCharger(TestCharger* charger) : charger_(charger) {
+ testing::Mock::AllowLeak(charger_);
+ }
+ TestCharger& operator*() { return *charger_; }
+ TestCharger* operator->() { return charger_; }
+ ~VerifiedTestCharger() { testing::Mock::VerifyAndClearExpectations(charger_); }
+
+ private:
+ TestCharger* charger_;
+};
+
+// Do not use SetUp and TearDown of a test suite, as they will be invoked in the parent process, not
+// the child process. In particular, if the test suite contains mocks, they will not be verified in
+// the child process. Instead, create mocks within closures in each tests.
+void ExpectChargerResAt(const std::string& root) {
+ sp<NiceMock<MockHealth>> health(new NiceMock<MockHealth>());
+ VerifiedTestCharger charger(new NiceMock<TestCharger>(health));
+
+ // Only one frame in all testdata/**/animation.txt
+ GRSurface* multi[] = {nullptr};
+
+ EXPECT_CALL(*charger, CreateDisplaySurface(StrEq(root + "charger/battery_fail.png"), _))
+ .WillRepeatedly(Invoke([](const auto&, GRSurface** surface) {
+ *surface = nullptr;
+ return 0;
+ }));
+ EXPECT_CALL(*charger,
+ CreateMultiDisplaySurface(StrEq(root + "charger/battery_scale.png"), _, _, _))
+ .WillRepeatedly(Invoke([&](const auto&, int* frames, int* fps, GRSurface*** surface) {
+ *frames = arraysize(multi);
+ *fps = 60; // Unused fps value
+ *surface = multi;
+ return 0;
+ }));
+ struct healthd_config healthd_config;
+ InitHealthdConfig(&healthd_config);
+ charger->Init(&healthd_config);
+};
+
+// Test that if resources does not exist in /res or in /product/etc/res, load from /system.
+TEST(ChargerLoadAnimationRes, Empty) {
+ ForkTest("empty", std::bind(&ExpectChargerResAt, "/system/etc/res/images/"));
+}
+
+// Test loading everything from /res
+TEST(ChargerLoadAnimationRes, Legacy) {
+ ForkTest("legacy", std::bind(&ExpectChargerResAt, "/res/images/"));
+}
+
+// Test loading animation text from /res but images from /system if images does not exist under
+// /res.
+TEST(ChargerLoadAnimationRes, LegacyTextSystemImages) {
+ ForkTest("legacy_text_system_images",
+ std::bind(&ExpectChargerResAt, "/system/etc/res/images/"));
+}
+
+// Test loading everything from /product
+TEST(ChargerLoadAnimationRes, Product) {
+ ForkTest("product", std::bind(&ExpectChargerResAt, "/product/etc/res/images/"));
+}
+
+} // namespace android
diff --git a/healthd/testdata/Android.bp b/healthd/testdata/Android.bp
new file mode 100644
index 000000000..110c79a87
--- /dev/null
+++ b/healthd/testdata/Android.bp
@@ -0,0 +1,20 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+filegroup {
+ name: "libhealthd_charger_test_data",
+ srcs: ["**/*.*"],
+}
diff --git a/healthd/testdata/empty/ensure_directory_creation.txt b/healthd/testdata/empty/ensure_directory_creation.txt
new file mode 100644
index 000000000..36ceff465
--- /dev/null
+++ b/healthd/testdata/empty/ensure_directory_creation.txt
@@ -0,0 +1 @@
+File is placed to ensure directory is created on the device.
diff --git a/healthd/testdata/legacy/res/images/charger/battery_fail.png b/healthd/testdata/legacy/res/images/charger/battery_fail.png
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/healthd/testdata/legacy/res/images/charger/battery_fail.png
diff --git a/healthd/testdata/legacy/res/images/charger/battery_scale.png b/healthd/testdata/legacy/res/images/charger/battery_scale.png
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/healthd/testdata/legacy/res/images/charger/battery_scale.png
diff --git a/healthd/testdata/legacy/res/values/charger/animation.txt b/healthd/testdata/legacy/res/values/charger/animation.txt
new file mode 100644
index 000000000..075333676
--- /dev/null
+++ b/healthd/testdata/legacy/res/values/charger/animation.txt
@@ -0,0 +1,9 @@
+# Sample Animation file for testing.
+
+# animation: num_cycles, first_frame_repeats, animation_file
+animation: 2 1 charger/battery_scale
+
+fail: charger/battery_fail
+
+# frame: disp_time min_level max_level
+frame: 15 0 100
diff --git a/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt b/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt
new file mode 100644
index 000000000..075333676
--- /dev/null
+++ b/healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt
@@ -0,0 +1,9 @@
+# Sample Animation file for testing.
+
+# animation: num_cycles, first_frame_repeats, animation_file
+animation: 2 1 charger/battery_scale
+
+fail: charger/battery_fail
+
+# frame: disp_time min_level max_level
+frame: 15 0 100
diff --git a/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png b/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/healthd/testdata/product/product/etc/res/images/charger/battery_fail.png
diff --git a/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png b/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/healthd/testdata/product/product/etc/res/images/charger/battery_scale.png
diff --git a/healthd/testdata/product/product/etc/res/values/charger/animation.txt b/healthd/testdata/product/product/etc/res/values/charger/animation.txt
new file mode 100644
index 000000000..075333676
--- /dev/null
+++ b/healthd/testdata/product/product/etc/res/values/charger/animation.txt
@@ -0,0 +1,9 @@
+# Sample Animation file for testing.
+
+# animation: num_cycles, first_frame_repeats, animation_file
+animation: 2 1 charger/battery_scale
+
+fail: charger/battery_fail
+
+# frame: disp_time min_level max_level
+frame: 15 0 100
diff --git a/healthd/tests/Android.mk b/healthd/tests/Android.mk
deleted file mode 100644
index 87e8862d0..000000000
--- a/healthd/tests/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2016 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- AnimationParser_test.cpp \
-
-LOCAL_MODULE := healthd_test
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_LIBRARIES := \
- libhealthd_internal \
-
-LOCAL_SHARED_LIBRARIES := \
- liblog \
- libbase \
- libcutils \
-
-include $(BUILD_NATIVE_TEST)
diff --git a/include/private/android_filesystem_capability.h b/include/private/android_filesystem_capability.h
deleted file mode 120000
index f310b35f5..000000000
--- a/include/private/android_filesystem_capability.h
+++ /dev/null
@@ -1 +0,0 @@
-../../libcutils/include/private/android_filesystem_capability.h \ No newline at end of file
diff --git a/include/private/android_logger.h b/include/private/android_logger.h
deleted file mode 120000
index f187a6d22..000000000
--- a/include/private/android_logger.h
+++ /dev/null
@@ -1 +0,0 @@
-../../liblog/include/private/android_logger.h \ No newline at end of file
diff --git a/include/private/canned_fs_config.h b/include/private/canned_fs_config.h
deleted file mode 120000
index 8f92b2d6a..000000000
--- a/include/private/canned_fs_config.h
+++ /dev/null
@@ -1 +0,0 @@
-../../libcutils/include/private/canned_fs_config.h \ No newline at end of file
diff --git a/include/private/fs_config.h b/include/private/fs_config.h
deleted file mode 100644
index e9868a41f..000000000
--- a/include/private/fs_config.h
+++ /dev/null
@@ -1,4 +0,0 @@
-// TODO(b/63135587) remove this file after the transitive dependency
-// from private/android_filesystem_config.h is resolved. All files that use
-// libcutils/include/private/fs_config.h should include the file directly, not
-// indirectly via private/android_filesystem_config.h.
diff --git a/include/sysutils b/include/sysutils
deleted file mode 120000
index 1c8e85bd1..000000000
--- a/include/sysutils
+++ /dev/null
@@ -1 +0,0 @@
-../libsysutils/include/sysutils/ \ No newline at end of file
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
index 17f509ae5..6f22ab792 100644
--- a/init/AndroidTest.xml
+++ b/init/AndroidTest.xml
@@ -32,4 +32,7 @@
<option name="module-name" value="CtsInitTestCases" />
<option name="runtime-hint" value="65s" />
</test>
+ <!-- Controller that will skip the module if a native bridge situation is detected -->
+ <!-- For example: module wants to run arm32 and device is x86 -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" />
</configuration>
diff --git a/init/builtins.cpp b/init/builtins.cpp
index f85246d5c..d58dd6076 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -570,7 +570,6 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "block");
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
return {};
} else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
@@ -602,7 +601,6 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "file");
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
@@ -613,7 +611,6 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "file");
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
@@ -624,7 +621,6 @@ static Result<void> queue_fs_event(int code, bool userdata_remount) {
return Error() << "FscryptInstallKeyring() failed";
}
SetProperty("ro.crypto.state", "encrypted");
- SetProperty("ro.crypto.type", "file");
// Although encrypted, vold has already set the device up, so we do not need to
// do anything different from the nonencrypted case.
@@ -673,18 +669,26 @@ static Result<void> do_mount_all(const BuiltinArguments& args) {
}
}
- auto mount_fstab_return_code = fs_mgr_mount_all(&fstab, mount_all->mode);
+ auto mount_fstab_result = fs_mgr_mount_all(&fstab, mount_all->mode);
SetProperty(prop_name, std::to_string(t.duration().count()));
if (mount_all->import_rc) {
import_late(mount_all->rc_paths);
}
+ if (mount_fstab_result.userdata_mounted) {
+ // This call to fs_mgr_mount_all mounted userdata. Keep the result in
+ // order for userspace reboot to correctly remount userdata.
+ LOG(INFO) << "Userdata mounted using "
+ << (mount_all->fstab_path.empty() ? "(default fstab)" : mount_all->fstab_path)
+ << " result : " << mount_fstab_result.code;
+ initial_mount_fstab_return_code = mount_fstab_result.code;
+ }
+
if (queue_event) {
/* queue_fs_event will queue event based on mount_fstab return code
* and return processed return code*/
- initial_mount_fstab_return_code = mount_fstab_return_code;
- auto queue_fs_result = queue_fs_event(mount_fstab_return_code, false);
+ auto queue_fs_result = queue_fs_event(mount_fstab_result.code, false);
if (!queue_fs_result.ok()) {
return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
}
@@ -1199,6 +1203,10 @@ static Result<void> do_remount_userdata(const BuiltinArguments& args) {
}
// TODO(b/135984674): check that fstab contains /data.
if (auto rc = fs_mgr_remount_userdata_into_checkpointing(&fstab); rc < 0) {
+ std::string proc_mounts_output;
+ android::base::ReadFileToString("/proc/mounts", &proc_mounts_output, true);
+ android::base::WriteStringToFile(proc_mounts_output,
+ "/metadata/userspacereboot/mount_info.txt");
trigger_shutdown("reboot,mount_userdata_failed");
}
if (auto result = queue_fs_event(initial_mount_fstab_return_code, true); !result.ok()) {
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index a91cd1dd1..0b9f16107 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -28,47 +28,55 @@ namespace android {
namespace init {
static const std::map<std::string, int> cap_map = {
- CAP_MAP_ENTRY(CHOWN),
- CAP_MAP_ENTRY(DAC_OVERRIDE),
- CAP_MAP_ENTRY(DAC_READ_SEARCH),
- CAP_MAP_ENTRY(FOWNER),
- CAP_MAP_ENTRY(FSETID),
- CAP_MAP_ENTRY(KILL),
- CAP_MAP_ENTRY(SETGID),
- CAP_MAP_ENTRY(SETUID),
- CAP_MAP_ENTRY(SETPCAP),
- CAP_MAP_ENTRY(LINUX_IMMUTABLE),
- CAP_MAP_ENTRY(NET_BIND_SERVICE),
- CAP_MAP_ENTRY(NET_BROADCAST),
- CAP_MAP_ENTRY(NET_ADMIN),
- CAP_MAP_ENTRY(NET_RAW),
- CAP_MAP_ENTRY(IPC_LOCK),
- CAP_MAP_ENTRY(IPC_OWNER),
- CAP_MAP_ENTRY(SYS_MODULE),
- CAP_MAP_ENTRY(SYS_RAWIO),
- CAP_MAP_ENTRY(SYS_CHROOT),
- CAP_MAP_ENTRY(SYS_PTRACE),
- CAP_MAP_ENTRY(SYS_PACCT),
- CAP_MAP_ENTRY(SYS_ADMIN),
- CAP_MAP_ENTRY(SYS_BOOT),
- CAP_MAP_ENTRY(SYS_NICE),
- CAP_MAP_ENTRY(SYS_RESOURCE),
- CAP_MAP_ENTRY(SYS_TIME),
- CAP_MAP_ENTRY(SYS_TTY_CONFIG),
- CAP_MAP_ENTRY(MKNOD),
- CAP_MAP_ENTRY(LEASE),
- CAP_MAP_ENTRY(AUDIT_WRITE),
- CAP_MAP_ENTRY(AUDIT_CONTROL),
- CAP_MAP_ENTRY(SETFCAP),
- CAP_MAP_ENTRY(MAC_OVERRIDE),
- CAP_MAP_ENTRY(MAC_ADMIN),
- CAP_MAP_ENTRY(SYSLOG),
- CAP_MAP_ENTRY(WAKE_ALARM),
- CAP_MAP_ENTRY(BLOCK_SUSPEND),
- CAP_MAP_ENTRY(AUDIT_READ),
+ CAP_MAP_ENTRY(CHOWN),
+ CAP_MAP_ENTRY(DAC_OVERRIDE),
+ CAP_MAP_ENTRY(DAC_READ_SEARCH),
+ CAP_MAP_ENTRY(FOWNER),
+ CAP_MAP_ENTRY(FSETID),
+ CAP_MAP_ENTRY(KILL),
+ CAP_MAP_ENTRY(SETGID),
+ CAP_MAP_ENTRY(SETUID),
+ CAP_MAP_ENTRY(SETPCAP),
+ CAP_MAP_ENTRY(LINUX_IMMUTABLE),
+ CAP_MAP_ENTRY(NET_BIND_SERVICE),
+ CAP_MAP_ENTRY(NET_BROADCAST),
+ CAP_MAP_ENTRY(NET_ADMIN),
+ CAP_MAP_ENTRY(NET_RAW),
+ CAP_MAP_ENTRY(IPC_LOCK),
+ CAP_MAP_ENTRY(IPC_OWNER),
+ CAP_MAP_ENTRY(SYS_MODULE),
+ CAP_MAP_ENTRY(SYS_RAWIO),
+ CAP_MAP_ENTRY(SYS_CHROOT),
+ CAP_MAP_ENTRY(SYS_PTRACE),
+ CAP_MAP_ENTRY(SYS_PACCT),
+ CAP_MAP_ENTRY(SYS_ADMIN),
+ CAP_MAP_ENTRY(SYS_BOOT),
+ CAP_MAP_ENTRY(SYS_NICE),
+ CAP_MAP_ENTRY(SYS_RESOURCE),
+ CAP_MAP_ENTRY(SYS_TIME),
+ CAP_MAP_ENTRY(SYS_TTY_CONFIG),
+ CAP_MAP_ENTRY(MKNOD),
+ CAP_MAP_ENTRY(LEASE),
+ CAP_MAP_ENTRY(AUDIT_WRITE),
+ CAP_MAP_ENTRY(AUDIT_CONTROL),
+ CAP_MAP_ENTRY(SETFCAP),
+ CAP_MAP_ENTRY(MAC_OVERRIDE),
+ CAP_MAP_ENTRY(MAC_ADMIN),
+ CAP_MAP_ENTRY(SYSLOG),
+ CAP_MAP_ENTRY(WAKE_ALARM),
+ CAP_MAP_ENTRY(BLOCK_SUSPEND),
+ CAP_MAP_ENTRY(AUDIT_READ),
+#if defined(__BIONIC__)
+ CAP_MAP_ENTRY(PERFMON),
+ CAP_MAP_ENTRY(BPF),
+#endif
};
+#if defined(__BIONIC__)
+static_assert(CAP_LAST_CAP == CAP_BPF, "CAP_LAST_CAP is not CAP_BPF");
+#else
static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
+#endif
static bool ComputeCapAmbientSupported() {
#if defined(__ANDROID__)
diff --git a/init/init.cpp b/init/init.cpp
index 08898d1e4..73bf95f04 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -325,14 +325,14 @@ static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_
// late_import is available only in Q and earlier release. As we don't
// have system_ext in those versions, skip late_import for system_ext.
parser.ParseConfig("/system_ext/etc/init");
- if (!parser.ParseConfig("/product/etc/init")) {
- late_import_paths.emplace_back("/product/etc/init");
+ if (!parser.ParseConfig("/vendor/etc/init")) {
+ late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
- if (!parser.ParseConfig("/vendor/etc/init")) {
- late_import_paths.emplace_back("/vendor/etc/init");
+ if (!parser.ParseConfig("/product/etc/init")) {
+ late_import_paths.emplace_back("/product/etc/init");
}
} else {
parser.ParseConfig(bootscript);
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index 01abba8d1..46f833104 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -130,7 +130,11 @@ void MountHandler::MountHandlerFunction() {
char* buf = nullptr;
size_t len = 0;
while (getline(&buf, &len, fp_.get()) != -1) {
- auto entry = ParseMount(std::string(buf));
+ auto buf_string = std::string(buf);
+ if (buf_string.find("/emulated") != std::string::npos) {
+ continue;
+ }
+ auto entry = ParseMount(buf_string);
auto match = untouched.find(entry);
if (match == untouched.end()) {
touched.emplace_back(std::move(entry));
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index f8359bc64..59cc140e4 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -44,50 +44,17 @@ namespace android {
namespace init {
namespace {
-static bool BindMount(const std::string& source, const std::string& mount_point,
- bool recursive = false) {
- unsigned long mountflags = MS_BIND;
- if (recursive) {
- mountflags |= MS_REC;
- }
- if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
+static bool BindMount(const std::string& source, const std::string& mount_point) {
+ if (mount(source.c_str(), mount_point.c_str(), nullptr, MS_BIND | MS_REC, nullptr) == -1) {
PLOG(ERROR) << "Failed to bind mount " << source;
return false;
}
return true;
}
-static bool MakeShared(const std::string& mount_point, bool recursive = false) {
- unsigned long mountflags = MS_SHARED;
- if (recursive) {
- mountflags |= MS_REC;
- }
- if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Failed to change propagation type to shared";
- return false;
- }
- return true;
-}
-
-static bool MakeSlave(const std::string& mount_point, bool recursive = false) {
- unsigned long mountflags = MS_SLAVE;
- if (recursive) {
- mountflags |= MS_REC;
- }
- if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Failed to change propagation type to slave";
- return false;
- }
- return true;
-}
-
-static bool MakePrivate(const std::string& mount_point, bool recursive = false) {
- unsigned long mountflags = MS_PRIVATE;
- if (recursive) {
- mountflags |= MS_REC;
- }
+static bool ChangeMount(const std::string& mount_point, unsigned long mountflags) {
if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Failed to change propagation type to private";
+ PLOG(ERROR) << "Failed to remount " << mount_point << " as " << std::hex << mountflags;
return false;
}
return true;
@@ -225,17 +192,17 @@ bool SetupMountNamespaces() {
// needed for /foo/bar, then we will make /foo/bar as a mount point (by
// bind-mounting by to itself) and set the propagation type of the mount
// point to private.
- if (!MakeShared("/", true /*recursive*/)) return false;
+ if (!ChangeMount("/", MS_SHARED | MS_REC)) return false;
// /apex is a private mountpoint to give different sets of APEXes for
// the bootstrap and default mount namespaces. The processes running with
// the bootstrap namespace get APEXes from the read-only partition.
- if (!(MakePrivate("/apex"))) return false;
+ if (!(ChangeMount("/apex", MS_PRIVATE))) return false;
// /linkerconfig is a private mountpoint to give a different linker configuration
// based on the mount namespace. Subdirectory will be bind-mounted based on current mount
// namespace
- if (!(MakePrivate("/linkerconfig"))) return false;
+ if (!(ChangeMount("/linkerconfig", MS_PRIVATE))) return false;
// The two mount namespaces present challenges for scoped storage, because
// vold, which is responsible for most of the mounting, lives in the
@@ -266,15 +233,15 @@ bool SetupMountNamespaces() {
if (!mkdir_recursive("/mnt/user", 0755)) return false;
if (!mkdir_recursive("/mnt/installer", 0755)) return false;
if (!mkdir_recursive("/mnt/androidwritable", 0755)) return false;
- if (!(BindMount("/mnt/user", "/mnt/installer", true))) return false;
- if (!(BindMount("/mnt/user", "/mnt/androidwritable", true))) return false;
+ if (!(BindMount("/mnt/user", "/mnt/installer"))) return false;
+ if (!(BindMount("/mnt/user", "/mnt/androidwritable"))) return false;
// First, make /mnt/installer and /mnt/androidwritable a slave bind mount
- if (!(MakeSlave("/mnt/installer"))) return false;
- if (!(MakeSlave("/mnt/androidwritable"))) return false;
+ if (!(ChangeMount("/mnt/installer", MS_SLAVE))) return false;
+ if (!(ChangeMount("/mnt/androidwritable", MS_SLAVE))) return false;
// Then, make it shared again - effectively creating a new peer group, that
// will be inherited by new mount namespaces.
- if (!(MakeShared("/mnt/installer"))) return false;
- if (!(MakeShared("/mnt/androidwritable"))) return false;
+ if (!(ChangeMount("/mnt/installer", MS_SHARED))) return false;
+ if (!(ChangeMount("/mnt/androidwritable", MS_SHARED))) return false;
bootstrap_ns_fd.reset(OpenMountNamespace());
bootstrap_ns_id = GetMountNamespaceId();
diff --git a/init/perfboot.py b/init/perfboot.py
index 713290bcd..4b23ad28c 100755
--- a/init/perfboot.py
+++ b/init/perfboot.py
@@ -349,9 +349,9 @@ def print_summary(record_list, end_tag):
# 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)
+ print 'mean:', int(round(mean(end_times))), 'ms'
+ print 'median:', int(round(median(end_times))), 'ms'
+ print 'standard deviation:', int(round(stddev(end_times))), 'ms'
def do_iteration(device, interval_adjuster, event_tags_re, end_tag):
diff --git a/init/property_service.cpp b/init/property_service.cpp
index f0c9fb759..7a406ffd1 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1097,7 +1097,7 @@ void CreateSerializedPropertyInfo() {
&property_infos)) {
return;
}
- // Don't check for failure here, so we always have a sane list of properties.
+ // Don't check for failure here, since we don't always have all of these partitions.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index c5446717a..2065fcf16 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -815,11 +815,19 @@ static Result<void> DoUserspaceReboot() {
auto sigkill_timeout = GetMillisProperty("init.userspace_reboot.sigkill.timeoutmillis", 10s);
LOG(INFO) << "Timeout to terminate services: " << sigterm_timeout.count() << "ms "
<< "Timeout to kill services: " << sigkill_timeout.count() << "ms";
+ std::string services_file_name = "/metadata/userspacereboot/services.txt";
+ const int flags = O_RDWR | O_CREAT | O_SYNC | O_APPEND | O_CLOEXEC;
StopServicesAndLogViolations(stop_first, sigterm_timeout, true /* SIGTERM */);
if (int r = StopServicesAndLogViolations(stop_first, sigkill_timeout, false /* SIGKILL */);
r > 0) {
+ auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
+ android::base::WriteStringToFd("Post-data services still running: \n", fd);
+ for (const auto& s : stop_first) {
+ if (s->IsRunning()) {
+ android::base::WriteStringToFd(s->name() + "\n", fd);
+ }
+ }
sub_reason = "sigkill";
- // TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " post-data services are still running";
}
if (auto result = KillZramBackingDevice(); !result.ok()) {
@@ -833,8 +841,14 @@ static Result<void> DoUserspaceReboot() {
if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */),
sigkill_timeout, false /* SIGKILL */);
r > 0) {
+ auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
+ android::base::WriteStringToFd("Debugging services still running: \n", fd);
+ for (const auto& s : GetDebuggingServices(true)) {
+ if (s->IsRunning()) {
+ android::base::WriteStringToFd(s->name() + "\n", fd);
+ }
+ }
sub_reason = "sigkill_debug";
- // TODO(b/135984674): store information about offending services for debugging.
return Error() << r << " debugging services are still running";
}
{
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 05e632b68..f2383d7a6 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -60,13 +60,14 @@ Result<void> EnterNamespace(int nstype, const char* path) {
Result<void> SetUpMountNamespace(bool remount_proc, bool remount_sys) {
constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;
- // Recursively remount / as slave like zygote does so unmounting and mounting /proc
- // doesn't interfere with the parent namespace's /proc mount. This will also
- // prevent any other mounts/unmounts initiated by the service from interfering
- // with the parent namespace but will still allow mount events from the parent
+ // Recursively remount / as MS_SLAVE like zygote does so that
+ // unmounting and mounting /proc doesn't interfere with the parent
+ // namespace's /proc mount. This will also prevent any other
+ // mounts/unmounts initiated by the service from interfering with the
+ // parent namespace but will still allow mount events from the parent
// namespace to propagate to the child.
if (mount("rootfs", "/", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {
- return ErrnoError() << "Could not remount(/) recursively as slave";
+ return ErrnoError() << "Could not remount(/) recursively as MS_SLAVE";
}
// umount() then mount() /proc and/or /sys
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 9d4ea8cd3..dc2455e2f 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -30,6 +30,7 @@
#include "action.h"
#include "builtins.h"
+#include "mount_namespace.h"
#include "proto_utils.h"
#include "util.h"
@@ -217,7 +218,13 @@ void Subcontext::Fork() {
PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
}
}
-
+#if defined(__ANDROID__)
+ // subcontext init runs in "default" mount namespace
+ // so that it can access /apex/*
+ if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {
+ LOG(FATAL) << "Could not switch to \"default\" mount namespace: " << result.error();
+ }
+#endif
auto init_path = GetExecutablePath();
auto child_fd_string = std::to_string(child_fd);
const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(),
diff --git a/init/sysprop/api/com.android.sysprop.init-latest.txt b/init/sysprop/api/com.android.sysprop.init-latest.txt
index c835b95cf..01f4e9a91 100644
--- a/init/sysprop/api/com.android.sysprop.init-latest.txt
+++ b/init/sysprop/api/com.android.sysprop.init-latest.txt
@@ -1,8 +1,13 @@
props {
module: "android.sysprop.InitProperties"
prop {
+ api_name: "is_userspace_reboot_supported"
+ prop_name: "init.userspace_reboot.is_supported"
+ integer_as_bool: true
+ }
+ prop {
api_name: "userspace_reboot_in_progress"
- scope: Public
+ access: ReadWrite
prop_name: "sys.init.userspace_reboot.in_progress"
integer_as_bool: true
}
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
index 7e543f232..66a332892 100644
--- a/init/test_kill_services/init_kill_services_test.cpp
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -54,7 +54,7 @@ class InitKillServicesTest : public ::testing::TestWithParam<std::string> {};
TEST_P(InitKillServicesTest, KillCriticalProcesses) {
ExpectKillingServiceRecovers(GetParam());
- // sanity check init is still responding
+ // Ensure that init is still responding
EXPECT_TRUE(SetProperty("test.death.test", "asdf"));
EXPECT_EQ(GetProperty("test.death.test", ""), "asdf");
EXPECT_TRUE(SetProperty("test.death.test", ""));
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index f4191b908..cc32b6dc0 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -1584,7 +1584,7 @@ static void UnwindFromDevice(Backtrace* backtrace, void* device_map) {
// Verify the flag is set.
ASSERT_EQ(PROT_DEVICE_MAP, map.flags & PROT_DEVICE_MAP);
- // Quick sanity checks.
+ // Quick basic checks of functionality.
uint64_t offset;
ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset));
ASSERT_EQ(std::string(""), backtrace->GetFunctionName(device_map_uint, &offset, &map));
diff --git a/libcrypto_utils/.clang-format b/libcrypto_utils/.clang-format
new file mode 120000
index 000000000..fd0645fdf
--- /dev/null
+++ b/libcrypto_utils/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2 \ No newline at end of file
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index d7175e0be..923b29154 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -23,7 +23,7 @@ cc_library {
},
host_supported: true,
srcs: [
- "android_pubkey.c",
+ "android_pubkey.cpp",
],
cflags: [
"-Wall",
diff --git a/libcrypto_utils/android_pubkey.c b/libcrypto_utils/android_pubkey.cpp
index 3052e52bf..21e5663ee 100644
--- a/libcrypto_utils/android_pubkey.c
+++ b/libcrypto_utils/android_pubkey.cpp
@@ -35,37 +35,29 @@
// little-endian 32 bit words. Note that Android only supports little-endian
// processors, so we don't do any byte order conversions when parsing the binary
// struct.
-typedef struct RSAPublicKey {
- // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
- uint32_t modulus_size_words;
+struct RSAPublicKey {
+ // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.
+ uint32_t modulus_size_words;
- // Precomputed montgomery parameter: -1 / n[0] mod 2^32
- uint32_t n0inv;
+ // Precomputed montgomery parameter: -1 / n[0] mod 2^32
+ uint32_t n0inv;
- // RSA modulus as a little-endian array.
- uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
+ // RSA modulus as a little-endian array.
+ uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];
- // Montgomery parameter R^2 as a little-endian array of little-endian words.
- uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
+ // Montgomery parameter R^2 as a little-endian array.
+ uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];
- // RSA modulus: 3 or 65537
- uint32_t exponent;
-} RSAPublicKey;
-
-// Reverses byte order in |buffer|.
-static void reverse_bytes(uint8_t* buffer, size_t size) {
- for (size_t i = 0; i < (size + 1) / 2; ++i) {
- uint8_t tmp = buffer[i];
- buffer[i] = buffer[size - i - 1];
- buffer[size - i - 1] = tmp;
- }
-}
+ // RSA modulus: 3 or 65537
+ uint32_t exponent;
+};
bool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key) {
const RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
bool ret = false;
- uint8_t modulus_buffer[ANDROID_PUBKEY_MODULUS_SIZE];
RSA* new_key = RSA_new();
+ BIGNUM* n = NULL;
+ BIGNUM* e = NULL;
if (!new_key) {
goto cleanup;
}
@@ -79,18 +71,23 @@ bool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key) {
}
// Convert the modulus to big-endian byte order as expected by BN_bin2bn.
- memcpy(modulus_buffer, key_struct->modulus, sizeof(modulus_buffer));
- reverse_bytes(modulus_buffer, sizeof(modulus_buffer));
- new_key->n = BN_bin2bn(modulus_buffer, sizeof(modulus_buffer), NULL);
- if (!new_key->n) {
+ n = BN_le2bn(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, NULL);
+ if (!n) {
goto cleanup;
}
// Read the exponent.
- new_key->e = BN_new();
- if (!new_key->e || !BN_set_word(new_key->e, key_struct->exponent)) {
+ e = BN_new();
+ if (!e || !BN_set_word(e, key_struct->exponent)) {
+ goto cleanup;
+ }
+
+ if (!RSA_set0_key(new_key, n, e, NULL)) {
goto cleanup;
}
+ // RSA_set0_key takes ownership of its inputs on success.
+ n = NULL;
+ e = NULL;
// Note that we don't extract the montgomery parameters n0inv and rr from
// the RSAPublicKey structure. They assume a word size of 32 bits, but
@@ -101,24 +98,16 @@ bool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key) {
// pre-computed montgomery parameters.
*key = new_key;
+ new_key = NULL;
ret = true;
cleanup:
- if (!ret && new_key) {
- RSA_free(new_key);
- }
+ RSA_free(new_key);
+ BN_free(n);
+ BN_free(e);
return ret;
}
-static bool android_pubkey_encode_bignum(const BIGNUM* num, uint8_t* buffer) {
- if (!BN_bn2bin_padded(buffer, ANDROID_PUBKEY_MODULUS_SIZE, num)) {
- return false;
- }
-
- reverse_bytes(buffer, ANDROID_PUBKEY_MODULUS_SIZE);
- return true;
-}
-
bool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size) {
RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;
bool ret = false;
@@ -127,8 +116,7 @@ bool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size) {
BIGNUM* n0inv = BN_new();
BIGNUM* rr = BN_new();
- if (sizeof(RSAPublicKey) > size ||
- RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {
+ if (sizeof(RSAPublicKey) > size || RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {
goto cleanup;
}
@@ -136,27 +124,26 @@ bool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size) {
key_struct->modulus_size_words = ANDROID_PUBKEY_MODULUS_SIZE_WORDS;
// Compute and store n0inv = -1 / N[0] mod 2^32.
- if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) ||
- !BN_mod(n0inv, key->n, r32, ctx) ||
+ if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) || !BN_mod(n0inv, RSA_get0_n(key), r32, ctx) ||
!BN_mod_inverse(n0inv, n0inv, r32, ctx) || !BN_sub(n0inv, r32, n0inv)) {
goto cleanup;
}
key_struct->n0inv = (uint32_t)BN_get_word(n0inv);
// Store the modulus.
- if (!android_pubkey_encode_bignum(key->n, key_struct->modulus)) {
+ if (!BN_bn2le_padded(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, RSA_get0_n(key))) {
goto cleanup;
}
// Compute and store rr = (2^(rsa_size)) ^ 2 mod N.
if (!ctx || !rr || !BN_set_bit(rr, ANDROID_PUBKEY_MODULUS_SIZE * 8) ||
- !BN_mod_sqr(rr, rr, key->n, ctx) ||
- !android_pubkey_encode_bignum(rr, key_struct->rr)) {
+ !BN_mod_sqr(rr, rr, RSA_get0_n(key), ctx) ||
+ !BN_bn2le_padded(key_struct->rr, ANDROID_PUBKEY_MODULUS_SIZE, rr)) {
goto cleanup;
}
// Store the exponent.
- key_struct->exponent = (uint32_t)BN_get_word(key->e);
+ key_struct->exponent = (uint32_t)BN_get_word(RSA_get0_e(key));
ret = true;
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 8c232f0cd..6a27f9a20 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -122,7 +122,8 @@ static bool check_vendor_memfd_allowed() {
return true;
}
- /* If its not a number, assume string, but check if its a sane string */
+ // Non-numeric should be a single ASCII character. Characters after the
+ // first are ignored.
if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
vndk_version.c_str());
@@ -158,9 +159,11 @@ static bool __has_memfd_support() {
return false;
}
- /* Check if kernel support exists, otherwise fall back to ashmem */
+ // Check if kernel support exists, otherwise fall back to ashmem.
+ // This code needs to build on old API levels, so we can't use the libc
+ // wrapper.
android::base::unique_fd fd(
- syscall(__NR_memfd_create, "test_android_memfd", MFD_ALLOW_SEALING));
+ syscall(__NR_memfd_create, "test_android_memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING));
if (fd == -1) {
ALOGE("memfd_create failed: %s, no memfd support.\n", strerror(errno));
return false;
@@ -211,13 +214,16 @@ static int __ashmem_open_locked()
// fallback for APEX w/ use_vendor on Q, which would have still used /dev/ashmem
if (fd < 0) {
+ int saved_errno = errno;
fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
+ if (fd < 0) {
+ /* Q launching devices and newer must not reach here since they should have been
+ * able to open ashmem_device_path */
+ ALOGE("Unable to open ashmem device %s (error = %s) and /dev/ashmem(error = %s)",
+ ashmem_device_path.c_str(), strerror(saved_errno), strerror(errno));
+ return fd;
+ }
}
-
- if (fd < 0) {
- return fd;
- }
-
struct stat st;
int ret = TEMP_FAILURE_RETRY(fstat(fd, &st));
if (ret < 0) {
@@ -329,7 +335,9 @@ int ashmem_valid(int fd)
}
static int memfd_create_region(const char* name, size_t size) {
- android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING));
+ // This code needs to build on old API levels, so we can't use the libc
+ // wrapper.
+ android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_CLOEXEC | MFD_ALLOW_SEALING));
if (fd == -1) {
ALOGE("memfd_create(%s, %zd) failed: %s\n", name, size, strerror(errno));
diff --git a/libcutils/ashmem_test.cpp b/libcutils/ashmem_test.cpp
index b37d020fb..fb657f6e6 100644
--- a/libcutils/ashmem_test.cpp
+++ b/libcutils/ashmem_test.cpp
@@ -35,6 +35,11 @@ void TestCreateRegion(size_t size, unique_fd &fd, int prot) {
ASSERT_TRUE(ashmem_valid(fd));
ASSERT_EQ(size, static_cast<size_t>(ashmem_get_size_region(fd)));
ASSERT_EQ(0, ashmem_set_prot_region(fd, prot));
+
+ // We've been inconsistent historically about whether or not these file
+ // descriptors were CLOEXEC. Make sure we're consistent going forward.
+ // https://issuetracker.google.com/165667331
+ ASSERT_EQ(FD_CLOEXEC, (fcntl(fd, F_GETFD) & FD_CLOEXEC));
}
void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index c74ee3e9d..793e2ce8a 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,7 +75,8 @@ __BEGIN_DECLS
#define ATRACE_TAG_AIDL (1<<24)
#define ATRACE_TAG_NNAPI (1<<25)
#define ATRACE_TAG_RRO (1<<26)
-#define ATRACE_TAG_LAST ATRACE_TAG_RRO
+#define ATRACE_TAG_SYSPROP (1<<27)
+#define ATRACE_TAG_LAST ATRACE_TAG_SYSPROP
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index e4f45a85e..b4fe2e687 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -36,7 +36,7 @@
#pragma once
-/* This is the master Users and Groups config for the platform.
+/* This is the main Users and Groups config for the platform.
* DO NOT EVER RENUMBER
*/
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
index b94d134be..2fe877c4e 100644
--- a/libcutils/qtaguid.cpp
+++ b/libcutils/qtaguid.cpp
@@ -38,24 +38,24 @@ class netdHandler {
int (*netdDeleteTagData)(uint32_t, uid_t);
};
-int dummyTagSocket(int, uint32_t, uid_t) {
+int stubTagSocket(int, uint32_t, uid_t) {
return -EREMOTEIO;
}
-int dummyUntagSocket(int) {
+int stubUntagSocket(int) {
return -EREMOTEIO;
}
-int dummySetCounterSet(uint32_t, uid_t) {
+int stubSetCounterSet(uint32_t, uid_t) {
return -EREMOTEIO;
}
-int dummyDeleteTagData(uint32_t, uid_t) {
+int stubDeleteTagData(uint32_t, uid_t) {
return -EREMOTEIO;
}
netdHandler initHandler(void) {
- netdHandler handler = {dummyTagSocket, dummyUntagSocket, dummySetCounterSet, dummyDeleteTagData};
+ netdHandler handler = {stubTagSocket, stubUntagSocket, stubSetCounterSet, stubDeleteTagData};
void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
if (!netdClientHandle) {
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index 5a09a2d39..1ab63dc2f 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -30,9 +30,9 @@ void atrace_set_tracing_enabled(bool enabled)
static void atrace_init_once()
{
- atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+ atrace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
if (atrace_marker_fd == -1) {
- atrace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
+ atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
}
if (atrace_marker_fd == -1) {
diff --git a/libcutils/uevent.cpp b/libcutils/uevent.cpp
index bf244d2c7..40bbd5c8f 100644
--- a/libcutils/uevent.cpp
+++ b/libcutils/uevent.cpp
@@ -101,7 +101,7 @@ int uevent_open_socket(int buf_sz, bool passcred) {
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
- addr.nl_pid = getpid();
+ addr.nl_pid = 0;
addr.nl_groups = 0xffffffff;
s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
diff --git a/libkeyutils/keyutils_test.cpp b/libkeyutils/keyutils_test.cpp
index d41c91b68..d03747b3e 100644
--- a/libkeyutils/keyutils_test.cpp
+++ b/libkeyutils/keyutils_test.cpp
@@ -33,7 +33,7 @@
#include <gtest/gtest.h>
TEST(keyutils, smoke) {
- // Check that the exported type is sane.
+ // Check that the exported type is the right size.
ASSERT_EQ(4U, sizeof(key_serial_t));
// Check that all the functions actually exist.
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 6051ac7fa..59ab250b9 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -43,6 +43,7 @@ cc_library_headers {
"//apex_available:anyapex",
],
min_sdk_version: "29",
+ sdk_version: "minimum",
native_bridge_supported: true,
export_include_dirs: ["include"],
system_shared_libs: [],
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index 51c5e60a8..85556e86f 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -31,84 +31,13 @@
#include <unordered_map>
#include <log/event_tag_map.h>
-#include <log/log_properties.h>
#include <private/android_logger.h>
#include <utils/FastStrcmp.h>
#include <utils/RWLock.h>
-#include "logd_reader.h"
-
#define OUT_TAG "EventTagMap"
-class MapString {
- private:
- const std::string* alloc; // HAS-AN
- const std::string_view str; // HAS-A
-
- public:
- operator const std::string_view() const {
- return str;
- }
-
- const char* data() const {
- return str.data();
- }
- size_t length() const {
- return str.length();
- }
-
- bool operator==(const MapString& rval) const {
- if (length() != rval.length()) return false;
- if (length() == 0) return true;
- return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
- }
- bool operator!=(const MapString& rval) const {
- return !(*this == rval);
- }
-
- MapString(const char* str, size_t len) : alloc(NULL), str(str, len) {
- }
- explicit MapString(const std::string& str)
- : alloc(new std::string(str)), str(alloc->data(), alloc->length()) {
- }
- MapString(MapString&& rval) noexcept
- : alloc(rval.alloc), str(rval.data(), rval.length()) {
- rval.alloc = NULL;
- }
- explicit MapString(const MapString& rval)
- : alloc(rval.alloc ? new std::string(*rval.alloc) : NULL),
- str(alloc ? alloc->data() : rval.data(), rval.length()) {
- }
-
- ~MapString() {
- if (alloc) delete alloc;
- }
-};
-
-// Hash for MapString
-template <>
-struct std::hash<MapString>
- : public std::unary_function<const MapString&, size_t> {
- size_t operator()(const MapString& __t) const noexcept {
- if (!__t.length()) return 0;
- return std::hash<std::string_view>()(std::string_view(__t));
- }
-};
-
-typedef std::pair<MapString, MapString> TagFmt;
-
-template <>
-struct std::hash<TagFmt> : public std::unary_function<const TagFmt&, size_t> {
- size_t operator()(const TagFmt& __t) const noexcept {
- // Tag is typically unique. Will cost us an extra 100ns for the
- // unordered_map lookup if we instead did a hash that combined
- // both of tag and fmt members, e.g.:
- //
- // return std::hash<MapString>()(__t.first) ^
- // std::hash<MapString>()(__t.second);
- return std::hash<MapString>()(__t.first);
- }
-};
+typedef std::pair<std::string_view, std::string_view> TagFmt;
// Map
struct EventTagMap {
@@ -119,8 +48,7 @@ struct EventTagMap {
private:
std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
- std::unordered_map<TagFmt, uint32_t> TagFmt2Idx;
- std::unordered_map<MapString, uint32_t> Tag2Idx;
+ std::unordered_map<std::string_view, uint32_t> Tag2Idx;
// protect unordered sets
android::RWLock rwlock;
@@ -132,7 +60,6 @@ struct EventTagMap {
~EventTagMap() {
Idx2TagFmt.clear();
- TagFmt2Idx.clear();
Tag2Idx.clear();
for (size_t which = 0; which < NUM_MAPS; ++which) {
if (mapAddr[which]) {
@@ -144,8 +71,7 @@ struct EventTagMap {
bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
const TagFmt* find(uint32_t tag) const;
- int find(TagFmt&& tagfmt) const;
- int find(MapString&& tag) const;
+ int find(std::string_view tag) const;
};
bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt,
@@ -156,8 +82,7 @@ bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt,
":%.*s:%.*s)\n";
android::RWLock::AutoWLock writeLock(rwlock);
{
- std::unordered_map<uint32_t, TagFmt>::const_iterator it;
- it = Idx2TagFmt.find(tag);
+ auto it = Idx2TagFmt.find(tag);
if (it != Idx2TagFmt.end()) {
if (verbose) {
fprintf(stderr, errorFormat, it->first, (int)it->second.first.length(),
@@ -173,25 +98,7 @@ bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt,
}
{
- std::unordered_map<TagFmt, uint32_t>::const_iterator it;
- it = TagFmt2Idx.find(tagfmt);
- if (it != TagFmt2Idx.end()) {
- if (verbose) {
- fprintf(stderr, errorFormat, it->second, (int)it->first.first.length(),
- it->first.first.data(), (int)it->first.second.length(),
- it->first.second.data(), tag, (int)tagfmt.first.length(),
- tagfmt.first.data(), (int)tagfmt.second.length(),
- tagfmt.second.data());
- }
- ret = false;
- } else {
- TagFmt2Idx.emplace(std::make_pair(tagfmt, tag));
- }
- }
-
- {
- std::unordered_map<MapString, uint32_t>::const_iterator it;
- it = Tag2Idx.find(tagfmt.first);
+ auto it = Tag2Idx.find(tagfmt.first);
if (!tagfmt.second.length() && (it != Tag2Idx.end())) {
Tag2Idx.erase(it);
it = Tag2Idx.end();
@@ -205,25 +112,15 @@ bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt,
}
const TagFmt* EventTagMap::find(uint32_t tag) const {
- std::unordered_map<uint32_t, TagFmt>::const_iterator it;
android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
- it = Idx2TagFmt.find(tag);
+ auto it = Idx2TagFmt.find(tag);
if (it == Idx2TagFmt.end()) return NULL;
return &(it->second);
}
-int EventTagMap::find(TagFmt&& tagfmt) const {
- std::unordered_map<TagFmt, uint32_t>::const_iterator it;
- android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
- it = TagFmt2Idx.find(std::move(tagfmt));
- if (it == TagFmt2Idx.end()) return -1;
- return it->second;
-}
-
-int EventTagMap::find(MapString&& tag) const {
- std::unordered_map<MapString, uint32_t>::const_iterator it;
+int EventTagMap::find(std::string_view tag) const {
android::RWLock::AutoRLock readLock(const_cast<android::RWLock&>(rwlock));
- it = Tag2Idx.find(std::move(tag));
+ auto it = Tag2Idx.find(std::move(tag));
if (it == Tag2Idx.end()) return -1;
return it->second;
}
@@ -241,29 +138,20 @@ static const char* endOfTag(const char* cp) {
// successful return, it will be pointing to the last character in the
// tag line (i.e. the character before the start of the next line).
//
-// lineNum = 0 removes verbose comments and requires us to cache the
-// content rather than make direct raw references since the content
-// will disappear after the call. A non-zero lineNum means we own the
-// data and it will outlive the call.
-//
// Returns 0 on success, nonzero on failure.
-static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
+static int scanTagLine(EventTagMap* map, const char*& pData, int line_num) {
char* ep;
unsigned long val = strtoul(pData, &ep, 10);
const char* cp = ep;
if (cp == pData) {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
- }
+ fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", line_num);
errno = EINVAL;
return -1;
}
uint32_t tagIndex = val;
if (tagIndex != val) {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
- }
+ fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", line_num);
errno = ERANGE;
return -1;
}
@@ -272,9 +160,7 @@ static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
}
if (*cp == '\n') {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
- }
+ fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", line_num);
errno = EINVAL;
return -1;
}
@@ -284,10 +170,7 @@ static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
size_t tagLen = cp - tag;
if (!isspace(*cp)) {
- if (lineNum) {
- fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp,
- lineNum);
- }
+ fprintf(stderr, OUT_TAG ": invalid tag char %c on line %d\n", *cp, line_num);
errno = EINVAL;
return -1;
}
@@ -317,25 +200,15 @@ static int scanTagLine(EventTagMap* map, const char*& pData, int lineNum) {
while (*cp && (*cp != '\n')) ++cp;
#ifdef DEBUG
- fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - pData), pData);
+ fprintf(stderr, "%d: %p: %.*s\n", line_num, tag, (int)(cp - pData), pData);
#endif
pData = cp;
- if (lineNum) {
- if (map->emplaceUnique(tagIndex,
- TagFmt(std::make_pair(MapString(tag, tagLen),
- MapString(fmt, fmtLen))),
- verbose)) {
- return 0;
- }
- } else {
- // cache
- if (map->emplaceUnique(
- tagIndex,
- TagFmt(std::make_pair(MapString(std::string(tag, tagLen)),
- MapString(std::string(fmt, fmtLen)))))) {
- return 0;
- }
+ if (map->emplaceUnique(
+ tagIndex,
+ TagFmt(std::make_pair(std::string_view(tag, tagLen), std::string_view(fmt, fmtLen))),
+ verbose)) {
+ return 0;
}
errno = EMLINK;
return -1;
@@ -491,57 +364,10 @@ void android_closeEventTagMap(EventTagMap* map) {
if (map) delete map;
}
-// Cache miss, go to logd to acquire a public reference.
-// Because we lack access to a SHARED PUBLIC /dev/event-log-tags file map?
-static const TagFmt* __getEventTag([[maybe_unused]] EventTagMap* map, unsigned int tag) {
- // call event tag service to arrange for a new tag
- char* buf = NULL;
- // Can not use android::base::StringPrintf, asprintf + free instead.
- static const char command_template[] = "getEventTag id=%u";
- int ret = asprintf(&buf, command_template, tag);
- if (ret > 0) {
- // Add some buffer margin for an estimate of the full return content.
- size_t size =
- ret - strlen(command_template) +
- strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
- if (size > (size_t)ret) {
- char* np = static_cast<char*>(realloc(buf, size));
- if (np) {
- buf = np;
- } else {
- size = ret;
- }
- } else {
- size = ret;
- }
-#ifdef __ANDROID__
- // Ask event log tag service for an existing entry
- if (SendLogdControlMessage(buf, size) >= 0) {
- buf[size - 1] = '\0';
- char* ep;
- unsigned long val = strtoul(buf, &ep, 10); // return size
- const char* cp = ep;
- if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
- ++cp;
- if (!scanTagLine(map, cp, 0)) {
- free(buf);
- return map->find(tag);
- }
- }
- }
-#endif
- free(buf);
- }
- return NULL;
-}
-
// Look up an entry in the map.
const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len, unsigned int tag) {
if (len) *len = 0;
const TagFmt* str = map->find(tag);
- if (!str) {
- str = __getEventTag(const_cast<EventTagMap*>(map), tag);
- }
if (!str) return NULL;
if (len) *len = str->first.length();
return str->first.data();
@@ -551,98 +377,8 @@ const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len, unsi
const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len, unsigned int tag) {
if (len) *len = 0;
const TagFmt* str = map->find(tag);
- if (!str) {
- str = __getEventTag(const_cast<EventTagMap*>(map), tag);
- }
if (!str) return NULL;
if (len) *len = str->second.length();
return str->second.data();
}
-// This function is deprecated and replaced with android_lookupEventTag_len
-// since it will cause the map to change from Shared and backed by a file,
-// to Private Dirty and backed up by swap, albeit highly compressible. By
-// deprecating this function everywhere, we save 100s of MB of memory space.
-const char* android_lookupEventTag(const EventTagMap* map, unsigned int tag) {
- size_t len;
- const char* tagStr = android_lookupEventTag_len(map, &len, tag);
-
- if (!tagStr) return tagStr;
- char* cp = const_cast<char*>(tagStr);
- cp += len;
- if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
- return tagStr;
-}
-
-// Look up tagname, generate one if necessary, and return a tag
-int android_lookupEventTagNum(EventTagMap* map, const char* tagname, const char* format, int prio) {
- const char* ep = endOfTag(tagname);
- size_t len = ep - tagname;
- if (!len || *ep) {
- errno = EINVAL;
- return -1;
- }
-
- if ((prio != ANDROID_LOG_UNKNOWN) && (prio < ANDROID_LOG_SILENT) &&
- !__android_log_is_loggable_len(prio, tagname, len,
- __android_log_is_debuggable()
- ? ANDROID_LOG_VERBOSE
- : ANDROID_LOG_DEBUG)) {
- errno = EPERM;
- return -1;
- }
-
- if (!format) format = "";
- ssize_t fmtLen = strlen(format);
- int ret = map->find(TagFmt(
- std::make_pair(MapString(tagname, len), MapString(format, fmtLen))));
- if (ret != -1) return ret;
-
- // call event tag service to arrange for a new tag
- char* buf = NULL;
- // Can not use android::base::StringPrintf, asprintf + free instead.
- static const char command_template[] = "getEventTag name=%s format=\"%s\"";
- ret = asprintf(&buf, command_template, tagname, format);
- if (ret > 0) {
- // Add some buffer margin for an estimate of the full return content.
- char* cp;
- size_t size =
- ret - strlen(command_template) +
- strlen("65535\n4294967295\t?\t\t\t?\t# uid=32767\n\n\f?success?");
- if (size > (size_t)ret) {
- cp = static_cast<char*>(realloc(buf, size));
- if (cp) {
- buf = cp;
- } else {
- size = ret;
- }
- } else {
- size = ret;
- }
-#ifdef __ANDROID__
- // Ask event log tag service for an allocation
- if (SendLogdControlMessage(buf, size) >= 0) {
- buf[size - 1] = '\0';
- unsigned long val = strtoul(buf, &cp, 10); // return size
- if ((buf != cp) && (val > 0) && (*cp == '\n')) { // truncation OK
- val = strtoul(cp + 1, &cp, 10); // allocated tag number
- if ((val > 0) && (val < UINT32_MAX) && (*cp == '\t')) {
- free(buf);
- ret = val;
- // cache
- map->emplaceUnique(ret, TagFmt(std::make_pair(
- MapString(std::string(tagname, len)),
- MapString(std::string(format, fmtLen)))));
- return ret;
- }
- }
- }
-#endif
- free(buf);
- }
-
- // Hail Mary
- ret = map->find(MapString(tagname, len));
- if (ret == -1) errno = ESRCH;
- return ret;
-}
diff --git a/liblog/include/log/event_tag_map.h b/liblog/include/log/event_tag_map.h
index f7ec208b3..de49fbf9e 100644
--- a/liblog/include/log/event_tag_map.h
+++ b/liblog/include/log/event_tag_map.h
@@ -40,14 +40,6 @@ EventTagMap* android_openEventTagMap(const char* fileName);
void android_closeEventTagMap(EventTagMap* map);
/*
- * Look up a tag by index. Returns the tag string, or NULL if not found.
- */
-const char* android_lookupEventTag(const EventTagMap* map, unsigned int tag)
- __attribute__((
- deprecated("use android_lookupEventTag_len() instead to minimize "
- "MAP_PRIVATE copy-on-write memory impact")));
-
-/*
* Look up a tag by index. Returns the tag string & string length, or NULL if
* not found. Returned string is not guaranteed to be nul terminated.
*/
@@ -61,12 +53,6 @@ const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len,
const char* android_lookupEventFormat_len(const EventTagMap* map, size_t* len,
unsigned int tag);
-/*
- * Look up tagname, generate one if necessary, and return a tag
- */
-int android_lookupEventTagNum(EventTagMap* map, const char* tagname,
- const char* format, int prio);
-
#ifdef __cplusplus
}
#endif
diff --git a/liblog/include/log/log_properties.h b/liblog/include/log/log_properties.h
index 3497d63dc..2a0230f75 100644
--- a/liblog/include/log/log_properties.h
+++ b/liblog/include/log/log_properties.h
@@ -20,6 +20,7 @@
extern "C" {
#endif
+/* Returns `1` if the device is debuggable or `0` if not. */
int __android_log_is_debuggable();
#ifdef __cplusplus
diff --git a/liblog/include/log/log_read.h b/liblog/include/log/log_read.h
index 23d76f485..173693472 100644
--- a/liblog/include/log/log_read.h
+++ b/liblog/include/log/log_read.h
@@ -81,10 +81,17 @@ struct logger;
log_id_t android_logger_get_id(struct logger* logger);
+/* Clears the given log buffer. */
int android_logger_clear(struct logger* logger);
+/* Return the allotted size for the given log buffer. */
long android_logger_get_log_size(struct logger* logger);
+/* Set the allotted size for the given log buffer. */
int android_logger_set_log_size(struct logger* logger, unsigned long size);
+/* Return the actual, uncompressed size that can be read from the given log buffer. */
long android_logger_get_log_readable_size(struct logger* logger);
+/* Return the actual, compressed size that the given log buffer is consuming. */
+long android_logger_get_log_consumed_size(struct logger* logger);
+/* Deprecated. Always returns '4' regardless of input. */
int android_logger_get_log_version(struct logger* logger);
struct logger_list;
diff --git a/liblog/include/private/android_logger.h b/liblog/include/private/android_logger.h
index d3b72bcee..166f387c7 100644
--- a/liblog/include/private/android_logger.h
+++ b/liblog/include/private/android_logger.h
@@ -144,21 +144,6 @@ int __android_log_security_bwrite(int32_t tag, const void* payload, size_t len);
int __android_log_security_bswrite(int32_t tag, const char* payload);
int __android_log_security(); /* Device Owner is present */
-#define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
-#define BOOL_DEFAULT_FALSE 0x0 /* false if property not present */
-#define BOOL_DEFAULT_TRUE 0x1 /* true if property not present */
-#define BOOL_DEFAULT_FLAG_PERSIST 0x2 /* <key>, persist.<key>, ro.<key> */
-#define BOOL_DEFAULT_FLAG_ENG 0x4 /* off for user */
-#define BOOL_DEFAULT_FLAG_SVELTE 0x8 /* off for low_ram */
-bool __android_logger_property_get_bool(const char* key, int flag);
-
-#define LOG_BUFFER_SIZE (256 * 1024) /* Tuned with ro.logd.size per-platform \
- */
-#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
-#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
-unsigned long __android_logger_get_buffer_size(log_id_t logId);
-bool __android_logger_valid_buffer_size(unsigned long value);
-
/* Retrieve the composed event buffer */
int android_log_write_list_buffer(android_log_context ctx, const char** msg);
diff --git a/liblog/liblog.map.txt b/liblog/liblog.map.txt
index 161fcf1a9..f8d5ef0f3 100644
--- a/liblog/liblog.map.txt
+++ b/liblog/liblog.map.txt
@@ -84,13 +84,10 @@ LIBLOG_PRIVATE {
global:
__android_log_pmsg_file_read;
__android_log_pmsg_file_write;
- __android_logger_get_buffer_size;
- __android_logger_property_get_bool;
android_openEventTagMap;
android_log_processBinaryLogBuffer;
android_log_processLogBuffer;
android_log_read_next;
android_log_write_list_buffer;
- android_lookupEventTagNum;
create_android_log_parser;
};
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index 82ed6b2b5..611caedd8 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -35,13 +35,14 @@
#include <string>
+#include <android-base/parseint.h>
#include <private/android_logger.h>
#include "logger.h"
// Connects to /dev/socket/<name> and returns the associated fd or returns -1 on error.
// O_CLOEXEC is always set.
-static int socket_local_client(const std::string& name, int type) {
+static int socket_local_client(const std::string& name, int type, bool timeout) {
sockaddr_un addr = {.sun_family = AF_LOCAL};
std::string path = "/dev/socket/" + name;
@@ -55,6 +56,18 @@ static int socket_local_client(const std::string& name, int type) {
return -1;
}
+ if (timeout) {
+ // Sending and receiving messages should be instantaneous, but we don't want to wait forever if
+ // logd is hung, so we set a gracious 2s timeout.
+ struct timeval t = {2, 0};
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &t, sizeof(t)) == -1) {
+ return -1;
+ }
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)) == -1) {
+ return -1;
+ }
+ }
+
if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
close(fd);
return -1;
@@ -69,7 +82,7 @@ ssize_t SendLogdControlMessage(char* buf, size_t buf_size) {
size_t len;
char* cp;
int errno_save = 0;
- int sock = socket_local_client("logd", SOCK_STREAM);
+ int sock = socket_local_client("logd", SOCK_STREAM, true);
if (sock < 0) {
return sock;
}
@@ -148,63 +161,68 @@ int android_logger_clear(struct logger* logger) {
return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
}
-/* returns the total size of the log's ring buffer */
-long android_logger_get_log_size(struct logger* logger) {
+enum class LogSizeType : uint32_t {
+ kAllotted = 0,
+ kReadable,
+ kConsumed,
+};
+
+static long GetLogSize(struct logger* logger, LogSizeType type) {
if (!android_logger_is_logd(logger)) {
return -EINVAL;
}
uint32_t log_id = android_logger_get_id(logger);
char buf[512];
- snprintf(buf, sizeof(buf), "getLogSize %" PRIu32, log_id);
+ switch (type) {
+ case LogSizeType::kAllotted:
+ snprintf(buf, sizeof(buf), "getLogSize %" PRIu32, log_id);
+ break;
+ case LogSizeType::kReadable:
+ snprintf(buf, sizeof(buf), "getLogSizeReadable %" PRIu32, log_id);
+ break;
+ case LogSizeType::kConsumed:
+ snprintf(buf, sizeof(buf), "getLogSizeUsed %" PRIu32, log_id);
+ break;
+ default:
+ abort();
+ }
ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
if (ret < 0) {
return ret;
}
- if ((buf[0] < '0') || ('9' < buf[0])) {
+ long size;
+ if (!android::base::ParseInt(buf, &size)) {
return -1;
}
- return atol(buf);
+ return size;
}
-int android_logger_set_log_size(struct logger* logger, unsigned long size) {
- if (!android_logger_is_logd(logger)) {
- return -EINVAL;
- }
+long android_logger_get_log_size(struct logger* logger) {
+ return GetLogSize(logger, LogSizeType::kAllotted);
+}
- uint32_t log_id = android_logger_get_id(logger);
- char buf[512];
- snprintf(buf, sizeof(buf), "setLogSize %" PRIu32 " %lu", log_id, size);
+long android_logger_get_log_readable_size(struct logger* logger) {
+ return GetLogSize(logger, LogSizeType::kReadable);
+}
- return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
+long android_logger_get_log_consumed_size(struct logger* logger) {
+ return GetLogSize(logger, LogSizeType::kConsumed);
}
-/*
- * 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) {
+int android_logger_set_log_size(struct logger* logger, unsigned long size) {
if (!android_logger_is_logd(logger)) {
return -EINVAL;
}
uint32_t log_id = android_logger_get_id(logger);
char buf[512];
- snprintf(buf, sizeof(buf), "getLogSizeUsed %" PRIu32, log_id);
-
- ssize_t ret = SendLogdControlMessage(buf, sizeof(buf));
- if (ret < 0) {
- return ret;
- }
-
- if ((buf[0] < '0') || ('9' < buf[0])) {
- return -1;
- }
+ snprintf(buf, sizeof(buf), "setLogSize %" PRIu32 " %lu", log_id, size);
- return atol(buf);
+ return check_log_success(buf, SendLogdControlMessage(buf, sizeof(buf)));
}
int android_logger_get_log_version(struct logger*) {
@@ -268,7 +286,7 @@ static int logdOpen(struct logger_list* logger_list) {
return sock;
}
- sock = socket_local_client("logdr", SOCK_SEQPACKET);
+ sock = socket_local_client("logdr", SOCK_SEQPACKET, false);
if (sock <= 0) {
if ((sock == -1) && errno) {
return -errno;
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index a230749e1..f5d19cadb 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -59,8 +59,7 @@ static void GetSocket() {
return;
}
- int new_socket =
- TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ int new_socket = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
if (new_socket <= 0) {
return;
}
@@ -91,8 +90,6 @@ int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr)
struct iovec newVec[nr + headerLength];
android_log_header_t header;
size_t i, payloadSize;
- static atomic_int dropped;
- static atomic_int droppedSecurity;
GetSocket();
@@ -110,6 +107,7 @@ int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr)
return 0;
}
+ header.id = logId;
header.tid = gettid();
header.realtime.tv_sec = ts->tv_sec;
header.realtime.tv_nsec = ts->tv_nsec;
@@ -117,44 +115,6 @@ int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr)
newVec[0].iov_base = (unsigned char*)&header;
newVec[0].iov_len = sizeof(header);
- int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
- if (snapshot) {
- android_log_event_int_t buffer;
-
- header.id = LOG_ID_SECURITY;
- buffer.header.tag = LIBLOG_LOG_TAG;
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = snapshot;
-
- newVec[headerLength].iov_base = &buffer;
- newVec[headerLength].iov_len = sizeof(buffer);
-
- ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, 2));
- if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
- atomic_fetch_add_explicit(&droppedSecurity, snapshot, memory_order_relaxed);
- }
- }
- snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
- if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"),
- ANDROID_LOG_VERBOSE)) {
- android_log_event_int_t buffer;
-
- header.id = LOG_ID_EVENTS;
- buffer.header.tag = LIBLOG_LOG_TAG;
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = snapshot;
-
- newVec[headerLength].iov_base = &buffer;
- newVec[headerLength].iov_len = sizeof(buffer);
-
- ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, 2));
- if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
- atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
- }
- }
-
- header.id = logId;
-
for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
newVec[i].iov_base = vec[i - headerLength].iov_base;
payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
@@ -168,11 +128,8 @@ int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr)
}
}
- // The write below could be lost, but will never block.
- // EAGAIN occurs if logd is overloaded, other errors indicate that something went wrong with
- // the connection, so we reset it and try again.
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
- if (ret < 0 && errno != EAGAIN) {
+ if (ret < 0) {
LogdConnect();
ret = TEMP_FAILURE_RETRY(writev(logd_socket, newVec, i));
@@ -182,14 +139,5 @@ int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr)
ret = -errno;
}
- if (ret > (ssize_t)sizeof(header)) {
- ret -= sizeof(header);
- } else if (ret < 0) {
- atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
- if (logId == LOG_ID_SECURITY) {
- atomic_fetch_add_explicit(&droppedSecurity, 1, memory_order_relaxed);
- }
- }
-
return ret;
}
diff --git a/liblog/properties.cpp b/liblog/properties.cpp
index 239211273..88f0bf1ee 100644
--- a/liblog/properties.cpp
+++ b/liblog/properties.cpp
@@ -294,33 +294,12 @@ int __android_log_is_loggable(int prio, const char* tag, int default_prio) {
}
int __android_log_is_debuggable() {
- static uint32_t serial;
- static struct cache_char tag_cache;
- static const char key[] = "ro.debuggable";
- int ret;
-
- if (tag_cache.c) { /* ro property does not change after set */
- ret = tag_cache.c == '1';
- } else if (lock()) {
- struct cache_char temp_cache = {{NULL, 0xFFFFFFFF}, '\0'};
- refresh_cache(&temp_cache, key);
- ret = temp_cache.c == '1';
- } else {
- int change_detected = check_cache(&tag_cache.cache);
- uint32_t current_serial = __system_property_area_serial();
- if (current_serial != serial) {
- change_detected = 1;
- }
- if (change_detected) {
- refresh_cache(&tag_cache, key);
- serial = current_serial;
- }
- ret = tag_cache.c == '1';
-
- unlock();
- }
+ static int is_debuggable = [] {
+ char value[PROP_VALUE_MAX] = {};
+ return __system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1");
+ }();
- return ret;
+ return is_debuggable;
}
/*
@@ -385,216 +364,6 @@ int __android_log_security() {
return do_cache2_char(&security);
}
-/*
- * Interface that represents the logd buffer size determination so that others
- * need not guess our intentions.
- */
-
-/* Property helper */
-static bool check_flag(const char* prop, const char* flag) {
- const char* cp = strcasestr(prop, flag);
- if (!cp) {
- return false;
- }
- /* We only will document comma (,) */
- static const char sep[] = ",:;|+ \t\f";
- if ((cp != prop) && !strchr(sep, cp[-1])) {
- return false;
- }
- cp += strlen(flag);
- return !*cp || !!strchr(sep, *cp);
-}
-
-/* cache structure */
-struct cache_property {
- struct cache cache;
- char property[PROP_VALUE_MAX];
-};
-
-static void refresh_cache_property(struct cache_property* cache, const char* key) {
- if (!cache->cache.pinfo) {
- cache->cache.pinfo = __system_property_find(key);
- if (!cache->cache.pinfo) {
- return;
- }
- }
- cache->cache.serial = __system_property_serial(cache->cache.pinfo);
- __system_property_read(cache->cache.pinfo, 0, cache->property);
-}
-
-/* get boolean with the logger twist that supports eng adjustments */
-bool __android_logger_property_get_bool(const char* key, int flag) {
- struct cache_property property = {{NULL, 0xFFFFFFFF}, {0}};
- if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
- char newkey[strlen("persist.") + strlen(key) + 1];
- snprintf(newkey, sizeof(newkey), "ro.%s", key);
- refresh_cache_property(&property, newkey);
- property.cache.pinfo = NULL;
- property.cache.serial = 0xFFFFFFFF;
- snprintf(newkey, sizeof(newkey), "persist.%s", key);
- refresh_cache_property(&property, newkey);
- property.cache.pinfo = NULL;
- property.cache.serial = 0xFFFFFFFF;
- }
-
- refresh_cache_property(&property, key);
-
- if (check_flag(property.property, "true")) {
- return true;
- }
- if (check_flag(property.property, "false")) {
- return false;
- }
- if (property.property[0]) {
- flag &= ~(BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
- }
- if (check_flag(property.property, "eng")) {
- flag |= BOOL_DEFAULT_FLAG_ENG;
- }
- /* this is really a "not" flag */
- if (check_flag(property.property, "svelte")) {
- flag |= BOOL_DEFAULT_FLAG_SVELTE;
- }
-
- /* Sanity Check */
- if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) {
- flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE;
- flag |= BOOL_DEFAULT_TRUE;
- }
-
- if ((flag & BOOL_DEFAULT_FLAG_SVELTE) &&
- __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)) {
- return false;
- }
- if ((flag & BOOL_DEFAULT_FLAG_ENG) && !__android_log_is_debuggable()) {
- return false;
- }
-
- return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
-}
-
-bool __android_logger_valid_buffer_size(unsigned long value) {
- return LOG_BUFFER_MIN_SIZE <= value && value <= LOG_BUFFER_MAX_SIZE;
-}
-
-struct cache2_property_size {
- pthread_mutex_t lock;
- uint32_t serial;
- const char* key_persist;
- struct cache_property cache_persist;
- const char* key_ro;
- struct cache_property cache_ro;
- unsigned long (*const evaluate)(const struct cache2_property_size* self);
-};
-
-static inline unsigned long do_cache2_property_size(struct cache2_property_size* self) {
- uint32_t current_serial;
- int change_detected;
- unsigned long v;
-
- if (pthread_mutex_trylock(&self->lock)) {
- /* We are willing to accept some race in this context */
- return self->evaluate(self);
- }
-
- change_detected = check_cache(&self->cache_persist.cache) || check_cache(&self->cache_ro.cache);
- current_serial = __system_property_area_serial();
- if (current_serial != self->serial) {
- change_detected = 1;
- }
- if (change_detected) {
- refresh_cache_property(&self->cache_persist, self->key_persist);
- refresh_cache_property(&self->cache_ro, self->key_ro);
- self->serial = current_serial;
- }
- v = self->evaluate(self);
-
- pthread_mutex_unlock(&self->lock);
-
- return v;
-}
-
-static unsigned long property_get_size_from_cache(const struct cache_property* cache) {
- char* cp;
- unsigned long value = strtoul(cache->property, &cp, 10);
-
- switch (*cp) {
- case 'm':
- case 'M':
- value *= 1024;
- [[fallthrough]];
- case 'k':
- case 'K':
- value *= 1024;
- [[fallthrough]];
- case '\0':
- break;
-
- default:
- value = 0;
- }
-
- if (!__android_logger_valid_buffer_size(value)) {
- value = 0;
- }
-
- return value;
-}
-
-static unsigned long evaluate_property_get_size(const struct cache2_property_size* self) {
- unsigned long size = property_get_size_from_cache(&self->cache_persist);
- if (size) {
- return size;
- }
- return property_get_size_from_cache(&self->cache_ro);
-}
-
-unsigned long __android_logger_get_buffer_size(log_id_t logId) {
- static const char global_tunable[] = "persist.logd.size"; /* Settings App */
- static const char global_default[] = "ro.logd.size"; /* BoardConfig.mk */
- static struct cache2_property_size global = {
- /* clang-format off */
- PTHREAD_MUTEX_INITIALIZER, 0,
- global_tunable, { { NULL, 0xFFFFFFFF }, {} },
- global_default, { { NULL, 0xFFFFFFFF }, {} },
- evaluate_property_get_size
- /* clang-format on */
- };
- char key_persist[strlen(global_tunable) + strlen(".security") + 1];
- char key_ro[strlen(global_default) + strlen(".security") + 1];
- struct cache2_property_size local = {
- /* clang-format off */
- PTHREAD_MUTEX_INITIALIZER, 0,
- key_persist, { { NULL, 0xFFFFFFFF }, {} },
- key_ro, { { NULL, 0xFFFFFFFF }, {} },
- evaluate_property_get_size
- /* clang-format on */
- };
- unsigned long property_size, default_size;
-
- default_size = do_cache2_property_size(&global);
- if (!default_size) {
- default_size = __android_logger_property_get_bool("ro.config.low_ram", BOOL_DEFAULT_FALSE)
- ? LOG_BUFFER_MIN_SIZE /* 64K */
- : LOG_BUFFER_SIZE; /* 256K */
- }
-
- snprintf(key_persist, sizeof(key_persist), "%s.%s", global_tunable,
- android_log_id_to_name(logId));
- snprintf(key_ro, sizeof(key_ro), "%s.%s", global_default, android_log_id_to_name(logId));
- property_size = do_cache2_property_size(&local);
-
- if (!property_size) {
- property_size = default_size;
- }
-
- if (!property_size) {
- property_size = LOG_BUFFER_SIZE;
- }
-
- return property_size;
-}
-
#else
int __android_log_is_loggable(int prio, const char*, int) {
@@ -613,4 +382,4 @@ int __android_log_is_debuggable() {
return 1;
}
-#endif \ No newline at end of file
+#endif
diff --git a/liblog/tests/Android.bp b/liblog/tests/Android.bp
index 2a6424b05..a17d90cc0 100644
--- a/liblog/tests/Android.bp
+++ b/liblog/tests/Android.bp
@@ -97,6 +97,7 @@ cc_test {
cflags: ["-DNO_PSTORE"],
test_suites: [
"cts",
+ "device-tests",
"vts10",
],
}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 3bd5cf20f..d2f12d6d2 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -879,30 +879,6 @@ static void BM_lookupEventFormat(benchmark::State& state) {
}
BENCHMARK(BM_lookupEventFormat);
-/*
- * Measure the time it takes for android_lookupEventTagNum plus above
- */
-static void BM_lookupEventTagNum(benchmark::State& state) {
- prechargeEventMap();
-
- std::unordered_set<uint32_t>::const_iterator it = set.begin();
-
- while (state.KeepRunning()) {
- size_t len;
- const char* name = android_lookupEventTag_len(map, &len, (*it));
- std::string Name(name, len);
- const char* format = android_lookupEventFormat_len(map, &len, (*it));
- std::string Format(format, len);
- state.ResumeTiming();
- android_lookupEventTagNum(map, Name.c_str(), Format.c_str(),
- ANDROID_LOG_UNKNOWN);
- state.PauseTiming();
- ++it;
- if (it == set.end()) it = set.begin();
- }
-}
-BENCHMARK(BM_lookupEventTagNum);
-
// Must be functionally identical to liblog internal SendLogdControlMessage()
static void send_to_control(char* buf, size_t len) {
int sock =
diff --git a/liblog/tests/liblog_global_state.cpp b/liblog/tests/liblog_global_state.cpp
index 3508818f2..1d7ff9f22 100644
--- a/liblog/tests/liblog_global_state.cpp
+++ b/liblog/tests/liblog_global_state.cpp
@@ -153,56 +153,65 @@ TEST(liblog_global_state, SetAborter_with_liblog) {
message_seen = false;
}
+static std::string UniqueLogTag() {
+ std::string tag = LOG_TAG;
+ tag += "-" + std::to_string(getpid());
+ return tag;
+}
+
TEST(liblog_global_state, is_loggable_both_default) {
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ auto tag = UniqueLogTag();
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
}
TEST(liblog_global_state, is_loggable_minimum_log_priority_only) {
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ auto tag = UniqueLogTag();
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
EXPECT_EQ(android::base::WARNING, android::base::SetMinimumLogSeverity(android::base::DEBUG));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
EXPECT_EQ(android::base::DEBUG, android::base::SetMinimumLogSeverity(android::base::WARNING));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
}
TEST(liblog_global_state, is_loggable_tag_log_priority_only) {
#ifdef __ANDROID__
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
-
- auto log_tag_property = std::string("log.tag.") + LOG_TAG;
- android::base::SetProperty(log_tag_property, "d");
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
-
- android::base::SetProperty(log_tag_property, "w");
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
-
- android::base::SetProperty(log_tag_property, "");
+ auto tag = UniqueLogTag();
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
+
+ auto log_tag_property = std::string("log.tag.") + tag;
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, "d"));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
+
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, "w"));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
+
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, ""));
#else
GTEST_SKIP() << "No log tag properties on host";
#endif
@@ -210,39 +219,40 @@ TEST(liblog_global_state, is_loggable_tag_log_priority_only) {
TEST(liblog_global_state, is_loggable_both_set) {
#ifdef __ANDROID__
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ auto tag = UniqueLogTag();
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
// When both a tag and a minimum priority are set, we use the lower value of the two.
// tag = warning, minimum_priority = debug, expect 'debug'
- auto log_tag_property = std::string("log.tag.") + LOG_TAG;
- android::base::SetProperty(log_tag_property, "w");
+ auto log_tag_property = std::string("log.tag.") + tag;
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, "w"));
EXPECT_EQ(ANDROID_LOG_DEFAULT, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
// tag = warning, minimum_priority = warning, expect 'warning'
EXPECT_EQ(ANDROID_LOG_DEBUG, __android_log_set_minimum_priority(ANDROID_LOG_WARN));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(0, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
// tag = debug, minimum_priority = warning, expect 'debug'
- android::base::SetProperty(log_tag_property, "d");
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, "d"));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
// tag = debug, minimum_priority = debug, expect 'debug'
EXPECT_EQ(ANDROID_LOG_WARN, __android_log_set_minimum_priority(ANDROID_LOG_DEBUG));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, LOG_TAG, ANDROID_LOG_INFO));
- EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, LOG_TAG, ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_DEBUG, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_INFO, tag.c_str(), ANDROID_LOG_INFO));
+ EXPECT_EQ(1, __android_log_is_loggable(ANDROID_LOG_WARN, tag.c_str(), ANDROID_LOG_INFO));
- android::base::SetProperty(log_tag_property, "");
+ ASSERT_TRUE(android::base::SetProperty(log_tag_property, ""));
#else
GTEST_SKIP() << "No log tag properties on host";
#endif
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index fbc3d7a5a..c49d87b59 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -2768,20 +2768,3 @@ TEST(liblog, __android_log_pmsg_file_read) {
#endif
}
#endif // ENABLE_FLAKY_TESTS
-
-TEST(liblog, android_lookupEventTagNum) {
-#ifdef __ANDROID__
- EventTagMap* map = android_openEventTagMap(NULL);
- EXPECT_TRUE(NULL != map);
- std::string Name = android::base::StringPrintf("a%d", getpid());
- int tag = android_lookupEventTagNum(map, Name.c_str(), "(new|1)",
- ANDROID_LOG_UNKNOWN);
- android_closeEventTagMap(map);
- if (tag == -1) system("tail -3 /dev/event-log-tags >&2");
- EXPECT_NE(-1, tag);
- EXPECT_NE(0, tag);
- EXPECT_GT(UINT32_MAX, (unsigned)tag);
-#else
- GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
diff --git a/liblog/tests/log_read_test.cpp b/liblog/tests/log_read_test.cpp
index 3e0961749..7acd363dc 100644
--- a/liblog/tests/log_read_test.cpp
+++ b/liblog/tests/log_read_test.cpp
@@ -20,6 +20,7 @@
#include <string>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android/log.h> // minimal logging API
#include <gtest/gtest.h>
@@ -29,6 +30,8 @@
// Do not use anything in log/log_time.h despite side effects of the above.
#include <private/android_logger.h>
+using android::base::GetBoolProperty;
+
TEST(liblog, android_logger_get_) {
#ifdef __ANDROID__
// This test assumes the log buffers are filled with noise from
@@ -38,31 +41,27 @@ TEST(liblog, android_logger_get_) {
for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
log_id_t id = static_cast<log_id_t>(i);
- const char* name = android_log_id_to_name(id);
- if (id != android_name_to_log_id(name)) {
- continue;
- }
- fprintf(stderr, "log buffer %s\r", name);
+ std::string name = android_log_id_to_name(id);
+ fprintf(stderr, "log buffer %s\r", name.c_str());
struct logger* logger;
EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
EXPECT_EQ(id, android_logger_get_id(logger));
ssize_t get_log_size = android_logger_get_log_size(logger);
/* security buffer is allowed to be denied */
- if (strcmp("security", name)) {
- EXPECT_LT(0, get_log_size);
+ if (name != "security") {
+ EXPECT_GT(get_log_size, 0);
// crash buffer is allowed to be empty, that is actually healthy!
- // kernel buffer is allowed to be empty on "user" builds
- // stats buffer is allowed to be empty TEMPORARILY.
- // TODO: remove stats buffer from here once we start to use it in
- // framework (b/68266385).
- EXPECT_LE( // boolean 1 or 0 depending on expected content or empty
- !!((strcmp("crash", name) != 0) &&
- ((strcmp("kernel", name) != 0) ||
- __android_logger_property_get_bool(
- "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG |
- BOOL_DEFAULT_FLAG_SVELTE)) &&
- (strcmp("stats", name) != 0)),
- android_logger_get_log_readable_size(logger));
+ // stats buffer is no longer in use.
+ if (name == "crash" || name == "stats") {
+ continue;
+ }
+
+ // kernel buffer is empty if ro.logd.kernel is false
+ if (name == "kernel" && !GetBoolProperty("ro.logd.kernel", false)) {
+ continue;
+ }
+
+ EXPECT_LE(0, android_logger_get_log_readable_size(logger));
} else {
EXPECT_NE(0, get_log_size);
if (get_log_size < 0) {
@@ -71,7 +70,6 @@ TEST(liblog, android_logger_get_) {
EXPECT_LE(0, android_logger_get_log_readable_size(logger));
}
}
- EXPECT_LT(0, android_logger_get_log_version(logger));
}
android_logger_list_close(logger_list);
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index bbdd31778..ceabf626c 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -336,7 +336,6 @@ Modprobe::Modprobe(const std::vector<std::string>& base_paths, const std::string
}
ParseKernelCmdlineOptions();
- android::base::SetMinimumLogSeverity(android::base::INFO);
}
void Modprobe::EnableBlocklist(bool enable) {
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 63d5ce8ff..065b3ede3 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -39,19 +39,21 @@
"Mode": "0755",
"UID": "system",
"GID": "system"
- },
- {
- "Controller": "freezer",
- "Path": "/dev/freezer",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
}
],
"Cgroups2": {
- "Path": "/dev/cg2_bpf",
- "Mode": "0600",
- "UID": "root",
- "GID": "root"
+ "Path": "/sys/fs/cgroup",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system",
+ "Controllers": [
+ {
+ "Controller": "freezer",
+ "Path": "freezer",
+ "Mode": "0755",
+ "UID": "system",
+ "GID": "system"
+ }
+ ]
}
}
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
index f4070c550..13adcae07 100644
--- a/libprocessgroup/profiles/cgroups.proto
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -24,19 +24,24 @@ message Cgroups {
Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
}
-// Next: 6
+// Next: 7
message Cgroup {
string controller = 1 [json_name = "Controller"];
string path = 2 [json_name = "Path"];
string mode = 3 [json_name = "Mode"];
string uid = 4 [json_name = "UID"];
string gid = 5 [json_name = "GID"];
+// Booleans default to false when not specified. File reconstruction fails
+// when a boolean is specified as false, so leave unspecified in that case
+// https://developers.google.com/protocol-buffers/docs/proto3#default
+ bool needs_activation = 6 [json_name = "NeedsActivation"];
}
-// Next: 5
+// Next: 6
message Cgroups2 {
string path = 1 [json_name = "Path"];
string mode = 2 [json_name = "Mode"];
string uid = 3 [json_name = "UID"];
string gid = 4 [json_name = "GID"];
+ repeated Cgroup controllers = 5 [json_name = "Controllers"];
}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 77a809952..58a256bf1 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -59,7 +59,7 @@
{
"Name": "FreezerState",
"Controller": "freezer",
- "File": "frozen/freezer.state"
+ "File": "cgroup.freeze"
}
],
@@ -85,7 +85,7 @@
"Params":
{
"Controller": "freezer",
- "Path": "frozen"
+ "Path": ""
}
}
]
@@ -98,7 +98,7 @@
"Params":
{
"Controller": "freezer",
- "Path": ""
+ "Path": "../"
}
}
]
@@ -597,27 +597,27 @@
]
},
{
- "Name": "FreezerThawed",
+ "Name": "FreezerDisabled",
"Actions": [
{
"Name": "SetAttribute",
"Params":
{
"Name": "FreezerState",
- "Value": "THAWED"
+ "Value": "0"
}
}
]
},
{
- "Name": "FreezerFrozen",
+ "Name": "FreezerEnabled",
"Actions": [
{
"Name": "SetAttribute",
"Params":
{
"Name": "FreezerState",
- "Value": "FROZEN"
+ "Value": "1"
}
}
]
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 135904bd0..bf06bbc63 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -6,6 +6,7 @@ cc_library {
ramdisk_available: true,
recovery_available: true,
unique_host_soname: true,
+ vendor_available: true,
srcs: [
"backed_block.cpp",
"output_file.cpp",
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
index f3d8022ac..6229e7c6e 100644
--- a/libsparse/backed_block.cpp
+++ b/libsparse/backed_block.cpp
@@ -25,7 +25,7 @@
struct backed_block {
unsigned int block;
- unsigned int len;
+ uint64_t len;
enum backed_block_type type;
union {
struct {
@@ -60,7 +60,7 @@ struct backed_block* backed_block_iter_next(struct backed_block* bb) {
return bb->next;
}
-unsigned int backed_block_len(struct backed_block* bb) {
+uint64_t backed_block_len(struct backed_block* bb) {
return bb->len;
}
@@ -270,7 +270,7 @@ static int queue_bb(struct backed_block_list* bbl, struct backed_block* new_bb)
}
/* Queues a fill block of memory to be written to the specified data blocks */
-int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, uint64_t len,
unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
@@ -287,7 +287,7 @@ int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val,
}
/* Queues a block of memory to be written to the specified data blocks */
-int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+int backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t len,
unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
@@ -305,7 +305,7 @@ int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned in
/* Queues a chunk of a file on disk to be written to the specified data blocks */
int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
- unsigned int len, unsigned int block) {
+ uint64_t len, unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
return -ENOMEM;
@@ -322,7 +322,7 @@ int backed_block_add_file(struct backed_block_list* bbl, const char* filename, i
}
/* Queues a chunk of a fd to be written to the specified data blocks */
-int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, uint64_t len,
unsigned int block) {
struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));
if (bb == nullptr) {
diff --git a/libsparse/backed_block.h b/libsparse/backed_block.h
index 3a7546065..71a89698e 100644
--- a/libsparse/backed_block.h
+++ b/libsparse/backed_block.h
@@ -29,18 +29,18 @@ enum backed_block_type {
BACKED_BLOCK_FILL,
};
-int backed_block_add_data(struct backed_block_list* bbl, void* data, unsigned int len,
+int backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t len,
unsigned int block);
-int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, unsigned int len,
+int backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, uint64_t len,
unsigned int block);
int backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,
- unsigned int len, unsigned int block);
-int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, unsigned int len,
+ uint64_t len, unsigned int block);
+int backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, uint64_t len,
unsigned int block);
struct backed_block* backed_block_iter_new(struct backed_block_list* bbl);
struct backed_block* backed_block_iter_next(struct backed_block* bb);
-unsigned int backed_block_len(struct backed_block* bb);
+uint64_t backed_block_len(struct backed_block* bb);
unsigned int backed_block_block(struct backed_block* bb);
void* backed_block_data(struct backed_block* bb);
const char* backed_block_filename(struct backed_block* bb);
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 3d5fb0c53..2f75349a6 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -75,8 +75,7 @@ void sparse_file_destroy(struct sparse_file *s);
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_data(struct sparse_file *s,
- void *data, unsigned int len, unsigned int block);
+int sparse_file_add_data(struct sparse_file* s, void* data, uint64_t len, unsigned int block);
/**
* sparse_file_add_fill - associate a fill chunk with a sparse file
@@ -93,8 +92,8 @@ int sparse_file_add_data(struct sparse_file *s,
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_fill(struct sparse_file *s,
- uint32_t fill_val, unsigned int len, unsigned int block);
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, uint64_t len,
+ unsigned int block);
/**
* sparse_file_add_file - associate a chunk of a file with a sparse file
@@ -116,9 +115,8 @@ int sparse_file_add_fill(struct sparse_file *s,
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_file(struct sparse_file *s,
- const char *filename, int64_t file_offset, unsigned int len,
- unsigned int block);
+int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
+ uint64_t len, unsigned int block);
/**
* sparse_file_add_file - associate a chunk of a file with a sparse file
@@ -143,8 +141,8 @@ int sparse_file_add_file(struct sparse_file *s,
*
* Returns 0 on success, negative errno on error.
*/
-int sparse_file_add_fd(struct sparse_file *s,
- int fd, int64_t file_offset, unsigned int len, unsigned int block);
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, uint64_t len,
+ unsigned int block);
/**
* sparse_file_write - write a sparse file to a file
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index b883c13f2..b2c5407e1 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -65,9 +65,9 @@ struct output_file_ops {
};
struct sparse_file_ops {
- int (*write_data_chunk)(struct output_file* out, unsigned int len, void* data);
- int (*write_fill_chunk)(struct output_file* out, unsigned int len, uint32_t fill_val);
- int (*write_skip_chunk)(struct output_file* out, int64_t len);
+ int (*write_data_chunk)(struct output_file* out, uint64_t len, void* data);
+ int (*write_fill_chunk)(struct output_file* out, uint64_t len, uint32_t fill_val);
+ int (*write_skip_chunk)(struct output_file* out, uint64_t len);
int (*write_end_chunk)(struct output_file* out);
};
@@ -316,7 +316,7 @@ int read_all(int fd, void* buf, size_t len) {
return 0;
}
-static int write_sparse_skip_chunk(struct output_file* out, int64_t skip_len) {
+static int write_sparse_skip_chunk(struct output_file* out, uint64_t skip_len) {
chunk_header_t chunk_header;
int ret;
@@ -340,9 +340,10 @@ static int write_sparse_skip_chunk(struct output_file* out, int64_t skip_len) {
return 0;
}
-static int write_sparse_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+static int write_sparse_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
chunk_header_t chunk_header;
- int rnd_up_len, count;
+ uint64_t rnd_up_len;
+ int count;
int ret;
/* Round up the fill length to a multiple of the block size */
@@ -370,9 +371,9 @@ static int write_sparse_fill_chunk(struct output_file* out, unsigned int len, ui
return 0;
}
-static int write_sparse_data_chunk(struct output_file* out, unsigned int len, void* data) {
+static int write_sparse_data_chunk(struct output_file* out, uint64_t len, void* data) {
chunk_header_t chunk_header;
- int rnd_up_len, zero_len;
+ uint64_t rnd_up_len, zero_len;
int ret;
/* Round up the data length to a multiple of the block size */
@@ -437,9 +438,9 @@ static struct sparse_file_ops sparse_file_ops = {
.write_end_chunk = write_sparse_end_chunk,
};
-static int write_normal_data_chunk(struct output_file* out, unsigned int len, void* data) {
+static int write_normal_data_chunk(struct output_file* out, uint64_t len, void* data) {
int ret;
- unsigned int rnd_up_len = ALIGN(len, out->block_size);
+ uint64_t rnd_up_len = ALIGN(len, out->block_size);
ret = out->ops->write(out, data, len);
if (ret < 0) {
@@ -453,10 +454,10 @@ static int write_normal_data_chunk(struct output_file* out, unsigned int len, vo
return ret;
}
-static int write_normal_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+static int write_normal_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
int ret;
unsigned int i;
- unsigned int write_len;
+ uint64_t write_len;
/* Initialize fill_buf with the fill_val */
for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
@@ -464,7 +465,7 @@ static int write_normal_fill_chunk(struct output_file* out, unsigned int len, ui
}
while (len) {
- write_len = std::min(len, out->block_size);
+ write_len = std::min(len, (uint64_t)out->block_size);
ret = out->ops->write(out, out->fill_buf, write_len);
if (ret < 0) {
return ret;
@@ -476,7 +477,7 @@ static int write_normal_fill_chunk(struct output_file* out, unsigned int len, ui
return 0;
}
-static int write_normal_skip_chunk(struct output_file* out, int64_t len) {
+static int write_normal_skip_chunk(struct output_file* out, uint64_t len) {
return out->ops->skip(out, len);
}
@@ -639,16 +640,16 @@ struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t
}
/* Write a contiguous region of data blocks from a memory buffer */
-int write_data_chunk(struct output_file* out, unsigned int len, void* data) {
+int write_data_chunk(struct output_file* out, uint64_t len, void* data) {
return out->sparse_ops->write_data_chunk(out, len, data);
}
/* Write a contiguous region of data blocks with a fill value */
-int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val) {
+int write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {
return out->sparse_ops->write_fill_chunk(out, len, fill_val);
}
-int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset) {
+int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
auto m = android::base::MappedFile::FromFd(fd, offset, len, PROT_READ);
if (!m) return -errno;
@@ -656,7 +657,7 @@ int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t of
}
/* Write a contiguous region of data blocks from a file */
-int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset) {
+int write_file_chunk(struct output_file* out, uint64_t len, const char* file, int64_t offset) {
int ret;
int file_fd = open(file, O_RDONLY | O_BINARY);
@@ -671,6 +672,6 @@ int write_file_chunk(struct output_file* out, unsigned int len, const char* file
return ret;
}
-int write_skip_chunk(struct output_file* out, int64_t len) {
+int write_skip_chunk(struct output_file* out, uint64_t len) {
return out->sparse_ops->write_skip_chunk(out, len);
}
diff --git a/libsparse/output_file.h b/libsparse/output_file.h
index 278430b6f..ecbcdf308 100644
--- a/libsparse/output_file.h
+++ b/libsparse/output_file.h
@@ -30,11 +30,11 @@ struct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t
struct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,
unsigned int block_size, int64_t len, int gz,
int sparse, int chunks, int crc);
-int write_data_chunk(struct output_file* out, unsigned int len, void* data);
-int write_fill_chunk(struct output_file* out, unsigned int len, uint32_t fill_val);
-int write_file_chunk(struct output_file* out, unsigned int len, const char* file, int64_t offset);
-int write_fd_chunk(struct output_file* out, unsigned int len, int fd, int64_t offset);
-int write_skip_chunk(struct output_file* out, int64_t len);
+int write_data_chunk(struct output_file* out, uint64_t len, void* data);
+int write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val);
+int write_file_chunk(struct output_file* out, uint64_t len, const char* file, int64_t offset);
+int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset);
+int write_skip_chunk(struct output_file* out, uint64_t len);
void output_file_close(struct output_file* out);
int read_all(int fd, void* buf, size_t len);
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index 8622b4c39..396e7eb0c 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -50,21 +50,21 @@ void sparse_file_destroy(struct sparse_file* s) {
free(s);
}
-int sparse_file_add_data(struct sparse_file* s, void* data, unsigned int len, unsigned int block) {
+int sparse_file_add_data(struct sparse_file* s, void* data, uint64_t len, unsigned int block) {
return backed_block_add_data(s->backed_block_list, data, len, block);
}
-int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, unsigned int len,
+int sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, uint64_t len,
unsigned int block) {
return backed_block_add_fill(s->backed_block_list, fill_val, len, block);
}
int sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,
- unsigned int len, unsigned int block) {
+ uint64_t len, unsigned int block) {
return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);
}
-int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, unsigned int len,
+int sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, uint64_t len,
unsigned int block) {
return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);
}
diff --git a/libstats/pull/Android.bp b/libstats/pull/Android.bp
index a8b4a4faf..8a6cab8c9 100644
--- a/libstats/pull/Android.bp
+++ b/libstats/pull/Android.bp
@@ -30,18 +30,39 @@ cc_defaults {
shared_libs: [
"libbinder_ndk",
"liblog",
- "libstatssocket",
],
static_libs: [
"libutils",
"statsd-aidl-ndk_platform",
],
+ target: {
+ android: {
+ shared_libs: ["libstatssocket"],
+ },
+ host: {
+ static_libs: ["libstatssocket"],
+ },
+ },
}
-cc_library_shared {
+cc_library {
name: "libstatspull",
defaults: [
- "libstatspull_defaults"
+ "libstatspull_defaults",
+ "libbinder_ndk_host_user",
],
+ host_supported: true,
+ target: {
+ android: {
+ static: {
+ enabled: false,
+ },
+ },
+ host: {
+ shared: {
+ enabled: false,
+ },
+ },
+ },
// enumerate stable entry points for APEX use
stubs: {
symbol_file: "libstatspull.map.txt",
@@ -68,6 +89,9 @@ cc_library_static {
defaults: [
"libstatspull_defaults",
],
+ cflags: [
+ "-DLIB_STATS_PULL_TESTS_FLAG",
+ ],
visibility: [
"//frameworks/base/apex/statsd/tests/libstatspull",
],
diff --git a/libstats/pull/stats_pull_atom_callback.cpp b/libstats/pull/stats_pull_atom_callback.cpp
index 478cae760..2d924a5f3 100644
--- a/libstats/pull/stats_pull_atom_callback.cpp
+++ b/libstats/pull/stats_pull_atom_callback.cpp
@@ -120,6 +120,9 @@ class StatsPullAtomCallbackInternal : public BnPullAtomCallback {
// Convert stats_events into StatsEventParcels.
std::vector<StatsEventParcel> parcels;
+
+ // Resolves fuzz build failure in b/161575591.
+#if defined(__ANDROID_APEX__) || defined(LIB_STATS_PULL_TESTS_FLAG)
for (int i = 0; i < statsEventList.data.size(); i++) {
size_t size;
uint8_t* buffer = AStatsEvent_getBuffer(statsEventList.data[i], &size);
@@ -130,6 +133,7 @@ class StatsPullAtomCallbackInternal : public BnPullAtomCallback {
p.buffer.assign(buffer, buffer + size);
parcels.push_back(std::move(p));
}
+#endif
Status status = resultReceiver->pullFinished(atomTag, success, parcels);
if (!status.isOk()) {
diff --git a/libstats/socket/Android.bp b/libstats/socket/Android.bp
index bf79ea244..89cdfe52a 100644
--- a/libstats/socket/Android.bp
+++ b/libstats/socket/Android.bp
@@ -29,6 +29,7 @@ cc_defaults {
static_libs: [
"libcutils", // does not expose a stable C API
],
+ header_libs: ["liblog_headers"],
cflags: [
"-Wall",
"-Werror",
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 9c1621bc8..3b6cfd830 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -185,7 +185,6 @@ bool NetlinkEvent::parseIfAddrMessage(const struct nlmsghdr *nh) {
if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))
return false;
- // Sanity check.
int type = nh->nlmsg_type;
if (type != RTM_NEWADDR && type != RTM_DELADDR) {
SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type);
@@ -349,7 +348,6 @@ bool NetlinkEvent::parseRtMessage(const struct nlmsghdr *nh) {
uint8_t type = nh->nlmsg_type;
const char *msgname = rtMessageName(type);
- // Sanity check.
if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
SLOGE("%s: incorrect message type %d (%s)\n", __func__, type, msgname);
return false;
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index fe2f3d668..e90afcd18 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -201,50 +201,31 @@ int SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) {
return 0;
}
- int ret = 0;
- int e = 0; // SLOGW and sigaction are not inert regarding errno
int current = 0;
- struct sigaction new_action, old_action;
- memset(&new_action, 0, sizeof(new_action));
- new_action.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &new_action, &old_action);
-
for (;;) {
- ssize_t rc = TEMP_FAILURE_RETRY(
- writev(mSocket, iov + current, iovcnt - current));
-
- if (rc > 0) {
- size_t written = rc;
- while ((current < iovcnt) && (written >= iov[current].iov_len)) {
- written -= iov[current].iov_len;
- current++;
- }
- if (current == iovcnt) {
- break;
- }
- iov[current].iov_base = (char *)iov[current].iov_base + written;
- iov[current].iov_len -= written;
- continue;
- }
+ ssize_t rc = TEMP_FAILURE_RETRY(writev(mSocket, iov + current, iovcnt - current));
if (rc == 0) {
- e = EIO;
+ errno = EIO;
SLOGW("0 length write :(");
- } else {
- e = errno;
- SLOGW("write error (%s)", strerror(e));
+ return -1;
+ } else if (rc < 0) {
+ SLOGW("write error (%s)", strerror(errno));
+ return -1;
}
- ret = -1;
- break;
- }
- sigaction(SIGPIPE, &old_action, &new_action);
-
- if (e != 0) {
- errno = e;
+ size_t written = rc;
+ while (current < iovcnt && written >= iov[current].iov_len) {
+ written -= iov[current].iov_len;
+ current++;
+ }
+ if (current == iovcnt) {
+ return 0;
+ }
+ iov[current].iov_base = (char*)iov[current].iov_base + written;
+ iov[current].iov_len -= written;
}
- return ret;
}
void SocketClient::incRef() {
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 9e2a3cda7..bf86e6e66 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -465,13 +465,9 @@ bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint3
eval_info->return_address_undefined = true;
}
break;
- case DWARF_LOCATION_PSEUDO_REGISTER: {
- if (!eval_info->regs_info.regs->SetPseudoRegister(reg, loc->values[0])) {
- last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
- return false;
- }
- break;
- }
+ case DWARF_LOCATION_PSEUDO_REGISTER:
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
default:
break;
}
@@ -543,11 +539,15 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
// Skip this unknown register.
continue;
}
- }
-
- reg_ptr = eval_info.regs_info.Save(reg);
- if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
- return false;
+ if (!eval_info.regs_info.regs->SetPseudoRegister(reg, entry.second.values[0])) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ } else {
+ reg_ptr = eval_info.regs_info.Save(reg);
+ if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
+ return false;
+ }
}
}
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index c9e245d2f..26d9f6578 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -141,15 +141,14 @@ bool RegsX86_64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* proc
return false;
}
- uint16_t data2;
- if (!elf_memory->ReadFully(elf_offset + 8, &data2, sizeof(data2)) || data2 != 0x0f05) {
+ uint8_t data2;
+ if (!elf_memory->ReadFully(elf_offset + 8, &data2, sizeof(data2)) || data2 != 0x05) {
return false;
}
// __restore_rt:
// 0x48 0xc7 0xc0 0x0f 0x00 0x00 0x00 mov $0xf,%rax
// 0x0f 0x05 syscall
- // 0x0f nopl 0x0($rax)
// Read the mcontext data from the stack.
// sp points to the ucontext data structure, read only the mcontext part.
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 2d867cd5e..57806c157 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -75,6 +75,7 @@ void Unwinder::FillInDexFrame() {
frame->rel_pc = dex_pc - info->start;
} else {
frame->rel_pc = dex_pc;
+ warnings_ |= WARNING_DEX_PC_NOT_IN_MAP;
return;
}
@@ -142,6 +143,7 @@ static bool ShouldStop(const std::vector<std::string>* map_suffixes_to_ignore,
void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
const std::vector<std::string>* map_suffixes_to_ignore) {
frames_.clear();
+ warnings_ = WARNING_NONE;
last_error_.code = ERROR_NONE;
last_error_.address = 0;
elf_from_memory_not_file_ = false;
@@ -395,18 +397,20 @@ bool UnwinderFromPid::Init(ArchEnum arch) {
return true;
}
-FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc) {
+FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc, ArchEnum arch, Maps* maps,
+ JitDebug* jit_debug,
+ std::shared_ptr<Memory> process_memory,
+ bool resolve_names) {
FrameData frame;
- Maps* maps = GetMaps();
MapInfo* map_info = maps->Find(pc);
- if (!map_info) {
+ if (map_info == nullptr || arch == ARCH_UNKNOWN) {
+ frame.pc = pc;
frame.rel_pc = pc;
return frame;
}
- ArchEnum arch = Regs::CurrentArch();
- Elf* elf = map_info->GetElf(GetProcessMemory(), arch);
+ Elf* elf = map_info->GetElf(process_memory, arch);
uint64_t relative_pc = elf->GetRelPc(pc, map_info);
@@ -416,10 +420,9 @@ FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc) {
uint64_t debug_pc = relative_pc;
// If we don't have a valid ELF file, check the JIT.
- if (!elf->valid()) {
- JitDebug jit_debug(GetProcessMemory());
+ if (!elf->valid() && jit_debug != nullptr) {
uint64_t jit_pc = pc - pc_adjustment;
- Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
+ Elf* jit_elf = jit_debug->GetElf(maps, jit_pc);
if (jit_elf != nullptr) {
debug_pc = jit_pc;
elf = jit_elf;
@@ -437,12 +440,17 @@ FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc) {
frame.map_flags = map_info->flags;
frame.map_load_bias = elf->GetLoadBias();
- if (!resolve_names_ ||
- !elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
+ if (!resolve_names ||
+ !elf->GetFunctionName(debug_pc, &frame.function_name, &frame.function_offset)) {
frame.function_name = "";
frame.function_offset = 0;
}
return frame;
}
+FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc) {
+ return BuildFrameFromPcOnly(pc, regs_ ? regs_->Arch() : ARCH_UNKNOWN, maps_, jit_debug_,
+ process_memory_, resolve_names_);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Error.h b/libunwindstack/include/unwindstack/Error.h
index 72ec4547f..66fefe76d 100644
--- a/libunwindstack/include/unwindstack/Error.h
+++ b/libunwindstack/include/unwindstack/Error.h
@@ -21,6 +21,13 @@
namespace unwindstack {
+// A bit map of warnings, multiple warnings can be set at the same time.
+enum WarningCode : uint64_t {
+ WARNING_NONE = 0,
+ WARNING_DEX_PC_NOT_IN_MAP = 0x1, // A dex pc was found, but it doesn't exist
+ // in any valid map.
+};
+
enum ErrorCode : uint8_t {
ERROR_NONE, // No error.
ERROR_MEMORY_INVALID, // Memory read failed.
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 4d49f236a..3df8aadfd 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -113,12 +113,15 @@ class Unwinder {
ErrorCode LastErrorCode() { return last_error_.code; }
uint64_t LastErrorAddress() { return last_error_.address; }
+ uint64_t warnings() { return warnings_; }
// Builds a frame for symbolization using the maps from this unwinder. The
// constructed frame contains just enough information to be used to symbolize
// frames collected by frame-pointer unwinding that's done outside of
// libunwindstack. This is used by tombstoned to symbolize frame pointer-based
// stack traces that are collected by tools such as GWP-ASan and MTE.
+ static FrameData BuildFrameFromPcOnly(uint64_t pc, ArchEnum arch, Maps* maps, JitDebug* jit_debug,
+ std::shared_ptr<Memory> process_memory, bool resolve_names);
FrameData BuildFrameFromPcOnly(uint64_t pc);
protected:
@@ -141,6 +144,7 @@ class Unwinder {
// file. This is only true if there is an actual file backing up the elf.
bool elf_from_memory_not_file_ = false;
ErrorData last_error_;
+ uint64_t warnings_;
};
class UnwinderFromPid : public Unwinder {
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index d57cd339d..a08a8d070 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -492,6 +492,40 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
EXPECT_EQ(0x80000000U, regs.pc());
}
+TYPED_TEST_P(DwarfSectionImplTest, Eval_pseudo_register_invalid) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ regs.set_pseudo_reg(11);
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_PSEUDO_REGISTER, {20, 0}};
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+
+ loc_regs.clear();
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[12] = DwarfLocation{DWARF_LOCATION_PSEUDO_REGISTER, {20, 0}};
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_pseudo_register) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10);
+ regs.set_pseudo_reg(11);
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[11] = DwarfLocation{DWARF_LOCATION_PSEUDO_REGISTER, {20, 0}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, &regs, &finished));
+ uint64_t pseudo_value = 0;
+ ASSERT_TRUE(regs.GetPseudoRegister(11, &pseudo_value));
+ EXPECT_EQ(20U, pseudo_value);
+}
+
TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
DwarfCie cie{};
cie.cfa_instructions_offset = 0x3000;
@@ -581,6 +615,7 @@ REGISTER_TYPED_TEST_SUITE_P(DwarfSectionImplTest, GetCieFromOffset_fail_should_n
Eval_invalid_register, Eval_different_reg_locations,
Eval_return_address_undefined, Eval_pc_zero, Eval_return_address,
Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
+ Eval_pseudo_register_invalid, Eval_pseudo_register,
GetCfaLocationInfo_cie_not_cached, GetCfaLocationInfo_cie_cached, Log);
typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index 75fc9d02d..f67d7dc61 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -83,15 +83,33 @@ class RegsImplFake : public RegsImpl<TypeParam> {
uint64_t sp() override { return fake_sp_; }
void set_pc(uint64_t pc) override { fake_pc_ = pc; }
void set_sp(uint64_t sp) override { fake_sp_ = sp; }
+ void set_pseudo_reg(uint64_t reg) { fake_pseudo_reg_ = reg; }
bool SetPcFromReturnAddress(Memory*) override { return false; }
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+ bool SetPseudoRegister(uint16_t reg, uint64_t value) override {
+ if (fake_pseudo_reg_ != reg) {
+ return false;
+ }
+ fake_pseudo_reg_value_ = value;
+ return true;
+ }
+ bool GetPseudoRegister(uint16_t reg, uint64_t* value) override {
+ if (fake_pseudo_reg_ != reg) {
+ return false;
+ }
+ *value = fake_pseudo_reg_value_;
+ return true;
+ }
+
Regs* Clone() override { return nullptr; }
private:
uint64_t fake_pc_ = 0;
uint64_t fake_sp_ = 0;
+ uint16_t fake_pseudo_reg_ = 0;
+ uint64_t fake_pseudo_reg_value_ = 0;
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index dd33aa9d7..915f24884 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -37,6 +37,7 @@
#include <unwindstack/Unwinder.h>
#include "ElfFake.h"
+#include "ElfTestUtils.h"
#include "MemoryFake.h"
#include "RegsFake.h"
@@ -44,23 +45,31 @@ namespace unwindstack {
class UnwinderTest : public ::testing::Test {
protected:
- static void AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
- const char* name, Elf* elf = nullptr) {
+ static MapInfo* AddMapInfo(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags,
+ const char* name, Elf* elf = nullptr) {
std::string str_name(name);
maps_->Add(start, end, offset, flags, name, static_cast<uint64_t>(-1));
+ MapInfo* map_info = maps_->Find(start);
if (elf != nullptr) {
- const auto& map_info = *--maps_->end();
map_info->elf.reset(elf);
}
+ return map_info;
}
static void SetUpTestSuite() {
maps_.reset(new Maps);
- ElfFake* elf = new ElfFake(new MemoryFake);
- ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr);
- interface_fake->FakeSetBuildID("FAKE");
- elf->FakeSetInterface(interface_fake);
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+
+ ElfFake* elf;
+ ElfInterfaceFake* interface;
+ MapInfo* map_info;
+
+ elf = new ElfFake(new MemoryFake);
+ interface = new ElfInterfaceFake(nullptr);
+ interface->FakeSetBuildID("FAKE");
+ elf->FakeSetInterface(interface);
AddMapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so", elf);
AddMapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]");
@@ -81,19 +90,17 @@ class UnwinderTest : public ::testing::Test {
AddMapInfo(0x33000, 0x34000, 0, PROT_READ | PROT_WRITE, "/fake/compressed.so", elf);
elf = new ElfFake(new MemoryFake);
- ElfInterfaceFake* interface = new ElfInterfaceFake(nullptr);
+ interface = new ElfInterfaceFake(nullptr);
interface->FakeSetSoname("lib_fake.so");
elf->FakeSetInterface(interface);
- AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
- MapInfo* map_info = maps_->Find(0x43000);
- ASSERT_TRUE(map_info != nullptr);
+ map_info = AddMapInfo(0x43000, 0x44000, 0x1d000, PROT_READ | PROT_WRITE, "/fake/fake.apk", elf);
map_info->elf_start_offset = 0x1d000;
AddMapInfo(0x53000, 0x54000, 0, PROT_READ | PROT_WRITE, "/fake/fake.oat");
- AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
- const auto& info = *--maps_->end();
- info->load_bias = 0;
+ map_info =
+ AddMapInfo(0xa3000, 0xa4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.vdex");
+ map_info->load_bias = 0;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
@@ -103,40 +110,76 @@ class UnwinderTest : public ::testing::Test {
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake_offset.oat",
- elf);
- const auto& info2 = *--maps_->end();
- info2->elf_offset = 0x8000;
+ map_info = AddMapInfo(0xa7000, 0xa8000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/fake/fake_offset.oat", elf);
+ map_info->elf_offset = 0x8000;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- AddMapInfo(0xc0000, 0xc1000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/unreadable.so", elf);
- const auto& info3 = *--maps_->end();
- info3->memory_backed_elf = true;
+ map_info = AddMapInfo(0xc0000, 0xc1000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/fake/unreadable.so", elf);
+ map_info->memory_backed_elf = true;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- AddMapInfo(0xc1000, 0xc2000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "[vdso]", elf);
- const auto& info4 = *--maps_->end();
- info4->memory_backed_elf = true;
+ map_info = AddMapInfo(0xc1000, 0xc2000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "[vdso]", elf);
+ map_info->memory_backed_elf = true;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- AddMapInfo(0xc2000, 0xc3000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "", elf);
- const auto& info5 = *--maps_->end();
- info5->memory_backed_elf = true;
+ map_info = AddMapInfo(0xc2000, 0xc3000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "", elf);
+ map_info->memory_backed_elf = true;
elf = new ElfFake(new MemoryFake);
elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
- AddMapInfo(0xc3000, 0xc4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/memfd:/jit-cache", elf);
- const auto& info6 = *--maps_->end();
- info6->memory_backed_elf = true;
+ map_info = AddMapInfo(0xc3000, 0xc4000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/memfd:/jit-cache", elf);
+ map_info->memory_backed_elf = true;
- AddMapInfo(0xd0000, 0xd1000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.apk");
- const auto& info7 = *--maps_->end();
- info7->load_bias = 0;
+ map_info =
+ AddMapInfo(0xd0000, 0xd1000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/fake.apk");
+ map_info->load_bias = 0;
- process_memory_.reset(new MemoryFake);
+ elf = new ElfFake(new MemoryFake);
+ interface = new ElfInterfaceFake(nullptr);
+ elf->FakeSetInterface(interface);
+ interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x1800);
+ interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x1900);
+ interface->FakeSetDataOffset(0x1000);
+ interface->FakeSetDataVaddrStart(0x1000);
+ interface->FakeSetDataVaddrEnd(0x8000);
+ AddMapInfo(0xf0000, 0xf1000, 0, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/global.so", elf);
+ AddMapInfo(0xf1000, 0xf9000, 0x1000, PROT_READ | PROT_WRITE, "/fake/global.so");
+ // dex debug data
+ memory_->SetData32(0xf180c, 0xf3000);
+ memory_->SetData32(0xf3000, 0xf4000);
+ memory_->SetData32(0xf3004, 0xf4000);
+ memory_->SetData32(0xf3008, 0xf5000);
+ // jit debug data
+ memory_->SetData32(0xf1900, 1);
+ memory_->SetData32(0xf1904, 0);
+ memory_->SetData32(0xf1908, 0xf6000);
+ memory_->SetData32(0xf190c, 0xf6000);
+ memory_->SetData32(0xf6000, 0);
+ memory_->SetData32(0xf6004, 0);
+ memory_->SetData32(0xf6008, 0xf7000);
+ memory_->SetData32(0xf600c, 0);
+ memory_->SetData64(0xf6010, 0x1000);
+
+ elf = new ElfFake(new MemoryFake);
+ elf->FakeSetValid(false);
+ elf->FakeSetLoadBias(0x300);
+ map_info = AddMapInfo(0x100000, 0x101000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
+ "/fake/jit.so", elf);
+ map_info->elf_start_offset = 0x100;
+ map_info->offset = 0x200;
+
+#if 0
+ elf = new ElfFake(new MemoryFake);
+ interface = new ElfInterfaceFake(nullptr);
+ interface->FakePushFunctionData(FunctionData("Fake0", 10));
+ AddMapInfo(0x110000, 0x111000, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, "/fake/elf.so", elf);
+#endif
}
void SetUp() override {
@@ -147,11 +190,13 @@ class UnwinderTest : public ::testing::Test {
static std::unique_ptr<Maps> maps_;
static RegsFake regs_;
+ static MemoryFake* memory_;
static std::shared_ptr<Memory> process_memory_;
};
std::unique_ptr<Maps> UnwinderTest::maps_;
RegsFake UnwinderTest::regs_(5);
+MemoryFake* UnwinderTest::memory_;
std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
TEST_F(UnwinderTest, multiple_frames) {
@@ -168,6 +213,7 @@ TEST_F(UnwinderTest, multiple_frames) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -233,6 +279,7 @@ TEST_F(UnwinderTest, multiple_frames_dont_resolve_names) {
unwinder.SetResolveNames(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -293,6 +340,7 @@ TEST_F(UnwinderTest, non_zero_load_bias) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -323,6 +371,7 @@ TEST_F(UnwinderTest, non_zero_elf_offset) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -353,6 +402,7 @@ TEST_F(UnwinderTest, non_zero_map_offset) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -384,6 +434,7 @@ TEST_F(UnwinderTest, disable_embedded_soname) {
unwinder.SetEmbeddedSoname(false);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -421,6 +472,7 @@ TEST_F(UnwinderTest, no_frames_after_finished) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -454,6 +506,7 @@ TEST_F(UnwinderTest, max_frames) {
Unwinder unwinder(20, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(20U, unwinder.NumFrames());
@@ -497,6 +550,7 @@ TEST_F(UnwinderTest, verify_frames_skipped) {
std::vector<std::string> skip_libs{"libunwind.so", "libanother.so"};
unwinder.Unwind(&skip_libs);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -559,6 +613,7 @@ TEST_F(UnwinderTest, sp_not_in_map) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -607,6 +662,7 @@ TEST_F(UnwinderTest, pc_in_device_stops_unwind) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -627,6 +683,7 @@ TEST_F(UnwinderTest, sp_in_device_stops_unwind) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -642,6 +699,7 @@ TEST_F(UnwinderTest, pc_without_map) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -679,6 +737,7 @@ TEST_F(UnwinderTest, speculative_frame) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -745,6 +804,7 @@ TEST_F(UnwinderTest, speculative_frame_removed) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_INVALID_MAP, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -795,6 +855,7 @@ TEST_F(UnwinderTest, speculative_frame_not_removed_pc_bad) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -843,6 +904,7 @@ TEST_F(UnwinderTest, speculative_frame_check_with_no_frames) {
std::vector<std::string> skip_names{"libanother.so"};
unwinder.Unwind(&skip_names);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(0U, unwinder.NumFrames());
@@ -866,6 +928,7 @@ TEST_F(UnwinderTest, map_ignore_suffixes) {
std::vector<std::string> suffixes{"oat"};
unwinder.Unwind(nullptr, &suffixes);
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -925,6 +988,7 @@ TEST_F(UnwinderTest, sp_pc_do_not_change) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_REPEATED_FRAME, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -984,6 +1048,7 @@ TEST_F(UnwinderTest, dex_pc_in_map) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1028,6 +1093,7 @@ TEST_F(UnwinderTest, dex_pc_in_map_non_zero_offset) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1072,6 +1138,54 @@ TEST_F(UnwinderTest, dex_pc_not_in_map) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
+ EXPECT_FALSE(unwinder.elf_from_memory_not_file());
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x50000U, frame->rel_pc);
+ EXPECT_EQ(0x50000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0U, frame->map_start);
+ EXPECT_EQ(0U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(0, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_elf_start_offset);
+ EXPECT_EQ(0U, frame->map_exact_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+TEST_F(UnwinderTest, dex_pc_not_in_map_valid_dex_files) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
+ regs_.FakeSetDexPc(0x50000);
+
+ DexFiles dex_files(process_memory_);
+ Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
+ unwinder.SetDexFiles(&dex_files, ARCH_ARM);
+ unwinder.Unwind();
+ EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_DEX_PC_NOT_IN_MAP, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(2U, unwinder.NumFrames());
@@ -1119,6 +1233,7 @@ TEST_F(UnwinderTest, dex_pc_multiple_frames) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(3U, unwinder.NumFrames());
@@ -1178,6 +1293,7 @@ TEST_F(UnwinderTest, dex_pc_max_frames) {
Unwinder unwinder(1, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_MAX_FRAMES_EXCEEDED, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1208,6 +1324,7 @@ TEST_F(UnwinderTest, elf_from_memory_not_file) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_TRUE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1238,6 +1355,7 @@ TEST_F(UnwinderTest, elf_from_memory_but_no_valid_file_with_bracket) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1268,6 +1386,7 @@ TEST_F(UnwinderTest, elf_from_memory_but_empty_filename) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1298,6 +1417,7 @@ TEST_F(UnwinderTest, elf_from_memory_but_from_memfd) {
Unwinder unwinder(64, maps_.get(), &regs_, process_memory_);
unwinder.Unwind();
EXPECT_EQ(ERROR_NONE, unwinder.LastErrorCode());
+ EXPECT_EQ(WARNING_NONE, unwinder.warnings());
EXPECT_FALSE(unwinder.elf_from_memory_not_file());
ASSERT_EQ(1U, unwinder.NumFrames());
@@ -1474,4 +1594,161 @@ TEST_F(UnwinderTest, format_frame_by_arch) {
}
}
+TEST_F(UnwinderTest, build_frame_pc_only_errors) {
+ RegsFake regs(10);
+ regs.FakeSetArch(ARCH_ARM);
+ Unwinder unwinder(10, maps_.get(), &regs, process_memory_);
+
+ FrameData frame;
+
+ // Pc not in map
+ frame = unwinder.BuildFrameFromPcOnly(0x10);
+ EXPECT_EQ(0x10U, frame.pc);
+ EXPECT_EQ(0x10U, frame.rel_pc);
+
+ // No regs set
+ unwinder.SetRegs(nullptr);
+ frame = unwinder.BuildFrameFromPcOnly(0x100310);
+ EXPECT_EQ(0x100310U, frame.pc);
+ EXPECT_EQ(0x100310U, frame.rel_pc);
+ unwinder.SetRegs(&regs);
+
+ // Invalid elf
+ frame = unwinder.BuildFrameFromPcOnly(0x100310);
+ EXPECT_EQ(0x10030eU, frame.pc);
+ EXPECT_EQ(0x60eU, frame.rel_pc);
+ EXPECT_EQ("/fake/jit.so", frame.map_name);
+ EXPECT_EQ(0x100U, frame.map_elf_start_offset);
+ EXPECT_EQ(0x200U, frame.map_exact_offset);
+ EXPECT_EQ(0x100000U, frame.map_start);
+ EXPECT_EQ(0x101000U, frame.map_end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_flags);
+ EXPECT_EQ(0x300U, frame.map_load_bias);
+ EXPECT_EQ("", frame.function_name);
+ EXPECT_EQ(0U, frame.function_offset);
+}
+
+TEST_F(UnwinderTest, build_frame_pc_valid_elf) {
+ RegsFake regs(10);
+ regs.FakeSetArch(ARCH_ARM);
+ Unwinder unwinder(10, maps_.get(), &regs, process_memory_);
+
+ FrameData frame;
+
+ // Valid elf, no function data.
+ frame = unwinder.BuildFrameFromPcOnly(0x1010);
+ EXPECT_EQ(0x100cU, frame.pc);
+ EXPECT_EQ(0xcU, frame.rel_pc);
+ EXPECT_EQ("/system/fake/libc.so", frame.map_name);
+ EXPECT_EQ(0U, frame.map_elf_start_offset);
+ EXPECT_EQ(0U, frame.map_exact_offset);
+ EXPECT_EQ(0x1000U, frame.map_start);
+ EXPECT_EQ(0x8000U, frame.map_end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
+ EXPECT_EQ(0U, frame.map_load_bias);
+ EXPECT_EQ("", frame.function_name);
+ EXPECT_EQ(0U, frame.function_offset);
+
+ // Valid elf, function data present, but do not resolve.
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
+ unwinder.SetResolveNames(false);
+
+ frame = unwinder.BuildFrameFromPcOnly(0x1010);
+ EXPECT_EQ(0x100cU, frame.pc);
+ EXPECT_EQ(0xcU, frame.rel_pc);
+ EXPECT_EQ("/system/fake/libc.so", frame.map_name);
+ EXPECT_EQ(0U, frame.map_elf_start_offset);
+ EXPECT_EQ(0U, frame.map_exact_offset);
+ EXPECT_EQ(0x1000U, frame.map_start);
+ EXPECT_EQ(0x8000U, frame.map_end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
+ EXPECT_EQ(0U, frame.map_load_bias);
+ EXPECT_EQ("", frame.function_name);
+ EXPECT_EQ(0U, frame.function_offset);
+
+ // Valid elf, function data present.
+ unwinder.SetResolveNames(true);
+
+ frame = unwinder.BuildFrameFromPcOnly(0x1010);
+ EXPECT_EQ(0x100cU, frame.pc);
+ EXPECT_EQ(0xcU, frame.rel_pc);
+ EXPECT_EQ("/system/fake/libc.so", frame.map_name);
+ EXPECT_EQ(0U, frame.map_elf_start_offset);
+ EXPECT_EQ(0U, frame.map_exact_offset);
+ EXPECT_EQ(0x1000U, frame.map_start);
+ EXPECT_EQ(0x8000U, frame.map_end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame.map_flags);
+ EXPECT_EQ(0U, frame.map_load_bias);
+ EXPECT_EQ("Frame0", frame.function_name);
+ EXPECT_EQ(10U, frame.function_offset);
+}
+
+TEST_F(UnwinderTest, build_frame_pc_in_jit) {
+ // Create the elf data for the jit debug information.
+ Elf32_Ehdr ehdr = {};
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ ehdr.e_phoff = 0x50;
+ ehdr.e_phnum = 1;
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ ehdr.e_shoff = 0x100;
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+ ehdr.e_shnum = 3;
+ memory_->SetMemory(0xf7000, &ehdr, sizeof(ehdr));
+
+ Elf32_Phdr phdr = {};
+ phdr.p_flags = PF_X;
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x100000;
+ phdr.p_vaddr = 0x100000;
+ phdr.p_memsz = 0x1000;
+ memory_->SetMemory(0xf7050, &phdr, sizeof(phdr));
+
+ Elf32_Shdr shdr = {};
+ shdr.sh_type = SHT_NULL;
+ memory_->SetMemory(0xf7100, &shdr, sizeof(shdr));
+
+ shdr.sh_type = SHT_SYMTAB;
+ shdr.sh_link = 2;
+ shdr.sh_addr = 0x300;
+ shdr.sh_offset = 0x300;
+ shdr.sh_entsize = sizeof(Elf32_Sym);
+ shdr.sh_size = shdr.sh_entsize;
+ memory_->SetMemory(0xf7100 + sizeof(shdr), &shdr, sizeof(shdr));
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 0x500;
+ shdr.sh_offset = 0x400;
+ shdr.sh_size = 0x100;
+ memory_->SetMemory(0xf7100 + 2 * sizeof(shdr), &shdr, sizeof(shdr));
+
+ Elf32_Sym sym = {};
+ sym.st_shndx = 2;
+ sym.st_info = STT_FUNC;
+ sym.st_value = 0x100300;
+ sym.st_size = 0x100;
+ memory_->SetMemory(0xf7300, &sym, sizeof(sym));
+ memory_->SetMemory(0xf7400, "FakeJitFunction");
+
+ RegsFake regs(10);
+ regs.FakeSetArch(ARCH_ARM);
+ JitDebug jit_debug(process_memory_);
+ Unwinder unwinder(10, maps_.get(), &regs, process_memory_);
+ unwinder.SetJitDebug(&jit_debug, ARCH_ARM);
+
+ FrameData frame = unwinder.BuildFrameFromPcOnly(0x100310);
+ EXPECT_EQ(0x10030eU, frame.pc);
+ EXPECT_EQ(0x60eU, frame.rel_pc);
+ EXPECT_EQ("/fake/jit.so", frame.map_name);
+ EXPECT_EQ(0x100U, frame.map_elf_start_offset);
+ EXPECT_EQ(0x200U, frame.map_exact_offset);
+ EXPECT_EQ(0x100000U, frame.map_start);
+ EXPECT_EQ(0x101000U, frame.map_end);
+ EXPECT_EQ(PROT_READ | PROT_WRITE | PROT_EXEC, frame.map_flags);
+ EXPECT_EQ(0U, frame.map_load_bias);
+ EXPECT_EQ("FakeJitFunction", frame.function_name);
+ EXPECT_EQ(0xeU, frame.function_offset);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
index 94f5a73cd..9c5374a5b 100644
--- a/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
+++ b/libunwindstack/tests/fuzz/UnwinderComponentCreator.cpp
@@ -16,6 +16,11 @@
#include "UnwinderComponentCreator.h"
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
std::unique_ptr<Regs> GetRegisters(ArchEnum arch) {
switch (arch) {
case unwindstack::ARCH_ARM: {
@@ -109,13 +114,35 @@ ElfFake* PopulateElfFake(FuzzedDataProvider* data_provider) {
return elf;
}
+static constexpr size_t kPageSize = 4096;
+
+static constexpr uint64_t AlignToPage(uint64_t address) {
+ return (address + kPageSize - 1) & ~(kPageSize - 1);
+}
+
std::unique_ptr<Maps> GetMaps(FuzzedDataProvider* data_provider) {
std::unique_ptr<Maps> maps = std::make_unique<Maps>();
+ std::map<uint64_t, uint64_t> map_ends;
uint8_t entry_count = data_provider->ConsumeIntegralInRange<uint8_t>(0, kMaxMapEntryCount);
for (uint8_t i = 0; i < entry_count; i++) {
- uint64_t start = data_provider->ConsumeIntegral<uint64_t>();
- uint64_t end = data_provider->ConsumeIntegralInRange<uint64_t>(start, UINT64_MAX);
- uint64_t offset = data_provider->ConsumeIntegral<uint64_t>();
+ uint64_t start = AlignToPage(data_provider->ConsumeIntegral<uint64_t>());
+ uint64_t end = AlignToPage(data_provider->ConsumeIntegralInRange<uint64_t>(start, UINT64_MAX));
+ if (start == end) {
+ // It's impossible to see start == end in the real world, so
+ // make sure the map contains at least one page of data.
+ if (__builtin_add_overflow(end, 0x1000, &end)) {
+ continue;
+ }
+ }
+ // Make sure not to add overlapping maps, that is not something that can
+ // happen in the real world.
+ auto entry = map_ends.upper_bound(start);
+ if (entry != map_ends.end() && end > entry->second) {
+ continue;
+ }
+ map_ends[end] = start;
+
+ uint64_t offset = AlignToPage(data_provider->ConsumeIntegral<uint64_t>());
std::string map_info_name = data_provider->ConsumeRandomLengthString(kMaxMapInfoNameLen);
uint8_t flags = PROT_READ | PROT_WRITE;
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 022dedf05..926e3d764 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -132,7 +132,6 @@ cc_library {
"JenkinsHash.cpp",
"NativeHandle.cpp",
"Printer.cpp",
- "PropertyMap.cpp",
"RefBase.cpp",
"SharedBuffer.cpp",
"StopWatch.cpp",
@@ -205,6 +204,7 @@ cc_defaults {
shared_libs: [
"libutils",
"libbase",
+ "liblog",
],
}
@@ -238,6 +238,60 @@ cc_fuzz {
srcs: ["Vector_fuzz.cpp"],
}
+cc_fuzz {
+ name: "libutils_fuzz_printer",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["Printer_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_callstack",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["CallStack_fuzz.cpp"],
+ shared_libs: [
+ "libutilscallstack",
+ ],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_process_callstack",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["ProcessCallStack_fuzz.cpp"],
+ shared_libs: [
+ "libutilscallstack",
+ ],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_stopwatch",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["StopWatch_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_rwlock",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["RWLock_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_refbase",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["RefBase_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_lrucache",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["LruCache_fuzz.cpp"],
+}
+
+cc_fuzz {
+ name: "libutils_fuzz_looper",
+ defaults: ["libutils_fuzz_defaults"],
+ srcs: ["Looper_fuzz.cpp"],
+}
+
cc_test {
name: "libutils_test",
host_supported: true,
diff --git a/libutils/CallStack_fuzz.cpp b/libutils/CallStack_fuzz.cpp
new file mode 100644
index 000000000..e89b5b7ca
--- /dev/null
+++ b/libutils/CallStack_fuzz.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory.h>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/CallStack.h"
+
+static constexpr int MAX_STRING_SIZE = 500;
+static constexpr int MAX_IGNORE_DEPTH = 200;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ size_t ignoreDepth = dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_IGNORE_DEPTH);
+ int logPriority = dataProvider.ConsumeIntegral<int>();
+ pid_t tid = dataProvider.ConsumeIntegral<pid_t>();
+ std::string logTag = dataProvider.ConsumeRandomLengthString(MAX_STRING_SIZE);
+ std::string prefix = dataProvider.ConsumeRandomLengthString(MAX_STRING_SIZE);
+
+ const char* logTagChars = logTag.c_str();
+ const char* prefixChars = prefix.c_str();
+
+ android::CallStack::CallStackUPtr callStack = android::CallStack::getCurrent(ignoreDepth);
+ android::CallStack* callstackPtr = callStack.get();
+ android::CallStack::logStack(logTagChars, callstackPtr,
+ static_cast<android_LogPriority>(logPriority));
+ android::CallStack::stackToString(prefixChars);
+
+ callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
+ callstackPtr->clear();
+ callstackPtr->getCurrent(ignoreDepth);
+ callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
+ callstackPtr->update(ignoreDepth, tid);
+ callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);
+
+ return 0;
+}
diff --git a/libutils/Looper_fuzz.cpp b/libutils/Looper_fuzz.cpp
new file mode 100644
index 000000000..c3ae54ece
--- /dev/null
+++ b/libutils/Looper_fuzz.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/select.h>
+
+#include <iostream>
+
+#include <utils/Looper.h>
+
+#include "Looper_test_pipe.h"
+#include "fuzzer/FuzzedDataProvider.h"
+
+using android::Looper;
+using android::sp;
+
+// We don't want this to bog down fuzzing
+static constexpr int MAX_POLL_DELAY = 50;
+static constexpr int MAX_OPERATIONS = 500;
+
+void doNothing() {}
+void* doNothingPointer = reinterpret_cast<void*>(doNothing);
+
+static int noopCallback(int, int, void*) {
+ return 0;
+}
+
+std::vector<std::function<void(FuzzedDataProvider*, sp<Looper>, Pipe)>> operations = {
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe) -> void {
+ looper->pollOnce(dataProvider->ConsumeIntegralInRange<int>(0, MAX_POLL_DELAY));
+ },
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe) -> void {
+ looper->pollAll(dataProvider->ConsumeIntegralInRange<int>(0, MAX_POLL_DELAY));
+ },
+ // events and callback are nullptr
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+ looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+ dataProvider->ConsumeIntegral<int>(), nullptr, nullptr);
+ },
+ // Events is nullptr
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+ looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+ dataProvider->ConsumeIntegral<int>(), noopCallback, nullptr);
+ },
+ // callback is nullptr
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+ looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+ dataProvider->ConsumeIntegral<int>(), nullptr, doNothingPointer);
+ },
+ // callback and events both set
+ [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {
+ looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),
+ dataProvider->ConsumeIntegral<int>(), noopCallback, doNothingPointer);
+ },
+
+ [](FuzzedDataProvider*, sp<Looper> looper, Pipe) -> void { looper->wake(); },
+ [](FuzzedDataProvider*, sp<Looper>, Pipe pipeObj) -> void { pipeObj.writeSignal(); }};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ Pipe pipeObj;
+ FuzzedDataProvider dataProvider(data, size);
+ sp<Looper> looper = new Looper(dataProvider.ConsumeBool());
+
+ size_t opsRun = 0;
+ while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {
+ uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);
+ operations[op](&dataProvider, looper, pipeObj);
+ }
+ // Clear our pointer
+ looper.clear();
+ return 0;
+}
diff --git a/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index 37bdf0544..34f424b83 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -2,12 +2,13 @@
// Copyright 2010 The Android Open Source Project
//
-#include <utils/Looper.h>
-#include <utils/Timers.h>
-#include <utils/StopWatch.h>
#include <gtest/gtest.h>
-#include <unistd.h>
#include <time.h>
+#include <unistd.h>
+#include <utils/Looper.h>
+#include <utils/StopWatch.h>
+#include <utils/Timers.h>
+#include "Looper_test_pipe.h"
#include <utils/threads.h>
@@ -24,41 +25,6 @@ enum {
MSG_TEST4 = 4,
};
-class Pipe {
-public:
- int sendFd;
- int receiveFd;
-
- Pipe() {
- int fds[2];
- ::pipe(fds);
-
- receiveFd = fds[0];
- sendFd = fds[1];
- }
-
- ~Pipe() {
- if (sendFd != -1) {
- ::close(sendFd);
- }
-
- if (receiveFd != -1) {
- ::close(receiveFd);
- }
- }
-
- status_t writeSignal() {
- ssize_t nWritten = ::write(sendFd, "*", 1);
- return nWritten == 1 ? 0 : -errno;
- }
-
- status_t readSignal() {
- char buf[1];
- ssize_t nRead = ::read(receiveFd, buf, 1);
- return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
- }
-};
-
class DelayedTask : public Thread {
int mDelayMillis;
diff --git a/libutils/Looper_test_pipe.h b/libutils/Looper_test_pipe.h
new file mode 100644
index 000000000..77b7b8b36
--- /dev/null
+++ b/libutils/Looper_test_pipe.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <unistd.h>
+/**
+ * A pipe class for use when testing or fuzzing Looper
+ */
+class Pipe {
+ public:
+ int sendFd;
+ int receiveFd;
+
+ Pipe() {
+ int fds[2];
+ ::pipe(fds);
+
+ receiveFd = fds[0];
+ sendFd = fds[1];
+ }
+
+ ~Pipe() {
+ if (sendFd != -1) {
+ ::close(sendFd);
+ }
+
+ if (receiveFd != -1) {
+ ::close(receiveFd);
+ }
+ }
+
+ android::status_t writeSignal() {
+ ssize_t nWritten = ::write(sendFd, "*", 1);
+ return nWritten == 1 ? 0 : -errno;
+ }
+
+ android::status_t readSignal() {
+ char buf[1];
+ ssize_t nRead = ::read(receiveFd, buf, 1);
+ return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
+ }
+};
diff --git a/libutils/LruCache_fuzz.cpp b/libutils/LruCache_fuzz.cpp
new file mode 100644
index 000000000..f8bacfcbc
--- /dev/null
+++ b/libutils/LruCache_fuzz.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/LruCache.h"
+#include "utils/StrongPointer.h"
+
+typedef android::LruCache<size_t, size_t> FuzzCache;
+
+static constexpr uint32_t MAX_CACHE_ENTRIES = 800;
+
+class NoopRemovedCallback : public android::OnEntryRemoved<size_t, size_t> {
+ public:
+ void operator()(size_t&, size_t&) {
+ // noop
+ }
+};
+
+static NoopRemovedCallback callback;
+
+static const std::vector<std::function<void(FuzzedDataProvider*, FuzzCache*)>> operations = {
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->removeOldest(); },
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->peekOldestValue(); },
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->clear(); },
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->size(); },
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void {
+ android::LruCache<size_t, size_t>::Iterator iter(*cache);
+ while (iter.next()) {
+ iter.key();
+ iter.value();
+ }
+ },
+ [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
+ size_t key = dataProvider->ConsumeIntegral<size_t>();
+ size_t val = dataProvider->ConsumeIntegral<size_t>();
+ cache->put(key, val);
+ },
+ [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
+ size_t key = dataProvider->ConsumeIntegral<size_t>();
+ cache->get(key);
+ },
+ [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {
+ size_t key = dataProvider->ConsumeIntegral<size_t>();
+ cache->remove(key);
+ },
+ [](FuzzedDataProvider*, FuzzCache* cache) -> void {
+ cache->setOnEntryRemovedListener(&callback);
+ }};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ FuzzCache cache(MAX_CACHE_ENTRIES);
+ while (dataProvider.remaining_bytes() > 0) {
+ uint8_t op = dataProvider.ConsumeIntegral<uint8_t>() % operations.size();
+ operations[op](&dataProvider, &cache);
+ }
+
+ return 0;
+}
diff --git a/libutils/Printer_fuzz.cpp b/libutils/Printer_fuzz.cpp
new file mode 100755
index 000000000..0180d41f6
--- /dev/null
+++ b/libutils/Printer_fuzz.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "android-base/file.h"
+#include "android/log.h"
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/Printer.h"
+#include "utils/String8.h"
+static constexpr int MAX_STR_SIZE = 1000;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ android::String8 outStr = android::String8();
+ // Line indent/formatting
+ uint indent = dataProvider.ConsumeIntegral<uint>();
+ std::string prefix = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+ std::string line = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+
+ // Misc properties
+ std::string logTag = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);
+ android_LogPriority priority =
+ static_cast<android_LogPriority>(dataProvider.ConsumeIntegral<int>());
+ bool ignoreBlankLines = dataProvider.ConsumeBool();
+
+ TemporaryFile tf;
+ android::FdPrinter filePrinter = android::FdPrinter(tf.fd, indent, prefix.c_str());
+ android::String8Printer stringPrinter = android::String8Printer(&outStr);
+ android::PrefixPrinter printer = android::PrefixPrinter(stringPrinter, prefix.c_str());
+ android::LogPrinter logPrinter =
+ android::LogPrinter(logTag.c_str(), priority, prefix.c_str(), ignoreBlankLines);
+
+ printer.printLine(line.c_str());
+ printer.printFormatLine("%s", line.c_str());
+ logPrinter.printLine(line.c_str());
+ logPrinter.printFormatLine("%s", line.c_str());
+ filePrinter.printLine(line.c_str());
+ filePrinter.printFormatLine("%s", line.c_str());
+ return 0;
+}
diff --git a/libutils/ProcessCallStack_fuzz.cpp b/libutils/ProcessCallStack_fuzz.cpp
new file mode 100644
index 000000000..30136cda7
--- /dev/null
+++ b/libutils/ProcessCallStack_fuzz.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <atomic>
+#include <thread>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/ProcessCallStack.h"
+using android::ProcessCallStack;
+
+static constexpr int MAX_NAME_SIZE = 1000;
+static constexpr int MAX_LOG_META_SIZE = 1000;
+static constexpr uint8_t MAX_THREADS = 10;
+
+std::atomic_bool ranCallStackUpdate(false);
+void loop() {
+ while (!ranCallStackUpdate.load()) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ }
+}
+
+void spawnThreads(FuzzedDataProvider* dataProvider) {
+ std::vector<std::thread> threads = std::vector<std::thread>();
+
+ // Get the number of threads to generate
+ uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_THREADS);
+
+ // Generate threads
+ for (uint8_t i = 0; i < count; i++) {
+ std::string threadName =
+ dataProvider->ConsumeRandomLengthString(MAX_NAME_SIZE).append(std::to_string(i));
+ std::thread th = std::thread(loop);
+ pthread_setname_np(th.native_handle(), threadName.c_str());
+ threads.push_back(move(th));
+ }
+
+ // Collect thread information
+ ProcessCallStack callStack = ProcessCallStack();
+ callStack.update();
+
+ // Tell our patiently waiting threads they can be done now.
+ ranCallStackUpdate.store(true);
+
+ std::string logTag = dataProvider->ConsumeRandomLengthString(MAX_LOG_META_SIZE);
+ std::string prefix = dataProvider->ConsumeRandomLengthString(MAX_LOG_META_SIZE);
+ // Both of these, along with dump, all call print() under the hood,
+ // Which is covered by the Printer fuzzer.
+ callStack.log(logTag.c_str());
+ callStack.toString(prefix.c_str());
+
+ // Check size
+ callStack.size();
+
+ // wait for any remaining threads
+ for (auto& thread : threads) {
+ thread.join();
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ spawnThreads(&dataProvider);
+ return 0;
+}
diff --git a/libutils/PropertyMap.cpp b/libutils/PropertyMap.cpp
deleted file mode 100644
index f00272a0f..000000000
--- a/libutils/PropertyMap.cpp
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "PropertyMap"
-
-#include <utils/PropertyMap.h>
-
-// Enables debug output for the parser.
-#define DEBUG_PARSER 0
-
-// Enables debug output for parser performance.
-#define DEBUG_PARSER_PERFORMANCE 0
-
-
-namespace android {
-
-static const char* WHITESPACE = " \t\r";
-static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r=";
-
-
-// --- PropertyMap ---
-
-PropertyMap::PropertyMap() {
-}
-
-PropertyMap::~PropertyMap() {
-}
-
-void PropertyMap::clear() {
- mProperties.clear();
-}
-
-void PropertyMap::addProperty(const String8& key, const String8& value) {
- mProperties.add(key, value);
-}
-
-bool PropertyMap::hasProperty(const String8& key) const {
- return mProperties.indexOfKey(key) >= 0;
-}
-
-bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const {
- ssize_t index = mProperties.indexOfKey(key);
- if (index < 0) {
- return false;
- }
-
- outValue = mProperties.valueAt(index);
- return true;
-}
-
-bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const {
- int32_t intValue;
- if (!tryGetProperty(key, intValue)) {
- return false;
- }
-
- outValue = intValue;
- return true;
-}
-
-bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const {
- String8 stringValue;
- if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
- return false;
- }
-
- char* end;
- int value = strtol(stringValue.string(), & end, 10);
- if (*end != '\0') {
- ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.",
- key.string(), stringValue.string());
- return false;
- }
- outValue = value;
- return true;
-}
-
-bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const {
- String8 stringValue;
- if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) {
- return false;
- }
-
- char* end;
- float value = strtof(stringValue.string(), & end);
- if (*end != '\0') {
- ALOGW("Property key '%s' has invalid value '%s'. Expected a float.",
- key.string(), stringValue.string());
- return false;
- }
- outValue = value;
- return true;
-}
-
-void PropertyMap::addAll(const PropertyMap* map) {
- for (size_t i = 0; i < map->mProperties.size(); i++) {
- mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i));
- }
-}
-
-status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) {
- *outMap = nullptr;
-
- Tokenizer* tokenizer;
- status_t status = Tokenizer::open(filename, &tokenizer);
- if (status) {
- ALOGE("Error %d opening property file %s.", status, filename.string());
- } else {
- PropertyMap* map = new PropertyMap();
- if (!map) {
- ALOGE("Error allocating property map.");
- status = NO_MEMORY;
- } else {
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
-#endif
- Parser parser(map, tokenizer);
- status = parser.parse();
-#if DEBUG_PARSER_PERFORMANCE
- nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
- ALOGD("Parsed property file '%s' %d lines in %0.3fms.",
- tokenizer->getFilename().string(), tokenizer->getLineNumber(),
- elapsedTime / 1000000.0);
-#endif
- if (status) {
- delete map;
- } else {
- *outMap = map;
- }
- }
- delete tokenizer;
- }
- return status;
-}
-
-
-// --- PropertyMap::Parser ---
-
-PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) :
- mMap(map), mTokenizer(tokenizer) {
-}
-
-PropertyMap::Parser::~Parser() {
-}
-
-status_t PropertyMap::Parser::parse() {
- while (!mTokenizer->isEof()) {
-#if DEBUG_PARSER
- ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
-#endif
-
- mTokenizer->skipDelimiters(WHITESPACE);
-
- if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') {
- String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER);
- if (keyToken.isEmpty()) {
- ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
-
- if (mTokenizer->nextChar() != '=') {
- ALOGE("%s: Expected '=' between property key and value.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
-
- String8 valueToken = mTokenizer->nextToken(WHITESPACE);
- if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) {
- ALOGE("%s: Found reserved character '\\' or '\"' in property value.",
- mTokenizer->getLocation().string());
- return BAD_VALUE;
- }
-
- mTokenizer->skipDelimiters(WHITESPACE);
- if (!mTokenizer->isEol()) {
- ALOGE("%s: Expected end of line, got '%s'.",
- mTokenizer->getLocation().string(),
- mTokenizer->peekRemainderOfLine().string());
- return BAD_VALUE;
- }
-
- if (mMap->hasProperty(keyToken)) {
- ALOGE("%s: Duplicate property value for key '%s'.",
- mTokenizer->getLocation().string(), keyToken.string());
- return BAD_VALUE;
- }
-
- mMap->addProperty(keyToken, valueToken);
- }
-
- mTokenizer->nextLine();
- }
- return OK;
-}
-
-} // namespace android
diff --git a/libutils/RWLock_fuzz.cpp b/libutils/RWLock_fuzz.cpp
new file mode 100755
index 000000000..e07590549
--- /dev/null
+++ b/libutils/RWLock_fuzz.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <functional>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/RWLock.h"
+
+static constexpr int MAX_OPERATIONS = 100;
+static constexpr int MAX_NAME_LEN = 2048;
+
+static const std::vector<std::function<void(android::RWLock*)>> operations = {
+ [](android::RWLock* lock) -> void {
+ // This might return a non-zero value if already locked
+ // Either way we are definitely locked now.
+ lock->tryWriteLock();
+ },
+ [](android::RWLock* lock) -> void { lock->tryReadLock(); },
+ [](android::RWLock* lock) -> void { lock->unlock(); },
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ std::string nameStr = dataProvider.ConsumeRandomLengthString(MAX_NAME_LEN);
+ int type = dataProvider.ConsumeIntegral<int>();
+ android::RWLock rwLock = android::RWLock(type, nameStr.c_str());
+ std::vector<uint8_t> opsToRun = dataProvider.ConsumeRemainingBytes<uint8_t>();
+ int opsRun = 0;
+ for (auto it : opsToRun) {
+ if (opsRun++ >= MAX_OPERATIONS) {
+ break;
+ }
+ it = it % operations.size();
+ operations[it](&rwLock);
+ }
+ rwLock.unlock();
+ return 0;
+}
diff --git a/libutils/RefBase_fuzz.cpp b/libutils/RefBase_fuzz.cpp
new file mode 100755
index 000000000..2a92531ee
--- /dev/null
+++ b/libutils/RefBase_fuzz.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <atomic>
+#include <thread>
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/RefBase.h"
+#include "utils/StrongPointer.h"
+using android::RefBase;
+using android::sp;
+using android::wp;
+
+static constexpr int REFBASE_INITIAL_STRONG_VALUE = (1 << 28);
+static constexpr int REFBASE_MAX_COUNT = 0xfffff;
+
+static constexpr int MAX_OPERATIONS = 100;
+static constexpr int MAX_THREADS = 10;
+
+bool canDecrementStrong(RefBase* ref) {
+ // There's an assert around decrementing the strong count too much that causes an artificial
+ // crash This is just running BAD_STRONG from RefBase
+ const int32_t count = ref->getStrongCount() - 1;
+ return !(count == 0 || ((count) & (~(REFBASE_MAX_COUNT | REFBASE_INITIAL_STRONG_VALUE))) != 0);
+}
+bool canDecrementWeak(RefBase* ref) {
+ const int32_t count = ref->getWeakRefs()->getWeakCount() - 1;
+ return !((count) == 0 || ((count) & (~REFBASE_MAX_COUNT)) != 0);
+}
+
+struct RefBaseSubclass : public RefBase {
+ RefBaseSubclass() {}
+ virtual ~RefBaseSubclass() {}
+};
+
+std::vector<std::function<void(RefBaseSubclass*)>> operations = {
+ [](RefBaseSubclass* ref) -> void { ref->getStrongCount(); },
+ [](RefBaseSubclass* ref) -> void { ref->printRefs(); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->printRefs(); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->getWeakCount(); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->refBase(); },
+ [](RefBaseSubclass* ref) -> void { ref->incStrong(nullptr); },
+ [](RefBaseSubclass* ref) -> void {
+ if (canDecrementStrong(ref)) {
+ ref->decStrong(nullptr);
+ }
+ },
+ [](RefBaseSubclass* ref) -> void { ref->forceIncStrong(nullptr); },
+ [](RefBaseSubclass* ref) -> void { ref->createWeak(nullptr); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->attemptIncStrong(nullptr); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->attemptIncWeak(nullptr); },
+ [](RefBaseSubclass* ref) -> void {
+ if (canDecrementWeak(ref)) {
+ ref->getWeakRefs()->decWeak(nullptr);
+ }
+ },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->incWeak(nullptr); },
+ [](RefBaseSubclass* ref) -> void { ref->getWeakRefs()->printRefs(); },
+};
+
+void loop(RefBaseSubclass* loopRef, const std::vector<uint8_t>& fuzzOps) {
+ for (auto op : fuzzOps) {
+ operations[op % operations.size()](loopRef);
+ }
+}
+
+void spawnThreads(FuzzedDataProvider* dataProvider) {
+ std::vector<std::thread> threads = std::vector<std::thread>();
+
+ // Get the number of threads to generate
+ uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_THREADS);
+
+ // Generate threads
+ for (uint8_t i = 0; i < count; i++) {
+ RefBaseSubclass* threadRef = new RefBaseSubclass();
+ uint8_t opCount = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_OPERATIONS);
+ std::vector<uint8_t> threadOperations = dataProvider->ConsumeBytes<uint8_t>(opCount);
+ std::thread tmp = std::thread(loop, threadRef, threadOperations);
+ threads.push_back(move(tmp));
+ }
+
+ for (auto& th : threads) {
+ th.join();
+ }
+}
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ spawnThreads(&dataProvider);
+ return 0;
+}
diff --git a/libutils/StopWatch_fuzz.cpp b/libutils/StopWatch_fuzz.cpp
new file mode 100644
index 000000000..63d8a28c9
--- /dev/null
+++ b/libutils/StopWatch_fuzz.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include "utils/StopWatch.h"
+
+static constexpr int MAX_OPERATIONS = 100;
+static constexpr int MAX_NAME_LEN = 2048;
+
+static const std::vector<std::function<void(android::StopWatch)>> operations = {
+ [](android::StopWatch stopWatch) -> void { stopWatch.reset(); },
+ [](android::StopWatch stopWatch) -> void { stopWatch.lap(); },
+ [](android::StopWatch stopWatch) -> void { stopWatch.elapsedTime(); },
+ [](android::StopWatch stopWatch) -> void { stopWatch.name(); },
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider dataProvider(data, size);
+ std::string nameStr = dataProvider.ConsumeRandomLengthString(MAX_NAME_LEN);
+ int clockVal = dataProvider.ConsumeIntegral<int>();
+ android::StopWatch stopWatch = android::StopWatch(nameStr.c_str(), clockVal);
+ std::vector<uint8_t> opsToRun = dataProvider.ConsumeRemainingBytes<uint8_t>();
+ int opsRun = 0;
+ for (auto it : opsToRun) {
+ if (opsRun++ >= MAX_OPERATIONS) {
+ break;
+ }
+ it = it % operations.size();
+ operations[it](stopWatch);
+ }
+ return 0;
+}
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index c83789145..438ef83a9 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -309,8 +309,14 @@ status_t String8::appendFormatV(const char* fmt, va_list args)
n = vsnprintf(nullptr, 0, fmt, tmp_args);
va_end(tmp_args);
- if (n != 0) {
+ if (n < 0) return UNKNOWN_ERROR;
+
+ if (n > 0) {
size_t oldLength = length();
+ if (n > std::numeric_limits<size_t>::max() - 1 ||
+ oldLength > std::numeric_limits<size_t>::max() - n - 1) {
+ return NO_MEMORY;
+ }
char* buf = lockBuffer(oldLength + n);
if (buf) {
vsnprintf(buf + oldLength, n + 1, fmt, args);
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 73ec1be96..9c71141f2 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -39,8 +39,15 @@ namespace android {
*/
int64_t uptimeMillis()
{
- int64_t when = systemTime(SYSTEM_TIME_MONOTONIC);
- return (int64_t) nanoseconds_to_milliseconds(when);
+ return nanoseconds_to_milliseconds(uptimeNanos());
+}
+
+/*
+ * public static native long uptimeNanos();
+ */
+int64_t uptimeNanos()
+{
+ return systemTime(SYSTEM_TIME_MONOTONIC);
}
/*
diff --git a/libutils/SystemClock_test.cpp b/libutils/SystemClock_test.cpp
index 5ad060b04..7449dad77 100644
--- a/libutils/SystemClock_test.cpp
+++ b/libutils/SystemClock_test.cpp
@@ -29,16 +29,24 @@ static const int64_t SLACK_NS = SLACK_MS * MS_IN_NS;
TEST(SystemClock, SystemClock) {
auto startUptimeMs = android::uptimeMillis();
+ auto startUptimeNs = android::uptimeNanos();
auto startRealtimeMs = android::elapsedRealtime();
auto startRealtimeNs = android::elapsedRealtimeNano();
ASSERT_GT(startUptimeMs, 0)
<< "uptimeMillis() reported an impossible uptime";
+ ASSERT_GT(startUptimeNs, 0)
+ << "uptimeNanos() reported an impossible uptime";
ASSERT_GE(startRealtimeMs, startUptimeMs)
<< "elapsedRealtime() thinks we've suspended for negative time";
- ASSERT_GE(startRealtimeNs, startUptimeMs * MS_IN_NS)
+ ASSERT_GE(startRealtimeNs, startUptimeNs)
<< "elapsedRealtimeNano() thinks we've suspended for negative time";
+ ASSERT_GE(startUptimeNs, startUptimeMs * MS_IN_NS)
+ << "uptimeMillis() and uptimeNanos() are inconsistent";
+ ASSERT_LT(startUptimeNs, (startUptimeMs + SLACK_MS) * MS_IN_NS)
+ << "uptimeMillis() and uptimeNanos() are inconsistent";
+
ASSERT_GE(startRealtimeNs, startRealtimeMs * MS_IN_NS)
<< "elapsedRealtime() and elapsedRealtimeNano() are inconsistent";
ASSERT_LT(startRealtimeNs, (startRealtimeMs + SLACK_MS) * MS_IN_NS)
@@ -51,6 +59,7 @@ TEST(SystemClock, SystemClock) {
ASSERT_EQ(nanosleepErr, 0) << "nanosleep() failed: " << strerror(errno);
auto endUptimeMs = android::uptimeMillis();
+ auto endUptimeNs = android::uptimeNanos();
auto endRealtimeMs = android::elapsedRealtime();
auto endRealtimeNs = android::elapsedRealtimeNano();
@@ -58,6 +67,10 @@ TEST(SystemClock, SystemClock) {
<< "uptimeMillis() advanced too little after nanosleep()";
EXPECT_LT(endUptimeMs - startUptimeMs, SLEEP_MS + SLACK_MS)
<< "uptimeMillis() advanced too much after nanosleep()";
+ EXPECT_GE(endUptimeNs - startUptimeNs, SLEEP_NS)
+ << "uptimeNanos() advanced too little after nanosleep()";
+ EXPECT_LT(endUptimeNs - startUptimeNs, SLEEP_NS + SLACK_NS)
+ << "uptimeNanos() advanced too much after nanosleep()";
EXPECT_GE(endRealtimeMs - startRealtimeMs, SLEEP_MS)
<< "elapsedRealtime() advanced too little after nanosleep()";
EXPECT_LT(endRealtimeMs - startRealtimeMs, SLEEP_MS + SLACK_MS)
@@ -67,6 +80,11 @@ TEST(SystemClock, SystemClock) {
EXPECT_LT(endRealtimeNs - startRealtimeNs, SLEEP_NS + SLACK_NS)
<< "elapsedRealtimeNano() advanced too much after nanosleep()";
+ EXPECT_GE(endUptimeNs, endUptimeMs * MS_IN_NS)
+ << "uptimeMillis() and uptimeNanos() are inconsistent after nanosleep()";
+ EXPECT_LT(endUptimeNs, (endUptimeMs + SLACK_MS) * MS_IN_NS)
+ << "uptimeMillis() and uptimeNanos() are inconsistent after nanosleep()";
+
EXPECT_GE(endRealtimeNs, endRealtimeMs * MS_IN_NS)
<< "elapsedRealtime() and elapsedRealtimeNano() are inconsistent after nanosleep()";
EXPECT_LT(endRealtimeNs, (endRealtimeMs + SLACK_MS) * MS_IN_NS)
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 147db542d..55eadb07a 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -302,7 +302,8 @@ void androidSetCreateThreadFunc(android_create_thread_fn func)
}
#if defined(__ANDROID__)
-int androidSetThreadPriority(pid_t tid, int pri, bool change_policy) {
+namespace {
+int androidSetThreadPriorityInternal(pid_t tid, int pri, bool change_policy) {
int rc = 0;
int lasterr = 0;
int curr_pri = getpriority(PRIO_PROCESS, tid);
@@ -334,6 +335,15 @@ int androidSetThreadPriority(pid_t tid, int pri, bool change_policy) {
return rc;
}
+} // namespace
+
+int androidSetThreadPriority(pid_t tid, int pri) {
+ return androidSetThreadPriorityInternal(tid, pri, true);
+}
+
+int androidSetThreadPriorityAndPolicy(pid_t tid, int pri, bool change_policy) {
+ return androidSetThreadPriorityInternal(tid, pri, change_policy);
+}
int androidGetThreadPriority(pid_t tid) {
return getpriority(PRIO_PROCESS, tid);
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index b08e061c0..b6e457b04 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -162,9 +162,9 @@ int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t
if (index >= src_len) {
return -1;
}
- size_t dummy_index;
+ size_t unused_index;
if (next_index == nullptr) {
- next_index = &dummy_index;
+ next_index = &unused_index;
}
size_t num_read;
int32_t ret = utf32_at_internal(src + index, &num_read);
diff --git a/libutils/include/utils/AndroidThreads.h b/libutils/include/utils/AndroidThreads.h
index 3c30a2a85..cdb5442ae 100644
--- a/libutils/include/utils/AndroidThreads.h
+++ b/libutils/include/utils/AndroidThreads.h
@@ -78,9 +78,12 @@ extern void androidSetCreateThreadFunc(android_create_thread_fn func);
// 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;
// in either case errno is set. Thread ID zero means current thread.
+// This is equivalent to androidSetThreadPriorityAndPolicy(tid, prio, true);
+extern int androidSetThreadPriority(pid_t tid, int prio);
+
// Parameter "change_policy" indicates if sched policy should be changed. It needs
// not be checked again if the change is done elsewhere like activity manager.
-extern int androidSetThreadPriority(pid_t tid, int prio, bool change_policy = true);
+extern int androidSetThreadPriorityAndPolicy(pid_t tid, int prio, bool change_policy);
// Get the current priority of a particular thread. Returns one of the
// ANDROID_PRIORITY constants or a negative result in case of error.
diff --git a/libutils/include/utils/Debug.h b/libutils/include/utils/Debug.h
index 96bd70eb0..c3b902617 100644
--- a/libutils/include/utils/Debug.h
+++ b/libutils/include/utils/Debug.h
@@ -14,27 +14,9 @@
* limitations under the License.
*/
-#ifndef ANDROID_UTILS_DEBUG_H
-#define ANDROID_UTILS_DEBUG_H
+#pragma once
-#include <stdint.h>
-#include <sys/types.h>
+// Note: new code should use static_assert directly.
-namespace android {
-// ---------------------------------------------------------------------------
-
-#ifdef __cplusplus
-template<bool> struct CompileTimeAssert;
-template<> struct CompileTimeAssert<true> {};
-#define COMPILE_TIME_ASSERT(_exp) \
- template class CompileTimeAssert< (_exp) >;
-#endif
-
-// DO NOT USE: Please use static_assert instead
-#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \
- CompileTimeAssert<( _exp )>();
-
-// ---------------------------------------------------------------------------
-} // namespace android
-
-#endif // ANDROID_UTILS_DEBUG_H
+#define COMPILE_TIME_ASSERT static_assert
+#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE static_assert
diff --git a/libutils/include/utils/Flattenable.h b/libutils/include/utils/Flattenable.h
index 17c5e1057..8aa381a8f 100644
--- a/libutils/include/utils/Flattenable.h
+++ b/libutils/include/utils/Flattenable.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_UTILS_FLATTENABLE_H
-#define ANDROID_UTILS_FLATTENABLE_H
+#pragma once
// DO NOT USE: please use parcelable instead
// This code is deprecated and will not be supported via AIDL code gen. For data
@@ -25,7 +24,6 @@
#include <string.h>
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/Debug.h>
#include <type_traits>
@@ -217,5 +215,3 @@ public:
};
} // namespace android
-
-#endif /* ANDROID_UTILS_FLATTENABLE_H */
diff --git a/libutils/include/utils/PropertyMap.h b/libutils/include/utils/PropertyMap.h
deleted file mode 100644
index a9e674f9a..000000000
--- a/libutils/include/utils/PropertyMap.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _UTILS_PROPERTY_MAP_H
-#define _UTILS_PROPERTY_MAP_H
-
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/Errors.h>
-#include <utils/Tokenizer.h>
-
-namespace android {
-
-/*
- * Provides a mechanism for passing around string-based property key / value pairs
- * and loading them from property files.
- *
- * The property files have the following simple structure:
- *
- * # Comment
- * key = value
- *
- * Keys and values are any sequence of printable ASCII characters.
- * The '=' separates the key from the value.
- * The key and value may not contain whitespace.
- *
- * The '\' character is reserved for escape sequences and is not currently supported.
- * The '"" character is reserved for quoting and is not currently supported.
- * Files that contain the '\' or '"' character will fail to parse.
- *
- * The file must not contain duplicate keys.
- *
- * TODO Support escape sequences and quoted values when needed.
- */
-class PropertyMap {
-public:
- /* Creates an empty property map. */
- PropertyMap();
- ~PropertyMap();
-
- /* Clears the property map. */
- void clear();
-
- /* Adds a property.
- * Replaces the property with the same key if it is already present.
- */
- void addProperty(const String8& key, const String8& value);
-
- /* Returns true if the property map contains the specified key. */
- bool hasProperty(const String8& key) const;
-
- /* Gets the value of a property and parses it.
- * Returns true and sets outValue if the key was found and its value was parsed successfully.
- * Otherwise returns false and does not modify outValue. (Also logs a warning.)
- */
- bool tryGetProperty(const String8& key, String8& outValue) const;
- bool tryGetProperty(const String8& key, bool& outValue) const;
- bool tryGetProperty(const String8& key, int32_t& outValue) const;
- bool tryGetProperty(const String8& key, float& outValue) const;
-
- /* Adds all values from the specified property map. */
- void addAll(const PropertyMap* map);
-
- /* Gets the underlying property map. */
- inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; }
-
- /* Loads a property map from a file. */
- static status_t load(const String8& filename, PropertyMap** outMap);
-
-private:
- class Parser {
- PropertyMap* mMap;
- Tokenizer* mTokenizer;
-
- public:
- Parser(PropertyMap* map, Tokenizer* tokenizer);
- ~Parser();
- status_t parse();
-
- private:
- status_t parseType();
- status_t parseKey();
- status_t parseKeyProperty();
- status_t parseModifier(const String8& token, int32_t* outMetaState);
- status_t parseCharacterLiteral(char16_t* outCharacter);
- };
-
- KeyedVector<String8, String8> mProperties;
-};
-
-} // namespace android
-
-#endif // _UTILS_PROPERTY_MAP_H
diff --git a/libutils/include/utils/SystemClock.h b/libutils/include/utils/SystemClock.h
index f816fbaf7..892104cb8 100644
--- a/libutils/include/utils/SystemClock.h
+++ b/libutils/include/utils/SystemClock.h
@@ -23,6 +23,7 @@
namespace android {
int64_t uptimeMillis();
+int64_t uptimeNanos();
int64_t elapsedRealtime();
int64_t elapsedRealtimeNano();
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
index ddf71de24..be35ea2f0 100644
--- a/libutils/include/utils/Vector.h
+++ b/libutils/include/utils/Vector.h
@@ -23,15 +23,13 @@
#include <log/log.h>
#include <utils/TypeHelpers.h>
#include <utils/VectorImpl.h>
-
-/*
- * Used to blacklist some functions from CFI.
- *
- */
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
+/*
+ * Used to exclude some functions from CFI.
+ */
#if __has_attribute(no_sanitize)
#define UTILS_VECTOR_NO_CFI __attribute__((no_sanitize("cfi")))
#else
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index d15fa2be7..6b7e016a4 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -64,6 +64,7 @@ using android::base::ParseByteCount;
using android::base::ParseUint;
using android::base::Split;
using android::base::StringPrintf;
+using android::base::WriteFully;
class Logcat {
public:
@@ -616,15 +617,15 @@ int Logcat::Run(int argc, char** argv) {
if (long_options[option_index].name == wrap_str) {
mode |= ANDROID_LOG_WRAP | ANDROID_LOG_NONBLOCK;
// ToDo: implement API that supports setting a wrap timeout
- size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
- if (optarg && (!ParseUint(optarg, &dummy) || dummy < 1)) {
+ size_t timeout = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
+ if (optarg && (!ParseUint(optarg, &timeout) || timeout < 1)) {
error(EXIT_FAILURE, 0, "%s %s out of range.",
long_options[option_index].name, optarg);
}
- if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
+ if (timeout != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
fprintf(stderr, "WARNING: %s %u seconds, ignoring %zu\n",
long_options[option_index].name, ANDROID_LOG_WRAP_DEFAULT_TIMEOUT,
- dummy);
+ timeout);
}
break;
}
@@ -864,8 +865,7 @@ int Logcat::Run(int argc, char** argv) {
if (consolePipe) {
// need the trailing '\0'
- if (!android::base::WriteFully(fd, pipePurpose.c_str(),
- pipePurpose.size() + 1)) {
+ if (!WriteFully(fd, pipePurpose.c_str(), pipePurpose.size() + 1)) {
close(fd);
return EXIT_FAILURE;
}
@@ -1064,19 +1064,23 @@ int Logcat::Run(int argc, char** argv) {
if (getLogSize) {
long size = android_logger_get_log_size(logger);
long readable = android_logger_get_log_readable_size(logger);
+ long consumed = android_logger_get_log_consumed_size(logger);
if (size < 0 || readable < 0) {
ReportErrorName(buffer_name, security_buffer_selected, &get_size_failures);
} else {
auto size_format = format_of_size(size);
auto readable_format = format_of_size(readable);
+ auto consumed_format = format_of_size(consumed);
std::string str = android::base::StringPrintf(
- "%s: ring buffer is %lu %sB (%lu %sB consumed),"
+ "%s: ring buffer is %lu %sB (%lu %sB consumed, %lu %sB readable),"
" max entry is %d B, max payload is %d B\n",
- buffer_name, size_format.first, size_format.second, readable_format.first,
- readable_format.second, (int)LOGGER_ENTRY_MAX_LEN,
- (int)LOGGER_ENTRY_MAX_PAYLOAD);
- TEMP_FAILURE_RETRY(write(output_fd_.get(), str.data(), str.length()));
+ buffer_name, size_format.first, size_format.second, consumed_format.first,
+ consumed_format.second, readable_format.first, readable_format.second,
+ (int)LOGGER_ENTRY_MAX_LEN, (int)LOGGER_ENTRY_MAX_PAYLOAD);
+ if (!WriteFully(output_fd_, str.data(), str.length())) {
+ error(EXIT_FAILURE, errno, "Failed to write to output fd");
+ }
}
}
}
@@ -1146,7 +1150,9 @@ int Logcat::Run(int argc, char** argv) {
if (*cp == '\n') ++cp;
size_t len = strlen(cp);
- TEMP_FAILURE_RETRY(write(output_fd_.get(), cp, len));
+ if (!WriteFully(output_fd_, cp, len)) {
+ error(EXIT_FAILURE, errno, "Failed to write to output fd");
+ }
return EXIT_SUCCESS;
}
@@ -1190,7 +1196,9 @@ If you have enabled significant logging, look into using the -G option to increa
PrintDividers(log_msg.id(), printDividers);
if (print_binary_) {
- TEMP_FAILURE_RETRY(write(output_fd_.get(), &log_msg, log_msg.len()));
+ if (!WriteFully(output_fd_, &log_msg, log_msg.len())) {
+ error(EXIT_FAILURE, errno, "Failed to write to output fd");
+ }
} else {
ProcessBuffer(&log_msg);
}
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 61aa9381c..735fd94ca 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -539,7 +539,6 @@ TEST(logcat, End_to_End_multitude) {
static int get_groups(const char* cmd) {
FILE* fp;
- // NB: crash log only available in user space
EXPECT_TRUE(NULL != (fp = popen(cmd, "r")));
if (fp == NULL) {
@@ -551,17 +550,18 @@ static int get_groups(const char* cmd) {
int count = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
- int size, consumed, max, payload;
- char size_mult[4], consumed_mult[4];
+ int size, consumed, readable, max, payload;
+ char size_mult[4], consumed_mult[4], readable_mult[4];
long full_size, full_consumed;
size = consumed = max = payload = 0;
// NB: crash log can be very small, not hit a Kb of consumed space
// doubly lucky we are not including it.
- EXPECT_EQ(6, sscanf(buffer,
- "%*s ring buffer is %d %3s (%d %3s consumed),"
+ EXPECT_EQ(8, sscanf(buffer,
+ "%*s ring buffer is %d %3s (%d %3s consumed, %d %3s readable),"
" max entry is %d B, max payload is %d B",
- &size, size_mult, &consumed, consumed_mult, &max, &payload))
+ &size, size_mult, &consumed, consumed_mult, &readable, readable_mult,
+ &max, &payload))
<< "Parse error on: " << buffer;
full_size = size;
switch (size_mult[0]) {
@@ -1224,13 +1224,14 @@ TEST(logcat, blocking_clear) {
break;
}
- int size, consumed, max, payload;
- char size_mult[4], consumed_mult[4];
+ int size, consumed, readable, max, payload;
+ char size_mult[4], consumed_mult[4], readable_mult[4];
size = consumed = max = payload = 0;
- if (6 == sscanf(buffer,
- "events: ring buffer is %d %3s (%d %3s consumed),"
+ if (8 == sscanf(buffer,
+ "events: ring buffer is %d %3s (%d %3s consumed, %d %3s readable),"
" max entry is %d B, max payload is %d B",
- &size, size_mult, &consumed, consumed_mult, &max, &payload)) {
+ &size, size_mult, &consumed, consumed_mult, &readable, readable_mult, &max,
+ &payload)) {
long full_size = size, full_consumed = consumed;
switch (size_mult[0]) {
@@ -1654,68 +1655,6 @@ TEST(logcat, descriptive) {
EXPECT_GE(ret, 0);
EXPECT_TRUE(End_to_End(sync.tagStr, ""));
}
-
- {
- // Invent new entries because existing can not serve
- EventTagMap* map = android_openEventTagMap(nullptr);
- ASSERT_TRUE(nullptr != map);
- static const char name[] = logcat_executable ".descriptive-monotonic";
- int myTag = android_lookupEventTagNum(map, name, "(new|1|s)",
- ANDROID_LOG_UNKNOWN);
- android_closeEventTagMap(map);
- ASSERT_NE(-1, myTag);
-
- const struct tag sync = { (uint32_t)myTag, name };
-
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)7;
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=7s"));
- }
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)62;
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:02"));
- }
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)3673;
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=1:01:13"));
- }
- {
- android_log_event_list ctx(sync.tagNo);
- ctx << (uint32_t)(86400 + 7200 + 180 + 58);
- for (ret = -EBUSY; ret == -EBUSY; rest()) ret = ctx.write();
- EXPECT_GE(ret, 0);
- EXPECT_TRUE(End_to_End(sync.tagStr, "new=1d 2:03:58"));
- }
- }
-}
-
-static bool reportedSecurity(const char* command) {
- FILE* fp = popen(command, "r");
- if (!fp) return true;
-
- std::string ret;
- bool val = android::base::ReadFdToString(fileno(fp), &ret);
- pclose(fp);
-
- if (!val) return true;
- return std::string::npos != ret.find("'security'");
-}
-
-TEST(logcat, security) {
- EXPECT_FALSE(reportedSecurity(logcat_executable " -b all -g 2>&1"));
- EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -g 2>&1"));
- EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -c 2>&1"));
- EXPECT_TRUE(
- reportedSecurity(logcat_executable " -b security -G 256K 2>&1"));
}
static size_t commandOutputSize(const char* command) {
@@ -1749,7 +1688,7 @@ TEST(logcat, invalid_buffer) {
ASSERT_TRUE(android::base::ReadFdToString(fileno(fp), &output));
pclose(fp);
- ASSERT_TRUE(android::base::StartsWith(output, "unknown buffer foo\n"));
+ EXPECT_NE(std::string::npos, output.find("Unknown buffer 'foo'"));
}
static void SniffUid(const std::string& line, uid_t& uid) {
diff --git a/logd/Android.bp b/logd/Android.bp
index fe9787144..fe22d1caa 100644
--- a/logd/Android.bp
+++ b/logd/Android.bp
@@ -57,6 +57,7 @@ cc_library_static {
"LogReaderList.cpp",
"LogReaderThread.cpp",
"LogBufferElement.cpp",
+ "LogSize.cpp",
"LogStatistics.cpp",
"LogTags.cpp",
"PruneList.cpp",
@@ -65,6 +66,7 @@ cc_library_static {
"SerializedLogChunk.cpp",
"SimpleLogBuffer.cpp",
],
+ static_libs: ["liblog"],
logtags: ["event.logtags"],
export_include_dirs: ["."],
@@ -177,6 +179,7 @@ cc_test {
},
test_suites: [
"cts",
+ "device-tests",
"vts10",
],
}
diff --git a/logd/ChattyLogBuffer.h b/logd/ChattyLogBuffer.h
index ce3dc7bc2..b4d3a2ff3 100644
--- a/logd/ChattyLogBuffer.h
+++ b/logd/ChattyLogBuffer.h
@@ -25,7 +25,6 @@
#include <android-base/thread_annotations.h>
#include <android/log.h>
#include <private/android_filesystem_config.h>
-#include <sysutils/SocketClient.h>
#include "LogBuffer.h"
#include "LogBufferElement.h"
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index 2eeb0d977..0ba16213e 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -32,6 +32,7 @@
#include <string>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <cutils/sockets.h>
#include <log/log_properties.h>
@@ -46,6 +47,7 @@ CommandListener::CommandListener(LogBuffer* buf, LogTags* tags, PruneList* prune
registerCmd(new ClearCmd(this));
registerCmd(new GetBufSizeCmd(this));
registerCmd(new SetBufSizeCmd(this));
+ registerCmd(new GetBufSizeReadableCmd(this));
registerCmd(new GetBufSizeUsedCmd(this));
registerCmd(new GetStatisticsCmd(this));
registerCmd(new SetPruneListCmd(this));
@@ -63,53 +65,58 @@ static void setname() {
}
}
-int CommandListener::ClearCmd::runCommand(SocketClient* cli, int argc,
- char** argv) {
+template <typename F>
+static int LogIdCommand(SocketClient* cli, int argc, char** argv, F&& function) {
setname();
- uid_t uid = cli->getUid();
- if (clientHasLogCredentials(cli)) {
- uid = AID_ROOT;
- }
-
if (argc < 2) {
cli->sendMsg("Missing Argument");
return 0;
}
- int id = atoi(argv[1]);
- if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
+ int log_id;
+ if (!android::base::ParseInt(argv[1], &log_id, static_cast<int>(LOG_ID_MAIN),
+ static_cast<int>(LOG_ID_KERNEL))) {
cli->sendMsg("Range Error");
return 0;
}
- cli->sendMsg(buf()->Clear((log_id_t)id, uid) ? "success" : "busy");
+ function(static_cast<log_id_t>(log_id));
return 0;
}
-int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc,
- char** argv) {
- setname();
- if (argc < 2) {
- cli->sendMsg("Missing Argument");
- return 0;
+int CommandListener::ClearCmd::runCommand(SocketClient* cli, int argc, char** argv) {
+ uid_t uid = cli->getUid();
+ if (clientHasLogCredentials(cli)) {
+ uid = AID_ROOT;
}
- int id = atoi(argv[1]);
- if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
- cli->sendMsg("Range Error");
- return 0;
- }
+ return LogIdCommand(cli, argc, argv, [&](log_id_t id) {
+ cli->sendMsg(buf()->Clear(id, uid) ? "success" : "busy");
+ });
+}
- unsigned long size = buf()->GetSize((log_id_t)id);
- char buf[512];
- snprintf(buf, sizeof(buf), "%lu", size);
- cli->sendMsg(buf);
- return 0;
+template <typename F>
+static int LogSizeCommand(SocketClient* cli, int argc, char** argv, F&& size_function) {
+ return LogIdCommand(cli, argc, argv, [&](log_id_t log_id) {
+ cli->sendMsg(std::to_string(size_function(log_id)).c_str());
+ });
+}
+
+int CommandListener::GetBufSizeCmd::runCommand(SocketClient* cli, int argc, char** argv) {
+ return LogSizeCommand(cli, argc, argv, [this](log_id_t id) { return buf()->GetSize(id); });
+}
+
+int CommandListener::GetBufSizeReadableCmd::runCommand(SocketClient* cli, int argc, char** argv) {
+ return LogSizeCommand(cli, argc, argv,
+ [this](log_id_t id) { return stats()->SizeReadable(id); });
+}
+
+int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc, char** argv) {
+ return LogSizeCommand(cli, argc, argv, [this](log_id_t id) { return stats()->Sizes(id); });
}
int CommandListener::SetBufSizeCmd::runCommand(SocketClient* cli, int argc,
char** argv) {
- setname();
if (!clientHasLogCredentials(cli)) {
cli->sendMsg("Permission Denied");
return 0;
@@ -119,42 +126,11 @@ int CommandListener::SetBufSizeCmd::runCommand(SocketClient* cli, int argc,
cli->sendMsg("Missing Argument");
return 0;
}
+ size_t size = atol(argv[2]);
- int id = atoi(argv[1]);
- if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
- cli->sendMsg("Range Error");
- return 0;
- }
-
- unsigned long size = atol(argv[2]);
- if (buf()->SetSize((log_id_t)id, size)) {
- cli->sendMsg("Range Error");
- return 0;
- }
-
- cli->sendMsg("success");
- return 0;
-}
-
-int CommandListener::GetBufSizeUsedCmd::runCommand(SocketClient* cli, int argc,
- char** argv) {
- setname();
- if (argc < 2) {
- cli->sendMsg("Missing Argument");
- return 0;
- }
-
- int id = atoi(argv[1]);
- if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
- cli->sendMsg("Range Error");
- return 0;
- }
-
- unsigned long size = stats()->Sizes((log_id_t)id);
- char buf[512];
- snprintf(buf, sizeof(buf), "%lu", size);
- cli->sendMsg(buf);
- return 0;
+ return LogIdCommand(cli, argc, argv, [&](log_id_t log_id) {
+ cli->sendMsg(buf()->SetSize(log_id, size) ? "success" : "busy");
+ });
}
// This returns a string with a length prefix with the format <length>\n<data>\n\f. The length
@@ -179,8 +155,7 @@ static std::string PackageString(const std::string& str) {
return android::base::StringPrintf("%zu\n%s\n\f", total_size, str.c_str());
}
-int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc,
- char** argv) {
+int CommandListener::GetStatisticsCmd::runCommand(SocketClient* cli, int argc, char** argv) {
setname();
uid_t uid = cli->getUid();
if (clientHasLogCredentials(cli)) {
@@ -245,8 +220,7 @@ int CommandListener::SetPruneListCmd::runCommand(SocketClient* cli, int argc, ch
return 0;
}
-int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc,
- char** argv) {
+int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc, char** argv) {
setname();
uid_t uid = cli->getUid();
if (clientHasLogCredentials(cli)) {
@@ -290,8 +264,7 @@ int CommandListener::GetEventTagCmd::runCommand(SocketClient* cli, int argc,
return 0;
}
-int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int /*argc*/,
- char** /*argv*/) {
+int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int, char**) {
setname();
LOG(INFO) << "logd reinit";
@@ -314,8 +287,7 @@ int CommandListener::ReinitCmd::runCommand(SocketClient* cli, int /*argc*/,
return 0;
}
-int CommandListener::ExitCmd::runCommand(SocketClient* cli, int /*argc*/,
- char** /*argv*/) {
+int CommandListener::ExitCmd::runCommand(SocketClient* cli, int, char**) {
setname();
cli->sendMsg("success");
diff --git a/logd/CommandListener.h b/logd/CommandListener.h
index c3080ab97..8e12634af 100644
--- a/logd/CommandListener.h
+++ b/logd/CommandListener.h
@@ -57,6 +57,7 @@ class CommandListener : public FrameworkListener {
LogCmd(Clear, clear);
LogCmd(GetBufSize, getLogSize);
LogCmd(SetBufSize, setLogSize);
+ LogCmd(GetBufSizeReadable, getLogSizeReadable);
LogCmd(GetBufSizeUsed, getLogSizeUsed);
LogCmd(GetStatistics, getStatistics);
LogCmd(GetPruneList, getPruneList);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 0ce9796b0..0e17476ea 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -32,7 +32,7 @@
#include <sstream>
#include <android-base/macros.h>
-#include <log/log_properties.h>
+#include <android-base/properties.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
@@ -40,6 +40,8 @@
#include "LogUtils.h"
#include "libaudit.h"
+using android::base::GetBoolProperty;
+
#define KMSG_PRIORITY(PRI) \
'<', '0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) / 10, \
'0' + LOG_MAKEPRI(LOG_AUTH, LOG_PRI(PRI)) % 10, '>'
@@ -48,8 +50,8 @@ LogAudit::LogAudit(LogBuffer* buf, int fdDmesg, LogStatistics* stats)
: SocketListener(getLogSocket(), false),
logbuf(buf),
fdDmesg(fdDmesg),
- main(__android_logger_property_get_bool("ro.logd.auditd.main", BOOL_DEFAULT_TRUE)),
- events(__android_logger_property_get_bool("ro.logd.auditd.events", BOOL_DEFAULT_TRUE)),
+ main(GetBoolProperty("ro.logd.auditd.main", true)),
+ events(GetBoolProperty("ro.logd.auditd.events", true)),
initialized(false),
stats_(stats) {
static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index c5d333a9e..a98c4b971 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -68,8 +68,8 @@ class LogBuffer {
log_time realtime)>& filter) = 0;
virtual bool Clear(log_id_t id, uid_t uid) = 0;
- virtual unsigned long GetSize(log_id_t id) = 0;
- virtual int SetSize(log_id_t id, unsigned long size) = 0;
+ virtual size_t GetSize(log_id_t id) = 0;
+ virtual bool SetSize(log_id_t id, size_t size) = 0;
virtual uint64_t sequence() const = 0;
};
diff --git a/logd/LogBufferTest.cpp b/logd/LogBufferTest.cpp
index 47d2a2f92..191110522 100644
--- a/logd/LogBufferTest.cpp
+++ b/logd/LogBufferTest.cpp
@@ -34,16 +34,6 @@ using android::base::Join;
using android::base::Split;
using android::base::StringPrintf;
-#ifndef __ANDROID__
-unsigned long __android_logger_get_buffer_size(log_id_t) {
- return 1024 * 1024;
-}
-
-bool __android_logger_valid_buffer_size(unsigned long) {
- return true;
-}
-#endif
-
char* android::uidToName(uid_t) {
return nullptr;
}
diff --git a/logd/LogBufferTest.h b/logd/LogBufferTest.h
index 1fd22c236..eeeb98095 100644
--- a/logd/LogBufferTest.h
+++ b/logd/LogBufferTest.h
@@ -75,6 +75,8 @@ class LogBufferTest : public testing::TestWithParam<std::string> {
} else {
FAIL() << "Unknown buffer type selected for test";
}
+
+ log_id_for_each(i) { log_buffer_->SetSize(i, 1024 * 1024); }
}
void LogMessages(const std::vector<LogMessage>& messages) {
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index dbdf7fdd0..d6c7d2527 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -381,8 +381,8 @@ pid_t LogKlog::sniffPid(const char*& buf, ssize_t len) {
}
if ((i == 9) && (cp[i] == ' ')) {
int pid = 0;
- char dummy;
- if (sscanf(cp + 4, "%d%c", &pid, &dummy) == 2) {
+ char placeholder;
+ if (sscanf(cp + 4, "%d%c", &pid, &placeholder) == 2) {
buf = cp + 10; // skip-it-all
return pid;
}
@@ -392,8 +392,8 @@ pid_t LogKlog::sniffPid(const char*& buf, ssize_t len) {
// Mediatek kernels with modified printk
if (*cp == '[') {
int pid = 0;
- char dummy;
- if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &dummy) == 2) {
+ char placeholder;
+ if (sscanf(cp, "[%d:%*[a-z_./0-9:A-Z]]%c", &pid, &placeholder) == 2) {
return pid;
}
break; // Only the first one
@@ -703,7 +703,7 @@ int LogKlog::log(const char* buf, ssize_t len) {
p = " ";
b = 1;
}
- // paranoid sanity check, can not happen ...
+ // This shouldn't happen, but clamp the size if it does.
if (b > LOGGER_ENTRY_MAX_PAYLOAD) {
b = LOGGER_ENTRY_MAX_PAYLOAD;
}
@@ -712,7 +712,7 @@ int LogKlog::log(const char* buf, ssize_t len) {
}
// calculate buffer copy requirements
ssize_t n = 1 + taglen + 1 + b + 1;
- // paranoid sanity check, first two just can not happen ...
+ // Extra checks for likely impossible cases.
if ((taglen > n) || (b > n) || (n > (ssize_t)USHRT_MAX) || (n <= 0)) {
return -EINVAL;
}
@@ -722,7 +722,7 @@ int LogKlog::log(const char* buf, ssize_t len) {
// 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.
+ // against stack corruption and speedup, loss is truncated long-line content.
char newstr[n];
char* np = newstr;
diff --git a/logd/LogReader.h b/logd/LogReader.h
index b85a58478..a4e52c4eb 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -22,8 +22,6 @@
#include "LogReaderList.h"
#include "LogReaderThread.h"
-#define LOGD_SNDTIMEO 32
-
class LogReader : public SocketListener {
public:
explicit LogReader(LogBuffer* logbuf, LogReaderList* reader_list);
diff --git a/logd/LogReaderThread.h b/logd/LogReaderThread.h
index 1855c0e26..20624f2e4 100644
--- a/logd/LogReaderThread.h
+++ b/logd/LogReaderThread.h
@@ -27,7 +27,6 @@
#include <memory>
#include <log/log.h>
-#include <sysutils/SocketClient.h>
#include "LogBuffer.h"
#include "LogWriter.h"
diff --git a/logd/LogSize.cpp b/logd/LogSize.cpp
new file mode 100644
index 000000000..fe829baac
--- /dev/null
+++ b/logd/LogSize.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <LogSize.h>
+
+#include <array>
+#include <optional>
+#include <string>
+
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+
+bool IsValidBufferSize(size_t value) {
+ return kLogBufferMinSize <= value && value <= kLogBufferMaxSize;
+}
+
+static std::optional<size_t> GetBufferSizeProperty(const std::string& key) {
+ std::string value = android::base::GetProperty(key, "");
+ if (value.empty()) {
+ return {};
+ }
+
+ uint32_t size;
+ if (!android::base::ParseByteCount(value, &size)) {
+ return {};
+ }
+
+ if (!IsValidBufferSize(size)) {
+ return {};
+ }
+
+ return size;
+}
+
+size_t GetBufferSizeFromProperties(log_id_t log_id) {
+ std::string buffer_name = android_log_id_to_name(log_id);
+ std::array<std::string, 4> properties = {
+ "persist.logd.size." + buffer_name,
+ "ro.logd.size." + buffer_name,
+ "persist.logd.size",
+ "ro.logd.size",
+ };
+
+ for (const auto& property : properties) {
+ if (auto size = GetBufferSizeProperty(property)) {
+ return *size;
+ }
+ }
+
+ if (android::base::GetBoolProperty("ro.config.low_ram", false)) {
+ return kLogBufferMinSize;
+ }
+
+ return kDefaultLogBufferSize;
+}
diff --git a/logd/LogSize.h b/logd/LogSize.h
new file mode 100644
index 000000000..d5716ff48
--- /dev/null
+++ b/logd/LogSize.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stddef.h>
+
+#include <log/log.h>
+
+static constexpr size_t kDefaultLogBufferSize = 256 * 1024;
+static constexpr size_t kLogBufferMinSize = 64 * 1024;
+static constexpr size_t kLogBufferMaxSize = 256 * 1024 * 1024;
+
+bool IsValidBufferSize(size_t value);
+
+// This returns the buffer size as set in system properties for use in LogBuffer::Init().
+// Note that `logcat -G` calls LogBuffer::SetSize(), which configures log buffer sizes without
+// setting these properties, so this function should never be used except for LogBuffer::Init().
+// LogBuffer::GetSize() should be used instead within logd. Other processes can use
+// android_logger_get_log_size() or `logcat -g` to query the actual allotted buffer size.
+size_t GetBufferSizeFromProperties(log_id_t log_id);
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index e222d3f24..faf928397 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -544,7 +544,7 @@ class LogStatistics {
bool ShouldPrune(log_id id, unsigned long max_size, unsigned long* prune_rows) const
EXCLUDES(lock_);
- // Snapshot of the sizes for a given log buffer.
+ // Return the consumed size of the given buffer.
size_t Sizes(log_id_t id) const EXCLUDES(lock_) {
auto lock = std::lock_guard{lock_};
if (overhead_[id]) {
@@ -552,6 +552,13 @@ class LogStatistics {
}
return mSizes[id];
}
+
+ // Return the uncompressed size of the contents of the given buffer.
+ size_t SizeReadable(log_id_t id) const EXCLUDES(lock_) {
+ auto lock = std::lock_guard{lock_};
+ return mSizes[id];
+ }
+
// TODO: Get rid of this entirely.
static size_t sizesTotal() {
return SizesTotal;
diff --git a/logd/LogTags.cpp b/logd/LogTags.cpp
index 1b7107f8a..6ab3b48b4 100644
--- a/logd/LogTags.cpp
+++ b/logd/LogTags.cpp
@@ -24,6 +24,7 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/uio.h>
#include <unistd.h>
#include <string>
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index df78a508a..c0f62d39c 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -20,12 +20,13 @@
#include <sys/types.h>
#include <private/android_logger.h>
-#include <sysutils/SocketClient.h>
#include <utils/FastStrcmp.h>
// Hijack this header as a common include file used by most all sources
// to report some utilities defined here and there.
+#define LOGD_SNDTIMEO 32
+
namespace android {
// Furnished in main.cpp. Caller must own and free returned value
diff --git a/logd/README.compression.md b/logd/README.compression.md
new file mode 100644
index 000000000..4ba634abd
--- /dev/null
+++ b/logd/README.compression.md
@@ -0,0 +1,81 @@
+# Log Compression instead of Chatty in Android S
+
+## The problem
+
+* Log buffer space is precious, but suffers from the tragedy of the commons
+* Log spam fills the buffers making them less useful in logcat/bugreports
+* “Spam” is often in the eye of the beholder: which messages are important depends on what you’re trying to debug
+
+## The idea
+
+* Chatty isn’t helping as much as we’d hoped, and is surprisingly expensive
+* Compress logs to make more efficient use of the buffer
+* Address the root cause of log spam at its source:
+ * Do not hide log spam at runtime, which de-incentivize fixes
+ * Add presubmit coverage similar to SELinux violations to keep log spam down
+
+---
+
+## Chatty in Theory
+
+* Delete messages classified as spam to extend the range of logs from other sources
+* “Spam” defined as:
+ * Logs from UIDs whose logs consume over 12.5% of a log buffer
+ * Back-to-back exact duplicate messages
+
+## Chatty in Practice
+
+* Developer confusion about missing and de-duplicated logs
+* Lowered incentive to fix the root cause of bad logging behavior
+* High CPU overhead
+* Memory usage greatly exceeds configured buffer size
+* Only marginal increase in log range
+
+---
+
+## Log Compression in Theory
+
+* Store many more logs in the same log buffer size => better for diagnosis
+* Memory usage stays below configured log size => better system health
+* No gaps in logs, no de-duplicated logs => no developer confusion
+* No hiding bad behavior => increased accountability/incentive to fix root causes
+
+## Log Compression Preliminary Results
+
+* Captured 2, 5 day periods of full time personal usage of Pixel 4 and replayed the logs offline
+* Compression vs Chatty:
+ * **3.5x more log messages on average**
+ * **50% less CPU usage**
+ * **50% less memory usage**
+
+---
+
+## Log Messages in 1MB
+
+* The number of log messages still available in logcat after ‘Message Count’ messages have been logged to a 1MB log buffer
+* Note: ‘Simple’ is the Chatty code without log spam detection and without de-duplication.
+
+![Total Log Count](doc_images/total_log_count.png)
+
+---
+
+## CPU Time
+
+* Total CPU time on ARM64 (Walleye) and 32bit x86 (Cuttlefish)
+* X axis represents different log buffer size configurations.
+ * Chatty uses significantly more CPU time at 1MB (the default Pixel configuration)
+ * Chatty scales poorly with increased log buffer sizes
+* Note: “simple” isn’t “compression without actually compressing”, it’s “chatty without doing the chatty elimination”, which is why “simple” is more expensive than “compression” on walleye.
+
+![CPU Time Walleye](doc_images/cpu_walleye.png)
+![CPU Time Cuttlefish](doc_images/cpu_cuttlefish.png)
+
+---
+
+## Memory Usage
+
+* The memory used by ‘Message Count’ messages, on both Walleye and Cuttlefish
+* Note: Chatty does not consider the metadata (UID, PID, timestamp, etc) in its calculation of log buffer size, so a 1MB log buffer will consume more than 1MB. Note that there are 8 log buffers, 5 of which are typically filled.
+
+![Memory Usage](doc_images/memory_usage.png)
+
diff --git a/logd/README.property b/logd/README.property
index 1d7d99022..8fd7f481b 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -7,8 +7,8 @@ ro.logd.auditd.main bool true selinux audit messages sent to main.
ro.logd.auditd.events bool true selinux audit messages sent to events.
persist.logd.security bool false Enable security buffer.
ro.organization_owned bool false Override persist.logd.security to false
-ro.logd.kernel bool+ svelte+ Enable klogd daemon
-ro.logd.statistics bool+ svelte+ Enable logcat -S statistics.
+ro.logd.kernel bool svelte+ Enable klogd daemon
+logd.statistics bool svelte+ Enable logcat -S statistics.
ro.debuggable number if not "1", logd.statistics &
ro.logd.kernel default false.
logd.logpersistd.enable bool auto Safe to start logpersist daemon service
@@ -57,11 +57,8 @@ logd.buffer_type string (empty) Set the log buffer type. Current choi
NB:
- auto - managed by /init
-- bool+ - "true", "false" and comma separated list of "eng" (forced false if
- ro.debuggable is not "1") or "svelte" (forced false if ro.config.low_ram is
- true).
- svelte - see ro.config.low_ram for details.
-- svelte+ - see ro.config.low_ram and ro.debuggable for details.
+- svelte+ - If empty, default to true if `ro.config.low_ram == false && ro.debuggable == true`
- ro - <base property> temporary override, ro.<base property> platform default.
- persist - <base property> override, persist.<base property> platform default.
- build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
diff --git a/logd/ReplayMessages.cpp b/logd/ReplayMessages.cpp
index 73b0bd068..56509ecad 100644
--- a/logd/ReplayMessages.cpp
+++ b/logd/ReplayMessages.cpp
@@ -40,17 +40,6 @@ using android::base::ParseInt;
using android::base::ParseUint;
using android::base::Split;
-#ifndef __ANDROID__
-// This is hard coded to 1MB on host. On device use persist.logd.size to configure.
-unsigned long __android_logger_get_buffer_size(log_id_t) {
- return 1 * 1024 * 1024;
-}
-
-bool __android_logger_valid_buffer_size(unsigned long) {
- return true;
-}
-#endif
-
char* android::uidToName(uid_t) {
return nullptr;
}
@@ -117,6 +106,23 @@ static log_time GetFirstTimeStamp(const MappedFile& recorded_messages) {
return meta->realtime;
}
+static LogMask BuffersToLogMask(const char* buffers) {
+ if (buffers == nullptr || !strcmp(buffers, "all")) {
+ return kLogMaskAll;
+ }
+ auto string_ids = Split(buffers, ",");
+ LogMask log_mask = 0;
+ for (const auto& string_id : string_ids) {
+ int buffer_id;
+ if (!ParseInt(string_id, &buffer_id, 0, 7)) {
+ fprintf(stderr, "Could not parse buffer_id '%s'\n", string_id.c_str());
+ exit(1);
+ }
+ log_mask |= 1 << buffer_id;
+ }
+ return log_mask;
+}
+
class StdoutWriter : public LogWriter {
public:
StdoutWriter() : LogWriter(0, true) {}
@@ -134,6 +140,11 @@ class StdoutWriter : public LogWriter {
return true;
}
+ void Shutdown() override {
+ fprintf(stderr, "LogWriter::Shutdown() called\n");
+ exit(1);
+ }
+
std::string name() const override { return "stdout writer"; }
};
@@ -291,24 +302,7 @@ class PrintLogs : public SingleBufferOperation {
PrintLogs(log_time first_log_timestamp, const char* buffer, const char* buffers,
const char* print_point)
: SingleBufferOperation(first_log_timestamp, buffer) {
- if (buffers != nullptr) {
- if (strcmp(buffers, "all") != 0) {
- std::vector<int> buffer_ids;
- auto string_ids = Split(buffers, ",");
- for (const auto& string_id : string_ids) {
- int result;
- if (!ParseInt(string_id, &result, 0, 7)) {
- fprintf(stderr, "Could not parse buffer_id '%s'\n", string_id.c_str());
- exit(1);
- }
- buffer_ids.emplace_back(result);
- }
- mask_ = 0;
- for (const auto& buffer_id : buffer_ids) {
- mask_ |= 1 << buffer_id;
- }
- }
- }
+ mask_ = BuffersToLogMask(buffers);
if (print_point != nullptr) {
uint64_t result = 0;
if (!ParseUint(print_point, &result)) {
@@ -326,7 +320,7 @@ class PrintLogs : public SingleBufferOperation {
}
}
- void End() {
+ void End() override {
std::unique_ptr<LogWriter> test_writer(new StdoutWriter());
std::unique_ptr<FlushToState> flush_to_state = log_buffer_->CreateFlushToState(1, mask_);
log_buffer_->FlushTo(test_writer.get(), *flush_to_state, nullptr);
@@ -353,7 +347,7 @@ class PrintLatency : public SingleBufferOperation {
durations_.emplace_back(duration);
}
- void End() {
+ void End() override {
std::sort(durations_.begin(), durations_.end());
auto q1 = durations_.size() / 4;
auto q2 = durations_.size() / 2;
@@ -373,6 +367,27 @@ class PrintLatency : public SingleBufferOperation {
std::vector<long long> durations_;
};
+class PrintAllLogs : public SingleBufferOperation {
+ public:
+ PrintAllLogs(log_time first_log_timestamp, const char* buffer, const char* buffers)
+ : SingleBufferOperation(first_log_timestamp, buffer) {
+ LogMask mask = BuffersToLogMask(buffers);
+ auto lock = std::unique_lock{reader_list_.reader_threads_lock()};
+ std::unique_ptr<LogWriter> stdout_writer(new StdoutWriter());
+ std::unique_ptr<LogReaderThread> log_reader(
+ new LogReaderThread(log_buffer_.get(), &reader_list_, std::move(stdout_writer),
+ false, 0, mask, 0, {}, 1, {}));
+ reader_list_.reader_threads().emplace_back(std::move(log_reader));
+ }
+
+ void Operation() override {
+ // If the rate of reading logs is slower than the rate of incoming logs, then the reader
+ // thread is disconnected to not overflow log buffers, therefore we artificially slow down
+ // the incoming log rate.
+ usleep(100);
+ }
+};
+
int main(int argc, char** argv) {
if (argc < 3) {
fprintf(stderr, "Usage: %s FILE OPERATION [BUFFER] [OPTIONS]\n", argv[0]);
@@ -415,6 +430,9 @@ int main(int argc, char** argv) {
} else if (!strcmp(argv[2], "print_logs")) {
operation.reset(new PrintLogs(first_log_timestamp, argv[3], argc > 4 ? argv[4] : nullptr,
argc > 5 ? argv[5] : nullptr));
+ } else if (!strcmp(argv[2], "print_all_logs")) {
+ operation.reset(
+ new PrintAllLogs(first_log_timestamp, argv[3], argc > 4 ? argv[4] : nullptr));
} else if (!strcmp(argv[2], "nothing")) {
operation.reset(new SingleBufferOperation(first_log_timestamp, argv[3]));
} else {
diff --git a/logd/SerializedLogBuffer.cpp b/logd/SerializedLogBuffer.cpp
index 972a3f3a9..5012d3d2b 100644
--- a/logd/SerializedLogBuffer.cpp
+++ b/logd/SerializedLogBuffer.cpp
@@ -23,6 +23,7 @@
#include <android-base/logging.h>
#include <android-base/scopeguard.h>
+#include "LogSize.h"
#include "LogStatistics.h"
#include "SerializedFlushToState.h"
@@ -34,8 +35,8 @@ SerializedLogBuffer::SerializedLogBuffer(LogReaderList* reader_list, LogTags* ta
void SerializedLogBuffer::Init() {
log_id_for_each(i) {
- if (SetSize(i, __android_logger_get_buffer_size(i))) {
- SetSize(i, LOG_BUFFER_MIN_SIZE);
+ if (!SetSize(i, GetBufferSizeFromProperties(i))) {
+ SetSize(i, kLogBufferMinSize);
}
}
@@ -74,6 +75,10 @@ int SerializedLogBuffer::Log(log_id_t log_id, log_time realtime, uid_t uid, pid_
return -EINVAL;
}
+ if (len > LOGGER_ENTRY_MAX_PAYLOAD) {
+ len = LOGGER_ENTRY_MAX_PAYLOAD;
+ }
+
if (!ShouldLog(log_id, msg, len)) {
stats_->AddTotal(log_id, len);
return -EACCES;
@@ -134,33 +139,42 @@ void SerializedLogBuffer::NotifyReadersOfPrune(
}
}
-bool SerializedLogBuffer::Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) {
- // Don't prune logs that are newer than the point at which any reader threads are reading from.
- LogReaderThread* oldest = nullptr;
+void SerializedLogBuffer::Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) {
auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
- for (const auto& reader_thread : reader_list_->reader_threads()) {
- if (!reader_thread->IsWatching(log_id)) {
- continue;
- }
- if (!oldest || oldest->start() > reader_thread->start() ||
- (oldest->start() == reader_thread->start() &&
- reader_thread->deadline().time_since_epoch().count() != 0)) {
- oldest = reader_thread.get();
- }
- }
auto& log_buffer = logs_[log_id];
auto it = log_buffer.begin();
while (it != log_buffer.end()) {
- if (oldest != nullptr && it->highest_sequence_number() >= oldest->start()) {
- break;
+ for (const auto& reader_thread : reader_list_->reader_threads()) {
+ if (!reader_thread->IsWatching(log_id)) {
+ continue;
+ }
+
+ if (reader_thread->deadline().time_since_epoch().count() != 0) {
+ // Always wake up wrapped readers when pruning. 'Wrapped' readers are an
+ // optimization that allows the reader to wait until logs starting at a specified
+ // time stamp are about to be pruned. This is error-prone however, since if that
+ // timestamp is about to be pruned, the reader is not likely to read the messages
+ // fast enough to not back-up logd. Instead, we can achieve an nearly-as-efficient
+ // but not error-prune batching effect by waking the reader whenever any chunk is
+ // about to be pruned.
+ reader_thread->triggerReader_Locked();
+ }
+
+ // Some readers may be still reading from this log chunk, log a warning that they are
+ // about to lose logs.
+ // TODO: We should forcefully disconnect the reader instead, such that the reader itself
+ // has an indication that they've lost logs.
+ if (reader_thread->start() <= it->highest_sequence_number()) {
+ LOG(WARNING) << "Skipping entries from slow reader, " << reader_thread->name()
+ << ", from LogBuffer::Prune()";
+ }
}
// Increment ahead of time since we're going to erase this iterator from the list.
auto it_to_prune = it++;
- // The sequence number check ensures that all readers have read all logs in this chunk, but
- // they may still hold a reference to the chunk to track their last read log_position.
+ // Readers may have a reference to the chunk to track their last read log_position.
// Notify them to delete the reference.
NotifyReadersOfPrune(log_id, it_to_prune);
@@ -175,42 +189,11 @@ bool SerializedLogBuffer::Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid
RemoveChunkFromStats(log_id, *it_to_prune);
log_buffer.erase(it_to_prune);
if (buffer_size >= bytes_to_free) {
- return true;
+ return;
}
bytes_to_free -= buffer_size;
}
}
-
- // If we've deleted all buffers without bytes_to_free hitting 0, then we're called by Clear()
- // and should return true.
- if (it == log_buffer.end()) {
- return true;
- }
-
- // Otherwise we are stuck due to a reader, so mitigate it.
- CHECK(oldest != nullptr);
- KickReader(oldest, log_id, bytes_to_free);
- return false;
-}
-
-// If the selected reader is blocking our pruning progress, decide on
-// what kind of mitigation is necessary to unblock the situation.
-void SerializedLogBuffer::KickReader(LogReaderThread* reader, log_id_t id, size_t bytes_to_free) {
- if (bytes_to_free >= max_size_[id]) { // +100%
- // A misbehaving or slow reader is dropped if we hit too much memory pressure.
- LOG(WARNING) << "Kicking blocked reader, " << reader->name()
- << ", from LogBuffer::kickMe()";
- reader->release_Locked();
- } else if (reader->deadline().time_since_epoch().count() != 0) {
- // Allow a blocked WRAP deadline reader to trigger and start reporting the log data.
- reader->triggerReader_Locked();
- } else {
- // Tell slow reader to skip entries to catch up.
- unsigned long prune_rows = bytes_to_free / 300;
- LOG(WARNING) << "Skipping " << prune_rows << " entries from slow reader, " << reader->name()
- << ", from LogBuffer::kickMe()";
- reader->triggerSkip_Locked(id, prune_rows);
- }
}
std::unique_ptr<FlushToState> SerializedLogBuffer::CreateFlushToState(uint64_t start,
@@ -251,12 +234,18 @@ bool SerializedLogBuffer::FlushTo(
}
}
+ // We copy the log entry such that we can flush it without the lock. We never block pruning
+ // waiting for this Flush() to complete.
+ constexpr size_t kMaxEntrySize = sizeof(*entry) + LOGGER_ENTRY_MAX_PAYLOAD + 1;
+ unsigned char entry_copy[kMaxEntrySize] __attribute__((uninitialized));
+ CHECK_LT(entry->msg_len(), LOGGER_ENTRY_MAX_PAYLOAD + 1);
+ memcpy(entry_copy, entry, sizeof(*entry) + entry->msg_len());
lock.unlock();
- // We never prune logs equal to or newer than any LogReaderThreads' `start` value, so the
- // `entry` pointer is safe here without the lock
- if (!entry->Flush(writer, log_id)) {
+
+ if (!reinterpret_cast<SerializedLogEntry*>(entry_copy)->Flush(writer, log_id)) {
return false;
}
+
lock.lock();
}
@@ -265,41 +254,14 @@ bool SerializedLogBuffer::FlushTo(
}
bool SerializedLogBuffer::Clear(log_id_t id, uid_t uid) {
- // Try three times to clear, then disconnect the readers and try one final time.
- for (int retry = 0; retry < 3; ++retry) {
- {
- auto lock = std::lock_guard{lock_};
- bool prune_success = Prune(id, ULONG_MAX, uid);
- if (prune_success) {
- return true;
- }
- }
- sleep(1);
- }
- // Check if it is still busy after the sleep, we try to 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.
- bool busy = false;
- {
- auto lock = std::lock_guard{lock_};
- busy = !Prune(id, 1, uid);
- }
- // It is still busy, disconnect all readers.
- if (busy) {
- auto reader_threads_lock = std::lock_guard{reader_list_->reader_threads_lock()};
- for (const auto& reader_thread : reader_list_->reader_threads()) {
- if (reader_thread->IsWatching(id)) {
- LOG(WARNING) << "Kicking blocked reader, " << reader_thread->name()
- << ", from LogBuffer::clear()";
- reader_thread->release_Locked();
- }
- }
- }
auto lock = std::lock_guard{lock_};
- return Prune(id, ULONG_MAX, uid);
+ Prune(id, ULONG_MAX, uid);
+
+ // Clearing SerializedLogBuffer never waits for readers and therefore is always successful.
+ return true;
}
-unsigned long SerializedLogBuffer::GetSizeUsed(log_id_t id) {
+size_t SerializedLogBuffer::GetSizeUsed(log_id_t id) {
size_t total_size = 0;
for (const auto& chunk : logs_[id]) {
total_size += chunk.PruneSize();
@@ -307,7 +269,7 @@ unsigned long SerializedLogBuffer::GetSizeUsed(log_id_t id) {
return total_size;
}
-unsigned long SerializedLogBuffer::GetSize(log_id_t id) {
+size_t SerializedLogBuffer::GetSize(log_id_t id) {
auto lock = std::lock_guard{lock_};
return max_size_[id];
}
@@ -315,10 +277,10 @@ unsigned long SerializedLogBuffer::GetSize(log_id_t id) {
// New SerializedLogChunk objects will be allocated according to the new size, but older one are
// unchanged. MaybePrune() is called on the log buffer to reduce it to an appropriate size if the
// new size is lower.
-int SerializedLogBuffer::SetSize(log_id_t id, unsigned long size) {
+bool SerializedLogBuffer::SetSize(log_id_t id, size_t size) {
// Reasonable limits ...
- if (!__android_logger_valid_buffer_size(size)) {
- return -1;
+ if (!IsValidBufferSize(size)) {
+ return false;
}
auto lock = std::lock_guard{lock_};
@@ -326,5 +288,5 @@ int SerializedLogBuffer::SetSize(log_id_t id, unsigned long size) {
MaybePrune(id);
- return 0;
+ return true;
}
diff --git a/logd/SerializedLogBuffer.h b/logd/SerializedLogBuffer.h
index a03050e22..294cfe6f1 100644
--- a/logd/SerializedLogBuffer.h
+++ b/logd/SerializedLogBuffer.h
@@ -47,27 +47,25 @@ class SerializedLogBuffer final : public LogBuffer {
log_time realtime)>& filter) override;
bool Clear(log_id_t id, uid_t uid) override;
- unsigned long GetSize(log_id_t id) override;
- int SetSize(log_id_t id, unsigned long size) override;
+ size_t GetSize(log_id_t id) override;
+ bool SetSize(log_id_t id, size_t size) override;
uint64_t sequence() const override { return sequence_.load(std::memory_order_relaxed); }
private:
bool ShouldLog(log_id_t log_id, const char* msg, uint16_t len);
void MaybePrune(log_id_t log_id) REQUIRES(lock_);
- bool Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) REQUIRES(lock_);
- void KickReader(LogReaderThread* reader, log_id_t id, size_t bytes_to_free)
- REQUIRES_SHARED(lock_);
+ void Prune(log_id_t log_id, size_t bytes_to_free, uid_t uid) REQUIRES(lock_);
void NotifyReadersOfPrune(log_id_t log_id, const std::list<SerializedLogChunk>::iterator& chunk)
REQUIRES(reader_list_->reader_threads_lock());
void RemoveChunkFromStats(log_id_t log_id, SerializedLogChunk& chunk);
- unsigned long GetSizeUsed(log_id_t id) REQUIRES(lock_);
+ size_t GetSizeUsed(log_id_t id) REQUIRES(lock_);
LogReaderList* reader_list_;
LogTags* tags_;
LogStatistics* stats_;
- unsigned long max_size_[LOG_ID_MAX] GUARDED_BY(lock_) = {};
+ size_t max_size_[LOG_ID_MAX] GUARDED_BY(lock_) = {};
std::list<SerializedLogChunk> logs_[LOG_ID_MAX] GUARDED_BY(lock_);
RwLock lock_;
diff --git a/logd/SerializedLogChunk.cpp b/logd/SerializedLogChunk.cpp
index de641d62a..e4d89451d 100644
--- a/logd/SerializedLogChunk.cpp
+++ b/logd/SerializedLogChunk.cpp
@@ -25,12 +25,10 @@ SerializedLogChunk::~SerializedLogChunk() {
}
void SerializedLogChunk::Compress() {
- if (compressed_log_.size() == 0) {
- CompressionEngine::GetInstance().Compress(contents_, write_offset_, compressed_log_);
- LOG(INFO) << "Compressed Log, buffer max size: " << contents_.size()
- << " size used: " << write_offset_
- << " compressed size: " << compressed_log_.size();
- }
+ CHECK_EQ(compressed_log_.size(), 0U);
+ CompressionEngine::GetInstance().Compress(contents_, write_offset_, compressed_log_);
+ LOG(INFO) << "Compressed Log, buffer max size: " << contents_.size()
+ << " size used: " << write_offset_ << " compressed size: " << compressed_log_.size();
}
// TODO: Develop a better reference counting strategy to guard against the case where the writer is
@@ -89,9 +87,11 @@ bool SerializedLogChunk::ClearUidLogs(uid_t uid, log_id_t log_id, LogStatistics*
// Clear the old compressed logs and set write_offset_ appropriately to compress the new
// partially cleared log.
if (new_write_offset != write_offset_) {
- compressed_log_.Resize(0);
write_offset_ = new_write_offset;
- Compress();
+ if (!writer_active_) {
+ compressed_log_.Resize(0);
+ Compress();
+ }
}
DecReaderRefCount();
diff --git a/logd/SerializedLogChunkTest.cpp b/logd/SerializedLogChunkTest.cpp
index f10b9c673..3b451252d 100644
--- a/logd/SerializedLogChunkTest.cpp
+++ b/logd/SerializedLogChunkTest.cpp
@@ -281,3 +281,27 @@ TEST_P(UidClearTest, save_beginning_and_end) {
}
INSTANTIATE_TEST_CASE_P(UidClearTests, UidClearTest, testing::Values(true, false));
+
+// b/166187079: ClearUidLogs() should not compress the log if writer_active_ is true
+TEST(SerializedLogChunk, ClearUidLogs_then_FinishWriting) {
+ static constexpr size_t kChunkSize = 10 * 4096;
+ static constexpr uid_t kUidToClear = 1000;
+ static constexpr uid_t kOtherUid = 1234;
+
+ SerializedLogChunk chunk{kChunkSize};
+ static const char msg1[] = "saved first message";
+ static const char msg2[] = "cleared interior message";
+ static const char msg3[] = "last message stays";
+ ASSERT_NE(nullptr, chunk.Log(1, log_time(), kOtherUid, 1, 2, msg1, sizeof(msg1)));
+ ASSERT_NE(nullptr, chunk.Log(2, log_time(), kUidToClear, 1, 2, msg2, sizeof(msg2)));
+ ASSERT_NE(nullptr, chunk.Log(3, log_time(), kOtherUid, 1, 2, msg3, sizeof(msg3)));
+
+ chunk.ClearUidLogs(kUidToClear, LOG_ID_MAIN, nullptr);
+
+ ASSERT_NE(nullptr, chunk.Log(4, log_time(), kOtherUid, 1, 2, msg3, sizeof(msg3)));
+
+ chunk.FinishWriting();
+ // The next line would violate a CHECK() during decompression with the faulty code.
+ chunk.IncReaderRefCount();
+ chunk.DecReaderRefCount();
+}
diff --git a/logd/SimpleLogBuffer.cpp b/logd/SimpleLogBuffer.cpp
index ec08d54d4..b00dd25be 100644
--- a/logd/SimpleLogBuffer.cpp
+++ b/logd/SimpleLogBuffer.cpp
@@ -19,6 +19,7 @@
#include <android-base/logging.h>
#include "LogBufferElement.h"
+#include "LogSize.h"
SimpleLogBuffer::SimpleLogBuffer(LogReaderList* reader_list, LogTags* tags, LogStatistics* stats)
: reader_list_(reader_list), tags_(tags), stats_(stats) {
@@ -29,8 +30,8 @@ SimpleLogBuffer::~SimpleLogBuffer() {}
void SimpleLogBuffer::Init() {
log_id_for_each(i) {
- if (SetSize(i, __android_logger_get_buffer_size(i))) {
- SetSize(i, LOG_BUFFER_MIN_SIZE);
+ if (!SetSize(i, GetBufferSizeFromProperties(i))) {
+ SetSize(i, kLogBufferMinSize);
}
}
@@ -247,22 +248,22 @@ bool SimpleLogBuffer::Clear(log_id_t id, uid_t uid) {
}
// get the total space allocated to "id"
-unsigned long SimpleLogBuffer::GetSize(log_id_t id) {
+size_t SimpleLogBuffer::GetSize(log_id_t id) {
auto lock = SharedLock{lock_};
size_t retval = max_size_[id];
return retval;
}
// set the total space allocated to "id"
-int SimpleLogBuffer::SetSize(log_id_t id, unsigned long size) {
+bool SimpleLogBuffer::SetSize(log_id_t id, size_t size) {
// Reasonable limits ...
- if (!__android_logger_valid_buffer_size(size)) {
- return -1;
+ if (!IsValidBufferSize(size)) {
+ return false;
}
auto lock = std::lock_guard{lock_};
max_size_[id] = size;
- return 0;
+ return true;
}
void SimpleLogBuffer::MaybePrune(log_id_t id) {
diff --git a/logd/SimpleLogBuffer.h b/logd/SimpleLogBuffer.h
index 9f7d69903..8e5b50eb5 100644
--- a/logd/SimpleLogBuffer.h
+++ b/logd/SimpleLogBuffer.h
@@ -41,8 +41,8 @@ class SimpleLogBuffer : public LogBuffer {
log_time realtime)>& filter) override;
bool Clear(log_id_t id, uid_t uid) override;
- unsigned long GetSize(log_id_t id) override;
- int SetSize(log_id_t id, unsigned long size) override final;
+ size_t GetSize(log_id_t id) override;
+ bool SetSize(log_id_t id, size_t size) override final;
uint64_t sequence() const override { return sequence_.load(std::memory_order_relaxed); }
@@ -60,7 +60,7 @@ class SimpleLogBuffer : public LogBuffer {
LogStatistics* stats() { return stats_; }
LogReaderList* reader_list() { return reader_list_; }
- unsigned long max_size(log_id_t id) REQUIRES_SHARED(lock_) { return max_size_[id]; }
+ size_t max_size(log_id_t id) REQUIRES_SHARED(lock_) { return max_size_[id]; }
std::list<LogBufferElement>& logs() { return logs_; }
RwLock lock_;
@@ -75,7 +75,7 @@ class SimpleLogBuffer : public LogBuffer {
std::atomic<uint64_t> sequence_ = 1;
- unsigned long max_size_[LOG_ID_MAX] GUARDED_BY(lock_);
+ size_t max_size_[LOG_ID_MAX] GUARDED_BY(lock_);
std::list<LogBufferElement> logs_ GUARDED_BY(lock_);
// Keeps track of the iterator to the oldest log message of a given log type, as an
// optimization when pruning logs. Use GetOldest() to retrieve.
diff --git a/logd/doc_images/cpu_cuttlefish.png b/logd/doc_images/cpu_cuttlefish.png
new file mode 100644
index 000000000..8d809ca57
--- /dev/null
+++ b/logd/doc_images/cpu_cuttlefish.png
Binary files differ
diff --git a/logd/doc_images/cpu_walleye.png b/logd/doc_images/cpu_walleye.png
new file mode 100644
index 000000000..39c951b5f
--- /dev/null
+++ b/logd/doc_images/cpu_walleye.png
Binary files differ
diff --git a/logd/doc_images/memory_usage.png b/logd/doc_images/memory_usage.png
new file mode 100644
index 000000000..434d6d310
--- /dev/null
+++ b/logd/doc_images/memory_usage.png
Binary files differ
diff --git a/logd/doc_images/total_log_count.png b/logd/doc_images/total_log_count.png
new file mode 100644
index 000000000..e73c2c1ce
--- /dev/null
+++ b/logd/doc_images/total_log_count.png
Binary files differ
diff --git a/logd/fuzz/log_buffer_log_fuzzer.cpp b/logd/fuzz/log_buffer_log_fuzzer.cpp
index 8309f9514..d71a2f91f 100644
--- a/logd/fuzz/log_buffer_log_fuzzer.cpp
+++ b/logd/fuzz/log_buffer_log_fuzzer.cpp
@@ -30,16 +30,6 @@
#define MIN_TAG_ID 1000
#define TAG_MOD 10
-#ifndef __ANDROID__
-unsigned long __android_logger_get_buffer_size(log_id_t) {
- return 1024 * 1024;
-}
-
-bool __android_logger_valid_buffer_size(unsigned long) {
- return true;
-}
-#endif
-
char* android::uidToName(uid_t) {
return strdup("fake");
}
diff --git a/logd/logd_test.cpp b/logd/logd_test.cpp
index ed34ea413..828f58056 100644
--- a/logd/logd_test.cpp
+++ b/logd/logd_test.cpp
@@ -30,6 +30,7 @@
#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <cutils/sockets.h>
#include <gtest/gtest.h>
#include <log/log_read.h>
@@ -39,7 +40,9 @@
#include <selinux/selinux.h>
#endif
-#include "LogReader.h" // pickup LOGD_SNDTIMEO
+#include "LogUtils.h" // For LOGD_SNDTIMEO.
+
+using android::base::unique_fd;
#ifdef __ANDROID__
static void send_to_control(char* buf, size_t len) {
@@ -162,6 +165,7 @@ static char* find_benchmark_spam(char* cp) {
}
#endif
+#ifdef LOGD_ENABLE_FLAKY_TESTS
TEST(logd, statistics) {
#ifdef __ANDROID__
size_t len;
@@ -237,6 +241,7 @@ TEST(logd, statistics) {
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
#ifdef __ANDROID__
static void caught_signal(int /* signum */) {
@@ -720,6 +725,7 @@ TEST(logd, timeout) {
}
#endif
+#ifdef LOGD_ENABLE_FLAKY_TESTS
// b/27242723 confirmed fixed
TEST(logd, SNDTIMEO) {
#ifdef __ANDROID__
@@ -777,6 +783,7 @@ TEST(logd, SNDTIMEO) {
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
+#endif
TEST(logd, getEventTag_list) {
#ifdef __ANDROID__
@@ -833,126 +840,33 @@ TEST(logd, getEventTag_newentry) {
#endif
}
+TEST(logd, no_epipe) {
#ifdef __ANDROID__
-static inline uint32_t get4LE(const uint8_t* src) {
- return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
-static inline uint32_t get4LE(const char* src) {
- return get4LE(reinterpret_cast<const uint8_t*>(src));
-}
-#endif
-
-void __android_log_btwrite_multiple__helper(int count) {
-#ifdef __ANDROID__
- log_time ts(CLOCK_MONOTONIC);
- usleep(100);
- log_time ts1(CLOCK_MONOTONIC);
-
- // We fork to create a unique pid for the submitted log messages
- // so that we do not collide with the other _multiple_ tests.
-
- pid_t pid = fork();
-
- if (pid == 0) {
- // child
- for (int i = count; i; --i) {
- ASSERT_LT(
- 0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
- usleep(100);
- }
- ASSERT_LT(0,
- __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
- usleep(1000000);
-
- _exit(0);
- }
-
- siginfo_t info = {};
- ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
- ASSERT_EQ(0, info.si_status);
-
- struct logger_list* logger_list;
- ASSERT_TRUE(nullptr != (logger_list = android_logger_list_open(LOG_ID_EVENTS,
- ANDROID_LOG_NONBLOCK, 0, pid)));
-
- int expected_count = (count < 2) ? count : 2;
- int expected_chatty_count = (count <= 2) ? 0 : 1;
- int expected_identical_count = (count < 2) ? 0 : (count - 2);
- static const int expected_expire_count = 0;
-
- count = 0;
- int second_count = 0;
- int chatty_count = 0;
- int identical_count = 0;
- int expire_count = 0;
-
- for (;;) {
- log_msg log_msg;
- if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
-
- if ((log_msg.entry.pid != pid) || (log_msg.entry.len < (4 + 1 + 8)) ||
- (log_msg.id() != LOG_ID_EVENTS))
- continue;
-
- char* eventData = log_msg.msg();
- if (!eventData) continue;
-
- uint32_t tag = get4LE(eventData);
-
- if ((eventData[4] == EVENT_TYPE_LONG) &&
- (log_msg.entry.len == (4 + 1 + 8))) {
- if (tag != 0) continue;
-
- log_time* tx = reinterpret_cast<log_time*>(eventData + 4 + 1);
- if (ts == *tx) {
- ++count;
- } else if (ts1 == *tx) {
- ++second_count;
- }
- } else if (eventData[4] == EVENT_TYPE_STRING) {
- if (tag != CHATTY_LOG_TAG) continue;
- ++chatty_count;
- // int len = get4LE(eventData + 4 + 1);
- log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
- const char* cp;
- if ((cp = strstr(eventData + 4 + 1 + 4, " identical "))) {
- unsigned val = 0;
- sscanf(cp, " identical %u lines", &val);
- identical_count += val;
- } else if ((cp = strstr(eventData + 4 + 1 + 4, " expire "))) {
- unsigned val = 0;
- sscanf(cp, " expire %u lines", &val);
- expire_count += val;
- }
- }
+ // Actually generating SIGPIPE in logd is racy, since we need to close the socket quicker than
+ // logd finishes writing the data to it, so we try 10 times, which should be enough to trigger
+ // SIGPIPE if logd isn't ignoring SIGPIPE
+ for (int i = 0; i < 10; ++i) {
+ unique_fd sock1(
+ socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
+ ASSERT_GT(sock1, 0);
+ unique_fd sock2(
+ socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM));
+ ASSERT_GT(sock2, 0);
+
+ std::string message = "getStatistics 0 1 2 3 4 5 6 7";
+
+ ASSERT_GT(write(sock1, message.c_str(), message.length()), 0);
+ sock1.reset();
+ ASSERT_GT(write(sock2, message.c_str(), message.length()), 0);
+
+ struct pollfd p = {.fd = sock2, .events = POLLIN, .revents = 0};
+
+ int ret = poll(&p, 1, 20);
+ EXPECT_EQ(ret, 1);
+ EXPECT_TRUE(p.revents & POLLIN);
+ EXPECT_FALSE(p.revents & POLL_ERR);
}
-
- android_logger_list_close(logger_list);
-
- EXPECT_EQ(expected_count, count);
- EXPECT_EQ(1, second_count);
- EXPECT_EQ(expected_chatty_count, chatty_count);
- EXPECT_EQ(expected_identical_count, identical_count);
- EXPECT_EQ(expected_expire_count, expire_count);
#else
- count = 0;
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
}
-
-TEST(logd, multiple_test_1) {
- __android_log_btwrite_multiple__helper(1);
-}
-
-TEST(logd, multiple_test_2) {
- __android_log_btwrite_multiple__helper(2);
-}
-
-TEST(logd, multiple_test_3) {
- __android_log_btwrite_multiple__helper(3);
-}
-
-TEST(logd, multiple_test_10) {
- __android_log_btwrite_multiple__helper(10);
-}
diff --git a/logd/main.cpp b/logd/main.cpp
index 897e11e3a..c92c5b719 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -62,7 +62,9 @@
#include "SerializedLogBuffer.h"
#include "SimpleLogBuffer.h"
+using android::base::GetBoolProperty;
using android::base::GetProperty;
+using android::base::SetProperty;
#define KMSG_PRIORITY(PRI) \
'<', '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
@@ -82,9 +84,10 @@ static void DropPrivs(bool klogd, bool auditd) {
PLOG(FATAL) << "failed to set batch scheduler";
}
- if (!__android_logger_property_get_bool("ro.debuggable", BOOL_DEFAULT_FALSE) &&
- prctl(PR_SET_DUMPABLE, 0) == -1) {
- PLOG(FATAL) << "failed to clear PR_SET_DUMPABLE";
+ if (!GetBoolProperty("ro.debuggable", false)) {
+ if (prctl(PR_SET_DUMPABLE, 0) == -1) {
+ PLOG(FATAL) << "failed to clear PR_SET_DUMPABLE";
+ }
}
std::unique_ptr<struct _cap_struct, int (*)(void*)> caps(cap_init(), cap_free);
@@ -110,6 +113,14 @@ static void DropPrivs(bool klogd, bool auditd) {
}
}
+// GetBoolProperty that defaults to true if `ro.debuggable == true && ro.config.low_rawm == false`.
+static bool GetBoolPropertyEngSvelteDefault(const std::string& name) {
+ bool default_value =
+ GetBoolProperty("ro.debuggable", false) && !GetBoolProperty("ro.config.low_ram", false);
+
+ return GetBoolProperty(name, default_value);
+}
+
char* android::uidToName(uid_t u) {
struct Userdata {
uid_t uid;
@@ -207,6 +218,8 @@ static int issueReinit() {
// logging plugins like auditd and restart control. Additional
// transitory per-client threads are created for each reader.
int main(int argc, char* argv[]) {
+ // We want EPIPE when a reader disconnects, not to terminate logd.
+ signal(SIGPIPE, SIG_IGN);
// logd is written under the assumption that the timezone is UTC.
// If TZ is not set, persist.sys.timezone is looked up in some time utility
// libc functions, including mktime. It confuses the logd time handling,
@@ -236,10 +249,9 @@ int main(int argc, char* argv[]) {
}
int fdPmesg = -1;
- bool klogd = __android_logger_property_get_bool(
- "ro.logd.kernel",
- BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+ bool klogd = GetBoolPropertyEngSvelteDefault("ro.logd.kernel");
if (klogd) {
+ SetProperty("ro.logd.kernel", "true");
static const char proc_kmsg[] = "/proc/kmsg";
fdPmesg = android_get_control_file(proc_kmsg);
if (fdPmesg < 0) {
@@ -249,7 +261,7 @@ int main(int argc, char* argv[]) {
if (fdPmesg < 0) PLOG(ERROR) << "Failed to open " << proc_kmsg;
}
- bool auditd = __android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
+ bool auditd = GetBoolProperty("ro.logd.auditd", true);
DropPrivs(klogd, auditd);
// A cache of event log tags
@@ -258,13 +270,11 @@ int main(int argc, char* argv[]) {
// Pruning configuration.
PruneList prune_list;
- std::string buffer_type = GetProperty("logd.buffer_type", "chatty");
+ std::string buffer_type = GetProperty("logd.buffer_type", "serialized");
// Partial (required for chatty) or full logging statistics.
- bool enable_full_log_statistics = __android_logger_property_get_bool(
- "logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
- BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
- LogStatistics log_statistics(enable_full_log_statistics, buffer_type == "serialized");
+ LogStatistics log_statistics(GetBoolPropertyEngSvelteDefault("logd.statistics"),
+ buffer_type == "serialized");
// Serves the purpose of managing the last logs times read on a socket connection, and as a
// reader lock on a range of log entries.
@@ -309,9 +319,7 @@ int main(int argc, char* argv[]) {
// and LogReader is notified to send updates to connected clients.
LogAudit* al = nullptr;
if (auditd) {
- int dmesg_fd = __android_logger_property_get_bool("ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
- ? fdDmesg
- : -1;
+ int dmesg_fd = GetBoolProperty("ro.logd.auditd.dmesg", true) ? fdDmesg : -1;
al = new LogAudit(log_buffer, dmesg_fd, &log_statistics);
}
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index a64306280..3907413cc 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -139,7 +139,7 @@ TEST(propertyinfoserializer, GetPropertyInfo) {
auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
- // Sanity check
+ // Smoke test
auto root_node = property_info_area->root_node();
EXPECT_STREQ("root", root_node.name());
EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
diff --git a/qemu_pipe/qemu_pipe.cpp b/qemu_pipe/qemu_pipe.cpp
index beeccb07f..03afb211e 100644
--- a/qemu_pipe/qemu_pipe.cpp
+++ b/qemu_pipe/qemu_pipe.cpp
@@ -35,7 +35,6 @@ using android::base::WriteFully;
#endif
int qemu_pipe_open(const char* pipeName) {
- // Sanity check.
if (!pipeName) {
errno = EINVAL;
return -1;
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index 5de422f62..967205f93 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -10,6 +10,7 @@ libEGL.so
libGLESv1_CM.so
libGLESv2.so
libGLESv3.so
+libicu.so
libicui18n.so
libicuuc.so
libjnigraphics.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 4aa5b0d8a..52b016d2d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -81,6 +81,11 @@ on early-init
# Mount tracefs
mount tracefs tracefs /sys/kernel/tracing
+ # create sys dirctory
+ mkdir /dev/sys 0755 system system
+ mkdir /dev/sys/fs 0755 system system
+ mkdir /dev/sys/block 0755 system system
+
# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
on early-init && property:ro.product.cpu.abilist32=*
exec_start boringssl_self_test32
@@ -333,16 +338,6 @@ on init
chmod 0664 /dev/cpuset/restricted/tasks
chmod 0664 /dev/cpuset/tasks
- # freezer cgroup entries
- mkdir /dev/freezer/frozen
- write /dev/freezer/frozen/freezer.state FROZEN
- chown system system /dev/freezer/cgroup.procs
- chown system system /dev/freezer/frozen
- chown system system /dev/freezer/frozen/freezer.state
- chown system system /dev/freezer/frozen/cgroup.procs
-
- chmod 0664 /dev/freezer/frozen/freezer.state
-
# make the PSI monitor accessible to others
chown system system /proc/pressure/memory
chmod 0664 /proc/pressure/memory
@@ -357,8 +352,6 @@ on init
# This is needed by any process that uses socket tagging.
chmod 0644 /dev/xt_qtaguid
- chown root root /dev/cg2_bpf
- chmod 0600 /dev/cg2_bpf
mount bpf bpf /sys/fs/bpf nodev noexec nosuid
# Create location for fs_mgr to store abbreviated output from filesystem
@@ -484,6 +477,10 @@ on post-fs
# The bind+remount combination allows this to work in containers.
mount rootfs rootfs / remount bind ro nodev
+ # Mount default storage into root namespace
+ mount none /mnt/user/0 /storage bind rec
+ mount none none /storage slave rec
+
# Make sure /sys/kernel/debug (if present) is labeled properly
# Note that tracefs may be mounted under debug, so we need to cross filesystems
restorecon --recursive --cross-filesystems /sys/kernel/debug
@@ -533,6 +530,7 @@ on post-fs
mkdir /metadata/bootstat 0750 system log
mkdir /metadata/ota 0700 root system
mkdir /metadata/ota/snapshots 0700 root system
+ mkdir /metadata/userspacereboot 0770 root system
mkdir /metadata/apex 0700 root system
mkdir /metadata/apex/sessions 0700 root system
@@ -680,6 +678,15 @@ on post-fs-data
mkdir /data/vendor/tombstones 0771 root root
mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
+ # Create directories to push tests to for each linker namespace.
+ # Create the subdirectories in case the first test is run as root
+ # so it doesn't end up owned by root.
+ mkdir /data/local/tests 0700 shell shell
+ mkdir /data/local/tests/product 0700 shell shell
+ mkdir /data/local/tests/system 0700 shell shell
+ mkdir /data/local/tests/unrestricted 0700 shell shell
+ mkdir /data/local/tests/vendor 0700 shell shell
+
# create dalvik-cache, so as to enforce our permissions
mkdir /data/dalvik-cache 0771 root root encryption=Require
# create the A/B OTA directory, so as to enforce our permissions
@@ -819,22 +826,6 @@ on post-fs-data
# Enable FUSE by default
setprop persist.sys.fuse true
-# Switch between sdcardfs and FUSE depending on persist property
-# TODO: Move this to ro property before launch because FDE devices
-# interact with persistent properties differently during boot
-on zygote-start && property:persist.sys.fuse=true
- # Mount default storage into root namespace
- mount none /mnt/user/0 /storage bind rec
- mount none none /storage slave rec
-on zygote-start && property:persist.sys.fuse=false
- # Mount default storage into root namespace
- mount none /mnt/runtime/default /storage bind rec
- mount none none /storage slave rec
-on zygote-start && property:persist.sys.fuse=""
- # Mount default storage into root namespace
- mount none /mnt/runtime/default /storage bind rec
- mount none none /storage slave rec
-
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted
@@ -887,18 +878,26 @@ on boot
chown root system /sys/block/zram0/writeback
chmod 0664 /sys/block/zram0/writeback
+ # to access F2FS sysfs on dm-<num> directly
+ mkdir /dev/sys/fs/by-name 0755 system system
+ symlink /sys/fs/f2fs/${dev.mnt.blk.data} /dev/sys/fs/by-name/userdata
+
+ # to access dm-<num> sysfs
+ mkdir /dev/sys/block/by-name 0755 system system
+ symlink /sys/devices/virtual/block/${dev.mnt.blk.data} /dev/sys/block/by-name/userdata
+
# F2FS tuning. Set cp_interval larger than dirty_expire_centisecs, 30 secs,
# to avoid power consumption when system becomes mostly idle. Be careful
# to make it too large, since it may bring userdata loss, if they
# are not aware of using fsync()/sync() to prepare sudden power-cut.
- write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
- write /sys/fs/f2fs/${dev.mnt.blk.data}/gc_urgent_sleep_time 50
- write /sys/fs/f2fs/${dev.mnt.blk.data}/iostat_enable 1
+ write /dev/sys/fs/by-name/userdata/cp_interval 200
+ write /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time 50
+ write /dev/sys/fs/by-name/userdata/iostat_enable 1
# limit discard size to 128MB in order to avoid long IO latency
# for filesystem tuning first (dm or sda)
# Note that, if dm-<num> is used, sda/mmcblk0 should be tuned in vendor/init.rc
- write /sys/devices/virtual/block/${dev.mnt.blk.data}/queue/discard_max_bytes 134217728
+ write /dev/sys/block/by-name/userdata/queue/discard_max_bytes 134217728
# Permissions for System Server and daemons.
chown system system /sys/power/autosleep
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 9c2cdf27f..1994bdb8c 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -17,6 +17,9 @@ subsystem sound
devname uevent_devpath
dirname /dev/snd
+subsystem dma_heap
+ devname uevent_devpath
+ dirname /dev/dma_heap
# ueventd can only set permissions on device nodes and their associated
# sysfs attributes, not on arbitrary paths.
#
@@ -39,6 +42,7 @@ subsystem sound
/dev/vndbinder 0666 root root
/dev/pmsg0 0222 root log
+/dev/dma_heap/system 0666 system system
# kms driver for drm based gpu
/dev/dri/* 0666 root graphics
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 3bee87507..4510dc7a4 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -98,7 +98,7 @@ BSD: dd du grep
toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
-toybox: acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
env expand expr fallocate false find free getenforce getprop groups
head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
@@ -118,7 +118,7 @@ BSD: dd grep
toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
sendevent start stop top
-toybox: acpi base64 basename blockdev bzcat cal cat chcon chgrp chmod
+toybox (0.7.0-ish): acpi base64 basename blockdev bzcat cal cat chcon chgrp chmod
chown chroot cksum clear comm cmp cp cpio cut date df dirname dmesg
dos2unix du echo env expand expr fallocate false find flock free
getenforce getprop groups head hostname hwclock id ifconfig inotifyd
@@ -140,7 +140,7 @@ bzip2: bzcat bzip2 bunzip2
toolbox: getevent newfs\_msdos
-toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
dos2unix du echo env expand expr fallocate false file find flock free
getenforce getprop groups gunzip gzip head hostname hwclock id ifconfig
@@ -166,7 +166,7 @@ one-true-awk: awk
toolbox: getevent getprop newfs\_msdos
-toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
dos2unix du echo env expand expr fallocate false file find flock fmt free
getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
@@ -192,7 +192,7 @@ one-true-awk: awk
toolbox: getevent getprop
-toybox: acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
+toybox (0.8.0-ish): acpi base64 basename bc blkid blockdev cal cat chattr chcon chgrp
chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
false fgrep file find flock fmt free freeramdisk fsfreeze getconf
@@ -224,7 +224,7 @@ one-true-awk: awk
toolbox: getevent getprop setprop start stop
-toybox: acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
+toybox (0.8.3-ish): acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
chown chroot chrt cksum clear cmp comm cp cpio cut date dd devmem
df diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
false fgrep file find flock fmt free freeramdisk fsfreeze fsync getconf
diff --git a/trusty/confirmationui/NotSoSecureInput.cpp b/trusty/confirmationui/NotSoSecureInput.cpp
index 3d9a2d6e5..18e45cd35 100644
--- a/trusty/confirmationui/NotSoSecureInput.cpp
+++ b/trusty/confirmationui/NotSoSecureInput.cpp
@@ -82,7 +82,7 @@ Nonce generateNonce() {
/**
* This is an implementation of the SecureInput protocol in unserspace. This is
- * just an example and should not be used as is. The protocol implemented her
+ * just an example and should not be used as is. The protocol implemented here
* should be used by a trusted input device that can assert user events with
* high assurance even if the HLOS kernel is compromised. A confirmationui HAL
* that links directly against this implementation is not secure and shal not be
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
index d14966460..e416fb2ac 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.cpp
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -56,9 +56,9 @@ TrustyGateKeeperDevice::~TrustyGateKeeperDevice() {
SizedBuffer hidl_vec2sized_buffer(const hidl_vec<uint8_t>& vec) {
if (vec.size() == 0 || vec.size() > std::numeric_limits<uint32_t>::max()) return {};
- auto dummy = new uint8_t[vec.size()];
- std::copy(vec.begin(), vec.end(), dummy);
- return {dummy, static_cast<uint32_t>(vec.size())};
+ auto buffer = new uint8_t[vec.size()];
+ std::copy(vec.begin(), vec.end(), buffer);
+ return {buffer, static_cast<uint32_t>(vec.size())};
}
Return<void> TrustyGateKeeperDevice::enroll(uint32_t uid,
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 6840baa54..27e1a3f5b 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -75,3 +75,36 @@ cc_binary {
vintf_fragments: ["4.0/android.hardware.keymaster@4.0-service.trusty.xml"],
}
+
+prebuilt_etc {
+ name: "keymaster_soft_attestation_keys.xml",
+ vendor: true,
+ src: "set_attestation_key/keymaster_soft_attestation_keys.xml",
+}
+
+cc_binary {
+ name: "trusty_keymaster_set_attestation_key",
+ vendor: true,
+
+ srcs: [
+ "set_attestation_key/set_attestation_key.cpp",
+ "ipc/trusty_keymaster_ipc.cpp",
+ ],
+
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "libc",
+ "libcrypto",
+ "liblog",
+ "libtrusty",
+ "libhardware",
+ "libkeymaster_messages",
+ "libxml2",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index f3ef747f6..750a9d71c 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -173,7 +173,7 @@ void TrustyKeymaster::AbortOperation(const AbortOperationRequest& request,
}
GetHmacSharingParametersResponse TrustyKeymaster::GetHmacSharingParameters() {
- // Dummy empty buffer to allow ForwardCommand to have something to serialize
+ // Empty buffer to allow ForwardCommand to have something to serialize
Buffer request;
GetHmacSharingParametersResponse response;
ForwardCommand(KM_GET_HMAC_SHARING_PARAMETERS, request, &response);
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index 13e672573..ce2cc2e10 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -53,6 +53,19 @@ enum keymaster_command : uint32_t {
KM_DELETE_ALL_KEYS = (23 << KEYMASTER_REQ_SHIFT),
KM_DESTROY_ATTESTATION_IDS = (24 << KEYMASTER_REQ_SHIFT),
KM_IMPORT_WRAPPED_KEY = (25 << KEYMASTER_REQ_SHIFT),
+
+ // Bootloader/provisioning calls.
+ KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
+ KM_SET_ATTESTATION_KEY = (0x2000 << KEYMASTER_REQ_SHIFT),
+ KM_APPEND_ATTESTATION_CERT_CHAIN = (0x3000 << KEYMASTER_REQ_SHIFT),
+ KM_ATAP_GET_CA_REQUEST = (0x4000 << KEYMASTER_REQ_SHIFT),
+ KM_ATAP_SET_CA_RESPONSE_BEGIN = (0x5000 << KEYMASTER_REQ_SHIFT),
+ KM_ATAP_SET_CA_RESPONSE_UPDATE = (0x6000 << KEYMASTER_REQ_SHIFT),
+ KM_ATAP_SET_CA_RESPONSE_FINISH = (0x7000 << KEYMASTER_REQ_SHIFT),
+ KM_ATAP_READ_UUID = (0x8000 << KEYMASTER_REQ_SHIFT),
+ KM_SET_PRODUCT_ID = (0x9000 << KEYMASTER_REQ_SHIFT),
+ KM_CLEAR_ATTESTATION_CERT_CHAIN = (0xa000 << KEYMASTER_REQ_SHIFT),
+ KM_SET_WRAPPED_ATTESTATION_KEY = (0xb000 << KEYMASTER_REQ_SHIFT),
};
#ifdef __ANDROID__
diff --git a/trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml b/trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml
new file mode 100644
index 000000000..fce2ac219
--- /dev/null
+++ b/trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0"?>
+<AndroidAttestation>
+ <NumberOfKeyboxes>10</NumberOfKeyboxes>
+ <Keybox DeviceID="dev1">
+ <Key algorithm="rsa">
+ <PrivateKey format="pem">
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1qEIEir6LR752/q7yXPKb
+KvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX4YVBeuVKvClqOm21wAQI
+O2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmdXX0jYvKcXgLocQIDAQAB
+AoGBAL6GCwuZqAKm+xpZQ4p7txUGWwmjbcbpysxr88AsNNfXnpTGYGQo2Ix7f2V3
+wc3qZAdKvo5yht8fCBHclygmCGjeldMu/Ja20IT/JxpfYN78xwPno45uKbqaPF/C
+woB2tqiWrx0014gozpvdsfNPnJQEQweBKY4gExZyW728mTpBAkEA4cbZJ2RsCRbs
+NoJtWUmDdAwh8bB0xKGlmGfGaXlchdPcRkxbkp6Uv7NODcxQFLEPEzQat/3V9gQU
+0qMmytQcxQJBANpIWZd4XNVjD7D9jFJU+Y5TjhiYOq6ea35qWntdNDdVuSGOvUAy
+DSg4fXifdvohi8wti2il9kGPu+ylF5qzr70CQFD+/DJklVlhbtZTThVFCTKdk6PY
+ENvlvbmCKSz3i9i624Agro1X9LcdBThv/p6dsnHKNHejSZnbdvjl7OnA1J0CQBW3
+TPJ8zv+Ls2vwTZ2DRrCaL3DS9EObDyasfgP36dH3fUuRX9KbKCPwOstdUgDghX/y
+qAPpPu6W1iNc6VRCvCECQQCQp0XaiXCyzWSWYDJCKMX4KFb/1mW6moXI1g8bi+5x
+fs0scurgHa2GunZU1M9FrbXx8rMdn4Eiz6XxpVcPmy0l
+-----END RSA PRIVATE KEY-----
+ </PrivateKey>
+ <CertificateChain>
+ <NumberOfCertificates>2</NumberOfCertificates>
+ <Certificate format="pem">
+-----BEGIN CERTIFICATE-----
+MIICtjCCAh+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
+BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDAeFw0xNjAxMDQx
+MjQwNTNaFw0zNTEyMzAxMjQwNTNaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD
+YWxpZm9ybmlhMRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJv
+aWQxKTAnBgNVBAMMIEFuZHJvaWQgU29mdHdhcmUgQXR0ZXN0YXRpb24gS2V5MIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1
+qEIEir6LR752/q7yXPKbKvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX
+4YVBeuVKvClqOm21wAQIO2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmd
+XX0jYvKcXgLocQIDAQABo2YwZDAdBgNVHQ4EFgQU1AwQG/jNY7n3OVK1DhNcpteZ
+k4YwHwYDVR0jBBgwFoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wEgYDVR0TAQH/BAgw
+BgEB/wIBADAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAni1IX4xn
+M9waha2Z11Aj6hTsQ7DhnerCI0YecrUZ3GAi5KVoMWwLVcTmnKItnzpPk2sxixZ4
+Fg2Iy9mLzICdhPDCJ+NrOPH90ecXcjFZNX2W88V/q52PlmEmT7K+gbsNSQQiis6f
+9/VCLiVE+iEHElqDtVWtGIL4QBSbnCBjBH8=
+-----END CERTIFICATE-----
+ </Certificate>
+ <Certificate format="pem">
+-----BEGIN CERTIFICATE-----
+MIICpzCCAhCgAwIBAgIJAP+U2d2fB8gMMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+aWV3MRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQwHhcN
+MTYwMTA0MTIzMTA4WhcNMzUxMjMwMTIzMTA4WjBjMQswCQYDVQQGEwJVUzETMBEG
+A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMGA1UE
+CgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCia63rbi5EYe/VDoLmt5TRdSMfd5tjkWP/96r/C3JHTsAs
+Q+wzfNes7UA+jCigZtX3hwszl94OuE4TQKuvpSe/lWmgMdsGUmX4RFlXYfC78hdL
+t0GAZMAoDo9Sd47b0ke2RekZyOmLw9vCkT/X11DEHTVm+Vfkl5YLCazOkjWFmwID
+AQABo2MwYTAdBgNVHQ4EFgQUKfrxrMxN0kyWQCd1trDpMuUH/i4wHwYDVR0jBBgw
+FoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B
+Af8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAT3LzNlmNDsG5dFsxWfbwjSVJMJ6j
+HBwp0kUtILlNX2S06IDHeHqcOd6os/W/L3BfRxBcxebrTQaZYdKumgf/93y4q+uc
+DyQHXrF/unlx/U1bnt8Uqf7f7XzAiF343ZtkMlbVNZriE/mPzsF83O+kqrJVw4Op
+Lvtc9mL1J1IXvmM=
+-----END CERTIFICATE-----
+ </Certificate>
+ </CertificateChain>
+ </Key>
+ </Keybox>
+ <Keybox DeviceID="dev1">
+ <Key algorithm="ecdsa">
+ <PrivateKey format="pem">
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEICHghkMqFRmEWc82OlD8FMnarfk19SfC39ceTW28QuVEoAoGCCqGSM49
+AwEHoUQDQgAE6555+EJjWazLKpFMiYbMcK2QZpOCqXMmE/6sy/ghJ0whdJdKKv6l
+uU1/ZtTgZRBmNbxTt6CjpnFYPts+Ea4QFA==
+-----END EC PRIVATE KEY-----
+ </PrivateKey>
+ <CertificateChain>
+ <NumberOfCertificates>2</NumberOfCertificates>
+ <Certificate format="pem">
+-----BEGIN CERTIFICATE-----
+MIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw
+EQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD
+VQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu
+ZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx
+MTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB
+bmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz
+dGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue
+efhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8
+U7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R
+qTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG
+AQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8
+wDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9
+Xvsiu+f+uXc/WT/7
+-----END CERTIFICATE-----
+ </Certificate>
+ <Certificate format="pem">
+-----BEGIN CERTIFICATE-----
+MIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG
+EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll
+dzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD
+VQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw
+HhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
+BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq
+QW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH
+KoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59
+dx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O
+BBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W
+EOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG
+SM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN
+C/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw==
+-----END CERTIFICATE-----
+ </Certificate>
+ </CertificateChain>
+ </Key>
+ </Keybox>
+</AndroidAttestation>
diff --git a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
new file mode 100644
index 000000000..a89a4a87c
--- /dev/null
+++ b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <libxml/xmlreader.h>
+#include <openssl/pem.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <string>
+
+using std::string;
+
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+static const char* _sopts = "h";
+static const struct option _lopts[] = {
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0},
+};
+
+static const char* usage =
+ "Usage: %s [options] xml-file\n"
+ "\n"
+ "options:\n"
+ " -h, --help prints this message and exit\n"
+ "\n";
+
+static void print_usage_and_exit(const char* prog, int code) {
+ fprintf(stderr, usage, prog);
+ 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 'h':
+ print_usage_and_exit(argv[0], EXIT_SUCCESS);
+ break;
+
+ default:
+ print_usage_and_exit(argv[0], EXIT_FAILURE);
+ }
+ }
+}
+
+struct SetAttestationKeyRequest : public keymaster::KeymasterMessage {
+ explicit SetAttestationKeyRequest(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+ : KeymasterMessage(ver) {}
+
+ size_t SerializedSize() const override { return sizeof(uint32_t) + key_data.SerializedSize(); }
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {
+ buf = keymaster::append_uint32_to_buf(buf, end, algorithm);
+ return key_data.Serialize(buf, end);
+ }
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {
+ return keymaster::copy_uint32_from_buf(buf_ptr, end, &algorithm) &&
+ key_data.Deserialize(buf_ptr, end);
+ }
+
+ keymaster_algorithm_t algorithm;
+ keymaster::Buffer key_data;
+};
+
+struct KeymasterNoResponse : public keymaster::KeymasterResponse {
+ explicit KeymasterNoResponse(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+ : keymaster::KeymasterResponse(ver) {}
+
+ size_t NonErrorSerializedSize() const override { return 0; }
+ uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t*) const override { return buf; }
+ bool NonErrorDeserialize(const uint8_t**, const uint8_t*) override { return true; }
+};
+
+struct SetAttestationKeyResponse : public KeymasterNoResponse {};
+
+struct ClearAttestationCertChainRequest : public keymaster::KeymasterMessage {
+ explicit ClearAttestationCertChainRequest(int32_t ver = keymaster::MAX_MESSAGE_VERSION)
+ : KeymasterMessage(ver) {}
+
+ size_t SerializedSize() const override { return sizeof(uint32_t); }
+ uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {
+ return keymaster::append_uint32_to_buf(buf, end, algorithm);
+ }
+ bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {
+ return keymaster::copy_uint32_from_buf(buf_ptr, end, &algorithm);
+ }
+
+ keymaster_algorithm_t algorithm;
+};
+
+struct ClearAttestationCertChainResponse : public KeymasterNoResponse {};
+
+static int set_attestation_key_or_cert_bin(uint32_t cmd, keymaster_algorithm_t algorithm,
+ const void* key_data, size_t key_data_size) {
+ int ret;
+
+ SetAttestationKeyRequest req;
+ req.algorithm = algorithm;
+ req.key_data.Reinitialize(key_data, key_data_size);
+ SetAttestationKeyResponse rsp;
+
+ ret = trusty_keymaster_send(cmd, req, &rsp);
+ if (ret) {
+ fprintf(stderr, "trusty_keymaster_send cmd 0x%x failed %d\n", cmd, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int set_attestation_key_or_cert_pem(uint32_t cmd, keymaster_algorithm_t algorithm,
+ const xmlChar* pemkey) {
+ int ret;
+ int sslret;
+
+ /* Convert from pem to binary */
+ BIO* bio = BIO_new_mem_buf(pemkey, xmlStrlen(pemkey));
+ if (!bio) {
+ fprintf(stderr, "BIO_new_mem_buf failed\n");
+ ERR_print_errors_fp(stderr);
+ return -1;
+ }
+
+ char* key_name;
+ char* key_header;
+ uint8_t* key;
+ long keylen;
+ sslret = PEM_read_bio(bio, &key_name, &key_header, &key, &keylen);
+ BIO_free(bio);
+
+ if (!sslret) {
+ fprintf(stderr, "PEM_read_bio failed\n");
+ ERR_print_errors_fp(stderr);
+ return -1;
+ }
+
+ /* Send key in binary format to trusty */
+ ret = set_attestation_key_or_cert_bin(cmd, algorithm, key, keylen);
+
+ OPENSSL_free(key_name);
+ OPENSSL_free(key_header);
+ OPENSSL_free(key);
+
+ return ret;
+}
+
+static int set_attestation_key_or_cert_iecs(uint32_t cmd, keymaster_algorithm_t algorithm,
+ const xmlChar* key_base64) {
+ int ret;
+ int sslret;
+
+ /* Remove all whitespace. EVP_DecodeBase64 does not support whitespace. */
+ string key_base64_str((const char*)key_base64);
+ key_base64_str.erase(remove_if(key_base64_str.begin(), key_base64_str.end(), isspace),
+ key_base64_str.end());
+
+ /* Convert from base64 to binary */
+ uint8_t* key;
+ size_t keylen;
+ size_t key_base64_len = key_base64_str.length();
+
+ sslret = EVP_DecodedLength(&keylen, key_base64_len);
+ if (!sslret) {
+ fprintf(stderr, "invalid input length, %zu\n", key_base64_len);
+ return -1;
+ }
+ key = (uint8_t*)malloc(keylen);
+ if (!key) {
+ fprintf(stderr, "failed to allocate key, size %zu\n", key_base64_len);
+ return -1;
+ }
+ sslret = EVP_DecodeBase64(key, &keylen, keylen, (const uint8_t*)key_base64_str.data(),
+ key_base64_len);
+ if (!sslret) {
+ fprintf(stderr, "EVP_DecodeBase64 failed\n");
+ ERR_print_errors_fp(stderr);
+ free(key);
+ return -1;
+ }
+
+ /* Send key in binary format to trusty */
+ ret = set_attestation_key_or_cert_bin(cmd, algorithm, key, keylen);
+
+ free(key);
+
+ return ret;
+}
+
+static int str_to_algorithm(keymaster_algorithm_t* algorithm, const xmlChar* algorithm_str) {
+ if (xmlStrEqual(algorithm_str, BAD_CAST "rsa")) {
+ *algorithm = KM_ALGORITHM_RSA;
+ } else if (xmlStrEqual(algorithm_str, BAD_CAST "ecdsa")) {
+ *algorithm = KM_ALGORITHM_EC;
+ } else {
+ printf("unsupported algorithm: %s\n", algorithm_str);
+ return -1;
+ }
+ return 0;
+}
+
+static int set_attestation_key_or_cert(uint32_t cmd, const xmlChar* algorithm_str,
+ const xmlChar* format, const xmlChar* str) {
+ int ret;
+ keymaster_algorithm_t algorithm;
+
+ ret = str_to_algorithm(&algorithm, algorithm_str);
+ if (ret) {
+ return ret;
+ }
+
+ if (xmlStrEqual(format, BAD_CAST "pem")) {
+ ret = set_attestation_key_or_cert_pem(cmd, algorithm, str);
+ } else if (xmlStrEqual(format, BAD_CAST "iecs")) {
+ ret = set_attestation_key_or_cert_iecs(cmd, algorithm, str);
+ } else {
+ printf("unsupported key/cert format: %s\n", format);
+ return -1;
+ }
+ return ret;
+}
+
+static int clear_cert_chain(const xmlChar* algorithm_str) {
+ int ret;
+ ClearAttestationCertChainRequest req;
+ ClearAttestationCertChainResponse rsp;
+
+ ret = str_to_algorithm(&req.algorithm, algorithm_str);
+ if (ret) {
+ return ret;
+ }
+
+ ret = trusty_keymaster_send(KM_CLEAR_ATTESTATION_CERT_CHAIN, req, &rsp);
+ if (ret) {
+ fprintf(stderr, "%s: trusty_keymaster_send failed %d\n", __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int process_xml(xmlTextReaderPtr xml) {
+ int ret;
+ const xmlChar* algorithm = NULL;
+ const xmlChar* element = NULL;
+ const xmlChar* element_format = NULL;
+
+ while ((ret = xmlTextReaderRead(xml)) == 1) {
+ int nodetype = xmlTextReaderNodeType(xml);
+ const xmlChar *name, *value;
+ name = xmlTextReaderConstName(xml);
+ switch (nodetype) {
+ case XML_READER_TYPE_ELEMENT:
+ element = name;
+ element_format = xmlTextReaderGetAttribute(xml, BAD_CAST "format");
+ if (xmlStrEqual(name, BAD_CAST "Key")) {
+ algorithm = xmlTextReaderGetAttribute(xml, BAD_CAST "algorithm");
+ } else if (xmlStrEqual(name, BAD_CAST "CertificateChain")) {
+ ret = clear_cert_chain(algorithm);
+ if (ret) {
+ fprintf(stderr, "%s, algorithm %s: Clear cert chain cmd failed, %d\n",
+ element, algorithm, ret);
+ return ret;
+ }
+ printf("%s, algorithm %s: Clear cert chain cmd done\n", element, algorithm);
+ }
+ break;
+ case XML_READER_TYPE_TEXT:
+ value = xmlTextReaderConstValue(xml);
+ uint32_t cmd;
+ if (xmlStrEqual(element, BAD_CAST "PrivateKey")) {
+ cmd = KM_SET_ATTESTATION_KEY;
+ } else if (xmlStrEqual(element, BAD_CAST "WrappedPrivateKey")) {
+ cmd = KM_SET_WRAPPED_ATTESTATION_KEY;
+ } else if (xmlStrEqual(element, BAD_CAST "Certificate")) {
+ cmd = KM_APPEND_ATTESTATION_CERT_CHAIN;
+ } else {
+ break;
+ }
+
+ ret = set_attestation_key_or_cert(cmd, algorithm, element_format, value);
+ if (ret) {
+ fprintf(stderr, "%s, algorithm %s, format %s: Cmd 0x%x failed, %d\n", element,
+ algorithm, element_format, cmd, ret);
+ return ret;
+ }
+ printf("%s, algorithm %s, format %s: Cmd 0x%x done\n", element, algorithm,
+ element_format, cmd);
+ break;
+ case XML_READER_TYPE_END_ELEMENT:
+ element = NULL;
+ break;
+ }
+ }
+ return ret;
+}
+
+static int parse_xml_file(const char* filename) {
+ int ret;
+ xmlTextReaderPtr xml = xmlReaderForFile(filename, NULL, 0);
+ if (!xml) {
+ fprintf(stderr, "failed to open %s\n", filename);
+ return -1;
+ }
+
+ ret = process_xml(xml);
+
+ xmlFreeTextReader(xml);
+ if (ret != 0) {
+ fprintf(stderr, "Failed to parse or process %s\n", filename);
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ int ret = 0;
+
+ parse_options(argc, argv);
+ if (optind + 1 != argc) {
+ print_usage_and_exit(argv[0], EXIT_FAILURE);
+ }
+
+ ret = trusty_keymaster_connect();
+ if (ret) {
+ fprintf(stderr, "trusty_keymaster_connect failed %d\n", ret);
+ } else {
+ ret = parse_xml_file(argv[optind]);
+ trusty_keymaster_disconnect();
+ }
+
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/trusty/libtrusty/include/trusty/ipc.h b/trusty/libtrusty/include/trusty/ipc.h
new file mode 100644
index 000000000..1fa6fe4aa
--- /dev/null
+++ b/trusty/libtrusty/include/trusty/ipc.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _UAPI_LINUX_TRUSTY_IPC_H_
+#define _UAPI_LINUX_TRUSTY_IPC_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/uio.h>
+
+/**
+ * enum transfer_kind - How to send an fd to Trusty
+ * @TRUSTY_SHARE: Memory will be accessible by Linux and Trusty. On ARM it will
+ * be mapped as nonsecure. Suitable for shared memory. The paired
+ * fd must be a "memfd".
+ * @TRUSTY_LEND: Memory will be accessible only to Trusty. On ARM it will be
+ * transitioned to "Secure" memory if Trusty is in TrustZone.
+ * This transfer kind is suitable for donating video buffers or
+ * other similar resources. The paired fd may need to come from a
+ * platform-specific allocator for memory that may be
+ * transitioned to "Secure".
+ *
+ * Describes how the user would like the resource in question to be sent to
+ * Trusty. Options may be valid only for certain kinds of fds.
+ */
+enum transfer_kind {
+ TRUSTY_SHARE = 0,
+ TRUSTY_LEND = 1,
+};
+
+/**
+ * struct trusty_shm - Describes a transfer of memory to Trusty
+ * @fd: The fd to transfer
+ * @transfer: How to transfer it - see &enum transfer_kind
+ */
+struct trusty_shm {
+ __s32 fd;
+ __u32 transfer;
+};
+
+/**
+ * struct tipc_send_msg_req - Request struct for @TIPC_IOC_SEND_MSG
+ * @iov: Pointer to an array of &struct iovec describing data to be sent
+ * @shm: Pointer to an array of &struct trusty_shm describing any file
+ * descriptors to be transferred.
+ * @iov_cnt: Number of elements in the @iov array
+ * @shm_cnt: Number of elements in the @shm array
+ */
+struct tipc_send_msg_req {
+ __u64 iov;
+ __u64 shm;
+ __u64 iov_cnt;
+ __u64 shm_cnt;
+};
+
+#define TIPC_IOC_MAGIC 'r'
+#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char*)
+#define TIPC_IOC_SEND_MSG _IOW(TIPC_IOC_MAGIC, 0x81, struct tipc_send_msg_req)
+
+#if defined(CONFIG_COMPAT)
+#define TIPC_IOC_CONNECT_COMPAT _IOW(TIPC_IOC_MAGIC, 0x80, compat_uptr_t)
+#endif
+
+#endif
diff --git a/trusty/libtrusty/include/trusty/tipc.h b/trusty/libtrusty/include/trusty/tipc.h
index a3f2a3f61..b44afd337 100644
--- a/trusty/libtrusty/include/trusty/tipc.h
+++ b/trusty/libtrusty/include/trusty/tipc.h
@@ -21,7 +21,11 @@
extern "C" {
#endif
+#include <sys/uio.h>
+#include <trusty/ipc.h>
+
int tipc_connect(const char *dev_name, const char *srv_name);
+ssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shm, int shmcnt);
int tipc_close(int fd);
#ifdef __cplusplus
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index d20d4eebf..ca581dc2d 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -21,6 +21,8 @@
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
+#define __USE_GNU
+#include <sys/mman.h>
#include <sys/uio.h>
#include <trusty/tipc.h>
@@ -39,6 +41,7 @@ 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* receiver_name = "com.android.trusty.memref.receiver";
static const char *_sopts = "hsvD:t:r:m:b:";
static const struct option _lopts[] = {
@@ -66,25 +69,25 @@ static const char *usage =
"\n"
;
-static const char *usage_long =
-"\n"
-"The following tests are available:\n"
-" connect - connect to datasink service\n"
-" connect_foo - connect to non existing service\n"
-" burst_write - send messages to datasink service\n"
-" echo - send/receive messages to echo service\n"
-" select - test select call\n"
-" blocked_read - test blocked read\n"
-" closer1 - connection closed by remote (test1)\n"
-" closer2 - connection closed by remote (test2)\n"
-" closer3 - connection closed by remote (test3)\n"
-" ta2ta-ipc - execute TA to TA unittest\n"
-" dev-uuid - print device uuid\n"
-" ta-access - test ta-access flags\n"
-" writev - writev test\n"
-" readv - readv test\n"
-"\n"
-;
+static const char* usage_long =
+ "\n"
+ "The following tests are available:\n"
+ " connect - connect to datasink service\n"
+ " connect_foo - connect to non existing service\n"
+ " burst_write - send messages to datasink service\n"
+ " echo - send/receive messages to echo service\n"
+ " select - test select call\n"
+ " blocked_read - test blocked read\n"
+ " closer1 - connection closed by remote (test1)\n"
+ " closer2 - connection closed by remote (test2)\n"
+ " closer3 - connection closed by remote (test3)\n"
+ " ta2ta-ipc - execute TA to TA unittest\n"
+ " dev-uuid - print device uuid\n"
+ " ta-access - test ta-access flags\n"
+ " writev - writev test\n"
+ " readv - readv test\n"
+ " send-fd - transmit memfd to trusty, use as shm\n"
+ "\n";
static uint opt_repeat = 1;
static uint opt_msgsize = 32;
@@ -885,6 +888,66 @@ static int readv_test(uint repeat, uint msgsz, bool var)
return 0;
}
+static int send_fd_test(void) {
+ int ret;
+ int memfd = -1;
+ int fd = -1;
+ volatile char* buf = MAP_FAILED;
+
+ fd = tipc_connect(dev_name, receiver_name);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to connect to test support TA - is it missing?\n");
+ ret = -1;
+ goto cleanup;
+ }
+
+ memfd = memfd_create("tipc-send-fd", 0);
+ if (memfd < 0) {
+ fprintf(stderr, "Failed to create memfd: %s\n", strerror(errno));
+ ret = -1;
+ goto cleanup;
+ }
+
+ if (ftruncate(memfd, PAGE_SIZE) < 0) {
+ fprintf(stderr, "Failed to resize memfd: %s\n", strerror(errno));
+ ret = -1;
+ goto cleanup;
+ }
+
+ buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0);
+ if (buf == MAP_FAILED) {
+ fprintf(stderr, "Failed to map memfd: %s\n", strerror(errno));
+ ret = -1;
+ goto cleanup;
+ }
+
+ strcpy((char*)buf, "From NS");
+
+ struct trusty_shm shm = {
+ .fd = memfd,
+ .transfer = TRUSTY_SHARE,
+ };
+
+ ssize_t rc = tipc_send(fd, NULL, 0, &shm, 1);
+ if (rc < 0) {
+ fprintf(stderr, "tipc_send failed\n");
+ ret = rc;
+ goto cleanup;
+ }
+ char c;
+ read(fd, &c, 1);
+ tipc_close(fd);
+
+ ret = strcmp("Hello from Trusty!", (const char*)buf) ? (-1) : 0;
+
+cleanup:
+ if (buf != MAP_FAILED) {
+ munmap((char*)buf, PAGE_SIZE);
+ }
+ close(memfd);
+ tipc_close(fd);
+ return ret;
+}
int main(int argc, char **argv)
{
@@ -933,10 +996,12 @@ int main(int argc, char **argv)
rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
} else if (strcmp(test_name, "readv") == 0) {
rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
- } else {
- fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
- print_usage_and_exit(argv[0], EXIT_FAILURE, true);
- }
-
- return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+ } else if (strcmp(test_name, "send-fd") == 0) {
+ rc = send_fd_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;
}
diff --git a/trusty/libtrusty/trusty.c b/trusty/libtrusty/trusty.c
index a6238af7d..ad4d8cd54 100644
--- a/trusty/libtrusty/trusty.c
+++ b/trusty/libtrusty/trusty.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
#include <log/log.h>
-#include "tipc_ioctl.h"
+#include <trusty/ipc.h>
int tipc_connect(const char *dev_name, const char *srv_name)
{
@@ -55,6 +55,22 @@ int tipc_connect(const char *dev_name, const char *srv_name)
return fd;
}
+ssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shms,
+ int shmcnt) {
+ struct tipc_send_msg_req req;
+ req.iov = (__u64)iov;
+ req.iov_cnt = (__u64)iovcnt;
+ req.shm = (__u64)shms;
+ req.shm_cnt = (__u64)shmcnt;
+
+ int rc = ioctl(fd, TIPC_IOC_SEND_MSG, &req);
+ if (rc < 0) {
+ ALOGE("%s: failed to send message (err=%d)\n", __func__, rc);
+ }
+
+ return rc;
+}
+
void tipc_close(int fd)
{
close(fd);
diff --git a/trusty/trusty-test.mk b/trusty/trusty-test.mk
index fd353d12b..dc4c9622b 100644
--- a/trusty/trusty-test.mk
+++ b/trusty/trusty-test.mk
@@ -14,3 +14,5 @@
PRODUCT_PACKAGES += \
spiproxyd \
+ trusty_keymaster_set_attestation_key \
+ keymaster_soft_attestation_keys.xml \ \ No newline at end of file