diff options
127 files changed, 4421 insertions, 1524 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 8bcb1e5da5..7ef26bf8d6 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,10 +1,12 @@ [Builtin Hooks] +bpfmt = true clang_format = true [Builtin Hooks Options] # Only turn on clang-format check for the following subfolders. clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp cmds/idlcli/ + cmds/installd/ cmds/servicemanager/ include/input/ include/powermanager/ diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 783a475829..645944340e 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -239,7 +239,7 @@ static const TracingCategory k_categories[] = { } }, { "memory", "Memory", 0, { { OPT, "events/mm_event/mm_event_record/enable" }, - { OPT, "events/kmem/rss_stat/enable" }, + { OPT, "events/synthetic/rss_stat_throttled/enable" }, { OPT, "events/kmem/ion_heap_grow/enable" }, { OPT, "events/kmem/ion_heap_shrink/enable" }, { OPT, "events/ion/ion_stat/enable" }, diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 01c4723f29..34ccb21c1e 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -282,6 +282,21 @@ on late-init chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu23/trace chmod 0666 /sys/kernel/tracing/per_cpu/cpu23/trace +# Setup synthetic events + chmod 0666 /sys/kernel/tracing/synthetic_events + chmod 0666 /sys/kernel/debug/tracing/synthetic_events + + # rss_stat_throttled + write /sys/kernel/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" + write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" + +# Set up histogram triggers + # rss_stat_throttled (bucket size == 512KB) + chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger + chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger + write /sys/kernel/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)" + write /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)" + # Only create the tracing instance if persist.mm_events.enabled # Attempting to remove the tracing instance after it has been created # will likely fail with EBUSY as it would be in use by traced_probes. diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index 74dbf4b764..a2491e503f 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -86,9 +86,11 @@ cc_defaults { shared_libs: [ "android.hardware.dumpstate@1.0", "android.hardware.dumpstate@1.1", + "android.hardware.dumpstate-V1-ndk", "libziparchive", "libbase", "libbinder", + "libbinder_ndk", "libcrypto", "libcutils", "libdebuggerd_client", diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index ba25a5a603..77915d5376 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -192,7 +192,7 @@ status_t DumpstateService::dump(int fd, const Vector<String16>&) { dprintf(fd, "progress:\n"); ds_->progress_->Dump(fd, " "); dprintf(fd, "args: %s\n", ds_->options_->args.c_str()); - dprintf(fd, "bugreport_mode: %s\n", ds_->options_->bugreport_mode.c_str()); + dprintf(fd, "bugreport_mode: %s\n", ds_->options_->bugreport_mode_string.c_str()); dprintf(fd, "version: %s\n", ds_->version_.c_str()); dprintf(fd, "bugreport_dir: %s\n", destination.c_str()); dprintf(fd, "screenshot_path: %s\n", ds_->screenshot_path_.c_str()); diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index eab72f48b0..32e680dfea 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -57,12 +57,15 @@ #include <utility> #include <vector> +#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> #include <android/content/pm/IPackageManagerNative.h> #include <android/hardware/dumpstate/1.0/IDumpstateDevice.h> #include <android/hardware/dumpstate/1.1/IDumpstateDevice.h> @@ -89,11 +92,10 @@ #include "DumpstateService.h" #include "dumpstate.h" -using IDumpstateDevice_1_0 = ::android::hardware::dumpstate::V1_0::IDumpstateDevice; -using IDumpstateDevice_1_1 = ::android::hardware::dumpstate::V1_1::IDumpstateDevice; -using ::android::hardware::dumpstate::V1_1::DumpstateMode; -using ::android::hardware::dumpstate::V1_1::DumpstateStatus; -using ::android::hardware::dumpstate::V1_1::toString; +namespace dumpstate_hal_hidl_1_0 = android::hardware::dumpstate::V1_0; +namespace dumpstate_hal_hidl = android::hardware::dumpstate::V1_1; +namespace dumpstate_hal_aidl = aidl::android::hardware::dumpstate; + using ::std::literals::chrono_literals::operator""ms; using ::std::literals::chrono_literals::operator""s; using ::std::placeholders::_1; @@ -807,7 +809,7 @@ void Dumpstate::PrintHeader() const { printf("Bugreport format version: %s\n", version_.c_str()); printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n", id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(), - options_->args.c_str(), options_->bugreport_mode.c_str()); + options_->args.c_str(), options_->bugreport_mode_string.c_str()); printf("\n"); } @@ -2199,6 +2201,194 @@ Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { return RunStatus::OK; } +static dumpstate_hal_hidl::DumpstateMode GetDumpstateHalModeHidl( + const Dumpstate::BugreportMode bugreport_mode) { + switch (bugreport_mode) { + case Dumpstate::BugreportMode::BUGREPORT_FULL: + return dumpstate_hal_hidl::DumpstateMode::FULL; + case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: + return dumpstate_hal_hidl::DumpstateMode::INTERACTIVE; + case Dumpstate::BugreportMode::BUGREPORT_REMOTE: + return dumpstate_hal_hidl::DumpstateMode::REMOTE; + case Dumpstate::BugreportMode::BUGREPORT_WEAR: + return dumpstate_hal_hidl::DumpstateMode::WEAR; + case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: + return dumpstate_hal_hidl::DumpstateMode::CONNECTIVITY; + case Dumpstate::BugreportMode::BUGREPORT_WIFI: + return dumpstate_hal_hidl::DumpstateMode::WIFI; + case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: + return dumpstate_hal_hidl::DumpstateMode::DEFAULT; + } + return dumpstate_hal_hidl::DumpstateMode::DEFAULT; +} + +static dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode GetDumpstateHalModeAidl( + const Dumpstate::BugreportMode bugreport_mode) { + switch (bugreport_mode) { + case Dumpstate::BugreportMode::BUGREPORT_FULL: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::FULL; + case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::INTERACTIVE; + case Dumpstate::BugreportMode::BUGREPORT_REMOTE: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::REMOTE; + case Dumpstate::BugreportMode::BUGREPORT_WEAR: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WEAR; + case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::CONNECTIVITY; + case Dumpstate::BugreportMode::BUGREPORT_WIFI: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WIFI; + case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT; + } + return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT; +} + +static void DoDumpstateBoardHidl( + const sp<dumpstate_hal_hidl_1_0::IDumpstateDevice> dumpstate_hal_1_0, + const std::vector<::ndk::ScopedFileDescriptor>& dumpstate_fds, + const Dumpstate::BugreportMode bugreport_mode, + const size_t timeout_sec) { + + using ScopedNativeHandle = + std::unique_ptr<native_handle_t, std::function<void(native_handle_t*)>>; + ScopedNativeHandle handle(native_handle_create(static_cast<int>(dumpstate_fds.size()), 0), + [](native_handle_t* handle) { + // we don't close file handle's here + // via native_handle_close(handle) + // instead we let dumpstate_fds close the file handles when + // dumpstate_fds gets destroyed + native_handle_delete(handle); + }); + if (handle == nullptr) { + MYLOGE("Could not create native_handle for dumpstate HAL\n"); + return; + } + + for (size_t i = 0; i < dumpstate_fds.size(); i++) { + handle.get()->data[i] = dumpstate_fds[i].get(); + } + + // Prefer version 1.1 if available. New devices launching with R are no longer allowed to + // implement just 1.0. + const char* descriptor_to_kill; + using DumpstateBoardTask = std::packaged_task<bool()>; + DumpstateBoardTask dumpstate_board_task; + sp<dumpstate_hal_hidl::IDumpstateDevice> dumpstate_hal( + dumpstate_hal_hidl::IDumpstateDevice::castFrom(dumpstate_hal_1_0)); + if (dumpstate_hal != nullptr) { + MYLOGI("Using IDumpstateDevice v1.1 HIDL HAL"); + + dumpstate_hal_hidl::DumpstateMode dumpstate_hal_mode = + GetDumpstateHalModeHidl(bugreport_mode); + + descriptor_to_kill = dumpstate_hal_hidl::IDumpstateDevice::descriptor; + dumpstate_board_task = + DumpstateBoardTask([timeout_sec, dumpstate_hal_mode, dumpstate_hal, &handle]() -> bool { + ::android::hardware::Return<dumpstate_hal_hidl::DumpstateStatus> status = + dumpstate_hal->dumpstateBoard_1_1(handle.get(), dumpstate_hal_mode, + SEC_TO_MSEC(timeout_sec)); + if (!status.isOk()) { + MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); + return false; + } else if (status != dumpstate_hal_hidl::DumpstateStatus::OK) { + MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n", + dumpstate_hal_hidl::toString(status).c_str()); + return false; + } + return true; + }); + } else { + MYLOGI("Using IDumpstateDevice v1.0 HIDL HAL"); + + descriptor_to_kill = dumpstate_hal_hidl_1_0::IDumpstateDevice::descriptor; + dumpstate_board_task = DumpstateBoardTask([dumpstate_hal_1_0, &handle]() -> bool { + ::android::hardware::Return<void> status = + dumpstate_hal_1_0->dumpstateBoard(handle.get()); + if (!status.isOk()) { + MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); + return false; + } + return true; + }); + } + auto result = dumpstate_board_task.get_future(); + std::thread(std::move(dumpstate_board_task)).detach(); + + if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) { + MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate HAL\n", timeout_sec); + if (!android::base::SetProperty( + "ctl.interface_restart", + android::base::StringPrintf("%s/default", descriptor_to_kill))) { + MYLOGE("Couldn't restart dumpstate HAL\n"); + } + } + // Wait some time for init to kill dumpstate vendor HAL + constexpr size_t killing_timeout_sec = 10; + if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) { + MYLOGE( + "killing dumpstateBoard timed out after %zus, continue and " + "there might be racing in content\n", + killing_timeout_sec); + } +} + +static void DoDumpstateBoardAidl( + const std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> dumpstate_hal, + const std::vector<::ndk::ScopedFileDescriptor>& dumpstate_fds, + const Dumpstate::BugreportMode bugreport_mode, const size_t timeout_sec) { + MYLOGI("Using IDumpstateDevice AIDL HAL"); + + const char* descriptor_to_kill; + using DumpstateBoardTask = std::packaged_task<bool()>; + DumpstateBoardTask dumpstate_board_task; + dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode dumpstate_hal_mode = + GetDumpstateHalModeAidl(bugreport_mode); + + descriptor_to_kill = dumpstate_hal_aidl::IDumpstateDevice::descriptor; + dumpstate_board_task = DumpstateBoardTask([dumpstate_hal, &dumpstate_fds, dumpstate_hal_mode, + timeout_sec]() -> bool { + auto status = dumpstate_hal->dumpstateBoard(dumpstate_fds, dumpstate_hal_mode, timeout_sec); + + if (!status.isOk()) { + MYLOGE("dumpstateBoard failed: %s\n", status.getDescription().c_str()); + return false; + } + return true; + }); + auto result = dumpstate_board_task.get_future(); + std::thread(std::move(dumpstate_board_task)).detach(); + + if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) { + MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate HAL\n", timeout_sec); + if (!android::base::SetProperty( + "ctl.interface_restart", + android::base::StringPrintf("%s/default", descriptor_to_kill))) { + MYLOGE("Couldn't restart dumpstate HAL\n"); + } + } + // Wait some time for init to kill dumpstate vendor HAL + constexpr size_t killing_timeout_sec = 10; + if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) { + MYLOGE( + "killing dumpstateBoard timed out after %zus, continue and " + "there might be racing in content\n", + killing_timeout_sec); + } +} + +static std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> GetDumpstateBoardAidlService() { + const std::string aidl_instance_name = + std::string(dumpstate_hal_aidl::IDumpstateDevice::descriptor) + "/default"; + + if (!AServiceManager_isDeclared(aidl_instance_name.c_str())) { + return nullptr; + } + + ndk::SpAIBinder dumpstateBinder(AServiceManager_waitForService(aidl_instance_name.c_str())); + + return dumpstate_hal_aidl::IDumpstateDevice::fromBinder(dumpstateBinder); +} + void Dumpstate::DumpstateBoard(int out_fd) { dprintf(out_fd, "========================================================\n"); dprintf(out_fd, "== Board\n"); @@ -2220,8 +2410,7 @@ void Dumpstate::DumpstateBoard(int out_fd) { if (mount_debugfs) { RunCommand("mount debugfs", {"mount", "-t", "debugfs", "debugfs", "/sys/kernel/debug"}, AS_ROOT_20); - RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"}, - AS_ROOT_20); + RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"}, AS_ROOT_20); } std::vector<std::string> paths; @@ -2233,24 +2422,32 @@ void Dumpstate::DumpstateBoard(int out_fd) { std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i]))); } - sp<IDumpstateDevice_1_0> dumpstate_device_1_0(IDumpstateDevice_1_0::getService()); - if (dumpstate_device_1_0 == nullptr) { - MYLOGE("No IDumpstateDevice implementation\n"); - return; + // get dumpstate HAL AIDL implementation + std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> dumpstate_hal_handle_aidl( + GetDumpstateBoardAidlService()); + if (dumpstate_hal_handle_aidl == nullptr) { + MYLOGI("No IDumpstateDevice AIDL implementation\n"); } - using ScopedNativeHandle = - std::unique_ptr<native_handle_t, std::function<void(native_handle_t*)>>; - ScopedNativeHandle handle(native_handle_create(static_cast<int>(paths.size()), 0), - [](native_handle_t* handle) { - native_handle_close(handle); - native_handle_delete(handle); - }); - if (handle == nullptr) { - MYLOGE("Could not create native_handle\n"); + // get dumpstate HAL HIDL implementation, only if AIDL HAL implementation not found + sp<dumpstate_hal_hidl_1_0::IDumpstateDevice> dumpstate_hal_handle_hidl_1_0 = nullptr; + if (dumpstate_hal_handle_aidl == nullptr) { + dumpstate_hal_handle_hidl_1_0 = dumpstate_hal_hidl_1_0::IDumpstateDevice::getService(); + if (dumpstate_hal_handle_hidl_1_0 == nullptr) { + MYLOGI("No IDumpstateDevice HIDL implementation\n"); + } + } + + // if neither HIDL nor AIDL implementation found, then return + if (dumpstate_hal_handle_hidl_1_0 == nullptr && dumpstate_hal_handle_aidl == nullptr) { + MYLOGE("Could not find IDumpstateDevice implementation\n"); return; } + // this is used to hold the file descriptors and when this variable goes out of scope + // the file descriptors are closed + std::vector<::ndk::ScopedFileDescriptor> dumpstate_fds; + // TODO(128270426): Check for consent in between? for (size_t i = 0; i < paths.size(); i++) { MYLOGI("Calling IDumpstateDevice implementation using path %s\n", paths[i].c_str()); @@ -2262,65 +2459,26 @@ void Dumpstate::DumpstateBoard(int out_fd) { MYLOGE("Could not open file %s: %s\n", paths[i].c_str(), strerror(errno)); return; } - handle.get()->data[i] = fd.release(); + + dumpstate_fds.emplace_back(fd.release()); + // we call fd.release() here to make sure "fd" does not get closed + // after "fd" goes out of scope after this block. + // "fd" will be closed when "dumpstate_fds" goes out of scope + // i.e. when we exit this function } // Given that bugreport is required to diagnose failures, it's better to set an arbitrary amount // of timeout for IDumpstateDevice than to block the rest of bugreport. In the timeout case, we // will kill the HAL and grab whatever it dumped in time. constexpr size_t timeout_sec = 30; - // Prefer version 1.1 if available. New devices launching with R are no longer allowed to - // implement just 1.0. - const char* descriptor_to_kill; - using DumpstateBoardTask = std::packaged_task<bool()>; - DumpstateBoardTask dumpstate_board_task; - sp<IDumpstateDevice_1_1> dumpstate_device_1_1( - IDumpstateDevice_1_1::castFrom(dumpstate_device_1_0)); - if (dumpstate_device_1_1 != nullptr) { - MYLOGI("Using IDumpstateDevice v1.1"); - descriptor_to_kill = IDumpstateDevice_1_1::descriptor; - dumpstate_board_task = DumpstateBoardTask([this, dumpstate_device_1_1, &handle]() -> bool { - ::android::hardware::Return<DumpstateStatus> status = - dumpstate_device_1_1->dumpstateBoard_1_1(handle.get(), options_->dumpstate_hal_mode, - SEC_TO_MSEC(timeout_sec)); - if (!status.isOk()) { - MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); - return false; - } else if (status != DumpstateStatus::OK) { - MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n", toString(status).c_str()); - return false; - } - return true; - }); - } else { - MYLOGI("Using IDumpstateDevice v1.0"); - descriptor_to_kill = IDumpstateDevice_1_0::descriptor; - dumpstate_board_task = DumpstateBoardTask([dumpstate_device_1_0, &handle]() -> bool { - ::android::hardware::Return<void> status = - dumpstate_device_1_0->dumpstateBoard(handle.get()); - if (!status.isOk()) { - MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); - return false; - } - return true; - }); - } - auto result = dumpstate_board_task.get_future(); - std::thread(std::move(dumpstate_board_task)).detach(); - if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) { - MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate vendor HAL\n", timeout_sec); - if (!android::base::SetProperty( - "ctl.interface_restart", - android::base::StringPrintf("%s/default", descriptor_to_kill))) { - MYLOGE("Couldn't restart dumpstate HAL\n"); - } - } - // Wait some time for init to kill dumpstate vendor HAL - constexpr size_t killing_timeout_sec = 10; - if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) { - MYLOGE("killing dumpstateBoard timed out after %zus, continue and " - "there might be racing in content\n", killing_timeout_sec); + if (dumpstate_hal_handle_aidl != nullptr) { + DoDumpstateBoardAidl(dumpstate_hal_handle_aidl, dumpstate_fds, options_->bugreport_mode, + timeout_sec); + } else if (dumpstate_hal_handle_hidl_1_0 != nullptr) { + // run HIDL HAL only if AIDL HAL not found + DoDumpstateBoardHidl(dumpstate_hal_handle_hidl_1_0, dumpstate_fds, options_->bugreport_mode, + timeout_sec); } if (mount_debugfs) { @@ -2333,9 +2491,8 @@ void Dumpstate::DumpstateBoard(int out_fd) { auto file_sizes = std::make_unique<ssize_t[]>(paths.size()); for (size_t i = 0; i < paths.size(); i++) { struct stat s; - if (fstat(handle.get()->data[i], &s) == -1) { - MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(), - strerror(errno)); + if (fstat(dumpstate_fds[i].get(), &s) == -1) { + MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(), strerror(errno)); file_sizes[i] = -1; continue; } @@ -2574,40 +2731,35 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt bool is_screenshot_requested) { // Modify com.android.shell.BugreportProgressService#isDefaultScreenshotRequired as well for // default system screenshots. - options->bugreport_mode = ModeToString(mode); + options->bugreport_mode = mode; + options->bugreport_mode_string = ModeToString(mode); switch (mode) { case Dumpstate::BugreportMode::BUGREPORT_FULL: options->do_screenshot = is_screenshot_requested; - options->dumpstate_hal_mode = DumpstateMode::FULL; break; case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE: // Currently, the dumpstate binder is only used by Shell to update progress. options->do_progress_updates = true; options->do_screenshot = is_screenshot_requested; - options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE; break; case Dumpstate::BugreportMode::BUGREPORT_REMOTE: options->do_vibrate = false; options->is_remote_mode = true; options->do_screenshot = false; - options->dumpstate_hal_mode = DumpstateMode::REMOTE; break; case Dumpstate::BugreportMode::BUGREPORT_WEAR: options->do_progress_updates = true; options->do_screenshot = is_screenshot_requested; - options->dumpstate_hal_mode = DumpstateMode::WEAR; break; // TODO(b/148168577) rename TELEPHONY everywhere to CONNECTIVITY. case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: options->telephony_only = true; options->do_progress_updates = true; options->do_screenshot = false; - options->dumpstate_hal_mode = DumpstateMode::CONNECTIVITY; break; case Dumpstate::BugreportMode::BUGREPORT_WIFI: options->wifi_only = true; options->do_screenshot = false; - options->dumpstate_hal_mode = DumpstateMode::WIFI; break; case Dumpstate::BugreportMode::BUGREPORT_DEFAULT: break; @@ -2618,13 +2770,14 @@ static void LogDumpOptions(const Dumpstate::DumpOptions& options) { MYLOGI( "do_vibrate: %d stream_to_socket: %d progress_updates_to_socket: %d do_screenshot: %d " "is_remote_mode: %d show_header_only: %d telephony_only: %d " - "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s " + "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s " "limited_only: %d args: %s\n", options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket, options.do_screenshot, options.is_remote_mode, options.show_header_only, options.telephony_only, options.wifi_only, - options.do_progress_updates, options.bugreport_fd.get(), options.bugreport_mode.c_str(), - toString(options.dumpstate_hal_mode).c_str(), options.limited_only, options.args.c_str()); + options.do_progress_updates, options.bugreport_fd.get(), + options.bugreport_mode_string.c_str(), + options.limited_only, options.args.c_str()); } void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode, @@ -2838,7 +2991,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n", - id_, options_->args.c_str(), options_->bugreport_mode.c_str(), version_.c_str()); + id_, options_->args.c_str(), options_->bugreport_mode_string.c_str(), version_.c_str()); do_early_screenshot_ = options_->do_progress_updates; diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 3722383e9e..773e292b63 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -25,6 +25,7 @@ #include <string> #include <vector> +#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> #include <android-base/macros.h> #include <android-base/unique_fd.h> #include <android/hardware/dumpstate/1.1/types.h> @@ -400,19 +401,18 @@ class Dumpstate { bool limited_only = false; // Whether progress updates should be published. bool do_progress_updates = false; - // The mode we'll use when calling IDumpstateDevice::dumpstateBoard. + // this is used to derive dumpstate HAL bug report mode // TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead. // The HAL is actually an API surface that can be validated, while the AIDL is not (@hide). - ::android::hardware::dumpstate::V1_1::DumpstateMode dumpstate_hal_mode = - ::android::hardware::dumpstate::V1_1::DumpstateMode::DEFAULT; + BugreportMode bugreport_mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT; // File descriptor to output zip file. Takes precedence over out_dir. android::base::unique_fd bugreport_fd; // File descriptor to screenshot file. android::base::unique_fd screenshot_fd; // Custom output directory. std::string out_dir; - // Bugreport mode of the bugreport. - std::string bugreport_mode; + // Bugreport mode of the bugreport as a string + std::string bugreport_mode_string; // Command-line arguments as string std::string args; // Notification title and description diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index db508b52bd..42beb2b6cf 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -33,6 +33,7 @@ #include <unistd.h> #include <thread> +#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h> #include <android-base/file.h> #include <android-base/properties.h> #include <android-base/stringprintf.h> @@ -47,6 +48,7 @@ namespace android { namespace os { namespace dumpstate { +using DumpstateDeviceAidl = ::aidl::android::hardware::dumpstate::IDumpstateDevice; using ::android::hardware::dumpstate::V1_1::DumpstateMode; using ::testing::EndsWith; using ::testing::Eq; @@ -186,7 +188,6 @@ TEST_F(DumpOptionsTest, InitializeNone) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeAdbBugreport) { @@ -210,7 +211,6 @@ TEST_F(DumpOptionsTest, InitializeAdbBugreport) { EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.stream_to_socket); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) { @@ -234,13 +234,11 @@ TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeFullBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true); EXPECT_TRUE(options_.do_screenshot); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::FULL); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -256,7 +254,6 @@ TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true); EXPECT_TRUE(options_.do_progress_updates); EXPECT_TRUE(options_.do_screenshot); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -272,7 +269,6 @@ TEST_F(DumpOptionsTest, InitializeRemoteBugReport) { EXPECT_TRUE(options_.is_remote_mode); EXPECT_FALSE(options_.do_vibrate); EXPECT_FALSE(options_.do_screenshot); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::REMOTE); // Other options retain default values EXPECT_FALSE(options_.progress_updates_to_socket); @@ -286,7 +282,7 @@ TEST_F(DumpOptionsTest, InitializeWearBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true); EXPECT_TRUE(options_.do_screenshot); EXPECT_TRUE(options_.do_progress_updates); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR); + // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -302,7 +298,6 @@ TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { EXPECT_FALSE(options_.do_screenshot); EXPECT_TRUE(options_.telephony_only); EXPECT_TRUE(options_.do_progress_updates); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::CONNECTIVITY); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -317,7 +312,6 @@ TEST_F(DumpOptionsTest, InitializeWifiBugReport) { options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false); EXPECT_FALSE(options_.do_screenshot); EXPECT_TRUE(options_.wifi_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WIFI); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -354,7 +348,6 @@ TEST_F(DumpOptionsTest, InitializeLimitedOnlyBugreport) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.stream_to_socket); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { @@ -371,7 +364,6 @@ TEST_F(DumpOptionsTest, InitializeDefaultBugReport) { EXPECT_EQ(status, Dumpstate::RunStatus::OK); EXPECT_TRUE(options_.do_screenshot); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); @@ -408,7 +400,6 @@ TEST_F(DumpOptionsTest, InitializePartial1) { EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializePartial2) { @@ -436,7 +427,6 @@ TEST_F(DumpOptionsTest, InitializePartial2) { EXPECT_FALSE(options_.stream_to_socket); EXPECT_FALSE(options_.progress_updates_to_socket); EXPECT_FALSE(options_.limited_only); - EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT); } TEST_F(DumpOptionsTest, InitializeHelp) { diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index 3f180d94a7..00babc3346 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -28,6 +28,7 @@ cc_defaults { "dexopt.cpp", "execv_helper.cpp", "globals.cpp", + "restorable_file.cpp", "run_dex2oat.cpp", "unique_file.cpp", "utils.cpp", @@ -45,6 +46,7 @@ cc_defaults { "libprocessgroup", "libselinux", "libutils", + "libziparchive", "server_configurable_flags", ], static_libs: [ @@ -79,7 +81,7 @@ cc_defaults { "-cert-err58-cpp", ], tidy_flags: [ - "-warnings-as-errors=clang-analyzer-security*,cert-*" + "-warnings-as-errors=clang-analyzer-security*,cert-*", ], } @@ -131,7 +133,10 @@ cc_test_host { "unique_file.cpp", "execv_helper.cpp", ], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "server_configurable_flags", @@ -169,7 +174,7 @@ cc_binary { // Needs to be wherever installd is as it's execed by // installd. - required: [ "migrate_legacy_obb_data.sh" ], + required: ["migrate_legacy_obb_data.sh"], } // OTA chroot tool @@ -193,7 +198,7 @@ cc_binary { "libutils", ], required: [ - "apexd" + "apexd", ], } @@ -212,7 +217,7 @@ cc_library_static { name: "libotapreoptparameters", cflags: [ "-Wall", - "-Werror" + "-Werror", ], srcs: ["otapreopt_parameters.cpp"], @@ -236,7 +241,7 @@ cc_binary { name: "otapreopt", cflags: [ "-Wall", - "-Werror" + "-Werror", ], srcs: [ @@ -245,6 +250,7 @@ cc_binary { "globals.cpp", "otapreopt.cpp", "otapreopt_utils.cpp", + "restorable_file.cpp", "run_dex2oat.cpp", "unique_file.cpp", "utils.cpp", @@ -267,6 +273,7 @@ cc_binary { "libprocessgroup", "libselinux", "libutils", + "libziparchive", "server_configurable_flags", ], } @@ -294,5 +301,5 @@ sh_binary { // Script to migrate legacy obb data. sh_binary { name: "migrate_legacy_obb_data.sh", - src: "migrate_legacy_obb_data.sh" + src: "migrate_legacy_obb_data.sh", } diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp index e29ff4c248..27690a3039 100644 --- a/cmds/installd/CacheItem.cpp +++ b/cmds/installd/CacheItem.cpp @@ -116,6 +116,7 @@ int CacheItem::purge() { break; } } + fts_close(fts); } else { if (tombstone) { if (truncate(path.c_str(), 0) != 0) { diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index b5ae5c6daf..3b29e2bd16 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -77,6 +77,8 @@ #define LOG_TAG "installd" #endif +// #define GRANULAR_LOCKS + using android::base::ParseUint; using android::base::StringPrintf; using std::endl; @@ -265,6 +267,104 @@ binder::Status checkArgumentPath(const std::optional<std::string>& path) { } \ } +#ifdef GRANULAR_LOCKS + +/** + * This class obtains in constructor and keeps the local strong pointer to the RefLock. + * On destruction, it checks if there are any other strong pointers, and remove the map entry if + * this was the last one. + */ +template <class Key, class Mutex> +struct LocalLockHolder { + using WeakPointer = std::weak_ptr<Mutex>; + using StrongPointer = std::shared_ptr<Mutex>; + using Map = std::unordered_map<Key, WeakPointer>; + using MapLock = std::recursive_mutex; + + LocalLockHolder(Key key, Map& map, MapLock& mapLock) + : mKey(std::move(key)), mMap(map), mMapLock(mapLock) { + std::lock_guard lock(mMapLock); + auto& weakPtr = mMap[mKey]; + + // Check if the RefLock is still alive. + mRefLock = weakPtr.lock(); + if (!mRefLock) { + // Create a new lock. + mRefLock = std::make_shared<Mutex>(); + weakPtr = mRefLock; + } + } + LocalLockHolder(LocalLockHolder&& other) noexcept + : mKey(std::move(other.mKey)), + mMap(other.mMap), + mMapLock(other.mMapLock), + mRefLock(std::move(other.mRefLock)) { + other.mRefLock.reset(); + } + ~LocalLockHolder() { + if (!mRefLock) { + return; + } + + std::lock_guard lock(mMapLock); + // Clear the strong pointer. + mRefLock.reset(); + auto found = mMap.find(mKey); + if (found == mMap.end()) { + return; + } + const auto& weakPtr = found->second; + // If this was the last pointer then it's ok to remove the map entry. + if (weakPtr.expired()) { + mMap.erase(found); + } + } + + void lock() { mRefLock->lock(); } + void unlock() { mRefLock->unlock(); } + void lock_shared() { mRefLock->lock_shared(); } + void unlock_shared() { mRefLock->unlock_shared(); } + +private: + Key mKey; + Map& mMap; + MapLock& mMapLock; + StrongPointer mRefLock; +}; + +using UserLock = LocalLockHolder<userid_t, std::shared_mutex>; +using UserWriteLockGuard = std::unique_lock<UserLock>; +using UserReadLockGuard = std::shared_lock<UserLock>; + +using PackageLock = LocalLockHolder<std::string, std::recursive_mutex>; +using PackageLockGuard = std::lock_guard<PackageLock>; + +#define LOCK_USER() \ + UserLock localUserLock(userId, mUserIdLock, mLock); \ + UserWriteLockGuard userLock(localUserLock) + +#define LOCK_USER_READ() \ + UserLock localUserLock(userId, mUserIdLock, mLock); \ + UserReadLockGuard userLock(localUserLock) + +#define LOCK_PACKAGE() \ + PackageLock localPackageLock(packageName, mPackageNameLock, mLock); \ + PackageLockGuard packageLock(localPackageLock) + +#define LOCK_PACKAGE_USER() \ + LOCK_USER_READ(); \ + LOCK_PACKAGE() + +#else + +#define LOCK_USER() std::lock_guard lock(mLock) +#define LOCK_PACKAGE() std::lock_guard lock(mLock) +#define LOCK_PACKAGE_USER() \ + (void)userId; \ + std::lock_guard lock(mLock) + +#endif // GRANULAR_LOCKS + } // namespace status_t InstalldNativeService::start() { @@ -288,8 +388,6 @@ status_t InstalldNativeService::dump(int fd, const Vector<String16> & /* args */ return PERMISSION_DENIED; } - std::lock_guard<std::recursive_mutex> lock(mLock); - { std::lock_guard<std::recursive_mutex> lock(mMountsLock); dprintf(fd, "Storage mounts:\n"); @@ -562,14 +660,13 @@ static binder::Status createAppDataDirs(const std::string& path, return ok(); } -binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid, - const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, - int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, - int64_t* _aidl_return) { +binder::Status InstalldNativeService::createAppDataLocked( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, + int32_t targetSdkVersion, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -638,10 +735,22 @@ binder::Status InstalldNativeService::createAppData(const std::optional<std::str } binder::Status InstalldNativeService::createAppData( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, + int32_t targetSdkVersion, int64_t* _aidl_return) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(uuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); + LOCK_PACKAGE_USER(); + return createAppDataLocked(uuid, packageName, userId, flags, appId, previousAppId, seInfo, + targetSdkVersion, _aidl_return); +} + +binder::Status InstalldNativeService::createAppData( const android::os::CreateAppDataArgs& args, android::os::CreateAppDataResult* _aidl_return) { ENFORCE_UID(AID_SYSTEM); - std::lock_guard<std::recursive_mutex> lock(mLock); + // Locking is performed depeer in the callstack. int64_t ceDataInode = -1; auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId, @@ -656,7 +765,7 @@ binder::Status InstalldNativeService::createAppDataBatched( const std::vector<android::os::CreateAppDataArgs>& args, std::vector<android::os::CreateAppDataResult>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); - std::lock_guard<std::recursive_mutex> lock(mLock); + // Locking is performed depeer in the callstack. std::vector<android::os::CreateAppDataResult> results; for (const auto &arg : args) { @@ -673,7 +782,7 @@ binder::Status InstalldNativeService::migrateAppData(const std::optional<std::st ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -717,7 +826,7 @@ binder::Status InstalldNativeService::clearAppProfiles(const std::string& packag const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); binder::Status res = ok(); if (!clear_primary_reference_profile(packageName, profileName)) { @@ -734,7 +843,7 @@ binder::Status InstalldNativeService::clearAppData(const std::optional<std::stri ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -831,7 +940,7 @@ static int destroy_app_current_profiles(const std::string& pkgname, userid_t use binder::Status InstalldNativeService::destroyAppProfiles(const std::string& packageName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); binder::Status res = ok(); std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr); @@ -851,7 +960,7 @@ binder::Status InstalldNativeService::destroyAppData(const std::optional<std::st ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -922,15 +1031,15 @@ binder::Status InstalldNativeService::fixupAppData(const std::optional<std::stri int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; - for (auto user : get_known_users(uuid_)) { + for (auto userId : get_known_users(uuid_)) { + LOCK_USER(); ATRACE_BEGIN("fixup user"); FTS* fts; FTSENT* p; - auto ce_path = create_data_user_ce_path(uuid_, user); - auto de_path = create_data_user_de_path(uuid_, user); + auto ce_path = create_data_user_ce_path(uuid_, userId); + auto de_path = create_data_user_de_path(uuid_, userId); char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { return error("Failed to fts_open"); @@ -1037,14 +1146,14 @@ static int32_t copy_directory_recursive(const char* from, const char* to) { return logwrap_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, LOG_ALOG, false, nullptr); } -binder::Status InstalldNativeService::snapshotAppData( - const std::optional<std::string>& volumeUuid, - const std::string& packageName, int32_t user, int32_t snapshotId, - int32_t storageFlags, int64_t* _aidl_return) { +binder::Status InstalldNativeService::snapshotAppData(const std::optional<std::string>& volumeUuid, + const std::string& packageName, + int32_t userId, int32_t snapshotId, + int32_t storageFlags, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); @@ -1057,19 +1166,19 @@ binder::Status InstalldNativeService::snapshotAppData( bool clear_ce_on_exit = false; bool clear_de_on_exit = false; - auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name, - &snapshotId] { + auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &userId, &package_name, + &snapshotId] { if (clear_de_on_exit) { - auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId, - package_name); + auto to = create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } } if (clear_ce_on_exit) { - auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId, - package_name); + auto to = create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } @@ -1079,10 +1188,11 @@ binder::Status InstalldNativeService::snapshotAppData( auto scope_guard = android::base::make_scope_guard(deleter); if (storageFlags & FLAG_STORAGE_DE) { - auto from = create_data_user_de_package_path(volume_uuid, user, package_name); - auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId); - auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user, - snapshotId, package_name); + auto from = create_data_user_de_package_path(volume_uuid, userId, package_name); + auto to = create_data_misc_de_rollback_path(volume_uuid, userId, snapshotId); + auto rollback_package_path = + create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); if (rc != 0) { @@ -1106,15 +1216,15 @@ binder::Status InstalldNativeService::snapshotAppData( } // The app may not have any data at all, in which case it's OK to skip here. - auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name); + auto from_ce = create_data_user_ce_package_path(volume_uuid, userId, package_name); if (access(from_ce.c_str(), F_OK) != 0) { LOG(INFO) << "Missing source " << from_ce; return ok(); } // ce_data_inode is not needed when FLAG_CLEAR_CACHE_ONLY is set. - binder::Status clear_cache_result = clearAppData(volumeUuid, packageName, user, - storageFlags | FLAG_CLEAR_CACHE_ONLY, 0); + binder::Status clear_cache_result = + clearAppData(volumeUuid, packageName, userId, storageFlags | FLAG_CLEAR_CACHE_ONLY, 0); if (!clear_cache_result.isOk()) { // It should be fine to continue snapshot if we for some reason failed // to clear cache. @@ -1122,8 +1232,9 @@ binder::Status InstalldNativeService::snapshotAppData( } // ce_data_inode is not needed when FLAG_CLEAR_CODE_CACHE_ONLY is set. - binder::Status clear_code_cache_result = clearAppData(volumeUuid, packageName, user, - storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, 0); + binder::Status clear_code_cache_result = + clearAppData(volumeUuid, packageName, userId, storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, + 0); if (!clear_code_cache_result.isOk()) { // It should be fine to continue snapshot if we for some reason failed // to clear code_cache. @@ -1131,10 +1242,11 @@ binder::Status InstalldNativeService::snapshotAppData( } if (storageFlags & FLAG_STORAGE_CE) { - auto from = create_data_user_ce_package_path(volume_uuid, user, package_name); - auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId); - auto rollback_package_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, - snapshotId, package_name); + auto from = create_data_user_ce_package_path(volume_uuid, userId, package_name); + auto to = create_data_misc_ce_rollback_path(volume_uuid, userId, snapshotId); + auto rollback_package_path = + create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); if (rc != 0) { @@ -1153,8 +1265,9 @@ binder::Status InstalldNativeService::snapshotAppData( return res; } if (_aidl_return != nullptr) { - auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, - snapshotId, package_name); + auto ce_snapshot_path = + create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return)); if (rc != 0) { res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path); @@ -1169,20 +1282,20 @@ binder::Status InstalldNativeService::snapshotAppData( binder::Status InstalldNativeService::restoreAppDataSnapshot( const std::optional<std::string>& volumeUuid, const std::string& packageName, - const int32_t appId, const std::string& seInfo, const int32_t user, + const int32_t appId, const std::string& seInfo, const int32_t userId, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); - auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, - user, snapshotId, package_name); - auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, - user, snapshotId, package_name); + auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); + auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId, + package_name); const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) && (access(from_ce.c_str(), F_OK) == 0); @@ -1202,14 +1315,14 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( // It's fine to pass 0 as ceDataInode here, because restoreAppDataSnapshot // can only be called when user unlocks the phone, meaning that CE user data // is decrypted. - binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, - 0 /* ceDataInode */); + binder::Status res = + clearAppData(volumeUuid, packageName, userId, storageFlags, 0 /* ceDataInode */); if (!res.isOk()) { return res; } if (needs_ce_rollback) { - auto to_ce = create_data_user_ce_path(volume_uuid, user); + auto to_ce = create_data_user_ce_path(volume_uuid, userId); int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from_ce + " to " + to_ce); @@ -1219,11 +1332,11 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( } if (needs_de_rollback) { - auto to_de = create_data_user_de_path(volume_uuid, user); + auto to_de = create_data_user_de_path(volume_uuid, userId); int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str()); if (rc != 0) { if (needs_ce_rollback) { - auto ce_data = create_data_user_ce_package_path(volume_uuid, user, package_name); + auto ce_data = create_data_user_ce_package_path(volume_uuid, userId, package_name); LOG(WARNING) << "de_data rollback failed. Erasing rolled back ce_data " << ce_data; if (delete_dir_contents(ce_data.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete rolled back ce_data " << ce_data; @@ -1236,24 +1349,24 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( } // Finally, restore the SELinux label on the app data. - return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo); + return restoreconAppData(volumeUuid, packageName, userId, storageFlags, appId, seInfo); } binder::Status InstalldNativeService::destroyAppDataSnapshot( - const std::optional<std::string> &volumeUuid, const std::string& packageName, - const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId, + const std::optional<std::string>& volumeUuid, const std::string& packageName, + const int32_t userId, const int64_t ceSnapshotInode, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); if (storageFlags & FLAG_STORAGE_DE) { - auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, - user, snapshotId, package_name); + auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, userId, + snapshotId, package_name); int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */); if (res != 0) { @@ -1262,8 +1375,9 @@ binder::Status InstalldNativeService::destroyAppDataSnapshot( } if (storageFlags & FLAG_STORAGE_CE) { - auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, - user, snapshotId, package_name, ceSnapshotInode); + auto ce_snapshot_path = + create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId, + package_name, ceSnapshotInode); int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + ce_snapshot_path); @@ -1273,15 +1387,15 @@ binder::Status InstalldNativeService::destroyAppDataSnapshot( } binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified( - const std::optional<std::string> &volumeUuid, const int32_t user, + const std::optional<std::string>& volumeUuid, const int32_t userId, const std::vector<int32_t>& retainSnapshotIds) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; - auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, user); + auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, userId); std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(base_path.c_str()), closedir); if (!dir) { @@ -1299,8 +1413,8 @@ binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified( if (parse_ok && std::find(retainSnapshotIds.begin(), retainSnapshotIds.end(), snapshot_id) == retainSnapshotIds.end()) { - auto rollback_path = create_data_misc_ce_rollback_path( - volume_uuid, user, snapshot_id); + auto rollback_path = + create_data_misc_ce_rollback_path(volume_uuid, userId, snapshot_id); int res = delete_dir_contents_and_dir(rollback_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + rollback_path); @@ -1318,7 +1432,7 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s CHECK_ARGUMENT_UUID(fromUuid); CHECK_ARGUMENT_UUID(toUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* from_uuid = fromUuid ? fromUuid->c_str() : nullptr; const char* to_uuid = toUuid ? toUuid->c_str() : nullptr; @@ -1346,24 +1460,25 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } // Copy private data for all known users - for (auto user : users) { + for (auto userId : users) { + LOCK_USER(); // Data source may not exist for all users; that's okay - auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name); + auto from_ce = create_data_user_ce_package_path(from_uuid, userId, package_name); if (access(from_ce.c_str(), F_OK) != 0) { LOG(INFO) << "Missing source " << from_ce; continue; } - if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId, - /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr).isOk()) { + if (!createAppDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE, + appId, /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr) + .isOk()) { res = error("Failed to create package target"); goto fail; } - { - auto from = create_data_user_de_package_path(from_uuid, user, package_name); - auto to = create_data_user_de_path(to_uuid, user); + auto from = create_data_user_de_package_path(from_uuid, userId, package_name); + auto to = create_data_user_de_path(to_uuid, userId); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { @@ -1372,8 +1487,8 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } } { - auto from = create_data_user_ce_package_path(from_uuid, user, package_name); - auto to = create_data_user_ce_path(to_uuid, user); + auto from = create_data_user_ce_package_path(from_uuid, userId, package_name); + auto to = create_data_user_ce_path(to_uuid, userId); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { @@ -1382,8 +1497,9 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::s } } - if (!restoreconAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, - appId, seInfo).isOk()) { + if (!restoreconAppDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE, + appId, seInfo) + .isOk()) { res = error("Failed to restorecon"); goto fail; } @@ -1401,15 +1517,16 @@ fail: LOG(WARNING) << "Failed to rollback " << to_app_package_path; } } - for (auto user : users) { + for (auto userId : users) { + LOCK_USER(); { - auto to = create_data_user_de_package_path(to_uuid, user, package_name); + auto to = create_data_user_de_package_path(to_uuid, userId, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } } { - auto to = create_data_user_ce_package_path(to_uuid, user, package_name); + auto to = create_data_user_ce_package_path(to_uuid, userId, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to rollback " << to; } @@ -1422,7 +1539,7 @@ binder::Status InstalldNativeService::createUserData(const std::optional<std::st int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; if (flags & FLAG_STORAGE_DE) { @@ -1440,7 +1557,7 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; binder::Status res = ok(); @@ -1477,7 +1594,9 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); - std::lock_guard<std::recursive_mutex> lock(mLock); +#ifndef GRANULAR_LOCKS + std::lock_guard lock(mLock); +#endif // !GRANULAR_LOCKS auto uuidString = uuid.value_or(""); const char* uuid_ = uuid ? uuid->c_str() : nullptr; @@ -1504,13 +1623,24 @@ binder::Status InstalldNativeService::freeCache(const std::optional<std::string> // 1. Create trackers for every known UID ATRACE_BEGIN("create"); + const auto users = get_known_users(uuid_); +#ifdef GRANULAR_LOCKS + std::vector<UserLock> userLocks; + userLocks.reserve(users.size()); + std::vector<UserWriteLockGuard> lockGuards; + lockGuards.reserve(users.size()); +#endif // GRANULAR_LOCKS std::unordered_map<uid_t, std::shared_ptr<CacheTracker>> trackers; - for (auto user : get_known_users(uuid_)) { + for (auto userId : users) { +#ifdef GRANULAR_LOCKS + userLocks.emplace_back(userId, mUserIdLock, mLock); + lockGuards.emplace_back(userLocks.back()); +#endif // GRANULAR_LOCKS FTS *fts; FTSENT *p; - auto ce_path = create_data_user_ce_path(uuid_, user); - auto de_path = create_data_user_de_path(uuid_, user); - auto media_path = findDataMediaPath(uuid, user) + "/Android/data/"; + auto ce_path = create_data_user_ce_path(uuid_, userId); + auto de_path = create_data_user_de_path(uuid_, userId); + auto media_path = findDataMediaPath(uuid, userId) + "/Android/data/"; char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), (char*) media_path.c_str(), nullptr }; if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) { @@ -1646,7 +1776,6 @@ binder::Status InstalldNativeService::rmdex(const std::string& codePath, const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(codePath); - std::lock_guard<std::recursive_mutex> lock(mLock); char dex_path[PKG_PATH_MAX]; @@ -1876,7 +2005,18 @@ static void collectManualExternalStatsForUser(const std::string& path, struct st } fts_close(fts); } - +static bool ownsExternalStorage(int32_t appId) { + // Fetch external storage owner appid and check if it is the same as the + // current appId whose size is calculated + struct stat s; + auto _picDir = StringPrintf("%s/Pictures", create_data_media_path(nullptr, 0).c_str()); + // check if the stat are present + if (stat(_picDir.c_str(), &s) == 0) { + // fetch the appId from the uid of the media app + return ((int32_t)multiuser_get_app_id(s.st_uid) == appId); + } + return false; +} binder::Status InstalldNativeService::getAppSize(const std::optional<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, int32_t flags, int32_t appId, const std::vector<int64_t>& ceDataInodes, @@ -1931,8 +2071,10 @@ binder::Status InstalldNativeService::getAppSize(const std::optional<std::string calculate_tree_size(obbCodePath, &extStats.codeSize); } ATRACE_END(); - - if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) { + // Calculating the app size of the external storage owning app in a manual way, since + // calculating it through quota apis also includes external media storage in the app storage + // numbers + if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START && !ownsExternalStorage(appId)) { ATRACE_BEGIN("code"); for (const auto& codePath : codePaths) { calculate_tree_size(codePath, &stats.codeSize, -1, @@ -2362,7 +2504,7 @@ binder::Status InstalldNativeService::getAppCrates( CHECK_ARGUMENT_PACKAGE_NAME(packageName); } #ifdef ENABLE_STORAGE_CRATES - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); auto retVector = std::vector<std::optional<CrateMetadata>>(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; @@ -2408,7 +2550,7 @@ binder::Status InstalldNativeService::getUserCrates( ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); #ifdef ENABLE_STORAGE_CRATES - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto retVector = std::vector<std::optional<CrateMetadata>>(); @@ -2465,7 +2607,7 @@ binder::Status InstalldNativeService::dumpProfiles(int32_t uid, const std::strin ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(codePath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); *_aidl_return = dump_profiles(uid, packageName, profileName, codePath); return ok(); @@ -2477,7 +2619,7 @@ binder::Status InstalldNativeService::copySystemProfile(const std::string& syste bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName, profileName); return ok(); } @@ -2487,7 +2629,7 @@ binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::stri const std::string& profileName, int* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); *_aidl_return = analyze_primary_profiles(uid, packageName, profileName); return ok(); @@ -2498,7 +2640,7 @@ binder::Status InstalldNativeService::createProfileSnapshot(int32_t appId, const std::string& classpath, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); *_aidl_return = create_profile_snapshot(appId, packageName, profileName, classpath); return ok(); @@ -2508,7 +2650,7 @@ binder::Status InstalldNativeService::destroyProfileSnapshot(const std::string& const std::string& profileName) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); std::string snapshot = create_snapshot_profile_path(packageName, profileName); if ((unlink(snapshot.c_str()) != 0) && (errno != ENOENT)) { @@ -2539,7 +2681,8 @@ binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t } CHECK_ARGUMENT_PATH(outputPath); CHECK_ARGUMENT_PATH(dexMetadataPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + const auto userId = multiuser_get_user_id(uid); + LOCK_PACKAGE_USER(); const char* oat_dir = getCStr(outputPath); const char* instruction_set = instructionSet.c_str(); @@ -2590,7 +2733,7 @@ binder::Status InstalldNativeService::linkNativeLibraryDirectory( CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(nativeLibPath32); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); @@ -2681,7 +2824,16 @@ binder::Status InstalldNativeService::restoreconAppData(const std::optional<std: ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); + return restoreconAppDataLocked(uuid, packageName, userId, flags, appId, seInfo); +} + +binder::Status InstalldNativeService::restoreconAppDataLocked( + const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_UUID(uuid); + CHECK_ARGUMENT_PACKAGE_NAME(packageName); binder::Status res = ok(); @@ -2711,7 +2863,7 @@ binder::Status InstalldNativeService::createOatDir(const std::string& oatDir, const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(oatDir); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* oat_dir = oatDir.c_str(); const char* instruction_set = instructionSet.c_str(); @@ -2736,7 +2888,7 @@ binder::Status InstalldNativeService::createOatDir(const std::string& oatDir, binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(packageDir); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); if (validate_apk_path(packageDir.c_str())) { return error("Invalid path " + packageDir); @@ -2752,7 +2904,7 @@ binder::Status InstalldNativeService::linkFile(const std::string& relativePath, ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(fromBase); CHECK_ARGUMENT_PATH(toBase); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* relative_path = relativePath.c_str(); const char* from_base = fromBase.c_str(); @@ -2782,7 +2934,7 @@ binder::Status InstalldNativeService::moveAb(const std::string& apkPath, ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(apkPath); CHECK_ARGUMENT_PATH(outputPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* apk_path = apkPath.c_str(); const char* instruction_set = instructionSet.c_str(); @@ -2798,7 +2950,7 @@ binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath, ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(apkPath); CHECK_ARGUMENT_PATH(outputPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); const char* apk_path = apkPath.c_str(); const char* instruction_set = instructionSet.c_str(); @@ -2831,7 +2983,7 @@ binder::Status InstalldNativeService::installApkVerity(const std::string& filePa android::base::unique_fd verityInputAshmem, int32_t contentSize) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(filePath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) { return ok(); @@ -2913,7 +3065,7 @@ binder::Status InstalldNativeService::assertFsverityRootHashMatches(const std::s const std::vector<uint8_t>& expectedHash) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(filePath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE(); if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) { return ok(); @@ -2952,7 +3104,8 @@ binder::Status InstalldNativeService::reconcileSecondaryDexFile( CHECK_ARGUMENT_UUID(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(dexPath); - std::lock_guard<std::recursive_mutex> lock(mLock); + const auto userId = multiuser_get_user_id(uid); + LOCK_PACKAGE_USER(); bool result = android::installd::reconcile_secondary_dex_file( dexPath, packageName, uid, isas, volumeUuid, storage_flag, _aidl_return); @@ -3032,8 +3185,9 @@ binder::Status InstalldNativeService::tryMountDataMirror( const char* uuid_ = uuid->c_str(); + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_)); - std::lock_guard<std::recursive_mutex> lock(mLock); if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) { return error("Failed to create CE mirror"); } @@ -3102,8 +3256,9 @@ binder::Status InstalldNativeService::onPrivateVolumeRemoved( std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_)); std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_)); + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + // Unmount CE storage - std::lock_guard<std::recursive_mutex> lock(mLock); if (TEMP_FAILURE_RETRY(umount(mirrorCeVolPath.c_str())) != 0) { if (errno != ENOENT) { res = error(StringPrintf("Failed to umount %s %s", mirrorCeVolPath.c_str(), @@ -3152,7 +3307,7 @@ binder::Status InstalldNativeService::prepareAppProfile(const std::string& packa ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(codePath); - std::lock_guard<std::recursive_mutex> lock(mLock); + LOCK_PACKAGE_USER(); *_aidl_return = prepare_app_profile(packageName, userId, appId, profileName, codePath, dexMetadata); diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 8cfda010a8..09581bb544 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -21,8 +21,9 @@ #include <inttypes.h> #include <unistd.h> -#include <vector> +#include <shared_mutex> #include <unordered_map> +#include <vector> #include <android-base/macros.h> #include <binder/BinderService.h> @@ -49,6 +50,11 @@ public: const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return); + binder::Status createAppDataLocked(const std::optional<std::string>& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, int32_t previousAppId, + const std::string& seInfo, int32_t targetSdkVersion, + int64_t* _aidl_return); binder::Status createAppData( const android::os::CreateAppDataArgs& args, @@ -60,6 +66,9 @@ public: binder::Status restoreconAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo); + binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid, + const std::string& packageName, int32_t userId, + int32_t flags, int32_t appId, const std::string& seInfo); binder::Status migrateAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags); binder::Status clearAppData(const std::optional<std::string>& uuid, @@ -181,6 +190,8 @@ public: private: std::recursive_mutex mLock; + std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock; + std::unordered_map<std::string, std::weak_ptr<std::recursive_mutex>> mPackageNameLock; std::recursive_mutex mMountsLock; std::recursive_mutex mQuotasLock; diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index f3ec63f870..b6f42ad172 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -52,14 +52,16 @@ #include <server_configurable_flags/get_flags.h> #include <system/thread_defs.h> #include <utils/Mutex.h> +#include <ziparchive/zip_archive.h> #include "dexopt.h" #include "dexopt_return_codes.h" #include "execv_helper.h" #include "globals.h" -#include "installd_deps.h" #include "installd_constants.h" +#include "installd_deps.h" #include "otapreopt_utils.h" +#include "restorable_file.h" #include "run_dex2oat.h" #include "unique_file.h" #include "utils.h" @@ -308,12 +310,6 @@ static bool IsBootClassPathProfilingEnable() { return profile_boot_class_path == "true"; } -static void UnlinkIgnoreResult(const std::string& path) { - if (unlink(path.c_str()) < 0) { - PLOG(ERROR) << "Failed to unlink " << path; - } -} - /* * Whether dexopt should use a swap file when compiling an APK. * @@ -459,8 +455,8 @@ static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::st }); } -static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name, - const std::string& location) { +static unique_fd open_snapshot_profile(uid_t uid, const std::string& package_name, + const std::string& location) { std::string profile = create_snapshot_profile_path(package_name, location); return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); } @@ -987,42 +983,34 @@ static bool create_oat_out_path(const char* apk_path, const char* instruction_se } // (re)Creates the app image if needed. -UniqueFile maybe_open_app_image(const std::string& out_oat_path, - bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) { - +RestorableFile maybe_open_app_image(const std::string& out_oat_path, bool generate_app_image, + bool is_public, int uid, bool is_secondary_dex) { const std::string image_path = create_image_filename(out_oat_path); if (image_path.empty()) { // Happens when the out_oat_path has an unknown extension. - return UniqueFile(); + return RestorableFile(); } - // In case there is a stale image, remove it now. Ignore any error. - unlink(image_path.c_str()); - // Not enabled, exit. if (!generate_app_image) { - return UniqueFile(); + RestorableFile::RemoveAllFiles(image_path); + return RestorableFile(); } std::string app_image_format = GetProperty("dalvik.vm.appimageformat", ""); if (app_image_format.empty()) { - return UniqueFile(); + RestorableFile::RemoveAllFiles(image_path); + return RestorableFile(); } - // Recreate is true since we do not want to modify a mapped image. If the app is - // already running and we modify the image file, it can cause crashes (b/27493510). - UniqueFile image_file( - open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/), - image_path, - UnlinkIgnoreResult); + // If the app is already running and we modify the image file, it can cause crashes + // (b/27493510). + RestorableFile image_file = RestorableFile::CreateWritableFile(image_path, + /*permissions*/ 0600); if (image_file.fd() < 0) { // Could not create application image file. Go on since we can compile without it. LOG(ERROR) << "installd could not create '" << image_path << "' for image file during dexopt"; - // If we have a valid image file path but no image fd, explicitly erase the image file. - if (unlink(image_path.c_str()) < 0) { - if (errno != ENOENT) { - PLOG(ERROR) << "Couldn't unlink image file " << image_path; - } - } + // If we have a valid image file path but cannot create tmp file, reset it. + image_file.reset(); } else if (!set_permissions_and_ownership( image_file.fd(), is_public, uid, image_path.c_str(), is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str()); @@ -1096,9 +1084,9 @@ UniqueFile maybe_open_reference_profile(const std::string& pkgname, // Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to // out_vdex_wrapper. Returns true for success or false in case of errors. bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed, - const char* instruction_set, bool is_public, int uid, bool is_secondary_dex, - bool profile_guided, UniqueFile* in_vdex_wrapper, - UniqueFile* out_vdex_wrapper) { + const char* instruction_set, bool is_public, int uid, + bool is_secondary_dex, bool profile_guided, + UniqueFile* in_vdex_wrapper, RestorableFile* out_vdex_wrapper) { CHECK(in_vdex_wrapper != nullptr); CHECK(out_vdex_wrapper != nullptr); // Open the existing VDEX. We do this before creating the new output VDEX, which will @@ -1113,6 +1101,14 @@ bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, return false; } + // Create work file first. All files will be deleted when it fails. + *out_vdex_wrapper = RestorableFile::CreateWritableFile(out_vdex_path_str, + /*permissions*/ 0644); + if (out_vdex_wrapper->fd() < 0) { + ALOGE("installd cannot open vdex '%s' during dexopt\n", out_vdex_path_str.c_str()); + return false; + } + bool update_vdex_in_place = false; if (dexopt_action != DEX2OAT_FROM_SCRATCH) { // Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd. @@ -1144,41 +1140,19 @@ bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, (dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) && !profile_guided; if (update_vdex_in_place) { + // dex2oat marks it invalid anyway. So delete it and set work file fd. + unlink(in_vdex_path_str.c_str()); // Open the file read-write to be able to update it. - in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0), - in_vdex_path_str); - if (in_vdex_wrapper->fd() == -1) { - // If we failed to open the file, we cannot update it in place. - update_vdex_in_place = false; - } + in_vdex_wrapper->reset(out_vdex_wrapper->fd(), in_vdex_path_str); + // Disable auto close for the in wrapper fd (it will be done when destructing the out + // wrapper). + in_vdex_wrapper->DisableAutoClose(); } else { in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0), in_vdex_path_str); } } - // If we are updating the vdex in place, we do not need to recreate a vdex, - // and can use the same existing one. - if (update_vdex_in_place) { - // We unlink the file in case the invocation of dex2oat fails, to ensure we don't - // have bogus stale vdex files. - out_vdex_wrapper->reset( - in_vdex_wrapper->fd(), - out_vdex_path_str, - UnlinkIgnoreResult); - // Disable auto close for the in wrapper fd (it will be done when destructing the out - // wrapper). - in_vdex_wrapper->DisableAutoClose(); - } else { - out_vdex_wrapper->reset( - open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644), - out_vdex_path_str, - UnlinkIgnoreResult); - if (out_vdex_wrapper->fd() < 0) { - ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str()); - return false; - } - } if (!set_permissions_and_ownership(out_vdex_wrapper->fd(), is_public, uid, out_vdex_path_str.c_str(), is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str()); @@ -1190,16 +1164,13 @@ bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, } // Opens the output oat file for the given apk. -UniqueFile open_oat_out_file(const char* apk_path, const char* oat_dir, - bool is_public, int uid, const char* instruction_set, bool is_secondary_dex) { +RestorableFile open_oat_out_file(const char* apk_path, const char* oat_dir, bool is_public, int uid, + const char* instruction_set, bool is_secondary_dex) { char out_oat_path[PKG_PATH_MAX]; if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) { - return UniqueFile(); + return RestorableFile(); } - UniqueFile oat( - open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644), - out_oat_path, - UnlinkIgnoreResult); + RestorableFile oat = RestorableFile::CreateWritableFile(out_oat_path, /*permissions*/ 0644); if (oat.fd() < 0) { PLOG(ERROR) << "installd cannot open output during dexopt" << out_oat_path; } else if (!set_permissions_and_ownership( @@ -1838,6 +1809,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins if (sec_dex_result == kSecondaryDexOptProcessOk) { oat_dir = oat_dir_str.c_str(); if (dexopt_needed == NO_DEXOPT_NEEDED) { + *completed = true; return 0; // Nothing to do, report success. } } else if (sec_dex_result == kSecondaryDexOptProcessCancelled) { @@ -1873,8 +1845,8 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } // Create the output OAT file. - UniqueFile out_oat = open_oat_out_file(dex_path, oat_dir, is_public, uid, - instruction_set, is_secondary_dex); + RestorableFile out_oat = + open_oat_out_file(dex_path, oat_dir, is_public, uid, instruction_set, is_secondary_dex); if (out_oat.fd() < 0) { *error_msg = "Could not open out oat file."; return -1; @@ -1882,7 +1854,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins // Open vdex files. UniqueFile in_vdex; - UniqueFile out_vdex; + RestorableFile out_vdex; if (!open_vdex_files_for_dex2oat(dex_path, out_oat.path().c_str(), dexopt_needed, instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex, &out_vdex)) { @@ -1918,8 +1890,8 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } // Create the app image file if needed. - UniqueFile out_image = maybe_open_app_image( - out_oat.path(), generate_app_image, is_public, uid, is_secondary_dex); + RestorableFile out_image = maybe_open_app_image(out_oat.path(), generate_app_image, is_public, + uid, is_secondary_dex); UniqueFile dex_metadata; if (dex_metadata_path != nullptr) { @@ -1952,30 +1924,18 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---"; RunDex2Oat runner(dex2oat_bin, execv_helper.get()); - runner.Initialize(out_oat, - out_vdex, - out_image, - in_dex, - in_vdex, - dex_metadata, - reference_profile, - class_loader_context, - join_fds(context_input_fds), - swap_fd.get(), - instruction_set, - compiler_filter, - debuggable, - boot_complete, - for_restore, - target_sdk_version, - enable_hidden_api_checks, - generate_compact_dex, - use_jitzygote_image, + runner.Initialize(out_oat.GetUniqueFile(), out_vdex.GetUniqueFile(), out_image.GetUniqueFile(), + in_dex, in_vdex, dex_metadata, reference_profile, class_loader_context, + join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter, + debuggable, boot_complete, for_restore, target_sdk_version, + enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image, compilation_reason); bool cancelled = false; pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled); if (cancelled) { + *completed = false; + reference_profile.DisableCleanup(); return 0; } if (pid == 0) { @@ -2003,6 +1963,7 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- cancelled"; // cancelled, not an error *completed = false; + reference_profile.DisableCleanup(); return 0; } LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- status=0x" @@ -2012,13 +1973,42 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } } - // TODO(b/156537504) Implement SWAP of completed files - // We've been successful, don't delete output. - out_oat.DisableCleanup(); - out_vdex.DisableCleanup(); - out_image.DisableCleanup(); + // dex2oat ran successfully, so profile is safe to keep. reference_profile.DisableCleanup(); + // We've been successful, commit work files. + // If committing (=renaming tmp to regular) fails, try to restore backup files. + // If restoring fails as well, as a last resort, remove all files. + if (!out_oat.CreateBackupFile() || !out_vdex.CreateBackupFile() || + !out_image.CreateBackupFile()) { + // Renaming failure can mean that the original file may not be accessible from installd. + LOG(ERROR) << "Cannot create backup file from existing file, file in wrong state?" + << ", out_oat:" << out_oat.path() << " ,out_vdex:" << out_vdex.path() + << " ,out_image:" << out_image.path(); + out_oat.ResetAndRemoveAllFiles(); + out_vdex.ResetAndRemoveAllFiles(); + out_image.ResetAndRemoveAllFiles(); + return -1; + } + if (!out_oat.CommitWorkFile() || !out_vdex.CommitWorkFile() || !out_image.CommitWorkFile()) { + LOG(ERROR) << "Cannot commit, out_oat:" << out_oat.path() + << " ,out_vdex:" << out_vdex.path() << " ,out_image:" << out_image.path(); + if (!out_oat.RestoreBackupFile() || !out_vdex.RestoreBackupFile() || + !out_image.RestoreBackupFile()) { + LOG(ERROR) << "Cannot cancel commit, out_oat:" << out_oat.path() + << " ,out_vdex:" << out_vdex.path() << " ,out_image:" << out_image.path(); + // Restoring failed. + out_oat.ResetAndRemoveAllFiles(); + out_vdex.ResetAndRemoveAllFiles(); + out_image.ResetAndRemoveAllFiles(); + } + return -1; + } + // Now remove remaining backup files. + out_oat.RemoveBackupFile(); + out_vdex.RemoveBackupFile(); + out_image.RemoveBackupFile(); + *completed = true; return 0; } @@ -2562,7 +2552,7 @@ static bool create_app_profile_snapshot(int32_t app_id, const std::string& classpath) { int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id); - unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); + unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name); if (snapshot_fd < 0) { return false; } @@ -2636,7 +2626,7 @@ static bool create_boot_image_profile_snapshot(const std::string& package_name, } // Open and create the snapshot profile. - unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name); + unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name); // Collect all non empty profiles. // The collection will traverse all applications profiles and find the non empty files. @@ -2738,6 +2728,20 @@ bool create_profile_snapshot(int32_t app_id, const std::string& package_name, } } +static bool check_profile_exists_in_dexmetadata(const std::string& dex_metadata) { + ZipArchiveHandle zip = nullptr; + if (OpenArchive(dex_metadata.c_str(), &zip) != 0) { + PLOG(ERROR) << "Failed to open dm '" << dex_metadata << "'"; + return false; + } + + ZipEntry64 entry; + int result = FindEntry(zip, "primary.prof", &entry); + CloseArchive(zip); + + return result != 0 ? false : true; +} + bool prepare_app_profile(const std::string& package_name, userid_t user_id, appid_t app_id, @@ -2754,7 +2758,7 @@ bool prepare_app_profile(const std::string& package_name, } // Check if we need to install the profile from the dex metadata. - if (!dex_metadata) { + if (!dex_metadata || !check_profile_exists_in_dexmetadata(dex_metadata->c_str())) { return true; } diff --git a/cmds/installd/restorable_file.cpp b/cmds/installd/restorable_file.cpp new file mode 100644 index 0000000000..fd54a232d5 --- /dev/null +++ b/cmds/installd/restorable_file.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "restorable_file.h" + +#include <string> + +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <android-base/logging.h> +#include <android-base/stringprintf.h> + +namespace { + +constexpr char kTmpFileSuffix[] = ".tmp"; +constexpr char kBackupFileSuffix[] = ".backup"; + +std::string GetTmpFilePath(const std::string& path) { + return android::base::StringPrintf("%s%s", path.c_str(), kTmpFileSuffix); +} + +std::string GetBackupFilePath(const std::string& path) { + return android::base::StringPrintf("%s%s", path.c_str(), kBackupFileSuffix); +} + +void UnlinkPossiblyNonExistingFile(const std::string& path) { + if (unlink(path.c_str()) < 0) { + if (errno != ENOENT && errno != EROFS) { // EROFS reported even if it does not exist. + PLOG(ERROR) << "Cannot unlink: " << path; + } + } +} + +// Check if file for the given path exists +bool FileExists(const std::string& path) { + struct stat st; + return ::stat(path.c_str(), &st) == 0; +} + +} // namespace + +namespace android { +namespace installd { + +RestorableFile::RestorableFile() : RestorableFile(-1, "") {} + +RestorableFile::RestorableFile(int value, const std::string& path) : unique_file_(value, path) { + // As cleanup is null, this does not make much difference but use unique_file_ only for closing + // tmp file. + unique_file_.DisableCleanup(); +} + +RestorableFile::~RestorableFile() { + reset(); +} + +void RestorableFile::reset() { + // need to copy before reset clears it. + std::string path(unique_file_.path()); + unique_file_.reset(); + if (!path.empty()) { + UnlinkPossiblyNonExistingFile(GetTmpFilePath(path)); + } +} + +bool RestorableFile::CreateBackupFile() { + if (path().empty() || !FileExists(path())) { + return true; + } + std::string backup = GetBackupFilePath(path()); + UnlinkPossiblyNonExistingFile(backup); + if (rename(path().c_str(), backup.c_str()) < 0) { + PLOG(ERROR) << "Cannot rename " << path() << " to " << backup; + return false; + } + return true; +} + +bool RestorableFile::CommitWorkFile() { + std::string path(unique_file_.path()); + // Keep the path with Commit for debugging purpose. + unique_file_.reset(-1, path); + if (!path.empty()) { + if (rename(GetTmpFilePath(path).c_str(), path.c_str()) < 0) { + PLOG(ERROR) << "Cannot rename " << GetTmpFilePath(path) << " to " << path; + // Remove both files as renaming can fail due to the original file as well. + UnlinkPossiblyNonExistingFile(path); + UnlinkPossiblyNonExistingFile(GetTmpFilePath(path)); + return false; + } + } + + return true; +} + +bool RestorableFile::RestoreBackupFile() { + std::string backup = GetBackupFilePath(path()); + if (path().empty() || !FileExists(backup)) { + return true; + } + UnlinkPossiblyNonExistingFile(path()); + if (rename(backup.c_str(), path().c_str()) < 0) { + PLOG(ERROR) << "Cannot rename " << backup << " to " << path(); + return false; + } + return true; +} + +void RestorableFile::RemoveBackupFile() { + UnlinkPossiblyNonExistingFile(GetBackupFilePath(path())); +} + +const UniqueFile& RestorableFile::GetUniqueFile() const { + return unique_file_; +} + +void RestorableFile::ResetAndRemoveAllFiles() { + std::string path(unique_file_.path()); + reset(); + RemoveAllFiles(path); +} + +RestorableFile RestorableFile::CreateWritableFile(const std::string& path, int permissions) { + std::string tmp_file_path = GetTmpFilePath(path); + // If old tmp file exists, delete it. + UnlinkPossiblyNonExistingFile(tmp_file_path); + int fd = -1; + if (!path.empty()) { + fd = open(tmp_file_path.c_str(), O_RDWR | O_CREAT, permissions); + if (fd < 0) { + PLOG(ERROR) << "Cannot create file: " << tmp_file_path; + } + } + RestorableFile rf(fd, path); + return rf; +} + +void RestorableFile::RemoveAllFiles(const std::string& path) { + UnlinkPossiblyNonExistingFile(GetTmpFilePath(path)); + UnlinkPossiblyNonExistingFile(GetBackupFilePath(path)); + UnlinkPossiblyNonExistingFile(path); +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/restorable_file.h b/cmds/installd/restorable_file.h new file mode 100644 index 0000000000..eda229241a --- /dev/null +++ b/cmds/installd/restorable_file.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INSTALLD_RESTORABLE_FILE_H +#define ANDROID_INSTALLD_RESTORABLE_FILE_H + +#include <functional> +#include <string> + +#include "unique_file.h" + +namespace android { +namespace installd { + +// This is a file abstraction which allows restoring to the original file while temporary work +// file is updated. +// +// Typical flow for this API will be: +// RestorableFile rf = RestorableFile::CreateWritableFile(...) +// write to file using file descriptor acquired from: rf.fd() +// Make work file into a regular file with: rf.CommitWorkFile() +// Or throw away the work file by destroying the instance without calling CommitWorkFile(). +// The temporary work file is closed / removed when an instance is destroyed without calling +// CommitWorkFile(). The original file, if CommitWorkFile() is not called, will be kept. +// +// For safer restoration of original file when commit fails, following 3 steps can be taken: +// 1. CreateBackupFile(): This renames an existing regular file into a separate backup file. +// 2. CommitWorkFile(): Rename the work file into the regular file. +// 3. RemoveBackupFile(): Removes the backup file +// If CommitWorkFile fails, client can call RestoreBackupFile() which will restore regular file from +// the backup. +class RestorableFile { +public: + // Creates invalid instance with no fd (=-1) and empty path. + RestorableFile(); + RestorableFile(RestorableFile&& other) = default; + ~RestorableFile(); + + // Passes all contents of other file into the current file. + // Files kept for the current file will be either deleted or committed depending on + // CommitWorkFile() and DisableCleanUp() calls made before this. + RestorableFile& operator=(RestorableFile&& other) = default; + + // Gets file descriptor for backing work (=temporary) file. If work file does not exist, it will + // return -1. + int fd() const { return unique_file_.fd(); } + + // Gets the path name for the regular file (not temporary file). + const std::string& path() const { return unique_file_.path(); } + + // Closes work file, deletes it and resets all internal states into default states. + void reset(); + + // Closes work file and closes all files including work file, backup file and regular file. + void ResetAndRemoveAllFiles(); + + // Creates a backup file by renaming existing regular file. This will return false if renaming + // fails. If regular file for renaming does not exist, it will return true. + bool CreateBackupFile(); + + // Closes existing work file and makes it a regular file. + // Note that the work file is closed and fd() will return -1 after this. path() will still + // return the original path. + // This will return false when committing fails (=cannot rename). Both the regular file and tmp + // file will be deleted when it fails. + bool CommitWorkFile(); + + // Cancels the commit and restores the backup file into the regular one. If renaming fails, + // it will return false. This returns true if the backup file does not exist. + bool RestoreBackupFile(); + + // Removes the backup file. + void RemoveBackupFile(); + + // Gets UniqueFile with the same path and fd() pointing to the work file. + const UniqueFile& GetUniqueFile() const; + + // Creates writable RestorableFile. This involves creating tmp file for writing. + static RestorableFile CreateWritableFile(const std::string& path, int permissions); + + // Removes the specified file together with tmp file generated as RestorableFile. + static void RemoveAllFiles(const std::string& path); + +private: + RestorableFile(int value, const std::string& path); + + // Used as a storage for work file fd and path string. + UniqueFile unique_file_; +}; + +} // namespace installd +} // namespace android + +#endif // ANDROID_INSTALLD_RESTORABLE_FILE_H diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 70820172b7..51f7716d3a 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -13,7 +13,10 @@ cc_test { test_suites: ["device-tests"], clang: true, srcs: ["installd_utils_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "libutils", @@ -33,7 +36,10 @@ cc_test { test_suites: ["device-tests"], clang: true, srcs: ["installd_cache_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "libbinder", @@ -48,6 +54,7 @@ cc_test { "libasync_safe", "libdiskusage", "libinstalld", + "libziparchive", "liblog", "liblogwrap", ], @@ -74,7 +81,10 @@ cc_test { test_suites: ["device-tests"], clang: true, srcs: ["installd_service_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "libbinder", @@ -83,12 +93,14 @@ cc_test { "libprocessgroup", "libselinux", "libutils", + "packagemanager_aidl-cpp", "server_configurable_flags", ], static_libs: [ "libasync_safe", "libdiskusage", "libinstalld", + "libziparchive", "liblog", "liblogwrap", ], @@ -115,7 +127,10 @@ cc_test { test_suites: ["device-tests"], clang: true, srcs: ["installd_dexopt_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "libbinder", @@ -158,7 +173,10 @@ cc_test { test_suites: ["device-tests"], clang: true, srcs: ["installd_otapreopt_test.cpp"], - cflags: ["-Wall", "-Werror"], + cflags: [ + "-Wall", + "-Werror", + ], shared_libs: [ "libbase", "libcutils", @@ -167,6 +185,26 @@ cc_test { ], static_libs: [ "liblog", - "libotapreoptparameters" + "libotapreoptparameters", + ], +} + +cc_test { + name: "installd_file_test", + test_suites: ["device-tests"], + clang: true, + srcs: ["installd_file_test.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], + shared_libs: [ + "libbase", + "libcutils", + "libutils", + ], + static_libs: [ + "libinstalld", + "liblog", ], } diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp index 863cdfe55b..72f5f4b79c 100644 --- a/cmds/installd/tests/installd_cache_test.cpp +++ b/cmds/installd/tests/installd_cache_test.cpp @@ -122,6 +122,7 @@ protected: service = new InstalldNativeService(); testUuid = kTestUuid; + system("rm -rf /data/local/tmp/user"); system("mkdir -p /data/local/tmp/user/0"); } diff --git a/cmds/installd/tests/installd_file_test.cpp b/cmds/installd/tests/installd_file_test.cpp new file mode 100644 index 0000000000..00fb30875f --- /dev/null +++ b/cmds/installd/tests/installd_file_test.cpp @@ -0,0 +1,521 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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-base/logging.h> +#include <android-base/stringprintf.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <stdlib.h> +#include <string.h> + +#include "restorable_file.h" +#include "unique_file.h" +#include "utils.h" + +#undef LOG_TAG +#define LOG_TAG "installd_file_test" + +namespace { + +constexpr char kFileTestDir[] = "/data/local/tmp/installd_file_test_data"; +constexpr char kTmpFileSuffix[] = ".tmp"; +constexpr char kBackupFileSuffix[] = ".backup"; + +void UnlinkWithAssert(const std::string& path) { + ASSERT_EQ(0, unlink(path.c_str())); +} + +} // namespace + +namespace android { +namespace installd { + +// Add these as macros as functions make it hard to tell where the failure has happened. +#define ASSERT_FILE_NOT_EXISTING(path) \ + { \ + struct stat st; \ + ASSERT_NE(0, ::stat(path.c_str(), &st)); \ + } +#define ASSERT_FILE_EXISTING(path) \ + { \ + struct stat st; \ + ASSERT_EQ(0, ::stat(path.c_str(), &st)); \ + } +#define ASSERT_FILE_CONTENT(path, expectedContent) ASSERT_EQ(expectedContent, ReadTestFile(path)) +#define ASSERT_FILE_OPEN(path, fd) \ + { \ + fd = open(path.c_str(), O_RDWR); \ + ASSERT_TRUE(fd >= 0); \ + } +#define ASSERT_WRITE_TO_FD(fd, content) \ + ASSERT_TRUE(android::base::WriteStringToFd(content, android::base::borrowed_fd(fd))) + +class FileTest : public testing::Test { +protected: + virtual void SetUp() { + setenv("ANDROID_LOG_TAGS", "*:v", 1); + android::base::InitLogging(nullptr); + + ASSERT_EQ(0, create_dir_if_needed(kFileTestDir, 0777)); + } + + virtual void TearDown() { + system(android::base::StringPrintf("rm -rf %s", kFileTestDir).c_str()); + } + + std::string GetTestFilePath(const std::string& fileName) { + return android::base::StringPrintf("%s/%s", kFileTestDir, fileName.c_str()); + } + + void CreateTestFileWithContents(const std::string& path, const std::string& content) { + ALOGI("CreateTestFileWithContents:%s", path.c_str()); + ASSERT_TRUE(android::base::WriteStringToFile(content, path)); + } + + std::string GetTestName() { + std::string name(testing::UnitTest::GetInstance()->current_test_info()->name()); + return name; + } + + std::string ReadTestFile(const std::string& path) { + std::string content; + bool r = android::base::ReadFileToString(path, &content); + if (!r) { + PLOG(ERROR) << "Cannot read file:" << path; + } + return content; + } +}; + +TEST_F(FileTest, TestUniqueFileMoveConstruction) { + const int fd = 101; + std::string testFile = GetTestFilePath(GetTestName()); + UniqueFile uf1(fd, testFile); + uf1.DisableAutoClose(); + + UniqueFile uf2(std::move(uf1)); + + ASSERT_EQ(fd, uf2.fd()); + ASSERT_EQ(testFile, uf2.path()); +} + +TEST_F(FileTest, TestUniqueFileAssignment) { + const int fd1 = 101; + const int fd2 = 102; + std::string testFile1 = GetTestFilePath(GetTestName()); + std::string testFile2 = GetTestFilePath(GetTestName() + "2"); + + UniqueFile uf1(fd1, testFile1); + uf1.DisableAutoClose(); + + UniqueFile uf2(fd2, testFile2); + uf2.DisableAutoClose(); + + ASSERT_EQ(fd2, uf2.fd()); + ASSERT_EQ(testFile2, uf2.path()); + + uf2 = std::move(uf1); + + ASSERT_EQ(fd1, uf2.fd()); + ASSERT_EQ(testFile1, uf2.path()); +} + +TEST_F(FileTest, TestUniqueFileCleanup) { + std::string testFile = GetTestFilePath(GetTestName()); + CreateTestFileWithContents(testFile, "OriginalContent"); + + int fd; + ASSERT_FILE_OPEN(testFile, fd); + + { UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert); } + + ASSERT_FILE_NOT_EXISTING(testFile); +} + +TEST_F(FileTest, TestUniqueFileNoCleanup) { + std::string testFile = GetTestFilePath(GetTestName()); + CreateTestFileWithContents(testFile, "OriginalContent"); + + int fd; + ASSERT_FILE_OPEN(testFile, fd); + + { + UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert); + uf.DisableCleanup(); + } + + ASSERT_FILE_CONTENT(testFile, "OriginalContent"); +} + +TEST_F(FileTest, TestUniqueFileFd) { + std::string testFile = GetTestFilePath(GetTestName()); + CreateTestFileWithContents(testFile, "OriginalContent"); + + int fd; + ASSERT_FILE_OPEN(testFile, fd); + + UniqueFile uf(fd, testFile, UnlinkWithAssert); + + ASSERT_EQ(fd, uf.fd()); + + uf.reset(); + + ASSERT_EQ(-1, uf.fd()); +} + +TEST_F(FileTest, TestRestorableFileMoveConstruction) { + std::string testFile = GetTestFilePath(GetTestName()); + + RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile, 0600); + int fd = rf1.fd(); + + RestorableFile rf2(std::move(rf1)); + + ASSERT_EQ(fd, rf2.fd()); + ASSERT_EQ(testFile, rf2.path()); +} + +TEST_F(FileTest, TestRestorableFileAssignment) { + std::string testFile1 = GetTestFilePath(GetTestName()); + std::string testFile2 = GetTestFilePath(GetTestName() + "2"); + + RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile1, 0600); + int fd1 = rf1.fd(); + + RestorableFile rf2 = RestorableFile::CreateWritableFile(testFile2, 0600); + int fd2 = rf2.fd(); + + ASSERT_EQ(fd2, rf2.fd()); + ASSERT_EQ(testFile2, rf2.path()); + + rf2 = std::move(rf1); + + ASSERT_EQ(fd1, rf2.fd()); + ASSERT_EQ(testFile1, rf2.path()); +} + +TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithReset) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + + ASSERT_FILE_EXISTING(tmpFile); + + const UniqueFile& uf = rf.GetUniqueFile(); + + ASSERT_EQ(rf.fd(), uf.fd()); + ASSERT_EQ(rf.path(), uf.path()); + + rf.reset(); + + ASSERT_EQ(rf.fd(), uf.fd()); + ASSERT_EQ(rf.path(), uf.path()); + ASSERT_EQ(-1, rf.fd()); + ASSERT_TRUE(rf.path().empty()); + } +} + +TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithCommit) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + + ASSERT_FILE_EXISTING(tmpFile); + + const UniqueFile& uf = rf.GetUniqueFile(); + + ASSERT_EQ(rf.fd(), uf.fd()); + ASSERT_EQ(rf.path(), uf.path()); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(backupFile); + + rf.CommitWorkFile(); + + ASSERT_EQ(rf.fd(), uf.fd()); + ASSERT_EQ(rf.path(), uf.path()); + ASSERT_EQ(-1, rf.fd()); + ASSERT_EQ(testFile, rf.path()); + } +} + +TEST_F(FileTest, TestRestorableFileNewFileNotCommitted) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + + ASSERT_FILE_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); +} + +TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginal) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + ASSERT_FILE_EXISTING(testFile); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_CONTENT(testFile, "OriginalContent"); +} + +TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginalAndOldTmp) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + ASSERT_FILE_EXISTING(testFile); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_CONTENT(testFile, "OriginalContent"); +} + +TEST_F(FileTest, TestRestorableFileNewFileCommitted) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + + ASSERT_FILE_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(backupFile); + + ASSERT_TRUE(rf.CommitWorkFile()); + rf.RemoveBackupFile(); + + ASSERT_FILE_CONTENT(testFile, "NewContent"); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(backupFile); + ASSERT_FILE_CONTENT(testFile, "NewContent"); +} + +TEST_F(FileTest, TestRestorableFileCommittedWithOriginal) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_EXISTING(backupFile); + + ASSERT_TRUE(rf.CommitWorkFile()); + + ASSERT_FILE_EXISTING(backupFile); + ASSERT_FILE_CONTENT(testFile, "NewContent"); + + rf.RemoveBackupFile(); + + ASSERT_FILE_NOT_EXISTING(backupFile); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_CONTENT(testFile, "NewContent"); +} + +TEST_F(FileTest, TestRestorableFileCommittedWithOriginalAndOldTmp) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + ASSERT_FILE_CONTENT(tmpFile, "NewContent"); + + ASSERT_TRUE(rf.CommitWorkFile()); + + ASSERT_FILE_CONTENT(testFile, "NewContent"); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_CONTENT(testFile, "NewContent"); +} + +TEST_F(FileTest, TestRestorableFileCommitFailureNoOriginal) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(backupFile); + + // Now remove tmp file to force commit failure. + close(rf.fd()); + ASSERT_EQ(0, unlink(tmpFile.c_str())); + ASSERT_FILE_NOT_EXISTING(tmpFile); + + ASSERT_FALSE(rf.CommitWorkFile()); + + ASSERT_EQ(-1, rf.fd()); + ASSERT_EQ(testFile, rf.path()); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(tmpFile); + + ASSERT_TRUE(rf.RestoreBackupFile()); + } + + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(backupFile); +} + +TEST_F(FileTest, TestRestorableFileCommitFailureAndRollback) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_EXISTING(backupFile); + + // Now remove tmp file to force commit failure. + close(rf.fd()); + ASSERT_EQ(0, unlink(tmpFile.c_str())); + ASSERT_FILE_NOT_EXISTING(tmpFile); + + ASSERT_FALSE(rf.CommitWorkFile()); + + ASSERT_EQ(-1, rf.fd()); + ASSERT_EQ(testFile, rf.path()); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_EXISTING(backupFile); + + ASSERT_TRUE(rf.RestoreBackupFile()); + } + + ASSERT_FILE_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(backupFile); +} + +TEST_F(FileTest, TestRestorableFileResetAndRemoveAllFiles) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + + { + RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600); + ASSERT_WRITE_TO_FD(rf.fd(), "NewContent"); + + ASSERT_TRUE(rf.CreateBackupFile()); + + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_EXISTING(backupFile); + + rf.ResetAndRemoveAllFiles(); + + ASSERT_EQ(-1, rf.fd()); + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(backupFile); + } + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(backupFile); +} + +TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentFile) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + + RestorableFile::RemoveAllFiles(testFile); + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(backupFile); +} + +TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentAndTmpFile) { + std::string testFile = GetTestFilePath(GetTestName()); + std::string tmpFile = testFile + kTmpFileSuffix; + std::string backupFile = testFile + kBackupFileSuffix; + CreateTestFileWithContents(testFile, "OriginalContent"); + CreateTestFileWithContents(testFile + kTmpFileSuffix, "TmpContent"); + + RestorableFile::RemoveAllFiles(testFile); + + ASSERT_FILE_NOT_EXISTING(tmpFile); + ASSERT_FILE_NOT_EXISTING(testFile); + ASSERT_FILE_NOT_EXISTING(backupFile); +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/tests/installd_file_test.xml b/cmds/installd/tests/installd_file_test.xml new file mode 100644 index 0000000000..5ec6e3f019 --- /dev/null +++ b/cmds/installd/tests/installd_file_test.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- Note: this is derived from the autogenerated configuration. We require + root support. --> +<configuration description="Runs installd_file_test."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" + value="installd_file_test->/data/local/tmp/installd_file_test" /> + </target_preparer> + + <!-- The test requires root for file access (rollback. --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="installd_file_test" /> + </test> +</configuration> diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 1e7559d174..b831515b94 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -18,10 +18,11 @@ #include <string> #include <fcntl.h> +#include <pwd.h> #include <stdlib.h> #include <string.h> -#include <sys/statvfs.h> #include <sys/stat.h> +#include <sys/statvfs.h> #include <sys/xattr.h> #include <android-base/file.h> @@ -32,8 +33,10 @@ #include <cutils/properties.h> #include <gtest/gtest.h> -#include "binder_test_utils.h" +#include <android/content/pm/IPackageManagerNative.h> +#include <binder/IServiceManager.h> #include "InstalldNativeService.h" +#include "binder_test_utils.h" #include "dexopt.h" #include "globals.h" #include "utils.h" @@ -41,6 +44,34 @@ using android::base::StringPrintf; namespace android { +std::string get_package_name(uid_t uid) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<content::pm::IPackageManagerNative> package_mgr; + if (sm.get() == nullptr) { + LOG(INFO) << "Cannot find service manager"; + } else { + sp<IBinder> binder = sm->getService(String16("package_native")); + if (binder.get() == nullptr) { + LOG(INFO) << "Cannot find package_native"; + } else { + package_mgr = interface_cast<content::pm::IPackageManagerNative>(binder); + } + } + // find package name + std::string pkg; + if (package_mgr != nullptr) { + std::vector<std::string> names; + binder::Status status = package_mgr->getNamesForUids({(int)uid}, &names); + if (!status.isOk()) { + LOG(INFO) << "getNamesForUids failed: %s", status.exceptionMessage().c_str(); + } else { + if (!names[0].empty()) { + pkg = names[0].c_str(); + } + } + } + return pkg; +} namespace installd { constexpr const char* kTestUuid = "TEST"; @@ -107,6 +138,7 @@ protected: service = new InstalldNativeService(); testUuid = kTestUuid; + system("rm -rf /data/local/tmp/user"); system("mkdir -p /data/local/tmp/user/0"); init_globals_from_data_and_root(); @@ -247,7 +279,50 @@ TEST_F(ServiceTest, CalculateCache) { EXPECT_TRUE(create_cache_path(buf, "/path/to/file.apk", "isa")); EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf)); } - +TEST_F(ServiceTest, GetAppSize) { + struct stat s; + + std::string externalPicDir = + StringPrintf("%s/Pictures", create_data_media_path(nullptr, 0).c_str()); + if (stat(externalPicDir.c_str(), &s) == 0) { + // fetch the appId from the uid of the external storage owning app + int32_t externalStorageAppId = multiuser_get_app_id(s.st_uid); + // Fetch Package Name for the external storage owning app uid + std::string pkg = get_package_name(s.st_uid); + + std::vector<int64_t> externalStorageSize, externalStorageSizeAfterAddingExternalFile; + std::vector<int64_t> ceDataInodes; + + std::vector<std::string> codePaths; + std::vector<std::string> packageNames; + // set up parameters + packageNames.push_back(pkg); + ceDataInodes.push_back(0); + // initialise the mounts + service->invalidateMounts(); + // call the getAppSize to get the current size of the external storage owning app + service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, + externalStorageAppId, ceDataInodes, codePaths, &externalStorageSize); + // add a file with 20MB size to the external storage + std::string externalFileLocation = + StringPrintf("%s/Pictures/%s", getenv("EXTERNAL_STORAGE"), "External.jpg"); + std::string externalFileContentCommand = + StringPrintf("dd if=/dev/zero of=%s bs=1M count=20", externalFileLocation.c_str()); + system(externalFileContentCommand.c_str()); + // call the getAppSize again to get the new size of the external storage owning app + service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, + externalStorageAppId, ceDataInodes, codePaths, + &externalStorageSizeAfterAddingExternalFile); + // check that the size before adding the file and after should be the same, as the app size + // is not changed. + for (size_t i = 0; i < externalStorageSize.size(); i++) { + ASSERT_TRUE(externalStorageSize[i] == externalStorageSizeAfterAddingExternalFile[i]); + } + // remove the external file + std::string removeCommand = StringPrintf("rm -f %s", externalFileLocation.c_str()); + system(removeCommand.c_str()); + } +} static bool mkdirs(const std::string& path, mode_t mode) { struct stat sb; if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) { diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index 3ebdeee7aa..80c0548fca 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -47,6 +47,15 @@ cc_binary { } cc_binary { + name: "servicemanager.recovery", + stem: "servicemanager", + recovery: true, + defaults: ["servicemanager_defaults"], + init_rc: ["servicemanager.recovery.rc"], + srcs: ["main.cpp"], +} + +cc_binary { name: "vndservicemanager", defaults: ["servicemanager_defaults"], init_rc: ["vndservicemanager.rc"], diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 4e44ac7323..4374abe2ef 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -28,6 +28,9 @@ #ifndef VENDORSERVICEMANAGER #include <vintf/VintfObject.h> +#ifdef __ANDROID_RECOVERY__ +#include <vintf/VintfObjectRecovery.h> +#endif // __ANDROID_RECOVERY__ #include <vintf/constants.h> #endif // !VENDORSERVICEMANAGER @@ -37,16 +40,33 @@ using ::android::internal::Stability; namespace android { #ifndef VENDORSERVICEMANAGER + struct ManifestWithDescription { std::shared_ptr<const vintf::HalManifest> manifest; const char* description; }; +static std::vector<ManifestWithDescription> GetManifestsWithDescription() { +#ifdef __ANDROID_RECOVERY__ + auto vintfObject = vintf::VintfObjectRecovery::GetInstance(); + if (vintfObject == nullptr) { + LOG(ERROR) << "NULL VintfObjectRecovery!"; + return {}; + } + return {ManifestWithDescription{vintfObject->getRecoveryHalManifest(), "recovery"}}; +#else + auto vintfObject = vintf::VintfObject::GetInstance(); + if (vintfObject == nullptr) { + LOG(ERROR) << "NULL VintfObject!"; + return {}; + } + return {ManifestWithDescription{vintfObject->getDeviceHalManifest(), "device"}, + ManifestWithDescription{vintfObject->getFrameworkHalManifest(), "framework"}}; +#endif +} + // func true -> stop search and forEachManifest will return true static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) { - for (const ManifestWithDescription& mwd : { - ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" }, - ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" }, - }) { + for (const ManifestWithDescription& mwd : GetManifestsWithDescription()) { if (mwd.manifest == nullptr) { LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description; // note, we explicitly do not retry here, so that we can detect VINTF diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp index 8c1beaca20..2fb9c2bc9a 100644 --- a/cmds/servicemanager/main.cpp +++ b/cmds/servicemanager/main.cpp @@ -111,6 +111,10 @@ private: }; int main(int argc, char** argv) { +#ifdef __ANDROID_RECOVERY__ + android::base::InitLogging(argv, android::base::KernelLogger); +#endif + if (argc > 2) { LOG(FATAL) << "usage: " << argv[0] << " [binder driver]"; } diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc index 0dd29e05ba..e5d689ff91 100644 --- a/cmds/servicemanager/servicemanager.rc +++ b/cmds/servicemanager/servicemanager.rc @@ -6,8 +6,8 @@ service servicemanager /system/bin/servicemanager onrestart restart apexd onrestart restart audioserver onrestart restart gatekeeperd - onrestart class_restart main - onrestart class_restart hal - onrestart class_restart early_hal + onrestart class_restart --only-enabled main + onrestart class_restart --only-enabled hal + onrestart class_restart --only-enabled early_hal task_profiles ServiceCapacityLow shutdown critical diff --git a/cmds/servicemanager/servicemanager.recovery.rc b/cmds/servicemanager/servicemanager.recovery.rc new file mode 100644 index 0000000000..067faf9c8f --- /dev/null +++ b/cmds/servicemanager/servicemanager.recovery.rc @@ -0,0 +1,4 @@ +service servicemanager /system/bin/servicemanager + disabled + group system readproc + seclabel u:r:servicemanager:s0 diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 5fe4ea1787..931c5e3f2a 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -89,6 +89,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.fingerprint.prebuilt.xml", + src: "android.hardware.fingerprint.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.location.gps.prebuilt.xml", src: "android.hardware.location.gps.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/include/android/input.h b/include/android/input.h index bb98beb41a..76422154f1 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -947,9 +947,10 @@ int32_t AInputEvent_getSource(const AInputEvent* event); * and {@link AMotionEvent_fromJava()}. * After returning, the specified AInputEvent* object becomes invalid and should no longer be used. * The underlying Java object remains valid and does not change its state. + * + * Available since API level 31. */ - -void AInputEvent_release(const AInputEvent* event); +void AInputEvent_release(const AInputEvent* event) __INTRODUCED_IN(31); /*** Accessors for key events only. ***/ @@ -1001,8 +1002,10 @@ int64_t AKeyEvent_getEventTime(const AInputEvent* key_event); * Creates a native AInputEvent* object that is a copy of the specified Java android.view.KeyEvent. * The result may be used with generic and KeyEvent-specific AInputEvent_* functions. The object * returned by this function must be disposed using {@link AInputEvent_release()}. + * + * Available since API level 31. */ -const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent); +const AInputEvent* AKeyEvent_fromJava(JNIEnv* env, jobject keyEvent) __INTRODUCED_IN(31); /*** Accessors for motion events only. ***/ @@ -1324,8 +1327,10 @@ float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, * android.view.MotionEvent. The result may be used with generic and MotionEvent-specific * AInputEvent_* functions. The object returned by this function must be disposed using * {@link AInputEvent_release()}. + * + * Available since API level 31. */ -const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent); +const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent) __INTRODUCED_IN(31); struct AInputQueue; /** diff --git a/include/binder/Enum.h b/include/binder/Enum.h deleted file mode 100644 index 4c256546f6..0000000000 --- a/include/binder/Enum.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * 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 - -#error Do not rely on global include files. All Android cc_* programs are given access to \ - include_dirs for frameworks/native/include via global configuration, but this is legacy \ - configuration. Instead, you should have a direct dependency on libbinder OR one of your \ - dependencies should re-export libbinder headers with export_shared_lib_headers. diff --git a/include/input/Input.h b/include/input/Input.h index 2e326cb102..dce6ccb3d5 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -1015,6 +1015,25 @@ private: std::queue<std::unique_ptr<DragEvent>> mDragEventPool; }; +/* + * Describes a unique request to enable or disable Pointer Capture. + */ +struct PointerCaptureRequest { +public: + inline PointerCaptureRequest() : enable(false), seq(0) {} + inline PointerCaptureRequest(bool enable, uint32_t seq) : enable(enable), seq(seq) {} + inline bool operator==(const PointerCaptureRequest& other) const { + return enable == other.enable && seq == other.seq; + } + explicit inline operator bool() const { return enable; } + + // True iff this is a request to enable Pointer Capture. + bool enable; + + // The sequence number for the request. + uint32_t seq; +}; + } // namespace android #endif // _LIBINPUT_INPUT_H diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 8270ae5ce7..d8101fad4c 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -202,7 +202,7 @@ cc_library { sanitize: { misc_undefined: ["integer"], }, - min_sdk_version: "29", + min_sdk_version: "30", tidy: true, tidy_flags: [ diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 9e04ffeb11..55d3d70bd4 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -1410,23 +1410,6 @@ void IPCThreadState::threadDestructor(void *st) } } -status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, bool *sync_received, bool *async_received) -{ - int ret = 0; - binder_frozen_status_info info; - info.pid = pid; - -#if defined(__ANDROID__) - if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0) - ret = -errno; -#endif - *sync_received = info.sync_recv; - *async_received = info.async_recv; - - return ret; -} - -#ifndef __ANDROID_VNDK__ status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received, uint32_t *async_received) { @@ -1443,7 +1426,6 @@ status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received return ret; } -#endif status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) { struct binder_freeze_info info; diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 8f4f0f0016..7027a4b0a3 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -824,7 +824,7 @@ void* Parcel::writeInplace(size_t len) const size_t padded = pad_size(len); - // sanity check for integer overflow + // check for integer overflow if (mDataPos+padded < mDataPos) { return nullptr; } diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 065e6e3b1d..82bebc95f4 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -51,17 +51,11 @@ public: static status_t freeze(pid_t pid, bool enabled, uint32_t timeout_ms); // Provide information about the state of a frozen process - static status_t getProcessFreezeInfo(pid_t pid, bool *sync_received, - bool *async_received); - - // TODO: Remove the above legacy duplicated function in next version -#ifndef __ANDROID_VNDK__ static status_t getProcessFreezeInfo(pid_t pid, uint32_t *sync_received, uint32_t *async_received); -#endif sp<ProcessState> process(); - + status_t clearLastError(); /** diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 8fb4a377d6..8dbdc1d115 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -16,6 +16,7 @@ #pragma once +#include <array> #include <map> // for legacy reasons #include <string> #include <type_traits> @@ -207,6 +208,32 @@ public: status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) __attribute__((deprecated("use std::optional version instead"))); status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val); + // Write an IInterface or a vector of IInterface's + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t writeStrongBinder(const sp<T>& val) { + return writeStrongBinder(T::asBinder(val)); + } + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t writeStrongBinderVector(const std::vector<sp<T>>& val) { + return writeData(val); + } + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t writeStrongBinderVector(const std::optional<std::vector<sp<T>>>& val) { + return writeData(val); + } + + template <typename T, size_t N> + status_t writeFixedArray(const std::array<T, N>& val) { + return writeData(val); + } + template <typename T, size_t N> + status_t writeFixedArray(const std::optional<std::array<T, N>>& val) { + return writeData(val); + } + // Write an Enum vector with underlying type int8_t. // Does not use padding; each byte is contiguous. template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> @@ -421,6 +448,16 @@ public: status_t readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const; status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const __attribute__((deprecated("use std::optional version instead"))); status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const; + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t readStrongBinderVector(std::vector<sp<T>>* val) const { + return readData(val); + } + template <typename T, + std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true> + status_t readStrongBinderVector(std::optional<std::vector<sp<T>>>* val) const { + return readData(val); + } status_t readByteVector(std::optional<std::vector<int8_t>>* val) const; status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const __attribute__((deprecated("use std::optional version instead"))); @@ -460,6 +497,15 @@ public: std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const __attribute__((deprecated("use std::optional version instead"))); status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const; + template <typename T, size_t N> + status_t readFixedArray(std::array<T, N>* val) const { + return readData(val); + } + template <typename T, size_t N> + status_t readFixedArray(std::optional<std::array<T, N>>* val) const { + return readData(val); + } + template<typename T> status_t read(Flattenable<T>& val) const; @@ -791,6 +837,16 @@ private: || is_specialization_v<T, std::unique_ptr> || is_specialization_v<T, std::shared_ptr>; + // Tells if T is a fixed-size array. + template <typename T> + struct is_fixed_array : std::false_type {}; + + template <typename T, size_t N> + struct is_fixed_array<std::array<T, N>> : std::true_type {}; + + template <typename T> + static inline constexpr bool is_fixed_array_v = is_fixed_array<T>::value; + // special int32 value to indicate NonNull or Null parcelables // This is fixed to be only 0 or 1 by contract, do not change. static constexpr int32_t kNonNullParcelableFlag = 1; @@ -895,7 +951,9 @@ private: if (!c) return writeData(static_cast<int32_t>(kNullVectorSize)); } else if constexpr (std::is_base_of_v<Parcelable, T>) { if (!c) return writeData(static_cast<int32_t>(kNullParcelableFlag)); - } else /* constexpr */ { // could define this, but raise as error. + } else if constexpr (is_fixed_array_v<T>) { + if (!c) return writeData(static_cast<int32_t>(kNullVectorSize)); + } else /* constexpr */ { // could define this, but raise as error. static_assert(dependent_false_v<CT>); } return writeData(*c); @@ -934,6 +992,23 @@ private: return OK; } + template <typename T, size_t N> + status_t writeData(const std::array<T, N>& val) { + static_assert(N <= std::numeric_limits<int32_t>::max()); + status_t status = writeData(static_cast<int32_t>(N)); + if (status != OK) return status; + if constexpr (is_pointer_equivalent_array_v<T>) { + static_assert(N <= std::numeric_limits<size_t>::max() / sizeof(T)); + return write(val.data(), val.size() * sizeof(T)); + } else /* constexpr */ { + for (const auto& t : val) { + status = writeData(t); + if (status != OK) return status; + } + return OK; + } + } + // readData function overloads. // Implementation detail: Function overloading improves code readability over // template overloading, but prevents readData<T> from being used for those types. @@ -1026,9 +1101,8 @@ private: int32_t peek; status_t status = readData(&peek); if (status != OK) return status; - if constexpr (is_specialization_v<T, std::vector> - || std::is_same_v<T, String16> - || std::is_same_v<T, std::string>) { + if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T> || + std::is_same_v<T, String16> || std::is_same_v<T, std::string>) { if (peek == kNullVectorSize) { c->reset(); return OK; @@ -1038,12 +1112,15 @@ private: c->reset(); return OK; } - } else /* constexpr */ { // could define this, but raise as error. + } else /* constexpr */ { // could define this, but raise as error. static_assert(dependent_false_v<CT>); } // create a new object. if constexpr (is_specialization_v<CT, std::optional>) { - c->emplace(); + // Call default constructor explicitly + // - Clang bug: https://bugs.llvm.org/show_bug.cgi?id=35748 + // std::optional::emplace() doesn't work with nested types. + c->emplace(T()); } else /* constexpr */ { T* const t = new (std::nothrow) T; // contents read from Parcel below. if (t == nullptr) return NO_MEMORY; @@ -1052,7 +1129,7 @@ private: // rewind data ptr to reread (this is pretty quick), otherwise we could // pass an optional argument to readData to indicate a peeked value. setDataPosition(startPos); - if constexpr (is_specialization_v<T, std::vector>) { + if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T>) { return readData(&**c, READ_FLAG_SP_NULLABLE); // nullable sp<> allowed now } else { return readData(&**c); @@ -1115,6 +1192,41 @@ private: return OK; } + template <typename T, size_t N> + status_t readData(std::array<T, N>* val, ReadFlags readFlags = READ_FLAG_NONE) const { + static_assert(N <= std::numeric_limits<int32_t>::max()); + int32_t size; + status_t status = readInt32(&size); + if (status != OK) return status; + if (size < 0) return UNEXPECTED_NULL; + if (size != static_cast<int32_t>(N)) return BAD_VALUE; + if constexpr (is_pointer_equivalent_array_v<T>) { + auto data = reinterpret_cast<const T*>(readInplace(N * sizeof(T))); + if (data == nullptr) return BAD_VALUE; + memcpy(val->data(), data, N * sizeof(T)); + } else if constexpr (is_specialization_v<T, sp>) { + for (auto& t : *val) { + if (readFlags & READ_FLAG_SP_NULLABLE) { + status = readNullableStrongBinder(&t); // allow nullable + } else { + status = readStrongBinder(&t); + } + if (status != OK) return status; + } + } else if constexpr (is_fixed_array_v<T>) { // pass readFlags down to nested arrays + for (auto& t : *val) { + status = readData(&t, readFlags); + if (status != OK) return status; + } + } else /* constexpr */ { + for (auto& t : *val) { + status = readData(&t); + if (status != OK) return status; + } + } + return OK; + } + //----------------------------------------------------------------------------- private: diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index 34f1cbf33a..5baa4d7ad4 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -32,9 +32,11 @@ bool RunRpcServer(AIBinder* service, unsigned int port); bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param), void* param); -// Starts an RPC server on a given port and a given root IBinder object. -// This function sets up the server, calls readyCallback with a given param, and -// then joins before returning. +// Starts an RPC server on a given port and a given root IBinder factory. +// RunRpcServerWithFactory acts like RunRpcServerCallback, but instead of +// assigning single root IBinder object to all connections, factory is called +// whenever a client connects, making it possible to assign unique IBinder +// object to each client. bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context), void* factoryContext, unsigned int port); diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index ee46fcb884..42895740ea 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -38,7 +38,7 @@ cc_defaults { host: { cflags: [ "-D__INTRODUCED_IN(n)=", - "-D__assert(a,b,c)=", + "-D__assert(a,b,c)=do { syslog(LOG_ERR, a \": \" c); abort(); } while(false)", // We want all the APIs to be available on the host. "-D__ANDROID_API__=10000", ], diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 8ffa735700..6949c2c9e5 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -104,6 +104,17 @@ std::optional<bool> AIBinder::associateClassInternal(const AIBinder_Class* clazz return {}; } +// b/175635923 libcxx causes "implicit-conversion" with a string with invalid char +static std::string SanitizeString(const String16& str) { + std::string sanitized{String8(str)}; + for (auto& c : sanitized) { + if (!isprint(c)) { + c = '?'; + } + } + return sanitized; +} + bool AIBinder::associateClass(const AIBinder_Class* clazz) { if (clazz == nullptr) return false; @@ -118,7 +129,7 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { if (descriptor != newDescriptor) { if (getBinder()->isBinderAlive()) { LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor - << "' but descriptor is actually '" << descriptor << "'."; + << "' but descriptor is actually '" << SanitizeString(descriptor) << "'."; } else { // b/155793159 LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h index 0ad400bffb..c903998021 100644 --- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h @@ -365,6 +365,8 @@ class ScopedFileDescriptor : public impl::ScopedAResource<int, internal::closeWi ScopedFileDescriptor(ScopedFileDescriptor&&) = default; ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default; + ScopedFileDescriptor dup() const { return ScopedFileDescriptor(::dup(get())); } + bool operator!=(const ScopedFileDescriptor& rhs) const { return get() != rhs.get(); } bool operator<(const ScopedFileDescriptor& rhs) const { return get() < rhs.get(); } bool operator<=(const ScopedFileDescriptor& rhs) const { return get() <= rhs.get(); } diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h index 5de64f8c5c..09411e76a3 100644 --- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h @@ -190,9 +190,9 @@ class BnCInterface : public INTERFACE { BnCInterface() {} virtual ~BnCInterface() {} - SpAIBinder asBinder() override; + SpAIBinder asBinder() override final; - bool isRemote() override { return false; } + bool isRemote() override final { return false; } protected: /** @@ -215,9 +215,9 @@ class BpCInterface : public INTERFACE { explicit BpCInterface(const SpAIBinder& binder) : mBinder(binder) {} virtual ~BpCInterface() {} - SpAIBinder asBinder() override; + SpAIBinder asBinder() override final; - bool isRemote() override { return AIBinder_isRemote(mBinder.get()); } + bool isRemote() override final { return AIBinder_isRemote(mBinder.get()); } binder_status_t dump(int fd, const char** args, uint32_t numArgs) override { return AIBinder_dump(asBinder().get(), fd, args, numArgs); diff --git a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h index 2b18a0a1e0..0bf1e3dba9 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h @@ -27,15 +27,146 @@ #pragma once #include <android/binder_auto_utils.h> +#include <android/binder_interface_utils.h> #include <android/binder_internal_logging.h> #include <android/binder_parcel.h> +#include <array> #include <optional> #include <string> +#include <type_traits> #include <vector> namespace ndk { +namespace { +template <typename Test, template <typename...> class Ref> +struct is_specialization : std::false_type {}; + +template <template <typename...> class Ref, typename... Args> +struct is_specialization<Ref<Args...>, Ref> : std::true_type {}; + +template <typename Test, template <typename...> class Ref> +static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value; + +// Get the first template type from a container, the T from MyClass<T, ...>. +template <typename T> +struct first_template_type { + using type = void; +}; + +template <template <typename...> class V, typename T, typename... Args> +struct first_template_type<V<T, Args...>> { + using type = T; +}; + +template <typename T> +using first_template_type_t = typename first_template_type<T>::type; + +// Tells if T represents NDK interface (shared_ptr<ICInterface-derived>) +template <typename T> +static inline constexpr bool is_interface_v = is_specialization_v<T, std::shared_ptr>&& + std::is_base_of_v<::ndk::ICInterface, first_template_type_t<T>>; + +// Tells if T represents NDK parcelable with readFromParcel/writeToParcel methods defined +template <typename T, typename = void> +struct is_parcelable : std::false_type {}; + +template <typename T> +struct is_parcelable< + T, std::void_t<decltype(std::declval<T>().readFromParcel(std::declval<const AParcel*>())), + decltype(std::declval<T>().writeToParcel(std::declval<AParcel*>()))>> + : std::true_type {}; + +template <typename T> +static inline constexpr bool is_parcelable_v = is_parcelable<T>::value; + +// Tells if T represents nullable NDK parcelable (optional<parcelable> or unique_ptr<parcelable>) +template <typename T> +static inline constexpr bool is_nullable_parcelable_v = is_parcelable_v<first_template_type_t<T>> && + (is_specialization_v<T, std::optional> || + is_specialization_v<T, std::unique_ptr>); + +// Tells if T is a fixed-size array. +template <typename T> +struct is_fixed_array : std::false_type {}; + +template <typename T, size_t N> +struct is_fixed_array<std::array<T, N>> : std::true_type {}; + +template <typename T> +static inline constexpr bool is_fixed_array_v = is_fixed_array<T>::value; + +template <typename T> +static inline constexpr bool dependent_false_v = false; +} // namespace + +/** + * This checks the length against the array size and retrieves the buffer. No allocation required. + */ +template <typename T, size_t N> +static inline bool AParcel_stdArrayAllocator(void* arrayData, int32_t length, T** outBuffer) { + if (length < 0) return false; + + if (length != static_cast<int32_t>(N)) { + return false; + } + + std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData); + *outBuffer = arr->data(); + return true; +} + +/** + * This checks the length against the array size and retrieves the buffer. No allocation required. + */ +template <typename T, size_t N> +static inline bool AParcel_nullableStdArrayAllocator(void* arrayData, int32_t length, + T** outBuffer) { + std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData); + if (length < 0) { + *arr = std::nullopt; + return true; + } + + if (length != static_cast<int32_t>(N)) { + return false; + } + + arr->emplace(); + *outBuffer = (*arr)->data(); + return true; +} + +/** + * This checks the length against the array size. No allocation required. + */ +template <size_t N> +static inline bool AParcel_stdArrayExternalAllocator(void* arrayData, int32_t length) { + (void)arrayData; + return length == static_cast<int32_t>(N); +} + +/** + * This checks the length against the array size. No allocation required. + */ +template <typename T, size_t N> +static inline bool AParcel_nullableStdArrayExternalAllocator(void* arrayData, int32_t length) { + std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData); + + if (length < 0) { + *arr = std::nullopt; + return true; + } + + if (length != static_cast<int32_t>(N)) { + return false; + } + + arr->emplace(); + return true; +} + /** * This retrieves and allocates a vector to size 'length' and returns the underlying buffer. */ @@ -345,6 +476,118 @@ static inline const char* AParcel_nullableStdVectorStringElementGetter(const voi } /** + * This retrieves the underlying value in a std::array which may not be contiguous at index from a + * corresponding arrData. + */ +template <typename T, size_t N> +static inline T AParcel_stdArrayGetter(const void* arrData, size_t index) { + const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrData); + return (*arr)[index]; +} + +/** + * This sets the underlying value in a corresponding arrData which may not be contiguous at + * index. + */ +template <typename T, size_t N> +static inline void AParcel_stdArraySetter(void* arrData, size_t index, T value) { + std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrData); + (*arr)[index] = value; +} + +/** + * This retrieves the underlying value in a std::array which may not be contiguous at index from a + * corresponding arrData. + */ +template <typename T, size_t N> +static inline T AParcel_nullableStdArrayGetter(const void* arrData, size_t index) { + const std::optional<std::array<T, N>>* arr = + static_cast<const std::optional<std::array<T, N>>*>(arrData); + return (*arr)[index]; +} + +/** + * This sets the underlying value in a corresponding arrData which may not be contiguous at + * index. + */ +template <typename T, size_t N> +static inline void AParcel_nullableStdArraySetter(void* arrData, size_t index, T value) { + std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrData); + (*arr)->at(index) = value; +} + +/** + * Allocates a std::string inside of std::array<std::string, N> at index 'index' to size 'length'. + */ +template <size_t N> +static inline bool AParcel_stdArrayStringElementAllocator(void* arrData, size_t index, + int32_t length, char** buffer) { + std::array<std::string, N>* arr = static_cast<std::array<std::string, N>*>(arrData); + std::string& element = arr->at(index); + return AParcel_stdStringAllocator(static_cast<void*>(&element), length, buffer); +} + +/** + * This gets the length and buffer of a std::string inside of a std::array<std::string, N> at index + * 'index'. + */ +template <size_t N> +static const char* AParcel_stdArrayStringElementGetter(const void* arrData, size_t index, + int32_t* outLength) { + const std::array<std::string, N>* arr = static_cast<const std::array<std::string, N>*>(arrData); + const std::string& element = arr->at(index); + + *outLength = static_cast<int32_t>(element.size()); + return element.c_str(); +} + +/** + * Allocates a std::string inside of std::array<std::optional<std::string>, N> at index 'index' to + * size 'length'. + */ +template <size_t N> +static inline bool AParcel_stdArrayNullableStringElementAllocator(void* arrData, size_t index, + int32_t length, char** buffer) { + std::array<std::optional<std::string>, N>* arr = + static_cast<std::array<std::optional<std::string>, N>*>(arrData); + std::optional<std::string>& element = arr->at(index); + return AParcel_nullableStdStringAllocator(static_cast<void*>(&element), length, buffer); +} + +/** + * This gets the length and buffer of a std::string inside of a + * std::array<std::optional<std::string>, N> at index 'index'. + */ +template <size_t N> +static const char* AParcel_stdArrayNullableStringElementGetter(const void* arrData, size_t index, + int32_t* outLength) { + const std::array<std::optional<std::string>, N>* arr = + static_cast<const std::array<std::optional<std::string>, N>*>(arrData); + const std::optional<std::string>& element = arr->at(index); + + if (!element) { + *outLength = -1; + return nullptr; + } + + *outLength = static_cast<int32_t>(element->size()); + return element->c_str(); +} + +/** + * Allocates a std::string inside of std::optional<std::array<std::optional<std::string>, N>> at + * index 'index' to size 'length'. + */ +template <size_t N> +static inline bool AParcel_nullableStdArrayStringElementAllocator(void* arrData, size_t index, + int32_t length, char** buffer) { + std::optional<std::array<std::optional<std::string>, N>>* arr = + static_cast<std::optional<std::array<std::optional<std::string>, N>>*>(arrData); + std::optional<std::string>& element = (*arr)->at(index); + return AParcel_nullableStdStringAllocator(static_cast<void*>(&element), length, buffer); +} + +/** * Convenience API for writing a std::string. */ static inline binder_status_t AParcel_writeString(AParcel* parcel, const std::string& str) { @@ -429,11 +672,17 @@ static inline binder_status_t AParcel_readVector( */ template <typename P> static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) { - binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null - if (status != STATUS_OK) { - return status; + if constexpr (is_interface_v<P>) { + // Legacy behavior: allow null + return first_template_type_t<P>::writeToParcel(parcel, p); + } else { + static_assert(is_parcelable_v<P>); + binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null + if (status != STATUS_OK) { + return status; + } + return p.writeToParcel(parcel); } - return p.writeToParcel(parcel); } /** @@ -441,85 +690,134 @@ static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& */ template <typename P> static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) { - int32_t null; - binder_status_t status = AParcel_readInt32(parcel, &null); - if (status != STATUS_OK) { - return status; - } - if (null == 0) { - return STATUS_UNEXPECTED_NULL; + if constexpr (is_interface_v<P>) { + // Legacy behavior: allow null + return first_template_type_t<P>::readFromParcel(parcel, p); + } else { + static_assert(is_parcelable_v<P>); + int32_t null; + binder_status_t status = AParcel_readInt32(parcel, &null); + if (status != STATUS_OK) { + return status; + } + if (null == 0) { + return STATUS_UNEXPECTED_NULL; + } + return p->readFromParcel(parcel); } - return p->readFromParcel(parcel); } /** * Convenience API for writing a nullable parcelable. */ template <typename P> -static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, - const std::optional<P>& p) { - if (p == std::nullopt) { - return AParcel_writeInt32(parcel, 0); // null - } - binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null - if (status != STATUS_OK) { - return status; +static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, const P& p) { + if constexpr (is_interface_v<P>) { + return first_template_type_t<P>::writeToParcel(parcel, p); + } else { + static_assert(is_nullable_parcelable_v<P>); + if (!p) { + return AParcel_writeInt32(parcel, 0); // null + } + binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null + if (status != STATUS_OK) { + return status; + } + return p->writeToParcel(parcel); } - return p->writeToParcel(parcel); } /** - * Convenience API for writing a nullable parcelable. + * Convenience API for reading a nullable parcelable. */ template <typename P> -static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, - const std::unique_ptr<P>& p) { - if (!p) { - return AParcel_writeInt32(parcel, 0); // null - } - binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null - if (status != STATUS_OK) { - return status; +static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, P* p) { + if constexpr (is_interface_v<P>) { + return first_template_type_t<P>::readFromParcel(parcel, p); + } else if constexpr (is_specialization_v<P, std::optional>) { + int32_t null; + binder_status_t status = AParcel_readInt32(parcel, &null); + if (status != STATUS_OK) { + return status; + } + if (null == 0) { + *p = std::nullopt; + return STATUS_OK; + } + p->emplace(first_template_type_t<P>()); + return (*p)->readFromParcel(parcel); + } else { + static_assert(is_specialization_v<P, std::unique_ptr>); + int32_t null; + binder_status_t status = AParcel_readInt32(parcel, &null); + if (status != STATUS_OK) { + return status; + } + if (null == 0) { + p->reset(); + return STATUS_OK; + } + *p = std::make_unique<first_template_type_t<P>>(); + return (*p)->readFromParcel(parcel); } - return p->writeToParcel(parcel); +} + +// Forward decls +template <typename T> +static inline binder_status_t AParcel_writeData(AParcel* parcel, const T& value); +template <typename T> +static inline binder_status_t AParcel_writeNullableData(AParcel* parcel, const T& value); +template <typename T> +static inline binder_status_t AParcel_readData(const AParcel* parcel, T* value); +template <typename T> +static inline binder_status_t AParcel_readNullableData(const AParcel* parcel, T* value); + +/** + * Reads an object of type T inside a std::array<T, N> at index 'index' from 'parcel'. + */ +template <typename T, size_t N> +binder_status_t AParcel_readStdArrayData(const AParcel* parcel, void* arrayData, size_t index) { + std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData); + return AParcel_readData(parcel, &arr->at(index)); } /** - * Convenience API for reading a nullable parcelable. + * Reads a nullable object of type T inside a std::array<T, N> at index 'index' from 'parcel'. */ -template <typename P> -static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, - std::optional<P>* p) { - int32_t null; - binder_status_t status = AParcel_readInt32(parcel, &null); - if (status != STATUS_OK) { - return status; - } - if (null == 0) { - *p = std::nullopt; - return STATUS_OK; - } - *p = std::optional<P>(P{}); - return (*p)->readFromParcel(parcel); +template <typename T, size_t N> +binder_status_t AParcel_readStdArrayNullableData(const AParcel* parcel, void* arrayData, + size_t index) { + std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData); + return AParcel_readNullableData(parcel, &arr->at(index)); } /** - * Convenience API for reading a nullable parcelable. + * Reads a nullable object of type T inside a std::array<T, N> at index 'index' from 'parcel'. */ -template <typename P> -static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, - std::unique_ptr<P>* p) { - int32_t null; - binder_status_t status = AParcel_readInt32(parcel, &null); - if (status != STATUS_OK) { - return status; - } - if (null == 0) { - p->reset(); - return STATUS_OK; - } - *p = std::make_unique<P>(); - return (*p)->readFromParcel(parcel); +template <typename T, size_t N> +binder_status_t AParcel_readNullableStdArrayNullableData(const AParcel* parcel, void* arrayData, + size_t index) { + std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData); + return AParcel_readNullableData(parcel, &(*arr)->at(index)); +} + +/** + * Writes an object of type T inside a std::array<T, N> at index 'index' to 'parcel'. + */ +template <typename T, size_t N> +binder_status_t AParcel_writeStdArrayData(AParcel* parcel, const void* arrayData, size_t index) { + const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrayData); + return AParcel_writeData(parcel, arr->at(index)); +} + +/** + * Writes a nullable object of type T inside a std::array<T, N> at index 'index' to 'parcel'. + */ +template <typename T, size_t N> +binder_status_t AParcel_writeStdArrayNullableData(AParcel* parcel, const void* arrayData, + size_t index) { + const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrayData); + return AParcel_writeNullableData(parcel, arr->at(index)); } /** @@ -665,9 +963,25 @@ inline binder_status_t AParcel_readNullableStdVectorParcelableElement<SpAIBinder */ template <typename P> static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<P>& vec) { - const void* vectorData = static_cast<const void*>(&vec); - return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec.size()), - AParcel_writeStdVectorParcelableElement<P>); + if constexpr (std::is_enum_v<P>) { + if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) { + return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()), + static_cast<int32_t>(vec.size())); + } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) { + return AParcel_writeInt32Array(parcel, reinterpret_cast<const int32_t*>(vec.data()), + static_cast<int32_t>(vec.size())); + } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) { + return AParcel_writeInt64Array(parcel, reinterpret_cast<const int64_t*>(vec.data()), + static_cast<int32_t>(vec.size())); + } else { + static_assert(dependent_false_v<P>, "unrecognized type"); + } + } else { + static_assert(!std::is_same_v<P, std::string>, "specialization should be used"); + const void* vectorData = static_cast<const void*>(&vec); + return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec.size()), + AParcel_writeStdVectorParcelableElement<P>); + } } /** @@ -675,9 +989,24 @@ static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::ve */ template <typename P> static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<P>* vec) { - void* vectorData = static_cast<void*>(vec); - return AParcel_readParcelableArray(parcel, vectorData, AParcel_stdVectorExternalAllocator<P>, - AParcel_readStdVectorParcelableElement<P>); + if constexpr (std::is_enum_v<P>) { + void* vectorData = static_cast<void*>(vec); + if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) { + return AParcel_readByteArray(parcel, vectorData, AParcel_stdVectorAllocator<int8_t>); + } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) { + return AParcel_readInt32Array(parcel, vectorData, AParcel_stdVectorAllocator<int32_t>); + } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) { + return AParcel_readInt64Array(parcel, vectorData, AParcel_stdVectorAllocator<int64_t>); + } else { + static_assert(dependent_false_v<P>, "unrecognized type"); + } + } else { + static_assert(!std::is_same_v<P, std::string>, "specialization should be used"); + void* vectorData = static_cast<void*>(vec); + return AParcel_readParcelableArray(parcel, vectorData, + AParcel_stdVectorExternalAllocator<P>, + AParcel_readStdVectorParcelableElement<P>); + } } /** @@ -686,10 +1015,30 @@ static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vec template <typename P> static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::optional<std::vector<P>>& vec) { - if (!vec) return AParcel_writeInt32(parcel, -1); - const void* vectorData = static_cast<const void*>(&vec); - return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec->size()), - AParcel_writeNullableStdVectorParcelableElement<P>); + if constexpr (std::is_enum_v<P>) { + if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) { + return AParcel_writeByteArray( + parcel, vec ? reinterpret_cast<const int8_t*>(vec->data()) : nullptr, + vec ? static_cast<int32_t>(vec->size()) : -1); + } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) { + return AParcel_writeInt32Array( + parcel, vec ? reinterpret_cast<const int32_t*>(vec->data()) : nullptr, + vec ? static_cast<int32_t>(vec->size()) : -1); + } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) { + return AParcel_writeInt64Array( + parcel, vec ? reinterpret_cast<const int64_t*>(vec->data()) : nullptr, + vec ? static_cast<int32_t>(vec->size()) : -1); + } else { + static_assert(dependent_false_v<P>, "unrecognized type"); + } + } else { + static_assert(!std::is_same_v<P, std::optional<std::string>>, + "specialization should be used"); + if (!vec) return AParcel_writeInt32(parcel, -1); + const void* vectorData = static_cast<const void*>(&vec); + return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec->size()), + AParcel_writeNullableStdVectorParcelableElement<P>); + } } /** @@ -698,10 +1047,28 @@ static inline binder_status_t AParcel_writeVector(AParcel* parcel, template <typename P> static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::optional<std::vector<P>>* vec) { - void* vectorData = static_cast<void*>(vec); - return AParcel_readParcelableArray(parcel, vectorData, - AParcel_nullableStdVectorExternalAllocator<P>, - AParcel_readNullableStdVectorParcelableElement<P>); + if constexpr (std::is_enum_v<P>) { + void* vectorData = static_cast<void*>(vec); + if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) { + return AParcel_readByteArray(parcel, vectorData, + AParcel_nullableStdVectorAllocator<int8_t>); + } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) { + return AParcel_readInt32Array(parcel, vectorData, + AParcel_nullableStdVectorAllocator<int32_t>); + } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) { + return AParcel_readInt64Array(parcel, vectorData, + AParcel_nullableStdVectorAllocator<int64_t>); + } else { + static_assert(dependent_false_v<P>, "unrecognized type"); + } + } else { + static_assert(!std::is_same_v<P, std::optional<std::string>>, + "specialization should be used"); + void* vectorData = static_cast<void*>(vec); + return AParcel_readParcelableArray(parcel, vectorData, + AParcel_nullableStdVectorExternalAllocator<P>, + AParcel_readNullableStdVectorParcelableElement<P>); + } } // @START @@ -1083,6 +1450,294 @@ static inline binder_status_t AParcel_resizeVector(const AParcel* parcel, return STATUS_OK; } +/** + * Writes a fixed-size array of T. + */ +template <typename T, size_t N> +static inline binder_status_t AParcel_writeFixedArray(AParcel* parcel, + const std::array<T, N>& arr) { + if constexpr (std::is_same_v<T, bool>) { + const void* arrayData = static_cast<const void*>(&arr); + return AParcel_writeBoolArray(parcel, arrayData, static_cast<int32_t>(N), + &AParcel_stdArrayGetter<T, N>); + } else if constexpr (std::is_same_v<T, uint8_t>) { + return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(arr.data()), + static_cast<int32_t>(arr.size())); + } else if constexpr (std::is_same_v<T, char16_t>) { + return AParcel_writeCharArray(parcel, arr.data(), static_cast<int32_t>(arr.size())); + } else if constexpr (std::is_same_v<T, int32_t>) { + return AParcel_writeInt32Array(parcel, arr.data(), static_cast<int32_t>(arr.size())); + } else if constexpr (std::is_same_v<T, int64_t>) { + return AParcel_writeInt64Array(parcel, arr.data(), static_cast<int32_t>(arr.size())); + } else if constexpr (std::is_same_v<T, float>) { + return AParcel_writeFloatArray(parcel, arr.data(), static_cast<int32_t>(arr.size())); + } else if constexpr (std::is_same_v<T, double>) { + return AParcel_writeDoubleArray(parcel, arr.data(), static_cast<int32_t>(arr.size())); + } else if constexpr (std::is_same_v<T, std::string>) { + const void* arrayData = static_cast<const void*>(&arr); + return AParcel_writeStringArray(parcel, arrayData, static_cast<int32_t>(N), + &AParcel_stdArrayStringElementGetter<N>); + } else { + const void* arrayData = static_cast<const void*>(&arr); + return AParcel_writeParcelableArray(parcel, arrayData, static_cast<int32_t>(N), + &AParcel_writeStdArrayData<T, N>); + } +} + +/** + * Writes a fixed-size array of T. + */ +template <typename T, size_t N> +static inline binder_status_t AParcel_writeFixedArrayWithNullableData(AParcel* parcel, + const std::array<T, N>& arr) { + if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, uint8_t> || + std::is_same_v<T, char16_t> || std::is_same_v<T, int32_t> || + std::is_same_v<T, int64_t> || std::is_same_v<T, float> || + std::is_same_v<T, double> || std::is_same_v<T, std::string>) { + return AParcel_writeFixedArray(parcel, arr); + } else if constexpr (std::is_same_v<T, std::optional<std::string>>) { + const void* arrayData = static_cast<const void*>(&arr); + return AParcel_writeStringArray(parcel, arrayData, static_cast<int32_t>(N), + &AParcel_stdArrayNullableStringElementGetter<N>); + } else { + const void* arrayData = static_cast<const void*>(&arr); + return AParcel_writeParcelableArray(parcel, arrayData, static_cast<int32_t>(N), + &AParcel_writeStdArrayNullableData<T, N>); + } +} + +/** + * Writes a fixed-size array of T. + */ +template <typename T, size_t N> +static inline binder_status_t AParcel_writeNullableFixedArrayWithNullableData( + AParcel* parcel, const std::optional<std::array<T, N>>& arr) { + if (!arr) return AParcel_writeInt32(parcel, -1); + return AParcel_writeFixedArrayWithNullableData(parcel, arr.value()); +} + +/** + * Reads a fixed-size array of T. + */ +template <typename T, size_t N> +static inline binder_status_t AParcel_readFixedArray(const AParcel* parcel, std::array<T, N>* arr) { + void* arrayData = static_cast<void*>(arr); + if constexpr (std::is_same_v<T, bool>) { + return AParcel_readBoolArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>, + &AParcel_stdArraySetter<T, N>); + } else if constexpr (std::is_same_v<T, uint8_t>) { + return AParcel_readByteArray(parcel, arrayData, &AParcel_stdArrayAllocator<int8_t, N>); + } else if constexpr (std::is_same_v<T, char16_t>) { + return AParcel_readCharArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>); + } else if constexpr (std::is_same_v<T, int32_t>) { + return AParcel_readInt32Array(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>); + } else if constexpr (std::is_same_v<T, int64_t>) { + return AParcel_readInt64Array(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>); + } else if constexpr (std::is_same_v<T, float>) { + return AParcel_readFloatArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>); + } else if constexpr (std::is_same_v<T, double>) { + return AParcel_readDoubleArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>); + } else if constexpr (std::is_same_v<T, std::string>) { + return AParcel_readStringArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>, + &AParcel_stdArrayStringElementAllocator<N>); + } else { + return AParcel_readParcelableArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>, + &AParcel_readStdArrayData<T, N>); + } +} + +/** + * Reads a fixed-size array of T. + */ +template <typename T, size_t N> +static inline binder_status_t AParcel_readFixedArrayWithNullableData(const AParcel* parcel, + std::array<T, N>* arr) { + void* arrayData = static_cast<void*>(arr); + if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, uint8_t> || + std::is_same_v<T, char16_t> || std::is_same_v<T, int32_t> || + std::is_same_v<T, int64_t> || std::is_same_v<T, float> || + std::is_same_v<T, double> || std::is_same_v<T, std::string>) { + return AParcel_readFixedArray(parcel, arr); + } else if constexpr (std::is_same_v<T, std::optional<std::string>>) { + return AParcel_readStringArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>, + &AParcel_stdArrayNullableStringElementAllocator<N>); + } else { + return AParcel_readParcelableArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>, + &AParcel_readStdArrayNullableData<T, N>); + } +} + +/** + * Reads a fixed-size array of T. + */ +template <typename T, size_t N> +static inline binder_status_t AParcel_readNullableFixedArrayWithNullableData( + const AParcel* parcel, std::optional<std::array<T, N>>* arr) { + void* arrayData = static_cast<void*>(arr); + if constexpr (std::is_same_v<T, bool>) { + return AParcel_readBoolArray(parcel, arrayData, + &AParcel_nullableStdArrayExternalAllocator<T, N>, + &AParcel_nullableStdArraySetter<T, N>); + } else if constexpr (std::is_same_v<T, uint8_t>) { + return AParcel_readByteArray(parcel, arrayData, + &AParcel_nullableStdArrayAllocator<int8_t, N>); + } else if constexpr (std::is_same_v<T, char16_t>) { + return AParcel_readCharArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>); + } else if constexpr (std::is_same_v<T, int32_t>) { + return AParcel_readInt32Array(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>); + } else if constexpr (std::is_same_v<T, int64_t>) { + return AParcel_readInt64Array(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>); + } else if constexpr (std::is_same_v<T, float>) { + return AParcel_readFloatArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>); + } else if constexpr (std::is_same_v<T, double>) { + return AParcel_readDoubleArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>); + } else if constexpr (std::is_same_v<T, std::string>) { + return AParcel_readStringArray(parcel, arrayData, + &AParcel_nullableStdArrayExternalAllocator<N>, + &AParcel_nullableStdArrayStringElementAllocator<N>); + } else { + return AParcel_readParcelableArray(parcel, arrayData, + &AParcel_nullableStdArrayExternalAllocator<T, N>, + &AParcel_readStdArrayNullableData<T, N>); + } +} + +/** + * Convenience API for writing a value of any type. + */ +template <typename T> +static inline binder_status_t AParcel_writeData(AParcel* parcel, const T& value) { + if constexpr (is_specialization_v<T, std::vector>) { + return AParcel_writeVector(parcel, value); + } else if constexpr (is_fixed_array_v<T>) { + return AParcel_writeFixedArray(parcel, value); + } else if constexpr (std::is_same_v<std::string, T>) { + return AParcel_writeString(parcel, value); + } else if constexpr (std::is_same_v<bool, T>) { + return AParcel_writeBool(parcel, value); + } else if constexpr (std::is_same_v<int8_t, T> || std::is_same_v<uint8_t, T>) { + return AParcel_writeByte(parcel, value); + } else if constexpr (std::is_same_v<char16_t, T>) { + return AParcel_writeChar(parcel, value); + } else if constexpr (std::is_same_v<int32_t, T>) { + return AParcel_writeInt32(parcel, value); + } else if constexpr (std::is_same_v<int64_t, T>) { + return AParcel_writeInt64(parcel, value); + } else if constexpr (std::is_same_v<float, T>) { + return AParcel_writeFloat(parcel, value); + } else if constexpr (std::is_same_v<double, T>) { + return AParcel_writeDouble(parcel, value); + } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) { + return AParcel_writeRequiredParcelFileDescriptor(parcel, value); + } else if constexpr (std::is_same_v<SpAIBinder, T>) { + return AParcel_writeRequiredStrongBinder(parcel, value); + } else if constexpr (std::is_enum_v<T>) { + return AParcel_writeData(parcel, static_cast<std::underlying_type_t<T>>(value)); + } else if constexpr (is_interface_v<T>) { + return AParcel_writeParcelable(parcel, value); + } else if constexpr (is_parcelable_v<T>) { + return AParcel_writeParcelable(parcel, value); + } else { + static_assert(dependent_false_v<T>, "unrecognized type"); + return STATUS_OK; + } +} + +/** + * Convenience API for writing a nullable value of any type. + */ +template <typename T> +static inline binder_status_t AParcel_writeNullableData(AParcel* parcel, const T& value) { + if constexpr (is_specialization_v<T, std::optional> && + is_specialization_v<first_template_type_t<T>, std::vector>) { + return AParcel_writeVector(parcel, value); + } else if constexpr (is_specialization_v<T, std::optional> && + is_fixed_array_v<first_template_type_t<T>>) { + return AParcel_writeNullableFixedArrayWithNullableData(parcel, value); + } else if constexpr (is_fixed_array_v<T>) { // happens with a nullable multi-dimensional array. + return AParcel_writeFixedArrayWithNullableData(parcel, value); + } else if constexpr (is_specialization_v<T, std::optional> && + std::is_same_v<first_template_type_t<T>, std::string>) { + return AParcel_writeString(parcel, value); + } else if constexpr (is_nullable_parcelable_v<T> || is_interface_v<T>) { + return AParcel_writeNullableParcelable(parcel, value); + } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) { + return AParcel_writeNullableParcelFileDescriptor(parcel, value); + } else if constexpr (std::is_same_v<SpAIBinder, T>) { + return AParcel_writeNullableStrongBinder(parcel, value); + } else { + return AParcel_writeData(parcel, value); + } +} + +/** + * Convenience API for reading a value of any type. + */ +template <typename T> +static inline binder_status_t AParcel_readData(const AParcel* parcel, T* value) { + if constexpr (is_specialization_v<T, std::vector>) { + return AParcel_readVector(parcel, value); + } else if constexpr (is_fixed_array_v<T>) { + return AParcel_readFixedArray(parcel, value); + } else if constexpr (std::is_same_v<std::string, T>) { + return AParcel_readString(parcel, value); + } else if constexpr (std::is_same_v<bool, T>) { + return AParcel_readBool(parcel, value); + } else if constexpr (std::is_same_v<int8_t, T> || std::is_same_v<uint8_t, T>) { + return AParcel_readByte(parcel, value); + } else if constexpr (std::is_same_v<char16_t, T>) { + return AParcel_readChar(parcel, value); + } else if constexpr (std::is_same_v<int32_t, T>) { + return AParcel_readInt32(parcel, value); + } else if constexpr (std::is_same_v<int64_t, T>) { + return AParcel_readInt64(parcel, value); + } else if constexpr (std::is_same_v<float, T>) { + return AParcel_readFloat(parcel, value); + } else if constexpr (std::is_same_v<double, T>) { + return AParcel_readDouble(parcel, value); + } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) { + return AParcel_readRequiredParcelFileDescriptor(parcel, value); + } else if constexpr (std::is_same_v<SpAIBinder, T>) { + return AParcel_readRequiredStrongBinder(parcel, value); + } else if constexpr (std::is_enum_v<T>) { + return AParcel_readData(parcel, reinterpret_cast<std::underlying_type_t<T>*>(value)); + } else if constexpr (is_interface_v<T>) { + return AParcel_readParcelable(parcel, value); + } else if constexpr (is_parcelable_v<T>) { + return AParcel_readParcelable(parcel, value); + } else { + static_assert(dependent_false_v<T>, "unrecognized type"); + return STATUS_OK; + } +} + +/** + * Convenience API for reading a nullable value of any type. + */ +template <typename T> +static inline binder_status_t AParcel_readNullableData(const AParcel* parcel, T* value) { + if constexpr (is_specialization_v<T, std::optional> && + is_specialization_v<first_template_type_t<T>, std::vector>) { + return AParcel_readVector(parcel, value); + } else if constexpr (is_specialization_v<T, std::optional> && + is_fixed_array_v<first_template_type_t<T>>) { + return AParcel_readNullableFixedArrayWithNullableData(parcel, value); + } else if constexpr (is_fixed_array_v<T>) { // happens with a nullable multi-dimensional array. + return AParcel_readFixedArrayWithNullableData(parcel, value); + } else if constexpr (is_specialization_v<T, std::optional> && + std::is_same_v<first_template_type_t<T>, std::string>) { + return AParcel_readString(parcel, value); + } else if constexpr (is_nullable_parcelable_v<T> || is_interface_v<T>) { + return AParcel_readNullableParcelable(parcel, value); + } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) { + return AParcel_readNullableParcelFileDescriptor(parcel, value); + } else if constexpr (std::is_same_v<SpAIBinder, T>) { + return AParcel_readNullableStrongBinder(parcel, value); + } else { + return AParcel_readData(parcel, value); + } +} + } // namespace ndk /** @} */ diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h index aa3b97806b..972eca79ff 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h @@ -46,6 +46,18 @@ class AParcelableHolder { AParcelableHolder() = delete; explicit AParcelableHolder(parcelable_stability_t stability) : mParcel(AParcel_create()), mStability(stability) {} + +#if __ANDROID_API__ >= 31 + AParcelableHolder(const AParcelableHolder& other) + : mParcel(AParcel_create()), mStability(other.mStability) { + // AParcelableHolder has been introduced in 31. + if (__builtin_available(android 31, *)) { + AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0, + AParcel_getDataSize(other.mParcel.get())); + } + } +#endif + AParcelableHolder(AParcelableHolder&& other) = default; virtual ~AParcelableHolder() = default; diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index d63a8d0963..197c0a1db0 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -119,11 +119,11 @@ LIBBINDER_NDK31 { # introduced=31 AIBinder_setRequestingSid; # apex AParcel_markSensitive; # systemapi llndk AServiceManager_forEachDeclaredInstance; # apex llndk - AServiceManager_forceLazyServicesPersist; # llndk + AServiceManager_forceLazyServicesPersist; # apex llndk AServiceManager_isDeclared; # apex llndk AServiceManager_isUpdatableViaApex; # apex AServiceManager_reRegister; # llndk - AServiceManager_registerLazyService; # llndk + AServiceManager_registerLazyService; # apex llndk AServiceManager_setActiveServicesCallback; # llndk AServiceManager_tryUnregister; # llndk AServiceManager_waitForService; # apex llndk diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index d323022b01..e2fc18d859 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -20,16 +20,19 @@ rust_library { "libdowncast_rs", ], host_supported: true, + vendor_available: true, target: { darwin: { enabled: false, - } + }, }, apex_available: [ "//apex_available:platform", "com.android.compos", + "com.android.uwb", "com.android.virt", ], + min_sdk_version: "current", } rust_library { @@ -44,7 +47,7 @@ rust_library { target: { darwin: { enabled: false, - } + }, }, apex_available: [ "//apex_available:platform", @@ -64,16 +67,19 @@ rust_library { "libbinder_ndk", ], host_supported: true, + vendor_available: true, target: { darwin: { enabled: false, - } + }, }, apex_available: [ "//apex_available:platform", "com.android.compos", + "com.android.uwb", "com.android.virt", ], + min_sdk_version: "current", lints: "none", clippy_lints: "none", } @@ -86,25 +92,37 @@ rust_bindgen { bindgen_flags: [ // Unfortunately the only way to specify the rust_non_exhaustive enum // style for a type is to make it the default - "--default-enum-style", "rust_non_exhaustive", + "--default-enum-style", + "rust_non_exhaustive", // and then specify constified enums for the enums we don't want // rustified - "--constified-enum", "android::c_interface::consts::.*", + "--constified-enum", + "android::c_interface::consts::.*", - "--allowlist-type", "android::c_interface::.*", - "--allowlist-type", "AStatus", - "--allowlist-type", "AIBinder_Class", - "--allowlist-type", "AIBinder", - "--allowlist-type", "AIBinder_Weak", - "--allowlist-type", "AIBinder_DeathRecipient", - "--allowlist-type", "AParcel", - "--allowlist-type", "binder_status_t", - "--allowlist-function", ".*", + "--allowlist-type", + "android::c_interface::.*", + "--allowlist-type", + "AStatus", + "--allowlist-type", + "AIBinder_Class", + "--allowlist-type", + "AIBinder", + "--allowlist-type", + "AIBinder_Weak", + "--allowlist-type", + "AIBinder_DeathRecipient", + "--allowlist-type", + "AParcel", + "--allowlist-type", + "binder_status_t", + "--allowlist-function", + ".*", ], shared_libs: [ "libbinder_ndk", ], host_supported: true, + vendor_available: true, // Currently necessary for host builds // TODO(b/31559095): bionic on host should define this @@ -124,8 +142,10 @@ rust_bindgen { apex_available: [ "//apex_available:platform", "com.android.compos", + "com.android.uwb", "com.android.virt", ], + min_sdk_version: "current", } // TODO(b/184872979): remove once the Rust API is created. @@ -139,8 +159,10 @@ rust_bindgen { ], apex_available: [ "com.android.compos", + "com.android.uwb", "com.android.virt", ], + min_sdk_version: "current", } rust_test { diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs index 64833b6d60..91047bea48 100644 --- a/libs/binder/rust/binder_tokio/lib.rs +++ b/libs/binder/rust/binder_tokio/lib.rs @@ -35,6 +35,11 @@ use std::future::Future; /// Retrieve an existing service for a particular interface, sleeping for a few /// seconds if it doesn't yet exist. pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> { + if binder::is_handling_transaction() { + // See comment in the BinderAsyncPool impl. + return binder::public_api::get_interface::<T>(name); + } + let name = name.to_string(); let res = tokio::task::spawn_blocking(move || { binder::public_api::get_interface::<T>(&name) @@ -54,6 +59,11 @@ pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Res /// Retrieve an existing service for a particular interface, or start it if it /// is configured as a dynamic service and isn't yet started. pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> { + if binder::is_handling_transaction() { + // See comment in the BinderAsyncPool impl. + return binder::public_api::wait_for_interface::<T>(name); + } + let name = name.to_string(); let res = tokio::task::spawn_blocking(move || { binder::public_api::wait_for_interface::<T>(&name) @@ -86,18 +96,27 @@ impl BinderAsyncPool for Tokio { B: Send + 'a, E: From<crate::StatusCode>, { - let handle = tokio::task::spawn_blocking(spawn_me); - Box::pin(async move { - // The `is_panic` branch is not actually reachable in Android as we compile - // with `panic = abort`. - match handle.await { - Ok(res) => after_spawn(res).await, - Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()), - Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()), - Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()), - } - }) + if binder::is_handling_transaction() { + // We are currently on the thread pool for a binder server, so we should execute the + // transaction on the current thread so that the binder kernel driver is able to apply + // its deadlock prevention strategy to the sub-call. + // + // This shouldn't cause issues with blocking the thread as only one task will run in a + // call to `block_on`, so there aren't other tasks to block. + let result = spawn_me(); + Box::pin(after_spawn(result)) + } else { + let handle = tokio::task::spawn_blocking(spawn_me); + Box::pin(async move { + // The `is_panic` branch is not actually reachable in Android as we compile + // with `panic = abort`. + match handle.await { + Ok(res) => after_spawn(res).await, + Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()), + Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()), + Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()), + } + }) + } } } - - diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 4e048d7c5a..4d6b294000 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -17,7 +17,7 @@ //! Trait definitions for binder objects use crate::error::{status_t, Result, StatusCode}; -use crate::parcel::{OwnedParcel, Parcel}; +use crate::parcel::{Parcel, BorrowedParcel}; use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder}; use crate::sys; @@ -66,6 +66,35 @@ pub trait Interface: Send + Sync { } } +/// Implemented by sync interfaces to specify what the associated async interface is. +/// Generic to handle the fact that async interfaces are generic over a thread pool. +/// +/// The binder in any object implementing this trait should be compatible with the +/// `Target` associated type, and using `FromIBinder` to convert it to the target +/// should not fail. +pub trait ToAsyncInterface<P> +where + Self: Interface, + Self::Target: FromIBinder, +{ + /// The async interface associated with this sync interface. + type Target: ?Sized; +} + +/// Implemented by async interfaces to specify what the associated sync interface is. +/// +/// The binder in any object implementing this trait should be compatible with the +/// `Target` associated type, and using `FromIBinder` to convert it to the target +/// should not fail. +pub trait ToSyncInterface +where + Self: Interface, + Self::Target: FromIBinder, +{ + /// The sync interface associated with this async interface. + type Target: ?Sized; +} + /// Interface stability promise /// /// An interface can promise to be a stable vendor interface ([`Vintf`]), or @@ -129,7 +158,7 @@ pub trait Remotable: Send + Sync { /// Handle and reply to a request to invoke a transaction on this object. /// /// `reply` may be [`None`] if the sender does not expect a reply. - fn on_transact(&self, code: TransactionCode, data: &Parcel, reply: &mut Parcel) -> Result<()>; + fn on_transact(&self, code: TransactionCode, data: &BorrowedParcel<'_>, reply: &mut BorrowedParcel<'_>) -> Result<()>; /// Handle a request to invoke the dump transaction on this /// object. @@ -167,6 +196,7 @@ pub trait IBinderInternal: IBinder { fn ping_binder(&mut self) -> Result<()>; /// Indicate that the service intends to receive caller security contexts. + #[cfg(not(android_vndk))] fn set_requesting_sid(&mut self, enable: bool); /// Dump this object to the given file handle @@ -177,25 +207,25 @@ pub trait IBinderInternal: IBinder { fn get_extension(&mut self) -> Result<Option<SpIBinder>>; /// Create a Parcel that can be used with `submit_transact`. - fn prepare_transact(&self) -> Result<OwnedParcel>; + fn prepare_transact(&self) -> Result<Parcel>; /// Perform a generic operation with the object. /// - /// The provided [`OwnedParcel`] must have been created by a call to + /// The provided [`Parcel`] must have been created by a call to /// `prepare_transact` on the same binder. /// /// # Arguments /// /// * `code` - Transaction code for the operation. - /// * `data` - [`OwnedParcel`] with input data. + /// * `data` - [`Parcel`] with input data. /// * `flags` - Transaction flags, e.g. marking the transaction as /// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY)). fn submit_transact( &self, code: TransactionCode, - data: OwnedParcel, + data: Parcel, flags: TransactionFlags, - ) -> Result<OwnedParcel>; + ) -> Result<Parcel>; /// Perform a generic operation with the object. This is a convenience /// method that internally calls `prepare_transact` followed by @@ -206,15 +236,15 @@ pub trait IBinderInternal: IBinder { /// * `flags` - Transaction flags, e.g. marking the transaction as /// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY)) /// * `input_callback` A callback for building the `Parcel`. - fn transact<F: FnOnce(&mut Parcel) -> Result<()>>( + fn transact<F: FnOnce(BorrowedParcel<'_>) -> Result<()>>( &self, code: TransactionCode, flags: TransactionFlags, input_callback: F, ) -> Result<Parcel> { let mut parcel = self.prepare_transact()?; - input_callback(&mut parcel.borrowed())?; - self.submit_transact(code, parcel, flags).map(OwnedParcel::into_parcel) + input_callback(parcel.borrowed())?; + self.submit_transact(code, parcel, flags) } } @@ -336,6 +366,26 @@ impl<I: FromIBinder + ?Sized> Strong<I> { pub fn downgrade(this: &Strong<I>) -> Weak<I> { Weak::new(this) } + + /// Convert this synchronous binder handle into an asynchronous one. + pub fn into_async<P>(self) -> Strong<<I as ToAsyncInterface<P>>::Target> + where + I: ToAsyncInterface<P>, + { + // By implementing the ToAsyncInterface trait, it is guaranteed that the binder + // object is also valid for the target type. + FromIBinder::try_from(self.0.as_binder()).unwrap() + } + + /// Convert this asynchronous binder handle into a synchronous one. + pub fn into_sync(self) -> Strong<<I as ToSyncInterface>::Target> + where + I: ToSyncInterface, + { + // By implementing the ToSyncInterface trait, it is guaranteed that the binder + // object is also valid for the target type. + FromIBinder::try_from(self.0.as_binder()).unwrap() + } } impl<I: FromIBinder + ?Sized> Clone for Strong<I> { @@ -475,8 +525,8 @@ impl<I: FromIBinder + ?Sized> Eq for Weak<I> {} /// fn on_transact( /// &self, /// code: TransactionCode, -/// data: &Parcel, -/// reply: &mut Parcel, +/// data: &BorrowedParcel, +/// reply: &mut BorrowedParcel, /// ) -> Result<()> { /// // ... /// } @@ -635,6 +685,7 @@ unsafe impl<T, V: AsNative<T>> AsNative<T> for Option<V> { pub struct BinderFeatures { /// Indicates that the service intends to receive caller security contexts. This must be true /// for `ThreadState::with_calling_sid` to work. + #[cfg(not(android_vndk))] pub set_requesting_sid: bool, // Ensure that clients include a ..BinderFeatures::default() to preserve backwards compatibility // when new fields are added. #[non_exhaustive] doesn't work because it prevents struct @@ -655,13 +706,13 @@ pub struct BinderFeatures { /// have the following type: /// /// ``` -/// # use binder::{Interface, TransactionCode, Parcel}; +/// # use binder::{Interface, TransactionCode, BorrowedParcel}; /// # trait Placeholder { /// fn on_transact( /// service: &dyn Interface, /// code: TransactionCode, -/// data: &Parcel, -/// reply: &mut Parcel, +/// data: &BorrowedParcel, +/// reply: &mut BorrowedParcel, /// ) -> binder::Result<()>; /// # } /// ``` @@ -676,7 +727,7 @@ pub struct BinderFeatures { /// using the provided function, `on_transact`. /// /// ``` -/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, Parcel}; +/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, BorrowedParcel}; /// /// pub trait IServiceManager: Interface { /// // remote methods... @@ -692,8 +743,8 @@ pub struct BinderFeatures { /// fn on_transact( /// service: &dyn IServiceManager, /// code: TransactionCode, -/// data: &Parcel, -/// reply: &mut Parcel, +/// data: &BorrowedParcel, +/// reply: &mut BorrowedParcel, /// ) -> binder::Result<()> { /// // ... /// Ok(()) @@ -837,6 +888,7 @@ macro_rules! declare_binder_interface { /// Create a new binder service. pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> { let mut binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability); + #[cfg(not(android_vndk))] $crate::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid); $crate::Strong::new(Box::new(binder)) } @@ -847,7 +899,7 @@ macro_rules! declare_binder_interface { $descriptor } - fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::Parcel, reply: &mut $crate::Parcel) -> $crate::Result<()> { + fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::BorrowedParcel<'_>, reply: &mut $crate::BorrowedParcel<'_>) -> $crate::Result<()> { match $on_transact(&*self.0, code, data, reply) { // The C++ backend converts UNEXPECTED_NULL into an exception Err($crate::StatusCode::UNEXPECTED_NULL) => { @@ -922,14 +974,14 @@ macro_rules! declare_binder_interface { where dyn $interface: $crate::Interface { - fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { let binder = $crate::Interface::as_binder(self); parcel.write(&binder) } } impl $crate::parcel::SerializeOption for dyn $interface + '_ { - fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { parcel.write(&this.map($crate::Interface::as_binder)) } } @@ -988,14 +1040,14 @@ macro_rules! declare_binder_interface { } impl<P: $crate::BinderAsyncPool> $crate::parcel::Serialize for dyn $async_interface<P> + '_ { - fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { let binder = $crate::Interface::as_binder(self); parcel.write(&binder) } } impl<P: $crate::BinderAsyncPool> $crate::parcel::SerializeOption for dyn $async_interface<P> + '_ { - fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { parcel.write(&this.map($crate::Interface::as_binder)) } } @@ -1014,6 +1066,14 @@ macro_rules! declare_binder_interface { .expect(concat!("Error cloning interface ", stringify!($async_interface))) } } + + impl<P: $crate::BinderAsyncPool> $crate::ToAsyncInterface<P> for dyn $interface { + type Target = dyn $async_interface<P>; + } + + impl<P: $crate::BinderAsyncPool> $crate::ToSyncInterface for dyn $async_interface<P> { + type Target = dyn $interface; + } )? }; } @@ -1024,42 +1084,46 @@ macro_rules! declare_binder_interface { #[macro_export] macro_rules! declare_binder_enum { { + $( #[$attr:meta] )* $enum:ident : [$backing:ty; $size:expr] { $( $name:ident = $value:expr, )* } } => { + $( #[$attr] )* #[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)] + #[allow(missing_docs)] pub struct $enum(pub $backing); impl $enum { - $( pub const $name: Self = Self($value); )* + $( #[allow(missing_docs)] pub const $name: Self = Self($value); )* #[inline(always)] + #[allow(missing_docs)] pub const fn enum_values() -> [Self; $size] { [$(Self::$name),*] } } impl $crate::parcel::Serialize for $enum { - fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { parcel.write(&self.0) } } impl $crate::parcel::SerializeArray for $enum { - fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> { let v: Vec<$backing> = slice.iter().map(|x| x.0).collect(); <$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel) } } impl $crate::parcel::Deserialize for $enum { - fn deserialize(parcel: &$crate::parcel::Parcel) -> $crate::Result<Self> { + fn deserialize(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Self> { parcel.read().map(Self) } } impl $crate::parcel::DeserializeArray for $enum { - fn deserialize_array(parcel: &$crate::parcel::Parcel) -> $crate::Result<Option<Vec<Self>>> { + fn deserialize_array(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Option<Vec<Self>>> { let v: Option<Vec<$backing>> = <$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?; Ok(v.map(|v| v.into_iter().map(Self).collect())) diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index 2ac2d2f0bc..7c04a7207b 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -50,8 +50,8 @@ //! fn on_transact( //! service: &dyn ITest, //! code: TransactionCode, -//! _data: &Parcel, -//! reply: &mut Parcel, +//! _data: &BorrowedParcel, +//! reply: &mut BorrowedParcel, //! ) -> binder::Result<()> { //! match code { //! SpIBinder::FIRST_CALL_TRANSACTION => { @@ -109,13 +109,13 @@ pub mod parcel; pub use crate::binder::{ BinderFeatures, FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable, - Stability, Strong, TransactionCode, TransactionFlags, Weak, FIRST_CALL_TRANSACTION, - FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION, + Stability, Strong, ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, Weak, + FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION, }; pub use crate::binder_async::{BoxFuture, BinderAsyncPool}; pub use error::{status_t, ExceptionCode, Result, Status, StatusCode}; -pub use native::{add_service, force_lazy_services_persist, register_lazy_service, Binder}; -pub use parcel::{OwnedParcel, Parcel}; +pub use native::{add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, Binder}; +pub use parcel::{BorrowedParcel, Parcel}; pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service}; pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder}; pub use state::{ProcessState, ThreadState}; diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index a91092e2d4..b7c7ae4b54 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -18,7 +18,7 @@ use crate::binder::{ AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode, }; use crate::error::{status_result, status_t, Result, StatusCode}; -use crate::parcel::{Parcel, Serialize}; +use crate::parcel::{BorrowedParcel, Serialize}; use crate::proxy::SpIBinder; use crate::sys; @@ -161,8 +161,8 @@ impl<T: Remotable> Binder<T> { /// # fn on_transact( /// # service: &dyn IBar, /// # code: TransactionCode, - /// # data: &Parcel, - /// # reply: &mut Parcel, + /// # data: &BorrowedParcel, + /// # reply: &mut BorrowedParcel, /// # ) -> binder::Result<()> { /// # Ok(()) /// # } @@ -212,7 +212,7 @@ impl<T: Remotable> Binder<T> { /// Mark this binder object with local stability, which is vendor if we are /// building for the VNDK and system otherwise. - #[cfg(vendor_ndk)] + #[cfg(any(vendor_ndk, android_vndk))] fn mark_local_stability(&mut self) { unsafe { // Safety: Self always contains a valid `AIBinder` pointer, so @@ -223,7 +223,7 @@ impl<T: Remotable> Binder<T> { /// Mark this binder object with local stability, which is vendor if we are /// building for the VNDK and system otherwise. - #[cfg(not(vendor_ndk))] + #[cfg(not(any(vendor_ndk, android_vndk)))] fn mark_local_stability(&mut self) { unsafe { // Safety: Self always contains a valid `AIBinder` pointer, so @@ -277,8 +277,8 @@ impl<T: Remotable> InterfaceClassMethods for Binder<T> { reply: *mut sys::AParcel, ) -> status_t { let res = { - let mut reply = Parcel::borrowed(reply).unwrap(); - let data = Parcel::borrowed(data as *mut sys::AParcel).unwrap(); + let mut reply = BorrowedParcel::from_raw(reply).unwrap(); + let data = BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap(); let object = sys::AIBinder_getUserData(binder); let binder: &T = &*(object as *const T); binder.on_transact(code, &data, &mut reply) @@ -384,7 +384,7 @@ impl<T: Remotable> Deref for Binder<T> { } impl<B: Remotable> Serialize for Binder<B> { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { parcel.write_binder(Some(&self.as_binder())) } } @@ -503,8 +503,8 @@ impl Remotable for () { fn on_transact( &self, _code: TransactionCode, - _data: &Parcel, - _reply: &mut Parcel, + _data: &BorrowedParcel<'_>, + _reply: &mut BorrowedParcel<'_>, ) -> Result<()> { Ok(()) } @@ -517,3 +517,12 @@ impl Remotable for () { } impl Interface for () {} + +/// Determine whether the current thread is currently executing an incoming +/// transaction. +pub fn is_handling_transaction() -> bool { + unsafe { + // Safety: This method is always safe to call. + sys::AIBinder_isHandlingTransaction() + } +} diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index a0e147860d..206b90c807 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -21,11 +21,10 @@ use crate::error::{status_result, Result, StatusCode}; use crate::proxy::SpIBinder; use crate::sys; -use std::cell::RefCell; use std::convert::TryInto; use std::marker::PhantomData; use std::mem::ManuallyDrop; -use std::ptr; +use std::ptr::{self, NonNull}; use std::fmt; mod file_descriptor; @@ -46,53 +45,41 @@ pub use self::parcelable_holder::{ParcelableHolder, ParcelableMetadata}; /// other side of the IPC, and references to live Binder objects that will /// result in the other side receiving a proxy Binder connected with the /// original Binder in the Parcel. -pub enum Parcel { - /// Owned parcel pointer - Owned(*mut sys::AParcel), - /// Borrowed parcel pointer (will not be destroyed on drop) - Borrowed(*mut sys::AParcel), -} - -/// A variant of Parcel that is known to be owned. -pub struct OwnedParcel { - ptr: *mut sys::AParcel, +/// +/// This type represents a parcel that is owned by Rust code. +#[repr(transparent)] +pub struct Parcel { + ptr: NonNull<sys::AParcel>, } /// # Safety /// /// This type guarantees that it owns the AParcel and that all access to -/// the AParcel happens through the OwnedParcel, so it is ok to send across +/// the AParcel happens through the Parcel, so it is ok to send across /// threads. -unsafe impl Send for OwnedParcel {} +unsafe impl Send for Parcel {} -/// A variant of Parcel that is known to be borrowed. +/// Container for a message (data and object references) that can be sent +/// through Binder. +/// +/// This object is a borrowed variant of [`Parcel`]. It is a separate type from +/// `&mut Parcel` because it is not valid to `mem::swap` two parcels. +#[repr(transparent)] pub struct BorrowedParcel<'a> { - inner: Parcel, + ptr: NonNull<sys::AParcel>, _lifetime: PhantomData<&'a mut Parcel>, } -impl OwnedParcel { - /// Create a new empty `OwnedParcel`. - pub fn new() -> OwnedParcel { +impl Parcel { + /// Create a new empty `Parcel`. + pub fn new() -> Parcel { let ptr = unsafe { // Safety: If `AParcel_create` succeeds, it always returns // a valid pointer. If it fails, the process will crash. sys::AParcel_create() }; - assert!(!ptr.is_null()); - Self { ptr } - } - - /// Convert the provided parcel to an owned parcel, or return `None` if it - /// is borrowed. - pub fn try_from(parcel: Parcel) -> Option<OwnedParcel> { - match &parcel { - Parcel::Owned(ptr) => { - let ptr = *ptr; - std::mem::forget(parcel); - Some(OwnedParcel { ptr }) - } - Parcel::Borrowed(_) => None, + Self { + ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer") } } @@ -107,62 +94,93 @@ impl OwnedParcel { /// /// Additionally, the caller must guarantee that it is valid to take /// ownership of the AParcel object. All future access to the AParcel - /// must happen through this `OwnedParcel`. + /// must happen through this `Parcel`. /// - /// Because `OwnedParcel` implements `Send`, the pointer must never point - /// to any thread-local data, e.g., a variable on the stack, either directly - /// or indirectly. - pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<OwnedParcel> { - ptr.as_mut().map(|ptr| Self { ptr }) + /// Because `Parcel` implements `Send`, the pointer must never point to any + /// thread-local data, e.g., a variable on the stack, either directly or + /// indirectly. + pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<Parcel> { + NonNull::new(ptr).map(|ptr| Self { ptr }) } /// Consume the parcel, transferring ownership to the caller. pub(crate) fn into_raw(self) -> *mut sys::AParcel { - let ptr = self.ptr; + let ptr = self.ptr.as_ptr(); let _ = ManuallyDrop::new(self); ptr } - /// Convert this `OwnedParcel` into an owned `Parcel`. - pub fn into_parcel(self) -> Parcel { - Parcel::Owned(self.into_raw()) - } - /// Get a borrowed view into the contents of this `Parcel`. pub fn borrowed(&mut self) -> BorrowedParcel<'_> { + // Safety: The raw pointer is a valid pointer to an AParcel, and the + // lifetime of the returned `BorrowedParcel` is tied to `self`, so the + // borrow checker will ensure that the `AParcel` can only be accessed + // via the `BorrowParcel` until it goes out of scope. BorrowedParcel { - inner: Parcel::Borrowed(self.ptr), + ptr: self.ptr, _lifetime: PhantomData, } } + + /// Get an immutable borrowed view into the contents of this `Parcel`. + pub fn borrowed_ref(&self) -> &BorrowedParcel<'_> { + // Safety: Parcel and BorrowedParcel are both represented in the same + // way as a NonNull<sys::AParcel> due to their use of repr(transparent), + // so casting references as done here is valid. + unsafe { + &*(self as *const Parcel as *const BorrowedParcel<'_>) + } + } } -impl Default for OwnedParcel { +impl Default for Parcel { fn default() -> Self { Self::new() } } -impl Clone for OwnedParcel { +impl Clone for Parcel { fn clone(&self) -> Self { let mut new_parcel = Self::new(); new_parcel .borrowed() - .append_all_from(&Parcel::Borrowed(self.ptr)) + .append_all_from(self.borrowed_ref()) .expect("Failed to append from Parcel"); new_parcel } } -impl<'a> std::ops::Deref for BorrowedParcel<'a> { - type Target = Parcel; - fn deref(&self) -> &Parcel { - &self.inner +impl<'a> BorrowedParcel<'a> { + /// Create a borrowed reference to a parcel object from a raw pointer. + /// + /// # Safety + /// + /// This constructor is safe if the raw pointer parameter is either null + /// (resulting in `None`), or a valid pointer to an `AParcel` object. + /// + /// Since the raw pointer is not restricted by any lifetime, the lifetime on + /// the returned `BorrowedParcel` object can be chosen arbitrarily by the + /// caller. The caller must ensure it is valid to mutably borrow the AParcel + /// for the duration of the lifetime that the caller chooses. Note that + /// since this is a mutable borrow, it must have exclusive access to the + /// AParcel for the duration of the borrow. + pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<BorrowedParcel<'a>> { + Some(Self { + ptr: NonNull::new(ptr)?, + _lifetime: PhantomData, + }) } -} -impl<'a> std::ops::DerefMut for BorrowedParcel<'a> { - fn deref_mut(&mut self) -> &mut Parcel { - &mut self.inner + + /// Get a sub-reference to this reference to the parcel. + pub fn reborrow(&mut self) -> BorrowedParcel<'_> { + // Safety: The raw pointer is a valid pointer to an AParcel, and the + // lifetime of the returned `BorrowedParcel` is tied to `self`, so the + // borrow checker will ensure that the `AParcel` can only be accessed + // via the `BorrowParcel` until it goes out of scope. + BorrowedParcel { + ptr: self.ptr, + _lifetime: PhantomData, + } } } @@ -172,63 +190,30 @@ impl<'a> std::ops::DerefMut for BorrowedParcel<'a> { /// contain a valid pointer to an `AParcel`. unsafe impl AsNative<sys::AParcel> for Parcel { fn as_native(&self) -> *const sys::AParcel { - match *self { - Self::Owned(x) | Self::Borrowed(x) => x, - } + self.ptr.as_ptr() } fn as_native_mut(&mut self) -> *mut sys::AParcel { - match *self { - Self::Owned(x) | Self::Borrowed(x) => x, - } - } -} - -impl Parcel { - /// Create a new empty `Parcel`. - /// - /// Creates a new owned empty parcel that can be written to - /// using the serialization methods and appended to and - /// from using `append_from` and `append_from_all`. - pub fn new() -> Parcel { - let parcel = unsafe { - // Safety: If `AParcel_create` succeeds, it always returns - // a valid pointer. If it fails, the process will crash. - sys::AParcel_create() - }; - assert!(!parcel.is_null()); - Self::Owned(parcel) - } - - /// Create a borrowed reference to a parcel object from a raw pointer. - /// - /// # Safety - /// - /// This constructor is safe if the raw pointer parameter is either null - /// (resulting in `None`), or a valid pointer to an `AParcel` object. - pub(crate) unsafe fn borrowed(ptr: *mut sys::AParcel) -> Option<Parcel> { - ptr.as_mut().map(|ptr| Self::Borrowed(ptr)) + self.ptr.as_ptr() } } -impl Default for Parcel { - fn default() -> Self { - Self::new() +/// # Safety +/// +/// The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` object +/// will always contain a valid pointer to an `AParcel`. +unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> { + fn as_native(&self) -> *const sys::AParcel { + self.ptr.as_ptr() } -} -impl Clone for Parcel { - fn clone(&self) -> Self { - let mut new_parcel = Self::new(); - new_parcel - .append_all_from(self) - .expect("Failed to append from Parcel"); - new_parcel + fn as_native_mut(&mut self) -> *mut sys::AParcel { + self.ptr.as_ptr() } } // Data serialization methods -impl Parcel { +impl<'a> BorrowedParcel<'a> { /// Data written to parcelable is zero'd before being deleted or reallocated. pub fn mark_sensitive(&mut self) { unsafe { @@ -237,12 +222,12 @@ impl Parcel { } } - /// Write a type that implements [`Serialize`] to the `Parcel`. + /// Write a type that implements [`Serialize`] to the parcel. pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> { parcelable.serialize(self) } - /// Writes the length of a slice to the `Parcel`. + /// Writes the length of a slice to the parcel. /// /// This is used in AIDL-generated client side code to indicate the /// allocated space for an output array parameter. @@ -255,7 +240,7 @@ impl Parcel { } } - /// Perform a series of writes to the `Parcel`, prepended with the length + /// Perform a series of writes to the parcel, prepended with the length /// (in bytes) of the written data. /// /// The length `0i32` will be written to the parcel first, followed by the @@ -269,7 +254,7 @@ impl Parcel { /// /// ``` /// # use binder::{Binder, Interface, Parcel}; - /// # let mut parcel = Parcel::Owned(std::ptr::null_mut()); + /// # let mut parcel = Parcel::new(); /// parcel.sized_write(|subparcel| { /// subparcel.write(&1u32)?; /// subparcel.write(&2u32)?; @@ -283,14 +268,14 @@ impl Parcel { /// [16i32, 1u32, 2u32, 3u32] /// ``` pub fn sized_write<F>(&mut self, f: F) -> Result<()> - where for<'a> - F: Fn(&'a WritableSubParcel<'a>) -> Result<()> + where + for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()> { let start = self.get_data_position(); self.write(&0i32)?; { - let subparcel = WritableSubParcel(RefCell::new(self)); - f(&subparcel)?; + let mut subparcel = WritableSubParcel(self.reborrow()); + f(&mut subparcel)?; } let end = self.get_data_position(); unsafe { @@ -307,8 +292,8 @@ impl Parcel { /// Returns the current position in the parcel data. pub fn get_data_position(&self) -> i32 { unsafe { - // Safety: `Parcel` always contains a valid pointer to an `AParcel`, - // and this call is otherwise safe. + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and this call is otherwise safe. sys::AParcel_getDataPosition(self.as_native()) } } @@ -316,8 +301,8 @@ impl Parcel { /// Returns the total size of the parcel. pub fn get_data_size(&self) -> i32 { unsafe { - // Safety: `Parcel` always contains a valid pointer to an `AParcel`, - // and this call is otherwise safe. + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and this call is otherwise safe. sys::AParcel_getDataSize(self.as_native()) } } @@ -335,11 +320,11 @@ impl Parcel { status_result(sys::AParcel_setDataPosition(self.as_native(), pos)) } - /// Append a subset of another `Parcel`. + /// Append a subset of another parcel. /// /// This appends `size` bytes of data from `other` starting at offset - /// `start` to the current `Parcel`, or returns an error if not possible. - pub fn append_from(&mut self, other: &Self, start: i32, size: i32) -> Result<()> { + /// `start` to the current parcel, or returns an error if not possible. + pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> { let status = unsafe { // Safety: `Parcel::appendFrom` from C++ checks that `start` // and `size` are in bounds, and returns an error otherwise. @@ -354,33 +339,125 @@ impl Parcel { status_result(status) } - /// Append the contents of another `Parcel`. - pub fn append_all_from(&mut self, other: &Self) -> Result<()> { - self.append_from(other, 0, other.get_data_size()) + /// Append the contents of another parcel. + pub fn append_all_from(&mut self, other: &impl AsNative<sys::AParcel>) -> Result<()> { + // Safety: `BorrowedParcel` always contains a valid pointer to an + // `AParcel`, and this call is otherwise safe. + let size = unsafe { sys::AParcel_getDataSize(other.as_native()) }; + self.append_from(other, 0, size) } } -/// A segment of a writable parcel, used for [`Parcel::sized_write`]. -pub struct WritableSubParcel<'a>(RefCell<&'a mut Parcel>); +/// A segment of a writable parcel, used for [`BorrowedParcel::sized_write`]. +pub struct WritableSubParcel<'a>(BorrowedParcel<'a>); impl<'a> WritableSubParcel<'a> { /// Write a type that implements [`Serialize`] to the sub-parcel. - pub fn write<S: Serialize + ?Sized>(&self, parcelable: &S) -> Result<()> { - parcelable.serialize(&mut *self.0.borrow_mut()) + pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> { + parcelable.serialize(&mut self.0) } } -// Data deserialization methods impl Parcel { - /// Attempt to read a type that implements [`Deserialize`] from this - /// `Parcel`. + /// Data written to parcelable is zero'd before being deleted or reallocated. + pub fn mark_sensitive(&mut self) { + self.borrowed().mark_sensitive() + } + + /// Write a type that implements [`Serialize`] to the parcel. + pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> { + self.borrowed().write(parcelable) + } + + /// Writes the length of a slice to the parcel. + /// + /// This is used in AIDL-generated client side code to indicate the + /// allocated space for an output array parameter. + pub fn write_slice_size<T>(&mut self, slice: Option<&[T]>) -> Result<()> { + self.borrowed().write_slice_size(slice) + } + + /// Perform a series of writes to the parcel, prepended with the length + /// (in bytes) of the written data. + /// + /// The length `0i32` will be written to the parcel first, followed by the + /// writes performed by the callback. The initial length will then be + /// updated to the length of all data written by the callback, plus the + /// size of the length elemement itself (4 bytes). + /// + /// # Examples + /// + /// After the following call: + /// + /// ``` + /// # use binder::{Binder, Interface, Parcel}; + /// # let mut parcel = Parcel::new(); + /// parcel.sized_write(|subparcel| { + /// subparcel.write(&1u32)?; + /// subparcel.write(&2u32)?; + /// subparcel.write(&3u32) + /// }); + /// ``` + /// + /// `parcel` will contain the following: + /// + /// ```ignore + /// [16i32, 1u32, 2u32, 3u32] + /// ``` + pub fn sized_write<F>(&mut self, f: F) -> Result<()> + where + for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()> + { + self.borrowed().sized_write(f) + } + + /// Returns the current position in the parcel data. + pub fn get_data_position(&self) -> i32 { + self.borrowed_ref().get_data_position() + } + + /// Returns the total size of the parcel. + pub fn get_data_size(&self) -> i32 { + self.borrowed_ref().get_data_size() + } + + /// Move the current read/write position in the parcel. + /// + /// # Safety + /// + /// This method is safe if `pos` is less than the current size of the parcel + /// data buffer. Otherwise, we are relying on correct bounds checking in the + /// Parcel C++ code on every subsequent read or write to this parcel. If all + /// accesses are bounds checked, this call is still safe, but we can't rely + /// on that. + pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> { + self.borrowed_ref().set_data_position(pos) + } + + /// Append a subset of another parcel. + /// + /// This appends `size` bytes of data from `other` starting at offset + /// `start` to the current parcel, or returns an error if not possible. + pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> { + self.borrowed().append_from(other, start, size) + } + + /// Append the contents of another parcel. + pub fn append_all_from(&mut self, other: &impl AsNative<sys::AParcel>) -> Result<()> { + self.borrowed().append_all_from(other) + } +} + +// Data deserialization methods +impl<'a> BorrowedParcel<'a> { + /// Attempt to read a type that implements [`Deserialize`] from this parcel. pub fn read<D: Deserialize>(&self) -> Result<D> { D::deserialize(self) } - /// Attempt to read a type that implements [`Deserialize`] from this - /// `Parcel` onto an existing value. This operation will overwrite the old - /// value partially or completely, depending on how much data is available. + /// Attempt to read a type that implements [`Deserialize`] from this parcel + /// onto an existing value. This operation will overwrite the old value + /// partially or completely, depending on how much data is available. pub fn read_onto<D: Deserialize>(&self, x: &mut D) -> Result<()> { x.deserialize_from(self) } @@ -413,9 +490,9 @@ impl Parcel { /// }); /// ``` /// - pub fn sized_read<F>(&self, mut f: F) -> Result<()> + pub fn sized_read<F>(&self, f: F) -> Result<()> where - for<'a> F: FnMut(ReadableSubParcel<'a>) -> Result<()> + for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()> { let start = self.get_data_position(); let parcelable_size: i32 = self.read()?; @@ -430,7 +507,10 @@ impl Parcel { } let subparcel = ReadableSubParcel { - parcel: self, + parcel: BorrowedParcel { + ptr: self.ptr, + _lifetime: PhantomData, + }, end_position: end, }; f(subparcel)?; @@ -444,8 +524,8 @@ impl Parcel { Ok(()) } - /// Read a vector size from the `Parcel` and resize the given output vector - /// to be correctly sized for that amount of data. + /// Read a vector size from the parcel and resize the given output vector to + /// be correctly sized for that amount of data. /// /// This method is used in AIDL-generated server side code for methods that /// take a mutable slice reference parameter. @@ -463,7 +543,7 @@ impl Parcel { Ok(()) } - /// Read a vector size from the `Parcel` and either create a correctly sized + /// Read a vector size from the parcel and either create a correctly sized /// vector for that amount of data or set the output parameter to None if /// the vector should be null. /// @@ -491,7 +571,7 @@ impl Parcel { /// A segment of a readable parcel, used for [`Parcel::sized_read`]. pub struct ReadableSubParcel<'a> { - parcel: &'a Parcel, + parcel: BorrowedParcel<'a>, end_position: i32, } @@ -501,7 +581,7 @@ impl<'a> ReadableSubParcel<'a> { // The caller should have checked this, // but it can't hurt to double-check assert!(self.has_more_data()); - D::deserialize(self.parcel) + D::deserialize(&self.parcel) } /// Check if the sub-parcel has more data to read @@ -510,11 +590,82 @@ impl<'a> ReadableSubParcel<'a> { } } -// Internal APIs impl Parcel { + /// Attempt to read a type that implements [`Deserialize`] from this parcel. + pub fn read<D: Deserialize>(&self) -> Result<D> { + self.borrowed_ref().read() + } + + /// Attempt to read a type that implements [`Deserialize`] from this parcel + /// onto an existing value. This operation will overwrite the old value + /// partially or completely, depending on how much data is available. + pub fn read_onto<D: Deserialize>(&self, x: &mut D) -> Result<()> { + self.borrowed_ref().read_onto(x) + } + + /// Safely read a sized parcelable. + /// + /// Read the size of a parcelable, compute the end position + /// of that parcelable, then build a sized readable sub-parcel + /// and call a closure with the sub-parcel as its parameter. + /// The closure can keep reading data from the sub-parcel + /// until it runs out of input data. The closure is responsible + /// for calling [`ReadableSubParcel::has_more_data`] to check for + /// more data before every read, at least until Rust generators + /// are stabilized. + /// After the closure returns, skip to the end of the current + /// parcelable regardless of how much the closure has read. + /// + /// # Examples + /// + /// ```no_run + /// let mut parcelable = Default::default(); + /// parcel.sized_read(|subparcel| { + /// if subparcel.has_more_data() { + /// parcelable.a = subparcel.read()?; + /// } + /// if subparcel.has_more_data() { + /// parcelable.b = subparcel.read()?; + /// } + /// Ok(()) + /// }); + /// ``` + /// + pub fn sized_read<F>(&self, f: F) -> Result<()> + where + for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()> + { + self.borrowed_ref().sized_read(f) + } + + /// Read a vector size from the parcel and resize the given output vector to + /// be correctly sized for that amount of data. + /// + /// This method is used in AIDL-generated server side code for methods that + /// take a mutable slice reference parameter. + pub fn resize_out_vec<D: Default + Deserialize>(&self, out_vec: &mut Vec<D>) -> Result<()> { + self.borrowed_ref().resize_out_vec(out_vec) + } + + /// Read a vector size from the parcel and either create a correctly sized + /// vector for that amount of data or set the output parameter to None if + /// the vector should be null. + /// + /// This method is used in AIDL-generated server side code for methods that + /// take a mutable slice reference parameter. + pub fn resize_nullable_out_vec<D: Default + Deserialize>( + &self, + out_vec: &mut Option<Vec<D>>, + ) -> Result<()> { + self.borrowed_ref().resize_nullable_out_vec(out_vec) + } +} + +// Internal APIs +impl<'a> BorrowedParcel<'a> { pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> { unsafe { - // Safety: `Parcel` always contains a valid pointer to an + // Safety: `BorrowedParcel` always contains a valid pointer to an // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return // null or a valid pointer to an `AIBinder`, both of which are // valid, safe inputs to `AParcel_writeStrongBinder`. @@ -534,7 +685,7 @@ impl Parcel { pub(crate) fn read_binder(&self) -> Result<Option<SpIBinder>> { let mut binder = ptr::null_mut(); let status = unsafe { - // Safety: `Parcel` always contains a valid pointer to an + // Safety: `BorrowedParcel` always contains a valid pointer to an // `AParcel`. We pass a valid, mutable out pointer to the `binder` // parameter. After this call, `binder` will be either null or a // valid pointer to an `AIBinder` owned by the caller. @@ -554,25 +705,11 @@ impl Parcel { impl Drop for Parcel { fn drop(&mut self) { // Run the C++ Parcel complete object destructor - if let Self::Owned(ptr) = *self { - unsafe { - // Safety: `Parcel` always contains a valid pointer to an - // `AParcel`. If we own the parcel, we can safely delete it - // here. - sys::AParcel_delete(ptr) - } - } - } -} - -impl Drop for OwnedParcel { - fn drop(&mut self) { - // Run the C++ Parcel complete object destructor unsafe { - // Safety: `OwnedParcel` always contains a valid pointer to an + // Safety: `Parcel` always contains a valid pointer to an // `AParcel`. Since we own the parcel, we can safely delete it // here. - sys::AParcel_delete(self.ptr) + sys::AParcel_delete(self.ptr.as_ptr()) } } } @@ -584,9 +721,9 @@ impl fmt::Debug for Parcel { } } -impl fmt::Debug for OwnedParcel { +impl<'a> fmt::Debug for BorrowedParcel<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OwnedParcel") + f.debug_struct("BorrowedParcel") .finish() } } @@ -608,7 +745,7 @@ fn test_read_write() { assert_eq!(parcel.read::<Option<String>>(), Ok(None)); assert_eq!(parcel.read::<String>(), Err(StatusCode::UNEXPECTED_NULL)); - assert_eq!(parcel.read_binder().err(), Some(StatusCode::BAD_TYPE)); + assert_eq!(parcel.borrowed_ref().read_binder().err(), Some(StatusCode::BAD_TYPE)); parcel.write(&1i32).unwrap(); diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs index 8bcc5d0092..b0dea945e0 100644 --- a/libs/binder/rust/src/parcel/file_descriptor.rs +++ b/libs/binder/rust/src/parcel/file_descriptor.rs @@ -15,7 +15,7 @@ */ use super::{ - Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray, + Deserialize, DeserializeArray, DeserializeOption, BorrowedParcel, Serialize, SerializeArray, SerializeOption, }; use crate::binder::AsNative; @@ -61,7 +61,7 @@ impl IntoRawFd for ParcelFileDescriptor { } impl Serialize for ParcelFileDescriptor { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { let fd = self.0.as_raw_fd(); let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -78,7 +78,7 @@ impl Serialize for ParcelFileDescriptor { impl SerializeArray for ParcelFileDescriptor {} impl SerializeOption for ParcelFileDescriptor { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { if let Some(f) = this { f.serialize(parcel) } else { @@ -95,7 +95,7 @@ impl SerializeOption for ParcelFileDescriptor { } impl DeserializeOption for ParcelFileDescriptor { - fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { let mut fd = -1i32; unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -125,7 +125,7 @@ impl DeserializeOption for ParcelFileDescriptor { } impl Deserialize for ParcelFileDescriptor { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Deserialize::deserialize(parcel) .transpose() .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs index ec00e1dc86..9007cbafc2 100644 --- a/libs/binder/rust/src/parcel/parcelable.rs +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -16,14 +16,14 @@ use crate::binder::{AsNative, FromIBinder, Stability, Strong}; use crate::error::{status_result, status_t, Result, Status, StatusCode}; -use crate::parcel::Parcel; +use crate::parcel::BorrowedParcel; use crate::proxy::SpIBinder; use crate::sys; use std::convert::{TryFrom, TryInto}; use std::ffi::c_void; use std::os::raw::{c_char, c_ulong}; -use std::mem::{self, MaybeUninit}; +use std::mem::{self, MaybeUninit, ManuallyDrop}; use std::ptr; use std::slice; @@ -39,7 +39,7 @@ pub trait Parcelable { /// `Serialize::serialize` and its variants are generally /// preferred over this function, since the former also /// prepend a header. - fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()>; + fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>; /// Internal deserialization function for parcelables. /// @@ -47,26 +47,26 @@ pub trait Parcelable { /// `Deserialize::deserialize` and its variants are generally /// preferred over this function, since the former also /// parse the additional header. - fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()>; + fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()>; } /// A struct whose instances can be written to a [`Parcel`]. // Might be able to hook this up as a serde backend in the future? pub trait Serialize { /// Serialize this instance into the given [`Parcel`]. - fn serialize(&self, parcel: &mut Parcel) -> Result<()>; + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>; } /// A struct whose instances can be restored from a [`Parcel`]. // Might be able to hook this up as a serde backend in the future? pub trait Deserialize: Sized { /// Deserialize an instance from the given [`Parcel`]. - fn deserialize(parcel: &Parcel) -> Result<Self>; + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self>; /// Deserialize an instance from the given [`Parcel`] onto the /// current object. This operation will overwrite the old value /// partially or completely, depending on how much data is available. - fn deserialize_from(&mut self, parcel: &Parcel) -> Result<()> { + fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> { *self = Self::deserialize(parcel)?; Ok(()) } @@ -80,8 +80,8 @@ pub trait Deserialize: Sized { // We want the default implementation for most types, but an override for // a few special ones like `readByteArray` for `u8`. pub trait SerializeArray: Serialize + Sized { - /// Serialize an array of this type into the given [`Parcel`]. - fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + /// Serialize an array of this type into the given parcel. + fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { let res = unsafe { // Safety: Safe FFI, slice will always be a safe pointer to pass. sys::AParcel_writeParcelableArray( @@ -111,7 +111,7 @@ unsafe extern "C" fn serialize_element<T: Serialize>( let slice: &[T] = slice::from_raw_parts(array.cast(), index+1); - let mut parcel = match Parcel::borrowed(parcel) { + let mut parcel = match BorrowedParcel::from_raw(parcel) { None => return StatusCode::UNEXPECTED_NULL as status_t, Some(p) => p, }; @@ -126,8 +126,8 @@ unsafe extern "C" fn serialize_element<T: Serialize>( /// Defaults to calling Deserialize::deserialize() manually for every element, /// but can be overridden for custom implementations like `readByteArray`. pub trait DeserializeArray: Deserialize { - /// Deserialize an array of type from the given [`Parcel`]. - fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> { + /// Deserialize an array of type from the given parcel. + fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> { let mut vec: Option<Vec<MaybeUninit<Self>>> = None; let res = unsafe { // Safety: Safe FFI, vec is the correct opaque type expected by @@ -173,7 +173,7 @@ unsafe extern "C" fn deserialize_element<T: Deserialize>( None => return StatusCode::BAD_INDEX as status_t, }; - let parcel = match Parcel::borrowed(parcel as *mut _) { + let parcel = match BorrowedParcel::from_raw(parcel as *mut _) { None => return StatusCode::UNEXPECTED_NULL as status_t, Some(p) => p, }; @@ -205,8 +205,8 @@ pub const NULL_PARCELABLE_FLAG: i32 = 0; // We also use it to provide a default implementation for AIDL-generated // parcelables. pub trait SerializeOption: Serialize { - /// Serialize an Option of this type into the given [`Parcel`]. - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + /// Serialize an Option of this type into the given parcel. + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { if let Some(inner) = this { parcel.write(&NON_NULL_PARCELABLE_FLAG)?; parcel.write(inner) @@ -218,8 +218,8 @@ pub trait SerializeOption: Serialize { /// Helper trait for types that can be nullable when deserialized. pub trait DeserializeOption: Deserialize { - /// Deserialize an Option of this type from the given [`Parcel`]. - fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + /// Deserialize an Option of this type from the given parcel. + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { let null: i32 = parcel.read()?; if null == NULL_PARCELABLE_FLAG { Ok(None) @@ -228,10 +228,10 @@ pub trait DeserializeOption: Deserialize { } } - /// Deserialize an Option of this type from the given [`Parcel`] onto the + /// Deserialize an Option of this type from the given parcel onto the /// current object. This operation will overwrite the current value /// partially or completely, depending on how much data is available. - fn deserialize_option_from(this: &mut Option<Self>, parcel: &Parcel) -> Result<()> { + fn deserialize_option_from(this: &mut Option<Self>, parcel: &BorrowedParcel<'_>) -> Result<()> { *this = Self::deserialize_option(parcel)?; Ok(()) } @@ -297,10 +297,23 @@ macro_rules! parcelable_primitives { }; } +/// Safety: All elements in the vector must be properly initialized. +unsafe fn vec_assume_init<T>(vec: Vec<MaybeUninit<T>>) -> Vec<T> { + // We can convert from Vec<MaybeUninit<T>> to Vec<T> because MaybeUninit<T> + // has the same alignment and size as T, so the pointer to the vector + // allocation will be compatible. + let mut vec = ManuallyDrop::new(vec); + Vec::from_raw_parts( + vec.as_mut_ptr().cast(), + vec.len(), + vec.capacity(), + ) +} + macro_rules! impl_parcelable { {Serialize, $ty:ty, $write_fn:path} => { impl Serialize for $ty { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { unsafe { // Safety: `Parcel` always contains a valid pointer to an // `AParcel`, and any `$ty` literal value is safe to pass to @@ -313,7 +326,7 @@ macro_rules! impl_parcelable { {Deserialize, $ty:ty, $read_fn:path} => { impl Deserialize for $ty { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let mut val = Self::default(); unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -329,7 +342,7 @@ macro_rules! impl_parcelable { {SerializeArray, $ty:ty, $write_array_fn:path} => { impl SerializeArray for $ty { - fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` @@ -353,7 +366,7 @@ macro_rules! impl_parcelable { {DeserializeArray, $ty:ty, $read_array_fn:path} => { impl DeserializeArray for $ty { - fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> { + fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> { let mut vec: Option<Vec<MaybeUninit<Self>>> = None; let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -371,11 +384,8 @@ macro_rules! impl_parcelable { // Safety: We are assuming that the NDK correctly // initialized every element of the vector by now, so we // know that all the MaybeUninits are now properly - // initialized. We can transmute from Vec<MaybeUninit<T>> to - // Vec<T> because MaybeUninit<T> has the same alignment and - // size as T, so the pointer to the vector allocation will - // be compatible. - mem::transmute(vec) + // initialized. + vec.map(|vec| vec_assume_init(vec)) }; Ok(vec) } @@ -443,19 +453,19 @@ impl SerializeArray for bool {} impl DeserializeArray for bool {} impl Serialize for u8 { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { (*self as i8).serialize(parcel) } } impl Deserialize for u8 { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { i8::deserialize(parcel).map(|v| v as u8) } } impl SerializeArray for u8 { - fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a @@ -474,19 +484,19 @@ impl SerializeArray for u8 { } impl Serialize for i16 { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { (*self as u16).serialize(parcel) } } impl Deserialize for i16 { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { u16::deserialize(parcel).map(|v| v as i16) } } impl SerializeArray for i16 { - fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> { let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a @@ -505,7 +515,7 @@ impl SerializeArray for i16 { } impl SerializeOption for str { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { match this { None => unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -541,7 +551,7 @@ impl SerializeOption for str { } impl Serialize for str { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { Some(self).serialize(parcel) } } @@ -549,7 +559,7 @@ impl Serialize for str { impl SerializeArray for &str {} impl Serialize for String { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { Some(self.as_str()).serialize(parcel) } } @@ -557,13 +567,13 @@ impl Serialize for String { impl SerializeArray for String {} impl SerializeOption for String { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(this.map(String::as_str), parcel) } } impl Deserialize for Option<String> { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let mut vec: Option<Vec<u8>> = None; let status = unsafe { // Safety: `Parcel` always contains a valid pointer to an `AParcel`. @@ -591,7 +601,7 @@ impl Deserialize for Option<String> { impl DeserializeArray for Option<String> {} impl Deserialize for String { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Deserialize::deserialize(parcel) .transpose() .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) @@ -601,19 +611,19 @@ impl Deserialize for String { impl DeserializeArray for String {} impl<T: SerializeArray> Serialize for [T] { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeArray::serialize_array(self, parcel) } } impl<T: SerializeArray> Serialize for Vec<T> { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeArray::serialize_array(&self[..], parcel) } } impl<T: SerializeArray> SerializeOption for [T] { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { if let Some(v) = this { SerializeArray::serialize_array(v, parcel) } else { @@ -623,13 +633,13 @@ impl<T: SerializeArray> SerializeOption for [T] { } impl<T: SerializeArray> SerializeOption for Vec<T> { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(this.map(Vec::as_slice), parcel) } } impl<T: DeserializeArray> Deserialize for Vec<T> { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { DeserializeArray::deserialize_array(parcel) .transpose() .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) @@ -637,25 +647,25 @@ impl<T: DeserializeArray> Deserialize for Vec<T> { } impl<T: DeserializeArray> DeserializeOption for Vec<T> { - fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { DeserializeArray::deserialize_array(parcel) } } impl Serialize for Stability { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { i32::from(*self).serialize(parcel) } } impl Deserialize for Stability { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { i32::deserialize(parcel).and_then(Stability::try_from) } } impl Serialize for Status { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { unsafe { // Safety: `Parcel` always contains a valid pointer to an `AParcel` // and `Status` always contains a valid pointer to an `AStatus`, so @@ -670,7 +680,7 @@ impl Serialize for Status { } impl Deserialize for Status { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let mut status_ptr = ptr::null_mut(); let ret_status = unsafe { // Safety: `Parcel` always contains a valid pointer to an @@ -691,56 +701,60 @@ impl Deserialize for Status { } impl<T: Serialize + FromIBinder + ?Sized> Serialize for Strong<T> { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { Serialize::serialize(&**self, parcel) } } impl<T: SerializeOption + FromIBinder + ?Sized> SerializeOption for Strong<T> { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(this.map(|b| &**b), parcel) } } +impl<T: Serialize + FromIBinder + ?Sized> SerializeArray for Strong<T> {} + impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { let ibinder: SpIBinder = parcel.read()?; FromIBinder::try_from(ibinder) } } impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> { - fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { let ibinder: Option<SpIBinder> = parcel.read()?; ibinder.map(FromIBinder::try_from).transpose() } } +impl<T: FromIBinder + ?Sized> DeserializeArray for Strong<T> {} + // We need these to support Option<&T> for all T impl<T: Serialize + ?Sized> Serialize for &T { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { Serialize::serialize(*self, parcel) } } impl<T: SerializeOption + ?Sized> SerializeOption for &T { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(this.copied(), parcel) } } impl<T: SerializeOption> Serialize for Option<T> { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(self.as_ref(), parcel) } } impl<T: DeserializeOption> Deserialize for Option<T> { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { DeserializeOption::deserialize_option(parcel) } - fn deserialize_from(&mut self, parcel: &Parcel) -> Result<()> { + fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> { DeserializeOption::deserialize_option_from(self, parcel) } } @@ -758,7 +772,7 @@ macro_rules! impl_serialize_for_parcelable { impl $crate::parcel::Serialize for $parcelable { fn serialize( &self, - parcel: &mut $crate::parcel::Parcel, + parcel: &mut $crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<()> { <Self as $crate::parcel::SerializeOption>::serialize_option( Some(self), @@ -772,7 +786,7 @@ macro_rules! impl_serialize_for_parcelable { impl $crate::parcel::SerializeOption for $parcelable { fn serialize_option( this: Option<&Self>, - parcel: &mut $crate::parcel::Parcel, + parcel: &mut $crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<()> { if let Some(this) = this { use $crate::parcel::Parcelable; @@ -792,13 +806,12 @@ macro_rules! impl_serialize_for_parcelable { /// `Deserialize`, `DeserializeArray` and `DeserializeOption` for /// structured parcelables. The target type must implement the /// `Parcelable` trait. -/// ``` #[macro_export] macro_rules! impl_deserialize_for_parcelable { ($parcelable:ident) => { impl $crate::parcel::Deserialize for $parcelable { fn deserialize( - parcel: &$crate::parcel::Parcel, + parcel: &$crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<Self> { $crate::parcel::DeserializeOption::deserialize_option(parcel) .transpose() @@ -806,7 +819,7 @@ macro_rules! impl_deserialize_for_parcelable { } fn deserialize_from( &mut self, - parcel: &$crate::parcel::Parcel, + parcel: &$crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<()> { let status: i32 = parcel.read()?; if status == $crate::parcel::NULL_PARCELABLE_FLAG { @@ -822,7 +835,7 @@ macro_rules! impl_deserialize_for_parcelable { impl $crate::parcel::DeserializeOption for $parcelable { fn deserialize_option( - parcel: &$crate::parcel::Parcel, + parcel: &$crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<Option<Self>> { let mut result = None; Self::deserialize_option_from(&mut result, parcel)?; @@ -830,7 +843,7 @@ macro_rules! impl_deserialize_for_parcelable { } fn deserialize_option_from( this: &mut Option<Self>, - parcel: &$crate::parcel::Parcel, + parcel: &$crate::parcel::BorrowedParcel<'_>, ) -> $crate::Result<()> { let status: i32 = parcel.read()?; if status == $crate::parcel::NULL_PARCELABLE_FLAG { @@ -847,326 +860,332 @@ macro_rules! impl_deserialize_for_parcelable { } impl<T: Serialize> Serialize for Box<T> { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { Serialize::serialize(&**self, parcel) } } impl<T: Deserialize> Deserialize for Box<T> { - fn deserialize(parcel: &Parcel) -> Result<Self> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { Deserialize::deserialize(parcel).map(Box::new) } } impl<T: SerializeOption> SerializeOption for Box<T> { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { SerializeOption::serialize_option(this.map(|inner| &**inner), parcel) } } impl<T: DeserializeOption> DeserializeOption for Box<T> { - fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> { DeserializeOption::deserialize_option(parcel).map(|t| t.map(Box::new)) } } -#[test] -fn test_custom_parcelable() { - struct Custom(u32, bool, String, Vec<String>); - - impl Serialize for Custom { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { - self.0.serialize(parcel)?; - self.1.serialize(parcel)?; - self.2.serialize(parcel)?; - self.3.serialize(parcel) +#[cfg(test)] +mod tests { + use crate::Parcel; + use super::*; + + #[test] + fn test_custom_parcelable() { + struct Custom(u32, bool, String, Vec<String>); + + impl Serialize for Custom { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { + self.0.serialize(parcel)?; + self.1.serialize(parcel)?; + self.2.serialize(parcel)?; + self.3.serialize(parcel) + } } - } - impl Deserialize for Custom { - fn deserialize(parcel: &Parcel) -> Result<Self> { - Ok(Custom( - parcel.read()?, - parcel.read()?, - parcel.read()?, - parcel.read::<Option<Vec<String>>>()?.unwrap(), - )) + impl Deserialize for Custom { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> { + Ok(Custom( + parcel.read()?, + parcel.read()?, + parcel.read()?, + parcel.read::<Option<Vec<String>>>()?.unwrap(), + )) + } } - } - let string8 = "Custom Parcelable".to_string(); + let string8 = "Custom Parcelable".to_string(); - let s1 = "str1".to_string(); - let s2 = "str2".to_string(); - let s3 = "str3".to_string(); + let s1 = "str1".to_string(); + let s2 = "str2".to_string(); + let s3 = "str3".to_string(); - let strs = vec![s1, s2, s3]; + let strs = vec![s1, s2, s3]; - let custom = Custom(123_456_789, true, string8, strs); + let custom = Custom(123_456_789, true, string8, strs); - let mut parcel = Parcel::new(); - let start = parcel.get_data_position(); + let mut parcel = Parcel::new(); + let start = parcel.get_data_position(); - assert!(custom.serialize(&mut parcel).is_ok()); + assert!(custom.serialize(&mut parcel.borrowed()).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let custom2 = Custom::deserialize(&parcel).unwrap(); + let custom2 = Custom::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(custom2.0, 123_456_789); - assert!(custom2.1); - assert_eq!(custom2.2, custom.2); - assert_eq!(custom2.3, custom.3); -} + assert_eq!(custom2.0, 123_456_789); + assert!(custom2.1); + assert_eq!(custom2.2, custom.2); + assert_eq!(custom2.3, custom.3); + } -#[test] -#[allow(clippy::excessive_precision)] -fn test_slice_parcelables() { - let bools = [true, false, false, true]; + #[test] + #[allow(clippy::excessive_precision)] + fn test_slice_parcelables() { + let bools = [true, false, false, true]; - let mut parcel = Parcel::new(); - let start = parcel.get_data_position(); + let mut parcel = Parcel::new(); + let start = parcel.get_data_position(); - assert!(bools.serialize(&mut parcel).is_ok()); + assert!(bools.serialize(&mut parcel.borrowed()).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); - assert_eq!(parcel.read::<u32>().unwrap(), 1); - assert_eq!(parcel.read::<u32>().unwrap(), 0); - assert_eq!(parcel.read::<u32>().unwrap(), 0); - assert_eq!(parcel.read::<u32>().unwrap(), 1); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); + assert_eq!(parcel.read::<u32>().unwrap(), 1); + assert_eq!(parcel.read::<u32>().unwrap(), 0); + assert_eq!(parcel.read::<u32>().unwrap(), 0); + assert_eq!(parcel.read::<u32>().unwrap(), 1); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<bool>::deserialize(&parcel).unwrap(); + let vec = Vec::<bool>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [true, false, false, true]); + assert_eq!(vec, [true, false, false, true]); - let u8s = [101u8, 255, 42, 117]; + let u8s = [101u8, 255, 42, 117]; - let mut parcel = Parcel::new(); - let start = parcel.get_data_position(); + let mut parcel = Parcel::new(); + let start = parcel.get_data_position(); - assert!(parcel.write(&u8s[..]).is_ok()); + assert!(parcel.write(&u8s[..]).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<u8>::deserialize(&parcel).unwrap(); - assert_eq!(vec, [101, 255, 42, 117]); + let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap(); + assert_eq!(vec, [101, 255, 42, 117]); - let i8s = [-128i8, 127, 42, -117]; + let i8s = [-128i8, 127, 42, -117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert!(parcel.write(&i8s[..]).is_ok()); + assert!(parcel.write(&i8s[..]).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<u8>::deserialize(&parcel).unwrap(); - assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]); + let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap(); + assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]); - let u16s = [u16::max_value(), 12_345, 42, 117]; + let u16s = [u16::max_value(), 12_345, 42, 117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(u16s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(u16s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value() - assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 - assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 - assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<u16>::deserialize(&parcel).unwrap(); + let vec = Vec::<u16>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]); + assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]); - let i16s = [i16::max_value(), i16::min_value(), 42, -117]; + let i16s = [i16::max_value(), i16::min_value(), 42, -117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(i16s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(i16s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value() - assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value() - assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 - assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117 - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value() + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<i16>::deserialize(&parcel).unwrap(); + let vec = Vec::<i16>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]); + assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]); - let u32s = [u32::max_value(), 12_345, 42, 117]; + let u32s = [u32::max_value(), 12_345, 42, 117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(u32s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(u32s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value() - assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 - assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 - assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<u32>::deserialize(&parcel).unwrap(); + let vec = Vec::<u32>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]); + assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]); - let i32s = [i32::max_value(), i32::min_value(), 42, -117]; + let i32s = [i32::max_value(), i32::min_value(), 42, -117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(i32s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(i32s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items - assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value() - assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value() - assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 - assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117 - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value() + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<i32>::deserialize(&parcel).unwrap(); + let vec = Vec::<i32>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]); + assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]); - let u64s = [u64::max_value(), 12_345, 42, 117]; + let u64s = [u64::max_value(), 12_345, 42, 117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(u64s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(u64s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<u64>::deserialize(&parcel).unwrap(); + let vec = Vec::<u64>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]); + assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]); - let i64s = [i64::max_value(), i64::min_value(), 42, -117]; + let i64s = [i64::max_value(), i64::min_value(), 42, -117]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(i64s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(i64s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<i64>::deserialize(&parcel).unwrap(); + let vec = Vec::<i64>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]); + assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]); - let f32s = [ - std::f32::NAN, - std::f32::INFINITY, - 1.23456789, - std::f32::EPSILON, - ]; + let f32s = [ + std::f32::NAN, + std::f32::INFINITY, + 1.23456789, + std::f32::EPSILON, + ]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(f32s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(f32s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<f32>::deserialize(&parcel).unwrap(); + let vec = Vec::<f32>::deserialize(parcel.borrowed_ref()).unwrap(); - // NAN != NAN so we can't use it in the assert_eq: - assert!(vec[0].is_nan()); - assert_eq!(vec[1..], f32s[1..]); + // NAN != NAN so we can't use it in the assert_eq: + assert!(vec[0].is_nan()); + assert_eq!(vec[1..], f32s[1..]); - let f64s = [ - std::f64::NAN, - std::f64::INFINITY, - 1.234567890123456789, - std::f64::EPSILON, - ]; + let f64s = [ + std::f64::NAN, + std::f64::INFINITY, + 1.234567890123456789, + std::f64::EPSILON, + ]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(f64s.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(f64s.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<f64>::deserialize(&parcel).unwrap(); + let vec = Vec::<f64>::deserialize(parcel.borrowed_ref()).unwrap(); - // NAN != NAN so we can't use it in the assert_eq: - assert!(vec[0].is_nan()); - assert_eq!(vec[1..], f64s[1..]); + // NAN != NAN so we can't use it in the assert_eq: + assert!(vec[0].is_nan()); + assert_eq!(vec[1..], f64s[1..]); - let s1 = "Hello, Binder!"; - let s2 = "This is a utf8 string."; - let s3 = "Some more text here."; - let s4 = "Embedded nulls \0 \0"; + let s1 = "Hello, Binder!"; + let s2 = "This is a utf8 string."; + let s3 = "Some more text here."; + let s4 = "Embedded nulls \0 \0"; - let strs = [s1, s2, s3, s4]; + let strs = [s1, s2, s3, s4]; - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } - assert!(strs.serialize(&mut parcel).is_ok()); - unsafe { - assert!(parcel.set_data_position(start).is_ok()); - } + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(strs.serialize(&mut parcel.borrowed()).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } - let vec = Vec::<String>::deserialize(&parcel).unwrap(); + let vec = Vec::<String>::deserialize(parcel.borrowed_ref()).unwrap(); - assert_eq!(vec, strs); + assert_eq!(vec, strs); + } } diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs index bccfd2d25e..b4282b2ec8 100644 --- a/libs/binder/rust/src/parcel/parcelable_holder.rs +++ b/libs/binder/rust/src/parcel/parcelable_holder.rs @@ -16,7 +16,7 @@ use crate::binder::Stability; use crate::error::{Result, StatusCode}; -use crate::parcel::{OwnedParcel, Parcel, Parcelable}; +use crate::parcel::{Parcel, BorrowedParcel, Parcelable}; use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable}; use downcast_rs::{impl_downcast, DowncastSync}; @@ -50,7 +50,7 @@ enum ParcelableHolderData { parcelable: Arc<dyn AnyParcelable>, name: String, }, - Parcel(OwnedParcel), + Parcel(Parcel), } impl Default for ParcelableHolderData { @@ -148,7 +148,6 @@ impl ParcelableHolder { } } ParcelableHolderData::Parcel(ref mut parcel) => { - let parcel = parcel.borrowed(); unsafe { // Safety: 0 should always be a valid position. parcel.set_data_position(0)?; @@ -160,7 +159,7 @@ impl ParcelableHolder { } let mut parcelable = T::default(); - parcelable.read_from_parcel(&parcel)?; + parcelable.read_from_parcel(parcel.borrowed_ref())?; let parcelable = Arc::new(parcelable); let result = Arc::clone(&parcelable); @@ -181,7 +180,7 @@ impl_serialize_for_parcelable!(ParcelableHolder); impl_deserialize_for_parcelable!(ParcelableHolder); impl Parcelable for ParcelableHolder { - fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()> { + fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { parcel.write(&self.stability)?; let mut data = self.data.lock().unwrap(); @@ -214,14 +213,13 @@ impl Parcelable for ParcelableHolder { Ok(()) } ParcelableHolderData::Parcel(ref mut p) => { - let p = p.borrowed(); parcel.write(&p.get_data_size())?; - parcel.append_all_from(&p) + parcel.append_all_from(&*p) } } } - fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()> { + fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> { self.stability = parcel.read()?; let data_size: i32 = parcel.read()?; @@ -242,10 +240,8 @@ impl Parcelable for ParcelableHolder { .checked_add(data_size) .ok_or(StatusCode::BAD_VALUE)?; - let mut new_parcel = OwnedParcel::new(); - new_parcel - .borrowed() - .append_from(parcel, data_start, data_size)?; + let mut new_parcel = Parcel::new(); + new_parcel.append_from(parcel, data_start, data_size)?; *self.data.get_mut().unwrap() = ParcelableHolderData::Parcel(new_parcel); unsafe { diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index a8d0c33034..760d862c53 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -22,8 +22,7 @@ use crate::binder::{ }; use crate::error::{status_result, Result, StatusCode}; use crate::parcel::{ - Deserialize, DeserializeArray, DeserializeOption, OwnedParcel, Parcel, Serialize, SerializeArray, - SerializeOption, + Parcel, BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption, }; use crate::sys; @@ -235,7 +234,7 @@ impl Drop for SpIBinder { } impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { - fn prepare_transact(&self) -> Result<OwnedParcel> { + fn prepare_transact(&self) -> Result<Parcel> { let mut input = ptr::null_mut(); let status = unsafe { // Safety: `SpIBinder` guarantees that `self` always contains a @@ -255,16 +254,16 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { // Safety: At this point, `input` is either a valid, owned `AParcel` // pointer, or null. `OwnedParcel::from_raw` safely handles both cases, // taking ownership of the parcel. - OwnedParcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL) + Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL) } } fn submit_transact( &self, code: TransactionCode, - data: OwnedParcel, + data: Parcel, flags: TransactionFlags, - ) -> Result<OwnedParcel> { + ) -> Result<Parcel> { let mut reply = ptr::null_mut(); let status = unsafe { // Safety: `SpIBinder` guarantees that `self` always contains a @@ -299,7 +298,7 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { // construct a `Parcel` out of it. `AIBinder_transact` passes // ownership of the `reply` parcel to Rust, so we need to // construct an owned variant. - OwnedParcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL) + Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL) } } @@ -324,6 +323,7 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { status_result(status) } + #[cfg(not(android_vndk))] fn set_requesting_sid(&mut self, enable: bool) { unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) }; } @@ -415,13 +415,13 @@ impl<T: AsNative<sys::AIBinder>> IBinder for T { } impl Serialize for SpIBinder { - fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> { parcel.write_binder(Some(self)) } } impl SerializeOption for SpIBinder { - fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> { parcel.write_binder(this) } } @@ -429,7 +429,7 @@ impl SerializeOption for SpIBinder { impl SerializeArray for SpIBinder {} impl Deserialize for SpIBinder { - fn deserialize(parcel: &Parcel) -> Result<SpIBinder> { + fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<SpIBinder> { parcel .read_binder() .transpose() @@ -438,7 +438,7 @@ impl Deserialize for SpIBinder { } impl DeserializeOption for SpIBinder { - fn deserialize_option(parcel: &Parcel) -> Result<Option<SpIBinder>> { + fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<SpIBinder>> { parcel.read_binder() } } diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index ebfe879f49..80dc47682c 100644 --- a/libs/binder/rust/tests/integration.rs +++ b/libs/binder/rust/tests/integration.rs @@ -16,8 +16,8 @@ //! Rust Binder crate integration tests -use binder::declare_binder_interface; -use binder::parcel::{Parcel, OwnedParcel}; +use binder::{declare_binder_enum, declare_binder_interface}; +use binder::parcel::BorrowedParcel; use binder::{ Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode, FIRST_CALL_TRANSACTION, @@ -100,6 +100,7 @@ enum TestTransactionCode { Test = FIRST_CALL_TRANSACTION, GetDumpArgs, GetSelinuxContext, + GetIsHandlingTransaction, } impl TryFrom<u32> for TestTransactionCode { @@ -112,6 +113,7 @@ impl TryFrom<u32> for TestTransactionCode { _ if c == TestTransactionCode::GetSelinuxContext as u32 => { Ok(TestTransactionCode::GetSelinuxContext) } + _ if c == TestTransactionCode::GetIsHandlingTransaction as u32 => Ok(TestTransactionCode::GetIsHandlingTransaction), _ => Err(StatusCode::UNKNOWN_TRANSACTION), } } @@ -140,6 +142,10 @@ impl ITest for TestService { ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned())); sid.ok_or(StatusCode::UNEXPECTED_NULL) } + + fn get_is_handling_transaction(&self) -> binder::Result<bool> { + Ok(binder::is_handling_transaction()) + } } /// Trivial testing binder interface @@ -152,6 +158,9 @@ pub trait ITest: Interface { /// Returns the caller's SELinux context fn get_selinux_context(&self) -> binder::Result<String>; + + /// Returns the value of calling `is_handling_transaction`. + fn get_is_handling_transaction(&self) -> binder::Result<bool>; } /// Async trivial testing binder interface @@ -164,6 +173,9 @@ pub trait IATest<P>: Interface { /// Returns the caller's SELinux context fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>>; + + /// Returns the value of calling `is_handling_transaction`. + fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>>; } declare_binder_interface! { @@ -179,13 +191,14 @@ declare_binder_interface! { fn on_transact( service: &dyn ITest, code: TransactionCode, - _data: &Parcel, - reply: &mut Parcel, + _data: &BorrowedParcel<'_>, + reply: &mut BorrowedParcel<'_>, ) -> binder::Result<()> { match code.try_into()? { TestTransactionCode::Test => reply.write(&service.test()?), TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?), TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?), + TestTransactionCode::GetIsHandlingTransaction => reply.write(&service.get_is_handling_transaction()?), } } @@ -212,30 +225,47 @@ impl ITest for BpTest { )?; reply.read() } + + fn get_is_handling_transaction(&self) -> binder::Result<bool> { + let reply = self.binder.transact( + TestTransactionCode::GetIsHandlingTransaction as TransactionCode, + 0, + |_| Ok(()), + )?; + reply.read() + } } impl<P: binder::BinderAsyncPool> IATest<P> for BpTest { fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> { let binder = self.binder.clone(); P::spawn( - move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())).map(|p| OwnedParcel::try_from(p).unwrap()), - |reply| async move { reply?.into_parcel().read() } + move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())), + |reply| async move { reply?.read() } ) } fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> { let binder = self.binder.clone(); P::spawn( - move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())).map(|p| OwnedParcel::try_from(p).unwrap()), - |reply| async move { reply?.into_parcel().read() } + move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())), + |reply| async move { reply?.read() } ) } fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> { let binder = self.binder.clone(); P::spawn( - move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())).map(|p| OwnedParcel::try_from(p).unwrap()), - |reply| async move { reply?.into_parcel().read() } + move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())), + |reply| async move { reply?.read() } + ) + } + + fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> { + let binder = self.binder.clone(); + P::spawn( + move || binder.transact(TestTransactionCode::GetIsHandlingTransaction as TransactionCode, 0, |_| Ok(())), + |reply| async move { reply?.read() } ) } } @@ -252,6 +282,10 @@ impl ITest for Binder<BnTest> { fn get_selinux_context(&self) -> binder::Result<String> { self.0.get_selinux_context() } + + fn get_is_handling_transaction(&self) -> binder::Result<bool> { + self.0.get_is_handling_transaction() + } } impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> { @@ -269,6 +303,11 @@ impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> { let res = self.0.get_selinux_context(); Box::pin(async move { res }) } + + fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> { + let res = self.0.get_is_handling_transaction(); + Box::pin(async move { res }) + } } /// Trivial testing binder interface @@ -284,8 +323,8 @@ declare_binder_interface! { fn on_transact_same_descriptor( _service: &dyn ITestSameDescriptor, _code: TransactionCode, - _data: &Parcel, - _reply: &mut Parcel, + _data: &BorrowedParcel<'_>, + _reply: &mut BorrowedParcel<'_>, ) -> binder::Result<()> { Ok(()) } @@ -294,6 +333,23 @@ impl ITestSameDescriptor for BpTestSameDescriptor {} impl ITestSameDescriptor for Binder<BnTestSameDescriptor> {} +declare_binder_enum! { + TestEnum : [i32; 3] { + FOO = 1, + BAR = 2, + BAZ = 3, + } +} + +declare_binder_enum! { + #[deprecated(since = "1.0.0")] + TestDeprecatedEnum : [i32; 3] { + FOO = 1, + BAR = 2, + BAZ = 3, + } +} + #[cfg(test)] mod tests { use selinux_bindgen as selinux_sys; @@ -483,7 +539,7 @@ mod tests { #[tokio::test] async fn get_selinux_context_async() { - let service_name = "get_selinux_context"; + let service_name = "get_selinux_context_async"; let _process = ScopedServiceProcess::new(service_name); let test_client: Strong<dyn IATest<Tokio>> = binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); @@ -493,6 +549,32 @@ mod tests { ); } + #[tokio::test] + async fn get_selinux_context_sync_to_async() { + let service_name = "get_selinux_context"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Strong<dyn ITest> = + binder::get_interface(service_name).expect("Did not get manager binder service"); + let test_client = test_client.into_async::<Tokio>(); + assert_eq!( + test_client.get_selinux_context().await.unwrap(), + get_expected_selinux_context() + ); + } + + #[tokio::test] + async fn get_selinux_context_async_to_sync() { + let service_name = "get_selinux_context"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Strong<dyn IATest<Tokio>> = + binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + let test_client = test_client.into_sync(); + assert_eq!( + test_client.get_selinux_context().unwrap(), + get_expected_selinux_context() + ); + } + struct Bools { binder_died: Arc<AtomicBool>, binder_dealloc: Arc<AtomicBool>, @@ -850,4 +932,45 @@ mod tests { Err(err) => assert_eq!(err, binder::StatusCode::BAD_VALUE), } } + + #[test] + fn get_is_handling_transaction() { + let service_name = "get_is_handling_transaction"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Strong<dyn ITest> = + binder::get_interface(service_name).expect("Did not get manager binder service"); + // Should be true externally. + assert!(test_client.get_is_handling_transaction().unwrap()); + + // Should be false locally. + assert!(!binder::is_handling_transaction()); + + // Should also be false in spawned thread. + std::thread::spawn(|| { + assert!(!binder::is_handling_transaction()); + }).join().unwrap(); + } + + #[tokio::test] + async fn get_is_handling_transaction_async() { + let service_name = "get_is_handling_transaction_async"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Strong<dyn IATest<Tokio>> = + binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service"); + // Should be true externally. + assert!(test_client.get_is_handling_transaction().await.unwrap()); + + // Should be false locally. + assert!(!binder::is_handling_transaction()); + + // Should also be false in spawned task. + tokio::spawn(async { + assert!(!binder::is_handling_transaction()); + }).await.unwrap(); + + // And in spawn_blocking task. + tokio::task::spawn_blocking(|| { + assert!(!binder::is_handling_transaction()); + }).await.unwrap(); + } } diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs index 66ba846c2d..1fc761e2c8 100644 --- a/libs/binder/rust/tests/serialization.rs +++ b/libs/binder/rust/tests/serialization.rs @@ -20,7 +20,7 @@ use binder::declare_binder_interface; use binder::parcel::ParcelFileDescriptor; use binder::{ - Binder, BinderFeatures, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status, + Binder, BinderFeatures, BorrowedParcel, ExceptionCode, Interface, Result, SpIBinder, Status, StatusCode, TransactionCode, }; @@ -111,8 +111,8 @@ impl ReadParcelTest for () {} fn on_transact( _service: &dyn ReadParcelTest, code: TransactionCode, - parcel: &Parcel, - reply: &mut Parcel, + parcel: &BorrowedParcel<'_>, + reply: &mut BorrowedParcel<'_>, ) -> Result<()> { match code { bindings::Transaction_TEST_BOOL => { diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 4d316f72f0..c8938998a6 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -496,7 +496,7 @@ TEST_F(BinderLibTest, Freeze) { EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000)); EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply)); - bool sync_received, async_received; + uint32_t sync_received, async_received; EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received, &async_received)); @@ -504,15 +504,7 @@ TEST_F(BinderLibTest, Freeze) { EXPECT_EQ(sync_received, 1); EXPECT_EQ(async_received, 0); - uint32_t sync_received2, async_received2; - - EXPECT_EQ(NO_ERROR, IPCThreadState::self()->getProcessFreezeInfo(pid, &sync_received2, - &async_received2)); - - EXPECT_EQ(sync_received2, 1); - EXPECT_EQ(async_received2, 0); - - EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, 0, 0)); + EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, false, 0)); EXPECT_EQ(NO_ERROR, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply)); } diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 55ad3c6975..5a96b7835c 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -22,6 +22,7 @@ #include <aidl/IBinderRpcTest.h> #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/properties.h> #include <android/binder_auto_utils.h> #include <android/binder_libbinder.h> #include <binder/Binder.h> @@ -1514,7 +1515,17 @@ TEST(BinderRpc, Java) { auto socket = rpcServer->releaseServer(); auto keepAlive = sp<BBinder>::make(); - ASSERT_EQ(OK, binder->setRpcClientDebug(std::move(socket), keepAlive)); + auto setRpcClientDebugStatus = binder->setRpcClientDebug(std::move(socket), keepAlive); + + if (!android::base::GetBoolProperty("ro.debuggable", false)) { + ASSERT_EQ(INVALID_OPERATION, setRpcClientDebugStatus) + << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable builds, " + "but get " + << statusToString(setRpcClientDebugStatus); + GTEST_SKIP(); + } + + ASSERT_EQ(OK, setRpcClientDebugStatus); auto rpcSession = RpcSession::make(); ASSERT_EQ(OK, rpcSession->setupInetClient("127.0.0.1", port)); diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 155a25bbcd..077d915ca1 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -192,6 +192,8 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { // only reading one binder type for now PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder), PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder), + PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::os::IServiceManager>>, readStrongBinderVector), + PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::sp<android::os::IServiceManager>>>, readStrongBinderVector), PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector), PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector), @@ -231,6 +233,32 @@ std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector), PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector), +#define COMMA , + PARCEL_READ_WITH_STATUS(std::array<uint8_t COMMA 3>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::optional<std::array<uint8_t COMMA 3>>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::array<char16_t COMMA 3>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::optional<std::array<char16_t COMMA 3>>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::array<std::string COMMA 3>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<std::string> COMMA 3>>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::array<android::String16 COMMA 3>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<android::String16> COMMA 3>>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::array<android::sp<android::IBinder> COMMA 3>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::optional<std::array<android::sp<android::IBinder> COMMA 3>>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<ExampleParcelable> COMMA 3>>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::array<ByteEnum COMMA 3>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::optional<std::array<ByteEnum COMMA 3>>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::array<IntEnum COMMA 3>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::optional<std::array<IntEnum COMMA 3>>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::array<LongEnum COMMA 3>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::optional<std::array<LongEnum COMMA 3>>, readFixedArray), + // nested arrays + PARCEL_READ_WITH_STATUS(std::array<std::array<uint8_t COMMA 3> COMMA 4>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<uint8_t COMMA 3> COMMA 4>>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray), + PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<std::optional<ExampleParcelable> COMMA 3> COMMA 4>>, readFixedArray), +#undef COMMA + [] (const android::Parcel& p, uint8_t /*len*/) { FUZZ_LOG() << "about to read flattenable"; ExampleFlattenable f; diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp index c0a762d1de..5aeb5cc6f2 100644 --- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp @@ -25,6 +25,7 @@ // TODO(b/142061461): parent class class SomeParcelable { public: + binder_status_t writeToParcel(AParcel* /*parcel*/) { return STATUS_OK; } binder_status_t readFromParcel(const AParcel* parcel) { return AParcel_readInt32(parcel, &mValue); } @@ -33,6 +34,41 @@ private: int32_t mValue = 0; }; +class ISomeInterface : public ::ndk::ICInterface { +public: + ISomeInterface() = default; + virtual ~ISomeInterface() = default; + static binder_status_t readFromParcel(const AParcel* parcel, + std::shared_ptr<ISomeInterface>* instance); +}; + +static binder_status_t onTransact(AIBinder*, transaction_code_t, const AParcel*, AParcel*) { + return STATUS_UNKNOWN_TRANSACTION; +} + +static AIBinder_Class* g_class = ::ndk::ICInterface::defineClass("ISomeInterface", onTransact); + +class BpSomeInterface : public ::ndk::BpCInterface<ISomeInterface> { +public: + explicit BpSomeInterface(const ::ndk::SpAIBinder& binder) : BpCInterface(binder) {} + virtual ~BpSomeInterface() = default; +}; + +binder_status_t ISomeInterface::readFromParcel(const AParcel* parcel, + std::shared_ptr<ISomeInterface>* instance) { + ::ndk::SpAIBinder binder; + binder_status_t status = AParcel_readStrongBinder(parcel, binder.getR()); + if (status == STATUS_OK) { + if (AIBinder_associateClass(binder.get(), g_class)) { + *instance = std::static_pointer_cast<ISomeInterface>( + ::ndk::ICInterface::asInterface(binder.get())); + } else { + *instance = ::ndk::SharedRefBase::make<BpSomeInterface>(binder); + } + } + return status; +} + #define PARCEL_READ(T, FUN) \ [](const NdkParcelAdapter& p, uint8_t /*data*/) { \ FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \ @@ -100,6 +136,8 @@ std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ PARCEL_READ(std::optional<std::vector<ndk::SpAIBinder>>, ndk::AParcel_readVector), PARCEL_READ(std::vector<ndk::ScopedFileDescriptor>, ndk::AParcel_readVector), PARCEL_READ(std::optional<std::vector<ndk::ScopedFileDescriptor>>, ndk::AParcel_readVector), + PARCEL_READ(std::vector<std::shared_ptr<ISomeInterface>>, ndk::AParcel_readVector), + PARCEL_READ(std::optional<std::vector<std::shared_ptr<ISomeInterface>>>, ndk::AParcel_readVector), PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector), PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector), PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector), @@ -118,5 +156,21 @@ std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector), PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector), PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector), + + // methods for std::array<T,N> +#define COMMA , + PARCEL_READ(std::array<bool COMMA 3>, ndk::AParcel_readData), + PARCEL_READ(std::array<uint8_t COMMA 3>, ndk::AParcel_readData), + PARCEL_READ(std::array<char16_t COMMA 3>, ndk::AParcel_readData), + PARCEL_READ(std::array<int32_t COMMA 3>, ndk::AParcel_readData), + PARCEL_READ(std::array<int64_t COMMA 3>, ndk::AParcel_readData), + PARCEL_READ(std::array<float COMMA 3>, ndk::AParcel_readData), + PARCEL_READ(std::array<double COMMA 3>, ndk::AParcel_readData), + PARCEL_READ(std::array<std::string COMMA 3>, ndk::AParcel_readData), + PARCEL_READ(std::array<SomeParcelable COMMA 3>, ndk::AParcel_readData), + PARCEL_READ(std::array<ndk::SpAIBinder COMMA 3>, ndk::AParcel_readData), + PARCEL_READ(std::array<ndk::ScopedFileDescriptor COMMA 3>, ndk::AParcel_readData), + PARCEL_READ(std::array<std::shared_ptr<ISomeInterface> COMMA 3>, ndk::AParcel_readData), +#undef COMMA }; // clang-format on diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp index f2d0943e86..858739c9dd 100644 --- a/libs/graphicsenv/GpuStatsInfo.cpp +++ b/libs/graphicsenv/GpuStatsInfo.cpp @@ -88,6 +88,7 @@ status_t GpuStatsAppInfo::writeToParcel(Parcel* parcel) const { if ((status = parcel->writeBool(cpuVulkanInUse)) != OK) return status; if ((status = parcel->writeBool(falsePrerotation)) != OK) return status; if ((status = parcel->writeBool(gles1InUse)) != OK) return status; + if ((status = parcel->writeBool(angleInUse)) != OK) return status; return OK; } @@ -101,6 +102,7 @@ status_t GpuStatsAppInfo::readFromParcel(const Parcel* parcel) { if ((status = parcel->readBool(&cpuVulkanInUse)) != OK) return status; if ((status = parcel->readBool(&falsePrerotation)) != OK) return status; if ((status = parcel->readBool(&gles1InUse)) != OK) return status; + if ((status = parcel->readBool(&angleInUse)) != OK) return status; return OK; } @@ -111,6 +113,7 @@ std::string GpuStatsAppInfo::toString() const { StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse); StringAppendF(&result, "falsePrerotation = %d\n", falsePrerotation); StringAppendF(&result, "gles1InUse = %d\n", gles1InUse); + StringAppendF(&result, "angleInUse = %d\n", angleInUse); result.append("glDriverLoadingTime:"); for (int32_t loadingTime : glDriverLoadingTime) { StringAppendF(&result, " %d", loadingTime); diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index d54de4999c..7f0cac5d4f 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -343,80 +343,6 @@ void* GraphicsEnv::loadLibrary(std::string name) { return nullptr; } -bool GraphicsEnv::checkAngleRules(void* so) { - auto manufacturer = base::GetProperty("ro.product.manufacturer", "UNSET"); - auto model = base::GetProperty("ro.product.model", "UNSET"); - - auto ANGLEGetFeatureSupportUtilAPIVersion = - (fpANGLEGetFeatureSupportUtilAPIVersion)dlsym(so, - "ANGLEGetFeatureSupportUtilAPIVersion"); - - if (!ANGLEGetFeatureSupportUtilAPIVersion) { - ALOGW("Cannot find ANGLEGetFeatureSupportUtilAPIVersion function"); - return false; - } - - // Negotiate the interface version by requesting most recent known to the platform - unsigned int versionToUse = CURRENT_ANGLE_API_VERSION; - if (!(ANGLEGetFeatureSupportUtilAPIVersion)(&versionToUse)) { - ALOGW("Cannot use ANGLE feature-support library, it is older than supported by EGL, " - "requested version %u", - versionToUse); - return false; - } - - // Add and remove versions below as needed - bool useAngle = false; - switch (versionToUse) { - case 2: { - ALOGV("Using version %d of ANGLE feature-support library", versionToUse); - void* rulesHandle = nullptr; - int rulesVersion = 0; - void* systemInfoHandle = nullptr; - - // Get the symbols for the feature-support-utility library: -#define GET_SYMBOL(symbol) \ - fp##symbol symbol = (fp##symbol)dlsym(so, #symbol); \ - if (!symbol) { \ - ALOGW("Cannot find " #symbol " in ANGLE feature-support library"); \ - break; \ - } - GET_SYMBOL(ANGLEAndroidParseRulesString); - GET_SYMBOL(ANGLEGetSystemInfo); - GET_SYMBOL(ANGLEAddDeviceInfoToSystemInfo); - GET_SYMBOL(ANGLEShouldBeUsedForApplication); - GET_SYMBOL(ANGLEFreeRulesHandle); - GET_SYMBOL(ANGLEFreeSystemInfoHandle); - - // Parse the rules, obtain the SystemInfo, and evaluate the - // application against the rules: - if (!(ANGLEAndroidParseRulesString)(mRulesBuffer.data(), &rulesHandle, &rulesVersion)) { - ALOGW("ANGLE feature-support library cannot parse rules file"); - break; - } - if (!(ANGLEGetSystemInfo)(&systemInfoHandle)) { - ALOGW("ANGLE feature-support library cannot obtain SystemInfo"); - break; - } - if (!(ANGLEAddDeviceInfoToSystemInfo)(manufacturer.c_str(), model.c_str(), - systemInfoHandle)) { - ALOGW("ANGLE feature-support library cannot add device info to SystemInfo"); - break; - } - useAngle = (ANGLEShouldBeUsedForApplication)(rulesHandle, rulesVersion, - systemInfoHandle, mAngleAppName.c_str()); - (ANGLEFreeRulesHandle)(rulesHandle); - (ANGLEFreeSystemInfoHandle)(systemInfoHandle); - } break; - - default: - ALOGW("Version %u of ANGLE feature-support library is NOT supported.", versionToUse); - } - - ALOGV("Close temporarily-loaded ANGLE opt-in/out logic"); - return useAngle; -} - bool GraphicsEnv::shouldUseAngle(std::string appName) { if (appName != mAngleAppName) { // Make sure we are checking the app we were init'ed for @@ -444,31 +370,20 @@ void GraphicsEnv::updateUseAngle() { const char* ANGLE_PREFER_ANGLE = "angle"; const char* ANGLE_PREFER_NATIVE = "native"; + mUseAngle = NO; if (mAngleDeveloperOptIn == ANGLE_PREFER_ANGLE) { ALOGV("User set \"Developer Options\" to force the use of ANGLE"); mUseAngle = YES; } else if (mAngleDeveloperOptIn == ANGLE_PREFER_NATIVE) { ALOGV("User set \"Developer Options\" to force the use of Native"); - mUseAngle = NO; } else { - // The "Developer Options" value wasn't set to force the use of ANGLE. Need to temporarily - // load ANGLE and call the updatable opt-in/out logic: - void* featureSo = loadLibrary("feature_support"); - if (featureSo) { - ALOGV("loaded ANGLE's opt-in/out logic from namespace"); - mUseAngle = checkAngleRules(featureSo) ? YES : NO; - dlclose(featureSo); - featureSo = nullptr; - } else { - ALOGV("Could not load the ANGLE opt-in/out logic, cannot use ANGLE."); - } + ALOGV("User set invalid \"Developer Options\": '%s'", mAngleDeveloperOptIn.c_str()); } } void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName, const std::string developerOptIn, - const std::vector<std::string> eglFeatures, const int rulesFd, - const long rulesOffset, const long rulesLength) { + const std::vector<std::string> eglFeatures) { if (mUseAngle != UNKNOWN) { // We've already figured out an answer for this app, so just return. ALOGV("Already evaluated the rules file for '%s': use ANGLE = %s", appName.c_str(), @@ -485,22 +400,6 @@ void GraphicsEnv::setAngleInfo(const std::string path, const std::string appName ALOGV("setting ANGLE application opt-in to '%s'", developerOptIn.c_str()); mAngleDeveloperOptIn = developerOptIn; - lseek(rulesFd, rulesOffset, SEEK_SET); - mRulesBuffer = std::vector<char>(rulesLength + 1); - ssize_t numBytesRead = read(rulesFd, mRulesBuffer.data(), rulesLength); - if (numBytesRead < 0) { - ALOGE("Cannot read rules file: numBytesRead = %zd", numBytesRead); - numBytesRead = 0; - } else if (numBytesRead == 0) { - ALOGW("Empty rules file"); - } - if (numBytesRead != rulesLength) { - ALOGW("Did not read all of the necessary bytes from the rules file." - "expected: %ld, got: %zd", - rulesLength, numBytesRead); - } - mRulesBuffer[numBytesRead] = '\0'; - // Update the current status of whether we should use ANGLE or not updateUseAngle(); } diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h index 9aba69fd07..5b513d2a79 100644 --- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h +++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h @@ -16,6 +16,7 @@ #pragma once +#include <chrono> #include <string> #include <vector> @@ -52,7 +53,7 @@ public: }; /* - * class for transporting gpu app stats from GpuService to authorized recipents. + * class for transporting gpu app stats from GpuService to authorized recipients. * This class is intended to be a data container. */ class GpuStatsAppInfo : public Parcelable { @@ -72,6 +73,9 @@ public: bool cpuVulkanInUse = false; bool falsePrerotation = false; bool gles1InUse = false; + bool angleInUse = false; + + std::chrono::time_point<std::chrono::system_clock> lastAccessTime; }; /* diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 900fc49b59..56d1139f57 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -97,8 +97,7 @@ public: // in the search path must have a '!' after the zip filename, e.g. // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn, - const std::vector<std::string> eglFeatures, const int rulesFd, - const long rulesOffset, const long rulesLength); + const std::vector<std::string> eglFeatures); // Get the ANGLE driver namespace. android_namespace_t* getAngleNamespace(); // Get the app name for ANGLE debug message. @@ -129,8 +128,6 @@ private: // Load requested ANGLE library. void* loadLibrary(std::string name); - // Check ANGLE support with the rules. - bool checkAngleRules(void* so); // Update whether ANGLE should be used. void updateUseAngle(); // Link updatable driver namespace with llndk and vndk-sp libs. @@ -159,8 +156,6 @@ private: std::string mAngleDeveloperOptIn; // ANGLE EGL features; std::vector<std::string> mAngleEglFeatures; - // ANGLE rules. - std::vector<char> mRulesBuffer; // Use ANGLE flag. UseAngle mUseAngle = UNKNOWN; // Vulkan debug layers libs. diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 56a9683773..94e1ae1c74 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -118,12 +118,12 @@ void BLASTBufferItemConsumer::getConnectionEvents(uint64_t frameNumber, bool* ne } void BLASTBufferItemConsumer::setBlastBufferQueue(BLASTBufferQueue* blastbufferqueue) { - Mutex::Autolock lock(mMutex); + std::scoped_lock lock(mBufferQueueMutex); mBLASTBufferQueue = blastbufferqueue; } void BLASTBufferItemConsumer::onSidebandStreamChanged() { - Mutex::Autolock lock(mMutex); + std::scoped_lock lock(mBufferQueueMutex); if (mBLASTBufferQueue != nullptr) { sp<NativeHandle> stream = getSidebandStream(); mBLASTBufferQueue->setSidebandStream(stream); @@ -630,7 +630,10 @@ bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const { class BBQSurface : public Surface { private: + std::mutex mMutex; sp<BLASTBufferQueue> mBbq; + bool mDestroyed = false; + public: BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp, const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq) @@ -650,6 +653,10 @@ public: status_t setFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) override { + std::unique_lock _lock{mMutex}; + if (mDestroyed) { + return DEAD_OBJECT; + } if (!ValidateFrameRate(frameRate, compatibility, changeFrameRateStrategy, "BBQSurface::setFrameRate")) { return BAD_VALUE; @@ -658,8 +665,20 @@ public: } status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override { + std::unique_lock _lock{mMutex}; + if (mDestroyed) { + return DEAD_OBJECT; + } return mBbq->setFrameTimelineInfo(frameTimelineInfo); } + + void destroy() override { + Surface::destroy(); + + std::unique_lock _lock{mMutex}; + mDestroyed = true; + mBbq = nullptr; + } }; // TODO: Can we coalesce this with frame updates? Need to confirm diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp index 8edf60400c..a62697064b 100644 --- a/libs/gui/CpuConsumer.cpp +++ b/libs/gui/CpuConsumer.cpp @@ -71,6 +71,7 @@ static bool isPossiblyYUV(PixelFormat format) { case HAL_PIXEL_FORMAT_Y8: case HAL_PIXEL_FORMAT_Y16: case HAL_PIXEL_FORMAT_RAW16: + case HAL_PIXEL_FORMAT_RAW12: case HAL_PIXEL_FORMAT_RAW10: case HAL_PIXEL_FORMAT_RAW_OPAQUE: case HAL_PIXEL_FORMAT_BLOB: diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp index e1b1efc0ed..46800f2f14 100644 --- a/libs/gui/DisplayEventDispatcher.cpp +++ b/libs/gui/DisplayEventDispatcher.cpp @@ -33,10 +33,13 @@ namespace android { // using just a few large reads. static const size_t EVENT_BUFFER_SIZE = 100; +static constexpr nsecs_t WAITING_FOR_VSYNC_TIMEOUT = ms2ns(300); + DisplayEventDispatcher::DisplayEventDispatcher( const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::EventRegistrationFlags eventRegistration) - : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false) { + : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false), + mLastVsyncCount(0), mLastScheduleVsyncTime(0) { ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this); } @@ -86,6 +89,7 @@ status_t DisplayEventDispatcher::scheduleVsync() { } mWaitingForVsync = true; + mLastScheduleVsyncTime = systemTime(SYSTEM_TIME_MONOTONIC); } return OK; } @@ -124,9 +128,21 @@ int DisplayEventDispatcher::handleEvent(int, int events, void*) { this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount, vsyncEventData.id); mWaitingForVsync = false; + mLastVsyncCount = vsyncCount; dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData); } + if (mWaitingForVsync) { + const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + const nsecs_t vsyncScheduleDelay = currentTime - mLastScheduleVsyncTime; + if (vsyncScheduleDelay > WAITING_FOR_VSYNC_TIMEOUT) { + ALOGW("Vsync time out! vsyncScheduleDelay=%" PRId64 "ms", ns2ms(vsyncScheduleDelay)); + mWaitingForVsync = false; + dispatchVsync(currentTime, vsyncDisplayId /* displayId is not used */, + ++mLastVsyncCount, vsyncEventData /* empty data */); + } + } + return 1; // keep the callback } diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 0d7795e1ba..a82fc6fa81 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -291,6 +291,17 @@ public: return {}; } + status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId* displayId) const override { + Parcel data, reply; + SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor()); + SAFE_PARCEL(remote()->transact, BnSurfaceComposer::GET_PRIMARY_PHYSICAL_DISPLAY_ID, data, + &reply); + uint64_t rawId; + SAFE_PARCEL(reply.readUint64, &rawId); + *displayId = PhysicalDisplayId(rawId); + return NO_ERROR; + } + sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -1713,6 +1724,16 @@ status_t BnSurfaceComposer::onTransact( [](PhysicalDisplayId id) { return id.value; }); return reply->writeUint64Vector(rawIds); } + case GET_PRIMARY_PHYSICAL_DISPLAY_ID: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + PhysicalDisplayId id; + status_t result = getPrimaryPhysicalDisplayId(&id); + if (result != NO_ERROR) { + ALOGE("getPrimaryPhysicalDisplayId: Failed to get id"); + return result; + } + return reply->writeUint64(id.value); + } case ADD_REGION_SAMPLING_LISTENER: { CHECK_INTERFACE(ISurfaceComposer, data, reply); Rect samplingArea; diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 2edb4e4ba4..353a91d062 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -2622,4 +2622,14 @@ status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInf return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo); } +sp<IBinder> Surface::getSurfaceControlHandle() const { + Mutex::Autolock lock(mMutex); + return mSurfaceControlHandle; +} + +void Surface::destroy() { + Mutex::Autolock lock(mMutex); + mSurfaceControlHandle = nullptr; +} + }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 96da8efd19..1377284f92 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -913,6 +913,10 @@ std::vector<PhysicalDisplayId> SurfaceComposerClient::getPhysicalDisplayIds() { return ComposerService::getComposerService()->getPhysicalDisplayIds(); } +status_t SurfaceComposerClient::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) { + return ComposerService::getComposerService()->getPrimaryPhysicalDisplayId(id); +} + std::optional<PhysicalDisplayId> SurfaceComposerClient::getInternalDisplayId() { return ComposerService::getComposerService()->getInternalDisplayId(); } diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index ea9b1c68af..6c5b2aa53c 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -62,11 +62,12 @@ private: uint64_t mCurrentFrameNumber = 0; Mutex mMutex; + std::mutex mBufferQueueMutex; ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mMutex); std::queue<uint64_t> mDisconnectEvents GUARDED_BY(mMutex); bool mCurrentlyConnected GUARDED_BY(mMutex); bool mPreviouslyConnected GUARDED_BY(mMutex); - BLASTBufferQueue* mBLASTBufferQueue GUARDED_BY(mMutex); + BLASTBufferQueue* mBLASTBufferQueue GUARDED_BY(mBufferQueueMutex); }; class BLASTBufferQueue diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h index 4ade240dcf..08f3597bc5 100644 --- a/libs/gui/include/gui/DisplayEventDispatcher.h +++ b/libs/gui/include/gui/DisplayEventDispatcher.h @@ -56,6 +56,8 @@ private: sp<Looper> mLooper; DisplayEventReceiver mReceiver; bool mWaitingForVsync; + uint32_t mLastVsyncCount; + nsecs_t mLastScheduleVsyncTime; std::vector<FrameRateOverride> mFrameRateOverrides; diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 2a3f6a43d9..ef5353f594 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -140,6 +140,8 @@ public: */ virtual std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const = 0; + virtual status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const = 0; + // TODO(b/74619554): Remove this stopgap once the framework is display-agnostic. std::optional<PhysicalDisplayId> getInternalDisplayId() const { const auto displayIds = getPhysicalDisplayIds(); @@ -624,6 +626,7 @@ public: ON_PULL_ATOM, ADD_TUNNEL_MODE_ENABLED_LISTENER, REMOVE_TUNNEL_MODE_ENABLED_LISTENER, + GET_PRIMARY_PHYSICAL_DISPLAY_ID, // Always append new enum to the end. }; diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 7e4143b1f3..e5403512a9 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -99,7 +99,7 @@ public: */ sp<IGraphicBufferProducer> getIGraphicBufferProducer() const; - sp<IBinder> getSurfaceControlHandle() const { return mSurfaceControlHandle; } + sp<IBinder> getSurfaceControlHandle() const; /* convenience function to check that the given surface is non NULL as * well as its IGraphicBufferProducer */ @@ -333,6 +333,7 @@ public: virtual int connect( int api, bool reportBufferRemoval, const sp<SurfaceListener>& sListener); + virtual void destroy(); // When client connects to Surface with reportBufferRemoval set to true, any buffers removed // from this Surface will be collected and returned here. Once this method returns, these diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index baa0567617..4164ca36ba 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -307,6 +307,7 @@ public: //! Get stable IDs for connected physical displays static std::vector<PhysicalDisplayId> getPhysicalDisplayIds(); + static status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*); static std::optional<PhysicalDisplayId> getInternalDisplayId(); //! Get token for a physical display given its stable ID diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 59b0c04bc3..630dd17daa 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -694,6 +694,7 @@ public: bool /*secure*/) override { return nullptr; } void destroyDisplay(const sp<IBinder>& /*display */) override {} std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override { return {}; } + status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const override { return NO_ERROR; } sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId) const override { return nullptr; } status_t setTransactionState(const FrameTimelineInfo& /*frameTimelineInfo*/, const Vector<ComposerState>& /*state*/, diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 30c42a3daa..fcbc16f9c7 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -89,8 +89,15 @@ std::string getInputDeviceConfigurationFilePathByName( // Treblized input device config files will be located /product/usr, /system_ext/usr, // /odm/usr or /vendor/usr. - const char* rootsForPartition[]{"/product", "/system_ext", "/odm", "/vendor", - getenv("ANDROID_ROOT")}; + // These files may also be in the com.android.input.config APEX. + const char* rootsForPartition[]{ + "/product", + "/system_ext", + "/odm", + "/vendor", + "/apex/com.android.input.config/etc", + getenv("ANDROID_ROOT"), + }; for (size_t i = 0; i < size(rootsForPartition); i++) { if (rootsForPartition[i] == nullptr) { continue; diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 0c5a851c41..9e466b6c34 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -26,55 +26,39 @@ namespace android { namespace renderengine { -std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) { - RenderEngineType renderEngineType = args.renderEngineType; - +std::unique_ptr<RenderEngine> RenderEngine::create(RenderEngineCreationArgs args) { // Keep the ability to override by PROPERTIES: char prop[PROPERTY_VALUE_MAX]; property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); if (strcmp(prop, "gles") == 0) { - renderEngineType = RenderEngineType::GLES; + args.renderEngineType = RenderEngineType::GLES; } if (strcmp(prop, "threaded") == 0) { - renderEngineType = RenderEngineType::THREADED; + args.renderEngineType = RenderEngineType::THREADED; } if (strcmp(prop, "skiagl") == 0) { - renderEngineType = RenderEngineType::SKIA_GL; + args.renderEngineType = RenderEngineType::SKIA_GL; } if (strcmp(prop, "skiaglthreaded") == 0) { - renderEngineType = RenderEngineType::SKIA_GL_THREADED; + args.renderEngineType = RenderEngineType::SKIA_GL_THREADED; } - switch (renderEngineType) { + switch (args.renderEngineType) { case RenderEngineType::THREADED: ALOGD("Threaded RenderEngine with GLES Backend"); return renderengine::threaded::RenderEngineThreaded::create( [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); }, - renderEngineType); + args.renderEngineType); case RenderEngineType::SKIA_GL: ALOGD("RenderEngine with SkiaGL Backend"); return renderengine::skia::SkiaGLRenderEngine::create(args); case RenderEngineType::SKIA_GL_THREADED: { - // These need to be recreated, since they are a constant reference, and we need to - // let SkiaRE know that it's running as threaded, and all GL operation will happen on - // the same thread. - RenderEngineCreationArgs skiaArgs = - RenderEngineCreationArgs::Builder() - .setPixelFormat(args.pixelFormat) - .setImageCacheSize(args.imageCacheSize) - .setUseColorManagerment(args.useColorManagement) - .setEnableProtectedContext(args.enableProtectedContext) - .setPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly) - .setSupportsBackgroundBlur(args.supportsBackgroundBlur) - .setContextPriority(args.contextPriority) - .setRenderEngineType(renderEngineType) - .build(); ALOGD("Threaded RenderEngine with SkiaGL Backend"); return renderengine::threaded::RenderEngineThreaded::create( - [skiaArgs]() { - return android::renderengine::skia::SkiaGLRenderEngine::create(skiaArgs); + [args]() { + return android::renderengine::skia::SkiaGLRenderEngine::create(args); }, - renderEngineType); + args.renderEngineType); } case RenderEngineType::GLES: default: diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 5964bc3927..46a7d1eea6 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -98,7 +98,7 @@ public: SKIA_GL_THREADED = 4, }; - static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); + static std::unique_ptr<RenderEngine> create(RenderEngineCreationArgs args); virtual ~RenderEngine() = 0; diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 3c59f11395..94023e6247 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -424,14 +424,28 @@ base::unique_fd SkiaGLRenderEngine::flush() { return fenceFd; } -bool SkiaGLRenderEngine::waitFence(base::unique_fd fenceFd) { +void SkiaGLRenderEngine::waitFence(base::borrowed_fd fenceFd) { + if (fenceFd.get() >= 0 && !waitGpuFence(fenceFd)) { + ATRACE_NAME("SkiaGLRenderEngine::waitFence"); + sync_wait(fenceFd.get(), -1); + } +} + +bool SkiaGLRenderEngine::waitGpuFence(base::borrowed_fd fenceFd) { if (!gl::GLExtensions::getInstance().hasNativeFenceSync() || !gl::GLExtensions::getInstance().hasWaitSync()) { return false; } + // Duplicate the fence for passing to eglCreateSyncKHR. + base::unique_fd fenceDup(dup(fenceFd.get())); + if (fenceDup.get() < 0) { + ALOGE("failed to create duplicate fence fd: %d", fenceDup.get()); + return false; + } + // release the fd and transfer the ownership to EGLSync - EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd.release(), EGL_NONE}; + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceDup.release(), EGL_NONE}; EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); if (sync == EGL_NO_SYNC_KHR) { ALOGE("failed to create EGL native fence sync: %#x", eglGetError()); @@ -726,14 +740,6 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, return NO_ERROR; } - if (bufferFence.get() >= 0) { - // Duplicate the fence for passing to waitFence. - base::unique_fd bufferFenceDup(dup(bufferFence.get())); - if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) { - ATRACE_NAME("Waiting before draw"); - sync_wait(bufferFence.get(), -1); - } - } if (buffer == nullptr) { ALOGE("No output buffer provided. Aborting GPU composition."); return BAD_VALUE; @@ -758,6 +764,9 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, true, mTextureCleanupMgr); } + // wait on the buffer to be ready to use prior to using it + waitFence(bufferFence); + const ui::Dataspace dstDataspace = mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR; sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(dstDataspace, grContext); @@ -1014,6 +1023,12 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, false, mTextureCleanupMgr); } + // if the layer's buffer has a fence, then we must must respect the fence prior to using + // the buffer. + if (layer->source.buffer.fence != nullptr) { + waitFence(layer->source.buffer.fence->get()); + } + // isOpaque means we need to ignore the alpha in the image, // replacing it with the alpha specified by the LayerSettings. See // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean) diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index a852bbcaf8..238ad8f452 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -99,7 +99,10 @@ private: inline GrDirectContext* getActiveGrContext() const; base::unique_fd flush(); - bool waitFence(base::unique_fd fenceFd); + // waitFence attempts to wait in the GPU, and if unable to waits on the CPU instead. + void waitFence(base::borrowed_fd fenceFd); + bool waitGpuFence(base::borrowed_fd fenceFd); + void initCanvas(SkCanvas* canvas, const DisplaySettings& display); void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect, const ShadowSettings& shadowSettings); diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index eed58c5715..d4d0ee4f35 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -54,7 +54,7 @@ cc_library_static { target: { windows: { enabled: true, - } + }, }, defaults: [ @@ -225,6 +225,11 @@ cc_library_shared { "libui_headers", ], min_sdk_version: "29", + + pgo: { + sampling: true, + profile_file: "libui/libui.profdata", + }, } cc_library_headers { @@ -266,6 +271,6 @@ filegroup { "Rect.cpp", "Region.cpp", "PixelFormat.cpp", - "Transform.cpp" + "Transform.cpp", ], } diff --git a/opengl/libs/EGL/GLES_layers.md b/opengl/libs/EGL/GLES_layers.md index bfc44dbb69..f6a8f14a73 100644 --- a/opengl/libs/EGL/GLES_layers.md +++ b/opengl/libs/EGL/GLES_layers.md @@ -251,7 +251,7 @@ When layering is enabled, GLES 1.x exclusive functions will continue to route to - Secondly, if you want to determine from an application that can't call out to ADB for this, you can check for the [EGL_ANDROID_GLES_layers](../../specs/EGL_ANDROID_GLES_layers.txt). It simply indicates support of this layering system: ```cpp std::string display_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); - if (display_extension.find("EGL_ANDROID_GLES_layers") != std::string::npos) + if (display_extensions.find("EGL_ANDROID_GLES_layers") != std::string::npos) { // Layers are supported! } diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp index 3aa862f31f..dd3cc3bd86 100644 --- a/services/gpuservice/gpumem/GpuMem.cpp +++ b/services/gpuservice/gpumem/GpuMem.cpp @@ -22,7 +22,7 @@ #include <android-base/stringprintf.h> #include <libbpf.h> -#include <libbpf_android.h> +#include <bpf/WaitForProgsLoaded.h> #include <log/log.h> #include <unistd.h> #include <utils/Timers.h> diff --git a/services/gpuservice/gpuservice.rc b/services/gpuservice/gpuservice.rc index 65a5c2776a..0da8bd3812 100644 --- a/services/gpuservice/gpuservice.rc +++ b/services/gpuservice/gpuservice.rc @@ -1,4 +1,4 @@ service gpu /system/bin/gpuservice class core user gpu_service - group graphics + group graphics readtracefs diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp index 220952d892..d033453dd7 100644 --- a/services/gpuservice/gpustats/GpuStats.cpp +++ b/services/gpuservice/gpustats/GpuStats.cpp @@ -84,6 +84,38 @@ static void addLoadingTime(GpuStatsInfo::Driver driver, int64_t driverLoadingTim } } +void GpuStats::purgeOldDriverStats() { + ALOG_ASSERT(mAppStats.size() == MAX_NUM_APP_RECORDS); + + struct GpuStatsApp { + // Key is <app package name>+<driver version code>. + const std::string *appStatsKey = nullptr; + const std::chrono::time_point<std::chrono::system_clock> *lastAccessTime = nullptr; + }; + std::vector<GpuStatsApp> gpuStatsApps(MAX_NUM_APP_RECORDS); + + // Create a list of pointers to package names and their last access times. + int index = 0; + for (const auto & [appStatsKey, gpuStatsAppInfo] : mAppStats) { + GpuStatsApp &gpuStatsApp = gpuStatsApps[index]; + gpuStatsApp.appStatsKey = &appStatsKey; + gpuStatsApp.lastAccessTime = &gpuStatsAppInfo.lastAccessTime; + ++index; + } + + // Sort the list with the oldest access times at the front. + std::sort(gpuStatsApps.begin(), gpuStatsApps.end(), [](GpuStatsApp a, GpuStatsApp b) -> bool { + return *a.lastAccessTime < *b.lastAccessTime; + }); + + // Remove the oldest packages from mAppStats to make room for new apps. + for (int i = 0; i < APP_RECORD_HEADROOM; ++i) { + mAppStats.erase(*gpuStatsApps[i].appStatsKey); + gpuStatsApps[i].appStatsKey = nullptr; + gpuStatsApps[i].lastAccessTime = nullptr; + } +} + void GpuStats::insertDriverStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, @@ -123,19 +155,22 @@ void GpuStats::insertDriverStats(const std::string& driverPackageName, const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode); if (!mAppStats.count(appStatsKey)) { if (mAppStats.size() >= MAX_NUM_APP_RECORDS) { - ALOGV("GpuStatsAppInfo has reached maximum size. Ignore new stats."); - return; + ALOGV("GpuStatsAppInfo has reached maximum size. Removing old stats to make room."); + purgeOldDriverStats(); } GpuStatsAppInfo appInfo; addLoadingTime(driver, driverLoadingTime, &appInfo); appInfo.appPackageName = appPackageName; appInfo.driverVersionCode = driverVersionCode; + appInfo.angleInUse = driverPackageName == "angle"; + appInfo.lastAccessTime = std::chrono::system_clock::now(); mAppStats.insert({appStatsKey, appInfo}); - return; + } else { + mAppStats[appStatsKey].angleInUse = driverPackageName == "angle"; + addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]); + mAppStats[appStatsKey].lastAccessTime = std::chrono::system_clock::now(); } - - addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]); } void GpuStats::insertTargetStats(const std::string& appPackageName, @@ -311,7 +346,8 @@ AStatsManager_PullAtomCallbackReturn GpuStats::pullAppInfoAtom(AStatsEventList* angleDriverBytes.length()), ele.second.cpuVulkanInUse, ele.second.falsePrerotation, - ele.second.gles1InUse); + ele.second.gles1InUse, + ele.second.angleInUse); } } diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h index 55f0da1bc5..2aba651af9 100644 --- a/services/gpuservice/gpustats/include/gpustats/GpuStats.h +++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h @@ -46,6 +46,11 @@ public: // This limits the worst case number of loading times tracked. static const size_t MAX_NUM_LOADING_TIMES = 50; + // Below limits the memory usage of GpuStats to be less than 10KB. This is + // the preferred number for statsd while maintaining nice data quality. + static const size_t MAX_NUM_APP_RECORDS = 100; + // The number of apps to remove when mAppStats fills up. + static const size_t APP_RECORD_HEADROOM = 10; private: // Friend class for testing. @@ -55,6 +60,10 @@ private: static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag, AStatsEventList* data, void* cookie); + + // Remove old packages from mAppStats. + void purgeOldDriverStats(); + // Pull global into into global atom. AStatsManager_PullAtomCallbackReturn pullGlobalInfoAtom(AStatsEventList* data); // Pull app into into app atom. @@ -68,9 +77,6 @@ private: // Registers statsd callbacks if they have not already been registered void registerStatsdCallbacksIfNeeded(); - // Below limits the memory usage of GpuStats to be less than 10KB. This is - // the preferred number for statsd while maintaining nice data quality. - static const size_t MAX_NUM_APP_RECORDS = 100; // GpuStats access should be guarded by mLock. std::mutex mLock; // True if statsd callbacks have been registered. diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp index 37ebeae18d..20c8ccf9bf 100644 --- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp +++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "gpuservice_unittest" +#include <unistd.h> #include <cutils/properties.h> #include <gmock/gmock.h> #include <gpustats/GpuStats.h> @@ -221,6 +222,51 @@ TEST_F(GpuStatsTest, canInsertTargetStatsAfterProperSetup) { EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1")); } +// Verify we always have the most recently used apps in mAppStats, even when we fill it. +TEST_F(GpuStatsTest, canInsertMoreThanMaxNumAppRecords) { + constexpr int kNumExtraApps = 15; + static_assert(kNumExtraApps > GpuStats::APP_RECORD_HEADROOM); + + // Insert stats for GpuStats::MAX_NUM_APP_RECORDS so we fill it up. + for (int i = 0; i < GpuStats::MAX_NUM_APP_RECORDS + kNumExtraApps; ++i) { + std::stringstream nameStream; + nameStream << "testapp" << "_" << i; + std::string fullPkgName = nameStream.str(); + + mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME, + BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, + fullPkgName, VULKAN_VERSION, GpuStatsInfo::Driver::GL, true, + DRIVER_LOADING_TIME_1); + mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE, + GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0); + mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE, + GpuStatsInfo::Stats::FALSE_PREROTATION, 0); + mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE, + GpuStatsInfo::Stats::GLES_1_IN_USE, 0); + + EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(fullPkgName.c_str())); + EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1")); + EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1")); + EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1")); + } + + // mAppStats purges GpuStats::APP_RECORD_HEADROOM apps removed everytime it's filled up. + int numPurges = kNumExtraApps / GpuStats::APP_RECORD_HEADROOM; + numPurges += (kNumExtraApps % GpuStats::APP_RECORD_HEADROOM) == 0 ? 0 : 1; + + // Verify the remaining apps are present. + for (int i = numPurges * GpuStats::APP_RECORD_HEADROOM; + i < GpuStats::MAX_NUM_APP_RECORDS + kNumExtraApps; + ++i) { + std::stringstream nameStream; + // Add a newline to search for the exact package name. + nameStream << "testapp" << "_" << i << "\n"; + std::string fullPkgName = nameStream.str(); + + EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(fullPkgName.c_str())); + } +} + TEST_F(GpuStatsTest, canDumpAllBeforeClearAll) { mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME, BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1, diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 33b3e1ee2f..71b0f5fec9 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -287,16 +287,16 @@ void NotifyDeviceResetArgs::notify(const sp<InputListenerInterface>& listener) c // --- NotifyPointerCaptureChangedArgs --- -NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, - bool enabled) - : NotifyArgs(id, eventTime), enabled(enabled) {} +NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs( + int32_t id, nsecs_t eventTime, const PointerCaptureRequest& request) + : NotifyArgs(id, eventTime), request(request) {} NotifyPointerCaptureChangedArgs::NotifyPointerCaptureChangedArgs( const NotifyPointerCaptureChangedArgs& other) - : NotifyArgs(other.id, other.eventTime), enabled(other.enabled) {} + : NotifyArgs(other.id, other.eventTime), request(other.request) {} bool NotifyPointerCaptureChangedArgs::operator==(const NotifyPointerCaptureChangedArgs& rhs) const { - return id == rhs.id && eventTime == rhs.eventTime && enabled == rhs.enabled; + return id == rhs.id && eventTime == rhs.eventTime && request == rhs.request; } void NotifyPointerCaptureChangedArgs::notify(const sp<InputListenerInterface>& listener) const { diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index 9cc777d450..d34482f506 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -67,6 +67,9 @@ std::string InputReaderConfiguration::changesToString(uint32_t changes) { if (changes & CHANGE_EXTERNAL_STYLUS_PRESENCE) { result += "EXTERNAL_STYLUS_PRESENCE | "; } + if (changes & CHANGE_POINTER_CAPTURE) { + result += "POINTER_CAPTURE | "; + } if (changes & CHANGE_ENABLED_STATE) { result += "ENABLED_STATE | "; } diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index bc77b8aef4..aa8cc302c7 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -113,7 +113,7 @@ private: void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {} - void setPointerCapture(bool enabled) override {} + void setPointerCapture(const PointerCaptureRequest&) override {} void notifyDropWindow(const sp<IBinder>&, float x, float y) override {} diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 881024fc6e..5c3747e2ec 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -119,15 +119,15 @@ std::string FocusEntry::getDescription() const { // PointerCaptureChanged notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER // for all entries. PointerCaptureChangedEntry::PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, - bool hasPointerCapture) + const PointerCaptureRequest& request) : EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER), - pointerCaptureEnabled(hasPointerCapture) {} + pointerCaptureRequest(request) {} PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {} std::string PointerCaptureChangedEntry::getDescription() const { return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)", - pointerCaptureEnabled ? "true" : "false"); + pointerCaptureRequest.enable ? "true" : "false"); } // --- DragEntry --- @@ -324,8 +324,7 @@ CommandEntry::CommandEntry(Command command) keyEntry(nullptr), userActivityEventType(0), seq(0), - handled(false), - enabled(false) {} + handled(false) {} CommandEntry::~CommandEntry() {} diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index ebbd8e93bf..6f1dfadf59 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -104,9 +104,9 @@ struct FocusEntry : EventEntry { }; struct PointerCaptureChangedEntry : EventEntry { - bool pointerCaptureEnabled; + const PointerCaptureRequest pointerCaptureRequest; - PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, bool hasPointerCapture); + PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&); std::string getDescription() const override; ~PointerCaptureChangedEntry() override; @@ -284,7 +284,7 @@ struct CommandEntry { sp<IBinder> oldToken; sp<IBinder> newToken; std::string obscuringPackage; - bool enabled; + PointerCaptureRequest pointerCaptureRequest; int32_t pid; nsecs_t consumeTime; // time when the event was consumed by InputConsumer int32_t displayId; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a51978627f..6e9430a975 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -523,7 +523,6 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mInTouchMode(true), mMaximumObscuringOpacityForTouch(1.0f), mFocusedDisplayId(ADISPLAY_ID_DEFAULT), - mFocusedWindowRequestedPointerCapture(false), mWindowTokenWithPointerCapture(nullptr), mLatencyAggregator(), mLatencyTracker(&mLatencyAggregator), @@ -1311,36 +1310,51 @@ void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<F void InputDispatcher::dispatchPointerCaptureChangedLocked( nsecs_t currentTime, const std::shared_ptr<PointerCaptureChangedEntry>& entry, DropReason& dropReason) { - const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr; - if (entry->pointerCaptureEnabled && haveWindowWithPointerCapture) { - LOG_ALWAYS_FATAL("Pointer Capture has already been enabled for the window."); - } - if (!entry->pointerCaptureEnabled && !haveWindowWithPointerCapture) { - // Pointer capture was already forcefully disabled because of focus change. - dropReason = DropReason::NOT_DROPPED; - return; - } - - // Set drop reason for early returns - dropReason = DropReason::NO_POINTER_CAPTURE; + dropReason = DropReason::NOT_DROPPED; + const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr; sp<IBinder> token; - if (entry->pointerCaptureEnabled) { - // Enable Pointer Capture - if (!mFocusedWindowRequestedPointerCapture) { + + if (entry->pointerCaptureRequest.enable) { + // Enable Pointer Capture. + if (haveWindowWithPointerCapture && + (entry->pointerCaptureRequest == mCurrentPointerCaptureRequest)) { + LOG_ALWAYS_FATAL("This request to enable Pointer Capture has already been dispatched " + "to the window."); + } + if (!mCurrentPointerCaptureRequest.enable) { // This can happen if a window requests capture and immediately releases capture. ALOGW("No window requested Pointer Capture."); + dropReason = DropReason::NO_POINTER_CAPTURE; + return; + } + if (entry->pointerCaptureRequest.seq != mCurrentPointerCaptureRequest.seq) { + ALOGI("Skipping dispatch of Pointer Capture being enabled: sequence number mismatch."); return; } + token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId); LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture."); mWindowTokenWithPointerCapture = token; } else { - // Disable Pointer Capture + // Disable Pointer Capture. + // We do not check if the sequence number matches for requests to disable Pointer Capture + // for two reasons: + // 1. Pointer Capture can be disabled by a focus change, which means we can get two entries + // to disable capture with the same sequence number: one generated by + // disablePointerCaptureForcedLocked() and another as an acknowledgement of Pointer + // Capture being disabled in InputReader. + // 2. We respect any request to disable Pointer Capture generated by InputReader, since the + // actual Pointer Capture state that affects events being generated by input devices is + // in InputReader. + if (!haveWindowWithPointerCapture) { + // Pointer capture was already forcefully disabled because of focus change. + dropReason = DropReason::NOT_DROPPED; + return; + } token = mWindowTokenWithPointerCapture; mWindowTokenWithPointerCapture = nullptr; - if (mFocusedWindowRequestedPointerCapture) { - mFocusedWindowRequestedPointerCapture = false; + if (mCurrentPointerCaptureRequest.enable) { setPointerCaptureLocked(false); } } @@ -1349,8 +1363,7 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( if (channel == nullptr) { // Window has gone away, clean up Pointer Capture state. mWindowTokenWithPointerCapture = nullptr; - if (mFocusedWindowRequestedPointerCapture) { - mFocusedWindowRequestedPointerCapture = false; + if (mCurrentPointerCaptureRequest.enable) { setPointerCaptureLocked(false); } return; @@ -3185,7 +3198,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, static_cast<const PointerCaptureChangedEntry&>(eventEntry); status = connection->inputPublisher .publishCaptureEvent(dispatchEntry->seq, captureEntry.id, - captureEntry.pointerCaptureEnabled); + captureEntry.pointerCaptureRequest.enable); break; } @@ -3899,6 +3912,13 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->downTime, args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0); + if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && + IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER && + !mInputFilterEnabled) { + const bool isDown = args->action == AMOTION_EVENT_ACTION_DOWN; + mLatencyTracker.trackListener(args->id, isDown, args->eventTime, args->readTime); + } + needWake = enqueueInboundEventLocked(std::move(newEntry)); mLock.unlock(); } // release lock @@ -3983,14 +4003,14 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime, - args->enabled ? "true" : "false"); + args->request.enable ? "true" : "false"); #endif bool needWake; { // acquire lock std::scoped_lock _l(mLock); auto entry = std::make_unique<PointerCaptureChangedEntry>(args->id, args->eventTime, - args->enabled); + args->request); needWake = enqueueInboundEventLocked(std::move(entry)); } // release lock @@ -4934,8 +4954,8 @@ void InputDispatcher::logDispatchStateLocked() { std::string InputDispatcher::dumpPointerCaptureStateLocked() { std::string dump; - dump += StringPrintf(INDENT "FocusedWindowRequestedPointerCapture: %s\n", - toString(mFocusedWindowRequestedPointerCapture)); + dump += StringPrintf(INDENT "Pointer Capture Requested: %s\n", + toString(mCurrentPointerCaptureRequest.enable)); std::string windowName = "None"; if (mWindowTokenWithPointerCapture) { @@ -4944,7 +4964,7 @@ std::string InputDispatcher::dumpPointerCaptureStateLocked() { windowName = captureWindowHandle ? captureWindowHandle->getName().c_str() : "token has capture without window"; } - dump += StringPrintf(INDENT "CurrentWindowWithPointerCapture: %s\n", windowName.c_str()); + dump += StringPrintf(INDENT "Current Window with Pointer Capture: %s\n", windowName.c_str()); return dump; } @@ -5412,14 +5432,13 @@ void InputDispatcher::requestPointerCapture(const sp<IBinder>& windowToken, bool return; } - if (enabled == mFocusedWindowRequestedPointerCapture) { + if (enabled == mCurrentPointerCaptureRequest.enable) { ALOGW("Ignoring request to %s Pointer Capture: " "window has %s requested pointer capture.", enabled ? "enable" : "disable", enabled ? "already" : "not"); return; } - mFocusedWindowRequestedPointerCapture = enabled; setPointerCaptureLocked(enabled); } // release lock @@ -6178,14 +6197,13 @@ void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& ch } void InputDispatcher::disablePointerCaptureForcedLocked() { - if (!mFocusedWindowRequestedPointerCapture && !mWindowTokenWithPointerCapture) { + if (!mCurrentPointerCaptureRequest.enable && !mWindowTokenWithPointerCapture) { return; } ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus."); - if (mFocusedWindowRequestedPointerCapture) { - mFocusedWindowRequestedPointerCapture = false; + if (mCurrentPointerCaptureRequest.enable) { setPointerCaptureLocked(false); } @@ -6202,14 +6220,16 @@ void InputDispatcher::disablePointerCaptureForcedLocked() { } auto entry = std::make_unique<PointerCaptureChangedEntry>(mIdGenerator.nextId(), now(), - false /* hasCapture */); + mCurrentPointerCaptureRequest); mInboundQueue.push_front(std::move(entry)); } void InputDispatcher::setPointerCaptureLocked(bool enabled) { + mCurrentPointerCaptureRequest.enable = enabled; + mCurrentPointerCaptureRequest.seq++; std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( &InputDispatcher::doSetPointerCaptureLockedInterruptible); - commandEntry->enabled = enabled; + commandEntry->pointerCaptureRequest = mCurrentPointerCaptureRequest; postCommandLocked(std::move(commandEntry)); } @@ -6217,7 +6237,7 @@ void InputDispatcher::doSetPointerCaptureLockedInterruptible( android::inputdispatcher::CommandEntry* commandEntry) { mLock.unlock(); - mPolicy->setPointerCapture(commandEntry->enabled); + mPolicy->setPointerCapture(commandEntry->pointerCaptureRequest); mLock.lock(); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 9edf41c9c0..30652c65ee 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -357,10 +357,12 @@ private: // Keeps track of the focused window per display and determines focus changes. FocusResolver mFocusResolver GUARDED_BY(mLock); - // Whether the focused window on the focused display has requested Pointer Capture. - // The state of this variable should always be in sync with the state of Pointer Capture in the - // policy, which is updated through setPointerCaptureLocked(enabled). - bool mFocusedWindowRequestedPointerCapture GUARDED_BY(mLock); + + // The enabled state of this request is true iff the focused window on the focused display has + // requested Pointer Capture. This request also contains the sequence number associated with the + // current request. The state of this variable should always be in sync with the state of + // Pointer Capture in the policy, and is only updated through setPointerCaptureLocked(request). + PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock); // The window token that has Pointer Capture. // This should be in sync with PointerCaptureChangedEvents dispatched to the input channel. @@ -370,7 +372,7 @@ private: void disablePointerCaptureForcedLocked() REQUIRES(mLock); // Set the Pointer Capture state in the Policy. - void setPointerCaptureLocked(bool enabled) REQUIRES(mLock); + void setPointerCaptureLocked(bool enable) REQUIRES(mLock); // Dispatcher state at time of last ANR. std::string mLastAnrState GUARDED_BY(mLock); diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp index d634dcda1d..52f189c5c5 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.cpp +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -50,13 +50,12 @@ static bool isMatureEvent(nsecs_t eventTime, nsecs_t now) { * key-value pair. Equivalent to the imaginary std api std::multimap::erase(key, value). */ template <typename K, typename V> -static void eraseByKeyAndValue(std::multimap<K, V>& map, K key, V value) { - auto iterpair = map.equal_range(key); - - for (auto it = iterpair.first; it != iterpair.second; ++it) { +static void eraseByValue(std::multimap<K, V>& map, const V& value) { + for (auto it = map.begin(); it != map.end();) { if (it->second == value) { - map.erase(it); - break; + it = map.erase(it); + } else { + it++; } } } @@ -76,9 +75,7 @@ void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t ev // confuse us by reporting the rest of the timeline for one of them. This should happen // rarely, so we won't lose much data mTimelines.erase(it); - // In case we have another input event with a different id and at the same eventTime, - // only erase this specific inputEventId. - eraseByKeyAndValue(mEventTimes, eventTime, inputEventId); + eraseByValue(mEventTimes, inputEventId); return; } mTimelines.emplace(inputEventId, InputEventTimeline(isDown, eventTime, readTime)); @@ -90,7 +87,8 @@ void LatencyTracker::trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& nsecs_t finishTime) { const auto it = mTimelines.find(inputEventId); if (it == mTimelines.end()) { - // It's possible that an app sends a bad (or late)'Finish' signal, since it's free to do + // This could happen if we erased this event when duplicate events were detected. It's + // also possible that an app sent a bad (or late) 'Finish' signal, since it's free to do // anything in its process. Just drop the report and move on. return; } @@ -120,7 +118,8 @@ void LatencyTracker::trackGraphicsLatency( std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) { const auto it = mTimelines.find(inputEventId); if (it == mTimelines.end()) { - // It's possible that an app sends a bad (or late) 'Timeline' signal, since it's free to do + // This could happen if we erased this event when duplicate events were detected. It's + // also possible that an app sent a bad (or late) 'Timeline' signal, since it's free to do // anything in its process. Just drop the report and move on. return; } @@ -166,14 +165,6 @@ void LatencyTracker::reportAndPruneMatureRecords(nsecs_t newEventTime) { } } -void LatencyTracker::reportNow() { - for (const auto& [inputEventId, timeline] : mTimelines) { - mTimelineProcessor->processTimeline(timeline); - } - mTimelines.clear(); - mEventTimes.clear(); -} - std::string LatencyTracker::dump(const char* prefix) { return StringPrintf("%sLatencyTracker:\n", prefix) + StringPrintf("%s mTimelines.size() = %zu\n", prefix, mTimelines.size()) + diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h index 289b8ed6c4..4b0c618590 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -43,6 +43,12 @@ public: LatencyTracker(InputEventTimelineProcessor* processor); /** * Start keeping track of an event identified by inputEventId. This must be called first. + * If duplicate events are encountered (events that have the same eventId), none of them will be + * tracked. This is because there is not enough information to correctly track them. The api's + * 'trackFinishedEvent' and 'trackGraphicsLatency' only contain the inputEventId, and not the + * eventTime. Even if eventTime was provided, there would still be a possibility of having + * duplicate events that happen to have the same eventTime and inputEventId. Therefore, we + * must drop all duplicate data. */ void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime); void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken, @@ -50,14 +56,6 @@ public: void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); - /** - * Report all collected events immediately, even if some of them are currently incomplete - * and may receive 'trackFinishedEvent' or 'trackGraphicsLatency' calls in the future. - * This is useful for tests. Otherwise, tests would have to inject additional "future" events, - * which is not convenient. - */ - void reportNow(); - std::string dump(const char* prefix); private: diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 219f45a7c3..fd591e0e1c 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -156,7 +156,7 @@ public: * * InputDispatcher is solely responsible for updating the Pointer Capture state. */ - virtual void setPointerCapture(bool enabled) = 0; + virtual void setPointerCapture(const PointerCaptureRequest&) = 0; /* Notifies the policy that the drag window has moved over to another window */ virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0; diff --git a/services/inputflinger/docs/pointer_capture.md b/services/inputflinger/docs/pointer_capture.md index 8da699dc56..0b44187c08 100644 --- a/services/inputflinger/docs/pointer_capture.md +++ b/services/inputflinger/docs/pointer_capture.md @@ -17,6 +17,8 @@ `InputDispatcher` is responsible for controlling the state of Pointer Capture. Since the feature requires changes to how events are generated, Pointer Capture is configured in `InputReader`. +We use a sequence number to synchronize different requests to enable Pointer Capture between InputReader and InputDispatcher. + ### Enabling Pointer Capture There are four key steps that take place when Pointer Capture is enabled: @@ -40,5 +42,5 @@ When an `InputWindow` with Pointer Capture loses focus, Pointer Capture is disab `InputDispatcher` tracks two pieces of state information regarding Pointer Capture: -- `mFocusedWindowRequestedPointerCapture`: Whether or not the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`. +- `mCurrentPointerCaptureRequest`: The sequence number of the current Pointer Capture request. This request is enabled iff the focused window has requested Pointer Capture. This is updated whenever the Dispatcher receives requests from `InputManagerService`. - `mWindowTokenWithPointerCapture`: The Binder token of the `InputWindow` that currently has Pointer Capture. This is only updated during the dispatch cycle. If it is not `nullptr`, it signifies that the window was notified that it has Pointer Capture. diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index 4b7d26df2b..fe74214b36 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -211,11 +211,12 @@ struct NotifyDeviceResetArgs : public NotifyArgs { /* Describes a change in the state of Pointer Capture. */ struct NotifyPointerCaptureChangedArgs : public NotifyArgs { - bool enabled; + // The sequence number of the Pointer Capture request, if enabled. + PointerCaptureRequest request; inline NotifyPointerCaptureChangedArgs() {} - NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, bool enabled); + NotifyPointerCaptureChangedArgs(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&); NotifyPointerCaptureChangedArgs(const NotifyPointerCaptureChangedArgs& other); diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 7fdbbfdac4..3c8ac1cf7b 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -279,29 +279,30 @@ struct InputReaderConfiguration { // True to show the location of touches on the touch screen as spots. bool showTouches; - // True if pointer capture is enabled. - bool pointerCapture; + // The latest request to enable or disable Pointer Capture. + PointerCaptureRequest pointerCaptureRequest; // The set of currently disabled input devices. std::set<int32_t> disabledDevices; - InputReaderConfiguration() : - virtualKeyQuietTime(0), + InputReaderConfiguration() + : virtualKeyQuietTime(0), pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f), wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f), pointerGesturesEnabled(true), - pointerGestureQuietInterval(100 * 1000000LL), // 100 ms - pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second - pointerGestureTapInterval(150 * 1000000LL), // 150 ms - pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms - pointerGestureTapSlop(10.0f), // 10 pixels + pointerGestureQuietInterval(100 * 1000000LL), // 100 ms + pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second + pointerGestureTapInterval(150 * 1000000LL), // 150 ms + pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms + pointerGestureTapSlop(10.0f), // 10 pixels pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms - pointerGestureMultitouchMinDistance(15), // 15 pixels - pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees + pointerGestureMultitouchMinDistance(15), // 15 pixels + pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees pointerGestureSwipeMaxWidthRatio(0.25f), pointerGestureMovementSpeedRatio(0.8f), pointerGestureZoomSpeedRatio(0.3f), - showTouches(false), pointerCapture(false) { } + showTouches(false), + pointerCaptureRequest() {} static std::string changesToString(uint32_t changes); diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 10c04f606c..5120860503 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -367,9 +367,15 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { } if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) { - const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now, - mConfig.pointerCapture); - mQueuedListener->notifyPointerCaptureChanged(&args); + if (mCurrentPointerCaptureRequest == mConfig.pointerCaptureRequest) { + ALOGV("Skipping notifying pointer capture changes: " + "There was no change in the pointer capture state."); + } else { + mCurrentPointerCaptureRequest = mConfig.pointerCaptureRequest; + const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now, + mCurrentPointerCaptureRequest); + mQueuedListener->notifyPointerCaptureChanged(&args); + } } } diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index a00c5af4dc..e44aa0fe27 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -230,6 +230,8 @@ private: uint32_t mConfigurationChangesToRefresh GUARDED_BY(mLock); void refreshConfigurationLocked(uint32_t changes) REQUIRES(mLock); + PointerCaptureRequest mCurrentPointerCaptureRequest GUARDED_BY(mLock); + // state queries typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code); int32_t getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code, diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 437902a79b..2ac41b1e67 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -154,9 +154,9 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* mHWheelScale = 1.0f; } - if ((!changes && config->pointerCapture) || + if ((!changes && config->pointerCaptureRequest.enable) || (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) { - if (config->pointerCapture) { + if (config->pointerCaptureRequest.enable) { if (mParameters.mode == Parameters::MODE_POINTER) { mParameters.mode = Parameters::MODE_POINTER_RELATIVE; mSource = AINPUT_SOURCE_MOUSE_RELATIVE; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index f45731527f..ae89ca1ca9 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -603,7 +603,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { // Determine device mode. if (mParameters.deviceType == Parameters::DeviceType::POINTER && - mConfig.pointerGesturesEnabled && !mConfig.pointerCapture) { + mConfig.pointerGesturesEnabled && !mConfig.pointerCaptureRequest.enable) { mSource = AINPUT_SOURCE_MOUSE; mDeviceMode = DeviceMode::POINTER; if (hasStylus()) { @@ -776,11 +776,12 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { // preserve the cursor position. if (mDeviceMode == DeviceMode::POINTER || (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) || - (mParameters.deviceType == Parameters::DeviceType::POINTER && mConfig.pointerCapture)) { + (mParameters.deviceType == Parameters::DeviceType::POINTER && + mConfig.pointerCaptureRequest.enable)) { if (mPointerController == nullptr) { mPointerController = getContext()->getPointerController(getDeviceId()); } - if (mConfig.pointerCapture) { + if (mConfig.pointerCaptureRequest.enable) { mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); } } else { diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index dff0752b59..7d4c6386f8 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -239,19 +239,22 @@ public: mConfig.keyRepeatDelay = delay; } - void waitForSetPointerCapture(bool enabled) { + PointerCaptureRequest assertSetPointerCaptureCalled(bool enabled) { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); if (!mPointerCaptureChangedCondition.wait_for(lock, 100ms, [this, enabled]() REQUIRES(mLock) { - return mPointerCaptureEnabled && - *mPointerCaptureEnabled == + return mPointerCaptureRequest->enable == enabled; })) { - FAIL() << "Timed out waiting for setPointerCapture(" << enabled << ") to be called."; + ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << enabled + << ") to be called."; + return {}; } - mPointerCaptureEnabled.reset(); + auto request = *mPointerCaptureRequest; + mPointerCaptureRequest.reset(); + return request; } void assertSetPointerCaptureNotCalled() { @@ -259,11 +262,11 @@ public: base::ScopedLockAssertion assumeLocked(mLock); if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) { - FAIL() << "Expected setPointerCapture(enabled) to not be called, but was called. " + FAIL() << "Expected setPointerCapture(request) to not be called, but was called. " "enabled = " - << *mPointerCaptureEnabled; + << std::to_string(mPointerCaptureRequest->enable); } - mPointerCaptureEnabled.reset(); + mPointerCaptureRequest.reset(); } void assertDropTargetEquals(const sp<IBinder>& targetToken) { @@ -281,7 +284,8 @@ private: std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock); std::condition_variable mPointerCaptureChangedCondition; - std::optional<bool> mPointerCaptureEnabled GUARDED_BY(mLock); + + std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock); // ANR handling std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock); @@ -398,9 +402,9 @@ private: mOnPointerDownToken = newToken; } - void setPointerCapture(bool enabled) override { + void setPointerCapture(const PointerCaptureRequest& request) override { std::scoped_lock lock(mLock); - mPointerCaptureEnabled = {enabled}; + mPointerCaptureRequest = {request}; mPointerCaptureChangedCondition.notify_all(); } @@ -1379,8 +1383,9 @@ static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32 return generateMotionArgs(action, source, displayId, {PointF{100, 200}}); } -static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs(bool enabled) { - return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), enabled); +static NotifyPointerCaptureChangedArgs generatePointerCaptureChangedArgs( + const PointerCaptureRequest& request) { + return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), request); } TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) { @@ -4601,16 +4606,18 @@ protected: mWindow->consumeFocusEvent(true); } - void notifyPointerCaptureChanged(bool enabled) { - const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(enabled); + void notifyPointerCaptureChanged(const PointerCaptureRequest& request) { + const NotifyPointerCaptureChangedArgs args = generatePointerCaptureChangedArgs(request); mDispatcher->notifyPointerCaptureChanged(&args); } - void requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window, bool enabled) { + PointerCaptureRequest requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window, + bool enabled) { mDispatcher->requestPointerCapture(window->getToken(), enabled); - mFakePolicy->waitForSetPointerCapture(enabled); - notifyPointerCaptureChanged(enabled); + auto request = mFakePolicy->assertSetPointerCaptureCalled(enabled); + notifyPointerCaptureChanged(request); window->consumeCaptureEvent(enabled); + return request; } }; @@ -4632,7 +4639,7 @@ TEST_F(InputDispatcherPointerCaptureTests, EnablePointerCaptureWhenFocused) { } TEST_F(InputDispatcherPointerCaptureTests, DisablesPointerCaptureAfterWindowLosesFocus) { - requestAndVerifyPointerCapture(mWindow, true); + auto request = requestAndVerifyPointerCapture(mWindow, true); setFocusedWindow(mSecondWindow); @@ -4640,26 +4647,26 @@ TEST_F(InputDispatcherPointerCaptureTests, DisablesPointerCaptureAfterWindowLose mWindow->consumeCaptureEvent(false); mWindow->consumeFocusEvent(false); mSecondWindow->consumeFocusEvent(true); - mFakePolicy->waitForSetPointerCapture(false); + mFakePolicy->assertSetPointerCaptureCalled(false); // Ensure that additional state changes from InputReader are not sent to the window. - notifyPointerCaptureChanged(false); - notifyPointerCaptureChanged(true); - notifyPointerCaptureChanged(false); + notifyPointerCaptureChanged({}); + notifyPointerCaptureChanged(request); + notifyPointerCaptureChanged({}); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); mFakePolicy->assertSetPointerCaptureNotCalled(); } TEST_F(InputDispatcherPointerCaptureTests, UnexpectedStateChangeDisablesPointerCapture) { - requestAndVerifyPointerCapture(mWindow, true); + auto request = requestAndVerifyPointerCapture(mWindow, true); // InputReader unexpectedly disables and enables pointer capture. - notifyPointerCaptureChanged(false); - notifyPointerCaptureChanged(true); + notifyPointerCaptureChanged({}); + notifyPointerCaptureChanged(request); // Ensure that Pointer Capture is disabled. - mFakePolicy->waitForSetPointerCapture(false); + mFakePolicy->assertSetPointerCaptureCalled(false); mWindow->consumeCaptureEvent(false); mWindow->assertNoEvents(); } @@ -4669,24 +4676,43 @@ TEST_F(InputDispatcherPointerCaptureTests, OutOfOrderRequests) { // The first window loses focus. setFocusedWindow(mSecondWindow); - mFakePolicy->waitForSetPointerCapture(false); + mFakePolicy->assertSetPointerCaptureCalled(false); mWindow->consumeCaptureEvent(false); // Request Pointer Capture from the second window before the notification from InputReader // arrives. mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true); - mFakePolicy->waitForSetPointerCapture(true); + auto request = mFakePolicy->assertSetPointerCaptureCalled(true); // InputReader notifies Pointer Capture was disabled (because of the focus change). - notifyPointerCaptureChanged(false); + notifyPointerCaptureChanged({}); // InputReader notifies Pointer Capture was enabled (because of mSecondWindow's request). - notifyPointerCaptureChanged(true); + notifyPointerCaptureChanged(request); mSecondWindow->consumeFocusEvent(true); mSecondWindow->consumeCaptureEvent(true); } +TEST_F(InputDispatcherPointerCaptureTests, EnableRequestFollowsSequenceNumbers) { + // App repeatedly enables and disables capture. + mDispatcher->requestPointerCapture(mWindow->getToken(), true); + auto firstRequest = mFakePolicy->assertSetPointerCaptureCalled(true); + mDispatcher->requestPointerCapture(mWindow->getToken(), false); + mFakePolicy->assertSetPointerCaptureCalled(false); + mDispatcher->requestPointerCapture(mWindow->getToken(), true); + auto secondRequest = mFakePolicy->assertSetPointerCaptureCalled(true); + + // InputReader notifies that PointerCapture has been enabled for the first request. Since the + // first request is now stale, this should do nothing. + notifyPointerCaptureChanged(firstRequest); + mWindow->assertNoEvents(); + + // InputReader notifies that the second request was enabled. + notifyPointerCaptureChanged(secondRequest); + mWindow->consumeCaptureEvent(true); +} + class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest { protected: constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 997cbe88a1..38dfe4041f 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -299,8 +299,9 @@ public: transform = t; } - void setPointerCapture(bool enabled) { - mConfig.pointerCapture = enabled; + PointerCaptureRequest setPointerCapture(bool enabled) { + mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++}; + return mConfig.pointerCaptureRequest; } void setShowTouches(bool enabled) { @@ -314,6 +315,8 @@ public: float getPointerGestureMovementSpeedRatio() { return mConfig.pointerGestureMovementSpeedRatio; } private: + uint32_t mNextPointerCaptureSequenceNumber = 0; + DisplayViewport createDisplayViewport(int32_t displayId, int32_t width, int32_t height, int32_t orientation, bool isActive, const std::string& uniqueId, @@ -1961,24 +1964,24 @@ TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToSubdeviceMappers) { TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) { NotifyPointerCaptureChangedArgs args; - mFakePolicy->setPointerCapture(true); + auto request = mFakePolicy->setPointerCapture(true); mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE); mReader->loopOnce(); mFakeListener->assertNotifyCaptureWasCalled(&args); - ASSERT_TRUE(args.enabled) << "Pointer Capture should be enabled."; + ASSERT_TRUE(args.request.enable) << "Pointer Capture should be enabled."; + ASSERT_EQ(args.request, request) << "Pointer Capture sequence number should match."; mFakePolicy->setPointerCapture(false); mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE); mReader->loopOnce(); mFakeListener->assertNotifyCaptureWasCalled(&args); - ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled."; + ASSERT_FALSE(args.request.enable) << "Pointer Capture should be disabled."; - // Verify that the Pointer Capture state is re-configured correctly when the configuration value + // Verify that the Pointer Capture state is not updated when the configuration value // does not change. mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_POINTER_CAPTURE); mReader->loopOnce(); - mFakeListener->assertNotifyCaptureWasCalled(&args); - ASSERT_FALSE(args.enabled) << "Pointer Capture should be disabled."; + mFakeListener->assertNotifyCaptureWasNotCalled(); } class FakeVibratorInputMapper : public FakeInputMapper { diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index e7e1937235..89c0741dfd 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -16,6 +16,7 @@ #include "../dispatcher/LatencyTracker.h" +#include <android-base/properties.h> #include <binder/Binder.h> #include <gtest/gtest.h> #include <inttypes.h> @@ -23,11 +24,16 @@ #define TAG "LatencyTracker_test" +using android::base::HwTimeoutMultiplier; using android::inputdispatcher::InputEventTimeline; using android::inputdispatcher::LatencyTracker; namespace android::inputdispatcher { +const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds( + android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS * + HwTimeoutMultiplier()); + InputEventTimeline getTestTimeline() { InputEventTimeline t( /*isDown*/ true, @@ -57,6 +63,8 @@ protected: } void TearDown() override {} + void triggerEventReporting(nsecs_t lastEventTime); + void assertReceivedTimeline(const InputEventTimeline& timeline); /** * Timelines can be received in any order (order is not guaranteed). So if we are expecting more @@ -72,8 +80,17 @@ private: std::deque<InputEventTimeline> mReceivedTimelines; }; +/** + * Send an event that would trigger the reporting of all of the events that are at least as old as + * the provided 'lastEventTime'. + */ +void LatencyTrackerTest::triggerEventReporting(nsecs_t lastEventTime) { + const nsecs_t triggerEventTime = + lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1; + mTracker->trackListener(1 /*inputEventId*/, true /*isDown*/, triggerEventTime, 3 /*readTime*/); +} + void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) { - mTracker->reportNow(); ASSERT_FALSE(mReceivedTimelines.empty()); const InputEventTimeline& t = mReceivedTimelines.front(); ASSERT_EQ(timeline, t); @@ -88,7 +105,6 @@ void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeli * equal element in B, and for every element in B there is an equal element in A. */ void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines) { - mTracker->reportNow(); ASSERT_EQ(timelines.size(), mReceivedTimelines.size()); for (const InputEventTimeline& expectedTimeline : timelines) { bool found = false; @@ -121,6 +137,7 @@ void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTim */ TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) { mTracker->trackListener(1 /*inputEventId*/, false /*isDown*/, 2 /*eventTime*/, 3 /*readTime*/); + triggerEventReporting(2 /*eventTime*/); assertReceivedTimeline(InputEventTimeline{false, 2, 3}); } @@ -130,6 +147,7 @@ TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) { TEST_F(LatencyTrackerTest, TrackFinishedEvent_DoesNotTriggerReporting) { mTracker->trackFinishedEvent(1 /*inputEventId*/, connection1, 2 /*deliveryTime*/, 3 /*consumeTime*/, 4 /*finishTime*/); + triggerEventReporting(4 /*eventTime*/); assertReceivedTimelines({}); } @@ -141,6 +159,7 @@ TEST_F(LatencyTrackerTest, TrackGraphicsLatency_DoesNotTriggerReporting) { graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2; graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3; mTracker->trackGraphicsLatency(1 /*inputEventId*/, connection2, graphicsTimeline); + triggerEventReporting(3 /*eventTime*/); assertReceivedTimelines({}); } @@ -155,9 +174,30 @@ TEST_F(LatencyTrackerTest, TrackAllParameters_ReportsFullTimeline) { expectedCT.consumeTime, expectedCT.finishTime); mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline); + triggerEventReporting(expected.eventTime); assertReceivedTimeline(expected); } +/** + * Send 2 events with the same inputEventId, but different eventTime's. Ensure that no crash occurs, + * and that the tracker drops such events completely. + */ +TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) { + constexpr nsecs_t inputEventId = 1; + constexpr nsecs_t readTime = 3; // does not matter for this test + constexpr bool isDown = true; // does not matter for this test + + // In the following 2 calls to trackListener, the inputEventId's are the same, but event times + // are different. + mTracker->trackListener(inputEventId, isDown, 1 /*eventTime*/, readTime); + mTracker->trackListener(inputEventId, isDown, 2 /*eventTime*/, readTime); + + triggerEventReporting(2 /*eventTime*/); + // Since we sent duplicate input events, the tracker should just delete all of them, because it + // does not have enough information to properly track them. + assertReceivedTimelines({}); +} + TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { constexpr int32_t inputEventId1 = 1; InputEventTimeline timeline1( @@ -204,6 +244,7 @@ TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { mTracker->trackGraphicsLatency(inputEventId2, connection2, connectionTimeline2.graphicsTimeline); // Now both events should be completed + triggerEventReporting(timeline2.eventTime); assertReceivedTimelines({timeline1, timeline2}); } @@ -228,6 +269,7 @@ TEST_F(LatencyTrackerTest, IncompleteEvents_AreHandledConsistently) { mTracker->trackGraphicsLatency(1 /*inputEventId*/, token, expectedCT.graphicsTimeline); expectedTimelines[0].connectionTimelines.emplace(token, std::move(expectedCT)); + triggerEventReporting(timeline.eventTime); assertReceivedTimelines(expectedTimelines); } @@ -246,6 +288,7 @@ TEST_F(LatencyTrackerTest, EventsAreTracked_WhenTrackListenerIsCalledFirst) { mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline); mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime); + triggerEventReporting(expected.eventTime); assertReceivedTimeline( InputEventTimeline{expected.isDown, expected.eventTime, expected.readTime}); } diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index fb7de97b49..6a26c636d9 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -100,6 +100,11 @@ void TestInputListener::assertNotifyCaptureWasCalled( "to have been called.")); } +void TestInputListener::assertNotifyCaptureWasNotCalled() { + ASSERT_NO_FATAL_FAILURE(assertNotCalled<NotifyPointerCaptureChangedArgs>( + "notifyPointerCaptureChanged() should not be called.")); +} + template <class NotifyArgsType> void TestInputListener::assertCalled(NotifyArgsType* outEventArgs, std::string message) { std::unique_lock<std::mutex> lock(mLock); diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 0ffcaaa527..0a1dc4b0b9 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -55,6 +55,7 @@ public: void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr); void assertNotifyCaptureWasCalled(NotifyPointerCaptureChangedArgs* outEventArgs = nullptr); + void assertNotifyCaptureWasNotCalled(); void assertNotifySensorWasCalled(NotifySensorArgs* outEventArgs = nullptr); void assertNotifyVibratorStateWasCalled(NotifyVibratorStateArgs* outEventArgs = nullptr); diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp new file mode 100644 index 0000000000..df4db199f8 --- /dev/null +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -0,0 +1,45 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + + +cc_fuzz { + name: "inputflinger_latencytracker_fuzzer", + defaults: [ + "inputflinger_defaults", + ], + include_dirs: [ + "frameworks/native/services/inputflinger", + ], + shared_libs: [ + "libbase", + "libbinder", + "liblog", + "libui", + "libutils", + "libinput", + "libinputflinger", + ], + srcs: [ + "LatencyTrackerFuzzer.cpp", + ], +} diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp new file mode 100644 index 0000000000..4f066ad513 --- /dev/null +++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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 "dispatcher/LatencyTracker.h" + +namespace android { + +namespace inputdispatcher { + +/** + * A processor of InputEventTimelines that does nothing with the provided data. + */ +class EmptyProcessor : public InputEventTimelineProcessor { +public: + /** + * Just ignore the provided timeline + */ + void processTimeline(const InputEventTimeline& timeline) override { + for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) { + connectionTimeline.isComplete(); + } + }; +}; + +static sp<IBinder> getConnectionToken(FuzzedDataProvider& fdp, + std::array<sp<IBinder>, 10>& tokens) { + const bool useExistingToken = fdp.ConsumeBool(); + if (useExistingToken) { + return tokens[fdp.ConsumeIntegralInRange<size_t>(0ul, tokens.size() - 1)]; + } + return new BBinder(); +} + +extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + + EmptyProcessor emptyProcessor; + LatencyTracker tracker(&emptyProcessor); + + // Make some pre-defined tokens to ensure that some timelines are complete. + std::array<sp<IBinder> /*token*/, 10> predefinedTokens; + for (size_t i = 0; i < predefinedTokens.size(); i++) { + predefinedTokens[i] = new BBinder(); + } + + // Randomly invoke LatencyTracker api's until randomness is exhausted. + while (fdp.remaining_bytes() > 0) { + fdp.PickValueInArray<std::function<void()>>({ + [&]() -> void { + int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); + int32_t isDown = fdp.ConsumeBool(); + nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>(); + nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>(); + tracker.trackListener(inputEventId, isDown, eventTime, readTime); + }, + [&]() -> void { + int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); + sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens); + nsecs_t deliveryTime = fdp.ConsumeIntegral<nsecs_t>(); + nsecs_t consumeTime = fdp.ConsumeIntegral<nsecs_t>(); + nsecs_t finishTime = fdp.ConsumeIntegral<nsecs_t>(); + tracker.trackFinishedEvent(inputEventId, connectionToken, deliveryTime, + consumeTime, finishTime); + }, + [&]() -> void { + int32_t inputEventId = fdp.ConsumeIntegral<int32_t>(); + sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens); + std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline; + for (size_t i = 0; i < graphicsTimeline.size(); i++) { + graphicsTimeline[i] = fdp.ConsumeIntegral<nsecs_t>(); + } + tracker.trackGraphicsLatency(inputEventId, connectionToken, graphicsTimeline); + }, + })(); + } + + return 0; +} + +} // namespace inputdispatcher + +} // namespace android
\ No newline at end of file diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 1be5a96f67..b596708a72 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -78,6 +78,11 @@ cc_library_shared { "libsensorprivacy", "libpermission", ], + + pgo: { + sampling: true, + profile_file: "sensorservice/libsensorservice.profdata", + }, } cc_binary { diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index c233bf06cf..ec31afe367 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -326,7 +326,7 @@ void SensorDevice::reconnect() { mActivationCount.clear(); mSensorList.clear(); - if (connectHidlServiceV2_0() == HalConnectionStatus::CONNECTED) { + if (connectHidlService()) { initializeSensorList(); if (sensorHandlesChanged(previousSensorList, mSensorList)) { diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index e26ab117b8..82a9ae2578 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -326,7 +326,7 @@ status_t VirtualDisplaySurface::setAsyncMode(bool async) { status_t VirtualDisplaySurface::dequeueBuffer(Source source, PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) { - LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId)); + LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value()); status_t result = mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight, @@ -641,7 +641,7 @@ void VirtualDisplaySurface::resetPerFrameState() { } status_t VirtualDisplaySurface::refreshOutputBuffer() { - LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId)); + LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value()); if (mOutputProducerSlot >= 0) { mSource[SOURCE_SINK]->cancelBuffer( diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 75dd51c992..d8cead5e50 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -820,11 +820,7 @@ void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) { } bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) { - sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get()); - if (handle == nullptr) { - return false; - } - sp<Layer> relative = handle->owner.promote(); + sp<Layer> relative = fromHandle(relativeToHandle).promote(); if (relative == nullptr) { return false; } @@ -1610,8 +1606,7 @@ void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { bool Layer::reparent(const sp<IBinder>& newParentHandle) { sp<Layer> newParent; if (newParentHandle != nullptr) { - auto handle = static_cast<Handle*>(newParentHandle.get()); - newParent = handle->owner.promote(); + newParent = fromHandle(newParentHandle).promote(); if (newParent == nullptr) { ALOGE("Unable to promote Layer handle"); return false; @@ -1986,24 +1981,10 @@ void Layer::commitChildList() { mDrawingParent = mCurrentParent; } -static wp<Layer> extractLayerFromBinder(const wp<IBinder>& weakBinderHandle) { - if (weakBinderHandle == nullptr) { - return nullptr; - } - sp<IBinder> binderHandle = weakBinderHandle.promote(); - if (binderHandle == nullptr) { - return nullptr; - } - sp<Layer::Handle> handle = static_cast<Layer::Handle*>(binderHandle.get()); - if (handle == nullptr) { - return nullptr; - } - return handle->owner; -} void Layer::setInputInfo(const InputWindowInfo& info) { mDrawingState.inputInfo = info; - mDrawingState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle); + mDrawingState.touchableRegionCrop = fromHandle(info.touchableRegionCropHandle.promote()); mDrawingState.modified = true; mFlinger->mInputInfoChanged = true; setTransactionFlags(eTransactionNeeded); @@ -2562,6 +2543,23 @@ void Layer::setClonedChild(const sp<Layer>& clonedChild) { mFlinger->mNumClones++; } +const String16 Layer::Handle::kDescriptor = String16("android.Layer.Handle"); + +wp<Layer> Layer::fromHandle(const sp<IBinder>& handleBinder) { + if (handleBinder == nullptr) { + return nullptr; + } + + BBinder* b = handleBinder->localBinder(); + if (b == nullptr || b->getInterfaceDescriptor() != Handle::kDescriptor) { + return nullptr; + } + + // We can safely cast this binder since its local and we verified its interface descriptor. + sp<Handle> handle = static_cast<Handle*>(handleBinder.get()); + return handle->owner; +} + // --------------------------------------------------------------------------- std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) { diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 59f5b0dc73..8905548b64 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -289,16 +289,17 @@ public: class LayerCleaner { sp<SurfaceFlinger> mFlinger; sp<Layer> mLayer; + BBinder* mHandle; protected: ~LayerCleaner() { // destroy client resources - mFlinger->onHandleDestroyed(mLayer); + mFlinger->onHandleDestroyed(mHandle, mLayer); } public: - LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer) - : mFlinger(flinger), mLayer(layer) {} + LayerCleaner(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer, BBinder* handle) + : mFlinger(flinger), mLayer(layer), mHandle(handle) {} }; /* @@ -312,11 +313,15 @@ public: class Handle : public BBinder, public LayerCleaner { public: Handle(const sp<SurfaceFlinger>& flinger, const sp<Layer>& layer) - : LayerCleaner(flinger, layer), owner(layer) {} + : LayerCleaner(flinger, layer, this), owner(layer) {} + const String16& getInterfaceDescriptor() const override { return kDescriptor; } + static const String16 kDescriptor; wp<Layer> owner; }; + static wp<Layer> fromHandle(const sp<IBinder>& handle); + explicit Layer(const LayerCreationArgs& args); virtual ~Layer(); diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 0334d70bd5..73b7b6365c 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -911,7 +911,10 @@ RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction int RefreshRateConfigs::getFrameRateDivider(Fps displayFrameRate, Fps layerFrameRate) { // This calculation needs to be in sync with the java code // in DisplayManagerService.getDisplayInfoForFrameRateOverride - constexpr float kThreshold = 0.1f; + + // The threshold must be smaller than 0.001 in order to differentiate + // between the fractional pairs (e.g. 59.94 and 60). + constexpr float kThreshold = 0.0009f; const auto numPeriods = displayFrameRate.getValue() / layerFrameRate.getValue(); const auto numPeriodsRounded = std::round(numPeriods); if (std::abs(numPeriods - numPeriodsRounded) > kThreshold) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index e0b364020b..64ad178b26 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -386,6 +386,13 @@ void Scheduler::dispatchCachedReportedMode() { } const auto modeId = *mFeatures.modeId; + // If the modeId is not the current mode, this means that a + // mode change is in progress. In that case we shouldn't dispatch an event + // as it will be dispatched when the current mode changes. + if (mRefreshRateConfigs.getCurrentRefreshRate().getModeId() != modeId) { + return; + } + const auto vsyncPeriod = mRefreshRateConfigs.getRefreshRateFromModeId(modeId).getVsyncPeriod(); // If there is no change from cached mode, there is no need to dispatch an event diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 230810c936..6b5094fd21 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -653,6 +653,17 @@ std::vector<PhysicalDisplayId> SurfaceFlinger::getPhysicalDisplayIds() const { return displayIds; } +status_t SurfaceFlinger::getPrimaryPhysicalDisplayId(PhysicalDisplayId* id) const { + Mutex::Autolock lock(mStateLock); + const auto display = getInternalDisplayIdLocked(); + if (!display) { + return NAME_NOT_FOUND; + } + + *id = *display; + return NO_ERROR; +} + sp<IBinder> SurfaceFlinger::getPhysicalDisplayToken(PhysicalDisplayId displayId) const { Mutex::Autolock lock(mStateLock); return getPhysicalDisplayTokenLocked(displayId); @@ -3611,7 +3622,7 @@ bool SurfaceFlinger::transactionIsReadyToBeApplied( sp<Layer> layer = nullptr; if (s.surface) { - layer = fromHandleLocked(s.surface).promote(); + layer = fromHandle(s.surface).promote(); } else if (s.hasBufferChanges()) { ALOGW("Transaction with buffer, but no Layer?"); continue; @@ -3778,7 +3789,7 @@ void SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin setClientStateLocked(frameTimelineInfo, state, desiredPresentTime, isAutoTimestamp, postTime, permissions, listenerCallbacksWithSurfaces); if ((flags & eAnimation) && state.state.surface) { - if (const auto layer = fromHandleLocked(state.state.surface).promote(); layer) { + if (const auto layer = fromHandle(state.state.surface).promote(); layer) { mScheduler->recordLayerHistory(layer.get(), isAutoTimestamp ? 0 : desiredPresentTime, LayerHistory::LayerUpdateType::AnimationTX); @@ -3933,13 +3944,11 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (what & layer_state_t::eLayerCreated) { layer = handleLayerCreatedLocked(s.surface); if (layer) { - // put the created layer into mLayersByLocalBinderToken. - mLayersByLocalBinderToken.emplace(s.surface->localBinder(), layer); flags |= eTransactionNeeded | eTraversalNeeded; mLayersAdded = true; } } else { - layer = fromHandleLocked(s.surface).promote(); + layer = fromHandle(s.surface).promote(); } } else { // The client may provide us a null handle. Treat it as if the layer was removed. @@ -4267,7 +4276,7 @@ status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder> { Mutex::Autolock _l(mStateLock); - mirrorFrom = fromHandleLocked(mirrorFromHandle).promote(); + mirrorFrom = fromHandle(mirrorFromHandle).promote(); if (!mirrorFrom) { return NAME_NOT_FOUND; } @@ -4465,7 +4474,7 @@ void SurfaceFlinger::markLayerPendingRemovalLocked(const sp<Layer>& layer) { setTransactionFlags(eTransactionNeeded); } -void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer) { +void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer) { Mutex::Autolock lock(mStateLock); // If a layer has a parent, we allow it to out-live it's handle // with the idea that the parent holds a reference and will eventually @@ -4476,17 +4485,7 @@ void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer) { mCurrentState.layersSortedByZ.remove(layer); } markLayerPendingRemovalLocked(layer); - - auto it = mLayersByLocalBinderToken.begin(); - while (it != mLayersByLocalBinderToken.end()) { - if (it->second == layer) { - mBufferCountTracker.remove(it->first->localBinder()); - it = mLayersByLocalBinderToken.erase(it); - } else { - it++; - } - } - + mBufferCountTracker.remove(handle); layer.clear(); } @@ -5238,6 +5237,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case REMOVE_TUNNEL_MODE_ENABLED_LISTENER: case NOTIFY_POWER_BOOST: case SET_GLOBAL_SHADOW_SETTINGS: + case GET_PRIMARY_PHYSICAL_DISPLAY_ID: case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: { // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests, // which acquire the necessary permission dynamically. Don't use the permission cache @@ -6089,7 +6089,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, { Mutex::Autolock lock(mStateLock); - parent = fromHandleLocked(args.layerHandle).promote(); + parent = fromHandle(args.layerHandle).promote(); if (parent == nullptr || parent->isRemovedFromCurrentState()) { ALOGE("captureLayers called with an invalid or removed parent"); return NAME_NOT_FOUND; @@ -6120,7 +6120,7 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY); for (const auto& handle : args.excludeHandles) { - sp<Layer> excludeLayer = fromHandleLocked(handle).promote(); + sp<Layer> excludeLayer = fromHandle(handle).promote(); if (excludeLayer != nullptr) { excludeLayers.emplace(excludeLayer); } else { @@ -6616,24 +6616,8 @@ status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayTo } } -wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) { - Mutex::Autolock _l(mStateLock); - return fromHandleLocked(handle); -} - -wp<Layer> SurfaceFlinger::fromHandleLocked(const sp<IBinder>& handle) const { - BBinder* b = nullptr; - if (handle) { - b = handle->localBinder(); - } - if (b == nullptr) { - return nullptr; - } - auto it = mLayersByLocalBinderToken.find(b); - if (it != mLayersByLocalBinderToken.end()) { - return it->second; - } - return nullptr; +wp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) const { + return Layer::fromHandle(handle); } void SurfaceFlinger::onLayerFirstRef(Layer* layer) { @@ -6938,7 +6922,7 @@ sp<Layer> SurfaceFlinger::handleLayerCreatedLocked(const sp<IBinder>& handle) { sp<Layer> parent; bool allowAddRoot = state->addToRoot; if (state->initialParent != nullptr) { - parent = fromHandleLocked(state->initialParent.promote()).promote(); + parent = fromHandle(state->initialParent.promote()).promote(); if (parent == nullptr) { ALOGE("Invalid parent %p", state->initialParent.unsafe_get()); allowAddRoot = false; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 380f444221..4e8e614cbd 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -327,8 +327,7 @@ public: // Returns nullptr if the handle does not point to an existing layer. // Otherwise, returns a weak reference so that callers off the main-thread // won't accidentally hold onto the last strong reference. - wp<Layer> fromHandle(const sp<IBinder>& handle); - wp<Layer> fromHandleLocked(const sp<IBinder>& handle) const REQUIRES(mStateLock); + wp<Layer> fromHandle(const sp<IBinder>& handle) const; // If set, disables reusing client composition buffers. This can be set by // debug.sf.disable_client_composition_cache @@ -614,6 +613,7 @@ private: sp<IBinder> createDisplay(const String8& displayName, bool secure) override; void destroyDisplay(const sp<IBinder>& displayToken) override; std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const override; + status_t getPrimaryPhysicalDisplayId(PhysicalDisplayId*) const override EXCLUDES(mStateLock); sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId) const override; status_t setTransactionState(const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& state, @@ -898,7 +898,7 @@ private: // called when all clients have released all their references to // this layer meaning it is entirely safe to destroy all // resources associated to this layer. - void onHandleDestroyed(sp<Layer>& layer); + void onHandleDestroyed(BBinder* handle, sp<Layer>& layer); void markLayerPendingRemovalLocked(const sp<Layer>& layer); // add a layer to SurfaceFlinger @@ -1290,8 +1290,6 @@ private: std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal; } mVirtualDisplayIdGenerators; - std::unordered_map<BBinder*, wp<Layer>> mLayersByLocalBinderToken GUARDED_BY(mStateLock); - // don't use a lock for these, we don't care int mDebugRegion = 0; bool mDebugDisableHWC = false; diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index 4a69c8f0c3..e15eae8c36 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -34,6 +34,8 @@ using android::hardware::graphics::common::V1_2::Dataspace; using android::hardware::graphics::common::V1_2::PixelFormat; using android::ui::DisplayPrimaries; +// Keep logic in sync with WindowManagerService functions that query SurfaceFlinger properties. +// Consider exposing properties via ISurfaceComposer instead. int64_t vsync_event_phase_offset_ns(int64_t defaultValue) { auto temp = SurfaceFlingerProperties::vsync_event_phase_offset_ns(); if (temp.has_value()) { diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 23ab7c8bab..837b2e8924 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -183,12 +183,9 @@ status_t SurfaceInterceptor::writeProtoFileLocked() { return NO_ERROR; } -const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) const { - const sp<const IBinder>& handle(weakHandle.promote()); - const auto layerHandle(static_cast<const Layer::Handle*>(handle.get())); - const sp<const Layer> layer(layerHandle->owner.promote()); - // layer could be a nullptr at this point - return layer; +const sp<const Layer> SurfaceInterceptor::getLayer(const wp<IBinder>& weakHandle) const { + sp<IBinder> handle = weakHandle.promote(); + return Layer::fromHandle(handle).promote(); } int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const { @@ -203,12 +200,11 @@ int32_t SurfaceInterceptor::getLayerIdFromWeakRef(const wp<const Layer>& layer) return strongLayer == nullptr ? -1 : getLayerId(strongLayer); } -int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<const IBinder>& handle) const { +int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<IBinder>& handle) const { if (handle == nullptr) { return -1; } - const auto layerHandle(static_cast<const Layer::Handle*>(handle.get())); - const sp<const Layer> layer(layerHandle->owner.promote()); + const sp<const Layer> layer = Layer::fromHandle(handle).promote(); return layer == nullptr ? -1 : getLayerId(layer); } diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h index 673f9e789d..c9555969dc 100644 --- a/services/surfaceflinger/SurfaceInterceptor.h +++ b/services/surfaceflinger/SurfaceInterceptor.h @@ -133,10 +133,10 @@ private: void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display); status_t writeProtoFileLocked(); - const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle) const; + const sp<const Layer> getLayer(const wp<IBinder>& weakHandle) const; int32_t getLayerId(const sp<const Layer>& layer) const; int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const; - int32_t getLayerIdFromHandle(const sp<const IBinder>& weakHandle) const; + int32_t getLayerIdFromHandle(const sp<IBinder>& weakHandle) const; Increment* createTraceIncrementLocked(); void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer); diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp index 673239dfa9..caeff4ab21 100644 --- a/services/surfaceflinger/main_surfaceflinger.cpp +++ b/services/surfaceflinger/main_surfaceflinger.cpp @@ -63,18 +63,17 @@ static status_t startGraphicsAllocatorService() { return OK; } -static status_t startDisplayService() { +static void startDisplayService() { using android::frameworks::displayservice::V1_0::implementation::DisplayService; using android::frameworks::displayservice::V1_0::IDisplayService; sp<IDisplayService> displayservice = new DisplayService(); status_t err = displayservice->registerAsService(); + // b/141930622 if (err != OK) { - ALOGE("Could not register IDisplayService service."); + ALOGE("Did not register (deprecated) IDisplayService service."); } - - return err; } int main(int, char**) { diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc index 575e70d779..39d7bd948d 100644 --- a/services/surfaceflinger/surfaceflinger.rc +++ b/services/surfaceflinger/surfaceflinger.rc @@ -3,7 +3,7 @@ service surfaceflinger /system/bin/surfaceflinger user system group graphics drmrpc readproc capabilities SYS_NICE - onrestart restart zygote + onrestart restart --only-if-running zygote task_profiles HighPerformance socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0 socket pdx/system/vr/display/manager stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0 diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 3423bd590b..3b2bd818b7 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -2123,7 +2123,11 @@ TEST_F(RefreshRateConfigsTest, getFrameRateDivider) { refreshRateConfigs->setCurrentModeId(HWC_CONFIG_ID_90); displayRefreshRate = refreshRateConfigs->getCurrentRefreshRate().getFps(); EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.5f))); - EXPECT_EQ(4, RefreshRateConfigs::getFrameRateDivider(displayRefreshRate, Fps(22.6f))); + + EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(25.f))); + EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(24.f), Fps(23.976f))); + EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(30.f), Fps(29.97f))); + EXPECT_EQ(0, RefreshRateConfigs::getFrameRateDivider(Fps(60.f), Fps(59.94f))); } TEST_F(RefreshRateConfigsTest, getFrameRateOverrides_noLayers) { diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp index d1cd397da4..fa3b2601a4 100644 --- a/vulkan/libvulkan/api.cpp +++ b/vulkan/libvulkan/api.cpp @@ -965,6 +965,13 @@ VkResult LayerChain::ValidateExtensions(VkPhysicalDevice physical_dev, VkResult result = EnumerateDeviceExtensionProperties(physical_dev, nullptr, &count, nullptr); if (result == VK_SUCCESS && count) { + // Work-around a race condition during Android start-up, that can result + // in the second call to EnumerateDeviceExtensionProperties having + // another extension. That causes the second call to return + // VK_INCOMPLETE. A work-around is to add 1 to "count" and ask for one + // more extension property. See: http://anglebug.com/6715 and + // internal-to-Google b/206733351. + count++; driver_extensions_ = AllocateDriverExtensionArray(count); result = (driver_extensions_) ? EnumerateDeviceExtensionProperties( diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 271558784e..6191063894 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -537,30 +537,6 @@ android_dataspace GetNativeDataspace(VkColorSpaceKHR colorspace) { } } -int get_min_buffer_count(ANativeWindow* window, - uint32_t* out_min_buffer_count) { - constexpr int kExtraBuffers = 2; - - int err; - int min_undequeued_buffers; - err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, - &min_undequeued_buffers); - if (err != android::OK || min_undequeued_buffers < 0) { - ALOGE( - "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) " - "value=%d", - strerror(-err), err, min_undequeued_buffers); - if (err == android::OK) { - err = android::UNKNOWN_ERROR; - } - return err; - } - - *out_min_buffer_count = - static_cast<uint32_t>(min_undequeued_buffers + kExtraBuffers); - return android::OK; -} - } // anonymous namespace VKAPI_ATTR @@ -675,7 +651,7 @@ VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; } - capabilities->minImageCount = max_buffer_count == 1 ? 1 : 2; + capabilities->minImageCount = std::min(max_buffer_count, 3); capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count); capabilities->currentExtent = @@ -872,13 +848,18 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, int err; int query_value; - uint32_t min_buffer_count; ANativeWindow* window = SurfaceFromHandle(surface)->window.get(); - err = get_min_buffer_count(window, &min_buffer_count); - if (err != android::OK) { + err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &query_value); + if (err != android::OK || query_value < 0) { + ALOGE( + "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) " + "value=%d", + strerror(-err), err, query_value); return VK_ERROR_SURFACE_LOST_KHR; } + uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value); err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value); if (err != android::OK || query_value < 0) { @@ -889,7 +870,7 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, uint32_t max_buffer_count = static_cast<uint32_t>(query_value); std::vector<VkPresentModeKHR> present_modes; - if (min_buffer_count < max_buffer_count) + if (min_undequeued_buffers + 1 < max_buffer_count) present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); @@ -1210,27 +1191,40 @@ VkResult CreateSwapchainKHR(VkDevice device, } } - uint32_t min_buffer_count; - err = get_min_buffer_count(window, &min_buffer_count); - if (err != android::OK) { + int query_value; + err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + &query_value); + if (err != android::OK || query_value < 0) { + ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, + query_value); return VK_ERROR_SURFACE_LOST_KHR; } - - uint32_t num_images = - std::max(min_buffer_count, create_info->minImageCount); - - // Lower layer insists that we have at least two buffers. This is wasteful - // and we'd like to relax it in the shared case, but not all the pieces are - // in place for that to work yet. Note we only lie to the lower layer-- we - // don't want to give the app back a swapchain with extra images (which they - // can't actually use!). - err = native_window_set_buffer_count(window, std::max(2u, num_images)); + uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value); + const auto mailbox_num_images = std::max(3u, create_info->minImageCount); + const auto requested_images = + swap_interval ? create_info->minImageCount : mailbox_num_images; + uint32_t num_images = requested_images - 1 + min_undequeued_buffers; + + // Lower layer insists that we have at least min_undequeued_buffers + 1 + // buffers. This is wasteful and we'd like to relax it in the shared case, + // but not all the pieces are in place for that to work yet. Note we only + // lie to the lower layer--we don't want to give the app back a swapchain + // with extra images (which they can't actually use!). + uint32_t min_buffer_count = min_undequeued_buffers + 1; + err = native_window_set_buffer_count( + window, std::max(min_buffer_count, num_images)); if (err != android::OK) { ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images, strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; } + // In shared mode the num_images must be one regardless of how many + // buffers were allocated for the buffer queue. + if (swapchain_image_usage & VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID) { + num_images = 1; + } + int32_t legacy_usage = 0; if (dispatch.GetSwapchainGrallocUsage2ANDROID) { uint64_t consumer_usage, producer_usage; |