diff options
499 files changed, 27486 insertions, 19446 deletions
diff --git a/Android.bp b/Android.bp index de9ea86f1d..9829c7fbad 100644 --- a/Android.bp +++ b/Android.bp @@ -7,6 +7,7 @@ ndk_headers { } subdirs = [ + "adbd_auth", "cmds/*", "headers", "libs/*", @@ -20,3 +21,25 @@ cc_library_headers { vendor: true, export_include_dirs: ["include_sensor"], } + +filegroup { + name: "framework_native_aidl_binder", + srcs: ["aidl/binder/**/*.aidl"], + path: "aidl/binder", + visibility: ["//frameworks/native"], +} + +filegroup { + name: "framework_native_aidl_gui", + srcs: ["aidl/gui/**/*.aidl"], + path: "aidl/gui", + visibility: ["//frameworks/native"], +} + +filegroup { + name: "framework_native_aidl", + srcs: [ + ":framework_native_aidl_binder", + ":framework_native_aidl_gui", + ], +} diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 1a932c3d04..e048a19145 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -4,15 +4,22 @@ 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/ + include/input/ libs/binder/ndk/ + libs/binderthreadstate/ libs/graphicsenv/ libs/gui/ + libs/input/ libs/renderengine/ libs/ui/ libs/vr/ + opengl/libs/ services/bufferhub/ + services/inputflinger/ services/surfaceflinger/ services/vr/ + vulkan/ [Hook Scripts] owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$" diff --git a/TEST_MAPPING b/TEST_MAPPING new file mode 100644 index 0000000000..8173c8927e --- /dev/null +++ b/TEST_MAPPING @@ -0,0 +1,63 @@ +{ + "presubmit": [ + { + "name": "SurfaceFlinger_test", + "options": [ + { + "include-filter": "*CredentialsTest.*" + }, + { + "include-filter": "*SurfaceFlingerStress.*" + }, + { + "include-filter": "*SurfaceInterceptorTest.*" + }, + { + "include-filter": "*LayerTransactionTest.*" + }, + { + "include-filter": "*LayerTypeTransactionTest.*" + }, + { + "include-filter": "*LayerUpdateTest.*" + }, + { + "include-filter": "*GeometryLatchingTest.*" + }, + { + "include-filter": "*CropLatchingTest.*" + }, + { + "include-filter": "*ChildLayerTest.*" + }, + { + "include-filter": "*ScreenCaptureTest.*" + }, + { + "include-filter": "*ScreenCaptureChildOnlyTest.*" + }, + { + "include-filter": "*DereferenceSurfaceControlTest.*" + }, + { + "include-filter": "*BoundlessLayerTest.*" + }, + { + "include-filter": "*MultiDisplayLayerBoundsTest.*" + }, + { + "include-filter": "*InvalidHandleTest.*" + }, + { + "include-filter": "*VirtualDisplayTest.*" + }, + { + "include-filter": "*RelativeZTest.*" + } + ] + }, + { + "name": "libsurfaceflinger_unittest" + } + ] +} diff --git a/aidl/binder/android/os/PersistableBundle.aidl b/aidl/binder/android/os/PersistableBundle.aidl index 94e8607630..493ecb414c 100644 --- a/aidl/binder/android/os/PersistableBundle.aidl +++ b/aidl/binder/android/os/PersistableBundle.aidl @@ -17,4 +17,4 @@ package android.os; -parcelable PersistableBundle cpp_header "binder/PersistableBundle.h"; +@JavaOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h"; diff --git a/build/phone-xhdpi-2048-dalvik-heap.mk b/build/phone-xhdpi-2048-dalvik-heap.mk index 042a6e69ef..7ccfc13702 100644 --- a/build/phone-xhdpi-2048-dalvik-heap.mk +++ b/build/phone-xhdpi-2048-dalvik-heap.mk @@ -14,7 +14,7 @@ # limitations under the License. # -# Provides overrides to configure the Dalvik heap for a 2G phone +# Provides overrides to configure the Dalvik heap for a 2GB phone # 192m of RAM gives enough space for 5 8 megapixel camera bitmaps in RAM. PRODUCT_PROPERTY_OVERRIDES += \ diff --git a/libs/binder/ndk/scripts/format.sh b/build/phone-xhdpi-4096-dalvik-heap.mk index 698d291cb1..2b848413fb 100755..100644 --- a/libs/binder/ndk/scripts/format.sh +++ b/build/phone-xhdpi-4096-dalvik-heap.mk @@ -1,6 +1,5 @@ -#!/usr/bin/env bash - -# Copyright (C) 2018 The Android Open Source Project +# +# Copyright 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,10 +12,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# -set -e - -echo "Formatting code" +# Provides overrides to configure the Dalvik heap for a 4GB phone -bpfmt -w $(find $ANDROID_BUILD_TOP/frameworks/native/libs/binder/ndk/ -name "Android.bp") -clang-format -i $(find $ANDROID_BUILD_TOP/frameworks/native/libs/binder/ndk/ -\( -name "*.cpp" -o -name "*.h" -\)) +PRODUCT_PROPERTY_OVERRIDES += \ + dalvik.vm.heapstartsize=8m \ + dalvik.vm.heapgrowthlimit=192m \ + dalvik.vm.heapsize=512m \ + dalvik.vm.heaptargetutilization=0.6 \ + dalvik.vm.heapminfree=8m \ + dalvik.vm.heapmaxfree=16m diff --git a/libs/binder/ndk/update.sh b/build/phone-xhdpi-6144-dalvik-heap.mk index 9a4577ffff..2bacc4a9ab 100755..100644 --- a/libs/binder/ndk/update.sh +++ b/build/phone-xhdpi-6144-dalvik-heap.mk @@ -1,6 +1,5 @@ -#!/usr/bin/env bash - -# Copyright (C) 2018 The Android Open Source Project +# +# Copyright 2019 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,11 +12,14 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Provides overrides to configure the Dalvik heap for a 6GB phone -set -ex - -# This script makes sure that the source code is in sync with the various scripts -./scripts/gen_parcel_helper.py -./scripts/format.sh -./scripts/init_map.sh > libbinder_ndk.map.txt +PRODUCT_PROPERTY_OVERRIDES += \ + dalvik.vm.heapstartsize=16m \ + dalvik.vm.heapgrowthlimit=256m \ + dalvik.vm.heapsize=512m \ + dalvik.vm.heaptargetutilization=0.5 \ + dalvik.vm.heapminfree=8m \ + dalvik.vm.heapmaxfree=32m diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp index cc2a6f7e31..e7d0ad0549 100644 --- a/cmds/atrace/Android.bp +++ b/cmds/atrace/Android.bp @@ -10,9 +10,7 @@ cc_binary { shared_libs: [ "libbinder", - "libhwbinder", "libhidlbase", - "libhidltransport", "liblog", "libutils", "libcutils", diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 1429bc8b07..cf75bbab3b 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -854,7 +854,6 @@ static bool setUpUserspaceTracing() tags |= c.tags; } } - ok &= setTagsProperty(tags); bool coreServicesTagEnabled = false; for (size_t i = 0; i < arraysize(k_categories); i++) { @@ -876,9 +875,11 @@ static bool setUpUserspaceTracing() packageList += android::base::GetProperty(k_coreServicesProp, ""); } ok &= setAppCmdlineProperty(&packageList[0]); + ok &= setTagsProperty(tags); +#if !ATRACE_SHMEM ok &= pokeBinderServices(); pokeHalServices(); - +#endif if (g_tracePdx) { ok &= ServiceUtility::PokeServices(); } @@ -1468,10 +1469,11 @@ int main(int argc, char **argv) // Reset the trace buffer size to 1. if (traceStop) { - cleanUpVendorTracing(); cleanUpUserspaceTracing(); - if (!onlyUserspace) + if (!onlyUserspace) { + cleanUpVendorTracing(); cleanUpKernelTracing(); + } } return g_traceAborted ? 1 : 0; diff --git a/cmds/bugreport/bugreport.cpp b/cmds/bugreport/bugreport.cpp index 917c8132b7..840ae473bc 100644 --- a/cmds/bugreport/bugreport.cpp +++ b/cmds/bugreport/bugreport.cpp @@ -37,7 +37,7 @@ int main() { property_set("ctl.start", "dumpstate"); // Socket will not be available until service starts. - int s; + int s = -1; for (int i = 0; i < 20; i++) { s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp index 74a95b0b57..40346bee1f 100644 --- a/cmds/bugreportz/main.cpp +++ b/cmds/bugreportz/main.cpp @@ -72,7 +72,7 @@ int main(int argc, char* argv[]) { property_set("ctl.start", "dumpstatez"); // Socket will not be available until service starts. - int s; + int s = -1; for (int i = 0; i < 20; i++) { s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); if (s >= 0) break; diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp index 7b4aeb2cc2..8dad47502f 100644 --- a/cmds/cmd/cmd.cpp +++ b/cmds/cmd/cmd.cpp @@ -223,7 +223,8 @@ int cmdMain(const std::vector<std::string_view>& argv, TextOutput& outputLog, Te sp<MyResultReceiver> result = new MyResultReceiver(); #if DEBUG - ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", cmd, in, out, err); + ALOGD("cmd: Invoking %.*s in=%d, out=%d, err=%d", + static_cast<int>(cmd.size()), cmd.data(), in, out, err); #endif // TODO: block until a result is returned to MyResultReceiver. diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index ee32cb4495..09aee89e1b 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -86,15 +86,13 @@ cc_defaults { "libdumpstateaidl", "libdumpstateutil", "libdumputils", + "libhardware_legacy", "libhidlbase", - "libhidltransport", "liblog", "libutils", ], srcs: [ - "DumpstateSectionReporter.cpp", "DumpstateService.cpp", - "utils.cpp", ], static_libs: [ "libincidentcompanion", @@ -120,10 +118,11 @@ cc_binary { "kill", "librank", "logcat", + "lpdump", + "lpdumpd", "lsmod", "lsof", "netstat", - "parse_radio_log", "printenv", "procrank", "screencap", @@ -146,6 +145,12 @@ cc_test { "tests/dumpstate_test.cpp", ], static_libs: ["libgmock"], + test_config: "dumpstate_test.xml", + data: [ + ":dumpstate_test_fixture", + "tests/testdata/**/*", + ], + test_suites: ["device-tests"], } cc_test { diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp index 33e35f7274..bbc724c4c0 100644 --- a/cmds/dumpstate/DumpstateInternal.cpp +++ b/cmds/dumpstate/DumpstateInternal.cpp @@ -68,7 +68,8 @@ bool DropRootUser() { } static const std::vector<std::string> group_names{ - "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats", "readproc", "bluetooth"}; + "log", "sdcard_r", "sdcard_rw", "mount", "inet", "net_bw_stats", + "readproc", "bluetooth", "wakelock"}; std::vector<gid_t> groups(group_names.size(), 0); for (size_t i = 0; i < group_names.size(); ++i) { grp = getgrnam(group_names[i].c_str()); @@ -116,6 +117,11 @@ bool DropRootUser() { capdata[cap_syslog_index].effective |= cap_syslog_mask; } + const uint32_t cap_block_suspend_mask = CAP_TO_MASK(CAP_BLOCK_SUSPEND); + const uint32_t cap_block_suspend_index = CAP_TO_INDEX(CAP_BLOCK_SUSPEND); + capdata[cap_block_suspend_index].permitted |= cap_block_suspend_mask; + capdata[cap_block_suspend_index].effective |= cap_block_suspend_mask; + if (capset(&capheader, &capdata[0]) != 0) { MYLOGE("capset({%#x, %#x}) failed: %s\n", capdata[0].effective, capdata[1].effective, strerror(errno)); diff --git a/cmds/dumpstate/DumpstateInternal.h b/cmds/dumpstate/DumpstateInternal.h index 10db5d65e3..c1ec55ee61 100644 --- a/cmds/dumpstate/DumpstateInternal.h +++ b/cmds/dumpstate/DumpstateInternal.h @@ -49,6 +49,7 @@ // TODO: use functions from <chrono> instead const uint64_t NANOS_PER_SEC = 1000000000; +const uint64_t NANOS_PER_MILLI = 1000000; uint64_t Nanotime(); // Switches to non-root user and group. diff --git a/cmds/dumpstate/DumpstateSectionReporter.cpp b/cmds/dumpstate/DumpstateSectionReporter.cpp deleted file mode 100644 index f814bde26d..0000000000 --- a/cmds/dumpstate/DumpstateSectionReporter.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "dumpstate" - -#include "DumpstateSectionReporter.h" - -namespace android { -namespace os { -namespace dumpstate { - -DumpstateSectionReporter::DumpstateSectionReporter(const std::string& title, - sp<android::os::IDumpstateListener> listener, - bool sendReport) - : title_(title), listener_(listener), sendReport_(sendReport), status_(OK), size_(-1) { - started_ = std::chrono::steady_clock::now(); -} - -DumpstateSectionReporter::~DumpstateSectionReporter() { - if ((listener_ != nullptr) && (sendReport_)) { - auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( - std::chrono::steady_clock::now() - started_); - listener_->onSectionComplete(title_, status_, size_, (int32_t)elapsed.count()); - } -} - -} // namespace dumpstate -} // namespace os -} // namespace android diff --git a/cmds/dumpstate/DumpstateSectionReporter.h b/cmds/dumpstate/DumpstateSectionReporter.h deleted file mode 100644 index e971de84c5..0000000000 --- a/cmds/dumpstate/DumpstateSectionReporter.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ -#define ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ - -#include <android/os/IDumpstateListener.h> -#include <utils/StrongPointer.h> - -namespace android { -namespace os { -namespace dumpstate { - - -/* - * Helper class used to report per section details to a listener. - * - * Typical usage: - * - * DumpstateSectionReporter sectionReporter(title, listener, sendReport); - * sectionReporter.setSize(5000); - * - */ -class DumpstateSectionReporter { - public: - DumpstateSectionReporter(const std::string& title, sp<android::os::IDumpstateListener> listener, - bool sendReport); - - ~DumpstateSectionReporter(); - - void setStatus(status_t status) { - status_ = status; - } - - void setSize(int size) { - size_ = size; - } - - private: - std::string title_; - android::sp<android::os::IDumpstateListener> listener_; - bool sendReport_; - status_t status_; - int size_; - std::chrono::time_point<std::chrono::steady_clock> started_; -}; - -} // namespace dumpstate -} // namespace os -} // namespace android - -#endif // ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index ddae9ea8f6..10d3d1756b 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -115,8 +115,8 @@ binder::Status DumpstateService::setListener(const std::string& name, binder::Status DumpstateService::startBugreport(int32_t calling_uid, const std::string& calling_package, - const android::base::unique_fd& bugreport_fd, - const android::base::unique_fd& screenshot_fd, + android::base::unique_fd bugreport_fd, + android::base::unique_fd screenshot_fd, int bugreport_mode, const sp<IDumpstateListener>& listener) { MYLOGI("startBugreport() with mode: %d\n", bugreport_mode); @@ -151,15 +151,15 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid, signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); } - if (bugreport_fd.get() == -1 || screenshot_fd.get() == -1) { - // TODO(b/111441001): screenshot fd should be optional + std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>(); + options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd, + screenshot_fd); + + if (bugreport_fd.get() == -1 || (options->do_fb && screenshot_fd.get() == -1)) { MYLOGE("Invalid filedescriptor"); signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); } - std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>(); - options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd, - screenshot_fd); ds_ = &(Dumpstate::GetInstance()); ds_->SetOptions(std::move(options)); @@ -200,8 +200,7 @@ status_t DumpstateService::dump(int fd, const Vector<String16>&) { dprintf(fd, "id: %d\n", ds_->id_); dprintf(fd, "pid: %d\n", ds_->pid_); dprintf(fd, "update_progress: %s\n", ds_->options_->do_progress_updates ? "true" : "false"); - dprintf(fd, "update_progress_threshold: %d\n", ds_->update_progress_threshold_); - dprintf(fd, "last_updated_progress: %d\n", ds_->last_updated_progress_); + dprintf(fd, "last_percent_progress: %d\n", ds_->last_reported_percent_progress_); dprintf(fd, "progress:\n"); ds_->progress_->Dump(fd, " "); dprintf(fd, "args: %s\n", ds_->options_->args.c_str()); diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h index 68eda4763a..aaaa4286cc 100644 --- a/cmds/dumpstate/DumpstateService.h +++ b/cmds/dumpstate/DumpstateService.h @@ -43,8 +43,8 @@ class DumpstateService : public BinderService<DumpstateService>, public BnDumpst sp<IDumpstateToken>* returned_token) override; binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package, - const android::base::unique_fd& bugreport_fd, - const android::base::unique_fd& screenshot_fd, int bugreport_mode, + android::base::unique_fd bugreport_fd, + android::base::unique_fd screenshot_fd, int bugreport_mode, const sp<IDumpstateListener>& listener) override; // No-op diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp index 97c8ae2045..4b69607156 100644 --- a/cmds/dumpstate/DumpstateUtil.cpp +++ b/cmds/dumpstate/DumpstateUtil.cpp @@ -378,34 +378,6 @@ int RunCommandToFd(int fd, const std::string& title, const std::vector<std::stri return status; } -int GetPidByName(const std::string& ps_name) { - DIR* proc_dir; - struct dirent* ps; - unsigned int pid; - std::string cmdline; - - if (!(proc_dir = opendir("/proc"))) { - MYLOGE("Can't open /proc\n"); - return -1; - } - - while ((ps = readdir(proc_dir))) { - if (!(pid = atoi(ps->d_name))) { - continue; - } - android::base::ReadFileToString("/proc/" + std::string(ps->d_name) + "/cmdline", &cmdline); - if (cmdline.find(ps_name) == std::string::npos) { - continue; - } else { - closedir(proc_dir); - return pid; - } - } - MYLOGE("can't find the pid\n"); - closedir(proc_dir); - return -1; -} - } // namespace dumpstate } // namespace os } // namespace android diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h index d75b08c046..b7ac25c81e 100644 --- a/cmds/dumpstate/DumpstateUtil.h +++ b/cmds/dumpstate/DumpstateUtil.h @@ -205,12 +205,6 @@ int RunCommandToFd(int fd, const std::string& title, const std::vector<std::stri */ int DumpFileToFd(int fd, const std::string& title, const std::string& path); -/* - * Finds the process id by process name. - * |ps_name| the process name we want to search for - */ -int GetPidByName(const std::string& ps_name); - } // namespace dumpstate } // namespace os } // namespace android diff --git a/cmds/dumpstate/TEST_MAPPING b/cmds/dumpstate/TEST_MAPPING new file mode 100644 index 0000000000..083944f729 --- /dev/null +++ b/cmds/dumpstate/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "dumpstate_test" + } + ] +}
\ No newline at end of file diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl index 347856ddcb..cb2d8b8d2c 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl @@ -73,7 +73,7 @@ interface IDumpstate { * @param callingUid UID of the original application that requested the report. * @param callingPackage package of the original application that requested the report. * @param bugreportFd the file to which the zipped bugreport should be written - * @param screenshotFd the file to which screenshot should be written; optional + * @param screenshotFd the file to which screenshot should be written * @param bugreportMode the mode that specifies other run time options; must be one of above * @param listener callback for updates; optional */ diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl index ea1e467dca..e486460753 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl @@ -61,21 +61,4 @@ interface IDumpstateListener { * Called when taking bugreport finishes successfully. */ void onFinished(); - - // TODO(b/111441001): Remove old methods when not used anymore. - void onProgressUpdated(int progress); - void onMaxProgressUpdated(int maxProgress); - - /** - * Called after every section is complete. - * - * @param name section name - * @param status values from status_t - * {@code OK} section completed successfully - * {@code TIMEOUT} dump timed out - * {@code != OK} error - * @param size size in bytes, may be invalid if status != OK - * @param durationMs duration in ms - */ - void onSectionComplete(@utf8InCpp String name, int status, int size, int durationMs); } diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 5de40776b9..310b5cecf0 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -22,6 +22,8 @@ #include <inttypes.h> #include <libgen.h> #include <limits.h> +#include <math.h> +#include <poll.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -32,13 +34,22 @@ #include <sys/stat.h> #include <sys/time.h> #include <sys/wait.h> +#include <signal.h> +#include <stdarg.h> +#include <string.h> +#include <sys/capability.h> +#include <sys/inotify.h> +#include <sys/klog.h> +#include <time.h> #include <unistd.h> #include <chrono> +#include <cmath> #include <fstream> #include <functional> #include <future> #include <memory> +#include <numeric> #include <regex> #include <set> #include <string> @@ -58,17 +69,19 @@ #include <binder/IServiceManager.h> #include <cutils/native_handle.h> #include <cutils/properties.h> +#include <cutils/sockets.h> #include <debuggerd/client.h> #include <dumpsys.h> #include <dumputils/dump_utils.h> +#include <hardware_legacy/power.h> #include <hidl/ServiceManagement.h> +#include <log/log.h> #include <openssl/sha.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> #include <serviceutils/PriorityDumper.h> #include <utils/StrongPointer.h> #include "DumpstateInternal.h" -#include "DumpstateSectionReporter.h" #include "DumpstateService.h" #include "dumpstate.h" @@ -93,16 +106,40 @@ using android::base::StringPrintf; using android::os::IDumpstateListener; using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; -using android::os::dumpstate::DumpstateSectionReporter; -using android::os::dumpstate::GetPidByName; using android::os::dumpstate::PropertiesHelper; +// Keep in sync with +// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java +static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds + +/* Most simple commands have 10 as timeout, so 5 is a good estimate */ +static const int32_t WEIGHT_FILE = 5; + +// TODO: temporary variables and functions used during C++ refactoring +static Dumpstate& ds = Dumpstate::GetInstance(); +static int RunCommand(const std::string& title, const std::vector<std::string>& full_command, + const CommandOptions& options = CommandOptions::DEFAULT, + bool verbose_duration = false) { + return ds.RunCommand(title, full_command, options, verbose_duration); +} + +// Reasonable value for max stats. +static const int STATS_MAX_N_RUNS = 1000; +static const long STATS_MAX_AVERAGE = 100000; + +CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build(); + typedef Dumpstate::ConsentCallback::ConsentResult UserConsentResult; /* read before root is shed */ static char cmdline_buf[16384] = "(unknown)"; static const char *dump_traces_path = nullptr; static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000; +// Because telephony reports are significantly faster to collect (< 10 seconds vs. > 2 minutes), +// it's often the case that they time out far too quickly for consent with such a hefty dialog for +// the user to read. For telephony reports only, we increase the default timeout to 2 minutes to +// roughly match full reports' durations. +static const uint64_t TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS = 2 * 60 * 1000; // TODO: variables and functions below should be part of dumpstate object @@ -117,11 +154,15 @@ void add_mountinfo(); #define RECOVERY_DATA_DIR "/data/misc/recovery" #define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log" #define LOGPERSIST_DATA_DIR "/data/misc/logd" +#define PREREBOOT_DATA_DIR "/data/misc/prereboot" #define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur" #define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref" #define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat" #define WLUTIL "/vendor/xbin/wlutil" #define WMTRACE_DATA_DIR "/data/misc/wmtrace" +#define OTA_METADATA_DIR "/metadata/ota" +#define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log" +#define LINKERCONFIG_DIR "/linkerconfig" // TODO(narayan): Since this information has to be kept in sync // with tombstoned, we should just put it in a common header. @@ -133,7 +174,6 @@ static const std::string ANR_DIR = "/data/anr/"; static const std::string ANR_FILE_PREFIX = "anr_"; // TODO: temporary variables and functions used during C++ refactoring -static Dumpstate& ds = Dumpstate::GetInstance(); #define RETURN_IF_USER_DENIED_CONSENT() \ if (ds.IsUserConsentDenied()) { \ @@ -148,6 +188,8 @@ static Dumpstate& ds = Dumpstate::GetInstance(); func_ptr(__VA_ARGS__); \ RETURN_IF_USER_DENIED_CONSENT(); +static const char* WAKE_LOCK_NAME = "dumpstate_wakelock"; + namespace android { namespace os { namespace { @@ -220,7 +262,7 @@ int64_t GetModuleMetadataVersion() { MYLOGE("Failed to retrieve module metadata package name: %s", status.toString8().c_str()); return 0L; } - MYLOGD("Module metadata package name: %s", package_name.c_str()); + MYLOGD("Module metadata package name: %s\n", package_name.c_str()); int64_t version_code; status = package_service->getVersionCodeForPackage(android::String16(package_name.c_str()), &version_code); @@ -235,10 +277,6 @@ int64_t GetModuleMetadataVersion() { } // namespace os } // namespace android -static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand, - const CommandOptions& options = CommandOptions::DEFAULT) { - return ds.RunCommand(title, fullCommand, options); -} static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs, const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS, long dumpsysTimeoutMs = 0) { @@ -271,12 +309,10 @@ static const CommandOptions AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot( * Returns a vector of dump fds under |dir_path| with a given |file_prefix|. * The returned vector is sorted by the mtimes of the dumps. If |limit_by_mtime| * is set, the vector only contains files that were written in the last 30 minutes. - * If |limit_by_count| is set, the vector only contains the ten latest files. */ static std::vector<DumpData> GetDumpFds(const std::string& dir_path, const std::string& file_prefix, - bool limit_by_mtime, - bool limit_by_count = true) { + bool limit_by_mtime) { const time_t thirty_minutes_ago = ds.now_ - 60 * 30; std::unique_ptr<DIR, decltype(&closedir)> dump_dir(opendir(dir_path.c_str()), closedir); @@ -320,15 +356,6 @@ static std::vector<DumpData> GetDumpFds(const std::string& dir_path, dump_data.emplace_back(DumpData{abs_path, std::move(fd), st.st_mtime}); } - // Sort in descending modification time so that we only keep the newest - // reports if |limit_by_count| is true. - std::sort(dump_data.begin(), dump_data.end(), - [](const DumpData& d1, const DumpData& d2) { return d1.mtime > d2.mtime; }); - - if (limit_by_count && dump_data.size() > 10) { - dump_data.erase(dump_data.begin() + 10, dump_data.end()); - } - return dump_data; } @@ -421,108 +448,6 @@ static void dump_dev_files(const char *title, const char *driverpath, const char closedir(d); } - - -// dump anrd's trace and add to the zip file. -// 1. check if anrd is running on this device. -// 2. send a SIGUSR1 to its pid which will dump anrd's trace. -// 3. wait until the trace generation completes and add to the zip file. -static bool dump_anrd_trace() { - unsigned int pid; - char buf[50], path[PATH_MAX]; - struct dirent *trace; - struct stat st; - DIR *trace_dir; - int retry = 5; - long max_ctime = 0, old_mtime; - long long cur_size = 0; - const char *trace_path = "/data/misc/anrd/"; - - if (!ds.IsZipping()) { - MYLOGE("Not dumping anrd trace because it's not a zipped bugreport\n"); - return false; - } - - // find anrd's pid if it is running. - pid = GetPidByName("/system/bin/anrd"); - - if (pid > 0) { - if (stat(trace_path, &st) == 0) { - old_mtime = st.st_mtime; - } else { - MYLOGE("Failed to find: %s\n", trace_path); - return false; - } - - // send SIGUSR1 to the anrd to generate a trace. - sprintf(buf, "%u", pid); - if (RunCommand("ANRD_DUMP", {"kill", "-SIGUSR1", buf}, - CommandOptions::WithTimeout(1).Build())) { - MYLOGE("anrd signal timed out. Please manually collect trace\n"); - return false; - } - - while (retry-- > 0 && old_mtime == st.st_mtime) { - sleep(1); - stat(trace_path, &st); - } - - if (retry < 0 && old_mtime == st.st_mtime) { - MYLOGE("Failed to stat %s or trace creation timeout\n", trace_path); - return false; - } - - // identify the trace file by its creation time. - if (!(trace_dir = opendir(trace_path))) { - MYLOGE("Can't open trace file under %s\n", trace_path); - } - while ((trace = readdir(trace_dir))) { - if (strcmp(trace->d_name, ".") == 0 - || strcmp(trace->d_name, "..") == 0) { - continue; - } - sprintf(path, "%s%s", trace_path, trace->d_name); - if (stat(path, &st) == 0) { - if (st.st_ctime > max_ctime) { - max_ctime = st.st_ctime; - sprintf(buf, "%s", trace->d_name); - } - } - } - closedir(trace_dir); - - // Wait until the dump completes by checking the size of the trace. - if (max_ctime > 0) { - sprintf(path, "%s%s", trace_path, buf); - while(true) { - sleep(1); - if (stat(path, &st) == 0) { - if (st.st_size == cur_size) { - break; - } else if (st.st_size > cur_size) { - cur_size = st.st_size; - } else { - return false; - } - } else { - MYLOGE("Cant stat() %s anymore\n", path); - return false; - } - } - // Add to the zip file. - if (!ds.AddZipEntry("anrd_trace.txt", path)) { - MYLOGE("Unable to add anrd_trace file %s to zip file\n", path); - } else { - android::os::UnlinkAndLogOnError(path); - return true; - } - } else { - MYLOGE("Can't stats any trace file under %s\n", trace_path); - } - } - return false; -} - static bool skip_not_stat(const char *path) { static const char stat[] = "/stat"; size_t len = strlen(path); @@ -743,7 +668,7 @@ UserConsentResult Dumpstate::ConsentCallback::getResult() { } uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const { - return Nanotime() - start_time_; + return (Nanotime() - start_time_) / NANOS_PER_MILLI; } void Dumpstate::PrintHeader() const { @@ -960,6 +885,25 @@ static void DoKernelLogcat() { CommandOptions::WithTimeoutInMs(timeout_ms).Build()); } +static void DoSystemLogcat(time_t since) { + char since_str[80]; + strftime(since_str, sizeof(since_str), "%Y-%m-%d %H:%M:%S.000", localtime(&since)); + + unsigned long timeout_ms = logcat_timeout({"main", "system", "crash"}); + RunCommand("SYSTEM LOG", + {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v", "-T", + since_str}, + CommandOptions::WithTimeoutInMs(timeout_ms).Build()); +} + +static void DoRadioLogcat() { + unsigned long timeout_ms = logcat_timeout({"radio"}); + RunCommand( + "RADIO LOG", + {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, + CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */); +} + static void DoLogcat() { unsigned long timeout_ms; // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags"); @@ -972,17 +916,13 @@ static void DoLogcat() { RunCommand( "EVENT LOG", {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, - CommandOptions::WithTimeoutInMs(timeout_ms).Build()); + CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */); timeout_ms = logcat_timeout({"stats"}); RunCommand( "STATS LOG", {"logcat", "-b", "stats", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, - CommandOptions::WithTimeoutInMs(timeout_ms).Build()); - timeout_ms = logcat_timeout({"radio"}); - RunCommand( - "RADIO LOG", - {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, - CommandOptions::WithTimeoutInMs(timeout_ms).Build()); + CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */); + DoRadioLogcat(); RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"}); @@ -991,6 +931,31 @@ static void DoLogcat() { "-v", "uid", "-d", "*:v"}); } +static void DumpIncidentReport() { + if (!ds.IsZipping()) { + MYLOGD("Not dumping incident report because it's not a zipped bugreport\n"); + return; + } + DurationReporter duration_reporter("INCIDENT REPORT"); + const std::string path = ds.bugreport_internal_dir_ + "/tmp_incident_report"; + auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), + O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))); + if (fd < 0) { + MYLOGE("Could not open %s to dump incident report.\n", path.c_str()); + return; + } + RunCommandToFd(fd, "", {"incident", "-u"}, CommandOptions::WithTimeout(120).Build()); + bool empty = 0 == lseek(fd, 0, SEEK_END); + if (!empty) { + // Use a different name from "incident.proto" + // /proto/incident.proto is reserved for incident service dump + // i.e. metadata for debugging. + ds.AddZipEntry(kProtoPath + "incident_report" + kProtoExt, path); + } + unlink(path.c_str()); +} + static void DumpIpTablesAsRoot() { RunCommand("IPTABLES", {"iptables", "-L", "-nvx"}); RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"}); @@ -1002,6 +967,15 @@ static void DumpIpTablesAsRoot() { RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"}); } +static void DumpDynamicPartitionInfo() { + if (!::android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) { + return; + } + + RunCommand("LPDUMP", {"lpdump", "--all"}); + RunCommand("DEVICE-MAPPER", {"gsid", "dump-device-mapper"}); +} + static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_dir) { MYLOGD("AddAnrTraceDir(): dump_traces_file=%s, anr_traces_dir=%s\n", dump_traces_path, anr_traces_dir.c_str()); @@ -1121,20 +1095,17 @@ static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, i RETURN_IF_USER_DENIED_CONSENT(); std::string path(title); path.append(" - ").append(String8(service).c_str()); - DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); size_t bytes_written = 0; - status_t status = dumpsys.startDumpThread(service, args); + status_t status = dumpsys.startDumpThread(Dumpsys::Type::DUMP, service, args); if (status == OK) { dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority); std::chrono::duration<double> elapsed_seconds; status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout, /* as_proto = */ false, elapsed_seconds, bytes_written); - section_reporter.setSize(bytes_written); dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds); bool dump_complete = (status == OK); dumpsys.stopDumpThread(dump_complete); } - section_reporter.setStatus(status); auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - start); @@ -1197,8 +1168,7 @@ static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priori path.append("_HIGH"); } path.append(kProtoExt); - DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); - status_t status = dumpsys.startDumpThread(service, args); + status_t status = dumpsys.startDumpThread(Dumpsys::Type::DUMP, service, args); if (status == OK) { status = ds.AddZipEntryFromFd(path, dumpsys.getDumpFd(), service_timeout); bool dumpTerminated = (status == OK); @@ -1206,8 +1176,6 @@ static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priori } ZipWriter::FileEntry file_entry; ds.zip_writer_->GetLastEntry(&file_entry); - section_reporter.setSize(file_entry.compressed_size); - section_reporter.setStatus(status); auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - start); @@ -1314,6 +1282,45 @@ static void DumpHals() { } } +static void DumpExternalFragmentationInfo() { + struct stat st; + if (stat("/proc/buddyinfo", &st) != 0) { + MYLOGE("Unable to dump external fragmentation info\n"); + return; + } + + printf("------ EXTERNAL FRAGMENTATION INFO ------\n"); + std::ifstream ifs("/proc/buddyinfo"); + auto unusable_index_regex = std::regex{"Node\\s+([0-9]+),\\s+zone\\s+(\\S+)\\s+(.*)"}; + for (std::string line; std::getline(ifs, line);) { + std::smatch match_results; + if (std::regex_match(line, match_results, unusable_index_regex)) { + std::stringstream free_pages(std::string{match_results[3]}); + std::vector<int> free_pages_per_order(std::istream_iterator<int>{free_pages}, + std::istream_iterator<int>()); + + int total_free_pages = 0; + for (size_t i = 0; i < free_pages_per_order.size(); i++) { + total_free_pages += (free_pages_per_order[i] * std::pow(2, i)); + } + + printf("Node %s, zone %8s", match_results[1].str().c_str(), + match_results[2].str().c_str()); + + int usable_free_pages = total_free_pages; + for (size_t i = 0; i < free_pages_per_order.size(); i++) { + auto unusable_index = (total_free_pages - usable_free_pages) / + static_cast<double>(total_free_pages); + printf(" %5.3f", unusable_index); + usable_free_pages -= (free_pages_per_order[i] * std::pow(2, i)); + } + + printf("\n"); + } + } + printf("\n"); +} + // Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent // via the consent they are shown. Ignores other errors that occur while running various // commands. The consent checking is currently done around long running tasks, which happen to @@ -1327,7 +1334,6 @@ static Dumpstate::RunStatus dumpstate() { dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version"); RunCommand("UPTIME", {"uptime"}); DumpBlockStatFiles(); - dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd"); DumpFile("MEMORY INFO", "/proc/meminfo"); RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o", "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"}); @@ -1340,11 +1346,10 @@ static Dumpstate::RunStatus dumpstate() { DumpFile("ZONEINFO", "/proc/zoneinfo"); DumpFile("PAGETYPEINFO", "/proc/pagetypeinfo"); DumpFile("BUDDYINFO", "/proc/buddyinfo"); - DumpFile("FRAGMENTATION INFO", "/d/extfrag/unusable_index"); + DumpExternalFragmentationInfo(); DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources"); DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state"); - DumpFile("KERNEL SYNC", "/d/sync"); RunCommand("PROCESSES AND THREADS", {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy,time"}); @@ -1380,13 +1385,11 @@ static Dumpstate::RunStatus dumpstate() { /* Dump Bluetooth HCI logs */ ds.AddDir("/data/misc/bluetooth/logs", true); - if (!ds.do_early_screenshot_) { + if (ds.options_->do_fb && !ds.do_early_screenshot_) { MYLOGI("taking late screenshot\n"); ds.TakeScreenshot(); } - DoLogcat(); - AddAnrTraceFiles(); // NOTE: tombstones are always added as separate entries in the zip archive @@ -1419,16 +1422,16 @@ static Dumpstate::RunStatus dumpstate() { RunCommand("FILESYSTEMS & FREE SPACE", {"df"}); - RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"}); - /* Binder state is expensive to look at as it uses a lot of memory. */ - DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log"); - DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log"); - DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions"); - DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats"); - DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state"); + std::string binder_logs_dir = access("/dev/binderfs/binder_logs", R_OK) ? + "/sys/kernel/debug/binder" : "/dev/binderfs/binder_logs"; + + DumpFile("BINDER FAILED TRANSACTION LOG", binder_logs_dir + "/failed_transaction_log"); + DumpFile("BINDER TRANSACTION LOG", binder_logs_dir + "/transaction_log"); + DumpFile("BINDER TRANSACTIONS", binder_logs_dir + "/transactions"); + DumpFile("BINDER STATS", binder_logs_dir + "/stats"); + DumpFile("BINDER STATE", binder_logs_dir + "/state"); - RunDumpsys("WINSCOPE TRACE", {"window", "trace"}); /* Add window and surface trace files. */ if (!PropertiesHelper::IsUserBuild()) { ds.AddDir(WMTRACE_DATA_DIR, false); @@ -1527,6 +1530,12 @@ static Dumpstate::RunStatus dumpstate() { printf("========================================================\n"); // This differs from the usual dumpsys stats, which is the stats report data. RunDumpsys("STATSDSTATS", {"stats", "--metadata"}); + + // Add linker configuration directory + ds.AddDir(LINKERCONFIG_DIR, true); + + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport); + return Dumpstate::RunStatus::OK; } @@ -1539,13 +1548,16 @@ static Dumpstate::RunStatus dumpstate() { * with the caller. */ static Dumpstate::RunStatus DumpstateDefault() { - // Try to dump anrd trace if the daemon is running. - dump_anrd_trace(); - // Invoking the following dumpsys calls before DumpTraces() to try and // keep the system stats as close to its initial state as possible. RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysCritical); + // Capture first logcat early on; useful to take a snapshot before dumpstate logs take over the + // buffer. + DoLogcat(); + // Capture timestamp after first logcat to use in next logcat + time_t logcat_ts = time(nullptr); + /* collect stack traces from Dalvik and native processes (needs root) */ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path); @@ -1557,12 +1569,16 @@ static Dumpstate::RunStatus DumpstateDefault() { ds.AddDir(RECOVERY_DATA_DIR, true); ds.AddDir(UPDATE_ENGINE_LOG_DIR, true); ds.AddDir(LOGPERSIST_DATA_DIR, false); + ds.AddDir(SNAPSHOTCTL_LOG_DIR, false); if (!PropertiesHelper::IsUserBuild()) { ds.AddDir(PROFILE_DATA_DIR_CUR, true); ds.AddDir(PROFILE_DATA_DIR_REF, true); } + ds.AddDir(PREREBOOT_DATA_DIR, false); add_mountinfo(); DumpIpTablesAsRoot(); + DumpDynamicPartitionInfo(); + ds.AddDir(OTA_METADATA_DIR, true); // Capture any IPSec policies in play. No keys are exposed here. RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"}, CommandOptions::WithTimeout(10).Build()); @@ -1582,16 +1598,25 @@ static Dumpstate::RunStatus DumpstateDefault() { RunCommand("Dmabuf dump", {"/product/bin/dmabuf_dump"}); } + DumpFile("PSI cpu", "/proc/pressure/cpu"); + DumpFile("PSI memory", "/proc/pressure/memory"); + DumpFile("PSI io", "/proc/pressure/io"); + if (!DropRootUser()) { return Dumpstate::RunStatus::ERROR; } RETURN_IF_USER_DENIED_CONSENT(); - return dumpstate(); + Dumpstate::RunStatus status = dumpstate(); + // Capture logcat since the last time we did it. + DoSystemLogcat(logcat_ts); + return status; } -// This method collects common dumpsys for telephony and wifi -static void DumpstateRadioCommon() { +// This method collects common dumpsys for telephony and wifi. Typically, wifi +// reports are fine to include all information, but telephony reports on user +// builds need to strip some content (see DumpstateTelephonyOnly). +static void DumpstateRadioCommon(bool include_sensitive_info = true) { DumpIpTablesAsRoot(); ds.AddDir(LOGPERSIST_DATA_DIR, false); @@ -1600,26 +1625,51 @@ static void DumpstateRadioCommon() { return; } - do_dmesg(); - DoLogcat(); + // We need to be picky about some stuff for telephony reports on user builds. + if (!include_sensitive_info) { + // Only dump the radio log buffer (other buffers and dumps contain too much unrelated info). + DoRadioLogcat(); + } else { + // Contains various system properties and process startup info. + do_dmesg(); + // Logs other than the radio buffer may contain package/component names and potential PII. + DoLogcat(); + // Too broad for connectivity problems. + DoKmsg(); + // Contains unrelated hardware info (camera, NFC, biometrics, ...). + DumpHals(); + } + DumpPacketStats(); - DoKmsg(); DumpIpAddrAndRules(); dump_route_tables(); - DumpHals(); - RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"}, CommandOptions::WithTimeout(10).Build()); } -// This method collects dumpsys for telephony debugging only +// We use "telephony" here for legacy reasons, though this now really means "connectivity" (cellular +// + wifi + networking). This method collects dumpsys for connectivity debugging only. General rules +// for what can be included on user builds: all reported information MUST directly relate to +// connectivity debugging or customer support and MUST NOT contain unrelated personally identifiable +// information. This information MUST NOT identify user-installed packages (UIDs are OK, package +// names are not), and MUST NOT contain logs of user application traffic. +// TODO(b/148168577) rename this and other related fields/methods to "connectivity" instead. static void DumpstateTelephonyOnly() { DurationReporter duration_reporter("DUMPSTATE"); + const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build(); - DumpstateRadioCommon(); + const bool include_sensitive_info = !PropertiesHelper::IsUserBuild(); - RunCommand("SYSTEM PROPERTIES", {"getprop"}); + DumpstateRadioCommon(include_sensitive_info); + + if (include_sensitive_info) { + // Contains too much unrelated PII, and given the unstructured nature of sysprops, we can't + // really cherrypick all of the connectivity-related ones. Apps generally have no business + // reading these anyway, and there should be APIs to supply the info in a more app-friendly + // way. + RunCommand("SYSTEM PROPERTIES", {"getprop"}); + } printf("========================================================\n"); printf("== Android Framework Services\n"); @@ -1627,15 +1677,28 @@ static void DumpstateTelephonyOnly() { RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); - RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(), - SEC_TO_MSEC(10)); - RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + // TODO(b/146521742) build out an argument to include bound services here for user builds RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); - RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(), + RunDumpsys("DUMPSYS", {"netpolicy"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS", {"network_management"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + if (include_sensitive_info) { + // Contains raw IP addresses, omit from reports on user builds. + RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + // Contains raw destination IP/MAC addresses, omit from reports on user builds. + RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + // Contains package/component names, omit from reports on user builds. + RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + // Contains package names, but should be relatively simple to remove them (also contains + // UIDs already), omit from reports on user builds. + RunDumpsys("BATTERYSTATS", {"deviceidle"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + } printf("========================================================\n"); printf("== Running Application Services\n"); @@ -1643,18 +1706,24 @@ static void DumpstateTelephonyOnly() { RunDumpsys("TELEPHONY SERVICES", {"activity", "service", "TelephonyDebugService"}); - printf("========================================================\n"); - printf("== Running Application Services (non-platform)\n"); - printf("========================================================\n"); + if (include_sensitive_info) { + printf("========================================================\n"); + printf("== Running Application Services (non-platform)\n"); + printf("========================================================\n"); - RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, - DUMPSYS_COMPONENTS_OPTIONS); + // Contains package/component names and potential PII, omit from reports on user builds. + // To get dumps of the active CarrierService(s) on user builds, we supply an argument to the + // carrier_config dumpsys instead. + RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, + DUMPSYS_COMPONENTS_OPTIONS); - printf("========================================================\n"); - printf("== Checkins\n"); - printf("========================================================\n"); + printf("========================================================\n"); + printf("== Checkins\n"); + printf("========================================================\n"); - RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); + // Contains package/component names, omit from reports on user builds. + RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); + } printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); @@ -1904,22 +1973,21 @@ void Dumpstate::DumpstateBoard() { static void ShowUsage() { fprintf(stderr, - "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file] [-d] [-p] " + "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-d] [-p] " "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n" " -h: display this help message\n" " -b: play sound file instead of vibrate, at beginning of job\n" " -e: play sound file instead of vibrate, at end of job\n" - " -o: write to file (instead of stdout)\n" - " -d: append date to filename (requires -o)\n" - " -p: capture screenshot to filename.png (requires -o)\n" - " -z: generate zipped file (requires -o)\n" + " -d: append date to filename\n" + " -p: capture screenshot to filename.png\n" + " -z: generate zipped file\n" " -s: write output to control socket (for init)\n" - " -S: write file location to control socket (for init; requires -o and -z)\n" + " -S: write file location to control socket (for init; requires -z)\n" " -q: disable vibrate\n" - " -B: send broadcast when finished (requires -o)\n" + " -B: send broadcast when finished\n" " -P: send broadcast when started and update system properties on " - "progress (requires -o and -B)\n" - " -R: take bugreport in remote mode (requires -o, -z, -d and -B, " + "progress (requires -B)\n" + " -R: take bugreport in remote mode (requires -z, -d and -B, " "shouldn't be used with -P)\n" " -w: start binder service and make it wait for a call to startBugreport\n" " -v: prints the dumpstate header and exit\n"); @@ -2031,7 +2099,7 @@ static void SendBroadcast(const std::string& action, const std::vector<std::stri static void Vibrate(int duration_ms) { // clang-format off - RunCommand("", {"cmd", "vibrator", "vibrate", std::to_string(duration_ms), "dumpstate"}, + RunCommand("", {"cmd", "vibrator", "vibrate", "-f", std::to_string(duration_ms), "dumpstate"}, CommandOptions::WithTimeout(10) .Log("Vibrate: '%s'\n") .Always() @@ -2267,6 +2335,7 @@ static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOpt break; case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY: options->telephony_only = true; + options->do_progress_updates = true; options->do_fb = false; options->do_broadcast = true; break; @@ -2403,7 +2472,6 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) } } - // TODO: use helper function to convert argv into a string for (int i = 0; i < argc; i++) { args += argv[i]; if (i < argc - 1) { @@ -2554,6 +2622,13 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, MYLOGI("begin\n"); + if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) { + MYLOGE("Failed to acquire wake lock: %s\n", strerror(errno)); + } else { + // Wake lock will be released automatically on process death + MYLOGD("Wake lock acquired.\n"); + } + register_sig_handler(); // TODO(b/111441001): maybe skip if already started? @@ -2627,13 +2702,8 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } if (options_->do_fb && do_early_screenshot_) { - if (screenshot_path_.empty()) { - // should not have happened - MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n"); - } else { - MYLOGI("taking early screenshot\n"); - TakeScreenshot(); - } + MYLOGI("taking early screenshot\n"); + TakeScreenshot(); } if (options_->do_zip_file && zip_file != nullptr) { @@ -2689,7 +2759,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // Dump state for the default case. This also drops root. RunStatus s = DumpstateDefault(); if (s != RunStatus::OK) { - if (s == RunStatus::USER_CONSENT_TIMED_OUT) { + if (s == RunStatus::USER_CONSENT_DENIED) { HandleUserConsentDenied(); } return s; @@ -2816,8 +2886,13 @@ Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() { if (consent_result == UserConsentResult::UNAVAILABLE) { // User has not responded yet. uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs(); - if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) { - uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000; + // Telephony is a fast report type, particularly on user builds where information may be + // more aggressively limited. To give the user time to read the consent dialog, increase the + // timeout. + uint64_t timeout_ms = options_->telephony_only ? TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS + : USER_CONSENT_TIMEOUT_MS; + if (elapsed_ms < timeout_ms) { + uint delay_seconds = (timeout_ms - elapsed_ms) / 1000; MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds); sleep(delay_seconds); } @@ -2885,3 +2960,831 @@ int run_main(int argc, char* argv[]) { exit(2); } } + +// TODO(111441001): Default DumpOptions to sensible values. +Dumpstate::Dumpstate(const std::string& version) + : pid_(getpid()), + options_(new Dumpstate::DumpOptions()), + last_reported_percent_progress_(0), + version_(version), + now_(time(nullptr)) { +} + +Dumpstate& Dumpstate::GetInstance() { + static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT)); + return singleton_; +} + +DurationReporter::DurationReporter(const std::string& title, bool logcat_only, bool verbose) + : title_(title), logcat_only_(logcat_only), verbose_(verbose) { + if (!title_.empty()) { + started_ = Nanotime(); + } +} + +DurationReporter::~DurationReporter() { + if (!title_.empty()) { + float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC; + if (elapsed >= .5f || verbose_) { + MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed); + } + if (!logcat_only_) { + // Use "Yoda grammar" to make it easier to grep|sort sections. + printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); + } + } +} + +const int32_t Progress::kDefaultMax = 5000; + +Progress::Progress(const std::string& path) : Progress(Progress::kDefaultMax, 1.1, path) { +} + +Progress::Progress(int32_t initial_max, int32_t progress, float growth_factor) + : Progress(initial_max, growth_factor, "") { + progress_ = progress; +} + +Progress::Progress(int32_t initial_max, float growth_factor, const std::string& path) + : initial_max_(initial_max), + progress_(0), + max_(initial_max), + growth_factor_(growth_factor), + n_runs_(0), + average_max_(0), + path_(path) { + if (!path_.empty()) { + Load(); + } +} + +void Progress::Load() { + MYLOGD("Loading stats from %s\n", path_.c_str()); + std::string content; + if (!android::base::ReadFileToString(path_, &content)) { + MYLOGI("Could not read stats from %s; using max of %d\n", path_.c_str(), max_); + return; + } + if (content.empty()) { + MYLOGE("No stats (empty file) on %s; using max of %d\n", path_.c_str(), max_); + return; + } + std::vector<std::string> lines = android::base::Split(content, "\n"); + + if (lines.size() < 1) { + MYLOGE("Invalid stats on file %s: not enough lines (%d). Using max of %d\n", path_.c_str(), + (int)lines.size(), max_); + return; + } + char* ptr; + n_runs_ = strtol(lines[0].c_str(), &ptr, 10); + average_max_ = strtol(ptr, nullptr, 10); + if (n_runs_ <= 0 || average_max_ <= 0 || n_runs_ > STATS_MAX_N_RUNS || + average_max_ > STATS_MAX_AVERAGE) { + MYLOGE("Invalid stats line on file %s: %s\n", path_.c_str(), lines[0].c_str()); + initial_max_ = Progress::kDefaultMax; + } else { + initial_max_ = average_max_; + } + max_ = initial_max_; + + MYLOGI("Average max progress: %d in %d runs; estimated max: %d\n", average_max_, n_runs_, max_); +} + +void Progress::Save() { + int32_t total = n_runs_ * average_max_ + progress_; + int32_t runs = n_runs_ + 1; + int32_t average = floor(((float)total) / runs); + MYLOGI("Saving stats (total=%d, runs=%d, average=%d) on %s\n", total, runs, average, + path_.c_str()); + if (path_.empty()) { + return; + } + + std::string content = android::base::StringPrintf("%d %d\n", runs, average); + if (!android::base::WriteStringToFile(content, path_)) { + MYLOGE("Could not save stats on %s\n", path_.c_str()); + } +} + +int32_t Progress::Get() const { + return progress_; +} + +bool Progress::Inc(int32_t delta_sec) { + bool changed = false; + if (delta_sec >= 0) { + progress_ += delta_sec; + if (progress_ > max_) { + int32_t old_max = max_; + max_ = floor((float)progress_ * growth_factor_); + MYLOGD("Adjusting max progress from %d to %d\n", old_max, max_); + changed = true; + } + } + return changed; +} + +int32_t Progress::GetMax() const { + return max_; +} + +int32_t Progress::GetInitialMax() const { + return initial_max_; +} + +void Progress::Dump(int fd, const std::string& prefix) const { + const char* pr = prefix.c_str(); + dprintf(fd, "%sprogress: %d\n", pr, progress_); + dprintf(fd, "%smax: %d\n", pr, max_); + dprintf(fd, "%sinitial_max: %d\n", pr, initial_max_); + dprintf(fd, "%sgrowth_factor: %0.2f\n", pr, growth_factor_); + dprintf(fd, "%spath: %s\n", pr, path_.c_str()); + dprintf(fd, "%sn_runs: %d\n", pr, n_runs_); + dprintf(fd, "%saverage_max: %d\n", pr, average_max_); +} + +bool Dumpstate::IsZipping() const { + return zip_writer_ != nullptr; +} + +std::string Dumpstate::GetPath(const std::string& suffix) const { + return GetPath(bugreport_internal_dir_, suffix); +} + +std::string Dumpstate::GetPath(const std::string& directory, const std::string& suffix) const { + return android::base::StringPrintf("%s/%s-%s%s", directory.c_str(), base_name_.c_str(), + name_.c_str(), suffix.c_str()); +} + +void Dumpstate::SetProgress(std::unique_ptr<Progress> progress) { + progress_ = std::move(progress); +} + +void for_each_userid(void (*func)(int), const char *header) { + std::string title = header == nullptr ? "for_each_userid" : android::base::StringPrintf( + "for_each_userid(%s)", header); + DurationReporter duration_reporter(title); + if (PropertiesHelper::IsDryRun()) return; + + DIR *d; + struct dirent *de; + + if (header) printf("\n------ %s ------\n", header); + func(0); + + if (!(d = opendir("/data/system/users"))) { + printf("Failed to open /data/system/users (%s)\n", strerror(errno)); + return; + } + + while ((de = readdir(d))) { + int userid; + if (de->d_type != DT_DIR || !(userid = atoi(de->d_name))) { + continue; + } + func(userid); + } + + closedir(d); +} + +static void __for_each_pid(void (*helper)(int, const char *, void *), const char *header, void *arg) { + DIR *d; + struct dirent *de; + + if (!(d = opendir("/proc"))) { + printf("Failed to open /proc (%s)\n", strerror(errno)); + return; + } + + if (header) printf("\n------ %s ------\n", header); + while ((de = readdir(d))) { + if (ds.IsUserConsentDenied()) { + MYLOGE( + "Returning early because user denied consent to share bugreport with calling app."); + closedir(d); + return; + } + int pid; + int fd; + char cmdpath[255]; + char cmdline[255]; + + if (!(pid = atoi(de->d_name))) { + continue; + } + + memset(cmdline, 0, sizeof(cmdline)); + + snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/cmdline", pid); + if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { + TEMP_FAILURE_RETRY(read(fd, cmdline, sizeof(cmdline) - 2)); + close(fd); + if (cmdline[0]) { + helper(pid, cmdline, arg); + continue; + } + } + + // if no cmdline, a kernel thread has comm + snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/comm", pid); + if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { + TEMP_FAILURE_RETRY(read(fd, cmdline + 1, sizeof(cmdline) - 4)); + close(fd); + if (cmdline[1]) { + cmdline[0] = '['; + size_t len = strcspn(cmdline, "\f\b\r\n"); + cmdline[len] = ']'; + cmdline[len+1] = '\0'; + } + } + if (!cmdline[0]) { + strcpy(cmdline, "N/A"); + } + helper(pid, cmdline, arg); + } + + closedir(d); +} + +static void for_each_pid_helper(int pid, const char *cmdline, void *arg) { + for_each_pid_func *func = (for_each_pid_func*) arg; + func(pid, cmdline); +} + +void for_each_pid(for_each_pid_func func, const char *header) { + std::string title = header == nullptr ? "for_each_pid" + : android::base::StringPrintf("for_each_pid(%s)", header); + DurationReporter duration_reporter(title); + if (PropertiesHelper::IsDryRun()) return; + + __for_each_pid(for_each_pid_helper, header, (void *) func); +} + +static void for_each_tid_helper(int pid, const char *cmdline, void *arg) { + DIR *d; + struct dirent *de; + char taskpath[255]; + for_each_tid_func *func = (for_each_tid_func *) arg; + + snprintf(taskpath, sizeof(taskpath), "/proc/%d/task", pid); + + if (!(d = opendir(taskpath))) { + printf("Failed to open %s (%s)\n", taskpath, strerror(errno)); + return; + } + + func(pid, pid, cmdline); + + while ((de = readdir(d))) { + if (ds.IsUserConsentDenied()) { + MYLOGE( + "Returning early because user denied consent to share bugreport with calling app."); + closedir(d); + return; + } + int tid; + int fd; + char commpath[255]; + char comm[255]; + + if (!(tid = atoi(de->d_name))) { + continue; + } + + if (tid == pid) + continue; + + snprintf(commpath, sizeof(commpath), "/proc/%d/comm", tid); + memset(comm, 0, sizeof(comm)); + if ((fd = TEMP_FAILURE_RETRY(open(commpath, O_RDONLY | O_CLOEXEC))) < 0) { + strcpy(comm, "N/A"); + } else { + char *c; + TEMP_FAILURE_RETRY(read(fd, comm, sizeof(comm) - 2)); + close(fd); + + c = strrchr(comm, '\n'); + if (c) { + *c = '\0'; + } + } + func(pid, tid, comm); + } + + closedir(d); +} + +void for_each_tid(for_each_tid_func func, const char *header) { + std::string title = header == nullptr ? "for_each_tid" + : android::base::StringPrintf("for_each_tid(%s)", header); + DurationReporter duration_reporter(title); + + if (PropertiesHelper::IsDryRun()) return; + + __for_each_pid(for_each_tid_helper, header, (void *) func); +} + +void show_wchan(int pid, int tid, const char *name) { + if (PropertiesHelper::IsDryRun()) return; + + char path[255]; + char buffer[255]; + int fd, ret, save_errno; + char name_buffer[255]; + + memset(buffer, 0, sizeof(buffer)); + + snprintf(path, sizeof(path), "/proc/%d/wchan", tid); + if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { + printf("Failed to open '%s' (%s)\n", path, strerror(errno)); + return; + } + + ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); + save_errno = errno; + close(fd); + + if (ret < 0) { + printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); + return; + } + + snprintf(name_buffer, sizeof(name_buffer), "%*s%s", + pid == tid ? 0 : 3, "", name); + + printf("%-7d %-32s %s\n", tid, name_buffer, buffer); + + return; +} + +// print time in centiseconds +static void snprcent(char *buffer, size_t len, size_t spc, + unsigned long long time) { + static long hz; // cache discovered hz + + if (hz <= 0) { + hz = sysconf(_SC_CLK_TCK); + if (hz <= 0) { + hz = 1000; + } + } + + // convert to centiseconds + time = (time * 100 + (hz / 2)) / hz; + + char str[16]; + + snprintf(str, sizeof(str), " %llu.%02u", + time / 100, (unsigned)(time % 100)); + size_t offset = strlen(buffer); + snprintf(buffer + offset, (len > offset) ? len - offset : 0, + "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); +} + +// print permille as a percent +static void snprdec(char *buffer, size_t len, size_t spc, unsigned permille) { + char str[16]; + + snprintf(str, sizeof(str), " %u.%u%%", permille / 10, permille % 10); + size_t offset = strlen(buffer); + snprintf(buffer + offset, (len > offset) ? len - offset : 0, + "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); +} + +void show_showtime(int pid, const char *name) { + if (PropertiesHelper::IsDryRun()) return; + + char path[255]; + char buffer[1023]; + int fd, ret, save_errno; + + memset(buffer, 0, sizeof(buffer)); + + snprintf(path, sizeof(path), "/proc/%d/stat", pid); + if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { + printf("Failed to open '%s' (%s)\n", path, strerror(errno)); + return; + } + + ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); + save_errno = errno; + close(fd); + + if (ret < 0) { + printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); + return; + } + + // field 14 is utime + // field 15 is stime + // field 42 is iotime + unsigned long long utime = 0, stime = 0, iotime = 0; + if (sscanf(buffer, + "%*u %*s %*s %*d %*d %*d %*d %*d %*d %*d %*d " + "%*d %*d %llu %llu %*d %*d %*d %*d %*d %*d " + "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " + "%*d %*d %*d %*d %*d %*d %*d %*d %*d %llu ", + &utime, &stime, &iotime) != 3) { + return; + } + + unsigned long long total = utime + stime; + if (!total) { + return; + } + + unsigned permille = (iotime * 1000 + (total / 2)) / total; + if (permille > 1000) { + permille = 1000; + } + + // try to beautify and stabilize columns at <80 characters + snprintf(buffer, sizeof(buffer), "%-6d%s", pid, name); + if ((name[0] != '[') || utime) { + snprcent(buffer, sizeof(buffer), 57, utime); + } + snprcent(buffer, sizeof(buffer), 65, stime); + if ((name[0] != '[') || iotime) { + snprcent(buffer, sizeof(buffer), 73, iotime); + } + if (iotime) { + snprdec(buffer, sizeof(buffer), 79, permille); + } + puts(buffer); // adds a trailing newline + + return; +} + +void do_dmesg() { + const char *title = "KERNEL LOG (dmesg)"; + DurationReporter duration_reporter(title); + printf("------ %s ------\n", title); + + if (PropertiesHelper::IsDryRun()) return; + + /* Get size of kernel buffer */ + int size = klogctl(KLOG_SIZE_BUFFER, nullptr, 0); + if (size <= 0) { + printf("Unexpected klogctl return value: %d\n\n", size); + return; + } + char *buf = (char *) malloc(size + 1); + if (buf == nullptr) { + printf("memory allocation failed\n\n"); + return; + } + int retval = klogctl(KLOG_READ_ALL, buf, size); + if (retval < 0) { + printf("klogctl failure\n\n"); + free(buf); + return; + } + buf[retval] = '\0'; + printf("%s\n\n", buf); + free(buf); + return; +} + +void do_showmap(int pid, const char *name) { + char title[255]; + char arg[255]; + + snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name); + snprintf(arg, sizeof(arg), "%d", pid); + RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT); +} + +int Dumpstate::DumpFile(const std::string& title, const std::string& path) { + DurationReporter duration_reporter(title); + + int status = DumpFileToFd(STDOUT_FILENO, title, path); + + UpdateProgress(WEIGHT_FILE); + + return status; +} + +int read_file_as_long(const char *path, long int *output) { + int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC)); + if (fd < 0) { + int err = errno; + MYLOGE("Error opening file descriptor for %s: %s\n", path, strerror(err)); + return -1; + } + char buffer[50]; + ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); + if (bytes_read == -1) { + MYLOGE("Error reading file %s: %s\n", path, strerror(errno)); + return -2; + } + if (bytes_read == 0) { + MYLOGE("File %s is empty\n", path); + return -3; + } + *output = atoi(buffer); + return 0; +} + +/* calls skip to gate calling dump_from_fd recursively + * in the specified directory. dump_from_fd defaults to + * dump_file_from_fd above when set to NULL. skip defaults + * to false when set to NULL. dump_from_fd will always be + * called with title NULL. + */ +int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path), + int (*dump_from_fd)(const char* title, const char* path, int fd)) { + DurationReporter duration_reporter(title); + DIR *dirp; + struct dirent *d; + char *newpath = nullptr; + const char *slash = "/"; + int retval = 0; + + if (!title.empty()) { + printf("------ %s (%s) ------\n", title.c_str(), dir); + } + if (PropertiesHelper::IsDryRun()) return 0; + + if (dir[strlen(dir) - 1] == '/') { + ++slash; + } + dirp = opendir(dir); + if (dirp == nullptr) { + retval = -errno; + MYLOGE("%s: %s\n", dir, strerror(errno)); + return retval; + } + + if (!dump_from_fd) { + dump_from_fd = dump_file_from_fd; + } + for (; ((d = readdir(dirp))); free(newpath), newpath = nullptr) { + if ((d->d_name[0] == '.') + && (((d->d_name[1] == '.') && (d->d_name[2] == '\0')) + || (d->d_name[1] == '\0'))) { + continue; + } + asprintf(&newpath, "%s%s%s%s", dir, slash, d->d_name, + (d->d_type == DT_DIR) ? "/" : ""); + if (!newpath) { + retval = -errno; + continue; + } + if (skip && (*skip)(newpath)) { + continue; + } + if (d->d_type == DT_DIR) { + int ret = dump_files("", newpath, skip, dump_from_fd); + if (ret < 0) { + retval = ret; + } + continue; + } + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC))); + if (fd.get() < 0) { + retval = -1; + printf("*** %s: %s\n", newpath, strerror(errno)); + continue; + } + (*dump_from_fd)(nullptr, newpath, fd.get()); + } + closedir(dirp); + if (!title.empty()) { + printf("\n"); + } + return retval; +} + +/* fd must have been opened with the flag O_NONBLOCK. With this flag set, + * it's possible to avoid issues where opening the file itself can get + * stuck. + */ +int dump_file_from_fd(const char *title, const char *path, int fd) { + if (PropertiesHelper::IsDryRun()) return 0; + + int flags = fcntl(fd, F_GETFL); + if (flags == -1) { + printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno)); + return -1; + } else if (!(flags & O_NONBLOCK)) { + printf("*** %s: fd must have O_NONBLOCK set.\n", path); + return -1; + } + return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun()); +} + +int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command, + const CommandOptions& options, bool verbose_duration) { + DurationReporter duration_reporter(title, false /* logcat_only */, verbose_duration); + + int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options); + + /* TODO: for now we're simplifying the progress calculation by using the + * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys, + * where its weight should be much higher proportionally to its timeout. + * Ideally, it should use a options.EstimatedDuration() instead...*/ + UpdateProgress(options.Timeout()); + + return status; +} + +void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args, + const CommandOptions& options, long dumpsysTimeoutMs) { + long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs(); + std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)}; + dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end()); + RunCommand(title, dumpsys, options); +} + +int open_socket(const char *service) { + int s = android_get_control_socket(service); + if (s < 0) { + MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno)); + return -1; + } + fcntl(s, F_SETFD, FD_CLOEXEC); + + // Set backlog to 0 to make sure that queue size will be minimum. + // In Linux, because the minimum queue will be 1, connect() will be blocked + // if the other clients already called connect() and the connection request was not accepted. + if (listen(s, 0) < 0) { + MYLOGE("listen(control socket): %s\n", strerror(errno)); + return -1; + } + + struct sockaddr addr; + socklen_t alen = sizeof(addr); + int fd = accept4(s, &addr, &alen, SOCK_CLOEXEC); + + // Close socket just after accept(), to make sure that connect() by client will get error + // when the socket is used by the other services. + // There is still a race condition possibility between accept and close, but there is no way + // to close-on-accept atomically. + // See detail; b/123306389#comment25 + close(s); + + if (fd < 0) { + MYLOGE("accept(control socket): %s\n", strerror(errno)); + return -1; + } + + return fd; +} + +/* redirect output to a service control socket */ +bool redirect_to_socket(FILE* redirect, const char* service) { + int fd = open_socket(service); + if (fd == -1) { + return false; + } + fflush(redirect); + // TODO: handle dup2 failure + TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); + close(fd); + return true; +} + +// TODO: should call is_valid_output_file and/or be merged into it. +void create_parent_dirs(const char *path) { + char *chp = const_cast<char *> (path); + + /* skip initial slash */ + if (chp[0] == '/') + chp++; + + /* create leading directories, if necessary */ + struct stat dir_stat; + while (chp && chp[0]) { + chp = strchr(chp, '/'); + if (chp) { + *chp = 0; + if (stat(path, &dir_stat) == -1 || !S_ISDIR(dir_stat.st_mode)) { + MYLOGI("Creating directory %s\n", path); + if (mkdir(path, 0770)) { /* drwxrwx--- */ + MYLOGE("Unable to create directory %s: %s\n", path, strerror(errno)); + } else if (chown(path, AID_SHELL, AID_SHELL)) { + MYLOGE("Unable to change ownership of dir %s: %s\n", path, strerror(errno)); + } + } + *chp++ = '/'; + } + } +} + +bool _redirect_to_file(FILE* redirect, char* path, int truncate_flag) { + create_parent_dirs(path); + + int fd = TEMP_FAILURE_RETRY(open(path, + O_WRONLY | O_CREAT | truncate_flag | O_CLOEXEC | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); + if (fd < 0) { + MYLOGE("%s: %s\n", path, strerror(errno)); + return false; + } + + TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); + close(fd); + return true; +} + +bool redirect_to_file(FILE* redirect, char* path) { + return _redirect_to_file(redirect, path, O_TRUNC); +} + +bool redirect_to_existing_file(FILE* redirect, char* path) { + return _redirect_to_file(redirect, path, O_APPEND); +} + +void dump_route_tables() { + DurationReporter duration_reporter("DUMP ROUTE TABLES"); + if (PropertiesHelper::IsDryRun()) return; + const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables"; + ds.DumpFile("RT_TABLES", RT_TABLES_PATH); + FILE* fp = fopen(RT_TABLES_PATH, "re"); + if (!fp) { + printf("*** %s: %s\n", RT_TABLES_PATH, strerror(errno)); + return; + } + char table[16]; + // Each line has an integer (the table number), a space, and a string (the table name). We only + // need the table number. It's a 32-bit unsigned number, so max 10 chars. Skip the table name. + // Add a fixed max limit so this doesn't go awry. + for (int i = 0; i < 64 && fscanf(fp, " %10s %*s", table) == 1; ++i) { + RunCommand("ROUTE TABLE IPv4", {"ip", "-4", "route", "show", "table", table}); + RunCommand("ROUTE TABLE IPv6", {"ip", "-6", "route", "show", "table", table}); + } + fclose(fp); +} + +// TODO: make this function thread safe if sections are generated in parallel. +void Dumpstate::UpdateProgress(int32_t delta_sec) { + if (progress_ == nullptr) { + MYLOGE("UpdateProgress: progress_ not set\n"); + return; + } + + // Always update progess so stats can be tuned... + progress_->Inc(delta_sec); + + // ...but only notifiy listeners when necessary. + if (!options_->do_progress_updates) return; + + int progress = progress_->Get(); + int max = progress_->GetMax(); + int percent = 100 * progress / max; + + if (last_reported_percent_progress_ > 0 && percent <= last_reported_percent_progress_) { + return; + } + last_reported_percent_progress_ = percent; + + if (control_socket_fd_ >= 0) { + dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress, max); + fsync(control_socket_fd_); + } + + if (listener_ != nullptr) { + if (percent % 5 == 0) { + // We don't want to spam logcat, so only log multiples of 5. + MYLOGD("Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), progress, max, + percent); + } else { + // stderr is ignored on normal invocations, but useful when calling + // /system/bin/dumpstate directly for debuggging. + fprintf(stderr, "Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), + progress, max, percent); + } + + listener_->onProgress(percent); + } +} + +void Dumpstate::TakeScreenshot(const std::string& path) { + const std::string& real_path = path.empty() ? screenshot_path_ : path; + int status = + RunCommand("", {"/system/bin/screencap", "-p", real_path}, + CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); + if (status == 0) { + MYLOGD("Screenshot saved on %s\n", real_path.c_str()); + } else { + MYLOGE("Failed to take screenshot on %s\n", real_path.c_str()); + } +} + +bool is_dir(const char* pathname) { + struct stat info; + if (stat(pathname, &info) == -1) { + return false; + } + return S_ISDIR(info.st_mode); +} + +time_t get_mtime(int fd, time_t default_mtime) { + struct stat info; + if (fstat(fd, &info) == -1) { + return default_mtime; + } + return info.st_mtime; +} diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index d02ec759a7..82bf8219a2 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -73,13 +73,15 @@ extern "C" { */ class DurationReporter { public: - explicit DurationReporter(const std::string& title, bool logcat_only = false); + explicit DurationReporter(const std::string& title, bool logcat_only = false, + bool verbose = false); ~DurationReporter(); private: std::string title_; bool logcat_only_; + bool verbose_; uint64_t started_; DISALLOW_COPY_AND_ASSIGN(DurationReporter); @@ -224,7 +226,8 @@ class Dumpstate { */ int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand, const android::os::dumpstate::CommandOptions& options = - android::os::dumpstate::CommandOptions::DEFAULT); + android::os::dumpstate::CommandOptions::DEFAULT, + bool verbose_duration = false); /* * Runs `dumpsys` with the given arguments, automatically setting its timeout @@ -400,12 +403,8 @@ class Dumpstate { // Runtime options. std::unique_ptr<DumpOptions> options_; - // How frequently the progess should be updated;the listener will only be notificated when the - // delta from the previous update is more than the threshold. - int32_t update_progress_threshold_ = 100; - - // Last progress that triggered a listener updated - int32_t last_updated_progress_; + // Last progress that was sent to the listener [0-100]. + int last_reported_percent_progress_ = 0; // Whether it should take an screenshot earlier in the process. bool do_early_screenshot_ = false; @@ -441,8 +440,7 @@ class Dumpstate { // Full path of the bugreport file, be it zip or text, inside bugreport_internal_dir_. std::string path_; - // TODO: If temporary this should be removed at the end. - // Full path of the temporary file containing the screenshot (when requested). + // Full path of the file containing the screenshot (when requested). std::string screenshot_path_; // Pointer to the zipped file. @@ -586,9 +584,6 @@ bool is_dir(const char* pathname); /** Gets the last modification time of a file, or default time if file is not found. */ time_t get_mtime(int fd, time_t default_mtime); -/* Dumps eMMC Extended CSD data. */ -void dump_emmc_ecsd(const char *ext_csd_path); - /** Gets command-line arguments. */ void format_args(int argc, const char *argv[], std::string *args); diff --git a/cmds/dumpstate/dumpstate_test.xml b/cmds/dumpstate/dumpstate_test.xml new file mode 100644 index 0000000000..e4e4a30a1e --- /dev/null +++ b/cmds/dumpstate/dumpstate_test.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Config for dumpstate_test"> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="dumpstate_test->/data/local/tmp/dumpstate_test" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <option name="test-suite-tag" value="apct" /> + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tmp" /> + <option name="file-exclusion-filter-regex" value=".*/dumpstate_test_fixture" /> + <option name="file-exclusion-filter-regex" value=".*/tests/.*" /> + <option name="module-name" value="dumpstate_test" /> + </test> +</configuration> diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp index fc3642c912..256dc055e1 100644 --- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -14,20 +14,21 @@ * limitations under the License. */ -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <fcntl.h> -#include <libgen.h> - #include <android-base/file.h> #include <android/os/BnDumpstate.h> #include <android/os/BnDumpstateListener.h> #include <binder/IServiceManager.h> #include <binder/ProcessState.h> #include <cutils/properties.h> +#include <fcntl.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <libgen.h> #include <ziparchive/zip_archive.h> +#include <fstream> +#include <regex> + #include "dumpstate.h" #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) @@ -44,6 +45,11 @@ class DumpstateListener; namespace { +struct SectionInfo { + std::string name; + int32_t size_bytes; +}; + sp<IDumpstate> GetDumpstateService() { return android::interface_cast<IDumpstate>( android::defaultServiceManager()->getService(String16("dumpstate"))); @@ -55,14 +61,79 @@ int OpenForWrite(const std::string& filename) { S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); } -} // namespace +void GetEntry(const ZipArchiveHandle archive, const std::string_view entry_name, ZipEntry* data) { + int32_t e = FindEntry(archive, entry_name, data); + EXPECT_EQ(e, 0) << ErrorCodeString(e) << " entry name: " << entry_name; +} -struct SectionInfo { - std::string name; - status_t status; - int32_t size_bytes; - int32_t duration_ms; -}; +// Extracts the main bugreport txt from the given archive and writes into output_fd. +void ExtractBugreport(const ZipArchiveHandle* handle, int output_fd) { + // Read contents of main_entry.txt which is a single line indicating the name of the zip entry + // that contains the main bugreport txt. + ZipEntry main_entry; + GetEntry(*handle, "main_entry.txt", &main_entry); + std::string bugreport_txt_name; + bugreport_txt_name.resize(main_entry.uncompressed_length); + ExtractToMemory(*handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()), + main_entry.uncompressed_length); + + // Read the main bugreport txt and extract to output_fd. + ZipEntry entry; + GetEntry(*handle, bugreport_txt_name, &entry); + ExtractEntryToFile(*handle, &entry, output_fd); +} + +bool IsSectionStart(const std::string& line, std::string* section_name) { + static const std::regex kSectionStart = std::regex{"DUMP OF SERVICE (.*):"}; + std::smatch match; + if (std::regex_match(line, match, kSectionStart)) { + *section_name = match.str(1); + return true; + } + return false; +} + +bool IsSectionEnd(const std::string& line) { + // Not all lines that contain "was the duration of" is a section end, but all section ends do + // contain "was the duration of". The disambiguation can be done by the caller. + return (line.find("was the duration of") != std::string::npos); +} + +// Extracts the zipped bugreport and identifies the sections. +void ParseSections(const std::string& zip_path, std::vector<SectionInfo>* sections) { + // Open the archive + ZipArchiveHandle handle; + ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0); + + // Extract the main entry to a temp file + TemporaryFile tmp_binary; + ASSERT_NE(-1, tmp_binary.fd); + ExtractBugreport(&handle, tmp_binary.fd); + + // Read line by line and identify sections + std::ifstream ifs(tmp_binary.path, std::ifstream::in); + std::string line; + int section_bytes = 0; + std::string current_section_name; + while (std::getline(ifs, line)) { + std::string section_name; + if (IsSectionStart(line, §ion_name)) { + section_bytes = 0; + current_section_name = section_name; + } else if (IsSectionEnd(line)) { + if (!current_section_name.empty()) { + sections->push_back({current_section_name, section_bytes}); + } + current_section_name = ""; + } else if (!current_section_name.empty()) { + section_bytes += line.length(); + } + } + + CloseArchive(handle); +} + +} // namespace /** * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the @@ -96,26 +167,6 @@ class DumpstateListener : public BnDumpstateListener { return binder::Status::ok(); } - binder::Status onProgressUpdated(int32_t progress) override { - dprintf(out_fd_, "\rIn progress %d/%d", progress, max_progress_); - return binder::Status::ok(); - } - - binder::Status onMaxProgressUpdated(int32_t max_progress) override { - std::lock_guard<std::mutex> lock(lock_); - max_progress_ = max_progress; - return binder::Status::ok(); - } - - binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes, - int32_t duration_ms) override { - std::lock_guard<std::mutex> lock(lock_); - if (sections_.get() != nullptr) { - sections_->push_back({name, status, size_bytes, duration_ms}); - } - return binder::Status::ok(); - } - bool getIsFinished() { std::lock_guard<std::mutex> lock(lock_); return is_finished_; @@ -128,7 +179,6 @@ class DumpstateListener : public BnDumpstateListener { private: int out_fd_; - int max_progress_ = 5000; int error_code_ = -1; bool is_finished_ = false; std::shared_ptr<std::vector<SectionInfo>> sections_; @@ -166,8 +216,8 @@ class ZippedBugreportGenerationTest : public Test { duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); } - static const char* getZipFilePath() { - return ds.GetPath(".zip").c_str(); + static const std::string getZipFilePath() { + return ds.GetPath(".zip"); } }; std::shared_ptr<std::vector<SectionInfo>> ZippedBugreportGenerationTest::sections = @@ -176,12 +226,12 @@ Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance(); std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s; TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) { - EXPECT_EQ(access(getZipFilePath(), F_OK), 0); + EXPECT_EQ(access(getZipFilePath().c_str(), F_OK), 0); } TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) { struct stat st; - EXPECT_EQ(stat(getZipFilePath(), &st), 0); + EXPECT_EQ(stat(getZipFilePath().c_str(), &st), 0); EXPECT_GE(st.st_size, 3000000 /* 3MB */); EXPECT_LE(st.st_size, 30000000 /* 30MB */); } @@ -200,7 +250,7 @@ class ZippedBugReportContentsTest : public Test { public: ZipArchiveHandle handle; void SetUp() { - ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath(), &handle), 0); + ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath().c_str(), &handle), 0); } void TearDown() { CloseArchive(handle); @@ -208,29 +258,30 @@ class ZippedBugReportContentsTest : public Test { void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) { ZipEntry entry; - EXPECT_EQ(FindEntry(handle, ZipString(filename), &entry), 0); + GetEntry(handle, filename, &entry); EXPECT_GT(entry.uncompressed_length, minsize); EXPECT_LT(entry.uncompressed_length, maxsize); } }; TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) { - ZipEntry mainEntryLoc; + ZipEntry main_entry; // contains main entry name file - EXPECT_EQ(FindEntry(handle, ZipString("main_entry.txt"), &mainEntryLoc), 0); + GetEntry(handle, "main_entry.txt", &main_entry); - char* buf = new char[mainEntryLoc.uncompressed_length]; - ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length); - delete[] buf; + std::string bugreport_txt_name; + bugreport_txt_name.resize(main_entry.uncompressed_length); + ExtractToMemory(handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()), + main_entry.uncompressed_length); // contains main entry file - FileExists(buf, 1000000U, 50000000U); + FileExists(bugreport_txt_name.c_str(), 1000000U, 50000000U); } TEST_F(ZippedBugReportContentsTest, ContainsVersion) { ZipEntry entry; // contains main entry name file - EXPECT_EQ(FindEntry(handle, ZipString("version.txt"), &entry), 0); + GetEntry(handle, "version.txt", &entry); char* buf = new char[entry.uncompressed_length + 1]; ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length); @@ -244,6 +295,10 @@ TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) { FileExists("dumpstate_board.txt", 100000U, 1000000U); } +TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) { + FileExists("proto/activity.proto", 100000U, 1000000U); +} + // Spot check on some files pulled from the file system TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) { // FS/proc/*/mountinfo size > 0 @@ -258,6 +313,11 @@ TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) { */ class BugreportSectionTest : public Test { public: + static void SetUpTestCase() { + ParseSections(ZippedBugreportGenerationTest::getZipFilePath().c_str(), + ZippedBugreportGenerationTest::sections.get()); + } + int numMatches(const std::string& substring) { int matches = 0; for (auto const& section : *ZippedBugreportGenerationTest::sections) { @@ -267,10 +327,11 @@ class BugreportSectionTest : public Test { } return matches; } + void SectionExists(const std::string& sectionName, int minsize) { for (auto const& section : *ZippedBugreportGenerationTest::sections) { if (sectionName == section.name) { - EXPECT_GE(section.size_bytes, minsize); + EXPECT_GE(section.size_bytes, minsize) << " for section:" << sectionName; return; } } @@ -278,71 +339,59 @@ class BugreportSectionTest : public Test { } }; -// Test all sections are generated without timeouts or errors -TEST_F(BugreportSectionTest, GeneratedWithoutErrors) { - for (auto const& section : *ZippedBugreportGenerationTest::sections) { - EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status; - } -} - TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) { - int numSections = numMatches("DUMPSYS CRITICAL"); + int numSections = numMatches("CRITICAL"); EXPECT_GE(numSections, 3); } TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) { - int numSections = numMatches("DUMPSYS HIGH"); + int numSections = numMatches("HIGH"); EXPECT_GE(numSections, 2); } TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) { - int allSections = numMatches("DUMPSYS"); - int criticalSections = numMatches("DUMPSYS CRITICAL"); - int highSections = numMatches("DUMPSYS HIGH"); + int allSections = ZippedBugreportGenerationTest::sections->size(); + int criticalSections = numMatches("CRITICAL"); + int highSections = numMatches("HIGH"); int normalSections = allSections - criticalSections - highSections; EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections << "High:" << highSections << "Normal:" << normalSections << ")"; } -TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) { - int numSections = numMatches("proto/"); - EXPECT_GE(numSections, 1); -} - // Test if some critical sections are being generated. TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) { - SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000); + SectionExists("CRITICAL SurfaceFlinger", /* bytes= */ 10000); } TEST_F(BugreportSectionTest, ActivitySectionsGenerated) { - SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000); - SectionExists("DUMPSYS - activity", /* bytes= */ 10000); + SectionExists("CRITICAL activity", /* bytes= */ 5000); + SectionExists("activity", /* bytes= */ 10000); } TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) { - SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000); + SectionExists("CRITICAL cpuinfo", /* bytes= */ 1000); } TEST_F(BugreportSectionTest, WindowSectionGenerated) { - SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000); + SectionExists("CRITICAL window", /* bytes= */ 20000); } TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) { - SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000); - SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000); + SectionExists("HIGH connectivity", /* bytes= */ 3000); + SectionExists("connectivity", /* bytes= */ 5000); } TEST_F(BugreportSectionTest, MeminfoSectionGenerated) { - SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000); + SectionExists("HIGH meminfo", /* bytes= */ 100000); } TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) { - SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000); + SectionExists("batterystats", /* bytes= */ 1000); } TEST_F(BugreportSectionTest, WifiSectionGenerated) { - SectionExists("DUMPSYS - wifi", /* bytes= */ 100000); + SectionExists("wifi", /* bytes= */ 100000); } class DumpstateBinderTest : public Test { @@ -400,7 +449,7 @@ TEST_F(DumpstateBinderTest, Baseline) { sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)))); android::binder::Status status = - ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd), Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener); // startBugreport is an async call. Verify binder call succeeded first, then wait till listener // gets expected callbacks. @@ -436,7 +485,7 @@ TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) { // Call startBugreport with bad arguments. sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)))); android::binder::Status status = - ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd), 2000, // invalid bugreport mode listener); EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); @@ -457,20 +506,24 @@ TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) { // Prepare arguments unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip")); + unique_fd bugreport_fd2(dup(bugreport_fd.get())); unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png")); + unique_fd screenshot_fd2(dup(screenshot_fd.get())); EXPECT_NE(bugreport_fd.get(), -1); + EXPECT_NE(bugreport_fd2.get(), -1); EXPECT_NE(screenshot_fd.get(), -1); + EXPECT_NE(screenshot_fd2.get(), -1); sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout)))); android::binder::Status status = - ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd), Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1); EXPECT_TRUE(status.isOk()); // try to make another call to startBugreport. This should fail. sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout)))); - status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + status = ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd2), std::move(screenshot_fd2), Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2); EXPECT_FALSE(status.isOk()); WaitTillExecutionComplete(listener2.get()); diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index 71d15f4761..99d482f034 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -62,10 +62,6 @@ class DumpstateListenerMock : public IDumpstateListener { MOCK_METHOD1(onProgress, binder::Status(int32_t progress)); MOCK_METHOD1(onError, binder::Status(int32_t error_code)); MOCK_METHOD0(onFinished, binder::Status()); - MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress)); - MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress)); - MOCK_METHOD4(onSectionComplete, binder::Status(const ::std::string& name, int32_t status, - int32_t size, int32_t durationMs)); protected: MOCK_METHOD0(onAsBinder, IBinder*()); @@ -105,9 +101,8 @@ class DumpstateBaseTest : public Test { protected: const std::string kTestPath = dirname(android::base::GetExecutablePath().c_str()); - const std::string kFixturesPath = kTestPath + "/../dumpstate_test_fixture/"; - const std::string kTestDataPath = kFixturesPath + "tests/testdata/"; - const std::string kSimpleCommand = kFixturesPath + "dumpstate_test_fixture"; + const std::string kTestDataPath = kTestPath + "/tests/testdata/"; + const std::string kSimpleCommand = kTestPath + "/dumpstate_test_fixture"; const std::string kEchoCommand = "/system/bin/echo"; /* @@ -377,12 +372,12 @@ TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) { EXPECT_TRUE(options_.do_broadcast); EXPECT_TRUE(options_.do_zip_file); EXPECT_TRUE(options_.telephony_only); + EXPECT_TRUE(options_.do_progress_updates); // Other options retain default values EXPECT_TRUE(options_.do_vibrate); EXPECT_FALSE(options_.use_control_socket); EXPECT_FALSE(options_.show_header_only); - EXPECT_FALSE(options_.do_progress_updates); EXPECT_FALSE(options_.is_remote_mode); EXPECT_FALSE(options_.use_socket); } @@ -591,7 +586,6 @@ class DumpstateTest : public DumpstateBaseTest { SetDryRun(false); SetBuildType(android::base::GetProperty("ro.build.type", "(unknown)")); ds.progress_.reset(new Progress()); - ds.update_progress_threshold_ = 0; ds.options_.reset(new Dumpstate::DumpOptions()); } @@ -616,10 +610,9 @@ class DumpstateTest : public DumpstateBaseTest { return status; } - void SetProgress(long progress, long initial_max, long threshold = 0) { + void SetProgress(long progress, long initial_max) { + ds.last_reported_percent_progress_ = 0; ds.options_->do_progress_updates = true; - ds.update_progress_threshold_ = threshold; - ds.last_updated_progress_ = 0; ds.progress_.reset(new Progress(initial_max, progress, 1.2)); } @@ -664,7 +657,8 @@ TEST_F(DumpstateTest, RunCommandNoTitle) { TEST_F(DumpstateTest, RunCommandWithTitle) { EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand})); EXPECT_THAT(err, StrEq("stderr\n")); - // We don't know the exact duration, so we check the prefix and suffix + // The duration may not get output, depending on how long it takes, + // so we just check the prefix. EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n")); } @@ -699,7 +693,8 @@ TEST_F(DumpstateTest, RunCommandWithMultipleArgs) { TEST_F(DumpstateTest, RunCommandDryRun) { SetDryRun(true); EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand})); - // We don't know the exact duration, so we check the prefix and suffix + // The duration may not get output, depending on how long it takes, + // so we just check the prefix. EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\n\t(skipped on dry run)\n")); EXPECT_THAT(err, IsEmpty()); @@ -795,73 +790,36 @@ TEST_F(DumpstateTest, RunCommandProgress) { ds.listener_name_ = "FoxMulder"; SetProgress(0, 30); - EXPECT_CALL(*listener, onProgressUpdated(20)); EXPECT_CALL(*listener, onProgress(66)); // 20/30 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(20).Build())); std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); - EXPECT_CALL(*listener, onProgressUpdated(30)); - EXPECT_CALL(*listener, onProgress(100)); // 35/35 % - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(10).Build())); - progress_message = GetProgressMessage(ds.listener_name_, 30, 30); - EXPECT_THAT(out, StrEq("stdout\n")); - EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); - - // Run a command that will increase maximum timeout. - EXPECT_CALL(*listener, onProgressUpdated(31)); - EXPECT_CALL(*listener, onMaxProgressUpdated(37)); - EXPECT_CALL(*listener, onProgress(83)); // 31/37 % - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); - progress_message = GetProgressMessage(ds.listener_name_, 31, 37, 30); // 20% increase + EXPECT_CALL(*listener, onProgress(80)); // 24/30 % + EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build())); + progress_message = GetProgressMessage(ds.listener_name_, 24, 30); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); // Make sure command ran while in dry_run is counted. SetDryRun(true); - EXPECT_CALL(*listener, onProgressUpdated(35)); - EXPECT_CALL(*listener, onProgress(94)); // 35/37 % - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build())); - progress_message = GetProgressMessage(ds.listener_name_, 35, 37); + EXPECT_CALL(*listener, onProgress(90)); // 27/30 % + EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(3).Build())); + progress_message = GetProgressMessage(ds.listener_name_, 27, 30); EXPECT_THAT(out, IsEmpty()); EXPECT_THAT(err, StrEq(progress_message)); - ds.listener_.clear(); -} - -TEST_F(DumpstateTest, RunCommandProgressIgnoreThreshold) { - sp<DumpstateListenerMock> listener(new DumpstateListenerMock()); - ds.listener_ = listener; - ds.listener_name_ = "FoxMulder"; - SetProgress(0, 8, 5); // 8 max, 5 threshold - - // First update should always be sent. - EXPECT_CALL(*listener, onProgressUpdated(1)); - EXPECT_CALL(*listener, onProgress(12)); // 1/12 % - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); - std::string progress_message = GetProgressMessage(ds.listener_name_, 1, 8); + SetDryRun(false); + EXPECT_CALL(*listener, onProgress(96)); // 29/30 % + EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(2).Build())); + progress_message = GetProgressMessage(ds.listener_name_, 29, 30); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); - // Fourth update should be ignored because it's between the threshold (5 -1 = 4 < 5). - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(4).Build())); - EXPECT_THAT(out, StrEq("stdout\n")); - EXPECT_THAT(err, StrEq("stderr\n")); - - // Third update should be sent because it reaches threshold (6 - 1 = 5). - EXPECT_CALL(*listener, onProgressUpdated(6)); - EXPECT_CALL(*listener, onProgress(75)); // 6/8 % + EXPECT_CALL(*listener, onProgress(100)); // 30/30 % EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(1).Build())); - progress_message = GetProgressMessage(ds.listener_name_, 6, 8); - EXPECT_THAT(out, StrEq("stdout\n")); - EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); - - // Fourth update should be ignored because it's between the threshold (9 - 6 = 3 < 5). - // But max update should be sent. - EXPECT_CALL(*listener, onMaxProgressUpdated(10)); // 9 * 120% = 10.8 = 10 - EXPECT_EQ(0, RunCommand("", {kSimpleCommand}, CommandOptions::WithTimeout(3).Build())); - progress_message = GetProgressMessage(ds.listener_name_, 9, 10, 8, false); + progress_message = GetProgressMessage(ds.listener_name_, 30, 30); EXPECT_THAT(out, StrEq("stdout\n")); EXPECT_THAT(err, StrEq("stderr\n" + progress_message)); @@ -1037,7 +995,8 @@ TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) { TEST_F(DumpstateTest, DumpFileNotFoundWithTitle) { EXPECT_EQ(-1, DumpFile("Y U NO EXIST?", "/I/cant/believe/I/exist")); EXPECT_THAT(err, IsEmpty()); - // We don't know the exact duration, so we check the prefix and suffix + // The duration may not get output, depending on how long it takes, + // so we just check the prefix. EXPECT_THAT(out, StartsWith("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No " "such file or directory\n")); } @@ -1088,7 +1047,6 @@ TEST_F(DumpstateTest, DumpFileUpdateProgress) { ds.listener_name_ = "FoxMulder"; SetProgress(0, 30); - EXPECT_CALL(*listener, onProgressUpdated(5)); EXPECT_CALL(*listener, onProgress(16)); // 5/30 % EXPECT_EQ(0, DumpFile("", kTestDataPath + "single-line.txt")); @@ -1404,14 +1362,6 @@ class DumpstateUtilTest : public DumpstateBaseTest { return status; } - // Find out the pid of the process_name - int FindPidOfProcess(const std::string& process_name) { - CaptureStderr(); - int status = GetPidByName(process_name); - err = GetCapturedStderr(); - return status; - } - int fd; // 'fd` output and `stderr` from the last command ran. @@ -1761,18 +1711,6 @@ TEST_F(DumpstateUtilTest, DumpFileOnDryRun) { EXPECT_THAT(out, EndsWith("skipped on dry run\n")); } -TEST_F(DumpstateUtilTest, FindingPidWithExistingProcess) { - // init process always has pid 1. - EXPECT_EQ(1, FindPidOfProcess("init")); - EXPECT_THAT(err, IsEmpty()); -} - -TEST_F(DumpstateUtilTest, FindingPidWithNotExistingProcess) { - // find the process with abnormal name. - EXPECT_EQ(-1, FindPidOfProcess("abcdef12345-543")); - EXPECT_THAT(err, StrEq("can't find the pid\n")); -} - } // namespace dumpstate } // namespace os } // namespace android diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp deleted file mode 100644 index 0bb80dcfba..0000000000 --- a/cmds/dumpstate/utils.cpp +++ /dev/null @@ -1,1012 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "dumpstate" - -#include "dumpstate.h" - -#include <dirent.h> -#include <fcntl.h> -#include <libgen.h> -#include <math.h> -#include <poll.h> -#include <signal.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/capability.h> -#include <sys/inotify.h> -#include <sys/klog.h> -#include <sys/prctl.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/wait.h> -#include <time.h> -#include <unistd.h> - -#include <memory> -#include <set> -#include <string> -#include <vector> - -#include <android-base/file.h> -#include <android-base/properties.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include <android-base/unique_fd.h> -#include <cutils/properties.h> -#include <cutils/sockets.h> -#include <log/log.h> -#include <private/android_filesystem_config.h> - -#include "DumpstateInternal.h" - -// TODO: remove once moved to namespace -using android::os::dumpstate::CommandOptions; -using android::os::dumpstate::DumpFileToFd; -using android::os::dumpstate::PropertiesHelper; - -// Keep in sync with -// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java -static const int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds - -/* Most simple commands have 10 as timeout, so 5 is a good estimate */ -static const int32_t WEIGHT_FILE = 5; - -// TODO: temporary variables and functions used during C++ refactoring -static Dumpstate& ds = Dumpstate::GetInstance(); -static int RunCommand(const std::string& title, const std::vector<std::string>& full_command, - const CommandOptions& options = CommandOptions::DEFAULT) { - return ds.RunCommand(title, full_command, options); -} - -// Reasonable value for max stats. -static const int STATS_MAX_N_RUNS = 1000; -static const long STATS_MAX_AVERAGE = 100000; - -CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build(); - -// TODO(111441001): Default DumpOptions to sensible values. -Dumpstate::Dumpstate(const std::string& version) - : pid_(getpid()), - options_(new Dumpstate::DumpOptions()), - version_(version), - now_(time(nullptr)) { -} - -Dumpstate& Dumpstate::GetInstance() { - static Dumpstate singleton_(android::base::GetProperty("dumpstate.version", VERSION_CURRENT)); - return singleton_; -} - -DurationReporter::DurationReporter(const std::string& title, bool logcat_only) - : title_(title), logcat_only_(logcat_only) { - if (!title_.empty()) { - started_ = Nanotime(); - } -} - -DurationReporter::~DurationReporter() { - if (!title_.empty()) { - float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC; - if (elapsed < .5f) { - return; - } - MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed); - if (logcat_only_) { - return; - } - // Use "Yoda grammar" to make it easier to grep|sort sections. - printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); - } -} - -const int32_t Progress::kDefaultMax = 5000; - -Progress::Progress(const std::string& path) : Progress(Progress::kDefaultMax, 1.1, path) { -} - -Progress::Progress(int32_t initial_max, int32_t progress, float growth_factor) - : Progress(initial_max, growth_factor, "") { - progress_ = progress; -} - -Progress::Progress(int32_t initial_max, float growth_factor, const std::string& path) - : initial_max_(initial_max), - progress_(0), - max_(initial_max), - growth_factor_(growth_factor), - n_runs_(0), - average_max_(0), - path_(path) { - if (!path_.empty()) { - Load(); - } -} - -void Progress::Load() { - MYLOGD("Loading stats from %s\n", path_.c_str()); - std::string content; - if (!android::base::ReadFileToString(path_, &content)) { - MYLOGI("Could not read stats from %s; using max of %d\n", path_.c_str(), max_); - return; - } - if (content.empty()) { - MYLOGE("No stats (empty file) on %s; using max of %d\n", path_.c_str(), max_); - return; - } - std::vector<std::string> lines = android::base::Split(content, "\n"); - - if (lines.size() < 1) { - MYLOGE("Invalid stats on file %s: not enough lines (%d). Using max of %d\n", path_.c_str(), - (int)lines.size(), max_); - return; - } - char* ptr; - n_runs_ = strtol(lines[0].c_str(), &ptr, 10); - average_max_ = strtol(ptr, nullptr, 10); - if (n_runs_ <= 0 || average_max_ <= 0 || n_runs_ > STATS_MAX_N_RUNS || - average_max_ > STATS_MAX_AVERAGE) { - MYLOGE("Invalid stats line on file %s: %s\n", path_.c_str(), lines[0].c_str()); - initial_max_ = Progress::kDefaultMax; - } else { - initial_max_ = average_max_; - } - max_ = initial_max_; - - MYLOGI("Average max progress: %d in %d runs; estimated max: %d\n", average_max_, n_runs_, max_); -} - -void Progress::Save() { - int32_t total = n_runs_ * average_max_ + progress_; - int32_t runs = n_runs_ + 1; - int32_t average = floor(((float)total) / runs); - MYLOGI("Saving stats (total=%d, runs=%d, average=%d) on %s\n", total, runs, average, - path_.c_str()); - if (path_.empty()) { - return; - } - - std::string content = android::base::StringPrintf("%d %d\n", runs, average); - if (!android::base::WriteStringToFile(content, path_)) { - MYLOGE("Could not save stats on %s\n", path_.c_str()); - } -} - -int32_t Progress::Get() const { - return progress_; -} - -bool Progress::Inc(int32_t delta_sec) { - bool changed = false; - if (delta_sec >= 0) { - progress_ += delta_sec; - if (progress_ > max_) { - int32_t old_max = max_; - max_ = floor((float)progress_ * growth_factor_); - MYLOGD("Adjusting max progress from %d to %d\n", old_max, max_); - changed = true; - } - } - return changed; -} - -int32_t Progress::GetMax() const { - return max_; -} - -int32_t Progress::GetInitialMax() const { - return initial_max_; -} - -void Progress::Dump(int fd, const std::string& prefix) const { - const char* pr = prefix.c_str(); - dprintf(fd, "%sprogress: %d\n", pr, progress_); - dprintf(fd, "%smax: %d\n", pr, max_); - dprintf(fd, "%sinitial_max: %d\n", pr, initial_max_); - dprintf(fd, "%sgrowth_factor: %0.2f\n", pr, growth_factor_); - dprintf(fd, "%spath: %s\n", pr, path_.c_str()); - dprintf(fd, "%sn_runs: %d\n", pr, n_runs_); - dprintf(fd, "%saverage_max: %d\n", pr, average_max_); -} - -bool Dumpstate::IsZipping() const { - return zip_writer_ != nullptr; -} - -std::string Dumpstate::GetPath(const std::string& suffix) const { - return GetPath(bugreport_internal_dir_, suffix); -} - -std::string Dumpstate::GetPath(const std::string& directory, const std::string& suffix) const { - return android::base::StringPrintf("%s/%s-%s%s", directory.c_str(), base_name_.c_str(), - name_.c_str(), suffix.c_str()); -} - -void Dumpstate::SetProgress(std::unique_ptr<Progress> progress) { - progress_ = std::move(progress); -} - -void for_each_userid(void (*func)(int), const char *header) { - std::string title = header == nullptr ? "for_each_userid" : android::base::StringPrintf( - "for_each_userid(%s)", header); - DurationReporter duration_reporter(title); - if (PropertiesHelper::IsDryRun()) return; - - DIR *d; - struct dirent *de; - - if (header) printf("\n------ %s ------\n", header); - func(0); - - if (!(d = opendir("/data/system/users"))) { - printf("Failed to open /data/system/users (%s)\n", strerror(errno)); - return; - } - - while ((de = readdir(d))) { - int userid; - if (de->d_type != DT_DIR || !(userid = atoi(de->d_name))) { - continue; - } - func(userid); - } - - closedir(d); -} - -static void __for_each_pid(void (*helper)(int, const char *, void *), const char *header, void *arg) { - DIR *d; - struct dirent *de; - - if (!(d = opendir("/proc"))) { - printf("Failed to open /proc (%s)\n", strerror(errno)); - return; - } - - if (header) printf("\n------ %s ------\n", header); - while ((de = readdir(d))) { - if (ds.IsUserConsentDenied()) { - MYLOGE( - "Returning early because user denied consent to share bugreport with calling app."); - closedir(d); - return; - } - int pid; - int fd; - char cmdpath[255]; - char cmdline[255]; - - if (!(pid = atoi(de->d_name))) { - continue; - } - - memset(cmdline, 0, sizeof(cmdline)); - - snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/cmdline", pid); - if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { - TEMP_FAILURE_RETRY(read(fd, cmdline, sizeof(cmdline) - 2)); - close(fd); - if (cmdline[0]) { - helper(pid, cmdline, arg); - continue; - } - } - - // if no cmdline, a kernel thread has comm - snprintf(cmdpath, sizeof(cmdpath), "/proc/%d/comm", pid); - if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY | O_CLOEXEC))) >= 0) { - TEMP_FAILURE_RETRY(read(fd, cmdline + 1, sizeof(cmdline) - 4)); - close(fd); - if (cmdline[1]) { - cmdline[0] = '['; - size_t len = strcspn(cmdline, "\f\b\r\n"); - cmdline[len] = ']'; - cmdline[len+1] = '\0'; - } - } - if (!cmdline[0]) { - strcpy(cmdline, "N/A"); - } - helper(pid, cmdline, arg); - } - - closedir(d); -} - -static void for_each_pid_helper(int pid, const char *cmdline, void *arg) { - for_each_pid_func *func = (for_each_pid_func*) arg; - func(pid, cmdline); -} - -void for_each_pid(for_each_pid_func func, const char *header) { - std::string title = header == nullptr ? "for_each_pid" - : android::base::StringPrintf("for_each_pid(%s)", header); - DurationReporter duration_reporter(title); - if (PropertiesHelper::IsDryRun()) return; - - __for_each_pid(for_each_pid_helper, header, (void *) func); -} - -static void for_each_tid_helper(int pid, const char *cmdline, void *arg) { - DIR *d; - struct dirent *de; - char taskpath[255]; - for_each_tid_func *func = (for_each_tid_func *) arg; - - snprintf(taskpath, sizeof(taskpath), "/proc/%d/task", pid); - - if (!(d = opendir(taskpath))) { - printf("Failed to open %s (%s)\n", taskpath, strerror(errno)); - return; - } - - func(pid, pid, cmdline); - - while ((de = readdir(d))) { - if (ds.IsUserConsentDenied()) { - MYLOGE( - "Returning early because user denied consent to share bugreport with calling app."); - closedir(d); - return; - } - int tid; - int fd; - char commpath[255]; - char comm[255]; - - if (!(tid = atoi(de->d_name))) { - continue; - } - - if (tid == pid) - continue; - - snprintf(commpath, sizeof(commpath), "/proc/%d/comm", tid); - memset(comm, 0, sizeof(comm)); - if ((fd = TEMP_FAILURE_RETRY(open(commpath, O_RDONLY | O_CLOEXEC))) < 0) { - strcpy(comm, "N/A"); - } else { - char *c; - TEMP_FAILURE_RETRY(read(fd, comm, sizeof(comm) - 2)); - close(fd); - - c = strrchr(comm, '\n'); - if (c) { - *c = '\0'; - } - } - func(pid, tid, comm); - } - - closedir(d); -} - -void for_each_tid(for_each_tid_func func, const char *header) { - std::string title = header == nullptr ? "for_each_tid" - : android::base::StringPrintf("for_each_tid(%s)", header); - DurationReporter duration_reporter(title); - - if (PropertiesHelper::IsDryRun()) return; - - __for_each_pid(for_each_tid_helper, header, (void *) func); -} - -void show_wchan(int pid, int tid, const char *name) { - if (PropertiesHelper::IsDryRun()) return; - - char path[255]; - char buffer[255]; - int fd, ret, save_errno; - char name_buffer[255]; - - memset(buffer, 0, sizeof(buffer)); - - snprintf(path, sizeof(path), "/proc/%d/wchan", tid); - if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { - printf("Failed to open '%s' (%s)\n", path, strerror(errno)); - return; - } - - ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); - save_errno = errno; - close(fd); - - if (ret < 0) { - printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); - return; - } - - snprintf(name_buffer, sizeof(name_buffer), "%*s%s", - pid == tid ? 0 : 3, "", name); - - printf("%-7d %-32s %s\n", tid, name_buffer, buffer); - - return; -} - -// print time in centiseconds -static void snprcent(char *buffer, size_t len, size_t spc, - unsigned long long time) { - static long hz; // cache discovered hz - - if (hz <= 0) { - hz = sysconf(_SC_CLK_TCK); - if (hz <= 0) { - hz = 1000; - } - } - - // convert to centiseconds - time = (time * 100 + (hz / 2)) / hz; - - char str[16]; - - snprintf(str, sizeof(str), " %llu.%02u", - time / 100, (unsigned)(time % 100)); - size_t offset = strlen(buffer); - snprintf(buffer + offset, (len > offset) ? len - offset : 0, - "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); -} - -// print permille as a percent -static void snprdec(char *buffer, size_t len, size_t spc, unsigned permille) { - char str[16]; - - snprintf(str, sizeof(str), " %u.%u%%", permille / 10, permille % 10); - size_t offset = strlen(buffer); - snprintf(buffer + offset, (len > offset) ? len - offset : 0, - "%*s", (spc > offset) ? (int)(spc - offset) : 0, str); -} - -void show_showtime(int pid, const char *name) { - if (PropertiesHelper::IsDryRun()) return; - - char path[255]; - char buffer[1023]; - int fd, ret, save_errno; - - memset(buffer, 0, sizeof(buffer)); - - snprintf(path, sizeof(path), "/proc/%d/stat", pid); - if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC))) < 0) { - printf("Failed to open '%s' (%s)\n", path, strerror(errno)); - return; - } - - ret = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); - save_errno = errno; - close(fd); - - if (ret < 0) { - printf("Failed to read '%s' (%s)\n", path, strerror(save_errno)); - return; - } - - // field 14 is utime - // field 15 is stime - // field 42 is iotime - unsigned long long utime = 0, stime = 0, iotime = 0; - if (sscanf(buffer, - "%*u %*s %*s %*d %*d %*d %*d %*d %*d %*d %*d " - "%*d %*d %llu %llu %*d %*d %*d %*d %*d %*d " - "%*d %*d %*d %*d %*d %*d %*d %*d %*d %*d " - "%*d %*d %*d %*d %*d %*d %*d %*d %*d %llu ", - &utime, &stime, &iotime) != 3) { - return; - } - - unsigned long long total = utime + stime; - if (!total) { - return; - } - - unsigned permille = (iotime * 1000 + (total / 2)) / total; - if (permille > 1000) { - permille = 1000; - } - - // try to beautify and stabilize columns at <80 characters - snprintf(buffer, sizeof(buffer), "%-6d%s", pid, name); - if ((name[0] != '[') || utime) { - snprcent(buffer, sizeof(buffer), 57, utime); - } - snprcent(buffer, sizeof(buffer), 65, stime); - if ((name[0] != '[') || iotime) { - snprcent(buffer, sizeof(buffer), 73, iotime); - } - if (iotime) { - snprdec(buffer, sizeof(buffer), 79, permille); - } - puts(buffer); // adds a trailing newline - - return; -} - -void do_dmesg() { - const char *title = "KERNEL LOG (dmesg)"; - DurationReporter duration_reporter(title); - printf("------ %s ------\n", title); - - if (PropertiesHelper::IsDryRun()) return; - - /* Get size of kernel buffer */ - int size = klogctl(KLOG_SIZE_BUFFER, nullptr, 0); - if (size <= 0) { - printf("Unexpected klogctl return value: %d\n\n", size); - return; - } - char *buf = (char *) malloc(size + 1); - if (buf == nullptr) { - printf("memory allocation failed\n\n"); - return; - } - int retval = klogctl(KLOG_READ_ALL, buf, size); - if (retval < 0) { - printf("klogctl failure\n\n"); - free(buf); - return; - } - buf[retval] = '\0'; - printf("%s\n\n", buf); - free(buf); - return; -} - -void do_showmap(int pid, const char *name) { - char title[255]; - char arg[255]; - - snprintf(title, sizeof(title), "SHOW MAP %d (%s)", pid, name); - snprintf(arg, sizeof(arg), "%d", pid); - RunCommand(title, {"showmap", "-q", arg}, CommandOptions::AS_ROOT); -} - -int Dumpstate::DumpFile(const std::string& title, const std::string& path) { - DurationReporter duration_reporter(title); - - int status = DumpFileToFd(STDOUT_FILENO, title, path); - - UpdateProgress(WEIGHT_FILE); - - return status; -} - -int read_file_as_long(const char *path, long int *output) { - int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC)); - if (fd < 0) { - int err = errno; - MYLOGE("Error opening file descriptor for %s: %s\n", path, strerror(err)); - return -1; - } - char buffer[50]; - ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer))); - if (bytes_read == -1) { - MYLOGE("Error reading file %s: %s\n", path, strerror(errno)); - return -2; - } - if (bytes_read == 0) { - MYLOGE("File %s is empty\n", path); - return -3; - } - *output = atoi(buffer); - return 0; -} - -/* calls skip to gate calling dump_from_fd recursively - * in the specified directory. dump_from_fd defaults to - * dump_file_from_fd above when set to NULL. skip defaults - * to false when set to NULL. dump_from_fd will always be - * called with title NULL. - */ -int dump_files(const std::string& title, const char* dir, bool (*skip)(const char* path), - int (*dump_from_fd)(const char* title, const char* path, int fd)) { - DurationReporter duration_reporter(title); - DIR *dirp; - struct dirent *d; - char *newpath = nullptr; - const char *slash = "/"; - int retval = 0; - - if (!title.empty()) { - printf("------ %s (%s) ------\n", title.c_str(), dir); - } - if (PropertiesHelper::IsDryRun()) return 0; - - if (dir[strlen(dir) - 1] == '/') { - ++slash; - } - dirp = opendir(dir); - if (dirp == nullptr) { - retval = -errno; - MYLOGE("%s: %s\n", dir, strerror(errno)); - return retval; - } - - if (!dump_from_fd) { - dump_from_fd = dump_file_from_fd; - } - for (; ((d = readdir(dirp))); free(newpath), newpath = nullptr) { - if ((d->d_name[0] == '.') - && (((d->d_name[1] == '.') && (d->d_name[2] == '\0')) - || (d->d_name[1] == '\0'))) { - continue; - } - asprintf(&newpath, "%s%s%s%s", dir, slash, d->d_name, - (d->d_type == DT_DIR) ? "/" : ""); - if (!newpath) { - retval = -errno; - continue; - } - if (skip && (*skip)(newpath)) { - continue; - } - if (d->d_type == DT_DIR) { - int ret = dump_files("", newpath, skip, dump_from_fd); - if (ret < 0) { - retval = ret; - } - continue; - } - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(newpath, O_RDONLY | O_NONBLOCK | O_CLOEXEC))); - if (fd.get() < 0) { - retval = -1; - printf("*** %s: %s\n", newpath, strerror(errno)); - continue; - } - (*dump_from_fd)(nullptr, newpath, fd.get()); - } - closedir(dirp); - if (!title.empty()) { - printf("\n"); - } - return retval; -} - -/* fd must have been opened with the flag O_NONBLOCK. With this flag set, - * it's possible to avoid issues where opening the file itself can get - * stuck. - */ -int dump_file_from_fd(const char *title, const char *path, int fd) { - if (PropertiesHelper::IsDryRun()) return 0; - - int flags = fcntl(fd, F_GETFL); - if (flags == -1) { - printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno)); - return -1; - } else if (!(flags & O_NONBLOCK)) { - printf("*** %s: fd must have O_NONBLOCK set.\n", path); - return -1; - } - return DumpFileFromFdToFd(title, path, fd, STDOUT_FILENO, PropertiesHelper::IsDryRun()); -} - -int Dumpstate::RunCommand(const std::string& title, const std::vector<std::string>& full_command, - const CommandOptions& options) { - DurationReporter duration_reporter(title); - - int status = RunCommandToFd(STDOUT_FILENO, title, full_command, options); - - /* TODO: for now we're simplifying the progress calculation by using the - * timeout as the weight. It's a good approximation for most cases, except when calling dumpsys, - * where its weight should be much higher proportionally to its timeout. - * Ideally, it should use a options.EstimatedDuration() instead...*/ - UpdateProgress(options.Timeout()); - - return status; -} - -void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args, - const CommandOptions& options, long dumpsysTimeoutMs) { - long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs(); - std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)}; - dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end()); - RunCommand(title, dumpsys, options); -} - -int open_socket(const char *service) { - int s = android_get_control_socket(service); - if (s < 0) { - MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno)); - return -1; - } - fcntl(s, F_SETFD, FD_CLOEXEC); - if (listen(s, 4) < 0) { - MYLOGE("listen(control socket): %s\n", strerror(errno)); - return -1; - } - - struct sockaddr addr; - socklen_t alen = sizeof(addr); - int fd = accept(s, &addr, &alen); - if (fd < 0) { - MYLOGE("accept(control socket): %s\n", strerror(errno)); - return -1; - } - - return fd; -} - -/* redirect output to a service control socket */ -bool redirect_to_socket(FILE* redirect, const char* service) { - int fd = open_socket(service); - if (fd == -1) { - return false; - } - fflush(redirect); - // TODO: handle dup2 failure - TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); - close(fd); - return true; -} - -// TODO: should call is_valid_output_file and/or be merged into it. -void create_parent_dirs(const char *path) { - char *chp = const_cast<char *> (path); - - /* skip initial slash */ - if (chp[0] == '/') - chp++; - - /* create leading directories, if necessary */ - struct stat dir_stat; - while (chp && chp[0]) { - chp = strchr(chp, '/'); - if (chp) { - *chp = 0; - if (stat(path, &dir_stat) == -1 || !S_ISDIR(dir_stat.st_mode)) { - MYLOGI("Creating directory %s\n", path); - if (mkdir(path, 0770)) { /* drwxrwx--- */ - MYLOGE("Unable to create directory %s: %s\n", path, strerror(errno)); - } else if (chown(path, AID_SHELL, AID_SHELL)) { - MYLOGE("Unable to change ownership of dir %s: %s\n", path, strerror(errno)); - } - } - *chp++ = '/'; - } - } -} - -bool _redirect_to_file(FILE* redirect, char* path, int truncate_flag) { - create_parent_dirs(path); - - int fd = TEMP_FAILURE_RETRY(open(path, - O_WRONLY | O_CREAT | truncate_flag | O_CLOEXEC | O_NOFOLLOW, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); - if (fd < 0) { - MYLOGE("%s: %s\n", path, strerror(errno)); - return false; - } - - TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); - close(fd); - return true; -} - -bool redirect_to_file(FILE* redirect, char* path) { - return _redirect_to_file(redirect, path, O_TRUNC); -} - -bool redirect_to_existing_file(FILE* redirect, char* path) { - return _redirect_to_file(redirect, path, O_APPEND); -} - -void dump_route_tables() { - DurationReporter duration_reporter("DUMP ROUTE TABLES"); - if (PropertiesHelper::IsDryRun()) return; - const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables"; - ds.DumpFile("RT_TABLES", RT_TABLES_PATH); - FILE* fp = fopen(RT_TABLES_PATH, "re"); - if (!fp) { - printf("*** %s: %s\n", RT_TABLES_PATH, strerror(errno)); - return; - } - char table[16]; - // Each line has an integer (the table number), a space, and a string (the table name). We only - // need the table number. It's a 32-bit unsigned number, so max 10 chars. Skip the table name. - // Add a fixed max limit so this doesn't go awry. - for (int i = 0; i < 64 && fscanf(fp, " %10s %*s", table) == 1; ++i) { - RunCommand("ROUTE TABLE IPv4", {"ip", "-4", "route", "show", "table", table}); - RunCommand("ROUTE TABLE IPv6", {"ip", "-6", "route", "show", "table", table}); - } - fclose(fp); -} - -// TODO: make this function thread safe if sections are generated in parallel. -void Dumpstate::UpdateProgress(int32_t delta_sec) { - if (progress_ == nullptr) { - MYLOGE("UpdateProgress: progress_ not set\n"); - return; - } - - // Always update progess so stats can be tuned... - bool max_changed = progress_->Inc(delta_sec); - - // ...but only notifiy listeners when necessary. - if (!options_->do_progress_updates) return; - - int progress = progress_->Get(); - int max = progress_->GetMax(); - - // adjusts max on the fly - if (max_changed && listener_ != nullptr) { - listener_->onMaxProgressUpdated(max); - } - - int32_t last_update_delta = progress - last_updated_progress_; - if (last_updated_progress_ > 0 && last_update_delta < update_progress_threshold_) { - return; - } - last_updated_progress_ = progress; - - if (control_socket_fd_ >= 0) { - dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress, max); - fsync(control_socket_fd_); - } - - int percent = 100 * progress / max; - if (listener_ != nullptr) { - if (percent % 5 == 0) { - // We don't want to spam logcat, so only log multiples of 5. - MYLOGD("Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), progress, max, - percent); - } else { - // stderr is ignored on normal invocations, but useful when calling - // /system/bin/dumpstate directly for debuggging. - fprintf(stderr, "Setting progress (%s): %d/%d (%d%%)\n", listener_name_.c_str(), - progress, max, percent); - } - // TODO(b/111441001): Remove in favor of onProgress - listener_->onProgressUpdated(progress); - - listener_->onProgress(percent); - } -} - -void Dumpstate::TakeScreenshot(const std::string& path) { - const std::string& real_path = path.empty() ? screenshot_path_ : path; - int status = - RunCommand("", {"/system/bin/screencap", "-p", real_path}, - CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); - if (status == 0) { - MYLOGD("Screenshot saved on %s\n", real_path.c_str()); - } else { - MYLOGE("Failed to take screenshot on %s\n", real_path.c_str()); - } -} - -bool is_dir(const char* pathname) { - struct stat info; - if (stat(pathname, &info) == -1) { - return false; - } - return S_ISDIR(info.st_mode); -} - -time_t get_mtime(int fd, time_t default_mtime) { - struct stat info; - if (fstat(fd, &info) == -1) { - return default_mtime; - } - return info.st_mtime; -} - -void dump_emmc_ecsd(const char *ext_csd_path) { - // List of interesting offsets - struct hex { - char str[2]; - }; - static const size_t EXT_CSD_REV = 192 * sizeof(hex); - static const size_t EXT_PRE_EOL_INFO = 267 * sizeof(hex); - static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_A = 268 * sizeof(hex); - static const size_t EXT_DEVICE_LIFE_TIME_EST_TYP_B = 269 * sizeof(hex); - - std::string buffer; - if (!android::base::ReadFileToString(ext_csd_path, &buffer)) { - return; - } - - printf("------ %s Extended CSD ------\n", ext_csd_path); - - if (buffer.length() < (EXT_CSD_REV + sizeof(hex))) { - printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length()); - return; - } - - int ext_csd_rev = 0; - std::string sub = buffer.substr(EXT_CSD_REV, sizeof(hex)); - if (sscanf(sub.c_str(), "%2x", &ext_csd_rev) != 1) { - printf("*** %s: EXT_CSD_REV parse error \"%s\"\n\n", ext_csd_path, sub.c_str()); - return; - } - - static const char *ver_str[] = { - "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0" - }; - printf("rev 1.%d (MMC %s)\n", ext_csd_rev, - (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ? ver_str[ext_csd_rev] - : "Unknown"); - if (ext_csd_rev < 7) { - printf("\n"); - return; - } - - if (buffer.length() < (EXT_PRE_EOL_INFO + sizeof(hex))) { - printf("*** %s: truncated content %zu\n\n", ext_csd_path, buffer.length()); - return; - } - - int ext_pre_eol_info = 0; - sub = buffer.substr(EXT_PRE_EOL_INFO, sizeof(hex)); - if (sscanf(sub.c_str(), "%2x", &ext_pre_eol_info) != 1) { - printf("*** %s: PRE_EOL_INFO parse error \"%s\"\n\n", ext_csd_path, sub.c_str()); - return; - } - - static const char *eol_str[] = { - "Undefined", - "Normal", - "Warning (consumed 80% of reserve)", - "Urgent (consumed 90% of reserve)" - }; - printf( - "PRE_EOL_INFO %d (MMC %s)\n", ext_pre_eol_info, - eol_str[(ext_pre_eol_info < (int)(sizeof(eol_str) / sizeof(eol_str[0]))) ? ext_pre_eol_info - : 0]); - - for (size_t lifetime = EXT_DEVICE_LIFE_TIME_EST_TYP_A; - lifetime <= EXT_DEVICE_LIFE_TIME_EST_TYP_B; - lifetime += sizeof(hex)) { - int ext_device_life_time_est; - static const char *est_str[] = { - "Undefined", - "0-10% of device lifetime used", - "10-20% of device lifetime used", - "20-30% of device lifetime used", - "30-40% of device lifetime used", - "40-50% of device lifetime used", - "50-60% of device lifetime used", - "60-70% of device lifetime used", - "70-80% of device lifetime used", - "80-90% of device lifetime used", - "90-100% of device lifetime used", - "Exceeded the maximum estimated device lifetime", - }; - - if (buffer.length() < (lifetime + sizeof(hex))) { - printf("*** %s: truncated content %zu\n", ext_csd_path, buffer.length()); - break; - } - - ext_device_life_time_est = 0; - sub = buffer.substr(lifetime, sizeof(hex)); - if (sscanf(sub.c_str(), "%2x", &ext_device_life_time_est) != 1) { - printf("*** %s: DEVICE_LIFE_TIME_EST_TYP_%c parse error \"%s\"\n", ext_csd_path, - (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A', - sub.c_str()); - continue; - } - printf("DEVICE_LIFE_TIME_EST_TYP_%c %d (MMC %s)\n", - (unsigned)((lifetime - EXT_DEVICE_LIFE_TIME_EST_TYP_A) / sizeof(hex)) + 'A', - ext_device_life_time_est, - est_str[(ext_device_life_time_est < (int)(sizeof(est_str) / sizeof(est_str[0]))) - ? ext_device_life_time_est - : 0]); - } - - printf("\n"); -} diff --git a/cmds/dumpsys/TEST_MAPPING b/cmds/dumpsys/TEST_MAPPING new file mode 100644 index 0000000000..dc88ada034 --- /dev/null +++ b/cmds/dumpsys/TEST_MAPPING @@ -0,0 +1,13 @@ +{ + "presubmit": [ + { + // small test which assumes the output format of dumpsys, however + // there are many other parts of Android that expect the output + // to be a specific way (see b/141728094) + "name": "timezone_data_e2e_tests" + }, + { + "name": "dumpsys_test" + } + ] +} diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 4811927106..a427c8dd68 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -29,6 +29,7 @@ #include <utils/Log.h> #include <utils/Vector.h> +#include <iostream> #include <fcntl.h> #include <getopt.h> #include <stdio.h> @@ -59,12 +60,13 @@ static void usage() { "usage: dumpsys\n" " To dump all services.\n" "or:\n" - " dumpsys [-t TIMEOUT] [--priority LEVEL] [--help | -l | --skip SERVICES | " - "SERVICE [ARGS]]\n" + " dumpsys [-t TIMEOUT] [--priority LEVEL] [--pid] [--help | -l | --skip SERVICES " + "| SERVICE [ARGS]]\n" " --help: shows this help\n" " -l: only list services, do not dump them\n" " -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n" " -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n" + " --pid: dump PID instead of usual dump\n" " --proto: filter services that support dumping data in proto format. Dumps\n" " will be in proto format.\n" " --priority LEVEL: filter services based on specified priority\n" @@ -120,9 +122,11 @@ int Dumpsys::main(int argc, char* const argv[]) { bool showListOnly = false; bool skipServices = false; bool asProto = false; + Type type = Type::DUMP; int timeoutArgMs = 10000; int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL; - static struct option longOptions[] = {{"priority", required_argument, 0, 0}, + static struct option longOptions[] = {{"pid", no_argument, 0, 0}, + {"priority", required_argument, 0, 0}, {"proto", no_argument, 0, 0}, {"skip", no_argument, 0, 0}, {"help", no_argument, 0, 0}, @@ -157,6 +161,8 @@ int Dumpsys::main(int argc, char* const argv[]) { usage(); return -1; } + } else if (!strcmp(longOptions[optionIndex].name, "pid")) { + type = Type::PID; } break; @@ -201,7 +207,13 @@ int Dumpsys::main(int argc, char* const argv[]) { if (i == optind) { services.add(String16(argv[i])); } else { - args.add(String16(argv[i])); + const String16 arg(argv[i]); + args.add(arg); + // For backward compatible, if the proto argument is passed to the service, the + // dump request is also considered to use proto. + if (!asProto && !arg.compare(String16(PriorityDumper::PROTO_ARG))) { + asProto = true; + } } } } @@ -220,14 +232,14 @@ int Dumpsys::main(int argc, char* const argv[]) { const size_t N = services.size(); if (N > 1) { // first print a list of the current services - aout << "Currently running services:" << endl; + std::cout << "Currently running services:" << std::endl; for (size_t i=0; i<N; i++) { sp<IBinder> service = sm_->checkService(services[i]); if (service != nullptr) { bool skipped = IsSkipped(skippedServices, services[i]); - aout << " " << services[i] << (skipped ? " (skipped)" : "") << endl; + std::cout << " " << services[i] << (skipped ? " (skipped)" : "") << std::endl; } } } @@ -240,7 +252,7 @@ int Dumpsys::main(int argc, char* const argv[]) { const String16& serviceName = services[i]; if (IsSkipped(skippedServices, serviceName)) continue; - if (startDumpThread(serviceName, args) == OK) { + if (startDumpThread(type, serviceName, args) == OK) { bool addSeparator = (N > 1); if (addSeparator) { writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags); @@ -252,10 +264,10 @@ int Dumpsys::main(int argc, char* const argv[]) { asProto, elapsedDuration, bytesWritten); if (status == TIMED_OUT) { - aout << endl + std::cout << std::endl << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs - << "ms) EXPIRED ***" << endl - << endl; + << "ms) EXPIRED ***" << std::endl + << std::endl; } if (addSeparator) { @@ -307,17 +319,28 @@ void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityF } } -status_t Dumpsys::startDumpThread(const String16& serviceName, const Vector<String16>& args) { +static status_t dumpPidToFd(const sp<IBinder>& service, const unique_fd& fd) { + pid_t pid; + status_t status = service->getDebugPid(&pid); + if (status != OK) { + return status; + } + WriteStringToFd(std::to_string(pid) + "\n", fd.get()); + return OK; +} + +status_t Dumpsys::startDumpThread(Type type, const String16& serviceName, + const Vector<String16>& args) { sp<IBinder> service = sm_->checkService(serviceName); if (service == nullptr) { - aerr << "Can't find service: " << serviceName << endl; + std::cerr << "Can't find service: " << serviceName << std::endl; return NAME_NOT_FOUND; } int sfd[2]; if (pipe(sfd) != 0) { - aerr << "Failed to create pipe to dump service info for " << serviceName << ": " - << strerror(errno) << endl; + std::cerr << "Failed to create pipe to dump service info for " << serviceName << ": " + << strerror(errno) << std::endl; return -errno; } @@ -327,17 +350,23 @@ status_t Dumpsys::startDumpThread(const String16& serviceName, const Vector<Stri // dump blocks until completion, so spawn a thread.. activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable { - int err = service->dump(remote_end.get(), args); + status_t err = 0; - // It'd be nice to be able to close the remote end of the socketpair before the dump - // call returns, to terminate our reads if the other end closes their copy of the - // file descriptor, but then hangs for some reason. There doesn't seem to be a good - // way to do this, though. - remote_end.reset(); + switch (type) { + case Type::DUMP: + err = service->dump(remote_end.get(), args); + break; + case Type::PID: + err = dumpPidToFd(service, remote_end); + break; + default: + std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl; + return; + } - if (err != 0) { - aerr << "Error dumping service info: (" << strerror(err) << ") " - << serviceName << endl; + if (err != OK) { + std::cerr << "Error dumping service info status_t: " << statusToString(err) << " " + << serviceName << std::endl; } }); return OK; @@ -394,8 +423,8 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); if (rc < 0) { - aerr << "Error in poll while dumping service " << serviceName << " : " - << strerror(errno) << endl; + std::cerr << "Error in poll while dumping service " << serviceName << " : " + << strerror(errno) << std::endl; status = -errno; break; } else if (rc == 0) { @@ -406,8 +435,8 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi char buf[4096]; rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf))); if (rc < 0) { - aerr << "Failed to read while dumping service " << serviceName << ": " - << strerror(errno) << endl; + std::cerr << "Failed to read while dumping service " << serviceName << ": " + << strerror(errno) << std::endl; status = -errno; break; } else if (rc == 0) { @@ -416,8 +445,8 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi } if (!WriteFully(fd, buf, rc)) { - aerr << "Failed to write while dumping service " << serviceName << ": " - << strerror(errno) << endl; + std::cerr << "Failed to write while dumping service " << serviceName << ": " + << strerror(errno) << std::endl; status = -errno; break; } diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h index c48a1e959b..929c55c364 100644 --- a/cmds/dumpsys/dumpsys.h +++ b/cmds/dumpsys/dumpsys.h @@ -51,6 +51,11 @@ class Dumpsys { */ static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags); + enum class Type { + DUMP, // dump using `dump` function + PID, // dump pid of server only + }; + /** * Starts a thread to connect to a service and get its dump output. The thread redirects * the output to a pipe. Thread must be stopped by a subsequent callto {@code @@ -61,7 +66,8 @@ class Dumpsys { * {@code NAME_NOT_FOUND} service could not be found. * {@code != OK} error */ - status_t startDumpThread(const String16& serviceName, const Vector<String16>& args); + status_t startDumpThread(Type type, const String16& serviceName, + const Vector<String16>& args); /** * Writes a section header to a file descriptor. diff --git a/cmds/dumpsys/main.cpp b/cmds/dumpsys/main.cpp index 8ba0eba0fa..fa4cc97b91 100644 --- a/cmds/dumpsys/main.cpp +++ b/cmds/dumpsys/main.cpp @@ -21,10 +21,9 @@ #include "dumpsys.h" #include <binder/IServiceManager.h> -#include <binder/TextOutput.h> +#include <iostream> #include <signal.h> -#include <stdio.h> using namespace android; @@ -34,7 +33,7 @@ int main(int argc, char* const argv[]) { fflush(stdout); if (sm == nullptr) { ALOGE("Unable to get default service manager!"); - aerr << "dumpsys: Unable to get default service manager!" << endl; + std::cerr << "dumpsys: Unable to get default service manager!" << std::endl; return 20; } diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 3ada15398c..b9395ba4e2 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -53,7 +53,8 @@ class ServiceManagerMock : public IServiceManager { MOCK_CONST_METHOD1(checkService, sp<IBinder>(const String16&)); MOCK_METHOD4(addService, status_t(const String16&, const sp<IBinder>&, bool, int)); MOCK_METHOD1(listServices, Vector<String16>(int)); - + MOCK_METHOD1(waitForService, sp<IBinder>(const String16&)); + MOCK_METHOD1(isDeclared, bool(const String16&)); protected: MOCK_METHOD0(onAsBinder, IBinder*()); }; @@ -194,7 +195,7 @@ class DumpsysTest : public Test { CaptureStdout(); CaptureStderr(); dump_.setServiceArgs(args, supportsProto, priorityFlags); - status_t status = dump_.startDumpThread(serviceName, args); + status_t status = dump_.startDumpThread(Dumpsys::Type::DUMP, serviceName, args); EXPECT_THAT(status, Eq(0)); status = dump_.writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(500), false, elapsedDuration, bytesWritten); @@ -539,6 +540,27 @@ TEST_F(DumpsysTest, DumpWithPriorityHighAndProto) { AssertDumpedWithPriority("runninghigh2", "dump2", PriorityDumper::PRIORITY_ARG_HIGH); } +// Tests 'dumpsys --pid' +TEST_F(DumpsysTest, ListAllServicesWithPid) { + ExpectListServices({"Locksmith", "Valet"}); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + + CallMain({"--pid"}); + + AssertRunningServices({"Locksmith", "Valet"}); + AssertOutputContains(std::to_string(getpid())); +} + +// Tests 'dumpsys --pid service_name' +TEST_F(DumpsysTest, ListServiceWithPid) { + ExpectCheckService("Locksmith"); + + CallMain({"--pid", "Locksmith"}); + + AssertOutput(std::to_string(getpid()) + "\n"); +} + TEST_F(DumpsysTest, GetBytesWritten) { const char* serviceName = "service2"; const char* dumpContents = "dump1"; @@ -563,4 +585,4 @@ TEST_F(DumpsysTest, WriteDumpWithoutThreadStart) { dump_.writeDump(STDOUT_FILENO, String16("service"), std::chrono::milliseconds(500), /* as_proto = */ false, elapsedDuration, bytesWritten); EXPECT_THAT(status, Eq(INVALID_OPERATION)); -}
\ No newline at end of file +} diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp new file mode 100644 index 0000000000..402767a426 --- /dev/null +++ b/cmds/idlcli/Android.bp @@ -0,0 +1,60 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_defaults { + name: "idlcli-defaults", + shared_libs: [ + "android.hardware.vibrator-ndk_platform", + "android.hardware.vibrator@1.0", + "android.hardware.vibrator@1.1", + "android.hardware.vibrator@1.2", + "android.hardware.vibrator@1.3", + "libbase", + "libbinder_ndk", + "libhidlbase", + "liblog", + "libutils", + ], + cflags: [ + "-DLOG_TAG=\"idlcli\"", + ], + vendor_available: true, +} + +cc_library { + name: "libidlcli", + defaults: ["idlcli-defaults"], + srcs: [ + "CommandVibrator.cpp", + "vibrator/CommandCompose.cpp", + "vibrator/CommandGetCapabilities.cpp", + "vibrator/CommandGetCompositionDelayMax.cpp", + "vibrator/CommandGetCompositionSizeMax.cpp", + "vibrator/CommandOff.cpp", + "vibrator/CommandOn.cpp", + "vibrator/CommandPerform.cpp", + "vibrator/CommandSetAmplitude.cpp", + "vibrator/CommandSetExternalControl.cpp", + "vibrator/CommandSupportsAmplitudeControl.cpp", + "vibrator/CommandSupportsExternalControl.cpp", + ], + visibility: [":__subpackages__"], +} + +cc_binary { + name: "idlcli", + defaults: ["idlcli-defaults"], + srcs: ["main.cpp"], + whole_static_libs: ["libidlcli"], +} diff --git a/cmds/idlcli/CommandVibrator.cpp b/cmds/idlcli/CommandVibrator.cpp new file mode 100644 index 0000000000..a7a70c38af --- /dev/null +++ b/cmds/idlcli/CommandVibrator.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" + +namespace android { +namespace idlcli { + +class IdlCli; + +class CommandVibrator : public CommandWithSubcommands<CommandVibrator> { + std::string getDescription() const override { return "Invoke Vibrator HIDL APIs."; } + + std::string getUsageSummary() const override { return "<api> [arguments]"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<api>", CommandRegistry<CommandVibrator>::List()}, + }; + return details; + } +}; + +static const auto Command = CommandRegistry<IdlCli>::Register<CommandVibrator>("vibrator"); + +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/IdlCli.h b/cmds/idlcli/IdlCli.h new file mode 100644 index 0000000000..dd8430415e --- /dev/null +++ b/cmds/idlcli/IdlCli.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_ +#define FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_ + +#include "utils.h" + +namespace android { +namespace idlcli { + +class IdlCli : public CommandWithSubcommands<IdlCli> { + std::string getDescription() const override { return "Invoke IDL APIs."; } + + std::string getUsageSummary() const override { return "<idl> [arguments]"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<idl>", CommandRegistry<IdlCli>::List()}, + }; + return details; + } +}; + +} // namespace idlcli +} // namespace android + +#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_IDLCLI_H_ diff --git a/cmds/idlcli/main.cpp b/cmds/idlcli/main.cpp new file mode 100644 index 0000000000..9ed9d8216a --- /dev/null +++ b/cmds/idlcli/main.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IdlCli.h" +#include "utils.h" + +int main(const int argc, const char* const argv[]) { + using namespace ::android::idlcli; + return IdlCli{}.main(Args{argc, argv}); +} diff --git a/cmds/idlcli/utils.h b/cmds/idlcli/utils.h new file mode 100644 index 0000000000..a8e595470d --- /dev/null +++ b/cmds/idlcli/utils.h @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_ +#define FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_ + +#include <hidl/HidlSupport.h> + +#include <iomanip> +#include <iostream> +#include <map> +#include <sstream> +#include <string> +#include <vector> + +namespace android { +namespace idlcli { + +namespace overrides { + +namespace details { + +template <typename T> +inline std::istream &operator>>(std::istream &stream, T &out) { + auto pos = stream.tellg(); + auto tmp = +out; + auto min = +std::numeric_limits<T>::min(); + auto max = +std::numeric_limits<T>::max(); + stream >> tmp; + if (!stream) { + return stream; + } + if (tmp < min || tmp > max) { + stream.seekg(pos); + stream.setstate(std::ios_base::failbit); + return stream; + } + out = tmp; + return stream; +} + +} // namespace details + +// override for default behavior of treating as a character +inline std::istream &operator>>(std::istream &stream, int8_t &out) { + return details::operator>>(stream, out); +} + +// override for default behavior of treating as a character +inline std::istream &operator>>(std::istream &stream, uint8_t &out) { + return details::operator>>(stream, out); +} + +} // namespace overrides + +template <typename T, typename R = hardware::hidl_enum_range<T>> +inline std::istream &operator>>(std::istream &stream, T &out) { + using overrides::operator>>; + auto validRange = R(); + auto pos = stream.tellg(); + std::underlying_type_t<T> in; + T tmp; + stream >> in; + if (!stream) { + return stream; + } + tmp = static_cast<T>(in); + if (tmp < *validRange.begin() || tmp > *std::prev(validRange.end())) { + stream.seekg(pos); + stream.setstate(std::ios_base::failbit); + return stream; + } + out = tmp; + return stream; +} + +enum Status : unsigned int { + OK, + USAGE, + UNAVAILABLE, + ERROR, +}; + +class Args { +public: + Args(const int argc, const char *const argv[]) { + for (int argi = 0; argi < argc; argi++) { + mArgs.emplace_back(std::string_view(argv[argi])); + } + } + + template <typename T = std::string> + std::optional<T> get() { + return get<T>(false); + } + + template <typename T = std::string> + std::optional<T> pop() { + return get<T>(true); + } + + bool empty() { return mArgs.empty(); } + +private: + template <typename T> + std::optional<T> get(bool erase) { + using idlcli::operator>>; + using overrides::operator>>; + T retValue; + + if (mArgs.empty()) { + return {}; + } + + std::stringstream stream{std::string{mArgs.front()}}; + stream >> std::setbase(0) >> retValue; + if (!stream || !stream.eof()) { + return {}; + } + + if (erase) { + mArgs.erase(mArgs.begin()); + } + + return retValue; + } + + std::vector<std::string_view> mArgs; +}; + +class Command { +protected: + struct Usage { + std::string name; + std::vector<std::string> details; + }; + using UsageDetails = std::vector<Usage>; + +public: + virtual ~Command() = default; + + Status main(Args &&args) { + Status status = doArgsAndMain(std::move(args)); + if (status == USAGE) { + printUsage(); + return ERROR; + } + if (status == UNAVAILABLE) { + std::cerr << "The requested operation is unavailable." << std::endl; + return ERROR; + } + return status; + } + +private: + virtual std::string getDescription() const = 0; + virtual std::string getUsageSummary() const = 0; + virtual UsageDetails getUsageDetails() const = 0; + virtual Status doArgs(Args &args) = 0; + virtual Status doMain(Args &&args) = 0; + + void printUsage() const { + std::cerr << "Description:\n " << getDescription() << std::endl; + std::cerr << "Usage:\n " << mName << " " << getUsageSummary() << std::endl; + + std::cerr << "Details:" << std::endl; + size_t entryNameWidth = 0; + for (auto &entry : getUsageDetails()) { + entryNameWidth = std::max(entryNameWidth, entry.name.length()); + } + for (auto &entry : getUsageDetails()) { + auto prefix = entry.name; + for (auto &line : entry.details) { + std::cerr << " " << std::left << std::setw(entryNameWidth + 8) << prefix << line + << std::endl; + prefix = ""; + } + } + } + + Status doArgsAndMain(Args &&args) { + Status status; + mName = *args.pop(); + if ((status = doArgs(args)) != OK) { + return status; + } + if ((status = doMain(std::move(args))) != OK) { + return status; + } + return OK; + } + +protected: + std::string mName; +}; + +template <typename T> +class CommandRegistry { +private: + using CommandCreator = std::function<std::unique_ptr<Command>()>; + +public: + template <typename U> + static CommandCreator Register(const std::string name) { + Instance()->mCommands[name] = [] { return std::make_unique<U>(); }; + return Instance()->mCommands[name]; + } + + static std::unique_ptr<Command> Create(const std::string name) { + auto it = Instance()->mCommands.find(name); + if (it == Instance()->mCommands.end()) { + return nullptr; + } + return it->second(); + } + + static auto List() { + std::vector<std::string> list; + for (auto &it : Instance()->mCommands) { + list.push_back(it.first); + } + std::sort(list.begin(), list.end()); + return list; + } + +private: + static CommandRegistry *Instance() { + static CommandRegistry sRegistry; + return &sRegistry; + } + +private: + std::map<const std::string, CommandCreator> mCommands; +}; + +template <typename T> +class CommandWithSubcommands : public Command { +private: + Status doArgs(Args &args) override { + mCommand = CommandRegistry<T>::Create(*args.get()); + if (!mCommand) { + std::cerr << "Invalid Command!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args &&args) override { return mCommand->main(std::move(args)); } + +protected: + std::unique_ptr<Command> mCommand; +}; + +} // namespace idlcli +} // namespace android + +#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_UTILS_H_ diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h new file mode 100644 index 0000000000..ca5142dee9 --- /dev/null +++ b/cmds/idlcli/vibrator.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_ +#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_ + +#include <aidl/android/hardware/vibrator/IVibrator.h> +#include <android/binder_manager.h> +#include <android/hardware/vibrator/1.3/IVibrator.h> + +#include "utils.h" + +#include "log/log.h" + +namespace android { + +using hardware::Return; + +static constexpr int NUM_TRIES = 2; + +// Creates a Return<R> with STATUS::EX_NULL_POINTER. +template <class R> +inline R NullptrStatus() { + using ::android::hardware::Status; + return Status::fromExceptionCode(Status::EX_NULL_POINTER); +} + +template <> +inline ndk::ScopedAStatus NullptrStatus() { + return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_NULL_POINTER)); +} + +template <typename I> +inline auto getService() { + return I::getService(); +} + +template <> +inline auto getService<aidl::android::hardware::vibrator::IVibrator>() { + const auto instance = + std::string() + aidl::android::hardware::vibrator::IVibrator::descriptor + "/default"; + auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str())); + return aidl::android::hardware::vibrator::IVibrator::fromBinder(vibBinder); +} + +template <typename I> +using shared_ptr = std::result_of_t<decltype(getService<I>)&()>; + +template <typename I> +class HalWrapper { +public: + static std::unique_ptr<HalWrapper> Create() { + // Assume that if getService returns a nullptr, HAL is not available on the + // device. + auto hal = getService<I>(); + return hal ? std::unique_ptr<HalWrapper>(new HalWrapper(std::move(hal))) : nullptr; + } + + template <class R, class... Args0, class... Args1> + R call(R (I::*fn)(Args0...), Args1&&... args1) { + return (*mHal.*fn)(std::forward<Args1>(args1)...); + } + +private: + HalWrapper(shared_ptr<I>&& hal) : mHal(std::move(hal)) {} + +private: + shared_ptr<I> mHal; +}; + +template <typename I> +static auto getHal() { + static auto sHalWrapper = HalWrapper<I>::Create(); + return sHalWrapper.get(); +} + +template <class R, class I, class... Args0, class... Args1> +R halCall(R (I::*fn)(Args0...), Args1&&... args1) { + auto hal = getHal<I>(); + return hal ? hal->call(fn, std::forward<Args1>(args1)...) : NullptrStatus<R>(); +} + +namespace idlcli { +namespace vibrator { + +namespace V1_0 = ::android::hardware::vibrator::V1_0; +namespace V1_1 = ::android::hardware::vibrator::V1_1; +namespace V1_2 = ::android::hardware::vibrator::V1_2; +namespace V1_3 = ::android::hardware::vibrator::V1_3; +namespace aidl = ::aidl::android::hardware::vibrator; + +} // namespace vibrator +} // namespace idlcli + +} // namespace android + +#endif // FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_ diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp new file mode 100644 index 0000000000..4721a5f9ae --- /dev/null +++ b/cmds/idlcli/vibrator/CommandCompose.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +using aidl::CompositeEffect; + +class CommandCompose : public Command { + std::string getDescription() const override { return "Compose vibration."; } + + std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<delay>", {"In milliseconds"}}, + {"<primitive>", {"Primitive ID."}}, + {"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}}, + {"...", {"May repeat multiple times."}}, + }; + return details; + } + + Status doArgs(Args &args) override { + while (!args.empty()) { + CompositeEffect effect; + if (auto delay = args.pop<decltype(effect.delayMs)>()) { + effect.delayMs = *delay; + std::cout << "Delay: " << effect.delayMs << std::endl; + } else { + std::cerr << "Missing or Invalid Delay!" << std::endl; + return USAGE; + } + // TODO: Use range validation when supported by AIDL + if (auto primitive = args.pop<std::underlying_type_t<decltype(effect.primitive)>>()) { + effect.primitive = static_cast<decltype(effect.primitive)>(*primitive); + std::cout << "Primitive: " << toString(effect.primitive) << std::endl; + } else { + std::cerr << "Missing or Invalid Primitive!" << std::endl; + return USAGE; + } + if (auto scale = args.pop<decltype(effect.scale)>(); + scale && *scale > 0.0 && scale <= 1.0) { + effect.scale = *scale; + std::cout << "Scale: " << effect.scale << std::endl; + } else { + std::cerr << "Missing or Invalid Scale!" << std::endl; + return USAGE; + } + mComposite.emplace_back(std::move(effect)); + } + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + Status ret; + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + + return ret; + } + + std::vector<CompositeEffect> mComposite; +}; + +static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandCompose>("compose"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp new file mode 100644 index 0000000000..303a9895e4 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandGetCapabilities : public Command { + std::string getDescription() const override { return "Retrieves vibrator capabilities."; } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + int32_t cap; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getCapabilities, &cap); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Capabilities: " << std::bitset<32>(cap) << std::endl; + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetCapabilities>("getCapabilities"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp new file mode 100644 index 0000000000..10508bd4dc --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandGetCompositionDelayMax : public Command { + std::string getDescription() const override { + return "Retrieves vibrator composition delay max."; + } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + int32_t maxDelayMs; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getCompositionDelayMax, &maxDelayMs); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Max Delay: " << maxDelayMs << " ms" << std::endl; + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetCompositionDelayMax>( + "getCompositionDelayMax"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp new file mode 100644 index 0000000000..900cb18809 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandGetCompositionSizeMax : public Command { + std::string getDescription() const override { + return "Retrieves vibrator composition size max."; + } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + int32_t maxSize; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::getCompositionSizeMax, &maxSize); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Max Size: " << maxSize << std::endl; + + return ret; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandGetCompositionSizeMax>( + "getCompositionSizeMax"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp new file mode 100644 index 0000000000..cedb9fec06 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandOff.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandOff : public Command { + std::string getDescription() const override { return "Turn off vibrator."; } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::off); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else if (auto hal = getHal<V1_0::IVibrator>()) { + auto status = hal->call(&V1_0::IVibrator::off); + statusStr = toString(status); + ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + + return ret; + } +}; + +static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandOff>("off"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp new file mode 100644 index 0000000000..4e7e493d6d --- /dev/null +++ b/cmds/idlcli/vibrator/CommandOn.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandOn : public Command { + std::string getDescription() const override { return "Turn on vibrator."; } + + std::string getUsageSummary() const override { return "<duration>"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<duration>", {"In milliseconds."}}, + }; + return details; + } + + Status doArgs(Args &args) override { + if (auto duration = args.pop<decltype(mDuration)>()) { + mDuration = *duration; + } else { + std::cerr << "Missing or Invalid Duration!" << std::endl; + return USAGE; + } + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else if (auto hal = getHal<V1_0::IVibrator>()) { + auto status = hal->call(&V1_0::IVibrator::on, mDuration); + statusStr = toString(status); + ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + + return ret; + } + + uint32_t mDuration; +}; + +static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandOn>("on"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp new file mode 100644 index 0000000000..69c7e37744 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandPerform.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +/* + * The following static asserts are only relevant here because the argument + * parser uses a single implementation for determining the string names. + */ +static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) == + static_cast<uint8_t>(aidl::EffectStrength::LIGHT)); +static_assert(static_cast<uint8_t>(V1_0::EffectStrength::MEDIUM) == + static_cast<uint8_t>(aidl::EffectStrength::MEDIUM)); +static_assert(static_cast<uint8_t>(V1_0::EffectStrength::STRONG) == + static_cast<uint8_t>(aidl::EffectStrength::STRONG)); +static_assert(static_cast<uint8_t>(V1_3::Effect::CLICK) == + static_cast<uint8_t>(aidl::Effect::CLICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::DOUBLE_CLICK) == + static_cast<uint8_t>(aidl::Effect::DOUBLE_CLICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::TICK) == static_cast<uint8_t>(aidl::Effect::TICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::THUD) == static_cast<uint8_t>(aidl::Effect::THUD)); +static_assert(static_cast<uint8_t>(V1_3::Effect::POP) == static_cast<uint8_t>(aidl::Effect::POP)); +static_assert(static_cast<uint8_t>(V1_3::Effect::HEAVY_CLICK) == + static_cast<uint8_t>(aidl::Effect::HEAVY_CLICK)); +static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_1) == + static_cast<uint8_t>(aidl::Effect::RINGTONE_1)); +static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_2) == + static_cast<uint8_t>(aidl::Effect::RINGTONE_2)); +static_assert(static_cast<uint8_t>(V1_3::Effect::RINGTONE_15) == + static_cast<uint8_t>(aidl::Effect::RINGTONE_15)); +static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) == + static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK)); + +using V1_0::EffectStrength; +using V1_3::Effect; + +class CommandPerform : public Command { + std::string getDescription() const override { return "Perform vibration effect."; } + + std::string getUsageSummary() const override { return "<effect> <strength>"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<effect>", {"Effect ID."}}, + {"<strength>", {"0-2."}}, + }; + return details; + } + + Status doArgs(Args &args) override { + if (auto effect = args.pop<decltype(mEffect)>()) { + mEffect = *effect; + std::cout << "Effect: " << toString(mEffect) << std::endl; + } else { + std::cerr << "Missing or Invalid Effect!" << std::endl; + return USAGE; + } + if (auto strength = args.pop<decltype(mStrength)>()) { + mStrength = *strength; + std::cout << "Strength: " << toString(mStrength) << std::endl; + } else { + std::cerr << "Missing or Invalid Strength!" << std::endl; + return USAGE; + } + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + uint32_t lengthMs; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + int32_t aidlLengthMs; + auto status = + hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect), + static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs); + statusStr = status.getDescription(); + lengthMs = static_cast<uint32_t>(aidlLengthMs); + ret = status.isOk() ? OK : ERROR; + } else { + Return<void> hidlRet; + V1_0::Status status; + auto callback = [&status, &lengthMs](V1_0::Status retStatus, uint32_t retLengthMs) { + status = retStatus; + lengthMs = retLengthMs; + }; + + if (auto hal = getHal<V1_3::IVibrator>()) { + hidlRet = hal->call(&V1_3::IVibrator::perform_1_3, + static_cast<V1_3::Effect>(mEffect), mStrength, callback); + } else if (auto hal = getHal<V1_2::IVibrator>()) { + hidlRet = hal->call(&V1_2::IVibrator::perform_1_2, + static_cast<V1_2::Effect>(mEffect), mStrength, callback); + } else if (auto hal = getHal<V1_1::IVibrator>()) { + hidlRet = hal->call(&V1_1::IVibrator::perform_1_1, + static_cast<V1_1::Effect_1_1>(mEffect), mStrength, callback); + } else if (auto hal = getHal<V1_0::IVibrator>()) { + hidlRet = hal->call(&V1_0::IVibrator::perform, static_cast<V1_0::Effect>(mEffect), + mStrength, callback); + } else { + return UNAVAILABLE; + } + + statusStr = toString(status); + ret = hidlRet.isOk() && status == V1_0::Status::OK ? OK : ERROR; + } + + std::cout << "Status: " << statusStr << std::endl; + std::cout << "Length: " << lengthMs << std::endl; + + return ret; + } + + Effect mEffect; + EffectStrength mStrength; +}; + +static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandPerform>("perform"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp new file mode 100644 index 0000000000..8b8058c4fd --- /dev/null +++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandSetAmplitude : public Command { + std::string getDescription() const override { return "Set vibration amplitude."; } + + std::string getUsageSummary() const override { return "<amplitude>"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<amplitude>", {"1-255."}}, + }; + return details; + } + + Status doArgs(Args &args) override { + if (auto amplitude = args.pop<decltype(mAmplitude)>()) { + mAmplitude = *amplitude; + } else { + std::cerr << "Missing or Invalid Amplitude!" << std::endl; + return USAGE; + } + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::setAmplitude, + static_cast<float>(mAmplitude) / UINT8_MAX); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else if (auto hal = getHal<V1_0::IVibrator>()) { + auto status = hal->call(&V1_0::IVibrator::setAmplitude, mAmplitude); + statusStr = toString(status); + ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + + return ret; + } + + uint8_t mAmplitude; +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandSetAmplitude>("setAmplitude"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp new file mode 100644 index 0000000000..179579310a --- /dev/null +++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandSetExternalControl : public Command { + std::string getDescription() const override { + return "Enable/disable vibration external control."; + } + + std::string getUsageSummary() const override { return "<enable>"; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{ + {"<enable>", {"0/1."}}, + }; + return details; + } + + Status doArgs(Args &args) override { + if (auto enable = args.pop<decltype(mEnable)>()) { + mEnable = *enable; + } else { + std::cerr << "Missing Enable!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + std::string statusStr; + Status ret; + + if (auto hal = getHal<aidl::IVibrator>()) { + auto status = hal->call(&aidl::IVibrator::setExternalControl, mEnable); + statusStr = status.getDescription(); + ret = status.isOk() ? OK : ERROR; + } else if (auto hal = getHal<V1_3::IVibrator>()) { + auto status = hal->call(&V1_3::IVibrator::setExternalControl, mEnable); + statusStr = toString(status); + ret = status.isOk() && status == V1_0::Status::OK ? OK : ERROR; + } else { + return UNAVAILABLE; + } + + std::cout << "Status: " << statusStr << std::endl; + + return ret; + } + + bool mEnable; +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandSetExternalControl>("setExternalControl"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp new file mode 100644 index 0000000000..cdc529a2f3 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandSupportsAmplitudeControl.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandSupportsAmplitudeControl : public Command { + std::string getDescription() const override { return "Check support for amplitude control."; } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + auto ret = halCall(&V1_0::IVibrator::supportsAmplitudeControl); + + if (!ret.isOk()) { + return UNAVAILABLE; + } + + std::cout << "Result: " << std::boolalpha << ret << std::endl; + + return OK; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandSupportsAmplitudeControl>( + "supportsAmplitudeControl"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp new file mode 100644 index 0000000000..ed15d76286 --- /dev/null +++ b/cmds/idlcli/vibrator/CommandSupportsExternalControl.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 The Android Open Source Project * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils.h" +#include "vibrator.h" + +namespace android { +namespace idlcli { + +class CommandVibrator; + +namespace vibrator { + +class CommandSupportsExternalControl : public Command { + std::string getDescription() const override { return "Check support for external control."; } + + std::string getUsageSummary() const override { return ""; } + + UsageDetails getUsageDetails() const override { + UsageDetails details{}; + return details; + } + + Status doArgs(Args &args) override { + if (!args.empty()) { + std::cerr << "Unexpected Arguments!" << std::endl; + return USAGE; + } + return OK; + } + + Status doMain(Args && /*args*/) override { + auto ret = halCall(&V1_3::IVibrator::supportsExternalControl); + + if (!ret.isOk()) { + return UNAVAILABLE; + } + + std::cout << "Result: " << std::boolalpha << ret << std::endl; + + return OK; + } +}; + +static const auto Command = + CommandRegistry<CommandVibrator>::Register<CommandSupportsExternalControl>( + "supportsExternalControl"); + +} // namespace vibrator +} // namespace idlcli +} // namespace android diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index c80ae3bbf6..75dec371bc 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -138,6 +138,7 @@ cc_binary { cc_binary { name: "otapreopt_chroot", + defaults: ["libapexd-deps"], cflags: [ "-Wall", "-Werror", @@ -150,20 +151,11 @@ cc_binary { ], shared_libs: [ "libbase", - "libbinder", "liblog", - "libprotobuf-cpp-full", - "libselinux", "libutils", - "libziparchive", ], static_libs: [ - "libapex", "libapexd", - "lib_apex_manifest_proto", - "libavb", - "libdm", - "libvold_binder", ], } @@ -172,6 +164,7 @@ filegroup { srcs: [ "binder/android/os/IInstalld.aidl", ], + path: "binder", } // diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index caac2e89a7..0fde31a5cc 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -101,6 +101,10 @@ static constexpr const char* IDMAP_SUFFIX = "@idmap"; static constexpr int kVerityPageSize = 4096; static constexpr size_t kSha256Size = 32; static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode"; +static constexpr const char* kFuseProp = "persist.sys.fuse"; + +static constexpr const char* kMntSdcardfs = "/mnt/runtime/default/"; +static constexpr const char* kMntFuse = "/mnt/pass_through/0/"; namespace { @@ -592,12 +596,21 @@ binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::st std::lock_guard<std::recursive_mutex> lock(mMountsLock); for (const auto& n : mStorageMounts) { auto extPath = n.second; - if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) { - extPath += StringPrintf("/%d", userId); - } else if (userId != 0) { - // TODO: support devices mounted under secondary users - continue; + + if (android::base::GetBoolProperty(kFuseProp, false)) { + std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated"); + if (std::regex_match(extPath, re)) { + extPath += "/" + std::to_string(userId); + } + } else { + if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) { + extPath += StringPrintf("/%d", userId); + } else if (userId != 0) { + // TODO: support devices mounted under secondary users + continue; + } } + if (flags & FLAG_CLEAR_CACHE_ONLY) { // Clear only cached data from shared storage auto path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname); @@ -616,10 +629,8 @@ binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::st if (delete_dir_contents(path, true) != 0) { res = error("Failed to delete contents of " + path); } - path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname); - if (delete_dir_contents(path, true) != 0) { - res = error("Failed to delete contents of " + path); - } + // Note that we explicitly don't delete OBBs - those are only removed on + // app uninstall. } } } @@ -688,16 +699,26 @@ binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std:: std::lock_guard<std::recursive_mutex> lock(mMountsLock); for (const auto& n : mStorageMounts) { auto extPath = n.second; - if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) { - extPath += StringPrintf("/%d", userId); - } else if (userId != 0) { - // TODO: support devices mounted under secondary users - continue; + + if (android::base::GetBoolProperty(kFuseProp, false)) { + std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated"); + if (std::regex_match(extPath, re)) { + extPath += "/" + std::to_string(userId); + } + } else { + if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) { + extPath += StringPrintf("/%d", userId); + } else if (userId != 0) { + // TODO: support devices mounted under secondary users + continue; + } } + auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname); if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete contents of " + path); } + path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname); if (delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete contents of " + path); @@ -832,7 +853,7 @@ static int32_t copy_directory_recursive(const char* from, const char* to) { }; LOG(DEBUG) << "Copying " << from << " to " << to; - return android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); + return logwrap_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, LOG_ALOG, false, nullptr); } binder::Status InstalldNativeService::snapshotAppData( @@ -2107,10 +2128,15 @@ binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t CHECK_ARGUMENT_PATH(dexMetadataPath); std::lock_guard<std::recursive_mutex> lock(mLock); + const char* oat_dir = getCStr(outputPath); + const char* instruction_set = instructionSet.c_str(); + if (oat_dir != nullptr && !createOatDir(oat_dir, instruction_set).isOk()) { + // Can't create oat dir - let dexopt use cache dir. + oat_dir = nullptr; + } + const char* apk_path = apkPath.c_str(); const char* pkgname = getCStr(packageName, "*"); - const char* instruction_set = instructionSet.c_str(); - const char* oat_dir = getCStr(outputPath); const char* compiler_filter = compilerFilter.c_str(); const char* volume_uuid = getCStr(uuid); const char* class_loader_context = getCStr(classLoaderContext); @@ -2600,7 +2626,7 @@ struct fsverity_measurement { #endif binder::Status InstalldNativeService::installApkVerity(const std::string& filePath, - const ::android::base::unique_fd& verityInputAshmem, int32_t contentSize) { + android::base::unique_fd verityInputAshmem, int32_t contentSize) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(filePath); std::lock_guard<std::recursive_mutex> lock(mLock); @@ -2773,12 +2799,19 @@ binder::Status InstalldNativeService::invalidateMounts() { std::getline(in, target, ' '); std::getline(in, ignored); + if (android::base::GetBoolProperty(kFuseProp, false)) { + if (target.find(kMntFuse) == 0) { + LOG(DEBUG) << "Found storage mount " << source << " at " << target; + mStorageMounts[source] = target; + } + } else { #if !BYPASS_SDCARDFS - if (target.compare(0, 21, "/mnt/runtime/default/") == 0) { - LOG(DEBUG) << "Found storage mount " << source << " at " << target; - mStorageMounts[source] = target; - } + if (target.find(kMntSdcardfs) == 0) { + LOG(DEBUG) << "Found storage mount " << source << " at " << target; + mStorageMounts[source] = target; + } #endif + } } return ok(); } diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 2b7bf33cbc..149936dbaf 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -136,7 +136,7 @@ public: binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet, const std::unique_ptr<std::string>& outputPath); binder::Status installApkVerity(const std::string& filePath, - const ::android::base::unique_fd& verityInput, int32_t contentSize); + android::base::unique_fd verityInput, int32_t contentSize); binder::Status assertFsverityRootHashMatches(const std::string& filePath, const std::vector<uint8_t>& expectedHash); binder::Status reconcileSecondaryDexFile(const std::string& dexPath, diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS index 56739181bb..9a21104131 100644 --- a/cmds/installd/OWNERS +++ b/cmds/installd/OWNERS @@ -4,7 +4,9 @@ agampe@google.com calin@google.com jsharkey@android.com maco@google.com +mast@google.com mathieuc@google.com narayan@google.com ngeoffray@google.com +rpl@google.com toddke@google.com diff --git a/cmds/installd/QuotaUtils.cpp b/cmds/installd/QuotaUtils.cpp index b238dd36e3..f2abf3aea3 100644 --- a/cmds/installd/QuotaUtils.cpp +++ b/cmds/installd/QuotaUtils.cpp @@ -97,6 +97,26 @@ int64_t GetOccupiedSpaceForUid(const std::string& uuid, uid_t uid) { } } +int64_t GetOccupiedSpaceForProjectId(const std::string& uuid, int projectId) { + const std::string device = FindQuotaDeviceForUuid(uuid); + if (device == "") { + return -1; + } + struct dqblk dq; + if (quotactl(QCMD(Q_GETQUOTA, PRJQUOTA), device.c_str(), projectId, + reinterpret_cast<char*>(&dq)) != 0) { + if (errno != ESRCH) { + PLOG(ERROR) << "Failed to quotactl " << device << " for Project ID " << projectId; + } + return -1; + } else { +#if MEASURE_DEBUG + LOG(DEBUG) << "quotactl() for Project ID " << projectId << " " << dq.dqb_curspace; +#endif + return dq.dqb_curspace; + } +} + int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid) { const std::string device = FindQuotaDeviceForUuid(uuid); if (device == "") { diff --git a/cmds/installd/QuotaUtils.h b/cmds/installd/QuotaUtils.h index 9ad170fcbb..96aca0448e 100644 --- a/cmds/installd/QuotaUtils.h +++ b/cmds/installd/QuotaUtils.h @@ -35,6 +35,8 @@ int64_t GetOccupiedSpaceForUid(const std::string& uuid, uid_t uid); /* Get the current occupied space in bytes for a gid or -1 if fails */ int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid); +/* Get the current occupied space in bytes for a project id or -1 if fails */ +int64_t GetOccupiedSpaceForProjectId(const std::string& uuid, int projectId); } // namespace installd } // namespace android diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING index 287f2d9f41..c6583a1bf4 100644 --- a/cmds/installd/TEST_MAPPING +++ b/cmds/installd/TEST_MAPPING @@ -15,6 +15,10 @@ { "name": "installd_utils_test" }, + // AdoptableHostTest moves packages, part of which is handled by installd + { + "name": "AdoptableHostTest" + }, { "name": "CtsUsesLibraryHostTestCases" }, diff --git a/cmds/installd/art_helper/Android.bp b/cmds/installd/art_helper/Android.bp deleted file mode 100644 index c47dd722f9..0000000000 --- a/cmds/installd/art_helper/Android.bp +++ /dev/null @@ -1,12 +0,0 @@ -// Inherit image values. -art_global_defaults { - name: "libartimagevalues_defaults", -} - -cc_library_static { - name: "libartimagevalues", - defaults: ["libartimagevalues_defaults"], - srcs: ["art_image_values.cpp"], - export_include_dirs: ["."], - cflags: ["-Wconversion"], -} diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 26e9984f11..d99bcc8d13 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -127,4 +127,6 @@ interface IInstalld { const int FLAG_USE_QUOTA = 0x1000; const int FLAG_FORCE = 0x2000; + + const int FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES = 0x20000; } diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index dbb4f22372..70bbc33b42 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -299,9 +299,13 @@ const char* select_execution_binary( // Namespace for Android Runtime flags applied during boot time. static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot"; // Feature flag name for running the JIT in Zygote experiment, b/119800099. -static const char* ENABLE_APEX_IMAGE = "enable_apex_image"; -// Location of the apex image. -static const char* kApexImage = "/system/framework/apex.art"; +static const char* ENABLE_JITZYGOTE_IMAGE = "enable_apex_image"; +// Location of the JIT Zygote image. +static const char* kJitZygoteImage = + "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof"; + +// Phenotype property name for enabling profiling the boot class path. +static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath"; class RunDex2Oat : public ExecVHelper { public: @@ -336,6 +340,10 @@ class RunDex2Oat : public ExecVHelper { ? "dalvik.vm.dex2oat-threads" : "dalvik.vm.boot-dex2oat-threads"; std::string dex2oat_threads_arg = MapPropertyToArg(threads_property, "-j%s"); + const char* cpu_set_property = post_bootcomplete + ? "dalvik.vm.dex2oat-cpu-set" + : "dalvik.vm.boot-dex2oat-cpu-set"; + std::string dex2oat_cpu_set_arg = MapPropertyToArg(cpu_set_property, "--cpu-set=%s"); std::string bootclasspath; char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH"); @@ -398,14 +406,22 @@ class RunDex2Oat : public ExecVHelper { GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault); std::string boot_image; - std::string use_apex_image = + std::string use_jitzygote_image = server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, - ENABLE_APEX_IMAGE, + ENABLE_JITZYGOTE_IMAGE, /*default_value=*/ ""); - if (use_apex_image == "true") { - boot_image = StringPrintf("-Ximage:%s", kApexImage); + + std::string profile_boot_class_path = GetProperty("dalvik.vm.profilebootclasspath", ""); + profile_boot_class_path = + server_configurable_flags::GetServerConfigurableFlag( + RUNTIME_NATIVE_BOOT_NAMESPACE, + PROFILE_BOOT_CLASS_PATH, + /*default_value=*/ profile_boot_class_path); + + if (use_jitzygote_image == "true" || profile_boot_class_path == "true") { + boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage); } else { - boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s"); + boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s"); } // clang FORTIFY doesn't let us use strlen in constant array bounds, so we @@ -498,7 +514,8 @@ class RunDex2Oat : public ExecVHelper { AddArg(instruction_set_variant_arg); AddArg(instruction_set_features_arg); - AddRuntimeArg(boot_image); + AddArg(boot_image); + AddRuntimeArg(bootclasspath); AddRuntimeArg(dex2oat_Xms_arg); AddRuntimeArg(dex2oat_Xmx_arg); @@ -507,6 +524,7 @@ class RunDex2Oat : public ExecVHelper { AddArg(image_block_size_arg); AddArg(dex2oat_compiler_filter_arg); AddArg(dex2oat_threads_arg); + AddArg(dex2oat_cpu_set_arg); AddArg(dex2oat_swap_fd); AddArg(dex2oat_image_fd); @@ -697,11 +715,13 @@ static void open_profile_files(uid_t uid, const std::string& package_name, } } -static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 0; -static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 1; -static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 2; -static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3; -static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4; +static constexpr int PROFMAN_BIN_RETURN_CODE_SUCCESS = 0; +static constexpr int PROFMAN_BIN_RETURN_CODE_COMPILE = 1; +static constexpr int PROFMAN_BIN_RETURN_CODE_SKIP_COMPILATION = 2; +static constexpr int PROFMAN_BIN_RETURN_CODE_BAD_PROFILES = 3; +static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 4; +static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 5; +static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_DIFFERENT_VERSIONS = 6; class RunProfman : public ExecVHelper { public: @@ -710,7 +730,8 @@ class RunProfman : public ExecVHelper { const std::vector<unique_fd>& apk_fds, const std::vector<std::string>& dex_locations, bool copy_and_update, - bool store_aggregation_counters) { + bool for_snapshot, + bool for_boot_image) { // TODO(calin): Assume for now we run in the bg compile job (which is in // most of the invocation). With the current data flow, is not very easy or @@ -742,8 +763,12 @@ class RunProfman : public ExecVHelper { AddArg("--copy-and-update-profile-key"); } - if (store_aggregation_counters) { - AddArg("--store-aggregation-counters"); + if (for_snapshot) { + AddArg("--force-merge"); + } + + if (for_boot_image) { + AddArg("--boot-image-merge"); } // Do not add after dex2oat_flags, they should override others for debugging. @@ -754,13 +779,15 @@ class RunProfman : public ExecVHelper { const unique_fd& reference_profile_fd, const std::vector<unique_fd>& apk_fds = std::vector<unique_fd>(), const std::vector<std::string>& dex_locations = std::vector<std::string>(), - bool store_aggregation_counters = false) { + bool for_snapshot = false, + bool for_boot_image = false) { SetupArgs(profiles_fd, reference_profile_fd, apk_fds, dex_locations, - /*copy_and_update=*/false, - store_aggregation_counters); + /*copy_and_update=*/ false, + for_snapshot, + for_boot_image); } void SetupCopyAndUpdate(unique_fd&& profile_fd, @@ -778,7 +805,8 @@ class RunProfman : public ExecVHelper { apk_fds_, dex_locations, /*copy_and_update=*/true, - /*store_aggregation_counters=*/false); + /*for_snapshot*/false, + /*for_boot_image*/false); } void SetupDump(const std::vector<unique_fd>& profiles_fd, @@ -793,7 +821,8 @@ class RunProfman : public ExecVHelper { apk_fds, dex_locations, /*copy_and_update=*/false, - /*store_aggregation_counters=*/false); + /*for_snapshot*/false, + /*for_boot_image*/false); } void Exec() { @@ -868,9 +897,14 @@ static bool analyze_profiles(uid_t uid, const std::string& package_name, should_clear_current_profiles = false; should_clear_reference_profile = false; break; + case PROFMAN_BIN_RETURN_CODE_ERROR_DIFFERENT_VERSIONS: + need_to_compile = false; + should_clear_current_profiles = true; + should_clear_reference_profile = true; + break; default: // Unknown return code or error. Unlink profiles. - LOG(WARNING) << "Unknown error code while processing profiles for location " + LOG(WARNING) << "Unexpected error code while processing profiles for location " << location << ": " << return_code; need_to_compile = false; should_clear_current_profiles = true; @@ -2117,14 +2151,20 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins // Create a swap file if necessary. unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path); - // Create the app image file if needed. - Dex2oatFileWrapper image_fd = maybe_open_app_image( - out_oat_path, generate_app_image, is_public, uid, is_secondary_dex); - // Open the reference profile if needed. Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile( pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex); + if (reference_profile_fd.get() == -1) { + // We don't create an app image without reference profile since there is no speedup from + // loading it in that case and instead will be a small overhead. + generate_app_image = false; + } + + // Create the app image file if needed. + Dex2oatFileWrapper image_fd = maybe_open_app_image( + out_oat_path, generate_app_image, is_public, uid, is_secondary_dex); + unique_fd dex_metadata_fd; if (dex_metadata_path != nullptr) { dex_metadata_fd.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW))); @@ -2733,7 +2773,7 @@ static bool create_app_profile_snapshot(int32_t app_id, } RunProfman args; - args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations); + args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations, /*for_snapshot=*/true); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ @@ -2748,6 +2788,13 @@ static bool create_app_profile_snapshot(int32_t app_id, return false; } + // Verify that profman finished successfully. + int profman_code = WEXITSTATUS(return_code); + if (profman_code != PROFMAN_BIN_RETURN_CODE_SUCCESS) { + LOG(WARNING) << "profman error for " << package_name << ":" << profile_name + << ":" << profman_code; + return false; + } return true; } @@ -2810,20 +2857,29 @@ static bool create_boot_image_profile_snapshot(const std::string& package_name, // We do this to avoid opening a huge a amount of files. static constexpr size_t kAggregationBatchSize = 10; - std::vector<unique_fd> profiles_fd; for (size_t i = 0; i < profiles.size(); ) { + std::vector<unique_fd> profiles_fd; for (size_t k = 0; k < kAggregationBatchSize && i < profiles.size(); k++, i++) { unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY); if (fd.get() >= 0) { profiles_fd.push_back(std::move(fd)); } } + + // We aggregate (read & write) into the same fd multiple times in a row. + // We need to reset the cursor every time to ensure we read the whole file every time. + if (TEMP_FAILURE_RETRY(lseek(snapshot_fd, 0, SEEK_SET)) == static_cast<off_t>(-1)) { + PLOG(ERROR) << "Cannot reset position for snapshot profile"; + return false; + } + RunProfman args; args.SetupMerge(profiles_fd, snapshot_fd, apk_fds, dex_locations, - /*store_aggregation_counters=*/true); + /*for_snapshot=*/true, + /*for_boot_image=*/true); pid_t pid = fork(); if (pid == 0) { /* child -- drop privileges before continuing */ @@ -2836,12 +2892,21 @@ static bool create_boot_image_profile_snapshot(const std::string& package_name, /* parent */ int return_code = wait_child(pid); + if (!WIFEXITED(return_code)) { PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name; return false; } - return true; + + // Verify that profman finished successfully. + int profman_code = WEXITSTATUS(return_code); + if (profman_code != PROFMAN_BIN_RETURN_CODE_SUCCESS) { + LOG(WARNING) << "profman error for " << package_name << ":" << profile_name + << ":" << profman_code; + return false; + } } + return true; } diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index a8c48c564e..ef739bafd4 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -32,15 +32,15 @@ static constexpr int DEX2OAT_FROM_SCRATCH = 1; static constexpr int DEX2OAT_FOR_BOOT_IMAGE = 2; static constexpr int DEX2OAT_FOR_FILTER = 3; -#define ANDROID_RUNTIME_APEX_BIN "/apex/com.android.runtime/bin" +#define ANDROID_ART_APEX_BIN "/apex/com.android.art/bin" // Location of binaries in the Android Runtime APEX. -static constexpr const char* kDex2oatPath = ANDROID_RUNTIME_APEX_BIN "/dex2oat"; -static constexpr const char* kDex2oatDebugPath = ANDROID_RUNTIME_APEX_BIN "/dex2oatd"; -static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profman"; -static constexpr const char* kProfmanDebugPath = ANDROID_RUNTIME_APEX_BIN "/profmand"; -static constexpr const char* kDexoptanalyzerPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzer"; -static constexpr const char* kDexoptanalyzerDebugPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzerd"; -#undef ANDROID_RUNTIME_APEX_BIN +static constexpr const char* kDex2oatPath = ANDROID_ART_APEX_BIN "/dex2oat"; +static constexpr const char* kDex2oatDebugPath = ANDROID_ART_APEX_BIN "/dex2oatd"; +static constexpr const char* kProfmanPath = ANDROID_ART_APEX_BIN "/profman"; +static constexpr const char* kProfmanDebugPath = ANDROID_ART_APEX_BIN "/profmand"; +static constexpr const char* kDexoptanalyzerPath = ANDROID_ART_APEX_BIN "/dexoptanalyzer"; +static constexpr const char* kDexoptanalyzerDebugPath = ANDROID_ART_APEX_BIN "/dexoptanalyzerd"; +#undef ANDROID_ART_APEX_BIN // Clear the reference profile identified by the given profile name. bool clear_primary_reference_profile(const std::string& pkgname, const std::string& profile_name); diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp index 673ff0d513..b5bc28c1d3 100644 --- a/cmds/installd/installd.cpp +++ b/cmds/installd/installd.cpp @@ -74,7 +74,7 @@ static int initialize_directories() { // Read current filesystem layout version to handle upgrade paths char version_path[PATH_MAX]; - snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.c_str()); + snprintf(version_path, PATH_MAX, "%smisc/installd/layout_version", android_data_dir.c_str()); int oldVersion; if (fs_read_atomic_int(version_path, &oldVersion) == -1) { diff --git a/cmds/installd/migrate_legacy_obb_data.sh b/cmds/installd/migrate_legacy_obb_data.sh index 10756881be..7399681c3e 100644 --- a/cmds/installd/migrate_legacy_obb_data.sh +++ b/cmds/installd/migrate_legacy_obb_data.sh @@ -15,17 +15,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -rm -rf /sdcard/Android/obb/test_probe -mkdir -p /sdcard/Android/obb/ -touch /sdcard/Android/obb/test_probe +rm -rf /data/media/0/Android/obb/test_probe +mkdir -p /data/media/0/Android/obb/ +touch /data/media/0/Android/obb/test_probe if ! test -f /data/media/0/Android/obb/test_probe ; then log -p i -t migrate_legacy_obb_data "No support for 'unshared_obb'. Not migrating" - rm -rf /sdcard/Android/obb/test_probe + rm -rf /data/media/0/Android/obb/test_probe exit 0 fi # Delete the test file, and remove the obb folder if it is empty -rm -rf /sdcard/Android/obb/test_probe +rm -rf /data/media/0/Android/obb/test_probe rmdir /data/media/obb if ! test -d /data/media/obb ; then diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index de7b2499b8..eefbe4ffe2 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -445,9 +445,11 @@ private: } cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str())); - int32_t base_offset = ChooseRelocationOffsetDelta(art::GetImageMinBaseAddressDelta(), - art::GetImageMaxBaseAddressDelta()); - cmd.push_back(StringPrintf("--base=0x%x", art::GetImageBaseAddress() + base_offset)); + int32_t base_offset = ChooseRelocationOffsetDelta( + art::imagevalues::GetImageMinBaseAddressDelta(), + art::imagevalues::GetImageMaxBaseAddressDelta()); + cmd.push_back(StringPrintf("--base=0x%x", + art::imagevalues::GetImageBaseAddress() + base_offset)); cmd.push_back(StringPrintf("--instruction-set=%s", isa)); @@ -464,7 +466,7 @@ private: "--compiler-filter=", false, cmd); - cmd.push_back("--image-classes=/system/etc/preloaded-classes"); + cmd.push_back("--profile-file=/system/etc/boot-image.prof"); // TODO: Compiled-classes. const std::string* extra_opts = system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags"); @@ -478,6 +480,10 @@ private: "-j", false, cmd); + AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-cpu-set", + "--cpu-set=", + false, + cmd); AddCompilerOptionFromSystemProperty( StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(), "--instruction-set-variant=", diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index 2e2cc182ec..6459805ba3 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -61,19 +61,25 @@ static void CloseDescriptor(const char* descriptor_string) { static std::vector<apex::ApexFile> ActivateApexPackages() { // The logic here is (partially) copied and adapted from - // system/apex/apexd/apexd_main.cpp. + // system/apex/apexd/apexd.cpp. // - // Only scan the APEX directory under /system (within the chroot dir). - apex::scanPackagesDirAndActivate(apex::kApexPackageSystemDir); + // Only scan the APEX directory under /system, /system_ext and /vendor (within the chroot dir). + std::vector<const char*> apex_dirs{apex::kApexPackageSystemDir, apex::kApexPackageSystemExtDir, + apex::kApexPackageVendorDir}; + for (const auto& dir : apex_dirs) { + // Cast call to void to suppress warn_unused_result. + static_cast<void>(apex::scanPackagesDirAndActivate(dir)); + } return apex::getActivePackages(); } static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_packages) { for (const apex::ApexFile& apex_file : active_packages) { const std::string& package_path = apex_file.GetPath(); - apex::Status status = apex::deactivatePackage(package_path); - if (!status.Ok()) { - LOG(ERROR) << "Failed to deactivate " << package_path << ": " << status.ErrorMessage(); + base::Result<void> status = apex::deactivatePackage(package_path); + if (!status.ok()) { + LOG(ERROR) << "Failed to deactivate " << package_path << ": " + << status.error(); } } } @@ -231,9 +237,21 @@ static int otapreopt_chroot(const int argc, char **arg) { } // Try to mount APEX packages in "/apex" in the chroot dir. We need at least - // the Android Runtime APEX, as it is required by otapreopt to run dex2oat. + // the ART APEX, as it is required by otapreopt to run dex2oat. std::vector<apex::ApexFile> active_packages = ActivateApexPackages(); + // Check that an ART APEX has been activated; clean up and exit + // early otherwise. + if (std::none_of(active_packages.begin(), + active_packages.end(), + [](const apex::ApexFile& package){ + return package.GetManifest().name() == "com.android.art"; + })) { + LOG(FATAL_WITHOUT_ABORT) << "No activated com.android.art APEX package."; + DeactivateApexPackages(active_packages); + exit(217); + } + // Now go on and run otapreopt. // Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index aa79fdc100..bd45005fd1 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -89,6 +89,8 @@ cc_test { "libinstalld", "liblog", "liblogwrap", + "libziparchive", + "libz", ], test_config: "installd_dexopt_test.xml", } diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp index db0907017c..5a5cb53431 100644 --- a/cmds/installd/tests/installd_cache_test.cpp +++ b/cmds/installd/tests/installd_cache_test.cpp @@ -67,29 +67,29 @@ bool create_cache_path(char path[PKG_PATH_MAX] ATTRIBUTE_UNUSED, } static void mkdir(const char* path) { - const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); - ::mkdir(fullPath, 0755); + const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path); + ::mkdir(fullPath.c_str(), 0755); } static void touch(const char* path, int len, int time) { - const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); - int fd = ::open(fullPath, O_RDWR | O_CREAT, 0644); + const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path); + int fd = ::open(fullPath.c_str(), O_RDWR | O_CREAT, 0644); ::fallocate(fd, 0, 0, len); ::close(fd); struct utimbuf times; times.actime = times.modtime = std::time(0) + time; - ::utime(fullPath, ×); + ::utime(fullPath.c_str(), ×); } static int exists(const char* path) { - const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); - return ::access(fullPath, F_OK); + const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path); + return ::access(fullPath.c_str(), F_OK); } static int64_t size(const char* path) { - const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); + const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path); struct stat buf; - if (!stat(fullPath, &buf)) { + if (!stat(fullPath.c_str(), &buf)) { return buf.st_size; } else { return -1; @@ -107,8 +107,8 @@ static int64_t free() { } static void setxattr(const char* path, const char* key) { - const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str(); - ::setxattr(fullPath, key, "", 0, 0); + const std::string fullPath = StringPrintf("/data/local/tmp/user/0/%s", path); + ::setxattr(fullPath.c_str(), key, "", 0, 0); } class CacheTest : public testing::Test { diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index fa2b0d9660..69fefa199b 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -41,6 +41,7 @@ #include "globals.h" #include "tests/test_utils.h" #include "utils.h" +#include "ziparchive/zip_writer.h" using android::base::ReadFully; using android::base::unique_fd; @@ -195,6 +196,7 @@ protected: std::unique_ptr<std::string> volume_uuid_; std::string package_name_; std::string apk_path_; + std::string empty_dm_file_; std::string app_apk_dir_; std::string app_private_dir_ce_; std::string app_private_dir_de_; @@ -239,18 +241,14 @@ protected: } ::testing::AssertionResult create_mock_app() { - // Create the oat dir. - app_oat_dir_ = app_apk_dir_ + "/oat"; // For debug mode, the directory might already exist. Avoid erroring out. if (mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755) != 0 && !kDebug) { return ::testing::AssertionFailure() << "Could not create app dir " << app_apk_dir_ << " : " << strerror(errno); } - binder::Status status = service_->createOatDir(app_oat_dir_, kRuntimeIsa); - if (!status.isOk()) { - return ::testing::AssertionFailure() << "Could not create oat dir: " - << status.toString8().c_str(); - } + + // Initialize the oat dir path. + app_oat_dir_ = app_apk_dir_ + "/oat"; // Copy the primary apk. apk_path_ = app_apk_dir_ + "/base.jar"; @@ -260,8 +258,28 @@ protected: << " : " << error_msg; } + // Create an empty dm file. + empty_dm_file_ = apk_path_ + ".dm"; + { + int fd = open(empty_dm_file_.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); + if (fd < 0) { + return ::testing::AssertionFailure() << "Could not open " << empty_dm_file_; + } + FILE* file = fdopen(fd, "wb"); + if (file == nullptr) { + return ::testing::AssertionFailure() << "Null file for " << empty_dm_file_ + << " fd=" << fd; + } + ZipWriter writer(file); + // Add vdex to zip. + writer.StartEntry("primary.prof", ZipWriter::kCompress); + writer.FinishEntry(); + writer.Finish(); + fclose(file); + } + // Create the app user data. - status = service_->createAppData( + binder::Status status = service_->createAppData( volume_uuid_, package_name_, kTestUserId, @@ -479,7 +497,7 @@ protected: bool prof_result; ASSERT_BINDER_SUCCESS(service_->prepareAppProfile( package_name_, kTestUserId, kTestAppId, *profile_name_ptr, apk_path_, - /*dex_metadata*/ nullptr, &prof_result)); + dm_path_ptr, &prof_result)); ASSERT_TRUE(prof_result); binder::Status result = service_->dexopt(apk_path_, @@ -625,6 +643,16 @@ TEST_F(DexoptTest, DexoptPrimaryPublic) { DEX2OAT_FROM_SCRATCH); } +TEST_F(DexoptTest, DexoptPrimaryPublicCreateOatDir) { + LOG(INFO) << "DexoptPrimaryPublic"; + ASSERT_BINDER_SUCCESS(service_->createOatDir(app_oat_dir_, kRuntimeIsa)); + CompilePrimaryDexOk("verify", + DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC, + app_oat_dir_.c_str(), + kTestAppGid, + DEX2OAT_FROM_SCRATCH); +} + TEST_F(DexoptTest, DexoptPrimaryFailedInvalidFilter) { LOG(INFO) << "DexoptPrimaryFailedInvalidFilter"; binder::Status status; @@ -645,7 +673,9 @@ TEST_F(DexoptTest, DexoptPrimaryProfileNonPublic) { DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE, app_oat_dir_.c_str(), kTestAppGid, - DEX2OAT_FROM_SCRATCH); + DEX2OAT_FROM_SCRATCH, + /*binder_result=*/nullptr, + empty_dm_file_.c_str()); } TEST_F(DexoptTest, DexoptPrimaryProfilePublic) { @@ -655,7 +685,9 @@ TEST_F(DexoptTest, DexoptPrimaryProfilePublic) { DEXOPT_GENERATE_APP_IMAGE, app_oat_dir_.c_str(), kTestAppGid, - DEX2OAT_FROM_SCRATCH); + DEX2OAT_FROM_SCRATCH, + /*binder_result=*/nullptr, + empty_dm_file_.c_str()); } TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) { @@ -665,7 +697,9 @@ TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) { DEXOPT_GENERATE_APP_IMAGE, app_oat_dir_.c_str(), kTestAppGid, - DEX2OAT_FROM_SCRATCH); + DEX2OAT_FROM_SCRATCH, + /*binder_result=*/nullptr, + empty_dm_file_.c_str()); } TEST_F(DexoptTest, ResolveStartupConstStrings) { @@ -684,7 +718,9 @@ TEST_F(DexoptTest, ResolveStartupConstStrings) { DEXOPT_GENERATE_APP_IMAGE, app_oat_dir_.c_str(), kTestAppGid, - DEX2OAT_FROM_SCRATCH); + DEX2OAT_FROM_SCRATCH, + /*binder_result=*/nullptr, + empty_dm_file_.c_str()); run_cmd_and_process_output( "oatdump --header-only --oat-file=" + odex, [&](const std::string& line) { @@ -701,7 +737,9 @@ TEST_F(DexoptTest, ResolveStartupConstStrings) { DEXOPT_GENERATE_APP_IMAGE, app_oat_dir_.c_str(), kTestAppGid, - DEX2OAT_FROM_SCRATCH); + DEX2OAT_FROM_SCRATCH, + /*binder_result=*/nullptr, + empty_dm_file_.c_str()); run_cmd_and_process_output( "oatdump --header-only --oat-file=" + odex, [&](const std::string& line) { @@ -859,7 +897,9 @@ class ProfileTest : public DexoptTest { std::string expected_profile_content = snap_profile_ + ".expected"; run_cmd("rm -f " + expected_profile_content); run_cmd("touch " + expected_profile_content); - run_cmd("profman --profile-file=" + cur_profile_ + + // We force merging when creating the expected profile to make sure + // that the random profiles do not affect the output. + run_cmd("profman --force-merge --profile-file=" + cur_profile_ + " --profile-file=" + ref_profile_ + " --reference-profile-file=" + expected_profile_content + " --apk=" + apk_path_); @@ -1092,16 +1132,60 @@ TEST_F(ProfileTest, ProfilePrepareFailProfileChangedUid) { class BootProfileTest : public ProfileTest { public: - virtual void setup() { + std::vector<const std::string> extra_apps_; + std::vector<int64_t> extra_ce_data_inodes_; + + virtual void SetUp() { + ProfileTest::SetUp(); intial_android_profiles_dir = android_profiles_dir; + // Generate profiles for some extra apps. + // When merging boot profile we split profiles into small groups to avoid + // opening a lot of file descriptors at the same time. + // (Currently the group size for aggregation is 10) + // + // To stress test that works fine, create profile for more apps. + createAppProfilesForBootMerge(21); } virtual void TearDown() { android_profiles_dir = intial_android_profiles_dir; + deleteAppProfilesForBootMerge(); ProfileTest::TearDown(); } + void createAppProfilesForBootMerge(size_t number_of_profiles) { + for (size_t i = 0; i < number_of_profiles; i++) { + int64_t ce_data_inode; + std::string package_name = "dummy_test_pkg" + std::to_string(i); + LOG(INFO) << package_name; + ASSERT_BINDER_SUCCESS(service_->createAppData( + volume_uuid_, + package_name, + kTestUserId, + kAppDataFlags, + kTestAppUid, + se_info_, + kOSdkVersion, + &ce_data_inode)); + extra_apps_.push_back(package_name); + extra_ce_data_inodes_.push_back(ce_data_inode); + std::string profile = create_current_profile_path( + kTestUserId, package_name, kPrimaryProfile, /*is_secondary_dex*/ false); + SetupProfile(profile, kTestAppUid, kTestAppGid, 0600, 1); + } + } + + void deleteAppProfilesForBootMerge() { + if (kDebug) { + return; + } + for (size_t i = 0; i < extra_apps_.size(); i++) { + service_->destroyAppData( + volume_uuid_, extra_apps_[i], kTestUserId, kAppDataFlags, extra_ce_data_inodes_[i]); + } + } + void UpdateAndroidProfilesDir(const std::string& profile_dir) { android_profiles_dir = profile_dir; // We need to create the reference profile directory in the new profile dir. diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index e61eb6e52f..d236f76645 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -104,12 +104,12 @@ TEST_F(UtilsTest, IsValidApkPath_Internal) { EXPECT_EQ(-1, validate_apk_path(badint2)) << badint2 << " should be rejected as a invalid path"; - // Only one subdir should be allowed. - const char *bad_path3 = TEST_APP_DIR "example.com/subdir/pkg.apk"; + // Should not have more than two sub directories + const char *bad_path3 = TEST_APP_DIR "random/example.com/subdir/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path3)) << bad_path3 << " should be rejected as a invalid path"; - const char *bad_path4 = TEST_APP_DIR "example.com/subdir/../pkg.apk"; + const char *bad_path4 = TEST_APP_DIR "random/example.com/subdir/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path4)) << bad_path4 << " should be rejected as a invalid path"; @@ -120,6 +120,7 @@ TEST_F(UtilsTest, IsValidApkPath_Internal) { TEST_F(UtilsTest, IsValidApkPath_TopDir) { EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example")); + EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example")); EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example")); EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example")); EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example")); @@ -127,6 +128,7 @@ TEST_F(UtilsTest, IsValidApkPath_TopDir) { TEST_F(UtilsTest, IsValidApkPath_TopFile) { EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example/base.apk")); + EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example/base.apk")); EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example/base.apk")); EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example/base.apk")); EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example/base.apk")); @@ -134,6 +136,7 @@ TEST_F(UtilsTest, IsValidApkPath_TopFile) { TEST_F(UtilsTest, IsValidApkPath_OatDir) { EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat")); EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat")); @@ -141,6 +144,7 @@ TEST_F(UtilsTest, IsValidApkPath_OatDir) { TEST_F(UtilsTest, IsValidApkPath_OatDirDir) { EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64")); EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64")); @@ -148,6 +152,7 @@ TEST_F(UtilsTest, IsValidApkPath_OatDirDir) { TEST_F(UtilsTest, IsValidApkPath_OatDirDirFile) { EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64/base.odex")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64/base.odex")); EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64/base.odex")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64/base.odex")); EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64/base.odex")); @@ -164,6 +169,10 @@ TEST_F(UtilsTest, IsValidApkPath_Private) { EXPECT_EQ(0, validate_apk_path(path2)) << path2 << " should be allowed as a valid path"; + const char *path3 = TEST_APP_DIR "random/example.com/example.apk"; + EXPECT_EQ(0, validate_apk_path(path3)) + << path3 << " should be allowed as a valid path"; + const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk"; EXPECT_EQ(-1, validate_apk_path(badpriv1)) << badpriv1 << " should be rejected as a invalid path"; @@ -172,16 +181,16 @@ TEST_F(UtilsTest, IsValidApkPath_Private) { EXPECT_EQ(-1, validate_apk_path(badpriv2)) << badpriv2 << " should be rejected as a invalid path"; - // Only one subdir should be allowed. - const char *bad_path3 = TEST_APP_PRIVATE_DIR "example.com/subdir/pkg.apk"; + // Only one or two subdir should be allowed. + const char *bad_path3 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path3)) << bad_path3 << " should be rejected as a invalid path"; - const char *bad_path4 = TEST_APP_PRIVATE_DIR "example.com/subdir/../pkg.apk"; + const char *bad_path4 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/../pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path4)) << bad_path4 << " should be rejected as a invalid path"; - const char *bad_path5 = TEST_APP_PRIVATE_DIR "example.com1/../example.com2/pkg.apk"; + const char *bad_path5 = TEST_APP_PRIVATE_DIR "random/example.com1/../example.com2/pkg.apk"; EXPECT_EQ(-1, validate_apk_path(bad_path5)) << bad_path5 << " should be rejected as a invalid path"; } @@ -229,10 +238,16 @@ TEST_F(UtilsTest, IsValidApkPath_SubdirEscapeSingleFail) { << badasec6 << " should be rejected as a invalid path"; } -TEST_F(UtilsTest, IsValidApkPath_TwoSubdirFail) { - const char *badasec7 = TEST_ASEC_DIR "com.example.asec/subdir1/pkg.apk"; - EXPECT_EQ(-1, validate_apk_path(badasec7)) - << badasec7 << " should be rejected as a invalid path"; +TEST_F(UtilsTest, IsValidApkPath_TwoSubdir) { + const char *badasec7 = TEST_ASEC_DIR "random/com.example.asec/pkg.apk"; + EXPECT_EQ(0, validate_apk_path(badasec7)) + << badasec7 << " should be allowed as a valid path"; +} + +TEST_F(UtilsTest, IsValidApkPath_ThreeSubdirFail) { + const char *badasec8 = TEST_ASEC_DIR "random/com.example.asec/subdir/pkg.apk"; + EXPECT_EQ(-1, validate_apk_path(badasec8)) + << badasec8 << " should be rejcted as an invalid path"; } TEST_F(UtilsTest, CheckSystemApp_Dir1) { @@ -511,8 +526,8 @@ TEST_F(UtilsTest, ValidateApkPath) { EXPECT_EQ(0, validate_apk_path("/data/app/com.example")); EXPECT_EQ(0, validate_apk_path("/data/app/com.example/file")); EXPECT_EQ(0, validate_apk_path("/data/app/com.example//file")); - EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/")); - EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/file")); + EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/")); + EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/file")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/file")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir//file")); EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/dir/file")); @@ -527,8 +542,10 @@ TEST_F(UtilsTest, ValidateApkPathSubdirs) { EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/file")); EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/file")); EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir//file")); - EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file")); - EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file")); + EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file")); + EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file")); + EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir/file")); + EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir//file")); } TEST_F(UtilsTest, MatchExtension_Valid) { diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 4eb1df0b2e..2f79552d1c 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -945,11 +945,11 @@ static int validate_apk_path_internal(const std::string& path, int maxSubdirs) { } int validate_apk_path(const char* path) { - return validate_apk_path_internal(path, 1 /* maxSubdirs */); + return validate_apk_path_internal(path, 2 /* maxSubdirs */); } int validate_apk_path_subdirs(const char* path) { - return validate_apk_path_internal(path, 3 /* maxSubdirs */); + return validate_apk_path_internal(path, 4 /* maxSubdirs */); } int ensure_config_user_dirs(userid_t userid) { diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp index 93d878b607..5afae4b7d3 100644 --- a/cmds/lshal/Android.bp +++ b/cmds/lshal/Android.bp @@ -12,14 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -cc_library_shared { +cc_library_static { name: "liblshal", shared_libs: [ "libbase", "libcutils", "libutils", "libhidlbase", - "libhidltransport", "libhidl-gen-hash", "libhidl-gen-utils", "libvintf", @@ -36,6 +35,7 @@ cc_library_shared { "TableEntry.cpp", "TextTable.cpp", "utils.cpp", + "WaitCommand.cpp", ], cflags: [ "-Wall", @@ -47,13 +47,15 @@ cc_defaults { name: "lshal_defaults", shared_libs: [ "libbase", + "libcutils", + "libutils", "libhidlbase", + "libhidl-gen-hash", "libhidl-gen-utils", - "libhidltransport", - "liblshal", - "libutils", + "libvintf", ], static_libs: [ + "liblshal", "libprocpartition", ], cflags: ["-Wall", "-Werror"], @@ -69,14 +71,16 @@ cc_binary { cc_test { name: "lshal_test", + test_suites: ["device-tests"], defaults: ["lshal_defaults"], gtest: true, static_libs: [ - "libgmock" + "android.hardware.tests.baz@1.0", + "libgmock", ], shared_libs: [ + "libhidlbase", "libvintf", - "android.hardware.tests.baz@1.0" ], srcs: [ "test.cpp" diff --git a/cmds/lshal/Command.h b/cmds/lshal/Command.h index e19e3f7fc2..84809d9a5d 100644 --- a/cmds/lshal/Command.h +++ b/cmds/lshal/Command.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_COMMAND_H_ -#define FRAMEWORK_NATIVE_CMDS_LSHAL_COMMAND_H_ +#pragma once #include "utils.h" @@ -48,5 +47,3 @@ protected: } // namespace lshal } // namespace android - -#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp index 0952db6e72..af22ac9b3d 100644 --- a/cmds/lshal/DebugCommand.cpp +++ b/cmds/lshal/DebugCommand.cpp @@ -79,7 +79,7 @@ void DebugCommand::usage() const { " lshal debug [-E] <interface> [options [options [...]]] \n" " Print debug information of a specified interface.\n" " -E: excludes debug output if HAL is actually a subclass.\n" - " <inteface>: Format is `android.hardware.foo@1.0::IFoo/default`.\n" + " <interface>: Format is `android.hardware.foo@1.0::IFoo/default`.\n" " If instance name is missing `default` is used.\n" " options: space separated options to IBase::debug.\n"; @@ -88,4 +88,3 @@ void DebugCommand::usage() const { } // namespace lshal } // namespace android - diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h index 3c3f56fde5..cd57e31bfc 100644 --- a/cmds/lshal/DebugCommand.h +++ b/cmds/lshal/DebugCommand.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_ -#define FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_ +#pragma once #include <string> @@ -53,5 +52,3 @@ private: } // namespace lshal } // namespace android - -#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_DEBUG_COMMAND_H_ diff --git a/cmds/lshal/HelpCommand.h b/cmds/lshal/HelpCommand.h index da0cba6f42..bfa850075d 100644 --- a/cmds/lshal/HelpCommand.h +++ b/cmds/lshal/HelpCommand.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_ -#define FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_ +#pragma once #include <string> @@ -44,5 +43,3 @@ public: } // namespace lshal } // namespace android - -#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_HELP_COMMAND_H_ diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index c706d911ec..a7ccf64c50 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -163,11 +163,11 @@ template <typename ObjectType> VintfInfo getVintfInfo(const std::shared_ptr<const ObjectType>& object, const FqInstance& fqInstance, vintf::TransportArch ta, VintfInfo value) { bool found = false; - (void)object->forEachInstanceOfVersion(fqInstance.getPackage(), fqInstance.getVersion(), - [&](const auto& instance) { - found = match(instance, fqInstance, ta); - return !found; // continue if not found - }); + (void)object->forEachHidlInstanceOfVersion(fqInstance.getPackage(), fqInstance.getVersion(), + [&](const auto& instance) { + found = match(instance, fqInstance, ta); + return !found; // continue if not found + }); return found ? value : VINTF_INFO_EMPTY; } @@ -453,7 +453,7 @@ bool ListCommand::addEntryWithoutInstance(const TableEntry& entry, } bool found = false; - (void)manifest->forEachInstanceOfVersion(package, version, [&found](const auto&) { + (void)manifest->forEachHidlInstanceOfVersion(package, version, [&found](const auto&) { found = true; return false; // break }); @@ -797,9 +797,9 @@ Status ListCommand::fetchManifestHals() { std::map<std::string, TableEntry> entries; - manifest->forEachInstance([&] (const vintf::ManifestInstance& manifestInstance) { + manifest->forEachHidlInstance([&] (const vintf::ManifestInstance& manifestInstance) { TableEntry entry{ - .interfaceName = manifestInstance.getFqInstance().string(), + .interfaceName = manifestInstance.description(), .transport = manifestInstance.transport(), .arch = manifestInstance.arch(), // TODO(b/71555570): Device manifest does not distinguish HALs from vendor or ODM. @@ -975,7 +975,8 @@ void ListCommand::registerAllOptions() { " - DM: if the HAL is in the device manifest\n" " - DC: if the HAL is in the device compatibility matrix\n" " - FM: if the HAL is in the framework manifest\n" - " - FC: if the HAL is in the framework compatibility matrix"}); + " - FC: if the HAL is in the framework compatibility matrix\n" + " - X: if the HAL is in none of the above lists"}); mOptions.push_back({'S', "service-status", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::SERVICE_STATUS); return OK; diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h index 85195fcc54..b3ed23d1fc 100644 --- a/cmds/lshal/ListCommand.h +++ b/cmds/lshal/ListCommand.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ -#define FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ +#pragma once #include <getopt.h> #include <stdint.h> @@ -206,5 +205,3 @@ private: } // namespace lshal } // namespace android - -#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LIST_COMMAND_H_ diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp index 8c83457d3b..132b31ebc3 100644 --- a/cmds/lshal/Lshal.cpp +++ b/cmds/lshal/Lshal.cpp @@ -26,7 +26,9 @@ #include <hidl/HidlTransportUtils.h> #include "DebugCommand.h" +#include "HelpCommand.h" #include "ListCommand.h" +#include "WaitCommand.h" #include "PipeRelay.h" namespace android { @@ -49,6 +51,7 @@ Lshal::Lshal(std::ostream &out, std::ostream &err, mRegisteredCommands.push_back({std::make_unique<ListCommand>(*this)}); mRegisteredCommands.push_back({std::make_unique<DebugCommand>(*this)}); mRegisteredCommands.push_back({std::make_unique<HelpCommand>(*this)}); + mRegisteredCommands.push_back({std::make_unique<WaitCommand>(*this)}); } void Lshal::forEachCommand(const std::function<void(const Command* c)>& f) const { diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h index 9457f1e563..830bd872ff 100644 --- a/cmds/lshal/Lshal.h +++ b/cmds/lshal/Lshal.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_ -#define FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_ +#pragma once #include <iostream> #include <string> @@ -25,7 +24,6 @@ #include <utils/StrongPointer.h> #include "Command.h" -#include "HelpCommand.h" #include "NullableOStream.h" #include "utils.h" @@ -76,5 +74,3 @@ private: } // namespace lshal } // namespace android - -#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_ diff --git a/cmds/lshal/NullableOStream.h b/cmds/lshal/NullableOStream.h index 737d3a2963..7cffcf8193 100644 --- a/cmds/lshal/NullableOStream.h +++ b/cmds/lshal/NullableOStream.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_ -#define FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_ +#pragma once #include <iostream> @@ -69,5 +68,3 @@ private: } // namespace lshal } // namespace android - -#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_ diff --git a/cmds/lshal/PipeRelay.h b/cmds/lshal/PipeRelay.h index 8dc3093742..8350160419 100644 --- a/cmds/lshal/PipeRelay.h +++ b/cmds/lshal/PipeRelay.h @@ -14,9 +14,7 @@ * limitations under the License. */ -#ifndef FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_ - -#define FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_ +#pragma once #include <android-base/macros.h> #include <ostream> @@ -53,6 +51,3 @@ private: } // namespace lshal } // namespace android - -#endif // FRAMEWORKS_NATIVE_CMDS_LSHAL_PIPE_RELAY_H_ - diff --git a/cmds/lshal/TEST_MAPPING b/cmds/lshal/TEST_MAPPING new file mode 100644 index 0000000000..0320624699 --- /dev/null +++ b/cmds/lshal/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + "presubmit": [ + { + "name": "lshal_test" + } + ] +} + diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h index 601b7e25f9..0ff0c96d38 100644 --- a/cmds/lshal/TableEntry.h +++ b/cmds/lshal/TableEntry.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_ -#define FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_ +#pragma once #include <stdint.h> @@ -157,5 +156,3 @@ private: } // namespace lshal } // namespace android - -#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_ diff --git a/cmds/lshal/TextTable.h b/cmds/lshal/TextTable.h index 301b4bd969..be41a08251 100644 --- a/cmds/lshal/TextTable.h +++ b/cmds/lshal/TextTable.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ -#define FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ +#pragma once #include <iostream> #include <string> @@ -80,5 +79,3 @@ private: } // namespace lshal } // namespace android - -#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h index 46d817759d..e8d22d9b58 100644 --- a/cmds/lshal/Timeout.h +++ b/cmds/lshal/Timeout.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + #include <condition_variable> #include <chrono> #include <functional> diff --git a/cmds/lshal/WaitCommand.cpp b/cmds/lshal/WaitCommand.cpp new file mode 100644 index 0000000000..65b41b95d2 --- /dev/null +++ b/cmds/lshal/WaitCommand.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "WaitCommand.h" + +#include "Lshal.h" + +#include <hidl/ServiceManagement.h> +#include <hidl-util/FQName.h> + +namespace android { +namespace lshal { + +std::string WaitCommand::getName() const { + return "wait"; +} + +std::string WaitCommand::getSimpleDescription() const { + return "Wait for HAL to start if it is not already started."; +} + +Status WaitCommand::parseArgs(const Arg &arg) { + if (optind + 1 != arg.argc) { + return USAGE; + } + + mInterfaceName = arg.argv[optind]; + ++optind; + return OK; +} + +Status WaitCommand::main(const Arg &arg) { + Status status = parseArgs(arg); + if (status != OK) { + return status; + } + + auto [interface, instance] = splitFirst(mInterfaceName, '/'); + instance = instance.empty() ? "default" : instance; + + FQName fqName; + if (!FQName::parse(interface, &fqName) || fqName.isIdentifier() || !fqName.isFullyQualified()) { + mLshal.err() << "Invalid fully-qualified name '" << interface << "'\n\n"; + return USAGE; + } + + using android::hidl::manager::V1_0::IServiceManager; + + using android::hardware::details::getRawServiceInternal; + auto service = getRawServiceInternal(interface, instance, true /*retry*/, false /*getStub*/); + + if (service == nullptr) { + mLshal.err() << "Service not found (missing permissions or not in VINTF manifest?).\n"; + return NO_INTERFACE; + } + + return OK; +} + +void WaitCommand::usage() const { + static const std::string debug = + "wait:\n" + " lshal wait <interface/instance> \n" + " For a HAL that is on the device, wait for the HAL to start.\n" + " This will not start a HAL unless it is configured as a lazy HAL.\n" + " <interface>: Format is `android.hardware.foo@1.0::IFoo/default`.\n" + " If instance name is missing `default` is used.\n"; + + mLshal.err() << debug; +} + +} // namespace lshal +} // namespace android + diff --git a/cmds/lshal/WaitCommand.h b/cmds/lshal/WaitCommand.h new file mode 100644 index 0000000000..c9f67c2b27 --- /dev/null +++ b/cmds/lshal/WaitCommand.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> + +#include <android-base/macros.h> + +#include "Command.h" +#include "utils.h" + +namespace android { +namespace lshal { + +class Lshal; + +class WaitCommand : public Command { +public: + explicit WaitCommand(Lshal &lshal) : Command(lshal) {} + ~WaitCommand() = default; + Status main(const Arg &arg) override; + void usage() const override; + std::string getSimpleDescription() const override; + std::string getName() const override; +private: + Status parseArgs(const Arg &arg); + + std::string mInterfaceName; + + DISALLOW_COPY_AND_ASSIGN(WaitCommand); +}; + + +} // namespace lshal +} // namespace android diff --git a/cmds/lshal/libprocpartition/include/procpartition/procpartition.h b/cmds/lshal/libprocpartition/include/procpartition/procpartition.h index 7e864327af..ca1e690694 100644 --- a/cmds/lshal/libprocpartition/include/procpartition/procpartition.h +++ b/cmds/lshal/libprocpartition/include/procpartition/procpartition.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_PROCPARTITION_H_ -#define FRAMEWORK_NATIVE_CMDS_LSHAL_PROCPARTITION_H_ +#pragma once #include <sys/types.h> @@ -44,5 +43,3 @@ Partition getPartition(pid_t pid); } // namespace procpartition } // namespace android - -#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_PROCPARTITION_H_ diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index fc8d58b3d8..3d550babf4 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp @@ -452,7 +452,7 @@ TEST_F(ListTest, Fetch) { } TEST_F(ListTest, DumpVintf) { - const std::string expected = "<manifest version=\"1.0\" type=\"device\">\n" + const std::string expected = "<manifest version=\"2.0\" type=\"device\">\n" " <hal format=\"hidl\">\n" " <name>a.h.foo1</name>\n" " <transport>hwbinder</transport>\n" @@ -493,19 +493,19 @@ TEST_F(ListTest, DumpVintf) { TEST_F(ListTest, DumpDefault) { const std::string expected = "[fake description 0]\n" - "R Interface Thread Use Server Clients\n" - "N a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n" - "Y a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n" + "VINTF R Interface Thread Use Server Clients\n" + "X N a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n" + "X Y a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n" "\n" "[fake description 1]\n" - "R Interface Thread Use Server Clients\n" - "? a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n" - "? a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n" + "VINTF R Interface Thread Use Server Clients\n" + "X ? a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n" + "X ? a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n" "\n" "[fake description 2]\n" - "R Interface Thread Use Server Clients\n" - "? a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n" - "? a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n" + "VINTF R Interface Thread Use Server Clients\n" + "X ? a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n" + "X ? a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n" "\n"; optind = 1; // mimic Lshal::parseArg() diff --git a/cmds/lshal/utils.h b/cmds/lshal/utils.h index 240155e4d0..04f52726e3 100644 --- a/cmds/lshal/utils.h +++ b/cmds/lshal/utils.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_ -#define FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_ +#pragma once #include <iomanip> #include <iostream> @@ -88,5 +87,3 @@ void replaceAll(std::string *s, char from, char to); } // namespace lshal } // namespace android - -#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_UTILS_H_ diff --git a/cmds/service/Android.bp b/cmds/service/Android.bp index 9513ec1c23..a5b1ac5c5f 100644 --- a/cmds/service/Android.bp +++ b/cmds/service/Android.bp @@ -4,6 +4,7 @@ cc_binary { srcs: ["service.cpp"], shared_libs: [ + "libcutils", "libutils", "libbinder", ], @@ -22,6 +23,7 @@ cc_binary { srcs: ["service.cpp"], shared_libs: [ + "libcutils", "libutils", "libbinder", ], diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp index d5dc6b741d..18b6b58a9e 100644 --- a/cmds/service/service.cpp +++ b/cmds/service/service.cpp @@ -18,13 +18,18 @@ #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <binder/TextOutput.h> +#include <cutils/ashmem.h> #include <getopt.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> +#include <sys/mman.h> #include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> using namespace android; @@ -70,7 +75,7 @@ int main(int argc, char* const argv[]) { bool wantsUsage = false; int result = 0; - + while (1) { int ic = getopt(argc, argv, "h?"); if (ic < 0) @@ -97,7 +102,7 @@ int main(int argc, char* const argv[]) aerr << "service: Unable to get default service manager!" << endl; return 20; } - + if (optind >= argc) { wantsUsage = true; } else if (!wantsUsage) { @@ -119,8 +124,8 @@ int main(int argc, char* const argv[]) for (unsigned i = 0; i < services.size(); i++) { String16 name = services[i]; sp<IBinder> service = sm->checkService(name); - aout << i - << "\t" << good_old_string(name) + aout << i + << "\t" << good_old_string(name) << ": [" << good_old_string(get_interface_name(service)) << "]" << endl; } @@ -187,69 +192,120 @@ int main(int argc, char* const argv[]) } else if (strcmp(argv[optind], "null") == 0) { optind++; data.writeStrongBinder(nullptr); + } else if (strcmp(argv[optind], "fd") == 0) { + optind++; + if (optind >= argc) { + aerr << "service: no path supplied for 'fd'" << endl; + wantsUsage = true; + result = 10; + break; + } + const char *path = argv[optind++]; + int fd = open(path, O_RDONLY); + if (fd < 0) { + aerr << "service: could not open '" << path << "'" << endl; + wantsUsage = true; + result = 10; + break; + } + data.writeFileDescriptor(fd, true /* take ownership */); + } else if (strcmp(argv[optind], "afd") == 0) { + optind++; + if (optind >= argc) { + aerr << "service: no path supplied for 'afd'" << endl; + wantsUsage = true; + result = 10; + break; + } + const char *path = argv[optind++]; + int fd = open(path, O_RDONLY); + struct stat statbuf; + if (fd < 0 || fstat(fd, &statbuf) != 0) { + aerr << "service: could not open or stat '" << path << "'" << endl; + wantsUsage = true; + result = 10; + break; + } + int afd = ashmem_create_region("test", statbuf.st_size); + void* ptr = mmap(NULL, statbuf.st_size, + PROT_READ | PROT_WRITE, MAP_SHARED, afd, 0); + read(fd, ptr, statbuf.st_size); + close(fd); + data.writeFileDescriptor(afd, true /* take ownership */); + } else if (strcmp(argv[optind], "nfd") == 0) { + optind++; + if (optind >= argc) { + aerr << "service: no file descriptor supplied for 'nfd'" << endl; + wantsUsage = true; + result = 10; + break; + } + data.writeFileDescriptor( + atoi(argv[optind++]), true /* take ownership */); + } else if (strcmp(argv[optind], "intent") == 0) { - - char* action = nullptr; - char* dataArg = nullptr; - char* type = nullptr; - int launchFlags = 0; - char* component = nullptr; - int categoryCount = 0; - char* categories[16]; - - char* context1 = nullptr; - + + char* action = nullptr; + char* dataArg = nullptr; + char* type = nullptr; + int launchFlags = 0; + char* component = nullptr; + int categoryCount = 0; + char* categories[16]; + + char* context1 = nullptr; + optind++; - - while (optind < argc) - { - char* key = strtok_r(argv[optind], "=", &context1); - char* value = strtok_r(nullptr, "=", &context1); - + + while (optind < argc) + { + char* key = strtok_r(argv[optind], "=", &context1); + char* value = strtok_r(nullptr, "=", &context1); + // we have reached the end of the XXX=XXX args. if (key == nullptr) break; - - if (strcmp(key, "action") == 0) - { - action = value; - } - else if (strcmp(key, "data") == 0) - { - dataArg = value; - } - else if (strcmp(key, "type") == 0) - { - type = value; - } - else if (strcmp(key, "launchFlags") == 0) - { - launchFlags = atoi(value); - } - else if (strcmp(key, "component") == 0) - { - component = value; - } - else if (strcmp(key, "categories") == 0) - { - char* context2 = nullptr; - categories[categoryCount] = strtok_r(value, ",", &context2); - - while (categories[categoryCount] != nullptr) - { - categoryCount++; - categories[categoryCount] = strtok_r(nullptr, ",", &context2); - } - } - + + if (strcmp(key, "action") == 0) + { + action = value; + } + else if (strcmp(key, "data") == 0) + { + dataArg = value; + } + else if (strcmp(key, "type") == 0) + { + type = value; + } + else if (strcmp(key, "launchFlags") == 0) + { + launchFlags = atoi(value); + } + else if (strcmp(key, "component") == 0) + { + component = value; + } + else if (strcmp(key, "categories") == 0) + { + char* context2 = nullptr; + categories[categoryCount] = strtok_r(value, ",", &context2); + + while (categories[categoryCount] != nullptr) + { + categoryCount++; + categories[categoryCount] = strtok_r(nullptr, ",", &context2); + } + } + optind++; - } - + } + writeString16(data, action); writeString16(data, dataArg); writeString16(data, type); - data.writeInt32(launchFlags); + data.writeInt32(launchFlags); writeString16(data, component); - + if (categoryCount > 0) { data.writeInt32(categoryCount); @@ -261,10 +317,10 @@ int main(int argc, char* const argv[]) else { data.writeInt32(0); - } - + } + // for now just set the extra field to be null. - data.writeInt32(-1); + data.writeInt32(-1); } else { aerr << "service: unknown option " << argv[optind] << endl; wantsUsage = true; @@ -272,7 +328,7 @@ int main(int argc, char* const argv[]) break; } } - + service->transact(code, data, &reply); aout << "Result: " << reply << endl; } else { @@ -295,23 +351,29 @@ int main(int argc, char* const argv[]) result = 10; } } - + if (wantsUsage) { aout << "Usage: service [-h|-?]\n" " service list\n" " service check SERVICE\n" - " service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR ] ...\n" + " service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR | null" + " | fd f | nfd n | afd f ] ...\n" "Options:\n" " i32: Write the 32-bit integer N into the send parcel.\n" " i64: Write the 64-bit integer N into the send parcel.\n" " f: Write the 32-bit single-precision number N into the send parcel.\n" " d: Write the 64-bit double-precision number N into the send parcel.\n" - " s16: Write the UTF-16 string STR into the send parcel.\n"; + " s16: Write the UTF-16 string STR into the send parcel.\n" + " null: Write a null binder into the send parcel.\n" + " fd: Write a file descriptor for the file f to the send parcel.\n" + " nfd: Write file descriptor n to the send parcel.\n" + " afd: Write an ashmem file descriptor for a region containing the data from" + " file f to the send parcel.\n"; // " intent: Write and Intent int the send parcel. ARGS can be\n" // " action=STR data=STR type=STR launchFlags=INT component=STR categories=STR[,STR,...]\n"; return result; } - + return result; } diff --git a/cmds/servicemanager/Access.cpp b/cmds/servicemanager/Access.cpp new file mode 100644 index 0000000000..b7e520f2f1 --- /dev/null +++ b/cmds/servicemanager/Access.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Access.h" + +#include <android-base/logging.h> +#include <binder/IPCThreadState.h> +#include <log/log_safetynet.h> +#include <selinux/android.h> +#include <selinux/avc.h> + +namespace android { + +#ifdef VENDORSERVICEMANAGER +constexpr bool kIsVendor = true; +#else +constexpr bool kIsVendor = false; +#endif + +static std::string getPidcon(pid_t pid) { + android_errorWriteLog(0x534e4554, "121035042"); + + char* lookup = nullptr; + if (getpidcon(pid, &lookup) < 0) { + LOG(ERROR) << "SELinux: getpidcon(pid=" << pid << ") failed to retrieve pid context"; + return ""; + } + std::string result = lookup; + freecon(lookup); + return result; +} + +static struct selabel_handle* getSehandle() { + static struct selabel_handle* gSehandle = nullptr; + + if (gSehandle != nullptr && selinux_status_updated()) { + selabel_close(gSehandle); + gSehandle = nullptr; + } + + if (gSehandle == nullptr) { + gSehandle = kIsVendor + ? selinux_android_vendor_service_context_handle() + : selinux_android_service_context_handle(); + } + + CHECK(gSehandle != nullptr); + return gSehandle; +} + +struct AuditCallbackData { + const Access::CallingContext* context; + const std::string* tname; +}; + +static int auditCallback(void *data, security_class_t /*cls*/, char *buf, size_t len) { + const AuditCallbackData* ad = reinterpret_cast<AuditCallbackData*>(data); + + if (!ad) { + LOG(ERROR) << "No service manager audit data"; + return 0; + } + + snprintf(buf, len, "pid=%d uid=%d name=%s", ad->context->debugPid, ad->context->uid, + ad->tname->c_str()); + return 0; +} + +Access::Access() { + union selinux_callback cb; + + cb.func_audit = auditCallback; + selinux_set_callback(SELINUX_CB_AUDIT, cb); + + cb.func_log = kIsVendor ? selinux_vendor_log_callback : selinux_log_callback; + selinux_set_callback(SELINUX_CB_LOG, cb); + + CHECK(selinux_status_open(true /*fallback*/) >= 0); + + CHECK(getcon(&mThisProcessContext) == 0); +} + +Access::~Access() { + freecon(mThisProcessContext); +} + +Access::CallingContext Access::getCallingContext() { + IPCThreadState* ipc = IPCThreadState::self(); + + const char* callingSid = ipc->getCallingSid(); + pid_t callingPid = ipc->getCallingPid(); + + return CallingContext { + .debugPid = callingPid, + .uid = ipc->getCallingUid(), + .sid = callingSid ? std::string(callingSid) : getPidcon(callingPid), + }; +} + +bool Access::canFind(const CallingContext& ctx,const std::string& name) { + return actionAllowedFromLookup(ctx, name, "find"); +} + +bool Access::canAdd(const CallingContext& ctx, const std::string& name) { + return actionAllowedFromLookup(ctx, name, "add"); +} + +bool Access::canList(const CallingContext& ctx) { + return actionAllowed(ctx, mThisProcessContext, "list", "service_manager"); +} + +bool Access::actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm, + const std::string& tname) { + const char* tclass = "service_manager"; + + AuditCallbackData data = { + .context = &sctx, + .tname = &tname, + }; + + return 0 == selinux_check_access(sctx.sid.c_str(), tctx, tclass, perm, + reinterpret_cast<void*>(&data)); +} + +bool Access::actionAllowedFromLookup(const CallingContext& sctx, const std::string& name, const char *perm) { + char *tctx = nullptr; + if (selabel_lookup(getSehandle(), &tctx, name.c_str(), SELABEL_CTX_ANDROID_SERVICE) != 0) { + LOG(ERROR) << "SELinux: No match for " << name << " in service_contexts.\n"; + return false; + } + + bool allowed = actionAllowed(sctx, tctx, perm, name); + freecon(tctx); + return allowed; +} + +} // android diff --git a/cmds/servicemanager/Access.h b/cmds/servicemanager/Access.h new file mode 100644 index 0000000000..77c2cd4ed6 --- /dev/null +++ b/cmds/servicemanager/Access.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> +#include <sys/types.h> + +namespace android { + +// singleton +class Access { +public: + Access(); + virtual ~Access(); + + Access(const Access&) = delete; + Access& operator=(const Access&) = delete; + Access(Access&&) = delete; + Access& operator=(Access&&) = delete; + + struct CallingContext { + pid_t debugPid; + uid_t uid; + std::string sid; + }; + + virtual CallingContext getCallingContext(); + + virtual bool canFind(const CallingContext& ctx, const std::string& name); + virtual bool canAdd(const CallingContext& ctx, const std::string& name); + virtual bool canList(const CallingContext& ctx); + +private: + bool actionAllowed(const CallingContext& sctx, const char* tctx, const char* perm, + const std::string& tname); + bool actionAllowedFromLookup(const CallingContext& sctx, const std::string& name, + const char *perm); + + char* mThisProcessContext = nullptr; +}; + +}; diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index 428561bc8a..7277e85d99 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -1,51 +1,58 @@ cc_defaults { - name: "servicemanager_flags", + name: "servicemanager_defaults", cflags: [ "-Wall", "-Wextra", "-Werror", ], - product_variables: { - binder32bit: { - cflags: ["-DBINDER_IPC_32BIT=1"], - }, - }, - shared_libs: ["liblog"], -} - -cc_binary { - name: "bctest", - defaults: ["servicemanager_flags"], srcs: [ - "bctest.c", - "binder.c", + "Access.cpp", + "ServiceManager.cpp", ], + + shared_libs: [ + "libbase", + "libbinder", // also contains servicemanager_interface + "libvintf", + "libcutils", + "liblog", + "libutils", + "libselinux", + ], + + target: { + vendor: { + exclude_shared_libs: ["libvintf"], + }, + }, } cc_binary { name: "servicemanager", - defaults: ["servicemanager_flags"], - srcs: [ - "service_manager.c", - "binder.c", - ], - shared_libs: ["libcutils", "libselinux"], + defaults: ["servicemanager_defaults"], init_rc: ["servicemanager.rc"], + srcs: ["main.cpp"], } cc_binary { name: "vndservicemanager", - defaults: ["servicemanager_flags"], + defaults: ["servicemanager_defaults"], + init_rc: ["vndservicemanager.rc"], vendor: true, - srcs: [ - "service_manager.c", - "binder.c", - ], cflags: [ "-DVENDORSERVICEMANAGER=1", ], - shared_libs: ["libcutils", "libselinux"], - init_rc: ["vndservicemanager.rc"], + srcs: ["main.cpp"], +} + +cc_test { + name: "servicemanager_test", + test_suites: ["device-tests"], + defaults: ["servicemanager_defaults"], + srcs: [ + "test_sm.cpp", + ], + static_libs: ["libgmock"], } diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp new file mode 100644 index 0000000000..abe64365f3 --- /dev/null +++ b/cmds/servicemanager/ServiceManager.cpp @@ -0,0 +1,543 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ServiceManager.h" + +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <binder/BpBinder.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/Stability.h> +#include <cutils/android_filesystem_config.h> +#include <cutils/multiuser.h> +#include <thread> + +#ifndef VENDORSERVICEMANAGER +#include <vintf/VintfObject.h> +#include <vintf/constants.h> +#endif // !VENDORSERVICEMANAGER + +using ::android::binder::Status; +using ::android::internal::Stability; + +namespace android { + +#ifndef VENDORSERVICEMANAGER +static bool isVintfDeclared(const std::string& name) { + size_t firstSlash = name.find('/'); + size_t lastDot = name.rfind('.', firstSlash); + if (firstSlash == std::string::npos || lastDot == std::string::npos) { + LOG(ERROR) << "VINTF HALs require names in the format type/instance (e.g. " + << "some.package.foo.IFoo/default) but got: " << name; + return false; + } + const std::string package = name.substr(0, lastDot); + const std::string iface = name.substr(lastDot+1, firstSlash-lastDot-1); + const std::string instance = name.substr(firstSlash+1); + + for (const auto& manifest : { + vintf::VintfObject::GetDeviceHalManifest(), + vintf::VintfObject::GetFrameworkHalManifest() + }) { + if (manifest != nullptr && manifest->hasAidlInstance(package, iface, instance)) { + return true; + } + } + LOG(ERROR) << "Could not find " << package << "." << iface << "/" << instance + << " in the VINTF manifest."; + return false; +} + +static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::string& name) { + if (!Stability::requiresVintfDeclaration(binder)) { + return true; + } + + return isVintfDeclared(name); +} +#endif // !VENDORSERVICEMANAGER + +ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) { +#ifndef VENDORSERVICEMANAGER + // can process these at any times, don't want to delay first VINTF client + std::thread([] { + vintf::VintfObject::GetDeviceHalManifest(); + vintf::VintfObject::GetFrameworkHalManifest(); + }).detach(); +#endif // !VENDORSERVICEMANAGER +} +ServiceManager::~ServiceManager() { + // this should only happen in tests + + for (const auto& [name, callbacks] : mNameToRegistrationCallback) { + CHECK(!callbacks.empty()) << name; + for (const auto& callback : callbacks) { + CHECK(callback != nullptr) << name; + } + } + + for (const auto& [name, service] : mNameToService) { + CHECK(service.binder != nullptr) << name; + } +} + +Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) { + *outBinder = tryGetService(name, true); + // returns ok regardless of result for legacy reasons + return Status::ok(); +} + +Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBinder) { + *outBinder = tryGetService(name, false); + // returns ok regardless of result for legacy reasons + return Status::ok(); +} + +sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfNotFound) { + auto ctx = mAccess->getCallingContext(); + + sp<IBinder> out; + Service* service = nullptr; + if (auto it = mNameToService.find(name); it != mNameToService.end()) { + service = &(it->second); + + if (!service->allowIsolated) { + uid_t appid = multiuser_get_app_id(ctx.uid); + bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END; + + if (isIsolated) { + return nullptr; + } + } + out = service->binder; + } + + if (!mAccess->canFind(ctx, name)) { + return nullptr; + } + + if (!out && startIfNotFound) { + tryStartService(name); + } + + if (out) { + // Setting this guarantee each time we hand out a binder ensures that the client-checking + // loop knows about the event even if the client immediately drops the service + service->guaranteeClient = true; + } + + return out; +} + +bool isValidServiceName(const std::string& name) { + if (name.size() == 0) return false; + if (name.size() > 127) return false; + + for (char c : name) { + if (c == '_' || c == '-' || c == '.' || c == '/') continue; + if (c >= 'a' && c <= 'z') continue; + if (c >= 'A' && c <= 'Z') continue; + if (c >= '0' && c <= '9') continue; + return false; + } + + return true; +} + +Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) { + auto ctx = mAccess->getCallingContext(); + + // apps cannot add services + if (multiuser_get_app_id(ctx.uid) >= AID_APP) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + if (!mAccess->canAdd(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + if (binder == nullptr) { + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + } + + if (!isValidServiceName(name)) { + LOG(ERROR) << "Invalid service name: " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + } + +#ifndef VENDORSERVICEMANAGER + if (!meetsDeclarationRequirements(binder, name)) { + // already logged + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + } +#endif // !VENDORSERVICEMANAGER + + // implicitly unlinked when the binder is removed + if (binder->remoteBinder() != nullptr && binder->linkToDeath(this) != OK) { + LOG(ERROR) << "Could not linkToDeath when adding " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + auto entry = mNameToService.emplace(name, Service { + .binder = binder, + .allowIsolated = allowIsolated, + .dumpPriority = dumpPriority, + .debugPid = ctx.debugPid, + }); + + auto it = mNameToRegistrationCallback.find(name); + if (it != mNameToRegistrationCallback.end()) { + for (const sp<IServiceCallback>& cb : it->second) { + entry.first->second.guaranteeClient = true; + // permission checked in registerForNotifications + cb->onRegistration(name, binder); + } + } + + return Status::ok(); +} + +Status ServiceManager::listServices(int32_t dumpPriority, std::vector<std::string>* outList) { + if (!mAccess->canList(mAccess->getCallingContext())) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + size_t toReserve = 0; + for (auto const& [name, service] : mNameToService) { + (void) name; + + if (service.dumpPriority & dumpPriority) ++toReserve; + } + + CHECK(outList->empty()); + + outList->reserve(toReserve); + for (auto const& [name, service] : mNameToService) { + (void) service; + + if (service.dumpPriority & dumpPriority) { + outList->push_back(name); + } + } + + return Status::ok(); +} + +Status ServiceManager::registerForNotifications( + const std::string& name, const sp<IServiceCallback>& callback) { + auto ctx = mAccess->getCallingContext(); + + if (!mAccess->canFind(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + if (!isValidServiceName(name)) { + LOG(ERROR) << "Invalid service name: " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + } + + if (callback == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + + if (OK != IInterface::asBinder(callback)->linkToDeath(this)) { + LOG(ERROR) << "Could not linkToDeath when adding " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + mNameToRegistrationCallback[name].push_back(callback); + + if (auto it = mNameToService.find(name); it != mNameToService.end()) { + const sp<IBinder>& binder = it->second.binder; + + // never null if an entry exists + CHECK(binder != nullptr) << name; + callback->onRegistration(name, binder); + } + + return Status::ok(); +} +Status ServiceManager::unregisterForNotifications( + const std::string& name, const sp<IServiceCallback>& callback) { + auto ctx = mAccess->getCallingContext(); + + if (!mAccess->canFind(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + bool found = false; + + auto it = mNameToRegistrationCallback.find(name); + if (it != mNameToRegistrationCallback.end()) { + removeRegistrationCallback(IInterface::asBinder(callback), &it, &found); + } + + if (!found) { + LOG(ERROR) << "Trying to unregister callback, but none exists " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + return Status::ok(); +} + +Status ServiceManager::isDeclared(const std::string& name, bool* outReturn) { + auto ctx = mAccess->getCallingContext(); + + if (!mAccess->canFind(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + *outReturn = false; + +#ifndef VENDORSERVICEMANAGER + *outReturn = isVintfDeclared(name); +#endif + return Status::ok(); +} + +void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who, + ServiceCallbackMap::iterator* it, + bool* found) { + std::vector<sp<IServiceCallback>>& listeners = (*it)->second; + + for (auto lit = listeners.begin(); lit != listeners.end();) { + if (IInterface::asBinder(*lit) == who) { + if(found) *found = true; + lit = listeners.erase(lit); + } else { + ++lit; + } + } + + if (listeners.empty()) { + *it = mNameToRegistrationCallback.erase(*it); + } else { + (*it)++; + } +} + +void ServiceManager::binderDied(const wp<IBinder>& who) { + for (auto it = mNameToService.begin(); it != mNameToService.end();) { + if (who == it->second.binder) { + it = mNameToService.erase(it); + } else { + ++it; + } + } + + for (auto it = mNameToRegistrationCallback.begin(); it != mNameToRegistrationCallback.end();) { + removeRegistrationCallback(who, &it, nullptr /*found*/); + } + + for (auto it = mNameToClientCallback.begin(); it != mNameToClientCallback.end();) { + removeClientCallback(who, &it); + } +} + +void ServiceManager::tryStartService(const std::string& name) { + ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service", + name.c_str()); + + std::thread([=] { + (void)base::SetProperty("ctl.interface_start", "aidl/" + name); + }).detach(); +} + +Status ServiceManager::registerClientCallback(const std::string& name, const sp<IBinder>& service, + const sp<IClientCallback>& cb) { + if (cb == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + + auto ctx = mAccess->getCallingContext(); + if (!mAccess->canAdd(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + auto serviceIt = mNameToService.find(name); + if (serviceIt == mNameToService.end()) { + LOG(ERROR) << "Could not add callback for nonexistent service: " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + } + + if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) { + LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")"; + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION); + } + + if (serviceIt->second.binder != service) { + LOG(WARNING) << "Tried to register client callback for " << name + << " but a different service is registered under this name."; + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + } + + if (OK != IInterface::asBinder(cb)->linkToDeath(this)) { + LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + mNameToClientCallback[name].push_back(cb); + + return Status::ok(); +} + +void ServiceManager::removeClientCallback(const wp<IBinder>& who, + ClientCallbackMap::iterator* it) { + std::vector<sp<IClientCallback>>& listeners = (*it)->second; + + for (auto lit = listeners.begin(); lit != listeners.end();) { + if (IInterface::asBinder(*lit) == who) { + lit = listeners.erase(lit); + } else { + ++lit; + } + } + + if (listeners.empty()) { + *it = mNameToClientCallback.erase(*it); + } else { + (*it)++; + } +} + +ssize_t ServiceManager::Service::getNodeStrongRefCount() { + sp<BpBinder> bpBinder = binder->remoteBinder(); + if (bpBinder == nullptr) return -1; + + return ProcessState::self()->getStrongRefCountForNodeByHandle(bpBinder->handle()); +} + +void ServiceManager::handleClientCallbacks() { + for (const auto& [name, service] : mNameToService) { + handleServiceClientCallback(name, true); + } +} + +ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName, + bool isCalledOnInterval) { + auto serviceIt = mNameToService.find(serviceName); + if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) { + return -1; + } + + Service& service = serviceIt->second; + ssize_t count = service.getNodeStrongRefCount(); + + // binder driver doesn't support this feature + if (count == -1) return count; + + bool hasClients = count > 1; // this process holds a strong count + + if (service.guaranteeClient) { + // we have no record of this client + if (!service.hasClients && !hasClients) { + sendClientCallbackNotifications(serviceName, true); + } + + // guarantee is temporary + service.guaranteeClient = false; + } + + // only send notifications if this was called via the interval checking workflow + if (isCalledOnInterval) { + if (hasClients && !service.hasClients) { + // client was retrieved in some other way + sendClientCallbackNotifications(serviceName, true); + } + + // there are no more clients, but the callback has not been called yet + if (!hasClients && service.hasClients) { + sendClientCallbackNotifications(serviceName, false); + } + } + + return count; +} + +void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) { + auto serviceIt = mNameToService.find(serviceName); + if (serviceIt == mNameToService.end()) { + LOG(WARNING) << "sendClientCallbackNotifications could not find service " << serviceName; + return; + } + Service& service = serviceIt->second; + + CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients + << " so we can't tell clients again that we have client: " << hasClients; + + LOG(INFO) << "Notifying " << serviceName << " they have clients: " << hasClients; + + auto ccIt = mNameToClientCallback.find(serviceName); + CHECK(ccIt != mNameToClientCallback.end()) + << "sendClientCallbackNotifications could not find callbacks for service "; + + for (const auto& callback : ccIt->second) { + callback->onClients(service.binder, hasClients); + } + + service.hasClients = hasClients; +} + +Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IBinder>& binder) { + if (binder == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + + auto ctx = mAccess->getCallingContext(); + if (!mAccess->canAdd(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + auto serviceIt = mNameToService.find(name); + if (serviceIt == mNameToService.end()) { + LOG(WARNING) << "Tried to unregister " << name + << ", but that service wasn't registered to begin with."; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) { + LOG(WARNING) << "Only a server can unregister itself (for " << name << ")"; + return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION); + } + + sp<IBinder> storedBinder = serviceIt->second.binder; + + if (binder != storedBinder) { + LOG(WARNING) << "Tried to unregister " << name + << ", but a different service is registered under this name."; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + int clients = handleServiceClientCallback(name, false); + + // clients < 0: feature not implemented or other error. Assume clients. + // Otherwise: + // - kernel driver will hold onto one refcount (during this transaction) + // - servicemanager has a refcount (guaranteed by this transaction) + // So, if clients > 2, then at least one other service on the system must hold a refcount. + if (clients < 0 || clients > 2) { + // client callbacks are either disabled or there are other clients + LOG(INFO) << "Tried to unregister " << name << ", but there are clients: " << clients; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + mNameToService.erase(name); + + return Status::ok(); +} + +} // namespace android
\ No newline at end of file diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h new file mode 100644 index 0000000000..a2fc5a84c5 --- /dev/null +++ b/cmds/servicemanager/ServiceManager.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/os/BnServiceManager.h> +#include <android/os/IClientCallback.h> +#include <android/os/IServiceCallback.h> + +#include "Access.h" + +namespace android { + +using os::IClientCallback; +using os::IServiceCallback; + +class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient { +public: + ServiceManager(std::unique_ptr<Access>&& access); + ~ServiceManager(); + + // getService will try to start any services it cannot find + binder::Status getService(const std::string& name, sp<IBinder>* outBinder) override; + binder::Status checkService(const std::string& name, sp<IBinder>* outBinder) override; + binder::Status addService(const std::string& name, const sp<IBinder>& binder, + bool allowIsolated, int32_t dumpPriority) override; + binder::Status listServices(int32_t dumpPriority, std::vector<std::string>* outList) override; + binder::Status registerForNotifications(const std::string& name, + const sp<IServiceCallback>& callback) override; + binder::Status unregisterForNotifications(const std::string& name, + const sp<IServiceCallback>& callback) override; + + binder::Status isDeclared(const std::string& name, bool* outReturn) override; + binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service, + const sp<IClientCallback>& cb) override; + binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override; + void binderDied(const wp<IBinder>& who) override; + void handleClientCallbacks(); + +protected: + virtual void tryStartService(const std::string& name); + +private: + struct Service { + sp<IBinder> binder; // not null + bool allowIsolated; + int32_t dumpPriority; + bool hasClients = false; // notifications sent on true -> false. + bool guaranteeClient = false; // forces the client check to true + pid_t debugPid = 0; // the process in which this service runs + + // the number of clients of the service, including servicemanager itself + ssize_t getNodeStrongRefCount(); + }; + + using ServiceCallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>; + using ClientCallbackMap = std::map<std::string, std::vector<sp<IClientCallback>>>; + using ServiceMap = std::map<std::string, Service>; + + // removes a callback from mNameToRegistrationCallback, removing it if the vector is empty + // this updates iterator to the next location + void removeRegistrationCallback(const wp<IBinder>& who, + ServiceCallbackMap::iterator* it, + bool* found); + ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval); + // Also updates mHasClients (of what the last callback was) + void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients); + // removes a callback from mNameToClientCallback, deleting the entry if the vector is empty + // this updates the iterator to the next location + void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it); + + sp<IBinder> tryGetService(const std::string& name, bool startIfNotFound); + + ServiceMap mNameToService; + ServiceCallbackMap mNameToRegistrationCallback; + ClientCallbackMap mNameToClientCallback; + + std::unique_ptr<Access> mAccess; +}; + +} // namespace android diff --git a/cmds/servicemanager/TEST_MAPPING b/cmds/servicemanager/TEST_MAPPING new file mode 100644 index 0000000000..739740aa21 --- /dev/null +++ b/cmds/servicemanager/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "servicemanager_test" + } + ], + "imports": [ + { + "path": "frameworks/native/libs/binder" + } + ] +} diff --git a/cmds/servicemanager/bctest.c b/cmds/servicemanager/bctest.c deleted file mode 100644 index 354df670e5..0000000000 --- a/cmds/servicemanager/bctest.c +++ /dev/null @@ -1,107 +0,0 @@ -/* Copyright 2008 The Android Open Source Project - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include "binder.h" - -uint32_t svcmgr_lookup(struct binder_state *bs, uint32_t target, const char *name) -{ - uint32_t handle; - unsigned iodata[512/4]; - struct binder_io msg, reply; - - bio_init(&msg, iodata, sizeof(iodata), 4); - bio_put_uint32(&msg, 0); // strict mode header - bio_put_string16_x(&msg, SVC_MGR_NAME); - bio_put_string16_x(&msg, name); - - if (binder_call(bs, &msg, &reply, target, SVC_MGR_CHECK_SERVICE)) - return 0; - - handle = bio_get_ref(&reply); - - if (handle) - binder_acquire(bs, handle); - - binder_done(bs, &msg, &reply); - - return handle; -} - -int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr) -{ - int status; - unsigned iodata[512/4]; - struct binder_io msg, reply; - - bio_init(&msg, iodata, sizeof(iodata), 4); - bio_put_uint32(&msg, 0); // strict mode header - bio_put_string16_x(&msg, SVC_MGR_NAME); - bio_put_string16_x(&msg, name); - bio_put_obj(&msg, ptr); - - if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE)) - return -1; - - status = bio_get_uint32(&reply); - - binder_done(bs, &msg, &reply); - - return status; -} - -unsigned token; - -int main(int argc, char **argv) -{ - struct binder_state *bs; - uint32_t svcmgr = BINDER_SERVICE_MANAGER; - uint32_t handle; - - bs = binder_open("/dev/binder", 128*1024); - if (!bs) { - fprintf(stderr, "failed to open binder driver\n"); - return -1; - } - - argc--; - argv++; - while (argc > 0) { - if (!strcmp(argv[0],"alt")) { - handle = svcmgr_lookup(bs, svcmgr, "alt_svc_mgr"); - if (!handle) { - fprintf(stderr,"cannot find alt_svc_mgr\n"); - return -1; - } - svcmgr = handle; - fprintf(stderr,"svcmgr is via %x\n", handle); - } else if (!strcmp(argv[0],"lookup")) { - if (argc < 2) { - fprintf(stderr,"argument required\n"); - return -1; - } - handle = svcmgr_lookup(bs, svcmgr, argv[1]); - fprintf(stderr,"lookup(%s) = %x\n", argv[1], handle); - argc--; - argv++; - } else if (!strcmp(argv[0],"publish")) { - if (argc < 2) { - fprintf(stderr,"argument required\n"); - return -1; - } - svcmgr_publish(bs, svcmgr, argv[1], &token); - argc--; - argv++; - } else { - fprintf(stderr,"unknown command %s\n", argv[0]); - return -1; - } - argc--; - argv++; - } - return 0; -} diff --git a/cmds/servicemanager/binder.c b/cmds/servicemanager/binder.c deleted file mode 100644 index cf3b1728b6..0000000000 --- a/cmds/servicemanager/binder.c +++ /dev/null @@ -1,682 +0,0 @@ -/* Copyright 2008 The Android Open Source Project - */ - -#define LOG_TAG "Binder" - -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <unistd.h> - -#include <log/log.h> - -#include "binder.h" - -#define MAX_BIO_SIZE (1 << 30) - -#define TRACE 0 - -void bio_init_from_txn(struct binder_io *io, struct binder_transaction_data *txn); - -#if TRACE -void hexdump(void *_data, size_t len) -{ - unsigned char *data = _data; - size_t count; - - for (count = 0; count < len; count++) { - if ((count & 15) == 0) - fprintf(stderr,"%04zu:", count); - fprintf(stderr," %02x %c", *data, - (*data < 32) || (*data > 126) ? '.' : *data); - data++; - if ((count & 15) == 15) - fprintf(stderr,"\n"); - } - if ((count & 15) != 0) - fprintf(stderr,"\n"); -} - -void binder_dump_txn(struct binder_transaction_data *txn) -{ - struct flat_binder_object *obj; - binder_size_t *offs = (binder_size_t *)(uintptr_t)txn->data.ptr.offsets; - size_t count = txn->offsets_size / sizeof(binder_size_t); - - fprintf(stderr," target %016"PRIx64" cookie %016"PRIx64" code %08x flags %08x\n", - (uint64_t)txn->target.ptr, (uint64_t)txn->cookie, txn->code, txn->flags); - fprintf(stderr," pid %8d uid %8d data %"PRIu64" offs %"PRIu64"\n", - txn->sender_pid, txn->sender_euid, (uint64_t)txn->data_size, (uint64_t)txn->offsets_size); - hexdump((void *)(uintptr_t)txn->data.ptr.buffer, txn->data_size); - while (count--) { - obj = (struct flat_binder_object *) (((char*)(uintptr_t)txn->data.ptr.buffer) + *offs++); - fprintf(stderr," - type %08x flags %08x ptr %016"PRIx64" cookie %016"PRIx64"\n", - obj->type, obj->flags, (uint64_t)obj->binder, (uint64_t)obj->cookie); - } -} - -#define NAME(n) case n: return #n -const char *cmd_name(uint32_t cmd) -{ - switch(cmd) { - NAME(BR_NOOP); - NAME(BR_TRANSACTION_COMPLETE); - NAME(BR_INCREFS); - NAME(BR_ACQUIRE); - NAME(BR_RELEASE); - NAME(BR_DECREFS); - NAME(BR_TRANSACTION); - NAME(BR_REPLY); - NAME(BR_FAILED_REPLY); - NAME(BR_DEAD_REPLY); - NAME(BR_DEAD_BINDER); - default: return "???"; - } -} -#else -#define hexdump(a,b) do{} while (0) -#define binder_dump_txn(txn) do{} while (0) -#endif - -#define BIO_F_SHARED 0x01 /* needs to be buffer freed */ -#define BIO_F_OVERFLOW 0x02 /* ran out of space */ -#define BIO_F_IOERROR 0x04 -#define BIO_F_MALLOCED 0x08 /* needs to be free()'d */ - -struct binder_state -{ - int fd; - void *mapped; - size_t mapsize; -}; - -struct binder_state *binder_open(const char* driver, size_t mapsize) -{ - struct binder_state *bs; - struct binder_version vers; - - bs = malloc(sizeof(*bs)); - if (!bs) { - errno = ENOMEM; - return NULL; - } - - bs->fd = open(driver, O_RDWR | O_CLOEXEC); - if (bs->fd < 0) { - fprintf(stderr,"binder: cannot open %s (%s)\n", - driver, strerror(errno)); - goto fail_open; - } - - if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) || - (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) { - fprintf(stderr, - "binder: kernel driver version (%d) differs from user space version (%d)\n", - vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION); - goto fail_open; - } - - bs->mapsize = mapsize; - bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); - if (bs->mapped == MAP_FAILED) { - fprintf(stderr,"binder: cannot map device (%s)\n", - strerror(errno)); - goto fail_map; - } - - return bs; - -fail_map: - close(bs->fd); -fail_open: - free(bs); - return NULL; -} - -void binder_close(struct binder_state *bs) -{ - munmap(bs->mapped, bs->mapsize); - close(bs->fd); - free(bs); -} - -int binder_become_context_manager(struct binder_state *bs) -{ - struct flat_binder_object obj; - memset(&obj, 0, sizeof(obj)); - obj.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX; - - int result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR_EXT, &obj); - - // fallback to original method - if (result != 0) { - android_errorWriteLog(0x534e4554, "121035042"); - - result = ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0); - } - return result; -} - -int binder_write(struct binder_state *bs, void *data, size_t len) -{ - struct binder_write_read bwr; - int res; - - bwr.write_size = len; - bwr.write_consumed = 0; - bwr.write_buffer = (uintptr_t) data; - bwr.read_size = 0; - bwr.read_consumed = 0; - bwr.read_buffer = 0; - res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); - if (res < 0) { - fprintf(stderr,"binder_write: ioctl failed (%s)\n", - strerror(errno)); - } - return res; -} - -void binder_free_buffer(struct binder_state *bs, - binder_uintptr_t buffer_to_free) -{ - struct { - uint32_t cmd_free; - binder_uintptr_t buffer; - } __attribute__((packed)) data; - data.cmd_free = BC_FREE_BUFFER; - data.buffer = buffer_to_free; - binder_write(bs, &data, sizeof(data)); -} - -void binder_send_reply(struct binder_state *bs, - struct binder_io *reply, - binder_uintptr_t buffer_to_free, - int status) -{ - struct { - uint32_t cmd_free; - binder_uintptr_t buffer; - uint32_t cmd_reply; - struct binder_transaction_data txn; - } __attribute__((packed)) data; - - data.cmd_free = BC_FREE_BUFFER; - data.buffer = buffer_to_free; - data.cmd_reply = BC_REPLY; - data.txn.target.ptr = 0; - data.txn.cookie = 0; - data.txn.code = 0; - if (status) { - data.txn.flags = TF_STATUS_CODE; - data.txn.data_size = sizeof(int); - data.txn.offsets_size = 0; - data.txn.data.ptr.buffer = (uintptr_t)&status; - data.txn.data.ptr.offsets = 0; - } else { - data.txn.flags = 0; - data.txn.data_size = reply->data - reply->data0; - data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0); - data.txn.data.ptr.buffer = (uintptr_t)reply->data0; - data.txn.data.ptr.offsets = (uintptr_t)reply->offs0; - } - binder_write(bs, &data, sizeof(data)); -} - -int binder_parse(struct binder_state *bs, struct binder_io *bio, - uintptr_t ptr, size_t size, binder_handler func) -{ - int r = 1; - uintptr_t end = ptr + (uintptr_t) size; - - while (ptr < end) { - uint32_t cmd = *(uint32_t *) ptr; - ptr += sizeof(uint32_t); -#if TRACE - fprintf(stderr,"%s:\n", cmd_name(cmd)); -#endif - switch(cmd) { - case BR_NOOP: - break; - case BR_TRANSACTION_COMPLETE: - break; - case BR_INCREFS: - case BR_ACQUIRE: - case BR_RELEASE: - case BR_DECREFS: -#if TRACE - fprintf(stderr," %p, %p\n", (void *)ptr, (void *)(ptr + sizeof(void *))); -#endif - ptr += sizeof(struct binder_ptr_cookie); - break; - case BR_TRANSACTION_SEC_CTX: - case BR_TRANSACTION: { - struct binder_transaction_data_secctx txn; - if (cmd == BR_TRANSACTION_SEC_CTX) { - if ((end - ptr) < sizeof(struct binder_transaction_data_secctx)) { - ALOGE("parse: txn too small (binder_transaction_data_secctx)!\n"); - return -1; - } - memcpy(&txn, (void*) ptr, sizeof(struct binder_transaction_data_secctx)); - ptr += sizeof(struct binder_transaction_data_secctx); - } else /* BR_TRANSACTION */ { - if ((end - ptr) < sizeof(struct binder_transaction_data)) { - ALOGE("parse: txn too small (binder_transaction_data)!\n"); - return -1; - } - memcpy(&txn.transaction_data, (void*) ptr, sizeof(struct binder_transaction_data)); - ptr += sizeof(struct binder_transaction_data); - - txn.secctx = 0; - } - - binder_dump_txn(&txn.transaction_data); - if (func) { - unsigned rdata[256/4]; - struct binder_io msg; - struct binder_io reply; - int res; - - bio_init(&reply, rdata, sizeof(rdata), 4); - bio_init_from_txn(&msg, &txn.transaction_data); - res = func(bs, &txn, &msg, &reply); - if (txn.transaction_data.flags & TF_ONE_WAY) { - binder_free_buffer(bs, txn.transaction_data.data.ptr.buffer); - } else { - binder_send_reply(bs, &reply, txn.transaction_data.data.ptr.buffer, res); - } - } - break; - } - case BR_REPLY: { - struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr; - if ((end - ptr) < sizeof(*txn)) { - ALOGE("parse: reply too small!\n"); - return -1; - } - binder_dump_txn(txn); - if (bio) { - bio_init_from_txn(bio, txn); - bio = 0; - } else { - /* todo FREE BUFFER */ - } - ptr += sizeof(*txn); - r = 0; - break; - } - case BR_DEAD_BINDER: { - struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr; - ptr += sizeof(binder_uintptr_t); - death->func(bs, death->ptr); - break; - } - case BR_FAILED_REPLY: - r = -1; - break; - case BR_DEAD_REPLY: - r = -1; - break; - default: - ALOGE("parse: OOPS %d\n", cmd); - return -1; - } - } - - return r; -} - -void binder_acquire(struct binder_state *bs, uint32_t target) -{ - uint32_t cmd[2]; - cmd[0] = BC_ACQUIRE; - cmd[1] = target; - binder_write(bs, cmd, sizeof(cmd)); -} - -void binder_release(struct binder_state *bs, uint32_t target) -{ - uint32_t cmd[2]; - cmd[0] = BC_RELEASE; - cmd[1] = target; - binder_write(bs, cmd, sizeof(cmd)); -} - -void binder_link_to_death(struct binder_state *bs, uint32_t target, struct binder_death *death) -{ - struct { - uint32_t cmd; - struct binder_handle_cookie payload; - } __attribute__((packed)) data; - - data.cmd = BC_REQUEST_DEATH_NOTIFICATION; - data.payload.handle = target; - data.payload.cookie = (uintptr_t) death; - binder_write(bs, &data, sizeof(data)); -} - -int binder_call(struct binder_state *bs, - struct binder_io *msg, struct binder_io *reply, - uint32_t target, uint32_t code) -{ - int res; - struct binder_write_read bwr; - struct { - uint32_t cmd; - struct binder_transaction_data txn; - } __attribute__((packed)) writebuf; - unsigned readbuf[32]; - - if (msg->flags & BIO_F_OVERFLOW) { - fprintf(stderr,"binder: txn buffer overflow\n"); - goto fail; - } - - writebuf.cmd = BC_TRANSACTION; - writebuf.txn.target.handle = target; - writebuf.txn.code = code; - writebuf.txn.flags = 0; - writebuf.txn.data_size = msg->data - msg->data0; - writebuf.txn.offsets_size = ((char*) msg->offs) - ((char*) msg->offs0); - writebuf.txn.data.ptr.buffer = (uintptr_t)msg->data0; - writebuf.txn.data.ptr.offsets = (uintptr_t)msg->offs0; - - bwr.write_size = sizeof(writebuf); - bwr.write_consumed = 0; - bwr.write_buffer = (uintptr_t) &writebuf; - - hexdump(msg->data0, msg->data - msg->data0); - for (;;) { - bwr.read_size = sizeof(readbuf); - bwr.read_consumed = 0; - bwr.read_buffer = (uintptr_t) readbuf; - - res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); - - if (res < 0) { - fprintf(stderr,"binder: ioctl failed (%s)\n", strerror(errno)); - goto fail; - } - - res = binder_parse(bs, reply, (uintptr_t) readbuf, bwr.read_consumed, 0); - if (res == 0) return 0; - if (res < 0) goto fail; - } - -fail: - memset(reply, 0, sizeof(*reply)); - reply->flags |= BIO_F_IOERROR; - return -1; -} - -void binder_loop(struct binder_state *bs, binder_handler func) -{ - int res; - struct binder_write_read bwr; - uint32_t readbuf[32]; - - bwr.write_size = 0; - bwr.write_consumed = 0; - bwr.write_buffer = 0; - - readbuf[0] = BC_ENTER_LOOPER; - binder_write(bs, readbuf, sizeof(uint32_t)); - - for (;;) { - bwr.read_size = sizeof(readbuf); - bwr.read_consumed = 0; - bwr.read_buffer = (uintptr_t) readbuf; - - res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr); - - if (res < 0) { - ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno)); - break; - } - - res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func); - if (res == 0) { - ALOGE("binder_loop: unexpected reply?!\n"); - break; - } - if (res < 0) { - ALOGE("binder_loop: io error %d %s\n", res, strerror(errno)); - break; - } - } -} - -void bio_init_from_txn(struct binder_io *bio, struct binder_transaction_data *txn) -{ - bio->data = bio->data0 = (char *)(intptr_t)txn->data.ptr.buffer; - bio->offs = bio->offs0 = (binder_size_t *)(intptr_t)txn->data.ptr.offsets; - bio->data_avail = txn->data_size; - bio->offs_avail = txn->offsets_size / sizeof(size_t); - bio->flags = BIO_F_SHARED; -} - -void bio_init(struct binder_io *bio, void *data, - size_t maxdata, size_t maxoffs) -{ - size_t n = maxoffs * sizeof(size_t); - - if (n > maxdata) { - bio->flags = BIO_F_OVERFLOW; - bio->data_avail = 0; - bio->offs_avail = 0; - return; - } - - bio->data = bio->data0 = (char *) data + n; - bio->offs = bio->offs0 = data; - bio->data_avail = maxdata - n; - bio->offs_avail = maxoffs; - bio->flags = 0; -} - -static void *bio_alloc(struct binder_io *bio, size_t size) -{ - size = (size + 3) & (~3); - if (size > bio->data_avail) { - bio->flags |= BIO_F_OVERFLOW; - return NULL; - } else { - void *ptr = bio->data; - bio->data += size; - bio->data_avail -= size; - return ptr; - } -} - -void binder_done(struct binder_state *bs, - __unused struct binder_io *msg, - struct binder_io *reply) -{ - struct { - uint32_t cmd; - uintptr_t buffer; - } __attribute__((packed)) data; - - if (reply->flags & BIO_F_SHARED) { - data.cmd = BC_FREE_BUFFER; - data.buffer = (uintptr_t) reply->data0; - binder_write(bs, &data, sizeof(data)); - reply->flags = 0; - } -} - -static struct flat_binder_object *bio_alloc_obj(struct binder_io *bio) -{ - struct flat_binder_object *obj; - - obj = bio_alloc(bio, sizeof(*obj)); - - if (obj && bio->offs_avail) { - bio->offs_avail--; - *bio->offs++ = ((char*) obj) - ((char*) bio->data0); - return obj; - } - - bio->flags |= BIO_F_OVERFLOW; - return NULL; -} - -void bio_put_uint32(struct binder_io *bio, uint32_t n) -{ - uint32_t *ptr = bio_alloc(bio, sizeof(n)); - if (ptr) - *ptr = n; -} - -void bio_put_obj(struct binder_io *bio, void *ptr) -{ - struct flat_binder_object *obj; - - obj = bio_alloc_obj(bio); - if (!obj) - return; - - obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - obj->hdr.type = BINDER_TYPE_BINDER; - obj->binder = (uintptr_t)ptr; - obj->cookie = 0; -} - -void bio_put_ref(struct binder_io *bio, uint32_t handle) -{ - struct flat_binder_object *obj; - - if (handle) - obj = bio_alloc_obj(bio); - else - obj = bio_alloc(bio, sizeof(*obj)); - - if (!obj) - return; - - obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - obj->hdr.type = BINDER_TYPE_HANDLE; - obj->handle = handle; - obj->cookie = 0; -} - -void bio_put_string16(struct binder_io *bio, const uint16_t *str) -{ - size_t len; - uint16_t *ptr; - - if (!str) { - bio_put_uint32(bio, 0xffffffff); - return; - } - - len = 0; - while (str[len]) len++; - - if (len >= (MAX_BIO_SIZE / sizeof(uint16_t))) { - bio_put_uint32(bio, 0xffffffff); - return; - } - - /* Note: The payload will carry 32bit size instead of size_t */ - bio_put_uint32(bio, (uint32_t) len); - len = (len + 1) * sizeof(uint16_t); - ptr = bio_alloc(bio, len); - if (ptr) - memcpy(ptr, str, len); -} - -void bio_put_string16_x(struct binder_io *bio, const char *_str) -{ - unsigned char *str = (unsigned char*) _str; - size_t len; - uint16_t *ptr; - - if (!str) { - bio_put_uint32(bio, 0xffffffff); - return; - } - - len = strlen(_str); - - if (len >= (MAX_BIO_SIZE / sizeof(uint16_t))) { - bio_put_uint32(bio, 0xffffffff); - return; - } - - /* Note: The payload will carry 32bit size instead of size_t */ - bio_put_uint32(bio, len); - ptr = bio_alloc(bio, (len + 1) * sizeof(uint16_t)); - if (!ptr) - return; - - while (*str) - *ptr++ = *str++; - *ptr++ = 0; -} - -static void *bio_get(struct binder_io *bio, size_t size) -{ - size = (size + 3) & (~3); - - if (bio->data_avail < size){ - bio->data_avail = 0; - bio->flags |= BIO_F_OVERFLOW; - return NULL; - } else { - void *ptr = bio->data; - bio->data += size; - bio->data_avail -= size; - return ptr; - } -} - -uint32_t bio_get_uint32(struct binder_io *bio) -{ - uint32_t *ptr = bio_get(bio, sizeof(*ptr)); - return ptr ? *ptr : 0; -} - -uint16_t *bio_get_string16(struct binder_io *bio, size_t *sz) -{ - size_t len; - - /* Note: The payload will carry 32bit size instead of size_t */ - len = (size_t) bio_get_uint32(bio); - if (sz) - *sz = len; - return bio_get(bio, (len + 1) * sizeof(uint16_t)); -} - -static struct flat_binder_object *_bio_get_obj(struct binder_io *bio) -{ - size_t n; - size_t off = bio->data - bio->data0; - - /* TODO: be smarter about this? */ - for (n = 0; n < bio->offs_avail; n++) { - if (bio->offs[n] == off) - return bio_get(bio, sizeof(struct flat_binder_object)); - } - - bio->data_avail = 0; - bio->flags |= BIO_F_OVERFLOW; - return NULL; -} - -uint32_t bio_get_ref(struct binder_io *bio) -{ - struct flat_binder_object *obj; - - obj = _bio_get_obj(bio); - if (!obj) - return 0; - - if (obj->hdr.type == BINDER_TYPE_HANDLE) - return obj->handle; - - return 0; -} diff --git a/cmds/servicemanager/binder.h b/cmds/servicemanager/binder.h deleted file mode 100644 index a9ccc74130..0000000000 --- a/cmds/servicemanager/binder.h +++ /dev/null @@ -1,94 +0,0 @@ -/* Copyright 2008 The Android Open Source Project - */ - -#ifndef _BINDER_H_ -#define _BINDER_H_ - -#include <linux/android/binder.h> -#include <sys/ioctl.h> - -struct binder_state; - -struct binder_io -{ - char *data; /* pointer to read/write from */ - binder_size_t *offs; /* array of offsets */ - size_t data_avail; /* bytes available in data buffer */ - size_t offs_avail; /* entries available in offsets array */ - - char *data0; /* start of data buffer */ - binder_size_t *offs0; /* start of offsets buffer */ - uint32_t flags; - uint32_t unused; -}; - -struct binder_death { - void (*func)(struct binder_state *bs, void *ptr); - void *ptr; -}; - -/* the one magic handle */ -#define BINDER_SERVICE_MANAGER 0U - -#define SVC_MGR_NAME "android.os.IServiceManager" - -enum { - /* Must match definitions in IBinder.h and IServiceManager.h */ - PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'), - SVC_MGR_GET_SERVICE = 1, - SVC_MGR_CHECK_SERVICE, - SVC_MGR_ADD_SERVICE, - SVC_MGR_LIST_SERVICES, -}; - -typedef int (*binder_handler)(struct binder_state *bs, - struct binder_transaction_data_secctx *txn, - struct binder_io *msg, - struct binder_io *reply); - -struct binder_state *binder_open(const char* driver, size_t mapsize); -void binder_close(struct binder_state *bs); - -/* initiate a blocking binder call - * - returns zero on success - */ -int binder_call(struct binder_state *bs, - struct binder_io *msg, struct binder_io *reply, - uint32_t target, uint32_t code); - -/* release any state associate with the binder_io - * - call once any necessary data has been extracted from the - * binder_io after binder_call() returns - * - can safely be called even if binder_call() fails - */ -void binder_done(struct binder_state *bs, - struct binder_io *msg, struct binder_io *reply); - -/* manipulate strong references */ -void binder_acquire(struct binder_state *bs, uint32_t target); -void binder_release(struct binder_state *bs, uint32_t target); - -void binder_link_to_death(struct binder_state *bs, uint32_t target, struct binder_death *death); - -void binder_loop(struct binder_state *bs, binder_handler func); - -int binder_become_context_manager(struct binder_state *bs); - -/* allocate a binder_io, providing a stack-allocated working - * buffer, size of the working buffer, and how many object - * offset entries to reserve from the buffer - */ -void bio_init(struct binder_io *bio, void *data, - size_t maxdata, size_t maxobjects); - -void bio_put_obj(struct binder_io *bio, void *ptr); -void bio_put_ref(struct binder_io *bio, uint32_t handle); -void bio_put_uint32(struct binder_io *bio, uint32_t n); -void bio_put_string16(struct binder_io *bio, const uint16_t *str); -void bio_put_string16_x(struct binder_io *bio, const char *_str); - -uint32_t bio_get_uint32(struct binder_io *bio); -uint16_t *bio_get_string16(struct binder_io *bio, size_t *sz); -uint32_t bio_get_ref(struct binder_io *bio); - -#endif diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp new file mode 100644 index 0000000000..2618906261 --- /dev/null +++ b/cmds/servicemanager/main.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/logging.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/Status.h> +#include <sys/timerfd.h> +#include <utils/Looper.h> +#include <utils/StrongPointer.h> + +#include "Access.h" +#include "ServiceManager.h" + +using ::android::Access; +using ::android::sp; +using ::android::Looper; +using ::android::LooperCallback; +using ::android::ProcessState; +using ::android::IPCThreadState; +using ::android::ProcessState; +using ::android::ServiceManager; +using ::android::os::IServiceManager; +using ::android::sp; + +class BinderCallback : public LooperCallback { +public: + static sp<BinderCallback> setupTo(const sp<Looper>& looper) { + sp<BinderCallback> cb = new BinderCallback; + + int binder_fd = -1; + IPCThreadState::self()->setupPolling(&binder_fd); + LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd); + + // Flush after setupPolling(), to make sure the binder driver + // knows about this thread handling commands. + IPCThreadState::self()->flushCommands(); + + int ret = looper->addFd(binder_fd, + Looper::POLL_CALLBACK, + Looper::EVENT_INPUT, + cb, + nullptr /*data*/); + LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper"); + + return cb; + } + + int handleEvent(int /* fd */, int /* events */, void* /* data */) override { + IPCThreadState::self()->handlePolledCommands(); + return 1; // Continue receiving callbacks. + } +}; + +// LooperCallback for IClientCallback +class ClientCallbackCallback : public LooperCallback { +public: + static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) { + sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager); + + int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/); + LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno); + + itimerspec timespec { + .it_interval = { + .tv_sec = 5, + .tv_nsec = 0, + }, + .it_value = { + .tv_sec = 5, + .tv_nsec = 0, + }, + }; + + int timeRes = timerfd_settime(fdTimer, 0 /*flags*/, ×pec, nullptr); + LOG_ALWAYS_FATAL_IF(timeRes < 0, "Failed to timerfd_settime: res: %d err: %d", timeRes, errno); + + int addRes = looper->addFd(fdTimer, + Looper::POLL_CALLBACK, + Looper::EVENT_INPUT, + cb, + nullptr); + LOG_ALWAYS_FATAL_IF(addRes != 1, "Failed to add client callback FD to Looper"); + + return cb; + } + + int handleEvent(int fd, int /*events*/, void* /*data*/) override { + uint64_t expirations; + int ret = read(fd, &expirations, sizeof(expirations)); + if (ret != sizeof(expirations)) { + ALOGE("Read failed to callback FD: ret: %d err: %d", ret, errno); + } + + mManager->handleClientCallbacks(); + return 1; // Continue receiving callbacks. + } +private: + ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {} + sp<ServiceManager> mManager; +}; + +int main(int argc, char** argv) { + if (argc > 2) { + LOG(FATAL) << "usage: " << argv[0] << " [binder driver]"; + } + + const char* driver = argc == 2 ? argv[1] : "/dev/binder"; + + sp<ProcessState> ps = ProcessState::initWithDriver(driver); + ps->setThreadPoolMaxThreadCount(0); + ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY); + + sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>()); + if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) { + LOG(ERROR) << "Could not self register servicemanager"; + } + + IPCThreadState::self()->setTheContextObject(manager); + ps->becomeContextManager(nullptr, nullptr); + + sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/); + + BinderCallback::setupTo(looper); + ClientCallbackCallback::setupTo(looper, manager); + + while(true) { + looper->pollAll(-1); + } + + // should not be reached + return EXIT_FAILURE; +} diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c deleted file mode 100644 index ec3fac538d..0000000000 --- a/cmds/servicemanager/service_manager.c +++ /dev/null @@ -1,442 +0,0 @@ -/* Copyright 2008 The Android Open Source Project - */ - -#include <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <cutils/android_filesystem_config.h> -#include <cutils/multiuser.h> - -#include <selinux/android.h> -#include <selinux/avc.h> - -#include "binder.h" - -#ifdef VENDORSERVICEMANAGER -#define LOG_TAG "VendorServiceManager" -#else -#define LOG_TAG "ServiceManager" -#endif -#include <log/log.h> - -struct audit_data { - pid_t pid; - uid_t uid; - const char *name; -}; - -const char *str8(const uint16_t *x, size_t x_len) -{ - static char buf[128]; - size_t max = 127; - char *p = buf; - - if (x_len < max) { - max = x_len; - } - - if (x) { - while ((max > 0) && (*x != '\0')) { - *p++ = *x++; - max--; - } - } - *p++ = 0; - return buf; -} - -int str16eq(const uint16_t *a, const char *b) -{ - while (*a && *b) - if (*a++ != *b++) return 0; - if (*a || *b) - return 0; - return 1; -} - -static char *service_manager_context; -static struct selabel_handle* sehandle; - -static bool check_mac_perms(pid_t spid, const char* sid, uid_t uid, const char *tctx, const char *perm, const char *name) -{ - char *lookup_sid = NULL; - const char *class = "service_manager"; - bool allowed; - struct audit_data ad; - - if (sid == NULL && getpidcon(spid, &lookup_sid) < 0) { - ALOGE("SELinux: getpidcon(pid=%d) failed to retrieve pid context.\n", spid); - return false; - } - - ad.pid = spid; - ad.uid = uid; - ad.name = name; - - if (sid == NULL) { - android_errorWriteLog(0x534e4554, "121035042"); - } - - int result = selinux_check_access(sid ? sid : lookup_sid, tctx, class, perm, (void *) &ad); - allowed = (result == 0); - - freecon(lookup_sid); - return allowed; -} - -static bool check_mac_perms_from_getcon(pid_t spid, const char* sid, uid_t uid, const char *perm) -{ - return check_mac_perms(spid, sid, uid, service_manager_context, perm, NULL); -} - -static bool check_mac_perms_from_lookup(pid_t spid, const char* sid, uid_t uid, const char *perm, const char *name) -{ - bool allowed; - char *tctx = NULL; - - if (!sehandle) { - ALOGE("SELinux: Failed to find sehandle. Aborting service_manager.\n"); - abort(); - } - - if (selabel_lookup(sehandle, &tctx, name, 0) != 0) { - ALOGE("SELinux: No match for %s in service_contexts.\n", name); - return false; - } - - allowed = check_mac_perms(spid, sid, uid, tctx, perm, name); - freecon(tctx); - return allowed; -} - -static int svc_can_register(const uint16_t *name, size_t name_len, pid_t spid, const char* sid, uid_t uid) -{ - const char *perm = "add"; - - if (multiuser_get_app_id(uid) >= AID_APP) { - return 0; /* Don't allow apps to register services */ - } - - return check_mac_perms_from_lookup(spid, sid, uid, perm, str8(name, name_len)) ? 1 : 0; -} - -static int svc_can_list(pid_t spid, const char* sid, uid_t uid) -{ - const char *perm = "list"; - return check_mac_perms_from_getcon(spid, sid, uid, perm) ? 1 : 0; -} - -static int svc_can_find(const uint16_t *name, size_t name_len, pid_t spid, const char* sid, uid_t uid) -{ - const char *perm = "find"; - return check_mac_perms_from_lookup(spid, sid, uid, perm, str8(name, name_len)) ? 1 : 0; -} - -struct svcinfo -{ - struct svcinfo *next; - uint32_t handle; - struct binder_death death; - int allow_isolated; - uint32_t dumpsys_priority; - size_t len; - uint16_t name[0]; -}; - -struct svcinfo *svclist = NULL; - -struct svcinfo *find_svc(const uint16_t *s16, size_t len) -{ - struct svcinfo *si; - - for (si = svclist; si; si = si->next) { - if ((len == si->len) && - !memcmp(s16, si->name, len * sizeof(uint16_t))) { - return si; - } - } - return NULL; -} - -void svcinfo_death(struct binder_state *bs, void *ptr) -{ - struct svcinfo *si = (struct svcinfo* ) ptr; - - ALOGI("service '%s' died\n", str8(si->name, si->len)); - if (si->handle) { - binder_release(bs, si->handle); - si->handle = 0; - } -} - -uint16_t svcmgr_id[] = { - 'a','n','d','r','o','i','d','.','o','s','.', - 'I','S','e','r','v','i','c','e','M','a','n','a','g','e','r' -}; - - -uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid, const char* sid) -{ - struct svcinfo *si = find_svc(s, len); - - if (!si || !si->handle) { - return 0; - } - - if (!si->allow_isolated) { - // If this service doesn't allow access from isolated processes, - // then check the uid to see if it is isolated. - uid_t appid = uid % AID_USER; - if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) { - return 0; - } - } - - if (!svc_can_find(s, len, spid, sid, uid)) { - return 0; - } - - return si->handle; -} - -int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle, - uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid, const char* sid) { - struct svcinfo *si; - - //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle, - // allow_isolated ? "allow_isolated" : "!allow_isolated", uid); - - if (!handle || (len == 0) || (len > 127)) - return -1; - - if (!svc_can_register(s, len, spid, sid, uid)) { - ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n", - str8(s, len), handle, uid); - return -1; - } - - si = find_svc(s, len); - if (si) { - if (si->handle) { - ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n", - str8(s, len), handle, uid); - svcinfo_death(bs, si); - } - si->handle = handle; - } else { - si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t)); - if (!si) { - ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n", - str8(s, len), handle, uid); - return -1; - } - si->handle = handle; - si->len = len; - memcpy(si->name, s, (len + 1) * sizeof(uint16_t)); - si->name[len] = '\0'; - si->death.func = (void*) svcinfo_death; - si->death.ptr = si; - si->allow_isolated = allow_isolated; - si->dumpsys_priority = dumpsys_priority; - si->next = svclist; - svclist = si; - } - - binder_acquire(bs, handle); - binder_link_to_death(bs, handle, &si->death); - return 0; -} - -int svcmgr_handler(struct binder_state *bs, - struct binder_transaction_data_secctx *txn_secctx, - struct binder_io *msg, - struct binder_io *reply) -{ - struct svcinfo *si; - uint16_t *s; - size_t len; - uint32_t handle; - uint32_t strict_policy; - int allow_isolated; - uint32_t dumpsys_priority; - - struct binder_transaction_data *txn = &txn_secctx->transaction_data; - - //ALOGI("target=%p code=%d pid=%d uid=%d\n", - // (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid); - - if (txn->target.ptr != BINDER_SERVICE_MANAGER) - return -1; - - if (txn->code == PING_TRANSACTION) - return 0; - - // Equivalent to Parcel::enforceInterface(), reading the RPC - // header with the strict mode policy mask and the interface name. - // Note that we ignore the strict_policy and don't propagate it - // further (since we do no outbound RPCs anyway). - strict_policy = bio_get_uint32(msg); - bio_get_uint32(msg); // Ignore worksource header. - s = bio_get_string16(msg, &len); - if (s == NULL) { - return -1; - } - - if ((len != (sizeof(svcmgr_id) / 2)) || - memcmp(svcmgr_id, s, sizeof(svcmgr_id))) { - fprintf(stderr,"invalid id %s\n", str8(s, len)); - return -1; - } - - if (sehandle && selinux_status_updated() > 0) { -#ifdef VENDORSERVICEMANAGER - struct selabel_handle *tmp_sehandle = selinux_android_vendor_service_context_handle(); -#else - struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle(); -#endif - if (tmp_sehandle) { - selabel_close(sehandle); - sehandle = tmp_sehandle; - } - } - - switch(txn->code) { - case SVC_MGR_GET_SERVICE: - case SVC_MGR_CHECK_SERVICE: - s = bio_get_string16(msg, &len); - if (s == NULL) { - return -1; - } - handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid, - (const char*) txn_secctx->secctx); - if (!handle) - break; - bio_put_ref(reply, handle); - return 0; - - case SVC_MGR_ADD_SERVICE: - s = bio_get_string16(msg, &len); - if (s == NULL) { - return -1; - } - handle = bio_get_ref(msg); - allow_isolated = bio_get_uint32(msg) ? 1 : 0; - dumpsys_priority = bio_get_uint32(msg); - if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority, - txn->sender_pid, (const char*) txn_secctx->secctx)) - return -1; - break; - - case SVC_MGR_LIST_SERVICES: { - uint32_t n = bio_get_uint32(msg); - uint32_t req_dumpsys_priority = bio_get_uint32(msg); - - if (!svc_can_list(txn->sender_pid, (const char*) txn_secctx->secctx, txn->sender_euid)) { - ALOGE("list_service() uid=%d - PERMISSION DENIED\n", - txn->sender_euid); - return -1; - } - si = svclist; - // walk through the list of services n times skipping services that - // do not support the requested priority - while (si) { - if (si->dumpsys_priority & req_dumpsys_priority) { - if (n == 0) break; - n--; - } - si = si->next; - } - if (si) { - bio_put_string16(reply, si->name); - return 0; - } - return -1; - } - default: - ALOGE("unknown code %d\n", txn->code); - return -1; - } - - bio_put_uint32(reply, 0); - return 0; -} - - -static int audit_callback(void *data, __unused security_class_t cls, char *buf, size_t len) -{ - struct audit_data *ad = (struct audit_data *)data; - - if (!ad || !ad->name) { - ALOGE("No service manager audit data"); - return 0; - } - - snprintf(buf, len, "service=%s pid=%d uid=%d", ad->name, ad->pid, ad->uid); - return 0; -} - -int main(int argc, char** argv) -{ - struct binder_state *bs; - union selinux_callback cb; - char *driver; - - if (argc > 1) { - driver = argv[1]; - } else { - driver = "/dev/binder"; - } - - bs = binder_open(driver, 128*1024); - if (!bs) { -#ifdef VENDORSERVICEMANAGER - ALOGW("failed to open binder driver %s\n", driver); - while (true) { - sleep(UINT_MAX); - } -#else - ALOGE("failed to open binder driver %s\n", driver); -#endif - return -1; - } - - if (binder_become_context_manager(bs)) { - ALOGE("cannot become context manager (%s)\n", strerror(errno)); - return -1; - } - - cb.func_audit = audit_callback; - selinux_set_callback(SELINUX_CB_AUDIT, cb); -#ifdef VENDORSERVICEMANAGER - cb.func_log = selinux_vendor_log_callback; -#else - cb.func_log = selinux_log_callback; -#endif - selinux_set_callback(SELINUX_CB_LOG, cb); - -#ifdef VENDORSERVICEMANAGER - sehandle = selinux_android_vendor_service_context_handle(); -#else - sehandle = selinux_android_service_context_handle(); -#endif - selinux_status_open(true); - - if (sehandle == NULL) { - ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n"); - abort(); - } - - if (getcon(&service_manager_context) != 0) { - ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n"); - abort(); - } - - - binder_loop(bs, svcmgr_handler); - - return 0; -} diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp new file mode 100644 index 0000000000..25245beaf7 --- /dev/null +++ b/cmds/servicemanager/test_sm.cpp @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/os/BnServiceCallback.h> +#include <binder/Binder.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> +#include <cutils/android_filesystem_config.h> +#include <gtest/gtest.h> +#include <gmock/gmock.h> + +#include "Access.h" +#include "ServiceManager.h" + +using android::sp; +using android::Access; +using android::BBinder; +using android::IBinder; +using android::ServiceManager; +using android::binder::Status; +using android::os::BnServiceCallback; +using android::os::IServiceManager; +using testing::_; +using testing::ElementsAre; +using testing::NiceMock; +using testing::Return; + +static sp<IBinder> getBinder() { + class LinkableBinder : public BBinder { + android::status_t linkToDeath(const sp<DeathRecipient>&, void*, uint32_t) override { + // let SM linkToDeath + return android::OK; + } + }; + + return new LinkableBinder; +} + +class MockAccess : public Access { +public: + MOCK_METHOD0(getCallingContext, CallingContext()); + MOCK_METHOD2(canAdd, bool(const CallingContext&, const std::string& name)); + MOCK_METHOD2(canFind, bool(const CallingContext&, const std::string& name)); + MOCK_METHOD1(canList, bool(const CallingContext&)); +}; + +class MockServiceManager : public ServiceManager { + public: + MockServiceManager(std::unique_ptr<Access>&& access) : ServiceManager(std::move(access)) {} + MOCK_METHOD1(tryStartService, void(const std::string& name)); +}; + +static sp<ServiceManager> getPermissiveServiceManager() { + std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); + + ON_CALL(*access, getCallingContext()).WillByDefault(Return(Access::CallingContext{})); + ON_CALL(*access, canAdd(_, _)).WillByDefault(Return(true)); + ON_CALL(*access, canFind(_, _)).WillByDefault(Return(true)); + ON_CALL(*access, canList(_)).WillByDefault(Return(true)); + + sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access)); + return sm; +} + +TEST(AddService, HappyHappy) { + auto sm = getPermissiveServiceManager(); + EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); +} + +TEST(AddService, EmptyNameDisallowed) { + auto sm = getPermissiveServiceManager(); + EXPECT_FALSE(sm->addService("", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); +} + +TEST(AddService, JustShortEnoughServiceNameHappy) { + auto sm = getPermissiveServiceManager(); + EXPECT_TRUE(sm->addService(std::string(127, 'a'), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); +} + +TEST(AddService, TooLongNameDisallowed) { + auto sm = getPermissiveServiceManager(); + EXPECT_FALSE(sm->addService(std::string(128, 'a'), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); +} + +TEST(AddService, WeirdCharactersDisallowed) { + auto sm = getPermissiveServiceManager(); + EXPECT_FALSE(sm->addService("happy$foo$foo", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); +} + +TEST(AddService, AddNullServiceDisallowed) { + auto sm = getPermissiveServiceManager(); + EXPECT_FALSE(sm->addService("foo", nullptr, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); +} + +TEST(AddService, AddDisallowedFromApp) { + for (uid_t uid : { AID_APP_START, AID_APP_START + 1, AID_APP_END }) { + std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); + EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{ + .debugPid = 1337, + .uid = uid, + })); + EXPECT_CALL(*access, canAdd(_, _)).Times(0); + sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access)); + + EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + } + +} + +TEST(AddService, HappyOverExistingService) { + auto sm = getPermissiveServiceManager(); + EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); +} + +TEST(AddService, NoPermissions) { + std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); + + EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{})); + EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(false)); + + sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access)); + + EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); +} + +TEST(GetService, HappyHappy) { + auto sm = getPermissiveServiceManager(); + sp<IBinder> service = getBinder(); + + EXPECT_TRUE(sm->addService("foo", service, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + sp<IBinder> out; + EXPECT_TRUE(sm->getService("foo", &out).isOk()); + EXPECT_EQ(service, out); +} + +TEST(GetService, NonExistant) { + auto sm = getPermissiveServiceManager(); + + sp<IBinder> out; + EXPECT_TRUE(sm->getService("foo", &out).isOk()); + EXPECT_EQ(nullptr, out.get()); +} + +TEST(GetService, NoPermissionsForGettingService) { + std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); + + EXPECT_CALL(*access, getCallingContext()).WillRepeatedly(Return(Access::CallingContext{})); + EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(false)); + + sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access)); + + EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + sp<IBinder> out; + // returns nullptr but has OK status for legacy compatibility + EXPECT_TRUE(sm->getService("foo", &out).isOk()); + EXPECT_EQ(nullptr, out.get()); +} + +TEST(GetService, AllowedFromIsolated) { + std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); + + EXPECT_CALL(*access, getCallingContext()) + // something adds it + .WillOnce(Return(Access::CallingContext{})) + // next call is from isolated app + .WillOnce(Return(Access::CallingContext{ + .uid = AID_ISOLATED_START, + })); + EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true)); + EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true)); + + sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access)); + + sp<IBinder> service = getBinder(); + EXPECT_TRUE(sm->addService("foo", service, true /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + sp<IBinder> out; + EXPECT_TRUE(sm->getService("foo", &out).isOk()); + EXPECT_EQ(service, out.get()); +} + +TEST(GetService, NotAllowedFromIsolated) { + std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); + + EXPECT_CALL(*access, getCallingContext()) + // something adds it + .WillOnce(Return(Access::CallingContext{})) + // next call is from isolated app + .WillOnce(Return(Access::CallingContext{ + .uid = AID_ISOLATED_START, + })); + EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true)); + + // TODO(b/136023468): when security check is first, this should be called first + // EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true)); + + sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access)); + + EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + sp<IBinder> out; + // returns nullptr but has OK status for legacy compatibility + EXPECT_TRUE(sm->getService("foo", &out).isOk()); + EXPECT_EQ(nullptr, out.get()); +} + +TEST(ListServices, NoPermissions) { + std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); + + EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{})); + EXPECT_CALL(*access, canList(_)).WillOnce(Return(false)); + + sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access)); + + std::vector<std::string> out; + EXPECT_FALSE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL, &out).isOk()); + EXPECT_TRUE(out.empty()); +} + +TEST(ListServices, AllServices) { + auto sm = getPermissiveServiceManager(); + + EXPECT_TRUE(sm->addService("sd", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + EXPECT_TRUE(sm->addService("sc", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_NORMAL).isOk()); + EXPECT_TRUE(sm->addService("sb", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_HIGH).isOk()); + EXPECT_TRUE(sm->addService("sa", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL).isOk()); + + std::vector<std::string> out; + EXPECT_TRUE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL, &out).isOk()); + + // all there and in the right order + EXPECT_THAT(out, ElementsAre("sa", "sb", "sc", "sd")); +} + +TEST(ListServices, CriticalServices) { + auto sm = getPermissiveServiceManager(); + + EXPECT_TRUE(sm->addService("sd", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + EXPECT_TRUE(sm->addService("sc", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_NORMAL).isOk()); + EXPECT_TRUE(sm->addService("sb", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_HIGH).isOk()); + EXPECT_TRUE(sm->addService("sa", getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL).isOk()); + + std::vector<std::string> out; + EXPECT_TRUE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, &out).isOk()); + + // all there and in the right order + EXPECT_THAT(out, ElementsAre("sa")); +} + +class CallbackHistorian : public BnServiceCallback { + Status onRegistration(const std::string& name, const sp<IBinder>& binder) override { + registrations.push_back(name); + binders.push_back(binder); + return Status::ok(); + } + + android::status_t linkToDeath(const sp<DeathRecipient>&, void*, uint32_t) override { + // let SM linkToDeath + return android::OK; + } + +public: + std::vector<std::string> registrations; + std::vector<sp<IBinder>> binders; +}; + +TEST(ServiceNotifications, NoPermissionsRegister) { + std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); + + EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{})); + EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false)); + + sp<ServiceManager> sm = new ServiceManager(std::move(access)); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + EXPECT_EQ(sm->registerForNotifications("foofoo", cb).exceptionCode(), + Status::EX_SECURITY); +} + +TEST(ServiceNotifications, NoPermissionsUnregister) { + std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); + + EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{})); + EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false)); + + sp<ServiceManager> sm = new ServiceManager(std::move(access)); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + // should always hit security error first + EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(), + Status::EX_SECURITY); +} + +TEST(ServiceNotifications, InvalidName) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + EXPECT_EQ(sm->registerForNotifications("foo@foo", cb).exceptionCode(), + Status::EX_ILLEGAL_ARGUMENT); +} + +TEST(ServiceNotifications, NullCallback) { + auto sm = getPermissiveServiceManager(); + + EXPECT_EQ(sm->registerForNotifications("foofoo", nullptr).exceptionCode(), + Status::EX_NULL_POINTER); +} + +TEST(ServiceNotifications, Unregister) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk()); + EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(), 0); +} + +TEST(ServiceNotifications, UnregisterWhenNoRegistrationExists) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(), + Status::EX_ILLEGAL_STATE); +} + +TEST(ServiceNotifications, NoNotification) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk()); + EXPECT_TRUE(sm->addService("otherservice", getBinder(), + false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + EXPECT_THAT(cb->registrations, ElementsAre()); + EXPECT_THAT(cb->binders, ElementsAre()); +} + +TEST(ServiceNotifications, GetNotification) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + sp<IBinder> service = getBinder(); + + EXPECT_TRUE(sm->registerForNotifications("asdfasdf", cb).isOk()); + EXPECT_TRUE(sm->addService("asdfasdf", service, + false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf")); + EXPECT_THAT(cb->binders, ElementsAre(service)); +} + +TEST(ServiceNotifications, GetNotificationForAlreadyRegisteredService) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + sp<IBinder> service = getBinder(); + + EXPECT_TRUE(sm->addService("asdfasdf", service, + false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + EXPECT_TRUE(sm->registerForNotifications("asdfasdf", cb).isOk()); + + EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf")); + EXPECT_THAT(cb->binders, ElementsAre(service)); +} + +TEST(ServiceNotifications, GetMultipleNotification) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + sp<IBinder> binder1 = getBinder(); + sp<IBinder> binder2 = getBinder(); + + EXPECT_TRUE(sm->registerForNotifications("asdfasdf", cb).isOk()); + EXPECT_TRUE(sm->addService("asdfasdf", binder1, + false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + EXPECT_TRUE(sm->addService("asdfasdf", binder2, + false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf", "asdfasdf")); + EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf", "asdfasdf")); +} diff --git a/data/etc/android.hardware.se.omapi.ese.xml b/data/etc/android.hardware.se.omapi.ese.xml new file mode 100644 index 0000000000..3b1d81cd87 --- /dev/null +++ b/data/etc/android.hardware.se.omapi.ese.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This feature indicates that the device supports The device supports + Open Mobile API capable ESE-based secure elements--> +<permissions> + <feature name="android.hardware.se.omapi.ese" /> +</permissions> diff --git a/data/etc/android.hardware.se.omapi.sd.xml b/data/etc/android.hardware.se.omapi.sd.xml new file mode 100644 index 0000000000..8fc2869d23 --- /dev/null +++ b/data/etc/android.hardware.se.omapi.sd.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This feature indicates that the device supports The device supports + Open Mobile API capable SD-based secure elements--> +<permissions> + <feature name="android.hardware.se.omapi.sd" /> +</permissions> diff --git a/data/etc/android.hardware.se.omapi.uicc.xml b/data/etc/android.hardware.se.omapi.uicc.xml new file mode 100644 index 0000000000..9c6f143990 --- /dev/null +++ b/data/etc/android.hardware.se.omapi.uicc.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This feature indicates that the device supports The device supports + Open Mobile API capable UICC-based secure elements--> +<permissions> + <feature name="android.hardware.se.omapi.uicc" /> +</permissions> diff --git a/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml new file mode 100644 index 0000000000..9c67d4abb7 --- /dev/null +++ b/data/etc/android.software.vulkan.deqp.level-2019-03-01.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature indicating that the device passes Vulkan deQP + tests associated with date 2019-03-01 (0x07E30301). --> +<permissions> + <feature name="android.software.vulkan.deqp.level" version="132317953" /> +</permissions> diff --git a/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml new file mode 100644 index 0000000000..19b269b607 --- /dev/null +++ b/data/etc/android.software.vulkan.deqp.level-2020-03-01.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature indicating that the device passes Vulkan deQP + tests associated with date 2020-03-01 (0x07E40301). --> +<permissions> + <feature name="android.software.vulkan.deqp.level" version="132383489" /> +</permissions> diff --git a/headers/media_plugin/media/cas/CasAPI.h b/headers/media_plugin/media/cas/CasAPI.h index c87ee5655e..8cc9d365a2 100644 --- a/headers/media_plugin/media/cas/CasAPI.h +++ b/headers/media_plugin/media/cas/CasAPI.h @@ -56,6 +56,11 @@ typedef void (*CasPluginCallbackExt)( size_t size, const CasSessionId *sessionId); +typedef void (*CasPluginStatusCallback)( + void *appData, + int32_t event, + int32_t arg); + struct CasFactory { CasFactory() {} virtual ~CasFactory() {} @@ -91,6 +96,10 @@ struct CasPlugin { CasPlugin() {} virtual ~CasPlugin() {} + // Provide a callback to report plugin status + virtual status_t setStatusCallback( + CasPluginStatusCallback callback) = 0; + // Provide the CA private data from a CA_descriptor in the conditional // access table to a CasPlugin. virtual status_t setPrivateData( @@ -100,6 +109,11 @@ struct CasPlugin { // streams. virtual status_t openSession(CasSessionId *sessionId) = 0; + // Open a session with intend and mode for descrambling a program, or one + // or more elementary streams. + virtual status_t openSession(uint32_t intent, uint32_t mode, + CasSessionId *sessionId) = 0; + // Close a previously opened session. virtual status_t closeSession(const CasSessionId &sessionId) = 0; diff --git a/headers/media_plugin/media/openmax/OMX_Video.h b/headers/media_plugin/media/openmax/OMX_Video.h index b6edaa900e..81ee5fb2e5 100644 --- a/headers/media_plugin/media/openmax/OMX_Video.h +++ b/headers/media_plugin/media/openmax/OMX_Video.h @@ -90,6 +90,7 @@ typedef enum OMX_VIDEO_CODINGTYPE { OMX_VIDEO_CodingHEVC, /**< ITU H.265/HEVC */ OMX_VIDEO_CodingDolbyVision,/**< Dolby Vision */ OMX_VIDEO_CodingImageHEIC, /**< HEIF image encoded with HEVC */ + OMX_VIDEO_CodingAV1, /**< AV1 */ OMX_VIDEO_CodingKhronosExtensions = 0x6F000000, /**< Reserved region for introducing Khronos Standard Extensions */ OMX_VIDEO_CodingVendorStartUnused = 0x7F000000, /**< Reserved region for introducing Vendor Extensions */ OMX_VIDEO_CodingMax = 0x7FFFFFFF diff --git a/include/android/choreographer.h b/include/android/choreographer.h index 44883cc498..346861f0a8 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -59,6 +59,8 @@ typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* dat /** * Get the AChoreographer instance for the current thread. This must be called * on an ALooper thread. + * + * Available since API level 24. */ AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24); @@ -82,6 +84,8 @@ void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, /** * Power a callback to be run on the next frame. The data pointer provided will * be passed to the callback function when it's called. + * + * Available since API level 29. */ void AChoreographer_postFrameCallback64(AChoreographer* chroreographer, AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29); @@ -90,6 +94,8 @@ void AChoreographer_postFrameCallback64(AChoreographer* chroreographer, * Post a callback to be run on the frame following the specified delay. The * data pointer provided will be passed to the callback function when it's * called. + * + * Available since API level 29. */ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29); diff --git a/include/android/configuration.h b/include/android/configuration.h index ef6c5a2f81..05f43407fb 100644 --- a/include/android/configuration.h +++ b/include/android/configuration.h @@ -645,10 +645,14 @@ int32_t AConfiguration_getScreenLong(AConfiguration* config); */ void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong); +#if __ANDROID_API__ >= 30 /** * Return the current ACONFIGURATION_SCREENROUND_* set in the configuration. + * + * Available since API level 30. */ -int32_t AConfiguration_getScreenRound(AConfiguration* config); +int32_t AConfiguration_getScreenRound(AConfiguration* config) __INTRODUCED_IN(30); +#endif /** * Set the current screen round in the configuration. @@ -675,50 +679,52 @@ int32_t AConfiguration_getUiModeNight(AConfiguration* config); */ void AConfiguration_setUiModeNight(AConfiguration* config, int32_t uiModeNight); -#if __ANDROID_API__ >= 13 /** * Return the current configuration screen width in dp units, or * ACONFIGURATION_SCREEN_WIDTH_DP_ANY if not set. */ -int32_t AConfiguration_getScreenWidthDp(AConfiguration* config) __INTRODUCED_IN(13); +int32_t AConfiguration_getScreenWidthDp(AConfiguration* config); /** * Set the configuration's current screen width in dp units. */ -void AConfiguration_setScreenWidthDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); +void AConfiguration_setScreenWidthDp(AConfiguration* config, int32_t value); /** * Return the current configuration screen height in dp units, or * ACONFIGURATION_SCREEN_HEIGHT_DP_ANY if not set. */ -int32_t AConfiguration_getScreenHeightDp(AConfiguration* config) __INTRODUCED_IN(13); +int32_t AConfiguration_getScreenHeightDp(AConfiguration* config); /** * Set the configuration's current screen width in dp units. */ -void AConfiguration_setScreenHeightDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); +void AConfiguration_setScreenHeightDp(AConfiguration* config, int32_t value); /** * Return the configuration's smallest screen width in dp units, or * ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY if not set. */ -int32_t AConfiguration_getSmallestScreenWidthDp(AConfiguration* config) __INTRODUCED_IN(13); +int32_t AConfiguration_getSmallestScreenWidthDp(AConfiguration* config); /** * Set the configuration's smallest screen width in dp units. */ -void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value) __INTRODUCED_IN(13); -#endif /* __ANDROID_API__ >= 13 */ +void AConfiguration_setSmallestScreenWidthDp(AConfiguration* config, int32_t value); #if __ANDROID_API__ >= 17 /** * Return the configuration's layout direction, or * ACONFIGURATION_LAYOUTDIR_ANY if not set. + * + * Available since API level 17. */ int32_t AConfiguration_getLayoutDirection(AConfiguration* config) __INTRODUCED_IN(17); /** * Set the configuration's layout direction. + * + * Available since API level 17. */ void AConfiguration_setLayoutDirection(AConfiguration* config, int32_t value) __INTRODUCED_IN(17); #endif /* __ANDROID_API__ >= 17 */ diff --git a/include/android/font.h b/include/android/font.h index 435a573f51..1618096d69 100644 --- a/include/android/font.h +++ b/include/android/font.h @@ -16,7 +16,7 @@ /** * @addtogroup Font - * { + * @{ */ /** @@ -96,6 +96,8 @@ struct AFont; /** * Close an AFont. * + * Available since API level 29. + * * \param font a font returned by ASystemFontIterator_next or AFontMatchert_match. * Do nothing if NULL is passed. */ @@ -116,6 +118,8 @@ void AFont_close(AFont* _Nullable font) __INTRODUCED_IN(29); * The font file returned is guaranteed to be opend with O_RDONLY. * Note that the returned pointer is valid until AFont_close() is called for the given font. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return a string of the font file path. */ @@ -184,6 +188,8 @@ const char* _Nonnull AFont_getFontFilePath(const AFont* _Nonnull font) __INTRODU * * For more information about font weight, read [OpenType usWeightClass](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass) * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return a positive integer less than or equal to {@link ASYSTEM_FONT_MAX_WEIGHT} is returned. */ @@ -192,6 +198,8 @@ uint16_t AFont_getWeight(const AFont* _Nonnull font) __INTRODUCED_IN(29); /** * Return true if the current font is italic, otherwise returns false. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return true if italic, otherwise false. */ @@ -204,6 +212,8 @@ bool AFont_isItalic(const AFont* _Nonnull font) __INTRODUCED_IN(29); * * Note that the returned pointer is valid until AFont_close() is called. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return a IETF BCP47 compliant language tag or nullptr if not available. */ @@ -216,6 +226,8 @@ const char* _Nullable AFont_getLocale(const AFont* _Nonnull font) __INTRODUCED_I * returns a non-negative value as an font offset in the collection. This * always returns 0 if the target font file is a regular font. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return a font collection index. */ @@ -247,6 +259,8 @@ size_t AFont_getCollectionIndex(const AFont* _Nonnull font) __INTRODUCED_IN(29); * * For more information about font variation settings, read [Font Variations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/fvar) * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \return a number of font variation settings. */ @@ -258,6 +272,8 @@ size_t AFont_getAxisCount(const AFont* _Nonnull font) __INTRODUCED_IN(29); * * See AFont_getAxisCount for more details. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \param axisIndex an index to the font variation settings. Passing value larger than or * equal to {@link AFont_getAxisCount} is not allowed. @@ -271,6 +287,8 @@ uint32_t AFont_getAxisTag(const AFont* _Nonnull font, uint32_t axisIndex) * * See AFont_getAxisCount for more details. * + * Available since API level 29. + * * \param font a font object. Passing NULL is not allowed. * \param axisIndex an index to the font variation settings. Passing value larger than or * equal to {@link ASYstemFont_getAxisCount} is not allwed. diff --git a/include/android/font_matcher.h b/include/android/font_matcher.h index e286a4c812..d4bd892bf6 100644 --- a/include/android/font_matcher.h +++ b/include/android/font_matcher.h @@ -16,7 +16,7 @@ /** * @addtogroup Font - * { + * @{ */ /** @@ -130,13 +130,17 @@ struct AFontMatcher; */ /** - * Creates a new AFontMatcher object + * Creates a new AFontMatcher object. + * + * Available since API level 29. */ AFontMatcher* _Nonnull AFontMatcher_create() __INTRODUCED_IN(29); /** * Destroy the matcher object. * + * Available since API level 29. + * * \param matcher a matcher object. Passing NULL is not allowed. */ void AFontMatcher_destroy(AFontMatcher* _Nonnull matcher) __INTRODUCED_IN(29); @@ -147,6 +151,8 @@ void AFontMatcher_destroy(AFontMatcher* _Nonnull matcher) __INTRODUCED_IN(29); * If this function is not called, the matcher performs with {@link ASYSTEM_FONT_WEIGHT_NORMAL} * with non-italic style. * + * Available since API level 29. + * * \param matcher a matcher object. Passing NULL is not allowed. * \param weight a font weight value. Only from 0 to 1000 value is valid * \param italic true if italic, otherwise false. @@ -161,6 +167,8 @@ void AFontMatcher_setStyle( * * If this function is not called, the matcher performs with empty locale list. * + * Available since API level 29. + * * \param matcher a matcher object. Passing NULL is not allowed. * \param languageTags a null character terminated comma separated IETF BCP47 compliant language * tags. @@ -174,6 +182,8 @@ void AFontMatcher_setLocales( * * If this function is not called, the matcher performs with {@link AFAMILY_VARIANT_DEFAULT}. * + * Available since API level 29. + * * \param matcher a matcher object. Passing NULL is not allowed. * \param familyVariant Must be one of {@link AFAMILY_VARIANT_DEFAULT}, * {@link AFAMILY_VARIANT_COMPACT} or {@link AFAMILY_VARIANT_ELEGANT} is valid. @@ -190,6 +200,8 @@ void AFontMatcher_setFamilyVariant( * Even if no font can render the given text, this function will return a non-null result for * drawing Tofu character. * + * Available since API level 29. + * * \param matcher a matcher object. Passing NULL is not allowed. * \param familyName a null character terminated font family name * \param text a UTF-16 encoded text buffer to be rendered. Do not pass empty string. diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h index aedf36903d..293e5ac469 100644 --- a/include/android/hardware_buffer_jni.h +++ b/include/android/hardware_buffer_jni.h @@ -42,6 +42,8 @@ __BEGIN_DECLS * that is returned. To keep the AHardwareBuffer live after the Java * HardwareBuffer object got garbage collected, be sure to use AHardwareBuffer_acquire() * to acquire an additional reference. + * + * Available since API level 26. */ AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env, jobject hardwareBufferObj) __INTRODUCED_IN(26); @@ -49,6 +51,8 @@ AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env, /** * Return a new Java HardwareBuffer object that wraps the passed native * AHardwareBuffer object. + * + * Available since API level 26. */ jobject AHardwareBuffer_toHardwareBuffer(JNIEnv* env, AHardwareBuffer* hardwareBuffer) __INTRODUCED_IN(26); diff --git a/include/android/input.h b/include/android/input.h index cfade6c806..ce439c6d75 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -986,10 +986,8 @@ int32_t AMotionEvent_getFlags(const AInputEvent* motion_event); */ int32_t AMotionEvent_getMetaState(const AInputEvent* motion_event); -#if __ANDROID_API__ >= 14 /** Get the button state of all buttons that are pressed. */ -int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event) __INTRODUCED_IN(14); -#endif +int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event); /** * Get a bitfield indicating which edges, if any, were touched by this motion event. @@ -1054,14 +1052,12 @@ size_t AMotionEvent_getPointerCount(const AInputEvent* motion_event); */ int32_t AMotionEvent_getPointerId(const AInputEvent* motion_event, size_t pointer_index); -#if __ANDROID_API__ >= 14 /** * Get the tool type of a pointer for the given pointer index. * The tool type indicates the type of tool used to make contact such as a * finger or stylus, if known. */ -int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index) __INTRODUCED_IN(14); -#endif +int32_t AMotionEvent_getToolType(const AInputEvent* motion_event, size_t pointer_index); /** * Get the original raw X coordinate of this event. @@ -1151,11 +1147,9 @@ float AMotionEvent_getToolMinor(const AInputEvent* motion_event, size_t pointer_ */ float AMotionEvent_getOrientation(const AInputEvent* motion_event, size_t pointer_index); -#if __ANDROID_API__ >= 13 /** Get the value of the request axis for the given pointer index. */ float AMotionEvent_getAxisValue(const AInputEvent* motion_event, - int32_t axis, size_t pointer_index) __INTRODUCED_IN(13); -#endif + int32_t axis, size_t pointer_index); /** * Get the number of historical points in this event. These are movements that @@ -1286,14 +1280,12 @@ float AMotionEvent_getHistoricalToolMinor(const AInputEvent* motion_event, size_ float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, size_t pointer_index, size_t history_index); -#if __ANDROID_API__ >= 13 /** * Get the historical value of the request axis for the given pointer index * that occurred between this event and the previous motion event. */ float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, - int32_t axis, size_t pointer_index, size_t history_index) __INTRODUCED_IN(13); -#endif + int32_t axis, size_t pointer_index, size_t history_index); struct AInputQueue; diff --git a/include/android/multinetwork.h b/include/android/multinetwork.h index d31d1f122f..59b1deb595 100644 --- a/include/android/multinetwork.h +++ b/include/android/multinetwork.h @@ -69,6 +69,7 @@ typedef uint64_t net_handle_t; * * This is the equivalent of: [android.net.Network#bindSocket()](https://developer.android.com/reference/android/net/Network.html#bindSocket(java.net.Socket)) * + * Available since API level 23. */ int android_setsocknetwork(net_handle_t network, int fd) __INTRODUCED_IN(23); @@ -86,6 +87,7 @@ int android_setsocknetwork(net_handle_t network, int fd) __INTRODUCED_IN(23); * * This is the equivalent of: [android.net.ConnectivityManager#setProcessDefaultNetwork()](https://developer.android.com/reference/android/net/ConnectivityManager.html#setProcessDefaultNetwork(android.net.Network)) * + * Available since API level 23. */ int android_setprocnetwork(net_handle_t network) __INTRODUCED_IN(23); @@ -103,6 +105,7 @@ int android_setprocnetwork(net_handle_t network) __INTRODUCED_IN(23); * * This is the equivalent of: [android.net.Network#getAllByName()](https://developer.android.com/reference/android/net/Network.html#getAllByName(java.lang.String)) * + * Available since API level 23. */ int android_getaddrinfofornetwork(net_handle_t network, const char *node, const char *service, @@ -144,6 +147,8 @@ enum ResNsendFlags : uint32_t { * * Returns a file descriptor to watch for read events, or a negative * POSIX error code (see errno.h) if an immediate error occurs. + * + * Available since API level 29. */ int android_res_nquery(net_handle_t network, const char *dname, int ns_class, int ns_type, uint32_t flags) __INTRODUCED_IN(29); @@ -155,6 +160,8 @@ int android_res_nquery(net_handle_t network, * * Returns a file descriptor to watch for read events, or a negative * POSIX error code (see errno.h) if an immediate error occurs. + * + * Available since API level 29. */ int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen, uint32_t flags) __INTRODUCED_IN(29); @@ -163,6 +170,8 @@ int android_res_nsend(net_handle_t network, * Read a result for the query associated with the |fd| descriptor. * Closes |fd| before returning. * + * Available since 29. + * * Returns: * < 0: negative POSIX error code (see errno.h for possible values). |rcode| is not set. * >= 0: length of |answer|. |rcode| is the resolver return code (e.g., ns_r_nxdomain) @@ -173,6 +182,8 @@ int android_res_nresult(int fd, /** * Attempts to cancel the in-progress query associated with the |nsend_fd| * descriptor. + * + * Available since API level 29. */ void android_res_cancel(int nsend_fd) __INTRODUCED_IN(29); diff --git a/include/android/native_window_jni.h b/include/android/native_window_jni.h index 0c196b9671..3a77ffe86b 100644 --- a/include/android/native_window_jni.h +++ b/include/android/native_window_jni.h @@ -51,6 +51,8 @@ ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface); * the ANativeWindow; maintains it through general Java object's life cycle; * and will automatically release the reference when the Java object gets garbage * collected. + * + * Available since API level 26. */ jobject ANativeWindow_toSurface(JNIEnv* env, ANativeWindow* window) __INTRODUCED_IN(26); #endif diff --git a/include/android/sensor.h b/include/android/sensor.h index e9d5c16d0d..3ebe79fd2e 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -564,6 +564,7 @@ ASensorManager* ASensorManager_getInstance(); * * ASensorManager* sensorManager = ASensorManager_getInstanceForPackage("foo.bar.baz"); * + * Available since API level 26. */ ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName) __INTRODUCED_IN(26); #endif @@ -583,6 +584,8 @@ ASensor const* ASensorManager_getDefaultSensor(ASensorManager* manager, int type /** * Returns the default sensor with the given type and wakeUp properties or NULL if no sensor * of this type and wakeUp properties exists. + * + * Available since API level 21. */ ASensor const* ASensorManager_getDefaultSensorEx(ASensorManager* manager, int type, bool wakeUp) __INTRODUCED_IN(21); #endif @@ -609,6 +612,8 @@ int ASensorManager_destroyEventQueue(ASensorManager* manager, ASensorEventQueue* * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY} to be used * for configuring sensor direct report. * + * Available since API level 26. + * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param fd file descriptor representing a shared memory created by @@ -627,6 +632,8 @@ int ASensorManager_createSharedMemoryDirectChannel(ASensorManager* manager, int * Create a direct channel of {@link ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER} type to be used * for configuring sensor direct report. * + * Available since API level 26. + * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param buffer {@link AHardwareBuffer} instance created by {@link AHardwareBuffer_allocate}. @@ -646,6 +653,8 @@ int ASensorManager_createHardwareBufferDirectChannel( * The buffer used for creating direct channel does not get destroyed with * {@link ASensorManager_destroy} and has to be close or released separately. * + * Available since API level 26. + * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param channelId channel id (a positive integer) returned from @@ -678,6 +687,8 @@ void ASensorManager_destroyDirectChannel(ASensorManager* manager, int channelId) * * ASensorManager_configureDirectReport(manager, sensor, channel_id, ASENSOR_DIRECT_RATE_FAST); * + * Available since API level 26. + * * \param manager the {@link ASensorManager} instance obtained from * {@link ASensorManager_getInstanceForPackage}. * \param sensor a {@link ASensor} to denote which sensor to be operate. It can be NULL if rate @@ -780,7 +791,7 @@ int ASensorEventQueue_hasEvents(ASensorEventQueue* queue); */ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* events, size_t count); -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 /** * Request that {@link ASENSOR_TYPE_ADDITIONAL_INFO} events to be delivered on * the given {@link ASensorEventQueue}. @@ -796,13 +807,15 @@ ssize_t ASensorEventQueue_getEvents(ASensorEventQueue* queue, ASensorEvent* even * {@link AAdditionalInfoEvent#type}, as new values may be defined in the future * and may delivered to the client. * + * Available since API level 29. + * * \param queue {@link ASensorEventQueue} to configure * \param enable true to request {@link ASENSOR_TYPE_ADDITIONAL_INFO} events, * false to stop receiving events * \return 0 on success or a negative error code on failure */ -int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable); -#endif /* __ANDROID_API__ >= __ANDRDOID_API_Q__ */ +int ASensorEventQueue_requestAdditionalInfoEvents(ASensorEventQueue* queue, bool enable) __INTRODUCED_IN(29); +#endif /* __ANDROID_API__ >= 29 */ /*****************************************************************************/ @@ -837,26 +850,36 @@ int ASensor_getMinDelay(ASensor const* sensor); /** * Returns the maximum size of batches for this sensor. Batches will often be * smaller, as the hardware fifo might be used for other sensors. + * + * Available since API level 21. */ int ASensor_getFifoMaxEventCount(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns the hardware batch fifo size reserved to this sensor. + * + * Available since API level 21. */ int ASensor_getFifoReservedEventCount(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns this sensor's string type. + * + * Available since API level 21. */ const char* ASensor_getStringType(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns the reporting mode for this sensor. One of AREPORTING_MODE_* constants. + * + * Available since API level 21. */ int ASensor_getReportingMode(ASensor const* sensor) __INTRODUCED_IN(21); /** * Returns true if this is a wake up sensor, false otherwise. + * + * Available since API level 21. */ bool ASensor_isWakeUpSensor(ASensor const* sensor) __INTRODUCED_IN(21); #endif /* __ANDROID_API__ >= 21 */ @@ -865,6 +888,8 @@ bool ASensor_isWakeUpSensor(ASensor const* sensor) __INTRODUCED_IN(21); /** * Test if sensor supports a certain type of direct channel. * + * Available since API level 26. + * * \param sensor a {@link ASensor} to denote the sensor to be checked. * \param channelType Channel type constant, either * {@ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY} @@ -874,7 +899,9 @@ bool ASensor_isWakeUpSensor(ASensor const* sensor) __INTRODUCED_IN(21); bool ASensor_isDirectChannelTypeSupported(ASensor const* sensor, int channelType) __INTRODUCED_IN(26); /** - * Get the highest direct rate level that a sensor support. + * Get the highest direct rate level that a sensor supports. + * + * Available since API level 26. * * \param sensor a {@link ASensor} to denote the sensor to be checked. * @@ -885,7 +912,7 @@ bool ASensor_isDirectChannelTypeSupported(ASensor const* sensor, int channelType int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor) __INTRODUCED_IN(26); #endif /* __ANDROID_API__ >= 26 */ -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 /** * Returns the sensor's handle. * @@ -899,9 +926,11 @@ int ASensor_getHighestDirectReportRateLevel(ASensor const* sensor) __INTRODUCED_ * It is important to note that the value returned by {@link ASensor_getHandle} is not the same as * the value returned by the Java API {@link android.hardware.Sensor#getId} and no mapping exists * between the values. + * + * Available since API level 29. */ -int ASensor_getHandle(ASensor const* sensor) __INTRODUCED_IN(__ANDROID_API_Q__); -#endif /* __ANDROID_API__ >= ANDROID_API_Q__ */ +int ASensor_getHandle(ASensor const* sensor) __INTRODUCED_IN(29); +#endif /* __ANDROID_API__ >= 29 */ #ifdef __cplusplus }; diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h index 7f5177bde9..6efa4f71cb 100644 --- a/include/android/sharedmem.h +++ b/include/android/sharedmem.h @@ -21,7 +21,7 @@ /** * @file sharedmem.h - * @brief Shared memory buffers that can be shared across process. + * @brief Shared memory buffers that can be shared between processes. */ #ifndef ANDROID_SHARED_MEMORY_H @@ -61,11 +61,15 @@ extern "C" { * * Use close() to release the shared memory region. * + * Use {@link android.os.ParcelFileDescriptor} to pass the file descriptor to + * another process. File descriptors may also be sent to other processes over a Unix domain + * socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and cmsg(3) man pages for more information. + * * Available since API level 26. * * \param name an optional name. * \param size size of the shared memory region - * \return file descriptor that denotes the shared memory; error code on failure. + * \return file descriptor that denotes the shared memory; -1 and sets errno on failure, or -EINVAL if the error is that size was 0. */ int ASharedMemory_create(const char *name, size_t size) __INTRODUCED_IN(26); @@ -109,7 +113,7 @@ size_t ASharedMemory_getSize(int fd) __INTRODUCED_IN(26); * \param fd file descriptor of the shared memory region. * \param prot any bitwise-or'ed combination of PROT_READ, PROT_WRITE, PROT_EXEC denoting * updated access. Note access can only be removed, but not added back. - * \return 0 for success, error code on failure. + * \return 0 for success, -1 and sets errno on failure. */ int ASharedMemory_setProt(int fd, int prot) __INTRODUCED_IN(26); diff --git a/include/android/surface_control.h b/include/android/surface_control.h index ef2ad9998c..90e565359e 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -46,7 +46,7 @@ struct ASurfaceControl; */ typedef struct ASurfaceControl ASurfaceControl; -/* +/** * Creates an ASurfaceControl with either ANativeWindow or an ASurfaceControl as its parent. * |debug_name| is a debug name associated with this surface. It can be used to * identify this surface in the SurfaceFlinger's layer tree. It must not be @@ -54,10 +54,17 @@ typedef struct ASurfaceControl ASurfaceControl; * * The caller takes ownership of the ASurfaceControl returned and must release it * using ASurfaceControl_release below. + * + * Available since API level 29. */ ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name) __INTRODUCED_IN(29); +/** + * See ASurfaceControl_createFromWindow. + * + * Available since API level 29. + */ ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name) __INTRODUCED_IN(29); @@ -65,6 +72,8 @@ ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* deb * Releases the |surface_control| object. After releasing the ASurfaceControl the caller no longer * has ownership of the AsurfaceControl. The surface and it's children may remain on display as long * as their parent remains on display. + * + * Available since API level 29. */ void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29); @@ -79,11 +88,15 @@ typedef struct ASurfaceTransaction ASurfaceTransaction; /** * The caller takes ownership of the transaction and must release it using * ASurfaceControl_delete below. + * + * Available since API level 29. */ ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29); /** * Destroys the |transaction| object. + * + * Available since API level 29. */ void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29); @@ -93,6 +106,8 @@ void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_I * Note that the transaction is guaranteed to be applied atomically. The * transactions which are applied on the same thread are also guaranteed to be * applied in order. + * + * Available since API level 29. */ void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29); @@ -116,6 +131,8 @@ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; * * THREADING * The transaction completed callback can be invoked on any thread. + * + * Available since API level 29. */ typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats) __INTRODUCED_IN(29); @@ -123,6 +140,8 @@ typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactio /** * Returns the timestamp of when the frame was latched by the framework. Once a frame is * latched by the framework, it is presented at the following hardware vsync. + * + * Available since API level 29. */ int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats) __INTRODUCED_IN(29); @@ -131,6 +150,8 @@ int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_ * Returns a sync fence that signals when the transaction has been presented. * The recipient of the callback takes ownership of the fence and is responsible for closing * it. + * + * Available since API level 29. */ int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats) __INTRODUCED_IN(29); @@ -141,6 +162,8 @@ int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface * When the client is done using the array, it must release it by calling * ASurfaceTransactionStats_releaseASurfaceControls. * + * Available since API level 29. + * * |outASurfaceControlsSize| returns the size of the ASurfaceControls array. */ void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats, @@ -150,6 +173,8 @@ void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surf /** * Releases the array of ASurfaceControls that were returned by * ASurfaceTransactionStats_getASurfaceControls. + * + * Available since API level 29. */ void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls) __INTRODUCED_IN(29); @@ -158,6 +183,8 @@ void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_ * Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered * acquired when its acquire_fence_fd has signaled. A buffer cannot be latched or presented until * it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1. + * + * Available since API level 29. */ int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats, ASurfaceControl* surface_control) @@ -180,6 +207,8 @@ int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surfac * * The client must ensure that all pending refs on a buffer are released before attempting to reuse * this buffer, otherwise synchronization errors may occur. + * + * Available since API level 29. */ int ASurfaceTransactionStats_getPreviousReleaseFenceFd( ASurfaceTransactionStats* surface_transaction_stats, @@ -190,6 +219,8 @@ int ASurfaceTransactionStats_getPreviousReleaseFenceFd( * Sets the callback that will be invoked when the updates from this transaction * are presented. For details on the callback semantics and data, see the * comments on the ASurfaceTransaction_OnComplete declaration above. + * + * Available since API level 29. */ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context, ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29); @@ -199,6 +230,8 @@ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* c * Any children of the* reparented |surface_control| will remain children of the |surface_control|. * * The |new_parent| can be null. Surface controls with a null parent do not appear on the display. + * + * Available since API level 29. */ void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, ASurfaceControl* new_parent) @@ -213,6 +246,8 @@ enum { * Updates the visibility of |surface_control|. If show is set to * ASURFACE_TRANSACTION_VISIBILITY_HIDE, the |surface_control| and all surfaces in its subtree will * be hidden. + * + * Available since API level 29. */ void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, int8_t visibility) @@ -224,6 +259,8 @@ void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction, * the same z order is undefined. * * Z orders may be from MIN_INT32 to MAX_INT32. A layer's default z order index is 0. + * + * Available since API level 29. */ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, int32_t z_order) @@ -236,6 +273,8 @@ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction, * * The frameworks takes ownership of the |acquire_fence_fd| passed and is responsible * for closing it. + * + * Available since API level 29. */ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, AHardwareBuffer* buffer, @@ -246,6 +285,8 @@ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction, * ASurfaceControl visible in transparent regions of the surface. Colors |r|, |g|, * and |b| must be within the range that is valid for |dataspace|. |dataspace| and |alpha| * will be the dataspace and alpha set for the background color layer. + * + * Available since API level 29. */ void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, float r, float g, float b, @@ -264,6 +305,8 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction, * |transform| the transform applied after the source rect is applied to the buffer. This parameter * should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_* * enum. + * + * Available since API level 29. */ void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, const ARect& source, @@ -281,6 +324,8 @@ enum { * Updates whether the content for the buffer associated with this surface is * completely opaque. If true, every pixel of content inside the buffer must be * opaque or visual errors can occur. + * + * Available since API level 29. */ void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, @@ -290,6 +335,8 @@ void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction, /** * Updates the region for the content on this surface updated in this * transaction. If unspecified, the complete surface is assumed to be damaged. + * + * Available since API level 29. */ void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, const ARect rects[], @@ -304,6 +351,8 @@ void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction, * * If an earlier transaction has a desired present time of x, and a later transaction has a desired * present time that is before x, the later transaction will not preempt the earlier transaction. + * + * Available since API level 29. */ void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction, int64_t desiredPresentTime) __INTRODUCED_IN(29); @@ -312,6 +361,8 @@ void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction, * Sets the alpha for the buffer. It uses a premultiplied blending. * * The |alpha| must be between 0.0 and 1.0. + * + * Available since API level 29. */ void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, float alpha) @@ -321,6 +372,8 @@ void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction, * Sets the data space of the surface_control's buffers. * * If no data space is set, the surface control defaults to ADATASPACE_SRGB. + * + * Available since API level 29. */ void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, ADataSpace data_space) @@ -331,6 +384,8 @@ void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction, * * When |metadata| is set to null, the framework does not use any smpte2086 metadata when rendering * the surface's buffer. + * + * Available since API level 29. */ void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, @@ -342,6 +397,8 @@ void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transacti * * When |metadata| is set to null, the framework does not use any cta861.3 metadata when rendering * the surface's buffer. + * + * Available since API level 29. */ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, diff --git a/include/android/surface_texture.h b/include/android/surface_texture.h index 540d23a4c7..dde7eaa0b6 100644 --- a/include/android/surface_texture.h +++ b/include/android/surface_texture.h @@ -65,6 +65,9 @@ typedef struct ASurfaceTexture ASurfaceTexture; * Release the reference to the native ASurfaceTexture acquired with * ASurfaceTexture_fromSurfaceTexture(). * Failing to do so will result in leaked memory and graphic resources. + * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() */ void ASurfaceTexture_release(ASurfaceTexture* st) __INTRODUCED_IN(28); @@ -73,6 +76,8 @@ void ASurfaceTexture_release(ASurfaceTexture* st) __INTRODUCED_IN(28); * Returns a reference to an ANativeWindow (i.e. the Producer) for this SurfaceTexture. * This is equivalent to Java's: Surface sur = new Surface(surfaceTexture); * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * @return A reference to an ANativeWindow. This reference MUST BE released when no longer needed * using ANativeWindow_release(). Failing to do so will result in leaked resources. nullptr is @@ -90,6 +95,8 @@ ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) __INTRO * contexts. Note, however, that the image contents are only accessible from one OpenGL ES * context at a time. * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \param texName The name of the OpenGL ES texture that will be created. This texture name * must be unusued in the OpenGL ES context that is current on the calling thread. @@ -108,6 +115,8 @@ int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t texName) __I * contexts. Note, however, that the image contents are only accessible from one OpenGL ES * context at a time. * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \return 0 on success, negative posix error code otherwise (see <errno.h>) */ @@ -118,6 +127,8 @@ int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) __INTRODUCED_IN(28) * called while the OpenGL ES context that owns the texture is current on the calling thread. * It will implicitly bind its texture to the GL_TEXTURE_EXTERNAL_OES texture target. * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \return 0 on success, negative posix error code otherwise (see <errno.h>) */ @@ -135,6 +146,8 @@ int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) __INTRODUCED_IN(28); * The matrix is stored in column-major order so that it may be passed directly to OpenGL ES via * the glLoadMatrixf or glUniformMatrix4fv functions. * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() * \param mtx the array into which the 4x4 matrix will be stored. The array must have exactly * 16 elements. @@ -156,6 +169,8 @@ void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) __IN * For EGL/Vulkan producers, this timestamp is the desired present time set with the * EGL_ANDROID_presentation_time or VK_GOOGLE_display_timing extensions * + * Available since API level 28. + * * \param st A ASurfaceTexture reference acquired with ASurfaceTexture_fromSurfaceTexture() */ int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) __INTRODUCED_IN(28); diff --git a/include/android/surface_texture_jni.h b/include/android/surface_texture_jni.h index b0e1edd590..2266d541f6 100644 --- a/include/android/surface_texture_jni.h +++ b/include/android/surface_texture_jni.h @@ -32,6 +32,8 @@ __BEGIN_DECLS +#if __ANDROID_API__ >= 28 + /** * Get a reference to the native ASurfaceTexture from the corresponding java object. * @@ -40,13 +42,17 @@ __BEGIN_DECLS * properly once the Java object gets finalized. * However, this will not result in program termination. * + * Available since API level 28. + * * \param env JNI environment * \param surfacetexture Instance of Java SurfaceTexture object * \return native ASurfaceTexture reference or nullptr if the java object is not a SurfaceTexture. * The returned reference MUST BE released when it's no longer needed using * ASurfaceTexture_release(). */ -ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture); +ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture) __INTRODUCED_IN(28); + +#endif __END_DECLS diff --git a/include/android/system_fonts.h b/include/android/system_fonts.h index dde9055c7a..6fd7d2c0ab 100644 --- a/include/android/system_fonts.h +++ b/include/android/system_fonts.h @@ -16,7 +16,7 @@ /** * @addtogroup Font - * { + * @{ */ /** @@ -102,6 +102,8 @@ struct ASystemFontIterator; * * Use ASystemFont_close() to close the iterator. * + * Available since API level 29. + * * \return a pointer for a newly allocated iterator, nullptr on failure. */ ASystemFontIterator* _Nullable ASystemFontIterator_open() __INTRODUCED_IN(29); @@ -109,6 +111,8 @@ ASystemFontIterator* _Nullable ASystemFontIterator_open() __INTRODUCED_IN(29); /** * Close an opened system font iterator, freeing any related resources. * + * Available since API level 29. + * * \param iterator a pointer of an iterator for the system fonts. Do nothing if NULL is passed. */ void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTRODUCED_IN(29); @@ -116,6 +120,8 @@ void ASystemFontIterator_close(ASystemFontIterator* _Nullable iterator) __INTROD /** * Move to the next system font. * + * Available since API level 29. + * * \param iterator an iterator for the system fonts. Passing NULL is not allowed. * \return a font. If no more font is available, returns nullptr. You need to release the returned * font by ASystemFont_close when it is no longer needed. diff --git a/include/android/trace.h b/include/android/trace.h index bb7ff28f79..d59690ab2e 100644 --- a/include/android/trace.h +++ b/include/android/trace.h @@ -74,7 +74,7 @@ void ATrace_endSection() __INTRODUCED_IN(23); #endif /* __ANDROID_API__ >= 23 */ -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 /** * Writes a trace message to indicate that a given section of code has @@ -83,6 +83,8 @@ void ATrace_endSection() __INTRODUCED_IN(23); * asynchronous events do not need to be nested. The name and cookie used to * begin an event must be used to end it. * + * Available since API level 29. + * * \param sectionName The method name to appear in the trace. * \param cookie Unique identifier for distinguishing simultaneous events */ @@ -93,6 +95,8 @@ void ATrace_beginAsyncSection(const char* sectionName, int32_t cookie) __INTRODU * Must be called exactly once for each call to {@link ATrace_beginAsyncSection} * using the same name and cookie. * + * Available since API level 29. + * * \param methodName The method name to appear in the trace. * \param cookie Unique identifier for distinguishing simultaneous events */ @@ -101,6 +105,8 @@ void ATrace_endAsyncSection(const char* sectionName, int32_t cookie) __INTRODUCE /** * Writes trace message to indicate the value of a given counter. * + * Available since API level 29. + * * \param counterName The counter name to appear in the trace. * \param counterValue The counter value. */ diff --git a/include/audiomanager/OWNERS b/include/audiomanager/OWNERS new file mode 100644 index 0000000000..2bd527cc3f --- /dev/null +++ b/include/audiomanager/OWNERS @@ -0,0 +1,2 @@ +elaurent@google.com +jmtrivi@google.com diff --git a/include/binder b/include/binder deleted file mode 120000 index 35a022ab67..0000000000 --- a/include/binder +++ /dev/null @@ -1 +0,0 @@ -../libs/binder/include/binder/
\ No newline at end of file diff --git a/include/binder/Binder.h b/include/binder/Binder.h new file mode 120000 index 0000000000..0fc6db741e --- /dev/null +++ b/include/binder/Binder.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/Binder.h
\ No newline at end of file diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h new file mode 120000 index 0000000000..370b260577 --- /dev/null +++ b/include/binder/BinderService.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/BinderService.h
\ No newline at end of file diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h new file mode 120000 index 0000000000..93a6219206 --- /dev/null +++ b/include/binder/IBinder.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/IBinder.h
\ No newline at end of file diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h new file mode 120000 index 0000000000..857987819f --- /dev/null +++ b/include/binder/IInterface.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/IInterface.h
\ No newline at end of file diff --git a/include/binder/IMemory.h b/include/binder/IMemory.h new file mode 120000 index 0000000000..5171c08d8e --- /dev/null +++ b/include/binder/IMemory.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/IMemory.h
\ No newline at end of file diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h new file mode 120000 index 0000000000..ecd4f815eb --- /dev/null +++ b/include/binder/IPCThreadState.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/IPCThreadState.h
\ No newline at end of file diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h new file mode 120000 index 0000000000..33d18ccd16 --- /dev/null +++ b/include/binder/IServiceManager.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/IServiceManager.h
\ No newline at end of file diff --git a/include/binder/MemoryDealer.h b/include/binder/MemoryDealer.h new file mode 120000 index 0000000000..71881fbfff --- /dev/null +++ b/include/binder/MemoryDealer.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/MemoryDealer.h
\ No newline at end of file diff --git a/include/binder/MemoryHeapBase.h b/include/binder/MemoryHeapBase.h new file mode 120000 index 0000000000..8fb51ccef6 --- /dev/null +++ b/include/binder/MemoryHeapBase.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/MemoryHeapBase.h
\ No newline at end of file diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h new file mode 120000 index 0000000000..23492be087 --- /dev/null +++ b/include/binder/Parcel.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/Parcel.h
\ No newline at end of file diff --git a/include/binder/ParcelFileDescriptor.h b/include/binder/ParcelFileDescriptor.h new file mode 120000 index 0000000000..777bd49b05 --- /dev/null +++ b/include/binder/ParcelFileDescriptor.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/ParcelFileDescriptor.h
\ No newline at end of file diff --git a/include/binder/Parcelable.h b/include/binder/Parcelable.h new file mode 120000 index 0000000000..438e223075 --- /dev/null +++ b/include/binder/Parcelable.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/Parcelable.h
\ No newline at end of file diff --git a/include/binder/PermissionCache.h b/include/binder/PermissionCache.h new file mode 120000 index 0000000000..e910c12157 --- /dev/null +++ b/include/binder/PermissionCache.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/PermissionCache.h
\ No newline at end of file diff --git a/include/binder/PersistableBundle.h b/include/binder/PersistableBundle.h new file mode 120000 index 0000000000..785f2b435c --- /dev/null +++ b/include/binder/PersistableBundle.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/PersistableBundle.h
\ No newline at end of file diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h new file mode 120000 index 0000000000..4cbe7a5b8f --- /dev/null +++ b/include/binder/ProcessState.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/ProcessState.h
\ No newline at end of file diff --git a/include/binder/Stability.h b/include/binder/Stability.h new file mode 120000 index 0000000000..9b431d2e28 --- /dev/null +++ b/include/binder/Stability.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/Stability.h
\ No newline at end of file diff --git a/include/binder/Status.h b/include/binder/Status.h new file mode 120000 index 0000000000..ccb994e252 --- /dev/null +++ b/include/binder/Status.h @@ -0,0 +1 @@ +../../libs/binder/include/binder/Status.h
\ No newline at end of file diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp new file mode 100644 index 0000000000..8ac044c091 --- /dev/null +++ b/libs/adbd_auth/Android.bp @@ -0,0 +1,47 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library { + name: "libadbd_auth", + cflags: [ + "-Wall", + "-Wextra", + "-Wthread-safety", + "-Werror", + ], + stl: "libc++_static", + + srcs: ["adbd_auth.cpp"], + export_include_dirs: ["include"], + + version_script: "libadbd_auth.map.txt", + stubs: { + versions: ["1"], + symbol_file: "libadbd_auth.map.txt", + }, + + host_supported: true, + recovery_available: true, + target: { + darwin: { + enabled: false, + } + }, + + static_libs: [ + "libbase", + "libcutils", + "liblog", + ], +} diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp new file mode 100644 index 0000000000..a9c23110c9 --- /dev/null +++ b/libs/adbd_auth/adbd_auth.cpp @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION + +#include "include/adbd_auth.h" + +#include <inttypes.h> +#include <sys/epoll.h> +#include <sys/eventfd.h> +#include <sys/uio.h> + +#include <chrono> +#include <deque> +#include <string> +#include <string_view> +#include <tuple> +#include <unordered_map> +#include <utility> +#include <variant> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/macros.h> +#include <android-base/strings.h> +#include <android-base/thread_annotations.h> +#include <android-base/unique_fd.h> +#include <cutils/sockets.h> + +using android::base::unique_fd; + +struct AdbdAuthPacketAuthenticated { + std::string public_key; +}; + +struct AdbdAuthPacketDisconnected { + std::string public_key; +}; + +struct AdbdAuthPacketRequestAuthorization { + std::string public_key; +}; + +using AdbdAuthPacket = std::variant<AdbdAuthPacketAuthenticated, AdbdAuthPacketDisconnected, + AdbdAuthPacketRequestAuthorization>; + +struct AdbdAuthContext { + static constexpr uint64_t kEpollConstSocket = 0; + static constexpr uint64_t kEpollConstEventFd = 1; + static constexpr uint64_t kEpollConstFramework = 2; + +public: + explicit AdbdAuthContext(AdbdAuthCallbacksV1* callbacks) : next_id_(0), callbacks_(*callbacks) { + epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC)); + if (epoll_fd_ == -1) { + PLOG(FATAL) << "failed to create epoll fd"; + } + + event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + if (event_fd_ == -1) { + PLOG(FATAL) << "failed to create eventfd"; + } + + sock_fd_.reset(android_get_control_socket("adbd")); + if (sock_fd_ == -1) { + PLOG(ERROR) << "failed to get adbd authentication socket"; + } else { + if (fcntl(sock_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) { + PLOG(FATAL) << "failed to make adbd authentication socket cloexec"; + } + + if (fcntl(sock_fd_.get(), F_SETFL, O_NONBLOCK) != 0) { + PLOG(FATAL) << "failed to make adbd authentication socket nonblocking"; + } + + if (listen(sock_fd_.get(), 4) != 0) { + PLOG(FATAL) << "failed to listen on adbd authentication socket"; + } + } + } + + AdbdAuthContext(const AdbdAuthContext& copy) = delete; + AdbdAuthContext(AdbdAuthContext&& move) = delete; + AdbdAuthContext& operator=(const AdbdAuthContext& copy) = delete; + AdbdAuthContext& operator=(AdbdAuthContext&& move) = delete; + + uint64_t NextId() { return next_id_++; } + + void DispatchPendingPrompt() REQUIRES(mutex_) { + if (dispatched_prompt_) { + LOG(INFO) << "adbd_auth: prompt currently pending, skipping"; + return; + } + + if (pending_prompts_.empty()) { + LOG(INFO) << "adbd_auth: no prompts to send"; + return; + } + + LOG(INFO) << "adbd_auth: prompting user for adb authentication"; + auto [id, public_key, arg] = std::move(pending_prompts_.front()); + pending_prompts_.pop_front(); + + this->output_queue_.emplace_back( + AdbdAuthPacketRequestAuthorization{.public_key = public_key}); + + Interrupt(); + dispatched_prompt_ = std::make_tuple(id, public_key, arg); + } + + void UpdateFrameworkWritable() REQUIRES(mutex_) { + // This might result in redundant calls to EPOLL_CTL_MOD if, for example, we get notified + // at the same time as a framework connection, but that's unlikely and this doesn't need to + // be fast anyway. + if (framework_fd_ != -1) { + struct epoll_event event; + event.events = EPOLLIN; + if (!output_queue_.empty()) { + LOG(INFO) << "marking framework writable"; + event.events |= EPOLLOUT; + } + event.data.u64 = kEpollConstFramework; + CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_MOD, framework_fd_.get(), &event)); + } + } + + void ReplaceFrameworkFd(unique_fd new_fd) REQUIRES(mutex_) { + LOG(INFO) << "received new framework fd " << new_fd.get() + << " (current = " << framework_fd_.get() << ")"; + + // If we already had a framework fd, clean up after ourselves. + if (framework_fd_ != -1) { + output_queue_.clear(); + dispatched_prompt_.reset(); + CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, framework_fd_.get(), nullptr)); + framework_fd_.reset(); + } + + if (new_fd != -1) { + struct epoll_event event; + event.events = EPOLLIN; + if (!output_queue_.empty()) { + LOG(INFO) << "marking framework writable"; + event.events |= EPOLLOUT; + } + event.data.u64 = kEpollConstFramework; + CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, new_fd.get(), &event)); + framework_fd_ = std::move(new_fd); + } + } + + void HandlePacket(std::string_view packet) REQUIRES(mutex_) { + LOG(INFO) << "received packet: " << packet; + + if (packet.length() < 2) { + LOG(ERROR) << "received packet of invalid length"; + ReplaceFrameworkFd(unique_fd()); + } + + if (packet[0] == 'O' && packet[1] == 'K') { + CHECK(this->dispatched_prompt_.has_value()); + auto& [id, key, arg] = *this->dispatched_prompt_; + keys_.emplace(id, std::move(key)); + + this->callbacks_.key_authorized(arg, id); + this->dispatched_prompt_ = std::nullopt; + + // We need to dispatch pending prompts here upon success as well, + // since we might have multiple queued prompts. + DispatchPendingPrompt(); + } else if (packet[0] == 'N' && packet[1] == 'O') { + CHECK_EQ(2UL, packet.length()); + // TODO: Do we want a callback if the key is denied? + this->dispatched_prompt_ = std::nullopt; + DispatchPendingPrompt(); + } else { + LOG(ERROR) << "unhandled packet: " << packet; + ReplaceFrameworkFd(unique_fd()); + } + } + + bool SendPacket() REQUIRES(mutex_) { + if (output_queue_.empty()) { + return false; + } + + CHECK_NE(-1, framework_fd_.get()); + + auto& packet = output_queue_.front(); + struct iovec iovs[2]; + if (auto* p = std::get_if<AdbdAuthPacketAuthenticated>(&packet)) { + iovs[0].iov_base = const_cast<char*>("CK"); + iovs[0].iov_len = 2; + iovs[1].iov_base = p->public_key.data(); + iovs[1].iov_len = p->public_key.size(); + } else if (auto* p = std::get_if<AdbdAuthPacketDisconnected>(&packet)) { + iovs[0].iov_base = const_cast<char*>("DC"); + iovs[0].iov_len = 2; + iovs[1].iov_base = p->public_key.data(); + iovs[1].iov_len = p->public_key.size(); + } else if (auto* p = std::get_if<AdbdAuthPacketRequestAuthorization>(&packet)) { + iovs[0].iov_base = const_cast<char*>("PK"); + iovs[0].iov_len = 2; + iovs[1].iov_base = p->public_key.data(); + iovs[1].iov_len = p->public_key.size(); + } else { + LOG(FATAL) << "unhandled packet type?"; + } + + output_queue_.pop_front(); + + ssize_t rc = writev(framework_fd_.get(), iovs, 2); + if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { + PLOG(ERROR) << "failed to write to framework fd"; + ReplaceFrameworkFd(unique_fd()); + return false; + } + + return true; + } + + void Run() { + if (sock_fd_ == -1) { + LOG(ERROR) << "adbd authentication socket unavailable, disabling user prompts"; + } else { + struct epoll_event event; + event.events = EPOLLIN; + event.data.u64 = kEpollConstSocket; + CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, sock_fd_.get(), &event)); + } + + { + struct epoll_event event; + event.events = EPOLLIN; + event.data.u64 = kEpollConstEventFd; + CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, event_fd_.get(), &event)); + } + + while (true) { + struct epoll_event events[3]; + int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 3, -1)); + if (rc == -1) { + PLOG(FATAL) << "epoll_wait failed"; + } else if (rc == 0) { + LOG(FATAL) << "epoll_wait returned 0"; + } + + bool restart = false; + for (int i = 0; i < rc; ++i) { + if (restart) { + break; + } + + struct epoll_event& event = events[i]; + switch (event.data.u64) { + case kEpollConstSocket: { + unique_fd new_framework_fd(accept4(sock_fd_.get(), nullptr, nullptr, + SOCK_CLOEXEC | SOCK_NONBLOCK)); + if (new_framework_fd == -1) { + PLOG(FATAL) << "failed to accept framework fd"; + } + + LOG(INFO) << "adbd_auth: received a new framework connection"; + std::lock_guard<std::mutex> lock(mutex_); + ReplaceFrameworkFd(std::move(new_framework_fd)); + + // Stop iterating over events: one of the later ones might be the old + // framework fd. + restart = false; + break; + } + + case kEpollConstEventFd: { + // We were woken up to write something. + uint64_t dummy; + int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy))); + if (rc != 8) { + PLOG(FATAL) << "failed to read from eventfd (rc = " << rc << ")"; + } + + std::lock_guard<std::mutex> lock(mutex_); + UpdateFrameworkWritable(); + break; + } + + case kEpollConstFramework: { + char buf[4096]; + if (event.events & EPOLLIN) { + int rc = TEMP_FAILURE_RETRY(read(framework_fd_.get(), buf, sizeof(buf))); + if (rc == -1) { + LOG(FATAL) << "failed to read from framework fd"; + } else if (rc == 0) { + LOG(INFO) << "hit EOF on framework fd"; + std::lock_guard<std::mutex> lock(mutex_); + ReplaceFrameworkFd(unique_fd()); + } else { + std::lock_guard<std::mutex> lock(mutex_); + HandlePacket(std::string_view(buf, rc)); + } + } + + if (event.events & EPOLLOUT) { + std::lock_guard<std::mutex> lock(mutex_); + while (SendPacket()) { + continue; + } + UpdateFrameworkWritable(); + } + + break; + } + } + } + } + } + + static constexpr const char* key_paths[] = {"/adb_keys", "/data/misc/adb/adb_keys"}; + void IteratePublicKeys(bool (*callback)(const char*, size_t, void*), void* arg) { + for (const auto& path : key_paths) { + if (access(path, R_OK) == 0) { + LOG(INFO) << "Loading keys from " << path; + std::string content; + if (!android::base::ReadFileToString(path, &content)) { + PLOG(ERROR) << "Couldn't read " << path; + continue; + } + for (const auto& line : android::base::Split(content, "\n")) { + if (!callback(line.data(), line.size(), arg)) { + return; + } + } + } + } + } + + uint64_t PromptUser(std::string_view public_key, void* arg) EXCLUDES(mutex_) { + uint64_t id = NextId(); + + std::lock_guard<std::mutex> lock(mutex_); + pending_prompts_.emplace_back(id, public_key, arg); + DispatchPendingPrompt(); + return id; + } + + uint64_t NotifyAuthenticated(std::string_view public_key) EXCLUDES(mutex_) { + uint64_t id = NextId(); + std::lock_guard<std::mutex> lock(mutex_); + keys_.emplace(id, public_key); + output_queue_.emplace_back( + AdbdAuthPacketDisconnected{.public_key = std::string(public_key)}); + return id; + } + + void NotifyDisconnected(uint64_t id) EXCLUDES(mutex_) { + std::lock_guard<std::mutex> lock(mutex_); + auto it = keys_.find(id); + if (it == keys_.end()) { + LOG(DEBUG) << "couldn't find public key to notify disconnection, skipping"; + return; + } + output_queue_.emplace_back(AdbdAuthPacketDisconnected{.public_key = std::move(it->second)}); + keys_.erase(it); + } + + // Interrupt the worker thread to do some work. + void Interrupt() { + uint64_t value = 1; + ssize_t rc = write(event_fd_.get(), &value, sizeof(value)); + if (rc == -1) { + PLOG(FATAL) << "write to eventfd failed"; + } else if (rc != sizeof(value)) { + LOG(FATAL) << "write to eventfd returned short (" << rc << ")"; + } + } + + unique_fd epoll_fd_; + unique_fd event_fd_; + unique_fd sock_fd_; + unique_fd framework_fd_; + + std::atomic<uint64_t> next_id_; + AdbdAuthCallbacksV1 callbacks_; + + std::mutex mutex_; + std::unordered_map<uint64_t, std::string> keys_ GUARDED_BY(mutex_); + + // We keep two separate queues: one to handle backpressure from the socket (output_queue_) + // and one to make sure we only dispatch one authrequest at a time (pending_prompts_). + std::deque<AdbdAuthPacket> output_queue_; + + std::optional<std::tuple<uint64_t, std::string, void*>> dispatched_prompt_ GUARDED_BY(mutex_); + std::deque<std::tuple<uint64_t, std::string, void*>> pending_prompts_ GUARDED_BY(mutex_); +}; + +AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks) { + if (callbacks->version != 1) { + LOG(ERROR) << "received unknown AdbdAuthCallbacks version " << callbacks->version; + return nullptr; + } + + return new AdbdAuthContext(&callbacks->callbacks.v1); +} + +void adbd_auth_delete(AdbdAuthContext* ctx) { + delete ctx; +} + +void adbd_auth_run(AdbdAuthContext* ctx) { + return ctx->Run(); +} + +void adbd_auth_get_public_keys(AdbdAuthContext* ctx, + bool (*callback)(const char* public_key, size_t len, void* arg), + void* arg) { + ctx->IteratePublicKeys(callback, arg); +} + +uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len) { + return ctx->NotifyAuthenticated(std::string_view(public_key, len)); +} + +void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id) { + return ctx->NotifyDisconnected(id); +} + +void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, + void* arg) { + ctx->PromptUser(std::string_view(public_key, len), arg); +} + +bool adbd_auth_supports_feature(AdbdAuthFeature) { + return false; +} diff --git a/libs/adbd_auth/include/adbd_auth.h b/libs/adbd_auth/include/adbd_auth.h new file mode 100644 index 0000000000..b7c1cb88cc --- /dev/null +++ b/libs/adbd_auth/include/adbd_auth.h @@ -0,0 +1,65 @@ +#pragma once + +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdbool.h> +#include <stdint.h> +#include <sys/types.h> + +extern "C" { + +struct AdbdAuthCallbacksV1 { + // Callback for a successful user authorization. + void (*key_authorized)(void* arg, uint64_t id); +}; + +struct AdbdAuthCallbacks { + uint32_t version; + union { + AdbdAuthCallbacksV1 v1; + } callbacks; +}; + +struct AdbdAuthContext; + +AdbdAuthContext* adbd_auth_new(AdbdAuthCallbacks* callbacks); +void adbd_auth_delete(AdbdAuthContext* ctx); + +void adbd_auth_run(AdbdAuthContext* ctx); + +// Iterate through the list of authorized public keys. +// Return false from the callback to stop iteration. +void adbd_auth_get_public_keys(AdbdAuthContext* ctx, + bool (*callback)(const char* public_key, size_t len, void* arg), + void* arg); + +// Let system_server know that a key has been successfully used for authentication. +uint64_t adbd_auth_notify_auth(AdbdAuthContext* ctx, const char* public_key, size_t len); + +// Let system_server know that a connection has been closed. +void adbd_auth_notify_disconnect(AdbdAuthContext* ctx, uint64_t id); + +// Prompt the user to authorize a public key. +// When this happens, a callback will be run on the auth thread with the result. +void adbd_auth_prompt_user(AdbdAuthContext* ctx, const char* public_key, size_t len, void* arg); + +enum AdbdAuthFeature { +}; + +bool adbd_auth_supports_feature(AdbdAuthFeature f); + +} diff --git a/libs/adbd_auth/libadbd_auth.map.txt b/libs/adbd_auth/libadbd_auth.map.txt new file mode 100644 index 0000000000..d01233c960 --- /dev/null +++ b/libs/adbd_auth/libadbd_auth.map.txt @@ -0,0 +1,13 @@ +LIBADBD_AUTH { + global: + adbd_auth_new; # apex + adbd_auth_delete; # apex + adbd_auth_run; # apex + adbd_auth_get_public_keys; #apex + adbd_auth_notify_auth; # apex + adbd_auth_notify_disconnect; # apex + adbd_auth_prompt_user; # apex + adbd_auth_supports_feature; # apex + local: + *; +}; diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp index 9284acbff3..09a5f39130 100644 --- a/libs/android_runtime_lazy/Android.bp +++ b/libs/android_runtime_lazy/Android.bp @@ -34,6 +34,7 @@ cc_library { name: "libandroid_runtime_lazy", vendor_available: true, double_loadable: true, + host_supported: true, cflags: [ "-Wall", @@ -51,10 +52,6 @@ cc_library { "libutils", ], - required: [ - "libandroid_runtime", - ], - export_include_dirs: [ "include", ], diff --git a/libs/android_runtime_lazy/android_runtime_lazy.cpp b/libs/android_runtime_lazy/android_runtime_lazy.cpp index 98d8e8a511..8062be676d 100644 --- a/libs/android_runtime_lazy/android_runtime_lazy.cpp +++ b/libs/android_runtime_lazy/android_runtime_lazy.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "ANDROID_RUNTIME_LAZY" #include "android_runtime/AndroidRuntime.h" +#include "android_os_Parcel.h" #include "android_util_Binder.h" #include <dlfcn.h> @@ -28,12 +29,18 @@ namespace { std::once_flag loadFlag; typedef JNIEnv* (*getJNIEnv_t)(); + +// android_util_Binder.h typedef sp<IBinder> (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj); typedef jobject (*javaObjectForIBinder_t)(JNIEnv* env, const sp<IBinder>& val); +// android_os_Parcel.h +typedef Parcel* (*parcelForJavaObject_t)(JNIEnv* env, jobject obj); + getJNIEnv_t _getJNIEnv; ibinderForJavaObject_t _ibinderForJavaObject; javaObjectForIBinder_t _javaObjectForIBinder; +parcelForJavaObject_t _parcelForJavaObject; void load() { std::call_once(loadFlag, []() { @@ -64,6 +71,13 @@ void load() { ALOGW("Could not find javaObjectForIBinder."); // no return } + + _parcelForJavaObject = reinterpret_cast<parcelForJavaObject_t>( + dlsym(handle, "_ZN7android19parcelForJavaObjectEP7_JNIEnvP8_jobject")); + if (_parcelForJavaObject == nullptr) { + ALOGW("Could not find parcelForJavaObject."); + // no return + } }); } @@ -95,4 +109,12 @@ jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) { return _javaObjectForIBinder(env, val); } +Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) { + load(); + if (_parcelForJavaObject == nullptr) { + return nullptr; + } + return _parcelForJavaObject(env, obj); +} + } // namespace android diff --git a/cmds/installd/art_helper/art_image_values.h b/libs/android_runtime_lazy/include/android_os_Parcel.h index 20c44c953f..19b094d02a 100644 --- a/cmds/installd/art_helper/art_image_values.h +++ b/libs/android_runtime_lazy/include/android_os_Parcel.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,21 +14,17 @@ * limitations under the License. */ -#ifndef FRAMEWORKS_NATIVE_CMDS_INSTALLD_ART_HELPER_ART_IMAGE_VALUES_H -#define FRAMEWORKS_NATIVE_CMDS_INSTALLD_ART_HELPER_ART_IMAGE_VALUES_H +#pragma once -#include <cstdint> +#include <binder/Parcel.h> +#include "jni.h" namespace android { -namespace installd { -namespace art { -uint32_t GetImageBaseAddress(); -int32_t GetImageMinBaseAddressDelta(); -int32_t GetImageMaxBaseAddressDelta(); +// The name of this file is same with the file in frameworks/base/core/jni/ +// This is intentional to make the client use these exported functions +// in the same way with the original. -} // namespace art -} // namespace installd -} // namespace android +Parcel* parcelForJavaObject(JNIEnv* env, jobject obj); -#endif // FRAMEWORKS_NATIVE_CMDS_INSTALLD_ART_HELPER_ART_IMAGE_VALUES_H +} // namespace android diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index ad8287c203..2518b1427d 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -13,13 +13,18 @@ // limitations under the License. ndk_headers { - name: "libarect_headers", + name: "libarect_headers_for_ndk", from: "include/android", to: "android", srcs: ["include/android/*.h"], license: "NOTICE", } +cc_library_headers { + name: "libarect_headers", + export_include_dirs: ["include"], +} + cc_library_static { name: "libarect", host_supported: true, diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp index 49a94146db..5e4c98fc7a 100644 --- a/libs/binder/ActivityManager.cpp +++ b/libs/binder/ActivityManager.cpp @@ -114,4 +114,4 @@ status_t ActivityManager::unlinkToDeath(const sp<IBinder::DeathRecipient>& recip return INVALID_OPERATION; } -}; // namespace android +} // namespace android diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index aedf6b0d18..bc541f4d31 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -16,6 +16,8 @@ cc_library_headers { name: "libbinder_headers", export_include_dirs: ["include"], vendor_available: true, + host_supported: true, + header_libs: [ "libbase_headers", "libcutils_headers", @@ -28,7 +30,28 @@ cc_library_headers { ], } -cc_library_shared { +// These interfaces are android-specific implementation unrelated to binder +// transport itself and should be moved to AIDL or in domain-specific libs. +// +// Currently, these are only on system android (not vendor, not host) +libbinder_device_interface_sources = [ + "ActivityManager.cpp", + "AppOpsManager.cpp", + "IActivityManager.cpp", + "IAppOpsCallback.cpp", + "IAppOpsService.cpp", + "IBatteryStats.cpp", + "IMediaResourceMonitor.cpp", + "IPermissionController.cpp", + "IProcessInfoService.cpp", + "IUidObserver.cpp", + "PermissionCache.cpp", + "PermissionController.cpp", + "ProcessInfoService.cpp", + "IpPrefix.cpp", +] + +cc_library { name: "libbinder", // for vndbinder @@ -37,65 +60,61 @@ cc_library_shared { enabled: true, }, double_loadable: true, + host_supported: true, + + // TODO(b/31559095): get headers from bionic on host + include_dirs: [ + "bionic/libc/kernel/android/uapi/", + "bionic/libc/kernel/uapi/", + ], + + // libbinder does not offer a stable wire protocol. + // if a second copy of it is installed, then it may break after security + // or dessert updates. Instead, apex users should use libbinder_ndk. + apex_available: [ + "//apex_available:platform", + // TODO(b/139016109) remove these three + "com.android.media.swcodec", + "test_com.android.media.swcodec", + ], srcs: [ - "ActivityManager.cpp", - "AppOpsManager.cpp", "Binder.cpp", "BpBinder.cpp", "BufferedTextOutput.cpp", "Debug.cpp", - "IActivityManager.cpp", - "IAppOpsCallback.cpp", - "IAppOpsService.cpp", - "IBatteryStats.cpp", "IInterface.cpp", - "IMediaResourceMonitor.cpp", "IMemory.cpp", "IPCThreadState.cpp", - "IPermissionController.cpp", - "IProcessInfoService.cpp", "IResultReceiver.cpp", "IServiceManager.cpp", "IShellCallback.cpp", - "IUidObserver.cpp", + "LazyServiceRegistrar.cpp", "MemoryBase.cpp", "MemoryDealer.cpp", "MemoryHeapBase.cpp", "Parcel.cpp", "ParcelFileDescriptor.cpp", - "PermissionCache.cpp", - "PermissionController.cpp", "PersistableBundle.cpp", - "ProcessInfoService.cpp", "ProcessState.cpp", "Static.cpp", + "Stability.cpp", "Status.cpp", "TextOutput.cpp", - "IpPrefix.cpp", - "Value.cpp", ":libbinder_aidl", ], target: { + android: { + srcs: libbinder_device_interface_sources, + + // NOT static to keep the wire protocol unfrozen + static: { + enabled: false, + }, + }, vendor: { - exclude_srcs: [ - "ActivityManager.cpp", - "AppOpsManager.cpp", - "IActivityManager.cpp", - "IAppOpsCallback.cpp", - "IAppOpsService.cpp", - "IBatteryStats.cpp", - "IMediaResourceMonitor.cpp", - "IPermissionController.cpp", - "IProcessInfoService.cpp", - "IUidObserver.cpp", - "PermissionCache.cpp", - "PermissionController.cpp", - "ProcessInfoService.cpp", - "IpPrefix.cpp", - ":libbinder_aidl", - ], + exclude_srcs: libbinder_device_interface_sources, }, }, @@ -116,11 +135,9 @@ cc_library_shared { }, shared_libs: [ - "libbase", "liblog", "libcutils", "libutils", - "libbinderthreadstate", ], header_libs: [ @@ -142,7 +159,21 @@ filegroup { name: "libbinder_aidl", srcs: [ "aidl/android/content/pm/IPackageManagerNative.aidl", + "aidl/android/os/IClientCallback.aidl", + "aidl/android/os/IServiceCallback.aidl", + "aidl/android/os/IServiceManager.aidl", ], + path: "aidl", } -subdirs = ["tests"] +aidl_interface { + name: "libbinder_aidl_test_stub", + local_include_dir: "aidl", + srcs: [":libbinder_aidl"], + vendor_available: true, + backend: { + java: { + enabled: false, + }, + }, +} diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp index 525685c35e..0a6685e14a 100644 --- a/libs/binder/AppOpsManager.cpp +++ b/libs/binder/AppOpsManager.cpp @@ -147,4 +147,4 @@ int32_t AppOpsManager::permissionToOpCode(const String16& permission) { } -}; // namespace android +} // namespace android diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index cb0e08d123..2f6e9c3a1b 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -81,6 +81,50 @@ status_t IBinder::shellCommand(const sp<IBinder>& target, int in, int out, int e return target->transact(SHELL_COMMAND_TRANSACTION, send, &reply); } +status_t IBinder::getExtension(sp<IBinder>* out) { + BBinder* local = this->localBinder(); + if (local != nullptr) { + *out = local->getExtension(); + return OK; + } + + BpBinder* proxy = this->remoteBinder(); + LOG_ALWAYS_FATAL_IF(proxy == nullptr); + + Parcel data; + Parcel reply; + status_t status = transact(EXTENSION_TRANSACTION, data, &reply); + if (status != OK) return status; + + return reply.readNullableStrongBinder(out); +} + +status_t IBinder::getDebugPid(pid_t* out) { + BBinder* local = this->localBinder(); + if (local != nullptr) { + *out = local->getDebugPid(); + return OK; + } + + BpBinder* proxy = this->remoteBinder(); + LOG_ALWAYS_FATAL_IF(proxy == nullptr); + + Parcel data; + Parcel reply; + status_t status = transact(DEBUG_PID_TRANSACTION, data, &reply); + if (status != OK) return status; + + int32_t pid; + status = reply.readInt32(&pid); + if (status != OK) return status; + + if (pid < 0 || pid > std::numeric_limits<pid_t>::max()) { + return BAD_VALUE; + } + *out = pid; + return OK; +} + // --------------------------------------------------------------------------- class BBinder::Extras @@ -88,6 +132,7 @@ class BBinder::Extras public: // unlocked objects bool mRequestingSid = false; + sp<IBinder> mExtension; // for below objects Mutex mLock; @@ -128,13 +173,20 @@ status_t BBinder::transact( status_t err = NO_ERROR; switch (code) { case PING_TRANSACTION: - reply->writeInt32(pingBinder()); + err = pingBinder(); + break; + case EXTENSION_TRANSACTION: + err = reply->writeStrongBinder(getExtension()); + break; + case DEBUG_PID_TRANSACTION: + err = reply->writeInt32(getDebugPid()); break; default: err = onTransact(code, data, reply, flags); break; } + // In case this is being transacted on in the same process. if (reply != nullptr) { reply->setDataPosition(0); } @@ -221,6 +273,21 @@ void BBinder::setRequestingSid(bool requestingSid) e->mRequestingSid = requestingSid; } +sp<IBinder> BBinder::getExtension() { + Extras* e = mExtras.load(std::memory_order_acquire); + if (e == nullptr) return nullptr; + return e->mExtension; +} + +pid_t BBinder::getDebugPid() { + return getpid(); +} + +void BBinder::setExtension(const sp<IBinder>& extension) { + Extras* e = getOrCreateExtras(); + e->mExtension = extension; +} + BBinder::~BBinder() { Extras* e = mExtras.load(std::memory_order_relaxed); @@ -352,4 +419,4 @@ bool BpRefBase::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/) // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index ec170f7a65..f16c39cdc1 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -21,6 +21,7 @@ #include <binder/IPCThreadState.h> #include <binder/IResultReceiver.h> +#include <binder/Stability.h> #include <cutils/compiler.h> #include <utils/Log.h> @@ -148,6 +149,10 @@ BpBinder::BpBinder(int32_t handle, int32_t trackedUid) IPCThreadState::self()->incWeakHandle(handle, this); } +int32_t BpBinder::handle() const { + return mHandle; +} + bool BpBinder::isDescriptorCached() const { Mutex::Autolock _l(mLock); return mDescriptorCache.size() ? true : false; @@ -186,10 +191,7 @@ status_t BpBinder::pingBinder() { Parcel send; Parcel reply; - status_t err = transact(PING_TRANSACTION, send, &reply); - if (err != NO_ERROR) return err; - if (reply.dataSize() < sizeof(status_t)) return NOT_ENOUGH_DATA; - return (status_t)reply.readInt32(); + return transact(PING_TRANSACTION, send, &reply); } status_t BpBinder::dump(int fd, const Vector<String16>& args) @@ -212,9 +214,29 @@ status_t BpBinder::transact( { // Once a binder has died, it will never come back to life. if (mAlive) { + bool privateVendor = flags & FLAG_PRIVATE_VENDOR; + // don't send userspace flags to the kernel + flags = flags & ~FLAG_PRIVATE_VENDOR; + + // user transactions require a given stability level + if (code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION) { + using android::internal::Stability; + + auto stability = Stability::get(this); + auto required = privateVendor ? Stability::VENDOR : Stability::kLocalStability; + + if (CC_UNLIKELY(!Stability::check(stability, required))) { + ALOGE("Cannot do a user transaction on a %s binder in a %s context.", + Stability::stabilityString(stability).c_str(), + Stability::stabilityString(required).c_str()); + return BAD_TYPE; + } + } + status_t status = IPCThreadState::self()->transact( mHandle, code, data, reply, flags); if (status == DEAD_OBJECT) mAlive = 0; + return status; } @@ -387,21 +409,6 @@ BpBinder::~BpBinder() } } - mLock.lock(); - Vector<Obituary>* obits = mObituaries; - if(obits != nullptr) { - if (ipc) ipc->clearDeathNotification(mHandle, this); - mObituaries = nullptr; - } - mLock.unlock(); - - if (obits != nullptr) { - // XXX Should we tell any remaining DeathRecipient - // objects that the last strong ref has gone away, so they - // are no longer linked? - delete obits; - } - if (ipc) { ipc->expungeHandle(mHandle, this); ipc->decWeakHandle(mHandle); @@ -423,6 +430,26 @@ void BpBinder::onLastStrongRef(const void* /*id*/) } IPCThreadState* ipc = IPCThreadState::self(); if (ipc) ipc->decStrongHandle(mHandle); + + mLock.lock(); + Vector<Obituary>* obits = mObituaries; + if(obits != nullptr) { + if (!obits->isEmpty()) { + ALOGI("onLastStrongRef automatically unlinking death recipients: %s", + mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>"); + } + + if (ipc) ipc->clearDeathNotification(mHandle, this); + mObituaries = nullptr; + } + mLock.unlock(); + + if (obits != nullptr) { + // XXX Should we tell any remaining DeathRecipient + // objects that the last strong ref has gone away, so they + // are no longer linked? + delete obits; + } } bool BpBinder::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/) @@ -470,4 +497,4 @@ void BpBinder::setBinderProxyCountWatermarks(int high, int low) { // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp index 857bbf9510..856a178f58 100644 --- a/libs/binder/BufferedTextOutput.cpp +++ b/libs/binder/BufferedTextOutput.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <binder/BufferedTextOutput.h> +#include "BufferedTextOutput.h" #include <binder/Debug.h> #include <cutils/atomic.h> @@ -23,12 +23,12 @@ #include <utils/RefBase.h> #include <utils/Vector.h> -#include <private/binder/Static.h> - #include <pthread.h> #include <stdio.h> #include <stdlib.h> +#include "Static.h" + // --------------------------------------------------------------------------- namespace android { @@ -280,4 +280,4 @@ BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const return mGlobalState; } -}; // namespace android +} // namespace android diff --git a/libs/binder/include/binder/BufferedTextOutput.h b/libs/binder/BufferedTextOutput.h index feae93dea1..1b27bb2249 100644 --- a/libs/binder/include/binder/BufferedTextOutput.h +++ b/libs/binder/BufferedTextOutput.h @@ -62,6 +62,6 @@ private: }; // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_BUFFEREDTEXTOUTPUT_H diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp index a1c2a8be08..64c1ff68c0 100644 --- a/libs/binder/Debug.cpp +++ b/libs/binder/Debug.cpp @@ -308,5 +308,5 @@ ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf) { return proc->getKernelReferences(count, buf); } -}; // namespace android +} // namespace android diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp index 377f604d44..1eb5363ae2 100644 --- a/libs/binder/IActivityManager.cpp +++ b/libs/binder/IActivityManager.cpp @@ -110,4 +110,4 @@ public: IMPLEMENT_META_INTERFACE(ActivityManager, "android.app.IActivityManager"); -}; // namespace android +} // namespace android diff --git a/libs/binder/IAppOpsCallback.cpp b/libs/binder/IAppOpsCallback.cpp index aba49673b1..0ce1dd59cf 100644 --- a/libs/binder/IAppOpsCallback.cpp +++ b/libs/binder/IAppOpsCallback.cpp @@ -22,8 +22,6 @@ #include <binder/Parcel.h> #include <utils/String8.h> -#include <private/binder/Static.h> - namespace android { // ---------------------------------------------------------------------- @@ -68,4 +66,4 @@ status_t BnAppOpsCallback::onTransact( } } -}; // namespace android +} // namespace android diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp index 66d6e31902..b2bd9e50b0 100644 --- a/libs/binder/IAppOpsService.cpp +++ b/libs/binder/IAppOpsService.cpp @@ -22,8 +22,6 @@ #include <binder/Parcel.h> #include <utils/String8.h> -#include <private/binder/Static.h> - namespace android { // ---------------------------------------------------------------------- @@ -241,4 +239,4 @@ status_t BnAppOpsService::onTransact( } } -}; // namespace android +} // namespace android diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp index b307e3e7b5..a47dbaccfe 100644 --- a/libs/binder/IBatteryStats.cpp +++ b/libs/binder/IBatteryStats.cpp @@ -20,8 +20,6 @@ #include <binder/Parcel.h> #include <utils/String8.h> -#include <private/binder/Static.h> - namespace android { // ---------------------------------------------------------------------- @@ -242,4 +240,4 @@ status_t BnBatteryStats::onTransact( } } -}; // namespace android +} // namespace android diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp index 59d51ed94a..b19004d454 100644 --- a/libs/binder/IInterface.cpp +++ b/libs/binder/IInterface.cpp @@ -46,4 +46,4 @@ sp<IBinder> IInterface::asBinder(const sp<IInterface>& iface) // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/libs/binder/IMediaResourceMonitor.cpp b/libs/binder/IMediaResourceMonitor.cpp index 77e3d239bc..4198e49259 100644 --- a/libs/binder/IMediaResourceMonitor.cpp +++ b/libs/binder/IMediaResourceMonitor.cpp @@ -59,4 +59,4 @@ status_t BnMediaResourceMonitor::onTransact( uint32_t code, const Parcel& data, // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index caf2318281..222b32c921 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -149,6 +149,10 @@ void* IMemory::fastPointer(const sp<IBinder>& binder, ssize_t offset) const return static_cast<char*>(base) + offset; } +void* IMemory::unsecurePointer() const { + return pointer(); +} + void* IMemory::pointer() const { ssize_t offset; sp<IMemoryHeap> heap = getMemory(&offset); @@ -510,4 +514,4 @@ void HeapCache::dump_heaps() // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 9a561cba64..9e89c57da3 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "IPCThreadState" #include <binder/IPCThreadState.h> -#include <binderthreadstate/IPCThreadStateBase.h> #include <binder/Binder.h> #include <binder/BpBinder.h> @@ -31,8 +30,8 @@ #include <utils/threads.h> #include <private/binder/binder_module.h> -#include <private/binder/Static.h> +#include <atomic> #include <errno.h> #include <inttypes.h> #include <pthread.h> @@ -43,6 +42,8 @@ #include <sys/resource.h> #include <unistd.h> +#include "Static.h" + #if LOG_NDEBUG #define IF_LOG_TRANSACTIONS() if (false) @@ -116,7 +117,7 @@ static const int64_t kWorkSourcePropagatedBitIndex = 32; static const char* getReturnString(uint32_t cmd) { - size_t idx = cmd & 0xff; + size_t idx = cmd & _IOC_NRMASK; if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0])) return kReturnStrings[idx]; else @@ -278,14 +279,14 @@ static const void* printCommand(TextOutput& out, const void* _cmd) } static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; -static bool gHaveTLS = false; +static std::atomic<bool> gHaveTLS(false); static pthread_key_t gTLS = 0; -static bool gShutdown = false; -static bool gDisableBackgroundScheduling = false; +static std::atomic<bool> gShutdown = false; +static std::atomic<bool> gDisableBackgroundScheduling = false; IPCThreadState* IPCThreadState::self() { - if (gHaveTLS) { + if (gHaveTLS.load(std::memory_order_acquire)) { restart: const pthread_key_t k = gTLS; IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); @@ -293,13 +294,14 @@ restart: return new IPCThreadState; } - if (gShutdown) { + // Racey, heuristic test for simultaneous shutdown. + if (gShutdown.load(std::memory_order_relaxed)) { ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n"); return nullptr; } pthread_mutex_lock(&gTLSMutex); - if (!gHaveTLS) { + if (!gHaveTLS.load(std::memory_order_relaxed)) { int key_create_value = pthread_key_create(&gTLS, threadDestructor); if (key_create_value != 0) { pthread_mutex_unlock(&gTLSMutex); @@ -307,7 +309,7 @@ restart: strerror(key_create_value)); return nullptr; } - gHaveTLS = true; + gHaveTLS.store(true, std::memory_order_release); } pthread_mutex_unlock(&gTLSMutex); goto restart; @@ -315,7 +317,7 @@ restart: IPCThreadState* IPCThreadState::selfOrNull() { - if (gHaveTLS) { + if (gHaveTLS.load(std::memory_order_acquire)) { const pthread_key_t k = gTLS; IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); return st; @@ -325,9 +327,9 @@ IPCThreadState* IPCThreadState::selfOrNull() void IPCThreadState::shutdown() { - gShutdown = true; + gShutdown.store(true, std::memory_order_relaxed); - if (gHaveTLS) { + if (gHaveTLS.load(std::memory_order_acquire)) { // XXX Need to wait for all thread pool threads to exit! IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS); if (st) { @@ -335,18 +337,18 @@ void IPCThreadState::shutdown() pthread_setspecific(gTLS, nullptr); } pthread_key_delete(gTLS); - gHaveTLS = false; + gHaveTLS.store(false, std::memory_order_release); } } void IPCThreadState::disableBackgroundScheduling(bool disable) { - gDisableBackgroundScheduling = disable; + gDisableBackgroundScheduling.store(disable, std::memory_order_relaxed); } bool IPCThreadState::backgroundSchedulingDisabled() { - return gDisableBackgroundScheduling; + return gDisableBackgroundScheduling.load(std::memory_order_relaxed); } sp<ProcessState> IPCThreadState::process() @@ -462,7 +464,7 @@ void IPCThreadState::clearCaller() void IPCThreadState::flushCommands() { - if (mProcess->mDriverFD <= 0) + if (mProcess->mDriverFD < 0) return; talkWithDriver(false); // The flush could have caused post-write refcount decrements to have @@ -594,9 +596,8 @@ void IPCThreadState::joinThreadPool(bool isMain) result = getAndExecuteCommand(); if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) { - ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting", + LOG_ALWAYS_FATAL("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting", mProcess->mDriverFD, result); - abort(); } // Let this thread exit the thread pool if it is no longer @@ -615,7 +616,7 @@ void IPCThreadState::joinThreadPool(bool isMain) int IPCThreadState::setupPolling(int* fd) { - if (mProcess->mDriverFD <= 0) { + if (mProcess->mDriverFD < 0) { return -EBADF; } @@ -674,11 +675,11 @@ status_t IPCThreadState::transact(int32_t handle, if ((flags & TF_ONE_WAY) == 0) { if (UNLIKELY(mCallRestriction != ProcessState::CallRestriction::NONE)) { if (mCallRestriction == ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY) { - ALOGE("Process making non-oneway call but is restricted."); + ALOGE("Process making non-oneway call (code: %u) but is restricted.", code); CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(), ANDROID_LOG_ERROR); } else /* FATAL_IF_NOT_ONEWAY */ { - LOG_ALWAYS_FATAL("Process may not make oneway calls."); + LOG_ALWAYS_FATAL("Process may not make oneway calls (code: %u).", code); } } @@ -801,6 +802,7 @@ status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) IPCThreadState::IPCThreadState() : mProcess(ProcessState::self()), + mServingStackPointer(nullptr), mWorkSource(kUnsetWorkSource), mPropagateWorkSource(false), mStrictModePolicy(0), @@ -811,7 +813,6 @@ IPCThreadState::IPCThreadState() clearCaller(); mIn.setDataCapacity(256); mOut.setDataCapacity(256); - mIPCThreadStateBase = IPCThreadStateBase::self(); } IPCThreadState::~IPCThreadState() @@ -921,7 +922,7 @@ finish: status_t IPCThreadState::talkWithDriver(bool doReceive) { - if (mProcess->mDriverFD <= 0) { + if (mProcess->mDriverFD < 0) { return -EBADF; } @@ -979,7 +980,7 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) #else err = INVALID_OPERATION; #endif - if (mProcess->mDriverFD <= 0) { + if (mProcess->mDriverFD < 0) { err = -EBADF; } IF_LOG_COMMANDS() { @@ -996,7 +997,7 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) if (err >= NO_ERROR) { if (bwr.write_consumed > 0) { if (bwr.write_consumed < mOut.dataSize()) - mOut.remove(0, bwr.write_consumed); + LOG_ALWAYS_FATAL("Driver did not consume write buffer"); else { mOut.setDataSize(0); processPostWriteDerefs(); @@ -1060,7 +1061,7 @@ status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, sp<BBinder> the_context_object; -void setTheContextObject(sp<BBinder> obj) +void IPCThreadState::setTheContextObject(sp<BBinder> obj) { the_context_object = obj; } @@ -1161,9 +1162,6 @@ status_t IPCThreadState::executeCommand(int32_t cmd) "Not enough command data for brTRANSACTION"); if (result != NO_ERROR) break; - //Record the fact that we're in a binder call. - mIPCThreadStateBase->pushCurrentState( - IPCThreadStateBase::CallState::BINDER); Parcel buffer; buffer.ipcSetDataReference( reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer), @@ -1171,6 +1169,9 @@ status_t IPCThreadState::executeCommand(int32_t cmd) reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets), tr.offsets_size/sizeof(binder_size_t), freeBuffer, this); + const void* origServingStackPointer = mServingStackPointer; + mServingStackPointer = &origServingStackPointer; // anything on the stack + const pid_t origPid = mCallingPid; const char* origSid = mCallingSid; const uid_t origUid = mCallingUid; @@ -1221,7 +1222,6 @@ status_t IPCThreadState::executeCommand(int32_t cmd) error = the_context_object->transact(tr.code, buffer, &reply, tr.flags); } - mIPCThreadStateBase->popCurrentState(); //ALOGI("<<<< TRANSACT from pid %d restore pid %d sid %s uid %d\n", // mCallingPid, origPid, (origSid ? origSid : "<N/A>"), origUid); @@ -1230,9 +1230,15 @@ status_t IPCThreadState::executeCommand(int32_t cmd) if (error < NO_ERROR) reply.setError(error); sendReply(reply, 0); } else { + if (error != OK || reply.dataSize() != 0) { + alog << "oneway function results will be dropped but finished with status " + << statusToString(error) + << " and parcel size " << reply.dataSize() << endl; + } LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); } + mServingStackPointer = origServingStackPointer; mCallingPid = origPid; mCallingSid = origSid; mCallingUid = origUid; @@ -1288,8 +1294,8 @@ status_t IPCThreadState::executeCommand(int32_t cmd) return result; } -bool IPCThreadState::isServingCall() const { - return mIPCThreadStateBase->getCurrentBinderCallState() == IPCThreadStateBase::CallState::BINDER; +const void* IPCThreadState::getServingStackPointer() const { + return mServingStackPointer; } void IPCThreadState::threadDestructor(void *st) @@ -1298,7 +1304,7 @@ void IPCThreadState::threadDestructor(void *st) if (self) { self->flushCommands(); #if defined(__ANDROID__) - if (self->mProcess->mDriverFD > 0) { + if (self->mProcess->mDriverFD >= 0) { ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); } #endif @@ -1323,4 +1329,4 @@ void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, state->mOut.writePointer((uintptr_t)data); } -}; // namespace android +} // namespace android diff --git a/libs/binder/IPermissionController.cpp b/libs/binder/IPermissionController.cpp index 6b99150edf..d9bf3cc7b6 100644 --- a/libs/binder/IPermissionController.cpp +++ b/libs/binder/IPermissionController.cpp @@ -22,8 +22,6 @@ #include <binder/Parcel.h> #include <utils/String8.h> -#include <private/binder/Static.h> - namespace android { // ---------------------------------------------------------------------- @@ -174,4 +172,4 @@ status_t BnPermissionController::onTransact( } } -}; // namespace android +} // namespace android diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp index 96e1a8c239..a38a27ad39 100644 --- a/libs/binder/IProcessInfoService.cpp +++ b/libs/binder/IProcessInfoService.cpp @@ -88,4 +88,4 @@ IMPLEMENT_META_INTERFACE(ProcessInfoService, "android.os.IProcessInfoService"); // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp index 159763d5bf..556288c5dd 100644 --- a/libs/binder/IResultReceiver.cpp +++ b/libs/binder/IResultReceiver.cpp @@ -22,8 +22,6 @@ #include <binder/Parcel.h> #include <utils/String8.h> -#include <private/binder/Static.h> - namespace android { // ---------------------------------------------------------------------- @@ -67,4 +65,4 @@ status_t BnResultReceiver::onTransact( } } -}; // namespace android +} // namespace android diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 4ba6c2a923..9888b59854 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -18,40 +18,106 @@ #include <binder/IServiceManager.h> -#include <utils/Log.h> +#include <android/os/BnServiceCallback.h> +#include <android/os/IServiceManager.h> #include <binder/IPCThreadState.h> +#include <binder/Parcel.h> +#include <utils/Log.h> +#include <utils/String8.h> +#include <utils/SystemClock.h> + #ifndef __ANDROID_VNDK__ #include <binder/IPermissionController.h> #endif -#include <binder/Parcel.h> + +#ifdef __ANDROID__ #include <cutils/properties.h> -#include <utils/String8.h> -#include <utils/SystemClock.h> +#endif -#include <private/binder/Static.h> +#include "Static.h" #include <unistd.h> namespace android { +using AidlServiceManager = android::os::IServiceManager; +using android::binder::Status; + +// libbinder's IServiceManager.h can't rely on the values generated by AIDL +// because many places use its headers via include_dirs (meaning, without +// declaring the dependency in the build system). So, for now, we can just check +// the values here. +static_assert(AidlServiceManager::DUMP_FLAG_PRIORITY_CRITICAL == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL); +static_assert(AidlServiceManager::DUMP_FLAG_PRIORITY_HIGH == IServiceManager::DUMP_FLAG_PRIORITY_HIGH); +static_assert(AidlServiceManager::DUMP_FLAG_PRIORITY_NORMAL == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL); +static_assert(AidlServiceManager::DUMP_FLAG_PRIORITY_DEFAULT == IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT); +static_assert(AidlServiceManager::DUMP_FLAG_PRIORITY_ALL == IServiceManager::DUMP_FLAG_PRIORITY_ALL); +static_assert(AidlServiceManager::DUMP_FLAG_PROTO == IServiceManager::DUMP_FLAG_PROTO); + +const String16& IServiceManager::getInterfaceDescriptor() const { + return AidlServiceManager::descriptor; +} +IServiceManager::IServiceManager() {} +IServiceManager::~IServiceManager() {} + +// From the old libbinder IServiceManager interface to IServiceManager. +class ServiceManagerShim : public IServiceManager +{ +public: + explicit ServiceManagerShim (const sp<AidlServiceManager>& impl); + + sp<IBinder> getService(const String16& name) const override; + sp<IBinder> checkService(const String16& name) const override; + status_t addService(const String16& name, const sp<IBinder>& service, + bool allowIsolated, int dumpsysPriority) override; + Vector<String16> listServices(int dumpsysPriority) override; + sp<IBinder> waitForService(const String16& name16) override; + bool isDeclared(const String16& name) override; + + // for legacy ABI + const String16& getInterfaceDescriptor() const override { + return mTheRealServiceManager->getInterfaceDescriptor(); + } + IBinder* onAsBinder() override { + return IInterface::asBinder(mTheRealServiceManager).get(); + } +private: + sp<AidlServiceManager> mTheRealServiceManager; +}; + +static std::once_flag gSmOnce; +static sp<IServiceManager> gDefaultServiceManager; + sp<IServiceManager> defaultServiceManager() { - if (gDefaultServiceManager != nullptr) return gDefaultServiceManager; - - { - AutoMutex _l(gDefaultServiceManagerLock); - while (gDefaultServiceManager == nullptr) { - gDefaultServiceManager = interface_cast<IServiceManager>( - ProcessState::self()->getContextObject(nullptr)); - if (gDefaultServiceManager == nullptr) + std::call_once(gSmOnce, []() { + sp<AidlServiceManager> sm = nullptr; + while (sm == nullptr) { + sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr)); + if (sm == nullptr) { sleep(1); + } } - } + + gDefaultServiceManager = new ServiceManagerShim(sm); + }); return gDefaultServiceManager; } -#ifndef __ANDROID_VNDK__ +void setDefaultServiceManager(const sp<IServiceManager>& sm) { + bool called = false; + std::call_once(gSmOnce, [&]() { + gDefaultServiceManager = sm; + called = true; + }); + + if (!called) { + LOG_ALWAYS_FATAL("setDefaultServiceManager() called after defaultServiceManager()."); + } +} + +#if !defined(__ANDROID_VNDK__) && defined(__ANDROID__) // IPermissionController is not accessible to vendors bool checkCallingPermission(const String16& permission) @@ -74,10 +140,13 @@ bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t bool checkPermission(const String16& permission, pid_t pid, uid_t uid) { + static Mutex gPermissionControllerLock; + static sp<IPermissionController> gPermissionController; + sp<IPermissionController> pc; - gDefaultServiceManagerLock.lock(); + gPermissionControllerLock.lock(); pc = gPermissionController; - gDefaultServiceManagerLock.unlock(); + gPermissionControllerLock.unlock(); int64_t startTime = 0; @@ -101,11 +170,11 @@ bool checkPermission(const String16& permission, pid_t pid, uid_t uid) } // Object is dead! - gDefaultServiceManagerLock.lock(); + gPermissionControllerLock.lock(); if (gPermissionController == pc) { gPermissionController = nullptr; } - gDefaultServiceManagerLock.unlock(); + gPermissionControllerLock.unlock(); } // Need to retrieve the permission controller. @@ -121,9 +190,9 @@ bool checkPermission(const String16& permission, pid_t pid, uid_t uid) } else { pc = interface_cast<IPermissionController>(binder); // Install the new permission controller, and try again. - gDefaultServiceManagerLock.lock(); + gPermissionControllerLock.lock(); gPermissionController = pc; - gDefaultServiceManagerLock.unlock(); + gPermissionControllerLock.unlock(); } } } @@ -132,84 +201,158 @@ bool checkPermission(const String16& permission, pid_t pid, uid_t uid) // ---------------------------------------------------------------------- -class BpServiceManager : public BpInterface<IServiceManager> +ServiceManagerShim::ServiceManagerShim(const sp<AidlServiceManager>& impl) + : mTheRealServiceManager(impl) +{} + +sp<IBinder> ServiceManagerShim::getService(const String16& name) const { -public: - explicit BpServiceManager(const sp<IBinder>& impl) - : BpInterface<IServiceManager>(impl) - { + static bool gSystemBootCompleted = false; + + sp<IBinder> svc = checkService(name); + if (svc != nullptr) return svc; + + const bool isVendorService = + strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0; + const long timeout = uptimeMillis() + 5000; + // Vendor code can't access system properties + if (!gSystemBootCompleted && !isVendorService) { +#ifdef __ANDROID__ + char bootCompleted[PROPERTY_VALUE_MAX]; + property_get("sys.boot_completed", bootCompleted, "0"); + gSystemBootCompleted = strcmp(bootCompleted, "1") == 0 ? true : false; +#else + gSystemBootCompleted = true; +#endif } + // retry interval in millisecond; note that vendor services stay at 100ms + const long sleepTime = gSystemBootCompleted ? 1000 : 100; + + int n = 0; + while (uptimeMillis() < timeout) { + n++; + ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(), + ProcessState::self()->getDriverName().c_str()); + usleep(1000*sleepTime); - virtual sp<IBinder> getService(const String16& name) const - { sp<IBinder> svc = checkService(name); if (svc != nullptr) return svc; + } + ALOGW("Service %s didn't start. Returning NULL", String8(name).string()); + return nullptr; +} - const bool isVendorService = - strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0; - const long timeout = uptimeMillis() + 5000; - if (!gSystemBootCompleted && !isVendorService) { - // Vendor code can't access system properties - char bootCompleted[PROPERTY_VALUE_MAX]; - property_get("sys.boot_completed", bootCompleted, "0"); - gSystemBootCompleted = strcmp(bootCompleted, "1") == 0 ? true : false; - } - // retry interval in millisecond; note that vendor services stay at 100ms - const long sleepTime = gSystemBootCompleted ? 1000 : 100; - - int n = 0; - while (uptimeMillis() < timeout) { - n++; - ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(), - ProcessState::self()->getDriverName().c_str()); - usleep(1000*sleepTime); - - sp<IBinder> svc = checkService(name); - if (svc != nullptr) return svc; - } - ALOGW("Service %s didn't start. Returning NULL", String8(name).string()); +sp<IBinder> ServiceManagerShim::checkService(const String16& name) const +{ + sp<IBinder> ret; + if (!mTheRealServiceManager->checkService(String8(name).c_str(), &ret).isOk()) { return nullptr; } + return ret; +} - virtual sp<IBinder> checkService( const String16& name) const - { - Parcel data, reply; - data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); - data.writeString16(name); - remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); - return reply.readStrongBinder(); +status_t ServiceManagerShim::addService(const String16& name, const sp<IBinder>& service, + bool allowIsolated, int dumpsysPriority) +{ + Status status = mTheRealServiceManager->addService( + String8(name).c_str(), service, allowIsolated, dumpsysPriority); + return status.exceptionCode(); +} + +Vector<String16> ServiceManagerShim::listServices(int dumpsysPriority) +{ + std::vector<std::string> ret; + if (!mTheRealServiceManager->listServices(dumpsysPriority, &ret).isOk()) { + return {}; } - virtual status_t addService(const String16& name, const sp<IBinder>& service, - bool allowIsolated, int dumpsysPriority) { - Parcel data, reply; - data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); - data.writeString16(name); - data.writeStrongBinder(service); - data.writeInt32(allowIsolated ? 1 : 0); - data.writeInt32(dumpsysPriority); - status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); - return err == NO_ERROR ? reply.readExceptionCode() : err; + Vector<String16> res; + res.setCapacity(ret.size()); + for (const std::string& name : ret) { + res.push(String16(name.c_str())); } + return res; +} - virtual Vector<String16> listServices(int dumpsysPriority) { - Vector<String16> res; - int n = 0; - - for (;;) { - Parcel data, reply; - data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); - data.writeInt32(n++); - data.writeInt32(dumpsysPriority); - status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply); - if (err != NO_ERROR) - break; - res.add(reply.readString16()); +sp<IBinder> ServiceManagerShim::waitForService(const String16& name16) +{ + class Waiter : public android::os::BnServiceCallback { + Status onRegistration(const std::string& /*name*/, + const sp<IBinder>& binder) override { + std::unique_lock<std::mutex> lock(mMutex); + mBinder = binder; + lock.unlock(); + // Flushing here helps ensure the service's ref count remains accurate + IPCThreadState::self()->flushCommands(); + mCv.notify_one(); + return Status::ok(); } - return res; + public: + sp<IBinder> mBinder; + std::mutex mMutex; + std::condition_variable mCv; + }; + + // Simple RAII object to ensure a function call immediately before going out of scope + class Defer { + public: + Defer(std::function<void()>&& f) : mF(std::move(f)) {} + ~Defer() { mF(); } + private: + std::function<void()> mF; + }; + + const std::string name = String8(name16).c_str(); + + sp<IBinder> out; + if (!mTheRealServiceManager->getService(name, &out).isOk()) { + return nullptr; } -}; + if (out != nullptr) return out; + + sp<Waiter> waiter = new Waiter; + if (!mTheRealServiceManager->registerForNotifications( + name, waiter).isOk()) { + return nullptr; + } + Defer unregister ([&] { + mTheRealServiceManager->unregisterForNotifications(name, waiter); + }); + + while(true) { + { + std::unique_lock<std::mutex> lock(waiter->mMutex); + using std::literals::chrono_literals::operator""s; + waiter->mCv.wait_for(lock, 1s, [&] { + return waiter->mBinder != nullptr; + }); + if (waiter->mBinder != nullptr) return waiter->mBinder; + } -IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); + // Handle race condition for lazy services. Here is what can happen: + // - the service dies (not processed by init yet). + // - sm processes death notification. + // - sm gets getService and calls init to start service. + // - init gets the start signal, but the service already appears + // started, so it does nothing. + // - init gets death signal, but doesn't know it needs to restart + // the service + // - we need to request service again to get it to start + if (!mTheRealServiceManager->getService(name, &out).isOk()) { + return nullptr; + } + if (out != nullptr) return out; + + ALOGW("Waited one second for %s", name.c_str()); + } +} + +bool ServiceManagerShim::isDeclared(const String16& name) { + bool declared; + if (!mTheRealServiceManager->isDeclared(String8(name).c_str(), &declared).isOk()) { + return false; + } + return declared; +} -}; // namespace android +} // namespace android diff --git a/libs/binder/IShellCallback.cpp b/libs/binder/IShellCallback.cpp index 6c697decca..a3e2b67bc6 100644 --- a/libs/binder/IShellCallback.cpp +++ b/libs/binder/IShellCallback.cpp @@ -25,8 +25,6 @@ #include <binder/Parcel.h> #include <utils/String8.h> -#include <private/binder/Static.h> - namespace android { // ---------------------------------------------------------------------- @@ -87,4 +85,4 @@ status_t BnShellCallback::onTransact( } } -}; // namespace android +} // namespace android diff --git a/libs/binder/IUidObserver.cpp b/libs/binder/IUidObserver.cpp index 82f9047595..038e6bf6ea 100644 --- a/libs/binder/IUidObserver.cpp +++ b/libs/binder/IUidObserver.cpp @@ -112,4 +112,4 @@ status_t BnUidObserver::onTransact( } } -}; // namespace android +} // namespace android diff --git a/libs/binder/IpPrefix.cpp b/libs/binder/IpPrefix.cpp index 3a8a63c46e..8d622668cc 100644 --- a/libs/binder/IpPrefix.cpp +++ b/libs/binder/IpPrefix.cpp @@ -30,7 +30,6 @@ using android::NO_ERROR; using android::Parcel; using android::status_t; using android::UNEXPECTED_NULL; -using namespace ::android::binder; namespace android { diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp new file mode 100644 index 0000000000..f064bd77ce --- /dev/null +++ b/libs/binder/LazyServiceRegistrar.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AidlLazyServiceRegistrar" + +#include <binder/LazyServiceRegistrar.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <android/os/BnClientCallback.h> +#include <android/os/IServiceManager.h> +#include <utils/Log.h> + +namespace android { +namespace binder { +namespace internal { + +using AidlServiceManager = android::os::IServiceManager; + +class ClientCounterCallback : public ::android::os::BnClientCallback { +public: + ClientCounterCallback() : mNumConnectedServices(0) {} + + bool registerService(const sp<IBinder>& service, const std::string& name, + bool allowIsolated, int dumpFlags); + +protected: + Status onClients(const sp<IBinder>& service, bool clients) override; + +private: + /** + * Unregisters all services that we can. If we can't unregister all, re-register other + * services. + */ + void tryShutdown(); + + /** + * Counter of the number of services that currently have at least one client. + */ + size_t mNumConnectedServices; + + struct Service { + sp<IBinder> service; + bool allowIsolated; + int dumpFlags; + }; + /** + * Map of registered names and services + */ + std::map<std::string, Service> mRegisteredServices; +}; + +bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name, + bool allowIsolated, int dumpFlags) { + auto manager = interface_cast<AidlServiceManager>( + ProcessState::self()->getContextObject(nullptr)); + + bool reRegister = mRegisteredServices.count(name) > 0; + std::string regStr = (reRegister) ? "Re-registering" : "Registering"; + ALOGI("%s service %s", regStr.c_str(), name.c_str()); + + if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) { + ALOGE("Failed to register service %s", name.c_str()); + return false; + } + + if (!manager->registerClientCallback(name, service, this).isOk()) { + ALOGE("Failed to add client callback for service %s", name.c_str()); + return false; + } + + if (!reRegister) { + // Only add this when a service is added for the first time, as it is not removed + mRegisteredServices[name] = {service, allowIsolated, dumpFlags}; + } + + return true; +} + +/** + * onClients is oneway, so no need to worry about multi-threading. Note that this means multiple + * invocations could occur on different threads however. + */ +Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) { + if (clients) { + mNumConnectedServices++; + } else { + mNumConnectedServices--; + } + + ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d", + mNumConnectedServices, mRegisteredServices.size(), + String8(service->getInterfaceDescriptor()).string(), clients); + + if (mNumConnectedServices == 0) { + tryShutdown(); + } + + return Status::ok(); +} + +void ClientCounterCallback::tryShutdown() { + ALOGI("Trying to shut down the service. No clients in use for any service in process."); + + // This makes the same assumption as IServiceManager.cpp. Could dedupe if used in more places. + auto manager = interface_cast<AidlServiceManager>( + ProcessState::self()->getContextObject(nullptr)); + + auto unRegisterIt = mRegisteredServices.begin(); + for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) { + auto& entry = (*unRegisterIt); + + bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk(); + + + if (!success) { + ALOGI("Failed to unregister service %s", entry.first.c_str()); + break; + } + } + + if (unRegisterIt == mRegisteredServices.end()) { + ALOGI("Unregistered all clients and exiting"); + exit(EXIT_SUCCESS); + } + + for (auto reRegisterIt = mRegisteredServices.begin(); reRegisterIt != unRegisterIt; + reRegisterIt++) { + auto& entry = (*reRegisterIt); + + // re-register entry + if (!registerService(entry.second.service, entry.first, entry.second.allowIsolated, + entry.second.dumpFlags)) { + // Must restart. Otherwise, clients will never be able to get a hold of this service. + ALOGE("Bad state: could not re-register services"); + } + } +} + +} // namespace internal + +LazyServiceRegistrar::LazyServiceRegistrar() { + mClientCC = std::make_shared<internal::ClientCounterCallback>(); +} + +LazyServiceRegistrar& LazyServiceRegistrar::getInstance() { + static auto registrarInstance = new LazyServiceRegistrar(); + return *registrarInstance; +} + +status_t LazyServiceRegistrar::registerService(const sp<IBinder>& service, const std::string& name, + bool allowIsolated, int dumpFlags) { + if (!mClientCC->registerService(service, name, allowIsolated, dumpFlags)) { + return UNKNOWN_ERROR; + } + return OK; +} + +} // namespace hardware +} // namespace android
\ No newline at end of file diff --git a/libs/binder/MemoryBase.cpp b/libs/binder/MemoryBase.cpp index 033066bea3..32300dfbb6 100644 --- a/libs/binder/MemoryBase.cpp +++ b/libs/binder/MemoryBase.cpp @@ -43,4 +43,4 @@ MemoryBase::~MemoryBase() } // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp index eacad3b6b3..ebf91f925e 100644 --- a/libs/binder/MemoryDealer.cpp +++ b/libs/binder/MemoryDealer.cpp @@ -481,4 +481,4 @@ void SimpleBestFitAllocator::dump_l(String8& result, } -}; // namespace android +} // namespace android diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 4c300b47c6..e4ea60f699 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -181,4 +181,4 @@ off_t MemoryHeapBase::getOffset() const { } // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 5ad30271d7..822247f561 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -35,9 +35,9 @@ #include <binder/IPCThreadState.h> #include <binder/Parcel.h> #include <binder/ProcessState.h> +#include <binder/Stability.h> #include <binder/Status.h> #include <binder/TextOutput.h> -#include <binder/Value.h> #include <cutils/ashmem.h> #include <utils/Debug.h> @@ -48,11 +48,7 @@ #include <utils/String16.h> #include <private/binder/binder_module.h> -#include <private/binder/Static.h> - -#ifndef INT32_MAX -#define INT32_MAX ((int32_t)(2147483647)) -#endif +#include "Static.h" #define LOG_REFS(...) //#define LOG_REFS(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) @@ -67,8 +63,8 @@ #define PAD_SIZE_UNSAFE(s) (((s)+3)&~3) static size_t pad_size(size_t s) { - if (s > (SIZE_T_MAX - 3)) { - abort(); + if (s > (std::numeric_limits<size_t>::max() - 3)) { + LOG_ALWAYS_FATAL("pad size too big %zu", s); } return PAD_SIZE_UNSAFE(s); } @@ -78,6 +74,9 @@ static size_t pad_size(size_t s) { namespace android { +// many things compile this into prebuilts on the stack +static_assert(sizeof(Parcel) == 60 || sizeof(Parcel) == 120); + static pthread_mutex_t gParcelGlobalAllocSizeLock = PTHREAD_MUTEX_INITIALIZER; static size_t gParcelGlobalAllocSize = 0; static size_t gParcelGlobalAllocCount = 0; @@ -93,7 +92,7 @@ enum { BLOB_ASHMEM_MUTABLE = 2, }; -void acquire_object(const sp<ProcessState>& proc, +static void acquire_object(const sp<ProcessState>& proc, const flat_binder_object& obj, const void* who, size_t* outAshmemSize) { switch (obj.hdr.type) { @@ -103,10 +102,6 @@ void acquire_object(const sp<ProcessState>& proc, reinterpret_cast<IBinder*>(obj.cookie)->incStrong(who); } return; - case BINDER_TYPE_WEAK_BINDER: - if (obj.binder) - reinterpret_cast<RefBase::weakref_type*>(obj.binder)->incWeak(who); - return; case BINDER_TYPE_HANDLE: { const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle); if (b != nullptr) { @@ -115,11 +110,6 @@ void acquire_object(const sp<ProcessState>& proc, } return; } - case BINDER_TYPE_WEAK_HANDLE: { - const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle); - if (b != nullptr) b.get_refs()->incWeak(who); - return; - } case BINDER_TYPE_FD: { if ((obj.cookie != 0) && (outAshmemSize != nullptr) && ashmem_valid(obj.handle)) { // If we own an ashmem fd, keep track of how much memory it refers to. @@ -135,12 +125,6 @@ void acquire_object(const sp<ProcessState>& proc, ALOGD("Invalid object type 0x%08x", obj.hdr.type); } -void acquire_object(const sp<ProcessState>& proc, - const flat_binder_object& obj, const void* who) -{ - acquire_object(proc, obj, who, nullptr); -} - static void release_object(const sp<ProcessState>& proc, const flat_binder_object& obj, const void* who, size_t* outAshmemSize) { @@ -151,10 +135,6 @@ static void release_object(const sp<ProcessState>& proc, reinterpret_cast<IBinder*>(obj.cookie)->decStrong(who); } return; - case BINDER_TYPE_WEAK_BINDER: - if (obj.binder) - reinterpret_cast<RefBase::weakref_type*>(obj.binder)->decWeak(who); - return; case BINDER_TYPE_HANDLE: { const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle); if (b != nullptr) { @@ -163,11 +143,6 @@ static void release_object(const sp<ProcessState>& proc, } return; } - case BINDER_TYPE_WEAK_HANDLE: { - const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle); - if (b != nullptr) b.get_refs()->decWeak(who); - return; - } case BINDER_TYPE_FD: { if (obj.cookie != 0) { // owned if ((outAshmemSize != nullptr) && ashmem_valid(obj.handle)) { @@ -189,20 +164,31 @@ static void release_object(const sp<ProcessState>& proc, ALOGE("Invalid object type 0x%08x", obj.hdr.type); } -void release_object(const sp<ProcessState>& proc, - const flat_binder_object& obj, const void* who) +status_t Parcel::finishFlattenBinder( + const sp<IBinder>& binder, const flat_binder_object& flat) { - release_object(proc, obj, who, nullptr); + status_t status = writeObject(flat, false); + if (status != OK) return status; + + internal::Stability::tryMarkCompilationUnit(binder.get()); + return writeInt32(internal::Stability::get(binder.get())); } -inline static status_t finish_flatten_binder( - const sp<IBinder>& /*binder*/, const flat_binder_object& flat, Parcel* out) +status_t Parcel::finishUnflattenBinder( + const sp<IBinder>& binder, sp<IBinder>* out) const { - return out->writeObject(flat, false); + int32_t stability; + status_t status = readInt32(&stability); + if (status != OK) return status; + + status = internal::Stability::set(binder.get(), stability, true /*log*/); + if (status != OK) return status; + + *out = binder; + return OK; } -status_t flatten_binder(const sp<ProcessState>& /*proc*/, - const sp<IBinder>& binder, Parcel* out) +status_t Parcel::flattenBinder(const sp<IBinder>& binder) { flat_binder_object obj; @@ -240,108 +226,24 @@ status_t flatten_binder(const sp<ProcessState>& /*proc*/, obj.cookie = 0; } - return finish_flatten_binder(binder, obj, out); -} - -status_t flatten_binder(const sp<ProcessState>& /*proc*/, - const wp<IBinder>& binder, Parcel* out) -{ - flat_binder_object obj; - - obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - if (binder != nullptr) { - sp<IBinder> real = binder.promote(); - if (real != nullptr) { - IBinder *local = real->localBinder(); - if (!local) { - BpBinder *proxy = real->remoteBinder(); - if (proxy == nullptr) { - ALOGE("null proxy"); - } - const int32_t handle = proxy ? proxy->handle() : 0; - obj.hdr.type = BINDER_TYPE_WEAK_HANDLE; - obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */ - obj.handle = handle; - obj.cookie = 0; - } else { - obj.hdr.type = BINDER_TYPE_WEAK_BINDER; - obj.binder = reinterpret_cast<uintptr_t>(binder.get_refs()); - obj.cookie = reinterpret_cast<uintptr_t>(binder.unsafe_get()); - } - return finish_flatten_binder(real, obj, out); - } - - // XXX How to deal? In order to flatten the given binder, - // we need to probe it for information, which requires a primary - // reference... but we don't have one. - // - // The OpenBinder implementation uses a dynamic_cast<> here, - // but we can't do that with the different reference counting - // implementation we are using. - ALOGE("Unable to unflatten Binder weak reference!"); - obj.hdr.type = BINDER_TYPE_BINDER; - obj.binder = 0; - obj.cookie = 0; - return finish_flatten_binder(nullptr, obj, out); - - } else { - obj.hdr.type = BINDER_TYPE_BINDER; - obj.binder = 0; - obj.cookie = 0; - return finish_flatten_binder(nullptr, obj, out); - } -} - -inline static status_t finish_unflatten_binder( - BpBinder* /*proxy*/, const flat_binder_object& /*flat*/, - const Parcel& /*in*/) -{ - return NO_ERROR; -} - -status_t unflatten_binder(const sp<ProcessState>& proc, - const Parcel& in, sp<IBinder>* out) -{ - const flat_binder_object* flat = in.readObject(false); - - if (flat) { - switch (flat->hdr.type) { - case BINDER_TYPE_BINDER: - *out = reinterpret_cast<IBinder*>(flat->cookie); - return finish_unflatten_binder(nullptr, *flat, in); - case BINDER_TYPE_HANDLE: - *out = proc->getStrongProxyForHandle(flat->handle); - return finish_unflatten_binder( - static_cast<BpBinder*>(out->get()), *flat, in); - } - } - return BAD_TYPE; + return finishFlattenBinder(binder, obj); } -status_t unflatten_binder(const sp<ProcessState>& proc, - const Parcel& in, wp<IBinder>* out) +status_t Parcel::unflattenBinder(sp<IBinder>* out) const { - const flat_binder_object* flat = in.readObject(false); + const flat_binder_object* flat = readObject(false); if (flat) { switch (flat->hdr.type) { - case BINDER_TYPE_BINDER: - *out = reinterpret_cast<IBinder*>(flat->cookie); - return finish_unflatten_binder(nullptr, *flat, in); - case BINDER_TYPE_WEAK_BINDER: - if (flat->binder != 0) { - out->set_object_and_refs( - reinterpret_cast<IBinder*>(flat->cookie), - reinterpret_cast<RefBase::weakref_type*>(flat->binder)); - } else { - *out = nullptr; - } - return finish_unflatten_binder(nullptr, *flat, in); - case BINDER_TYPE_HANDLE: - case BINDER_TYPE_WEAK_HANDLE: - *out = proc->getWeakProxyForHandle(flat->handle); - return finish_unflatten_binder( - static_cast<BpBinder*>(out->unsafe_get()), *flat, in); + case BINDER_TYPE_BINDER: { + sp<IBinder> binder = reinterpret_cast<IBinder*>(flat->cookie); + return finishUnflattenBinder(binder, out); + } + case BINDER_TYPE_HANDLE: { + sp<IBinder> binder = + ProcessState::self()->getStrongProxyForHandle(flat->handle); + return finishUnflattenBinder(binder, out); + } } } return BAD_TYPE; @@ -389,7 +291,7 @@ size_t Parcel::dataAvail() const { size_t result = dataSize() - dataPosition(); if (result > INT32_MAX) { - abort(); + LOG_ALWAYS_FATAL("result too big: %zu", result); } return result; } @@ -426,7 +328,7 @@ void Parcel::setDataPosition(size_t pos) const if (pos > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. - abort(); + LOG_ALWAYS_FATAL("pos too big: %zu", pos); } mDataPos = pos; @@ -603,6 +505,12 @@ void Parcel::updateWorkSourceRequestHeaderPosition() const { } } +#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) +constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R'); +#else +constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T'); +#endif + // Write RPC headers. (previously just the interface token) status_t Parcel::writeInterfaceToken(const String16& interface) { @@ -611,6 +519,7 @@ status_t Parcel::writeInterfaceToken(const String16& interface) updateWorkSourceRequestHeaderPosition(); writeInt32(threadState->shouldPropagateWorkSource() ? threadState->getCallingWorkSourceUid() : IPCThreadState::kUnsetWorkSource); + writeInt32(kHeader); // currently the interface identification token is just its name as a string return writeString16(interface); } @@ -628,7 +537,7 @@ bool Parcel::replaceCallingWorkSourceUid(uid_t uid) return err == NO_ERROR; } -uid_t Parcel::readCallingWorkSourceUid() +uid_t Parcel::readCallingWorkSourceUid() const { if (!mRequestHeaderPresent) { return IPCThreadState::kUnsetWorkSource; @@ -668,6 +577,12 @@ bool Parcel::enforceInterface(const String16& interface, updateWorkSourceRequestHeaderPosition(); int32_t workSource = readInt32(); threadState->setCallingWorkSourceUidWithoutPropagation(workSource); + // vendor header + int32_t header = readInt32(); + if (header != kHeader) { + ALOGE("Expecting header 0x%x but found 0x%x. Mixing copies of libbinder?", kHeader, header); + return false; + } // Interface descriptor. const String16 str(readString16()); if (str == interface) { @@ -679,11 +594,6 @@ bool Parcel::enforceInterface(const String16& interface, } } -const binder_size_t* Parcel::objects() const -{ - return mObjects; -} - size_t Parcel::objectsCount() const { return mObjectsSize; @@ -836,61 +746,37 @@ status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) { return writeUtf8AsUtf16(*str); } -namespace { - -template<typename T> -status_t writeByteVectorInternal(Parcel* parcel, const std::vector<T>& val) -{ - status_t status; - if (val.size() > std::numeric_limits<int32_t>::max()) { - status = BAD_VALUE; - return status; +status_t Parcel::writeByteVectorInternal(const int8_t* data, size_t size) { + if (size > std::numeric_limits<int32_t>::max()) { + return BAD_VALUE; } - status = parcel->writeInt32(val.size()); + status_t status = writeInt32(size); if (status != OK) { return status; } - void* data = parcel->writeInplace(val.size()); - if (!data) { - status = BAD_VALUE; - return status; - } - - memcpy(data, val.data(), val.size()); - return status; -} - -template<typename T> -status_t writeByteVectorInternalPtr(Parcel* parcel, - const std::unique_ptr<std::vector<T>>& val) -{ - if (!val) { - return parcel->writeInt32(-1); - } - - return writeByteVectorInternal(parcel, *val); + return write(data, size); } -} // namespace - status_t Parcel::writeByteVector(const std::vector<int8_t>& val) { - return writeByteVectorInternal(this, val); + return writeByteVectorInternal(val.data(), val.size()); } status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val) { - return writeByteVectorInternalPtr(this, val); + if (!val) return writeInt32(-1); + return writeByteVectorInternal(val->data(), val->size()); } status_t Parcel::writeByteVector(const std::vector<uint8_t>& val) { - return writeByteVectorInternal(this, val); + return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size()); } status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val) { - return writeByteVectorInternalPtr(this, val); + if (!val) return writeInt32(-1); + return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size()); } status_t Parcel::writeInt32Vector(const std::vector<int32_t>& val) @@ -1133,7 +1019,7 @@ status_t Parcel::writeString16(const char16_t* str, size_t len) status_t Parcel::writeStrongBinder(const sp<IBinder>& val) { - return flatten_binder(ProcessState::self(), val, this); + return flattenBinder(val); } status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) @@ -1154,11 +1040,6 @@ status_t Parcel::readStrongBinderVector(std::vector<sp<IBinder>>* val) const { return readTypedVector(val, &Parcel::readStrongBinder); } -status_t Parcel::writeWeakBinder(const wp<IBinder>& val) -{ - return flatten_binder(ProcessState::self(), val, this); -} - status_t Parcel::writeRawNullableParcelable(const Parcelable* parcelable) { if (!parcelable) { return writeInt32(0); @@ -1175,10 +1056,6 @@ status_t Parcel::writeParcelable(const Parcelable& parcelable) { return parcelable.writeToParcel(this); } -status_t Parcel::writeValue(const binder::Value& value) { - return value.writeToParcel(this); -} - status_t Parcel::writeNativeHandle(const native_handle* handle) { if (!handle || handle->version != sizeof(native_handle)) @@ -1416,125 +1293,6 @@ status_t Parcel::writeNoException() return status.writeToParcel(this); } -status_t Parcel::writeMap(const ::android::binder::Map& map_in) -{ - using ::std::map; - using ::android::binder::Value; - using ::android::binder::Map; - - Map::const_iterator iter; - status_t ret; - - ret = writeInt32(map_in.size()); - - if (ret != NO_ERROR) { - return ret; - } - - for (iter = map_in.begin(); iter != map_in.end(); ++iter) { - ret = writeValue(Value(iter->first)); - if (ret != NO_ERROR) { - return ret; - } - - ret = writeValue(iter->second); - if (ret != NO_ERROR) { - return ret; - } - } - - return ret; -} - -status_t Parcel::writeNullableMap(const std::unique_ptr<binder::Map>& map) -{ - if (map == nullptr) { - return writeInt32(-1); - } - - return writeMap(*map.get()); -} - -status_t Parcel::readMap(::android::binder::Map* map_out)const -{ - using ::std::map; - using ::android::String16; - using ::android::String8; - using ::android::binder::Value; - using ::android::binder::Map; - - status_t ret = NO_ERROR; - int32_t count; - - ret = readInt32(&count); - if (ret != NO_ERROR) { - return ret; - } - - if (count < 0) { - ALOGE("readMap: Unexpected count: %d", count); - return (count == -1) - ? UNEXPECTED_NULL - : BAD_VALUE; - } - - map_out->clear(); - - while (count--) { - Map::key_type key; - Value value; - - ret = readValue(&value); - if (ret != NO_ERROR) { - return ret; - } - - if (!value.getString(&key)) { - ALOGE("readMap: Key type not a string (parcelType = %d)", value.parcelType()); - return BAD_VALUE; - } - - ret = readValue(&value); - if (ret != NO_ERROR) { - return ret; - } - - (*map_out)[key] = value; - } - - return ret; -} - -status_t Parcel::readNullableMap(std::unique_ptr<binder::Map>* map) const -{ - const size_t start = dataPosition(); - int32_t count; - status_t status = readInt32(&count); - map->reset(); - - if (status != OK || count == -1) { - return status; - } - - setDataPosition(start); - map->reset(new binder::Map()); - - status = readMap(map->get()); - - if (status != OK) { - map->reset(); - } - - return status; -} - - - -void Parcel::remove(size_t /*start*/, size_t /*amt*/) -{ - LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); -} - status_t Parcel::validateReadData(size_t upperBound) const { // Don't allow non-object reads on object data @@ -1691,81 +1449,38 @@ restart_write: return err; } -namespace { - -template<typename T> -status_t readByteVectorInternal(const Parcel* parcel, - std::vector<T>* val) { - val->clear(); - - int32_t size; - status_t status = parcel->readInt32(&size); - - if (status != OK) { - return status; - } - - if (size < 0) { - status = UNEXPECTED_NULL; - return status; - } - if (size_t(size) > parcel->dataAvail()) { - status = BAD_VALUE; - return status; - } - - T* data = const_cast<T*>(reinterpret_cast<const T*>(parcel->readInplace(size))); - if (!data) { - status = BAD_VALUE; - return status; - } - val->reserve(size); - val->insert(val->end(), data, data + size); - - return status; -} - -template<typename T> -status_t readByteVectorInternalPtr( - const Parcel* parcel, - std::unique_ptr<std::vector<T>>* val) { - const int32_t start = parcel->dataPosition(); - int32_t size; - status_t status = parcel->readInt32(&size); - val->reset(); - - if (status != OK || size < 0) { - return status; - } - - parcel->setDataPosition(start); - val->reset(new (std::nothrow) std::vector<T>()); - - status = readByteVectorInternal(parcel, val->get()); - - if (status != OK) { - val->reset(); - } - - return status; -} - -} // namespace - status_t Parcel::readByteVector(std::vector<int8_t>* val) const { - return readByteVectorInternal(this, val); + size_t size; + if (status_t status = reserveOutVector(val, &size); status != OK) return status; + return readByteVectorInternal(val, size); } status_t Parcel::readByteVector(std::vector<uint8_t>* val) const { - return readByteVectorInternal(this, val); + size_t size; + if (status_t status = reserveOutVector(val, &size); status != OK) return status; + return readByteVectorInternal(val, size); } status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const { - return readByteVectorInternalPtr(this, val); + size_t size; + if (status_t status = reserveOutVector(val, &size); status != OK) return status; + if (val->get() == nullptr) { + // reserveOutVector does not create the out vector if size is < 0. + // This occurs when writing a null byte vector. + return OK; + } + return readByteVectorInternal(val->get(), size); } status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const { - return readByteVectorInternalPtr(this, val); + size_t size; + if (status_t status = reserveOutVector(val, &size); status != OK) return status; + if (val->get() == nullptr) { + // reserveOutVector does not create the out vector if size is < 0. + // This occurs when writing a null byte vector. + return OK; + } + return readByteVectorInternal(val->get(), size); } status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const { @@ -2207,7 +1922,7 @@ status_t Parcel::readStrongBinder(sp<IBinder>* val) const status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const { - return unflatten_binder(ProcessState::self(), *this, val); + return unflattenBinder(val); } sp<IBinder> Parcel::readStrongBinder() const @@ -2220,13 +1935,6 @@ sp<IBinder> Parcel::readStrongBinder() const return val; } -wp<IBinder> Parcel::readWeakBinder() const -{ - wp<IBinder> val; - unflatten_binder(ProcessState::self(), *this, &val); - return val; -} - status_t Parcel::readParcelable(Parcelable* parcelable) const { int32_t have_parcelable = 0; status_t status = readInt32(&have_parcelable); @@ -2239,10 +1947,6 @@ status_t Parcel::readParcelable(Parcelable* parcelable) const { return parcelable->readFromParcel(this); } -status_t Parcel::readValue(binder::Value* value) const { - return value->readFromParcel(this); -} - int32_t Parcel::readExceptionCode() const { binder::Status status; @@ -2594,7 +2298,7 @@ void Parcel::print(TextOutput& to, uint32_t /*flags*/) const } else if (dataSize() > 0) { const uint8_t* DATA = data(); to << indent << HexDump(DATA, dataSize()) << dedent; - const binder_size_t* OBJS = objects(); + const binder_size_t* OBJS = mObjects; const size_t N = objectsCount(); for (size_t i=0; i<N; i++) { const flat_binder_object* flat @@ -3004,4 +2708,4 @@ void Parcel::Blob::clear() { mMutable = false; } -}; // namespace android +} // namespace android diff --git a/libs/binder/include/private/binder/ParcelValTypes.h b/libs/binder/ParcelValTypes.h index 666d22a57f..666d22a57f 100644 --- a/libs/binder/include/private/binder/ParcelValTypes.h +++ b/libs/binder/ParcelValTypes.h diff --git a/libs/binder/PermissionCache.cpp b/libs/binder/PermissionCache.cpp index a4c28ad74e..75a6d22969 100644 --- a/libs/binder/PermissionCache.cpp +++ b/libs/binder/PermissionCache.cpp @@ -110,4 +110,4 @@ bool PermissionCache::checkPermission( } // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android diff --git a/libs/binder/PermissionController.cpp b/libs/binder/PermissionController.cpp index 34b2ca5170..0c8924503d 100644 --- a/libs/binder/PermissionController.cpp +++ b/libs/binder/PermissionController.cpp @@ -85,4 +85,4 @@ int PermissionController::getPackageUid(const String16& package, int flags) return service != nullptr ? service->getPackageUid(package, flags) : -1; } -}; // namespace android +} // namespace android diff --git a/libs/binder/PersistableBundle.cpp b/libs/binder/PersistableBundle.cpp index c0aec0a979..97a6c94635 100644 --- a/libs/binder/PersistableBundle.cpp +++ b/libs/binder/PersistableBundle.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "PersistableBundle" #include <binder/PersistableBundle.h> -#include <private/binder/ParcelValTypes.h> #include <limits> @@ -26,6 +25,8 @@ #include <log/log.h> #include <utils/Errors.h> +#include "ParcelValTypes.h" + using android::BAD_TYPE; using android::BAD_VALUE; using android::NO_ERROR; diff --git a/libs/binder/ProcessInfoService.cpp b/libs/binder/ProcessInfoService.cpp index 5cb2033b07..00d6eefabe 100644 --- a/libs/binder/ProcessInfoService.cpp +++ b/libs/binder/ProcessInfoService.cpp @@ -101,4 +101,4 @@ void ProcessInfoService::updateBinderLocked() { ANDROID_SINGLETON_STATIC_INSTANCE(ProcessInfoService); -}; // namespace android +} // namespace android diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 63f49ddba7..c0f5e31d72 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -21,14 +21,14 @@ #include <binder/BpBinder.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <binder/Stability.h> #include <cutils/atomic.h> #include <utils/Log.h> #include <utils/String8.h> -#include <utils/String8.h> #include <utils/threads.h> #include <private/binder/binder_module.h> -#include <private/binder/Static.h> +#include "Static.h" #include <errno.h> #include <fcntl.h> @@ -60,14 +60,14 @@ public: : mIsMain(isMain) { } - + protected: virtual bool threadLoop() { IPCThreadState::self()->joinThreadPool(mIsMain); return false; } - + const bool mIsMain; }; @@ -108,56 +108,15 @@ sp<ProcessState> ProcessState::selfOrNull() return gProcess; } -void ProcessState::setContextObject(const sp<IBinder>& object) -{ - setContextObject(object, String16("default")); -} - sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/) { - return getStrongProxyForHandle(0); -} + sp<IBinder> context = getStrongProxyForHandle(0); -void ProcessState::setContextObject(const sp<IBinder>& object, const String16& name) -{ - AutoMutex _l(mLock); - mContexts.add(name, object); -} + // The root object is special since we get it directly from the driver, it is never + // written by Parcell::writeStrongBinder. + internal::Stability::tryMarkCompilationUnit(context.get()); -sp<IBinder> ProcessState::getContextObject(const String16& name, const sp<IBinder>& caller) -{ - mLock.lock(); - sp<IBinder> object( - mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : nullptr); - mLock.unlock(); - - //printf("Getting context object %s for %p\n", String8(name).string(), caller.get()); - - if (object != nullptr) return object; - - // Don't attempt to retrieve contexts if we manage them - if (mManagesContexts) { - ALOGE("getContextObject(%s) failed, but we manage the contexts!\n", - String8(name).string()); - return nullptr; - } - - IPCThreadState* ipc = IPCThreadState::self(); - { - Parcel data, reply; - // no interface token on this magic transaction - data.writeString16(name); - data.writeStrongBinder(caller); - status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0); - if (result == NO_ERROR) { - object = reply.readStrongBinder(); - } - } - - ipc->flushCommands(); - - if (object != nullptr) setContextObject(object, name); - return object; + return context; } void ProcessState::startThreadPool() @@ -169,41 +128,33 @@ void ProcessState::startThreadPool() } } -bool ProcessState::isContextManager(void) const -{ - return mManagesContexts; -} - bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData) { - if (!mManagesContexts) { - AutoMutex _l(mLock); - mBinderContextCheckFunc = checkFunc; - mBinderContextUserData = userData; + AutoMutex _l(mLock); + mBinderContextCheckFunc = checkFunc; + mBinderContextUserData = userData; - flat_binder_object obj { - .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX, - }; + flat_binder_object obj { + .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX, + }; - status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj); + int result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj); - // fallback to original method - if (result != 0) { - android_errorWriteLog(0x534e4554, "121035042"); + // fallback to original method + if (result != 0) { + android_errorWriteLog(0x534e4554, "121035042"); - int dummy = 0; - result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); - } + int dummy = 0; + result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); + } - if (result == 0) { - mManagesContexts = true; - } else if (result == -1) { - mBinderContextCheckFunc = nullptr; - mBinderContextUserData = nullptr; - ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); - } + if (result == -1) { + mBinderContextCheckFunc = nullptr; + mBinderContextUserData = nullptr; + ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); } - return mManagesContexts; + + return result == 0; } // Get references to userspace objects held by the kernel binder driver @@ -237,8 +188,33 @@ ssize_t ProcessState::getKernelReferences(size_t buf_count, uintptr_t* buf) return count; } +// Queries the driver for the current strong reference count of the node +// that the handle points to. Can only be used by the servicemanager. +// +// Returns -1 in case of failure, otherwise the strong reference count. +ssize_t ProcessState::getStrongRefCountForNodeByHandle(int32_t handle) { + binder_node_info_for_ref info; + memset(&info, 0, sizeof(binder_node_info_for_ref)); + + info.handle = handle; + + status_t result = ioctl(mDriverFD, BINDER_GET_NODE_INFO_FOR_REF, &info); + + if (result != OK) { + static bool logged = false; + if (!logged) { + ALOGW("Kernel does not support BINDER_GET_NODE_INFO_FOR_REF."); + logged = true; + } + return -1; + } + + return info.strong_count; +} + void ProcessState::setCallRestriction(CallRestriction restriction) { - LOG_ALWAYS_FATAL_IF(IPCThreadState::selfOrNull(), "Call restrictions must be set before the threadpool is started."); + LOG_ALWAYS_FATAL_IF(IPCThreadState::selfOrNull() != nullptr, + "Call restrictions must be set before the threadpool is started."); mCallRestriction = restriction; } @@ -266,8 +242,12 @@ sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) if (e != nullptr) { // We need to create a new BpBinder if there isn't currently one, OR we - // are unable to acquire a weak reference on this current one. See comment - // in getWeakProxyForHandle() for more info about this. + // are unable to acquire a weak reference on this current one. The + // attemptIncWeak() is safe because we know the BpBinder destructor will always + // call expungeHandle(), which acquires the same lock we are holding now. + // We need to do this because there is a race condition between someone + // releasing a reference on this BpBinder, and a new reference on its handle + // arriving from the driver. IBinder* b = e->binder; if (b == nullptr || !e->refs->attemptIncWeak(this)) { if (handle == 0) { @@ -313,41 +293,10 @@ sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) return result; } -wp<IBinder> ProcessState::getWeakProxyForHandle(int32_t handle) -{ - wp<IBinder> result; - - AutoMutex _l(mLock); - - handle_entry* e = lookupHandleLocked(handle); - - if (e != nullptr) { - // We need to create a new BpBinder if there isn't currently one, OR we - // are unable to acquire a weak reference on this current one. The - // attemptIncWeak() is safe because we know the BpBinder destructor will always - // call expungeHandle(), which acquires the same lock we are holding now. - // We need to do this because there is a race condition between someone - // releasing a reference on this BpBinder, and a new reference on its handle - // arriving from the driver. - IBinder* b = e->binder; - if (b == nullptr || !e->refs->attemptIncWeak(this)) { - b = BpBinder::create(handle); - result = b; - e->binder = b; - if (b) e->refs = b->getWeakRefs(); - } else { - result = b; - e->refs->decWeak(this); - } - } - - return result; -} - void ProcessState::expungeHandle(int32_t handle, IBinder* binder) { AutoMutex _l(mLock); - + handle_entry* e = lookupHandleLocked(handle); // This handle may have already been replaced with a new BpBinder @@ -430,13 +379,18 @@ ProcessState::ProcessState(const char *driver) , mExecutingThreadsCount(0) , mMaxThreads(DEFAULT_MAX_BINDER_THREADS) , mStarvationStartTimeMs(0) - , mManagesContexts(false) , mBinderContextCheckFunc(nullptr) , mBinderContextUserData(nullptr) , mThreadPoolStarted(false) , mThreadPoolSeq(1) , mCallRestriction(CallRestriction::NONE) { + +// TODO(b/139016109): enforce in build system +#if defined(__ANDROID_APEX__) + LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable."); +#endif + if (mDriverFD >= 0) { // mmap the binder, providing a chunk of virtual address space to receive transactions. mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); @@ -449,7 +403,7 @@ ProcessState::ProcessState(const char *driver) } } - LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating."); + LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver '%s' could not be opened. Terminating.", driver); } ProcessState::~ProcessState() @@ -462,5 +416,5 @@ ProcessState::~ProcessState() } mDriverFD = -1; } - -}; // namespace android + +} // namespace android diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp new file mode 100644 index 0000000000..7ce5e36292 --- /dev/null +++ b/libs/binder/Stability.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include <binder/Stability.h> + +namespace android { +namespace internal { + +void Stability::markCompilationUnit(IBinder* binder) { + status_t result = set(binder, kLocalStability, true /*log*/); + LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object."); +} + +void Stability::markVintf(IBinder* binder) { + status_t result = set(binder, Level::VINTF, true /*log*/); + LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object."); +} + +void Stability::debugLogStability(const std::string& tag, const sp<IBinder>& binder) { + ALOGE("%s: stability is %s", tag.c_str(), stabilityString(get(binder.get())).c_str()); +} + +void Stability::markVndk(IBinder* binder) { + status_t result = set(binder, Level::VENDOR, true /*log*/); + LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object."); +} + +bool Stability::requiresVintfDeclaration(const sp<IBinder>& binder) { + return check(get(binder.get()), Level::VINTF); +} + +void Stability::tryMarkCompilationUnit(IBinder* binder) { + (void) set(binder, kLocalStability, false /*log*/); +} + +status_t Stability::set(IBinder* binder, int32_t stability, bool log) { + Level currentStability = get(binder); + + // null binder is always written w/ 'UNDECLARED' stability + if (binder == nullptr) { + if (stability == UNDECLARED) { + return OK; + } else { + if (log) { + ALOGE("Null binder written with stability %s.", + stabilityString(stability).c_str()); + } + return BAD_TYPE; + } + } + + if (!isDeclaredStability(stability)) { + if (log) { + ALOGE("Can only set known stability, not %d.", stability); + } + return BAD_TYPE; + } + + if (currentStability != Level::UNDECLARED && currentStability != stability) { + if (log) { + ALOGE("Interface being set with %s but it is already marked as %s.", + stabilityString(stability).c_str(), stabilityString(currentStability).c_str()); + } + return BAD_TYPE; + } + + if (currentStability == stability) return OK; + + binder->attachObject( + reinterpret_cast<void*>(&Stability::get), + reinterpret_cast<void*>(stability), + nullptr /*cleanupCookie*/, + nullptr /*cleanup function*/); + + return OK; +} + +Stability::Level Stability::get(IBinder* binder) { + if (binder == nullptr) return UNDECLARED; + + return static_cast<Level>(reinterpret_cast<intptr_t>( + binder->findObject(reinterpret_cast<void*>(&Stability::get)))); +} + +bool Stability::check(int32_t provided, Level required) { + bool stable = (provided & required) == required; + + if (!isDeclaredStability(provided) && provided != UNDECLARED) { + ALOGE("Unknown stability when checking interface stability %d.", provided); + + stable = false; + } + + return stable; +} + +bool Stability::isDeclaredStability(int32_t stability) { + return stability == VENDOR || stability == SYSTEM || stability == VINTF; +} + +std::string Stability::stabilityString(int32_t stability) { + switch (stability) { + case Level::UNDECLARED: return "undeclared stability"; + case Level::VENDOR: return "vendor stability"; + case Level::SYSTEM: return "system stability"; + case Level::VINTF: return "vintf stability"; + } + return "unknown stability " + std::to_string(stability); +} + +} // namespace internal +} // namespace stability diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp index bd0e6f9a11..7a77f6de54 100644 --- a/libs/binder/Static.cpp +++ b/libs/binder/Static.cpp @@ -17,9 +17,9 @@ // All static variables go here, to control initialization and // destruction order in the library. -#include <private/binder/Static.h> +#include "Static.h" -#include <binder/BufferedTextOutput.h> +#include "BufferedTextOutput.h" #include <binder/IPCThreadState.h> #include <utils/Log.h> @@ -54,7 +54,9 @@ public: protected: virtual status_t writeLines(const struct iovec& vec, size_t N) { - writev(mFD, &vec, N); + ssize_t ret = writev(mFD, &vec, N); + if (ret == -1) return -errno; + if (static_cast<size_t>(ret) != N) return UNKNOWN_ERROR; return NO_ERROR; } @@ -75,13 +77,4 @@ TextOutput& aerr(gStderrTextOutput); Mutex& gProcessMutex = *new Mutex; sp<ProcessState> gProcess; -// ------------ IServiceManager.cpp - -Mutex gDefaultServiceManagerLock; -sp<IServiceManager> gDefaultServiceManager; -#ifndef __ANDROID_VNDK__ -sp<IPermissionController> gPermissionController; -#endif -bool gSystemBootCompleted = false; - } // namespace android diff --git a/libs/binder/include/private/binder/Static.h b/libs/binder/Static.h index 171be7791e..f8e0ee5f8d 100644 --- a/libs/binder/include/private/binder/Static.h +++ b/libs/binder/Static.h @@ -21,10 +21,6 @@ #include <binder/IBinder.h> #include <binder/ProcessState.h> -#ifndef __ANDROID_VNDK__ -#include <binder/IPermissionController.h> -#endif -#include <binder/IServiceManager.h> namespace android { @@ -35,12 +31,4 @@ extern Vector<int32_t> gTextBuffers; extern Mutex& gProcessMutex; extern sp<ProcessState> gProcess; -// For IServiceManager.cpp -extern Mutex gDefaultServiceManagerLock; -extern sp<IServiceManager> gDefaultServiceManager; -#ifndef __ANDROID_VNDK__ -extern sp<IPermissionController> gPermissionController; -#endif -extern bool gSystemBootCompleted; - } // namespace android diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp index 0ad99cee3f..674f0657a9 100644 --- a/libs/binder/Status.cpp +++ b/libs/binder/Status.cpp @@ -232,9 +232,10 @@ String8 Status::toString8() const { ret.append("No error"); } else { ret.appendFormat("Status(%d, %s): '", mException, exceptionToString(mException).c_str()); - if (mException == EX_SERVICE_SPECIFIC || - mException == EX_TRANSACTION_FAILED) { + if (mException == EX_SERVICE_SPECIFIC) { ret.appendFormat("%d: ", mErrorCode); + } else if (mException == EX_TRANSACTION_FAILED) { + ret.appendFormat("%s: ", statusToString(mErrorCode).c_str()); } ret.append(String8(mMessage)); ret.append("'"); diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING new file mode 100644 index 0000000000..9aa76512c8 --- /dev/null +++ b/libs/binder/TEST_MAPPING @@ -0,0 +1,34 @@ +{ + "presubmit": [ + { + "name": "binderSafeInterfaceTest" + }, + { + "name": "binderVendorDoubleLoadTest" + }, + { + "name": "binderDriverInterfaceTest" + }, + { + "name": "binderTextOutputTest" + }, + { + "name": "binderLibTest" + }, + { + "name": "binderStabilityTest" + }, + { + "name": "libbinder_ndk_unit_test" + }, + { + "name": "CtsNdkBinderTestCases" + }, + { + "name": "aidl_lazy_test" + }, + { + "name": "libbinderthreadstateutils_test" + } + ] +} diff --git a/libs/binder/TextOutput.cpp b/libs/binder/TextOutput.cpp index 101eba318f..684a7dcc51 100644 --- a/libs/binder/TextOutput.cpp +++ b/libs/binder/TextOutput.cpp @@ -69,4 +69,4 @@ TextOutput& operator<<(TextOutput& to, const HexDump& val) return to; } -}; // namespace android +} // namespace android diff --git a/libs/binder/Value.cpp b/libs/binder/Value.cpp deleted file mode 100644 index 19c57ba128..0000000000 --- a/libs/binder/Value.cpp +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "Value" - -#include <binder/Value.h> - -#include <limits> - -#include <binder/IBinder.h> -#include <binder/Parcel.h> -#include <binder/Map.h> -#include <private/binder/ParcelValTypes.h> -#include <log/log.h> -#include <utils/Errors.h> - -using android::BAD_TYPE; -using android::BAD_VALUE; -using android::NO_ERROR; -using android::UNEXPECTED_NULL; -using android::Parcel; -using android::sp; -using android::status_t; -using std::map; -using std::set; -using std::vector; -using android::binder::Value; -using android::IBinder; -using android::os::PersistableBundle; -using namespace android::binder; - -// ==================================================================== - -#define RETURN_IF_FAILED(calledOnce) \ - do { \ - status_t returnStatus = calledOnce; \ - if (returnStatus) { \ - ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \ - return returnStatus; \ - } \ - } while(false) - -// ==================================================================== - -/* These `internal_type_ptr()` functions allow this - * class to work without C++ RTTI support. This technique - * only works properly when called directly from this file, - * but that is OK because that is the only place we will - * be calling them from. */ -template<class T> const void* internal_type_ptr() -{ - static const T *marker; - return (void*)▮ -} - -/* Allows the type to be specified by the argument - * instead of inside angle brackets. */ -template<class T> const void* internal_type_ptr(const T&) -{ - return internal_type_ptr<T>(); -} - -// ==================================================================== - -namespace android { - -namespace binder { - -class Value::ContentBase { -public: - virtual ~ContentBase() = default; - virtual const void* type_ptr() const = 0; - virtual ContentBase * clone() const = 0; - virtual bool operator==(const ContentBase& rhs) const = 0; - -#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO - virtual const std::type_info &type() const = 0; -#endif - - template<typename T> bool get(T* out) const; -}; - -/* This is the actual class that holds the value. */ -template<typename T> class Value::Content : public Value::ContentBase { -public: - Content() = default; - explicit Content(const T & value) : mValue(value) { } - - virtual ~Content() = default; - -#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO - virtual const std::type_info &type() const override - { - return typeid(T); - } -#endif - - virtual const void* type_ptr() const override - { - return internal_type_ptr<T>(); - } - - virtual ContentBase * clone() const override - { - return new Content(mValue); - }; - - virtual bool operator==(const ContentBase& rhs) const override - { - if (type_ptr() != rhs.type_ptr()) { - return false; - } - return mValue == static_cast<const Content<T>* >(&rhs)->mValue; - } - - T mValue; -}; - -template<typename T> bool Value::ContentBase::get(T* out) const -{ - if (internal_type_ptr(*out) != type_ptr()) - { - return false; - } - - *out = static_cast<const Content<T>*>(this)->mValue; - - return true; -} - -// ==================================================================== - -Value::Value() : mContent(nullptr) -{ -} - -Value::Value(const Value& value) - : mContent(value.mContent ? value.mContent->clone() : nullptr) -{ -} - -Value::~Value() -{ - delete mContent; -} - -bool Value::operator==(const Value& rhs) const -{ - const Value& lhs(*this); - - if (lhs.empty() && rhs.empty()) { - return true; - } - - if ( (lhs.mContent == nullptr) - || (rhs.mContent == nullptr) - ) { - return false; - } - - return *lhs.mContent == *rhs.mContent; -} - -Value& Value::swap(Value &rhs) -{ - std::swap(mContent, rhs.mContent); - return *this; -} - -Value& Value::operator=(const Value& rhs) -{ - if (this != &rhs) { - delete mContent; - mContent = rhs.mContent - ? rhs.mContent->clone() - : nullptr; - } - return *this; -} - -bool Value::empty() const -{ - return mContent == nullptr; -} - -void Value::clear() -{ - delete mContent; - mContent = nullptr; -} - -int32_t Value::parcelType() const -{ - const void* t_info(mContent ? mContent->type_ptr() : nullptr); - - if (t_info == internal_type_ptr<bool>()) return VAL_BOOLEAN; - if (t_info == internal_type_ptr<uint8_t>()) return VAL_BYTE; - if (t_info == internal_type_ptr<int32_t>()) return VAL_INTEGER; - if (t_info == internal_type_ptr<int64_t>()) return VAL_LONG; - if (t_info == internal_type_ptr<double>()) return VAL_DOUBLE; - if (t_info == internal_type_ptr<String16>()) return VAL_STRING; - - if (t_info == internal_type_ptr<vector<bool>>()) return VAL_BOOLEANARRAY; - if (t_info == internal_type_ptr<vector<uint8_t>>()) return VAL_BYTEARRAY; - if (t_info == internal_type_ptr<vector<int32_t>>()) return VAL_INTARRAY; - if (t_info == internal_type_ptr<vector<int64_t>>()) return VAL_LONGARRAY; - if (t_info == internal_type_ptr<vector<double>>()) return VAL_DOUBLEARRAY; - if (t_info == internal_type_ptr<vector<String16>>()) return VAL_STRINGARRAY; - - if (t_info == internal_type_ptr<Map>()) return VAL_MAP; - if (t_info == internal_type_ptr<PersistableBundle>()) return VAL_PERSISTABLEBUNDLE; - - return VAL_NULL; -} - -#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO -const std::type_info& Value::type() const -{ - return mContent != nullptr - ? mContent->type() - : typeid(void); -} -#endif // ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO - -#define DEF_TYPE_ACCESSORS(T, TYPENAME) \ - bool Value::is ## TYPENAME() const \ - { \ - return mContent \ - ? internal_type_ptr<T>() == mContent->type_ptr() \ - : false; \ - } \ - bool Value::get ## TYPENAME(T* out) const \ - { \ - return mContent \ - ? mContent->get(out) \ - : false; \ - } \ - void Value::put ## TYPENAME(const T& in) \ - { \ - *this = in; \ - } \ - Value& Value::operator=(const T& rhs) \ - { \ - delete mContent; \ - mContent = new Content< T >(rhs); \ - return *this; \ - } \ - Value::Value(const T& value) \ - : mContent(new Content< T >(value)) \ - { } - -DEF_TYPE_ACCESSORS(bool, Boolean) -DEF_TYPE_ACCESSORS(int8_t, Byte) -DEF_TYPE_ACCESSORS(int32_t, Int) -DEF_TYPE_ACCESSORS(int64_t, Long) -DEF_TYPE_ACCESSORS(double, Double) -DEF_TYPE_ACCESSORS(String16, String) - -DEF_TYPE_ACCESSORS(std::vector<bool>, BooleanVector) -DEF_TYPE_ACCESSORS(std::vector<uint8_t>, ByteVector) -DEF_TYPE_ACCESSORS(std::vector<int32_t>, IntVector) -DEF_TYPE_ACCESSORS(std::vector<int64_t>, LongVector) -DEF_TYPE_ACCESSORS(std::vector<double>, DoubleVector) -DEF_TYPE_ACCESSORS(std::vector<String16>, StringVector) - -DEF_TYPE_ACCESSORS(::android::binder::Map, Map) -DEF_TYPE_ACCESSORS(PersistableBundle, PersistableBundle) - -bool Value::getString(String8* out) const -{ - String16 val; - bool ret = getString(&val); - if (ret) { - *out = String8(val); - } - return ret; -} - -bool Value::getString(::std::string* out) const -{ - String8 val; - bool ret = getString(&val); - if (ret) { - *out = val.string(); - } - return ret; -} - -status_t Value::writeToParcel(Parcel* parcel) const -{ - // This implementation needs to be kept in sync with the writeValue - // implementation in frameworks/base/core/java/android/os/Parcel.java - -#define BEGIN_HANDLE_WRITE() \ - do { \ - const void* t_info(mContent?mContent->type_ptr():nullptr); \ - if (false) { } -#define HANDLE_WRITE_TYPE(T, TYPEVAL, TYPEMETHOD) \ - else if (t_info == internal_type_ptr<T>()) { \ - RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \ - RETURN_IF_FAILED(parcel->TYPEMETHOD(static_cast<const Content<T>*>(mContent)->mValue)); \ - } -#define HANDLE_WRITE_PARCELABLE(T, TYPEVAL) \ - else if (t_info == internal_type_ptr<T>()) { \ - RETURN_IF_FAILED(parcel->writeInt32(TYPEVAL)); \ - RETURN_IF_FAILED(static_cast<const Content<T>*>(mContent)->mValue.writeToParcel(parcel)); \ - } -#define END_HANDLE_WRITE() \ - else { \ - ALOGE("writeToParcel: Type not supported"); \ - return BAD_TYPE; \ - } \ - } while (false); - - BEGIN_HANDLE_WRITE() - - HANDLE_WRITE_TYPE(bool, VAL_BOOLEAN, writeBool) - HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte) - HANDLE_WRITE_TYPE(int8_t, VAL_BYTE, writeByte) - HANDLE_WRITE_TYPE(int32_t, VAL_INTEGER, writeInt32) - HANDLE_WRITE_TYPE(int64_t, VAL_LONG, writeInt64) - HANDLE_WRITE_TYPE(double, VAL_DOUBLE, writeDouble) - HANDLE_WRITE_TYPE(String16, VAL_STRING, writeString16) - - HANDLE_WRITE_TYPE(vector<bool>, VAL_BOOLEANARRAY, writeBoolVector) - HANDLE_WRITE_TYPE(vector<uint8_t>, VAL_BYTEARRAY, writeByteVector) - HANDLE_WRITE_TYPE(vector<int8_t>, VAL_BYTEARRAY, writeByteVector) - HANDLE_WRITE_TYPE(vector<int32_t>, VAL_INTARRAY, writeInt32Vector) - HANDLE_WRITE_TYPE(vector<int64_t>, VAL_LONGARRAY, writeInt64Vector) - HANDLE_WRITE_TYPE(vector<double>, VAL_DOUBLEARRAY, writeDoubleVector) - HANDLE_WRITE_TYPE(vector<String16>, VAL_STRINGARRAY, writeString16Vector) - - HANDLE_WRITE_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE) - - END_HANDLE_WRITE() - - return NO_ERROR; - -#undef BEGIN_HANDLE_WRITE -#undef HANDLE_WRITE_TYPE -#undef HANDLE_WRITE_PARCELABLE -#undef END_HANDLE_WRITE -} - -status_t Value::readFromParcel(const Parcel* parcel) -{ - // This implementation needs to be kept in sync with the readValue - // implementation in frameworks/base/core/java/android/os/Parcel.javai - -#define BEGIN_HANDLE_READ() \ - switch(value_type) { \ - default: \ - ALOGE("readFromParcel: Parcel type %d is not supported", value_type); \ - return BAD_TYPE; -#define HANDLE_READ_TYPE(T, TYPEVAL, TYPEMETHOD) \ - case TYPEVAL: \ - mContent = new Content<T>(); \ - RETURN_IF_FAILED(parcel->TYPEMETHOD(&static_cast<Content<T>*>(mContent)->mValue)); \ - break; -#define HANDLE_READ_PARCELABLE(T, TYPEVAL) \ - case TYPEVAL: \ - mContent = new Content<T>(); \ - RETURN_IF_FAILED(static_cast<Content<T>*>(mContent)->mValue.readFromParcel(parcel)); \ - break; -#define END_HANDLE_READ() \ - } - - int32_t value_type = VAL_NULL; - - delete mContent; - mContent = nullptr; - - RETURN_IF_FAILED(parcel->readInt32(&value_type)); - - BEGIN_HANDLE_READ() - - HANDLE_READ_TYPE(bool, VAL_BOOLEAN, readBool) - HANDLE_READ_TYPE(int8_t, VAL_BYTE, readByte) - HANDLE_READ_TYPE(int32_t, VAL_INTEGER, readInt32) - HANDLE_READ_TYPE(int64_t, VAL_LONG, readInt64) - HANDLE_READ_TYPE(double, VAL_DOUBLE, readDouble) - HANDLE_READ_TYPE(String16, VAL_STRING, readString16) - - HANDLE_READ_TYPE(vector<bool>, VAL_BOOLEANARRAY, readBoolVector) - HANDLE_READ_TYPE(vector<uint8_t>, VAL_BYTEARRAY, readByteVector) - HANDLE_READ_TYPE(vector<int32_t>, VAL_INTARRAY, readInt32Vector) - HANDLE_READ_TYPE(vector<int64_t>, VAL_LONGARRAY, readInt64Vector) - HANDLE_READ_TYPE(vector<double>, VAL_DOUBLEARRAY, readDoubleVector) - HANDLE_READ_TYPE(vector<String16>, VAL_STRINGARRAY, readString16Vector) - - HANDLE_READ_PARCELABLE(PersistableBundle, VAL_PERSISTABLEBUNDLE) - - END_HANDLE_READ() - - return NO_ERROR; - -#undef BEGIN_HANDLE_READ -#undef HANDLE_READ_TYPE -#undef HANDLE_READ_PARCELABLE -#undef END_HANDLE_READ -} - -} // namespace binder - -} // namespace android - -/* vim: set ts=4 sw=4 tw=0 et :*/ diff --git a/libs/binder/aidl/android/os/IClientCallback.aidl b/libs/binder/aidl/android/os/IClientCallback.aidl new file mode 100644 index 0000000000..36d7ee61fb --- /dev/null +++ b/libs/binder/aidl/android/os/IClientCallback.aidl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * @hide + */ +oneway interface IClientCallback { + /** + * This is called when there is a transition between having >= 1 clients and having 0 clients + * (or vice versa). + * + * Upon receiving hasClients false, if the process decides to exit, it is recommended to try to + * unregister using IServiceManager's tryUnregister before quitting in case another client + * associates. + * + * @param registered binder 'server' registered with IServiceManager's registerClientCallback + * @param hasClients whether there are currently clients + * true - when there are >= 1 clients. This must be called as soon as IServiceManager::get + * is called (no race). + * false - when there are 0 clients. This may be delayed if it is thought that another + * may be used again soon. + */ + void onClients(IBinder registered, boolean hasClients); +} diff --git a/libs/binder/aidl/android/os/IServiceCallback.aidl b/libs/binder/aidl/android/os/IServiceCallback.aidl new file mode 100644 index 0000000000..b29dfedf70 --- /dev/null +++ b/libs/binder/aidl/android/os/IServiceCallback.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * @hide + */ +oneway interface IServiceCallback { + /** + * Called when a service is registered. + * + * @param name the service name that has been registered with + * @param binder the binder that is registered + */ + void onRegistration(@utf8InCpp String name, IBinder binder); +} diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl new file mode 100644 index 0000000000..ff154603e4 --- /dev/null +++ b/libs/binder/aidl/android/os/IServiceManager.aidl @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import android.os.IClientCallback; +import android.os.IServiceCallback; + +/** + * Basic interface for finding and publishing system services. + * + * You likely want to use android.os.ServiceManager in Java or + * android::IServiceManager in C++ in order to use this interface. + * + * @hide + */ +interface IServiceManager { + /* + * Must update values in IServiceManager.h + */ + /* Allows services to dump sections according to priorities. */ + const int DUMP_FLAG_PRIORITY_CRITICAL = 1 << 0; + const int DUMP_FLAG_PRIORITY_HIGH = 1 << 1; + const int DUMP_FLAG_PRIORITY_NORMAL = 1 << 2; + /** + * Services are by default registered with a DEFAULT dump priority. DEFAULT priority has the + * same priority as NORMAL priority but the services are not called with dump priority + * arguments. + */ + const int DUMP_FLAG_PRIORITY_DEFAULT = 1 << 3; + + const int DUMP_FLAG_PRIORITY_ALL = 15; + // DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_HIGH + // | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PRIORITY_DEFAULT; + + /* Allows services to dump sections in protobuf format. */ + const int DUMP_FLAG_PROTO = 1 << 4; + + /** + * Retrieve an existing service called @a name from the + * service manager. + * + * This is the same as checkService (returns immediately) but + * exists for legacy purposes. + * + * Returns null if the service does not exist. + */ + @UnsupportedAppUsage + @nullable IBinder getService(@utf8InCpp String name); + + /** + * Retrieve an existing service called @a name from the service + * manager. Non-blocking. Returns null if the service does not + * exist. + */ + @UnsupportedAppUsage + @nullable IBinder checkService(@utf8InCpp String name); + + /** + * Place a new @a service called @a name into the service + * manager. + */ + void addService(@utf8InCpp String name, IBinder service, + boolean allowIsolated, int dumpPriority); + + /** + * Return a list of all currently running services. + */ + @utf8InCpp String[] listServices(int dumpPriority); + + /** + * Request a callback when a service is registered. + */ + void registerForNotifications(@utf8InCpp String name, IServiceCallback callback); + + /** + * Unregisters all requests for notifications for a specific callback. + */ + void unregisterForNotifications(@utf8InCpp String name, IServiceCallback callback); + + /** + * Returns whether a given interface is declared on the device, even if it + * is not started yet. For instance, this could be a service declared in the VINTF + * manifest. + */ + boolean isDeclared(@utf8InCpp String name); + + /** + * Request a callback when the number of clients of the service changes. + * Used by LazyServiceRegistrar to dynamically stop services that have no clients. + */ + void registerClientCallback(@utf8InCpp String name, IBinder service, IClientCallback callback); + + /** + * Attempt to unregister and remove a service. Will fail if the service is still in use. + */ + void tryUnregisterService(@utf8InCpp String name, IBinder service); +} diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h index 5f324c7965..9108e31758 100644 --- a/libs/binder/include/binder/ActivityManager.h +++ b/libs/binder/include/binder/ActivityManager.h @@ -89,7 +89,7 @@ private: }; -}; // namespace android +} // namespace android // --------------------------------------------------------------------------- #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h index 17493b4252..b19cde75b6 100644 --- a/libs/binder/include/binder/AppOpsManager.h +++ b/libs/binder/include/binder/AppOpsManager.h @@ -133,7 +133,7 @@ private: }; -}; // namespace android +} // namespace android // --------------------------------------------------------------------------- #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h index cf3ef84caa..3be61f9409 100644 --- a/libs/binder/include/binder/Binder.h +++ b/libs/binder/include/binder/Binder.h @@ -38,7 +38,7 @@ public: virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0); + uint32_t flags = 0) final; // NOLINTNEXTLINE(google-default-arguments) virtual status_t linkToDeath(const sp<DeathRecipient>& recipient, @@ -54,9 +54,9 @@ public: virtual void attachObject( const void* objectID, void* object, void* cleanupCookie, - object_cleanup_func func); - virtual void* findObject(const void* objectID) const; - virtual void detachObject(const void* objectID); + object_cleanup_func func) final; + virtual void* findObject(const void* objectID) const final; + virtual void detachObject(const void* objectID) final; virtual BBinder* localBinder(); @@ -64,6 +64,12 @@ public: // This must be called before the object is sent to another process. Not thread safe. void setRequestingSid(bool requestSid); + sp<IBinder> getExtension(); + // This must be called before the object is sent to another process. Not thread safe. + void setExtension(const sp<IBinder>& extension); + + pid_t getDebugPid(); + protected: virtual ~BBinder(); @@ -108,7 +114,7 @@ private: std::atomic<int32_t> mState; }; -}; // namespace android +} // namespace android // --------------------------------------------------------------------------- diff --git a/libs/binder/include/binder/BinderService.h b/libs/binder/include/binder/BinderService.h index 9230e89cdf..c17ae6f5fe 100644 --- a/libs/binder/include/binder/BinderService.h +++ b/libs/binder/include/binder/BinderService.h @@ -62,6 +62,6 @@ private: }; -}; // namespace android +} // namespace android // --------------------------------------------------------------------------- #endif // ANDROID_BINDER_SERVICE_H diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 1d4f88113a..7dca733b52 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -34,7 +34,7 @@ class BpBinder : public IBinder public: static BpBinder* create(int32_t handle); - inline int32_t handle() const { return mHandle; } + int32_t handle() const; virtual const String16& getInterfaceDescriptor() const; virtual bool isBinderAlive() const; @@ -45,7 +45,7 @@ public: virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0); + uint32_t flags = 0) final; // NOLINTNEXTLINE(google-default-arguments) virtual status_t linkToDeath(const sp<DeathRecipient>& recipient, @@ -61,13 +61,12 @@ public: virtual void attachObject( const void* objectID, void* object, void* cleanupCookie, - object_cleanup_func func); - virtual void* findObject(const void* objectID) const; - virtual void detachObject(const void* objectID); + object_cleanup_func func) final; + virtual void* findObject(const void* objectID) const final; + virtual void detachObject(const void* objectID) final; virtual BpBinder* remoteBinder(); - status_t setConstantData(const void* data, size_t size); void sendObituary(); static uint32_t getBinderProxyCount(uint32_t uid); @@ -145,7 +144,7 @@ private: static bool sBinderProxyThrottleCreate; }; -}; // namespace android +} // namespace android // --------------------------------------------------------------------------- diff --git a/libs/binder/include/binder/Debug.h b/libs/binder/include/binder/Debug.h index 58e2b32b3a..324e5c1c81 100644 --- a/libs/binder/include/binder/Debug.h +++ b/libs/binder/include/binder/Debug.h @@ -44,6 +44,6 @@ ssize_t getBinderKernelReferences(size_t count, uintptr_t* buf); __END_DECLS // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_BINDER_DEBUG_H diff --git a/libs/binder/include/binder/Enums.h b/libs/binder/include/binder/Enums.h new file mode 100644 index 0000000000..aec6f7038d --- /dev/null +++ b/libs/binder/include/binder/Enums.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <iterator> +#include <type_traits> + +namespace android { + +namespace internal { + +// Never instantiated. Used as a placeholder for template variables. +template <typename T> +struct invalid_type; + +// AIDL generates specializations of this for enums. +template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>> +constexpr invalid_type<EnumType> enum_values; +} // namespace internal + +// Usage: for (const auto v : enum_range<EnumType>() ) { ... } +template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>> +struct enum_range { + constexpr auto begin() const { return std::begin(internal::enum_values<EnumType>); } + constexpr auto end() const { return std::end(internal::enum_values<EnumType>); } +}; + +} // namespace android
\ No newline at end of file diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h index 6abc071c45..e0248f6624 100644 --- a/libs/binder/include/binder/IActivityManager.h +++ b/libs/binder/include/binder/IActivityManager.h @@ -51,7 +51,7 @@ public: // ------------------------------------------------------------------------------------ -}; // namespace android +} // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/IAppOpsCallback.h b/libs/binder/include/binder/IAppOpsCallback.h index b500219e37..76642606fc 100644 --- a/libs/binder/include/binder/IAppOpsCallback.h +++ b/libs/binder/include/binder/IAppOpsCallback.h @@ -52,7 +52,7 @@ public: // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h index 3dbd0d9f7a..b74c623e44 100644 --- a/libs/binder/include/binder/IAppOpsService.h +++ b/libs/binder/include/binder/IAppOpsService.h @@ -79,7 +79,7 @@ public: // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/IBatteryStats.h b/libs/binder/include/binder/IBatteryStats.h index 48da865702..b786f89f74 100644 --- a/libs/binder/include/binder/IBatteryStats.h +++ b/libs/binder/include/binder/IBatteryStats.h @@ -77,7 +77,7 @@ public: // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h index aa44285b6e..64604b74f0 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -22,9 +22,8 @@ #include <utils/String16.h> #include <utils/Vector.h> - -// linux/binder.h already defines this, but we can't just include it from there -// because there are host builds that include this file. +// linux/binder.h defines this, but we don't want to include it here in order to +// avoid exporting the kernel headers #ifndef B_PACK_CHARS #define B_PACK_CHARS(c1, c2, c3, c4) \ ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4)) @@ -59,9 +58,15 @@ public: SHELL_COMMAND_TRANSACTION = B_PACK_CHARS('_','C','M','D'), INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), SYSPROPS_TRANSACTION = B_PACK_CHARS('_', 'S', 'P', 'R'), + EXTENSION_TRANSACTION = B_PACK_CHARS('_', 'E', 'X', 'T'), + DEBUG_PID_TRANSACTION = B_PACK_CHARS('_', 'P', 'I', 'D'), // Corresponds to TF_ONE_WAY -- an asynchronous call. - FLAG_ONEWAY = 0x00000001 + FLAG_ONEWAY = 0x00000001, + + // Private userspace flag for transaction which is being requested from + // a vendor context. + FLAG_PRIVATE_VENDOR = 0x10000000, }; IBinder(); @@ -86,6 +91,54 @@ public: Vector<String16>& args, const sp<IShellCallback>& callback, const sp<IResultReceiver>& resultReceiver); + /** + * This allows someone to add their own additions to an interface without + * having to modify the original interface. + * + * For instance, imagine if we have this interface: + * interface IFoo { void doFoo(); } + * + * If an unrelated owner (perhaps in a downstream codebase) wants to make a + * change to the interface, they have two options: + * + * A). Historical option that has proven to be BAD! Only the original + * author of an interface should change an interface. If someone + * downstream wants additional functionality, they should not ever + * change the interface or use this method. + * + * BAD TO DO: interface IFoo { BAD TO DO + * BAD TO DO: void doFoo(); BAD TO DO + * BAD TO DO: + void doBar(); // adding a method BAD TO DO + * BAD TO DO: } BAD TO DO + * + * B). Option that this method enables! + * Leave the original interface unchanged (do not change IFoo!). + * Instead, create a new interface in a downstream package: + * + * package com.<name>; // new functionality in a new package + * interface IBar { void doBar(); } + * + * When registering the interface, add: + * sp<MyFoo> foo = new MyFoo; // class in AOSP codebase + * sp<MyBar> bar = new MyBar; // custom extension class + * foo->setExtension(bar); // use method in BBinder + * + * Then, clients of IFoo can get this extension: + * sp<IBinder> binder = ...; + * sp<IFoo> foo = interface_cast<IFoo>(binder); // handle if null + * sp<IBinder> barBinder; + * ... handle error ... = binder->getExtension(&barBinder); + * sp<IBar> bar = interface_cast<IBar>(barBinder); + * // if bar is null, then there is no extension or a different + * // type of extension + */ + status_t getExtension(sp<IBinder>* out); + + /** + * Dump PID for a binder, for debugging. + */ + status_t getDebugPid(pid_t* outPid); + // NOLINTNEXTLINE(google-default-arguments) virtual status_t transact( uint32_t code, const Parcel& data, @@ -193,7 +246,7 @@ protected: private: }; -}; // namespace android +} // namespace android // --------------------------------------------------------------------------- diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index 0d305608ca..79d9b79915 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -38,12 +38,32 @@ protected: // ---------------------------------------------------------------------- +/** + * If this is a local object and the descriptor matches, this will return the + * actual local object which is implementing the interface. Otherwise, this will + * return a proxy to the interface without checking the interface descriptor. + * This means that subsequent calls may fail with BAD_TYPE. + */ template<typename INTERFACE> inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj) { return INTERFACE::asInterface(obj); } +/** + * This is the same as interface_cast, except that it always checks to make sure + * the descriptor matches, and if it doesn't match, it will return nullptr. + */ +template<typename INTERFACE> +inline sp<INTERFACE> checked_interface_cast(const sp<IBinder>& obj) +{ + if (obj->getInterfaceDescriptor() != INTERFACE::descriptor) { + return nullptr; + } + + return interface_cast<INTERFACE>(obj); +} + // ---------------------------------------------------------------------- template<typename INTERFACE> @@ -88,8 +108,32 @@ private: \ public: \ +#define __IINTF_CONCAT(x, y) (x ## y) + +#ifndef DO_NOT_CHECK_MANUAL_BINDER_INTERFACES + +#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ + static_assert(internal::allowedManualInterface(NAME), \ + "b/64223827: Manually written binder interfaces are " \ + "considered error prone and frequently have bugs. " \ + "The preferred way to add interfaces is to define " \ + "an .aidl file to auto-generate the interface. If " \ + "an interface must be manually written, add its " \ + "name to the whitelist."); \ + DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ + +#else + #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ - const ::android::String16 I##INTERFACE::descriptor(NAME); \ + DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ + +#endif + +#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)\ + const ::android::StaticString16 \ + I##INTERFACE##_descriptor_static_str16(__IINTF_CONCAT(u, NAME));\ + const ::android::String16 I##INTERFACE::descriptor( \ + I##INTERFACE##_descriptor_static_str16); \ const ::android::String16& \ I##INTERFACE::getInterfaceDescriptor() const { \ return I##INTERFACE::descriptor; \ @@ -168,6 +212,122 @@ inline IBinder* BpInterface<INTERFACE>::onAsBinder() // ---------------------------------------------------------------------- -}; // namespace android +namespace internal { +constexpr const char* const kManualInterfaces[] = { + "android.app.IActivityManager", + "android.app.IUidObserver", + "android.drm.IDrm", + "android.dvr.IVsyncCallback", + "android.dvr.IVsyncService", + "android.gfx.tests.ICallback", + "android.gfx.tests.IIPCTest", + "android.gfx.tests.ISafeInterfaceTest", + "android.graphicsenv.IGpuService", + "android.gui.DisplayEventConnection", + "android.gui.IConsumerListener", + "android.gui.IGraphicBufferConsumer", + "android.gui.IRegionSamplingListener", + "android.gui.ITransactionComposerListener", + "android.gui.SensorEventConnection", + "android.gui.SensorServer", + "android.hardware.ICamera", + "android.hardware.ICameraClient", + "android.hardware.ICameraRecordingProxy", + "android.hardware.ICameraRecordingProxyListener", + "android.hardware.ICrypto", + "android.hardware.IOMXObserver", + "android.hardware.ISoundTrigger", + "android.hardware.ISoundTriggerClient", + "android.hardware.ISoundTriggerHwService", + "android.hardware.IStreamListener", + "android.hardware.IStreamSource", + "android.input.IInputFlinger", + "android.input.ISetInputWindowsListener", + "android.media.IAudioFlinger", + "android.media.IAudioFlingerClient", + "android.media.IAudioPolicyService", + "android.media.IAudioPolicyServiceClient", + "android.media.IAudioService", + "android.media.IAudioTrack", + "android.media.IDataSource", + "android.media.IDrmClient", + "android.media.IEffect", + "android.media.IEffectClient", + "android.media.IMediaAnalyticsService", + "android.media.IMediaCodecList", + "android.media.IMediaDrmService", + "android.media.IMediaExtractor", + "android.media.IMediaExtractorService", + "android.media.IMediaHTTPConnection", + "android.media.IMediaHTTPService", + "android.media.IMediaLogService", + "android.media.IMediaMetadataRetriever", + "android.media.IMediaPlayer", + "android.media.IMediaPlayerClient", + "android.media.IMediaPlayerService", + "android.media.IMediaRecorder", + "android.media.IMediaRecorderClient", + "android.media.IMediaResourceMonitor", + "android.media.IMediaSource", + "android.media.IRemoteDisplay", + "android.media.IRemoteDisplayClient", + "android.media.IResourceManagerClient", + "android.media.IResourceManagerService", + "android.os.IComplexTypeInterface", + "android.os.IPermissionController", + "android.os.IPingResponder", + "android.os.IPowerManager", + "android.os.IProcessInfoService", + "android.os.ISchedulingPolicyService", + "android.os.IStringConstants", + "android.os.storage.IObbActionListener", + "android.os.storage.IStorageEventListener", + "android.os.storage.IStorageManager", + "android.os.storage.IStorageShutdownObserver", + "android.service.vr.IPersistentVrStateCallbacks", + "android.service.vr.IVrManager", + "android.service.vr.IVrStateCallbacks", + "android.ui.ISurfaceComposer", + "android.ui.ISurfaceComposerClient", + "android.utils.IMemory", + "android.utils.IMemoryHeap", + "com.android.car.procfsinspector.IProcfsInspector", + "com.android.internal.app.IAppOpsCallback", + "com.android.internal.app.IAppOpsService", + "com.android.internal.app.IBatteryStats", + "com.android.internal.os.IResultReceiver", + "com.android.internal.os.IShellCallback", + "drm.IDrmManagerService", + "drm.IDrmServiceListener", + "IAAudioClient", + "IAAudioService", + "VtsFuzzer", + nullptr, +}; + +constexpr const char* const kDownstreamManualInterfaces[] = { + // Add downstream interfaces here. + nullptr, +}; + +constexpr bool equals(const char* a, const char* b) { + if (*a != *b) return false; + if (*a == '\0') return true; + return equals(a + 1, b + 1); +} + +constexpr bool inList(const char* a, const char* const* whitelist) { + if (*whitelist == nullptr) return false; + if (equals(a, *whitelist)) return true; + return inList(a, whitelist + 1); +} + +constexpr bool allowedManualInterface(const char* name) { + return inList(name, kManualInterfaces) || + inList(name, kDownstreamManualInterfaces); +} + +} // namespace internal +} // namespace android #endif // ANDROID_IINTERFACE_H diff --git a/libs/binder/include/binder/IMediaResourceMonitor.h b/libs/binder/include/binder/IMediaResourceMonitor.h index 213ee63ea8..da2b7cf62d 100644 --- a/libs/binder/include/binder/IMediaResourceMonitor.h +++ b/libs/binder/include/binder/IMediaResourceMonitor.h @@ -52,7 +52,7 @@ public: // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h index 071946f50c..98e92c4441 100644 --- a/libs/binder/include/binder/IMemory.h +++ b/libs/binder/include/binder/IMemory.h @@ -76,6 +76,8 @@ public: // NOLINTNEXTLINE(google-default-arguments) virtual sp<IMemoryHeap> getMemory(ssize_t* offset=nullptr, size_t* size=nullptr) const = 0; + void* unsecurePointer() const; + // helpers void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const; void* pointer() const; @@ -100,6 +102,6 @@ protected: // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_IMEMORY_H diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 614b0b33dd..4818889436 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -29,8 +29,6 @@ typedef int uid_t; // --------------------------------------------------------------------------- namespace android { -class IPCThreadStateBase; - class IPCThreadState { public: @@ -110,32 +108,15 @@ public: // the maximum number of binder threads threads allowed for this process. void blockUntilThreadAvailable(); + // Service manager registration + void setTheContextObject(sp<BBinder> obj); - // Is this thread currently serving a binder call. This method - // returns true if while traversing backwards from the function call - // stack for this thread, we encounter a function serving a binder - // call before encountering a hwbinder call / hitting the end of the - // call stack. - // Eg: If thread T1 went through the following call pattern - // 1) T1 receives and executes hwbinder call H1. - // 2) While handling H1, T1 makes binder call B1. - // 3) The handler of B1, calls into T1 with a callback B2. - // If isServingCall() is called during H1 before 3), this method - // will return false, else true. - // - // ---- - // | B2 | ---> While callback B2 is being handled, during 3). - // ---- - // | H1 | ---> While H1 is being handled. - // ---- - // Fig: Thread Call stack while handling B2 + // WARNING: DO NOT USE THIS API // - // This is since after 3), while traversing the thread call stack, - // we hit a binder call before a hwbinder call / end of stack. This - // method may be typically used to determine whether to use - // hardware::IPCThreadState methods or IPCThreadState methods to - // infer information about thread state. - bool isServingCall() const; + // Returns a pointer to the stack from the last time a transaction + // was initiated by the kernel. Used to compare when making nested + // calls between multiple different transports. + const void* getServingStackPointer() const; // The work source represents the UID of the process we should attribute the transaction // to. We use -1 to specify that the work source was not set using #setWorkSource. @@ -179,6 +160,7 @@ private: Parcel mIn; Parcel mOut; status_t mLastError; + const void* mServingStackPointer; pid_t mCallingPid; const char* mCallingSid; uid_t mCallingUid; @@ -189,12 +171,11 @@ private: bool mPropagateWorkSource; int32_t mStrictModePolicy; int32_t mLastTransactionBinderFlags; - IPCThreadStateBase *mIPCThreadStateBase; ProcessState::CallRestriction mCallRestriction; }; -}; // namespace android +} // namespace android // --------------------------------------------------------------------------- diff --git a/libs/binder/include/binder/IPermissionController.h b/libs/binder/include/binder/IPermissionController.h index 26a1b23a9a..4b66df8d6e 100644 --- a/libs/binder/include/binder/IPermissionController.h +++ b/libs/binder/include/binder/IPermissionController.h @@ -65,7 +65,7 @@ public: // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/IProcessInfoService.h b/libs/binder/include/binder/IProcessInfoService.h index 033c145363..ca30ad3b95 100644 --- a/libs/binder/include/binder/IProcessInfoService.h +++ b/libs/binder/include/binder/IProcessInfoService.h @@ -46,7 +46,7 @@ public: // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/IResultReceiver.h b/libs/binder/include/binder/IResultReceiver.h index 00b3d8954c..70e99e7c38 100644 --- a/libs/binder/include/binder/IResultReceiver.h +++ b/libs/binder/include/binder/IResultReceiver.h @@ -50,7 +50,7 @@ public: // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_IRESULT_RECEIVER_H diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index aeea1a2676..1d520c127b 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -26,12 +26,22 @@ namespace android { // ---------------------------------------------------------------------- +/** + * Service manager for C++ services. + * + * IInterface is only for legacy ABI compatibility + */ class IServiceManager : public IInterface { public: - DECLARE_META_INTERFACE(ServiceManager) + // for ABI compatibility + virtual const String16& getInterfaceDescriptor() const; + + IServiceManager(); + virtual ~IServiceManager(); + /** - * Must match values in IServiceManager.java + * Must match values in IServiceManager.aidl */ /* Allows services to dump sections according to priorities. */ static const int DUMP_FLAG_PRIORITY_CRITICAL = 1 << 0; @@ -72,16 +82,66 @@ public: // NOLINTNEXTLINE(google-default-arguments) virtual Vector<String16> listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0; - enum { - GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, - CHECK_SERVICE_TRANSACTION, - ADD_SERVICE_TRANSACTION, - LIST_SERVICES_TRANSACTION, - }; + /** + * Efficiently wait for a service. + * + * Returns nullptr only for permission problem or fatal error. + */ + virtual sp<IBinder> waitForService(const String16& name) = 0; + + /** + * Check if a service is declared (e.g. VINTF manifest). + * + * If this returns true, waitForService should always be able to return the + * service. + */ + virtual bool isDeclared(const String16& name) = 0; }; sp<IServiceManager> defaultServiceManager(); +/** + * Directly set the default service manager. Only used for testing. + * Note that the caller is responsible for caling this method + * *before* any call to defaultServiceManager(); if the latter is + * called first, setDefaultServiceManager() will abort. + */ +void setDefaultServiceManager(const sp<IServiceManager>& sm); + +template<typename INTERFACE> +sp<INTERFACE> waitForService(const String16& name) { + const sp<IServiceManager> sm = defaultServiceManager(); + return interface_cast<INTERFACE>(sm->waitForService(name)); +} + +template<typename INTERFACE> +sp<INTERFACE> waitForDeclaredService(const String16& name) { + const sp<IServiceManager> sm = defaultServiceManager(); + if (!sm->isDeclared(name)) return nullptr; + return interface_cast<INTERFACE>(sm->waitForService(name)); +} + +template <typename INTERFACE> +sp<INTERFACE> checkDeclaredService(const String16& name) { + const sp<IServiceManager> sm = defaultServiceManager(); + if (!sm->isDeclared(name)) return nullptr; + return interface_cast<INTERFACE>(sm->checkService(name)); +} + +template<typename INTERFACE> +sp<INTERFACE> waitForVintfService( + const String16& instance = String16("default")) { + return waitForDeclaredService<INTERFACE>( + INTERFACE::descriptor + String16("/") + instance); +} + +template<typename INTERFACE> +sp<INTERFACE> checkVintfService( + const String16& instance = String16("default")) { + return checkDeclaredService<INTERFACE>( + INTERFACE::descriptor + String16("/") + instance); +} + template<typename INTERFACE> status_t getService(const String16& name, sp<INTERFACE>* outService) { @@ -98,7 +158,7 @@ bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid); bool checkPermission(const String16& permission, pid_t pid, uid_t uid); -}; // namespace android +} // namespace android #endif // ANDROID_ISERVICE_MANAGER_H diff --git a/libs/binder/include/binder/IShellCallback.h b/libs/binder/include/binder/IShellCallback.h index 67156787d3..b7ab6eab88 100644 --- a/libs/binder/include/binder/IShellCallback.h +++ b/libs/binder/include/binder/IShellCallback.h @@ -51,7 +51,7 @@ public: // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_ISHELL_CALLBACK_H diff --git a/libs/binder/include/binder/IUidObserver.h b/libs/binder/include/binder/IUidObserver.h index a1f530dc71..09e50a9de8 100644 --- a/libs/binder/include/binder/IUidObserver.h +++ b/libs/binder/include/binder/IUidObserver.h @@ -58,7 +58,7 @@ public: // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h new file mode 100644 index 0000000000..efdecc4eca --- /dev/null +++ b/libs/binder/include/binder/LazyServiceRegistrar.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IServiceManager.h> +#include <binder/Status.h> +#include <utils/StrongPointer.h> + +namespace android { +namespace binder { +namespace internal { +class ClientCounterCallback; +} // namespace internal + +/** Exits when all services registered through this object have 0 clients */ +class LazyServiceRegistrar { + public: + static LazyServiceRegistrar& getInstance(); + status_t registerService(const sp<IBinder>& service, + const std::string& name = "default", + bool allowIsolated = false, + int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT); + + private: + std::shared_ptr<internal::ClientCounterCallback> mClientCC; + LazyServiceRegistrar(); +}; + +} // namespace binder +} // namespace android
\ No newline at end of file diff --git a/libs/binder/include/binder/MemoryBase.h b/libs/binder/include/binder/MemoryBase.h index 463e26d977..4dd363808c 100644 --- a/libs/binder/include/binder/MemoryBase.h +++ b/libs/binder/include/binder/MemoryBase.h @@ -46,6 +46,6 @@ private: }; // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_MEMORY_BASE_H diff --git a/libs/binder/include/binder/MemoryDealer.h b/libs/binder/include/binder/MemoryDealer.h index b483be0fd5..6c1c4122d8 100644 --- a/libs/binder/include/binder/MemoryDealer.h +++ b/libs/binder/include/binder/MemoryDealer.h @@ -59,6 +59,6 @@ private: // ---------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_MEMORY_DEALER_H diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h index 100d784a83..3fccddcc59 100644 --- a/libs/binder/include/binder/MemoryHeapBase.h +++ b/libs/binder/include/binder/MemoryHeapBase.h @@ -98,6 +98,6 @@ private: }; // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_MEMORY_HEAP_BASE_H diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index e5219a5590..d4bb85b102 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -17,11 +17,11 @@ #ifndef ANDROID_PARCEL_H #define ANDROID_PARCEL_H +#include <map> // for legacy reasons #include <string> +#include <type_traits> #include <vector> -#include <linux/android/binder.h> - #include <android-base/unique_fd.h> #include <cutils/native_handle.h> #include <utils/Errors.h> @@ -32,23 +32,26 @@ #include <binder/IInterface.h> #include <binder/Parcelable.h> -#include <binder/Map.h> + +#ifdef BINDER_IPC_32BIT +typedef unsigned int binder_size_t; +#else +typedef unsigned long long binder_size_t; +#endif + // --------------------------------------------------------------------------- namespace android { template <typename T> class Flattenable; template <typename T> class LightFlattenable; +struct flat_binder_object; class IBinder; class IPCThreadState; class ProcessState; class String8; class TextOutput; -namespace binder { -class Value; -}; - class Parcel { friend class IPCThreadState; public: @@ -67,7 +70,7 @@ public: status_t setDataSize(size_t size); void setDataPosition(size_t pos) const; status_t setDataCapacity(size_t size); - + status_t setData(const uint8_t* buffer, size_t len); status_t appendFrom(const Parcel *parcel, @@ -97,10 +100,6 @@ public: void freeData(); -private: - const binder_size_t* objects() const; - -public: size_t objectsCount() const; status_t errorCheck() const; @@ -121,7 +120,6 @@ public: status_t writeString16(const std::unique_ptr<String16>& str); status_t writeString16(const char16_t* str, size_t len); status_t writeStrongBinder(const sp<IBinder>& val); - status_t writeWeakBinder(const wp<IBinder>& val); status_t writeInt32Array(size_t len, const int32_t *val); status_t writeByteArray(size_t len, const uint8_t *val); status_t writeBool(bool val); @@ -160,6 +158,18 @@ public: status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val); status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& 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> + status_t writeEnumVector(const std::vector<T>& val); + 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> + status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val); + // Write an Enum vector with underlying type != int8_t. + 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> + status_t writeEnumVector(const std::vector<T>& val); + 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> + status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val); + template<typename T> status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val); template<typename T> @@ -172,8 +182,6 @@ public: status_t writeParcelable(const Parcelable& parcelable); - status_t writeValue(const binder::Value& value); - template<typename T> status_t write(const Flattenable<T>& val); @@ -185,9 +193,6 @@ public: template<typename T> status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val); - status_t writeMap(const binder::Map& map); - status_t writeNullableMap(const std::unique_ptr<binder::Map>& map); - // Place a native_handle into the parcel (the native_handle's file- // descriptors are dup'ed, so it is safe to delete the native_handle // when this function returns). @@ -244,8 +249,6 @@ public: // Currently the native implementation doesn't do any of the StrictMode // stack gathering and serialization that the Java implementation does. status_t writeNoException(); - - void remove(size_t start, size_t amt); status_t read(void* outData, size_t len) const; const void* readInplace(size_t len) const; @@ -284,7 +287,19 @@ public: sp<IBinder> readStrongBinder() const; status_t readStrongBinder(sp<IBinder>* val) const; status_t readNullableStrongBinder(sp<IBinder>* val) const; - wp<IBinder> readWeakBinder() const; + + + // Read 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> + status_t readEnumVector(std::vector<T>* val) const; + 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> + status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const; + // Read an Enum vector with underlying type != int8_t. + 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> + status_t readEnumVector(std::vector<T>* val) const; + 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> + status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const; template<typename T> status_t readParcelableVector( @@ -297,8 +312,6 @@ public: template<typename T> status_t readParcelable(std::unique_ptr<T>* parcelable) const; - status_t readValue(binder::Value* value) const; - template<typename T> status_t readStrongBinder(sp<T>* val) const; @@ -343,9 +356,11 @@ public: status_t resizeOutVector(std::vector<T>* val) const; template<typename T> status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const; - - status_t readMap(binder::Map* map)const; - status_t readNullableMap(std::unique_ptr<binder::Map>* map) const; + template<typename T> + status_t reserveOutVector(std::vector<T>* val, size_t* size) const; + template<typename T> + status_t reserveOutVector(std::unique_ptr<std::vector<T>>* val, + size_t* size) const; // Like Parcel.java's readExceptionCode(). Reads the first int32 // off of a Parcel's header, returning 0 or the negative error @@ -399,8 +414,7 @@ public: bool replaceCallingWorkSourceUid(uid_t uid); // Returns the work source provided by the caller. This can only be trusted for trusted calling // uid. - uid_t readCallingWorkSourceUid(); - void readRequestHeaders() const; + uid_t readCallingWorkSourceUid() const; private: typedef void (*release_func)(Parcel* parcel, @@ -437,7 +451,13 @@ private: void scanForFds() const; status_t validateReadData(size_t len) const; void updateWorkSourceRequestHeaderPosition() const; - + + status_t finishFlattenBinder(const sp<IBinder>& binder, + const flat_binder_object& flat); + status_t finishUnflattenBinder(const sp<IBinder>& binder, sp<IBinder>* out) const; + status_t flattenBinder(const sp<IBinder>& binder); + status_t unflattenBinder(sp<IBinder>* out) const; + template<class T> status_t readAligned(T *pArg) const; @@ -449,6 +469,20 @@ private: status_t writeRawNullableParcelable(const Parcelable* parcelable); + template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool> = 0> + status_t writeEnum(const T& val); + template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool> = 0> + status_t writeEnum(const T& val); + + template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool> = 0> + status_t readEnum(T* pArg) const; + template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool> = 0> + status_t readEnum(T* pArg) const; + + status_t writeByteVectorInternal(const int8_t* data, size_t size); + template<typename T> + status_t readByteVectorInternal(std::vector<T>* val, size_t size) const; + template<typename T, typename U> status_t unsafeReadTypedVector(std::vector<T>* val, status_t(Parcel::*read_func)(U*) const) const; @@ -692,6 +726,42 @@ status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const { } template<typename T> +status_t Parcel::reserveOutVector(std::vector<T>* val, size_t* size) const { + int32_t read_size; + status_t err = readInt32(&read_size); + if (err != NO_ERROR) { + return err; + } + + if (read_size < 0) { + return UNEXPECTED_NULL; + } + *size = static_cast<size_t>(read_size); + val->reserve(*size); + return OK; +} + +template<typename T> +status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val, + size_t* size) const { + int32_t read_size; + status_t err = readInt32(&read_size); + if (err != NO_ERROR) { + return err; + } + + if (read_size >= 0) { + *size = static_cast<size_t>(read_size); + val->reset(new std::vector<T>()); + (*val)->reserve(*size); + } else { + val->reset(); + } + + return OK; +} + +template<typename T> status_t Parcel::readStrongBinder(sp<T>* val) const { sp<IBinder> tmp; status_t ret = readStrongBinder(&tmp); @@ -924,6 +994,79 @@ status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::un return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>); } +template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>> +status_t Parcel::writeEnum(const T& val) { + return writeInt32(static_cast<int32_t>(val)); +} +template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool>> +status_t Parcel::writeEnum(const T& val) { + return writeInt64(static_cast<int64_t>(val)); +} + +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>> +status_t Parcel::writeEnumVector(const std::vector<T>& val) { + return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size()); +} +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>> +status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) { + if (!val) return writeInt32(-1); + return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size()); +} +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>> +status_t Parcel::writeEnumVector(const std::vector<T>& val) { + return writeTypedVector(val, &Parcel::writeEnum); +} +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>> +status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) { + return writeNullableTypedVector(val, &Parcel::writeEnum); +} + +template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>> +status_t Parcel::readEnum(T* pArg) const { + return readInt32(reinterpret_cast<int32_t *>(pArg)); +} +template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int64_t>, bool>> +status_t Parcel::readEnum(T* pArg) const { + return readInt64(reinterpret_cast<int64_t *>(pArg)); +} + +template<typename T> +inline status_t Parcel::readByteVectorInternal(std::vector<T>* val, size_t size) const { + // readByteVectorInternal expects a vector that has been reserved (but not + // resized) to have the provided size. + const T* data = reinterpret_cast<const T*>(readInplace(size)); + if (!data) return BAD_VALUE; + val->clear(); + val->insert(val->begin(), data, data+size); + return NO_ERROR; +} + +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>> +status_t Parcel::readEnumVector(std::vector<T>* val) const { + size_t size; + if (status_t status = reserveOutVector(val, &size); status != OK) return status; + return readByteVectorInternal(val, size); +} +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>> +status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const { + size_t size; + if (status_t status = reserveOutVector(val, &size); status != OK) return status; + if (val->get() == nullptr) { + // reserveOutVector does not create the out vector if size is < 0. + // This occurs when writing a null Enum vector. + return OK; + } + return readByteVectorInternal(val->get(), size); +} +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>> +status_t Parcel::readEnumVector(std::vector<T>* val) const { + return readTypedVector(val, &Parcel::readEnum); +} +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>> +status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const { + return readNullableTypedVector(val, &Parcel::readEnum); +} + // --------------------------------------------------------------------------- inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel) @@ -932,24 +1075,7 @@ inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel) return to; } -// --------------------------------------------------------------------------- - -// Generic acquire and release of objects. -void acquire_object(const sp<ProcessState>& proc, - const flat_binder_object& obj, const void* who); -void release_object(const sp<ProcessState>& proc, - const flat_binder_object& obj, const void* who); - -void flatten_binder(const sp<ProcessState>& proc, - const sp<IBinder>& binder, flat_binder_object* out); -void flatten_binder(const sp<ProcessState>& proc, - const wp<IBinder>& binder, flat_binder_object* out); -status_t unflatten_binder(const sp<ProcessState>& proc, - const flat_binder_object& flat, sp<IBinder>* out); -status_t unflatten_binder(const sp<ProcessState>& proc, - const flat_binder_object& flat, wp<IBinder>* out); - -}; // namespace android +} // namespace android // --------------------------------------------------------------------------- diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h index 662e56e864..4635ad84c6 100644 --- a/libs/binder/include/binder/ParcelFileDescriptor.h +++ b/libs/binder/include/binder/ParcelFileDescriptor.h @@ -42,6 +42,24 @@ public: android::status_t writeToParcel(android::Parcel* parcel) const override; android::status_t readFromParcel(const android::Parcel* parcel) override; + inline bool operator!=(const ParcelFileDescriptor& rhs) const { + return mFd != rhs.mFd; + } + inline bool operator<(const ParcelFileDescriptor& rhs) const { + return mFd < rhs.mFd; + } + inline bool operator<=(const ParcelFileDescriptor& rhs) const { + return mFd <= rhs.mFd; + } + inline bool operator==(const ParcelFileDescriptor& rhs) const { + return mFd == rhs.mFd; + } + inline bool operator>(const ParcelFileDescriptor& rhs) const { + return mFd > rhs.mFd; + } + inline bool operator>=(const ParcelFileDescriptor& rhs) const { + return mFd >= rhs.mFd; + } private: android::base::unique_fd mFd; }; diff --git a/libs/binder/include/binder/PermissionCache.h b/libs/binder/include/binder/PermissionCache.h index 95eabff7ac..c2582150df 100644 --- a/libs/binder/include/binder/PermissionCache.h +++ b/libs/binder/include/binder/PermissionCache.h @@ -77,7 +77,7 @@ public: }; // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/PermissionController.h b/libs/binder/include/binder/PermissionController.h index d81f5142bc..4db522ab1f 100644 --- a/libs/binder/include/binder/PermissionController.h +++ b/libs/binder/include/binder/PermissionController.h @@ -60,7 +60,7 @@ private: }; -}; // namespace android +} // namespace android // --------------------------------------------------------------------------- #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/ProcessInfoService.h b/libs/binder/include/binder/ProcessInfoService.h index a03aae98ee..6bfd1bc17d 100644 --- a/libs/binder/include/binder/ProcessInfoService.h +++ b/libs/binder/include/binder/ProcessInfoService.h @@ -78,7 +78,7 @@ public: // ---------------------------------------------------------------------- -}; // namespace android +} // namespace android #else // __ANDROID_VNDK__ #error "This header is not visible to vendors" diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index 224cb36807..e57ff1c260 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -45,27 +45,19 @@ public: */ static sp<ProcessState> initWithDriver(const char *driver); - void setContextObject(const sp<IBinder>& object); sp<IBinder> getContextObject(const sp<IBinder>& caller); - - void setContextObject(const sp<IBinder>& object, - const String16& name); - sp<IBinder> getContextObject(const String16& name, - const sp<IBinder>& caller); void startThreadPool(); typedef bool (*context_check_func)(const String16& name, const sp<IBinder>& caller, void* userData); - - bool isContextManager(void) const; + bool becomeContextManager( context_check_func checkFunc, void* userData); sp<IBinder> getStrongProxyForHandle(int32_t handle); - wp<IBinder> getWeakProxyForHandle(int32_t handle); void expungeHandle(int32_t handle, IBinder* binder); void spawnPooledThread(bool isMain); @@ -77,6 +69,14 @@ public: ssize_t getKernelReferences(size_t count, uintptr_t* buf); + // Only usable by the context manager. + // This refcount includes: + // 1. Strong references to the node by this and other processes + // 2. Temporary strong references held by the kernel during a + // transaction on the node. + // It does NOT include local strong references to the node + ssize_t getStrongRefCountForNodeByHandle(int32_t handle); + enum class CallRestriction { // all calls okay NONE, @@ -124,14 +124,9 @@ private: Vector<handle_entry>mHandleToObject; - bool mManagesContexts; context_check_func mBinderContextCheckFunc; void* mBinderContextUserData; - KeyedVector<String16, sp<IBinder> > - mContexts; - - String8 mRootDir; bool mThreadPoolStarted; volatile int32_t mThreadPoolSeq; @@ -139,7 +134,7 @@ private: CallRestriction mCallRestriction; }; -}; // namespace android +} // namespace android // --------------------------------------------------------------------------- diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h new file mode 100644 index 0000000000..2894482f55 --- /dev/null +++ b/libs/binder/include/binder/Stability.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IBinder.h> +#include <string> + +namespace android { + +class BpBinder; +class ProcessState; + +namespace internal { + +// WARNING: These APIs are only ever expected to be called by auto-generated code. +// Instead of calling them, you should set the stability of a .aidl interface +class Stability final { +public: + // WARNING: This is only ever expected to be called by auto-generated code. You likely want to + // change or modify the stability class of the interface you are using. + // This must be called as soon as the binder in question is constructed. No thread safety + // is provided. + // E.g. stability is according to libbinder compilation unit + static void markCompilationUnit(IBinder* binder); + // WARNING: This is only ever expected to be called by auto-generated code. You likely want to + // change or modify the stability class of the interface you are using. + // This must be called as soon as the binder in question is constructed. No thread safety + // is provided. + // E.g. stability is according to libbinder_ndk or Java SDK AND the interface + // expressed here is guaranteed to be stable for multiple years (Stable AIDL) + static void markVintf(IBinder* binder); + + // WARNING: for debugging only + static void debugLogStability(const std::string& tag, const sp<IBinder>& binder); + + // WARNING: This is only ever expected to be called by auto-generated code or tests. + // You likely want to change or modify the stability of the interface you are using. + // This must be called as soon as the binder in question is constructed. No thread safety + // is provided. + // E.g. stability is according to libbinder_ndk or Java SDK AND the interface + // expressed here is guaranteed to be stable for multiple years (Stable AIDL) + // If this is called when __ANDROID_VNDK__ is not defined, then it is UB and will likely + // break the device during GSI or other tests. + static void markVndk(IBinder* binder); + + // Returns true if the binder needs to be declared in the VINTF manifest or + // else false if the binder is local to the current partition. + static bool requiresVintfDeclaration(const sp<IBinder>& binder); +private: + // Parcel needs to read/write stability level in an unstable format. + friend ::android::Parcel; + + // only expose internal APIs inside of libbinder, for checking stability + friend ::android::BpBinder; + + // so that it can mark the context object (only the root object doesn't go + // through Parcel) + friend ::android::ProcessState; + + static void tryMarkCompilationUnit(IBinder* binder); + + enum Level : int32_t { + UNDECLARED = 0, + + VENDOR = 0b000011, + SYSTEM = 0b001100, + VINTF = 0b111111, + }; + +#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) + static constexpr Level kLocalStability = Level::VENDOR; +#else + static constexpr Level kLocalStability = Level::SYSTEM; +#endif + + // applies stability to binder if stability level is known + __attribute__((warn_unused_result)) + static status_t set(IBinder* binder, int32_t stability, bool log); + + static Level get(IBinder* binder); + + static bool check(int32_t provided, Level required); + + static bool isDeclaredStability(int32_t stability); + static std::string stabilityString(int32_t stability); + + Stability(); +}; + +} // namespace internal +} // namespace android diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h index 5b5f76688b..f66406f7d4 100644 --- a/libs/binder/include/binder/TextOutput.h +++ b/libs/binder/include/binder/TextOutput.h @@ -199,6 +199,6 @@ inline size_t HexDump::alignment() const { return mAlignment; } inline bool HexDump::carrayStyle() const { return mCArrayStyle; } // --------------------------------------------------------------------------- -}; // namespace android +} // namespace android #endif // ANDROID_TEXTOUTPUT_H diff --git a/libs/binder/include/binder/Value.h b/libs/binder/include/binder/Value.h deleted file mode 100644 index 735f40eb1f..0000000000 --- a/libs/binder/include/binder/Value.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_VALUE_H -#define ANDROID_VALUE_H - -#include <stdint.h> -#include <map> -#include <set> -#include <vector> -#include <string> - -#include <binder/Parcelable.h> -#include <binder/PersistableBundle.h> -#include <binder/Map.h> -#include <utils/String8.h> -#include <utils/String16.h> -#include <utils/StrongPointer.h> - -namespace android { - -class Parcel; - -namespace binder { - -/** - * A limited C++ generic type. The purpose of this class is to allow C++ - * programs to make use of (or implement) Binder interfaces which make use - * the Java "Object" generic type (either via the use of the Map type or - * some other mechanism). - * - * This class only supports a limited set of types, but additional types - * may be easily added to this class in the future as needed---without - * breaking binary compatability. - * - * This class was written in such a way as to help avoid type errors by - * giving each type their own explicity-named accessor methods (rather than - * overloaded methods). - * - * When reading or writing this class to a Parcel, use the `writeValue()` - * and `readValue()` methods. - */ -class Value { -public: - Value(); - virtual ~Value(); - - Value& swap(Value &); - - bool empty() const; - - void clear(); - -#ifdef LIBBINDER_VALUE_SUPPORTS_TYPE_INFO - const std::type_info& type() const; -#endif - - int32_t parcelType() const; - - bool operator==(const Value& rhs) const; - bool operator!=(const Value& rhs) const { return !this->operator==(rhs); } - - Value(const Value& value); - Value(const bool& value); // NOLINT(google-explicit-constructor) - Value(const int8_t& value); // NOLINT(google-explicit-constructor) - Value(const int32_t& value); // NOLINT(google-explicit-constructor) - Value(const int64_t& value); // NOLINT(google-explicit-constructor) - Value(const double& value); // NOLINT(google-explicit-constructor) - Value(const String16& value); // NOLINT(google-explicit-constructor) - Value(const std::vector<bool>& value); // NOLINT(google-explicit-constructor) - Value(const std::vector<uint8_t>& value); // NOLINT(google-explicit-constructor) - Value(const std::vector<int32_t>& value); // NOLINT(google-explicit-constructor) - Value(const std::vector<int64_t>& value); // NOLINT(google-explicit-constructor) - Value(const std::vector<double>& value); // NOLINT(google-explicit-constructor) - Value(const std::vector<String16>& value); // NOLINT(google-explicit-constructor) - Value(const os::PersistableBundle& value); // NOLINT(google-explicit-constructor) - Value(const binder::Map& value); // NOLINT(google-explicit-constructor) - - Value& operator=(const Value& rhs); - Value& operator=(const int8_t& rhs); - Value& operator=(const bool& rhs); - Value& operator=(const int32_t& rhs); - Value& operator=(const int64_t& rhs); - Value& operator=(const double& rhs); - Value& operator=(const String16& rhs); - Value& operator=(const std::vector<bool>& rhs); - Value& operator=(const std::vector<uint8_t>& rhs); - Value& operator=(const std::vector<int32_t>& rhs); - Value& operator=(const std::vector<int64_t>& rhs); - Value& operator=(const std::vector<double>& rhs); - Value& operator=(const std::vector<String16>& rhs); - Value& operator=(const os::PersistableBundle& rhs); - Value& operator=(const binder::Map& rhs); - - void putBoolean(const bool& value); - void putByte(const int8_t& value); - void putInt(const int32_t& value); - void putLong(const int64_t& value); - void putDouble(const double& value); - void putString(const String16& value); - void putBooleanVector(const std::vector<bool>& value); - void putByteVector(const std::vector<uint8_t>& value); - void putIntVector(const std::vector<int32_t>& value); - void putLongVector(const std::vector<int64_t>& value); - void putDoubleVector(const std::vector<double>& value); - void putStringVector(const std::vector<String16>& value); - void putPersistableBundle(const os::PersistableBundle& value); - void putMap(const binder::Map& value); - - bool getBoolean(bool* out) const; - bool getByte(int8_t* out) const; - bool getInt(int32_t* out) const; - bool getLong(int64_t* out) const; - bool getDouble(double* out) const; - bool getString(String16* out) const; - bool getBooleanVector(std::vector<bool>* out) const; - bool getByteVector(std::vector<uint8_t>* out) const; - bool getIntVector(std::vector<int32_t>* out) const; - bool getLongVector(std::vector<int64_t>* out) const; - bool getDoubleVector(std::vector<double>* out) const; - bool getStringVector(std::vector<String16>* out) const; - bool getPersistableBundle(os::PersistableBundle* out) const; - bool getMap(binder::Map* out) const; - - bool isBoolean() const; - bool isByte() const; - bool isInt() const; - bool isLong() const; - bool isDouble() const; - bool isString() const; - bool isBooleanVector() const; - bool isByteVector() const; - bool isIntVector() const; - bool isLongVector() const; - bool isDoubleVector() const; - bool isStringVector() const; - bool isPersistableBundle() const; - bool isMap() const; - - // String Convenience Adapters - // --------------------------- - - explicit Value(const String8& value): Value(String16(value)) { } - explicit Value(const ::std::string& value): Value(String8(value.c_str())) { } - void putString(const String8& value) { return putString(String16(value)); } - void putString(const ::std::string& value) { return putString(String8(value.c_str())); } - Value& operator=(const String8& rhs) { return *this = String16(rhs); } - Value& operator=(const ::std::string& rhs) { return *this = String8(rhs.c_str()); } - bool getString(String8* out) const; - bool getString(::std::string* out) const; - -private: - - // This allows ::android::Parcel to call the two methods below. - friend class ::android::Parcel; - - // This is called by ::android::Parcel::writeValue() - status_t writeToParcel(Parcel* parcel) const; - - // This is called by ::android::Parcel::readValue() - status_t readFromParcel(const Parcel* parcel); - - template<typename T> class Content; - class ContentBase; - - ContentBase* mContent; -}; - -} // namespace binder - -} // namespace android - -#endif // ANDROID_VALUE_H diff --git a/libs/binder/include/private/binder/binder_module.h b/libs/binder/include/private/binder/binder_module.h index 2f11622e70..c22be9f786 100644 --- a/libs/binder/include/private/binder/binder_module.h +++ b/libs/binder/include/private/binder/binder_module.h @@ -23,6 +23,16 @@ namespace android { /* obtain structures and constants from the kernel header */ +// TODO(b/31559095): bionic on host +#ifndef __ANDROID__ +#define __packed __attribute__((__packed__)) +#endif + +// TODO(b/31559095): bionic on host +#if defined(B_PACK_CHARS) && !defined(_UAPI_LINUX_BINDER_H) +#undef B_PACK_CHARS +#endif + #include <sys/ioctl.h> #include <linux/android/binder.h> diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 21bef2e930..e66e425aee 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -14,13 +14,30 @@ * limitations under the License. */ -cc_library { +// TODO(b/31559095): bionic on host should define this +cc_defaults { + name: "libbinder_ndk_host_user", + target: { + host: { + cflags: [ + "-D__INTRODUCED_IN(n)=", + "-D__assert(a,b,c)=", + // We want all the APIs to be available on the host. + "-D__ANDROID_API__=10000", + ], + }, + }, +} + +cc_library_shared { name: "libbinder_ndk", - vendor_available: true, + + defaults: ["libbinder_ndk_host_user"], + host_supported: true, export_include_dirs: [ "include_ndk", - "include_apex", + "include_platform", ], cflags: [ @@ -33,7 +50,9 @@ cc_library { "ibinder.cpp", "ibinder_jni.cpp", "parcel.cpp", + "parcel_jni.cpp", "process.cpp", + "stability.cpp", "status.cpp", "service_manager.cpp", ], @@ -52,10 +71,17 @@ cc_library { "jni_headers", ], - version_script: "libbinder_ndk.map.txt", + target: { + linux: { + version_script: "libbinder_ndk.map.txt", + }, + }, stubs: { symbol_file: "libbinder_ndk.map.txt", - versions: ["29"], + versions: [ + "29", + "30", + ], }, } @@ -74,3 +100,12 @@ ndk_library { symbol_file: "libbinder_ndk.map.txt", first_version: "29", } + +llndk_library { + name: "libbinder_ndk", + symbol_file: "libbinder_ndk.map.txt", + export_include_dirs: [ + "include_ndk", + "include_platform", + ], +} diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index bd6886d1ee..75dcdc8389 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -17,16 +17,20 @@ #include <android/binder_ibinder.h> #include "ibinder_internal.h" +#include <android/binder_stability.h> #include <android/binder_status.h> #include "parcel_internal.h" #include "status_internal.h" #include <android-base/logging.h> #include <binder/IPCThreadState.h> +#include <binder/IResultReceiver.h> +#include <private/android_filesystem_config.h> using DeathRecipient = ::android::IBinder::DeathRecipient; using ::android::IBinder; +using ::android::IResultReceiver; using ::android::Parcel; using ::android::sp; using ::android::status_t; @@ -157,6 +161,45 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce binder_status_t status = getClass()->onTransact(this, code, &in, &out); return PruneStatusT(status); + } else if (code == SHELL_COMMAND_TRANSACTION) { + int in = data.readFileDescriptor(); + int out = data.readFileDescriptor(); + int err = data.readFileDescriptor(); + + int argc = data.readInt32(); + std::vector<String8> utf8Args; // owns memory of utf8s + std::vector<const char*> utf8Pointers; // what can be passed over NDK API + for (int i = 0; i < argc && data.dataAvail() > 0; i++) { + utf8Args.push_back(String8(data.readString16())); + utf8Pointers.push_back(utf8Args[i].c_str()); + } + + data.readStrongBinder(); // skip over the IShellCallback + sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(data.readStrongBinder()); + + // Shell commands should only be callable by ADB. + uid_t uid = AIBinder_getCallingUid(); + if (uid != AID_ROOT && uid != AID_SHELL) { + if (resultReceiver != nullptr) { + resultReceiver->send(-1); + } + return STATUS_PERMISSION_DENIED; + } + + // Check that the file descriptors are valid. + if (in == STATUS_BAD_TYPE || out == STATUS_BAD_TYPE || err == STATUS_BAD_TYPE) { + if (resultReceiver != nullptr) { + resultReceiver->send(-1); + } + return STATUS_BAD_VALUE; + } + + binder_status_t status = getClass()->handleShellCommand( + this, in, out, err, utf8Pointers.data(), utf8Pointers.size()); + if (resultReceiver != nullptr) { + resultReceiver->send(status); + } + return status; } else { return BBinder::onTransact(code, data, reply, flags); } @@ -265,6 +308,13 @@ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) { clazz->onDump = onDump; } +void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz, + AIBinder_handleShellCommand handleShellCommand) { + CHECK(clazz != nullptr) << "setHandleShellCommand requires non-null clazz"; + + clazz->handleShellCommand = handleShellCommand; +} + void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) { CHECK(who == mWho); @@ -542,7 +592,8 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa return STATUS_UNKNOWN_TRANSACTION; } - if ((flags & ~FLAG_ONEWAY) != 0) { + constexpr binder_flags_t kAllFlags = FLAG_PRIVATE_VENDOR | FLAG_ONEWAY; + if ((flags & ~kAllFlags) != 0) { LOG(ERROR) << __func__ << ": Unrecognized flags sent: " << flags; return STATUS_BAD_VALUE; } @@ -589,3 +640,40 @@ void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) { recipient->decStrong(nullptr); } + +binder_status_t AIBinder_getExtension(AIBinder* binder, AIBinder** outExt) { + if (binder == nullptr || outExt == nullptr) { + if (outExt != nullptr) { + *outExt = nullptr; + } + return STATUS_UNEXPECTED_NULL; + } + + sp<IBinder> ext; + status_t res = binder->getBinder()->getExtension(&ext); + + if (res != android::OK) { + *outExt = nullptr; + return PruneStatusT(res); + } + + sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(ext); + if (ret != nullptr) ret->incStrong(binder); + + *outExt = ret.get(); + return STATUS_OK; +} + +binder_status_t AIBinder_setExtension(AIBinder* binder, AIBinder* ext) { + if (binder == nullptr || ext == nullptr) { + return STATUS_UNEXPECTED_NULL; + } + + ABBinder* rawBinder = binder->asABBinder(); + if (rawBinder == nullptr) { + return STATUS_INVALID_OPERATION; + } + + rawBinder->setExtension(ext->getBinder()); + return STATUS_OK; +} diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 5cb68c291b..57794279f2 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -17,6 +17,7 @@ #pragma once #include <android/binder_ibinder.h> +#include <android/binder_shell.h> #include "ibinder_internal.h" #include <atomic> @@ -115,6 +116,7 @@ struct AIBinder_Class { // optional methods for a class AIBinder_onDump onDump; + AIBinder_handleShellCommand handleShellCommand; private: // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h index c6868b07ef..2b61cf18c2 100644 --- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h +++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h @@ -21,7 +21,7 @@ /** * @file binder_auto_utils.h - * @brief These objects provide a more C++-like thin interface to the . + * @brief These objects provide a more C++-like thin interface to the binder. */ #pragma once @@ -34,6 +34,7 @@ #include <unistd.h> #include <cstddef> +#include <string> namespace ndk { @@ -159,13 +160,17 @@ class ScopedAResource { */ T* getR() { return &mT; } - // copy-constructing, or move/copy assignment is disallowed + // copy-constructing/assignment is disallowed ScopedAResource(const ScopedAResource&) = delete; ScopedAResource& operator=(const ScopedAResource&) = delete; - ScopedAResource& operator=(ScopedAResource&&) = delete; - // move-constructing is okay + // move-constructing/assignment is okay ScopedAResource(ScopedAResource&& other) : mT(std::move(other.mT)) { other.mT = DEFAULT; } + ScopedAResource& operator=(ScopedAResource&& other) { + set(other.mT); + other.mT = DEFAULT; + return *this; + } private: T mT; @@ -197,16 +202,61 @@ class ScopedAStatus : public impl::ScopedAResource<AStatus*, void, AStatus_delet explicit ScopedAStatus(AStatus* a = nullptr) : ScopedAResource(a) {} ~ScopedAStatus() {} ScopedAStatus(ScopedAStatus&&) = default; + ScopedAStatus& operator=(ScopedAStatus&&) = default; /** * See AStatus_isOk. */ - bool isOk() { return get() != nullptr && AStatus_isOk(get()); } + bool isOk() const { return get() != nullptr && AStatus_isOk(get()); } + + /** + * See AStatus_getExceptionCode + */ + binder_exception_t getExceptionCode() const { return AStatus_getExceptionCode(get()); } + + /** + * See AStatus_getServiceSpecificError + */ + int32_t getServiceSpecificError() const { return AStatus_getServiceSpecificError(get()); } + + /** + * See AStatus_getStatus + */ + binder_status_t getStatus() const { return AStatus_getStatus(get()); } /** - * Convenience method for okay status. + * See AStatus_getMessage + */ + const char* getMessage() const { return AStatus_getMessage(get()); } + + std::string getDescription() const { + const char* cStr = AStatus_getDescription(get()); + std::string ret = cStr; + AStatus_deleteDescription(cStr); + return ret; + } + + /** + * Convenience methods for creating scoped statuses. */ static ScopedAStatus ok() { return ScopedAStatus(AStatus_newOk()); } + static ScopedAStatus fromExceptionCode(binder_exception_t exception) { + return ScopedAStatus(AStatus_fromExceptionCode(exception)); + } + static ScopedAStatus fromExceptionCodeWithMessage(binder_exception_t exception, + const char* message) { + return ScopedAStatus(AStatus_fromExceptionCodeWithMessage(exception, message)); + } + static ScopedAStatus fromServiceSpecificError(int32_t serviceSpecific) { + return ScopedAStatus(AStatus_fromServiceSpecificError(serviceSpecific)); + } + static ScopedAStatus fromServiceSpecificErrorWithMessage(int32_t serviceSpecific, + const char* message) { + return ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(serviceSpecific, message)); + } + static ScopedAStatus fromStatus(binder_status_t status) { + return ScopedAStatus(AStatus_fromStatus(status)); + } }; /** diff --git a/libs/binder/ndk/include_ndk/android/binder_enums.h b/libs/binder/ndk/include_ndk/android/binder_enums.h new file mode 100644 index 0000000000..ee819c0b23 --- /dev/null +++ b/libs/binder/ndk/include_ndk/android/binder_enums.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @addtogroup NdkBinder + * @{ + */ + +/** + * @file binder_enums.h + * @brief Helpers for AIDL enum types. + */ + +#pragma once + +#include <iterator> +#include <type_traits> + +namespace ndk { + +namespace internal { +/** + * Never instantiated. Used as a placeholder for template variables. + */ +template <typename T> +struct invalid_type; + +/** + * AIDL generates specializations of this for enums. + */ +template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>> +constexpr invalid_type<EnumType> enum_values; +} // namespace internal + +/** + * Iterable interface to enumerate all values of AIDL enum types. + */ +template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>> +struct enum_range { + /** + * Return an iterator pointing to the first enum value. + */ + constexpr auto begin() const { return std::begin(internal::enum_values<EnumType>); } + /** + * Return an iterator pointing to one past the last enum value. + */ + constexpr auto end() const { return std::end(internal::enum_values<EnumType>); } +}; + +} // namespace ndk + +/** @} */
\ No newline at end of file diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 80d12541be..4560f222cb 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -34,7 +34,13 @@ #include <android/binder_status.h> __BEGIN_DECLS -#if __ANDROID_API__ >= __ANDROID_API_Q__ + +#ifndef __ANDROID_API__ +#error Android builds must be compiled against a specific API. If this is an \ + android platform host build, you must use libbinder_ndk_host_user. +#endif + +#if __ANDROID_API__ >= 29 // Also see TF_* in kernel's binder.h typedef uint32_t binder_flags_t; @@ -165,6 +171,8 @@ typedef binder_status_t (*AIBinder_Class_onTransact)(AIBinder* binder, transacti * * None of these parameters can be null. * + * Available since API level 29. + * * \param interfaceDescriptor this is a unique identifier for the class. This is used internally for * sanity checks on transactions. * \param onCreate see AIBinder_Class_onCreate. @@ -199,6 +207,8 @@ typedef binder_status_t (*AIBinder_onDump)(AIBinder* binder, int fd, const char* * If this isn't set, nothing will be dumped when dump is called (for instance with * android.os.Binder#dump). Must be called before any instance of the class is created. * + * Available since API level 29. + * * \param dump function to call when an instance of this binder class is being dumped. */ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29); @@ -220,6 +230,8 @@ void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __I * these two objects are actually equal using the AIBinder pointer alone (which they should be able * to do). Also see the suggested memory ownership model suggested above. * + * Available since API level 29. + * * \param clazz the type of the object to be created. * \param args the args to pass to AIBinder_onCreate for that class. * @@ -231,6 +243,8 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_new(const AIBinder_Class* /** * If this is hosted in a process other than the current one. * + * Available since API level 29. + * * \param binder the binder being queried. * * \return true if the AIBinder represents an object in another process. @@ -244,6 +258,8 @@ bool AIBinder_isRemote(const AIBinder* binder) __INTRODUCED_IN(29); * updated as the result of a transaction made using AIBinder_transact, but it will also be updated * based on the results of bookkeeping or other transactions made internally. * + * Available since API level 29. + * * \param binder the binder being queried. * * \return true if the binder is alive. @@ -255,6 +271,8 @@ bool AIBinder_isAlive(const AIBinder* binder) __INTRODUCED_IN(29); * return. Usually this is used to make sure that a binder is alive, as a placeholder call, or as a * sanity check. * + * Available since API level 29. + * * \param binder the binder being queried. * * \return STATUS_OK if the ping succeeds. @@ -264,7 +282,9 @@ binder_status_t AIBinder_ping(AIBinder* binder) __INTRODUCED_IN(29); /** * Built-in transaction for all binder objects. This dumps information about a given binder. * - * See also AIBinder_Class_setOnDump, AIBinder_onDump + * See also AIBinder_Class_setOnDump, AIBinder_onDump. + * + * Available since API level 29. * * \param binder the binder to dump information about * \param fd where information should be dumped to @@ -287,6 +307,8 @@ binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint3 * * If binder is local, this will return STATUS_INVALID_OPERATION. * + * Available since API level 29. + * * \param binder the binder object you want to receive death notifications from. * \param recipient the callback that will receive notifications when/if the binder dies. * \param cookie the value that will be passed to the death recipient on death. @@ -306,6 +328,8 @@ binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* * If the binder dies, it will automatically unlink. If the binder is deleted, it will be * automatically unlinked. * + * Available since API level 29. + * * \param binder the binder object to remove a previously linked death recipient from. * \param recipient the callback to remove. * \param cookie the cookie used to link to death. @@ -322,9 +346,11 @@ binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient * This can be used with higher-level system services to determine the caller's identity and check * permissions. * + * Available since API level 29. + * * \return calling uid or the current process's UID if this thread isn't processing a transaction. */ -uid_t AIBinder_getCallingUid(); +uid_t AIBinder_getCallingUid() __INTRODUCED_IN(29); /** * This returns the calling PID assuming that this thread is called from a thread that is processing @@ -335,14 +361,18 @@ uid_t AIBinder_getCallingUid(); * calling process dies and is replaced with another process with elevated permissions and the same * PID. * + * Available since API level 29. + * * \return calling pid or the current process's PID if this thread isn't processing a transaction. * If the transaction being processed is a oneway transaction, then this method will return 0. */ -pid_t AIBinder_getCallingPid(); +pid_t AIBinder_getCallingPid() __INTRODUCED_IN(29); /** * This can only be called if a strong reference to this object already exists in process. * + * Available since API level 29. + * * \param binder the binder object to add a refcount to. */ void AIBinder_incStrong(AIBinder* binder) __INTRODUCED_IN(29); @@ -350,6 +380,8 @@ void AIBinder_incStrong(AIBinder* binder) __INTRODUCED_IN(29); /** * This will delete the object and call onDestroy once the refcount reaches zero. * + * Available since API level 29. + * * \param binder the binder object to remove a refcount from. */ void AIBinder_decStrong(AIBinder* binder) __INTRODUCED_IN(29); @@ -357,6 +389,8 @@ void AIBinder_decStrong(AIBinder* binder) __INTRODUCED_IN(29); /** * For debugging only! * + * Available since API level 29. + * * \param binder the binder object to retrieve the refcount of. * * \return the number of strong-refs on this binder in this process. If binder is null, this will be @@ -373,6 +407,8 @@ int32_t AIBinder_debugGetRefCount(AIBinder* binder) __INTRODUCED_IN(29); * This returns true if the class association succeeds. If it fails, no change is made to the * binder object. * + * Available since API level 29. + * * \param binder the object to attach the class to. * \param clazz the clazz to attach to binder. * @@ -383,6 +419,8 @@ bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz) __IN /** * Returns the class that this binder was constructed with or associated with. * + * Available since API level 29. + * * \param binder the object that is being queried. * * \return the class that this binder is associated with. If this binder wasn't created with @@ -394,6 +432,8 @@ const AIBinder_Class* AIBinder_getClass(AIBinder* binder) __INTRODUCED_IN(29); * Value returned by onCreate for a local binder. For stateless classes (if onCreate returns * null), this also returns null. For a remote binder, this will always return null. * + * Available since API level 29. + * * \param binder the object that is being queried. * * \return the userdata returned from AIBinder_onCreate when this object was created. This may be @@ -422,6 +462,8 @@ void* AIBinder_getUserData(AIBinder* binder) __INTRODUCED_IN(29); * AIBinder_transact. Alternatively, if there is an error while filling out the parcel, it can be * deleted with AParcel_delete. * + * Available since API level 29. + * * \param binder the binder object to start a transaction on. * \param in out parameter for input data to the transaction. * @@ -442,6 +484,8 @@ binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) __IN * This does not affect the ownership of binder. The out parcel's ownership is passed to the caller * and must be released with AParcel_delete when finished reading. * + * Available since API level 29. + * * \param binder the binder object to transact on. * \param code the implementation-specific code representing which transaction should be taken. * \param in the implementation-specific input data to this transaction. @@ -459,6 +503,8 @@ binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, APa * This does not take any ownership of the input binder, but it can be used to retrieve it if * something else in some process still holds a reference to it. * + * Available since API level 29. + * * \param binder object to create a weak pointer to. * * \return object representing a weak pointer to binder (or null if binder is null). @@ -469,6 +515,8 @@ __attribute__((warn_unused_result)) AIBinder_Weak* AIBinder_Weak_new(AIBinder* b /** * Deletes the weak reference. This will have no impact on the lifetime of the binder. * + * Available since API level 29. + * * \param weakBinder object created with AIBinder_Weak_new. */ void AIBinder_Weak_delete(AIBinder_Weak* weakBinder) __INTRODUCED_IN(29); @@ -477,6 +525,8 @@ void AIBinder_Weak_delete(AIBinder_Weak* weakBinder) __INTRODUCED_IN(29); * If promotion succeeds, result will have one strong refcount added to it. Otherwise, this returns * null. * + * Available since API level 29. + * * \param weakBinder weak pointer to attempt retrieving the original object from. * * \return an AIBinder object with one refcount given to the caller or null. @@ -487,6 +537,8 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_Weak_promote(AIBinder_Wea /** * This function is executed on death receipt. See AIBinder_linkToDeath/AIBinder_unlinkToDeath. * + * Available since API level 29. + * * \param cookie the cookie passed to AIBinder_linkToDeath. */ typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_IN(29); @@ -494,6 +546,8 @@ typedef void (*AIBinder_DeathRecipient_onBinderDied)(void* cookie) __INTRODUCED_ /** * Creates a new binder death recipient. This can be attached to multiple different binder objects. * + * Available since API level 29. + * * \param onBinderDied the callback to call when this death recipient is invoked. * * \return the newly constructed object (or null if onBinderDied is null). @@ -505,11 +559,87 @@ __attribute__((warn_unused_result)) AIBinder_DeathRecipient* AIBinder_DeathRecip * Deletes a binder death recipient. It is not necessary to call AIBinder_unlinkToDeath before * calling this as these will all be automatically unlinked. * + * Available since API level 29. + * * \param recipient the binder to delete (previously created with AIBinder_DeathRecipient_new). */ void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) __INTRODUCED_IN(29); -#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +#endif //__ANDROID_API__ >= 29 + +#if __ANDROID_API__ >= 30 + +/** + * Gets the extension registered with AIBinder_setExtension. + * + * See AIBinder_setExtension. + * + * Available since API level 30. + * + * \param binder the object to get the extension of. + * \param outExt the returned extension object. Will be null if there is no extension set or + * non-null with one strong ref count. + * + * \return error of getting the interface (may be a transaction error if this is + * remote binder). STATUS_UNEXPECTED_NULL if binder is null. + */ +binder_status_t AIBinder_getExtension(AIBinder* binder, AIBinder** outExt) __INTRODUCED_IN(30); + +/** + * Gets the extension of a binder interface. This allows a downstream developer to add + * an extension to an interface without modifying its interface file. This should be + * called immediately when the object is created before it is passed to another thread. + * No thread safety is required. + * + * For instance, imagine if we have this interface: + * interface IFoo { void doFoo(); } + * + * A). Historical option that has proven to be BAD! Only the original + * author of an interface should change an interface. If someone + * downstream wants additional functionality, they should not ever + * change the interface or use this method. + * + * BAD TO DO: interface IFoo { BAD TO DO + * BAD TO DO: void doFoo(); BAD TO DO + * BAD TO DO: + void doBar(); // adding a method BAD TO DO + * BAD TO DO: } BAD TO DO + * + * B). Option that this method enables. + * Leave the original interface unchanged (do not change IFoo!). + * Instead, create a new interface in a downstream package: + * + * package com.<name>; // new functionality in a new package + * interface IBar { void doBar(); } + * + * When registering the interface, add: + * std::shared_ptr<MyFoo> foo = new MyFoo; // class in AOSP codebase + * std::shared_ptr<MyBar> bar = new MyBar; // custom extension class + * ... = AIBinder_setExtension(foo->asBinder().get(), bar->asBinder().get()); + * // handle error + * + * Then, clients of IFoo can get this extension: + * SpAIBinder binder = ...; + * std::shared_ptr<IFoo> foo = IFoo::fromBinder(binder); // handle if null + * SpAIBinder barBinder; + * ... = AIBinder_getExtension(barBinder.get()); + * // handle error + * std::shared_ptr<IBar> bar = IBar::fromBinder(barBinder); + * // type is checked with AIBinder_associateClass + * // if bar is null, then there is no extension or a different + * // type of extension + * + * Available since API level 30. + * + * \param binder the object to get the extension on. Must be local. + * \param ext the extension to set (binder will hold a strong reference to this) + * + * \return OK on success, STATUS_INVALID_OPERATION if binder is not local, STATUS_UNEXPECTED_NULL + * if either binder is null. + */ +binder_status_t AIBinder_setExtension(AIBinder* binder, AIBinder* ext) __INTRODUCED_IN(30); + +#endif //__ANDROID_API__ >= 30 + __END_DECLS /** @} */ diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h index 124f36c55b..cd1ff1fd79 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h @@ -31,16 +31,18 @@ #include <jni.h> __BEGIN_DECLS -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 /** * Converts an android.os.IBinder object into an AIBinder* object. * - * If either env or the binder is null, null is returned. If this binder object was originally an + * If the binder is null, null is returned. If this binder object was originally an * AIBinder object, the original object is returned. The returned object has one refcount * associated with it, and so this should be accompanied with an AIBinder_decStrong call. * - * \param env Java environment. + * Available since API level 29. + * + * \param env Java environment. Must not be null. * \param binder android.os.IBinder java object. * * \return an AIBinder object representing the Java binder object. If either parameter is null, or @@ -52,10 +54,12 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_fromJavaBinder(JNIEnv* en /** * Converts an AIBinder* object into an android.os.IBinder object. * - * If either env or the binder is null, null is returned. If this binder object was originally an - * IBinder object, the original java object will be returned. + * If the binder is null, null is returned. If this binder object was originally an IBinder object, + * the original java object will be returned. + * + * Available since API level 29. * - * \param env Java environment. + * \param env Java environment. Must not be null. * \param binder the object to convert. * * \return an android.os.IBinder object or null if the parameters were null. @@ -63,7 +67,7 @@ __attribute__((warn_unused_result)) AIBinder* AIBinder_fromJavaBinder(JNIEnv* en __attribute__((warn_unused_result)) jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder) __INTRODUCED_IN(29); -#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +#endif //__ANDROID_API__ >= 29 __END_DECLS /** @} */ diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h index 83a10488e0..e6b743ba3c 100644 --- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h @@ -30,6 +30,11 @@ #include <android/binder_auto_utils.h> #include <android/binder_ibinder.h> +#if __has_include(<android/binder_shell.h>) +#include <android/binder_shell.h> +#define HAS_BINDER_SHELL_COMMAND +#endif //_has_include + #include <assert.h> #include <memory> @@ -81,9 +86,15 @@ class SharedRefBase { return t->template ref<T>(); } + static void operator delete(void* p) { std::free(p); } + private: std::once_flag mFlagThis; std::weak_ptr<SharedRefBase> mThis; + + // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit + // ownership. Making this operator private to avoid double-ownership. + static void* operator new(size_t s) { return std::malloc(s); } }; /** @@ -108,7 +119,15 @@ class ICInterface : public SharedRefBase { /** * Dumps information about the interface. By default, dumps nothing. */ - virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/); + virtual inline binder_status_t dump(int fd, const char** args, uint32_t numArgs); + +#ifdef HAS_BINDER_SHELL_COMMAND + /** + * Process shell commands. By default, does nothing. + */ + virtual inline binder_status_t handleShellCommand(int in, int out, int err, const char** argv, + uint32_t argc); +#endif /** * Interprets this binder as this underlying interface if this has stored an ICInterface in the @@ -136,6 +155,11 @@ class ICInterface : public SharedRefBase { static inline void onDestroy(void* userData); static inline binder_status_t onDump(AIBinder* binder, int fd, const char** args, uint32_t numArgs); + +#ifdef HAS_BINDER_SHELL_COMMAND + static inline binder_status_t handleShellCommand(AIBinder* binder, int in, int out, int err, + const char** argv, uint32_t argc); +#endif }; }; @@ -191,6 +215,13 @@ binder_status_t ICInterface::dump(int /*fd*/, const char** /*args*/, uint32_t /* return STATUS_OK; } +#ifdef HAS_BINDER_SHELL_COMMAND +binder_status_t ICInterface::handleShellCommand(int /*in*/, int /*out*/, int /*err*/, + const char** /*argv*/, uint32_t /*argc*/) { + return STATUS_OK; +} +#endif + std::shared_ptr<ICInterface> ICInterface::asInterface(AIBinder* binder) { return ICInterfaceData::getInterface(binder); } @@ -203,9 +234,12 @@ AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor, return nullptr; } - // We can't know if this method is overriden by a subclass interface, so we must register - // ourselves. The default (nothing to dump) is harmless. + // We can't know if these methods are overridden by a subclass interface, so we must register + // ourselves. The defaults are harmless. AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump); +#ifdef HAS_BINDER_SHELL_COMMAND + AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand); +#endif return clazz; } @@ -234,6 +268,15 @@ binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, c return interface->dump(fd, args, numArgs); } +#ifdef HAS_BINDER_SHELL_COMMAND +binder_status_t ICInterface::ICInterfaceData::handleShellCommand(AIBinder* binder, int in, int out, + int err, const char** argv, + uint32_t argc) { + std::shared_ptr<ICInterface> interface = getInterface(binder); + return interface->handleShellCommand(in, out, err, argv, argc); +} +#endif + template <typename INTERFACE> SpAIBinder BnCInterface<INTERFACE>::asBinder() { std::lock_guard<std::mutex> l(mMutex); diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h index 2258210f2e..86b75b8c61 100644 --- a/libs/binder/ndk/include_ndk/android/binder_parcel.h +++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h @@ -26,6 +26,7 @@ #pragma once +#include <stddef.h> #include <sys/cdefs.h> #include <android/binder_status.h> @@ -34,7 +35,7 @@ struct AIBinder; typedef struct AIBinder AIBinder; __BEGIN_DECLS -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 /** * This object represents a package of data that can be sent between processes. When transacting, an @@ -48,6 +49,8 @@ typedef struct AParcel AParcel; /** * Cleans up a parcel. * + * Available since API level 29. + * * \param parcel A parcel returned by AIBinder_prepareTransaction or AIBinder_transact when a * transaction is being aborted. */ @@ -56,6 +59,8 @@ void AParcel_delete(AParcel* parcel) __INTRODUCED_IN(29); /** * Sets the position within the parcel. * + * Available since API level 29. + * * \param parcel The parcel of which to set the position. * \param position Position of the parcel to set. This must be a value returned by * AParcel_getDataPosition. Positions are constant for a given parcel between processes. @@ -68,6 +73,8 @@ binder_status_t AParcel_setDataPosition(const AParcel* parcel, int32_t position) /** * Gets the current position within the parcel. * + * Available since API level 29. + * * \param parcel The parcel of which to get the position. * * \return The size of the parcel. This will always be greater than 0. The values returned by this @@ -388,6 +395,8 @@ typedef bool (*AParcel_byteArrayAllocator)(void* arrayData, int32_t length, int8 * Writes an AIBinder to the next location in a non-null parcel. Can be null. This does not take any * refcounts of ownership of the binder from the client. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param binder the value to write to the parcel. * @@ -399,6 +408,8 @@ binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) __I * Reads an AIBinder from the next location in a non-null parcel. One strong ref-count of ownership * is passed to the caller of this function. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param binder the out parameter for what is read from the parcel. This may be null. * @@ -413,12 +424,14 @@ binder_status_t AParcel_readStrongBinder(const AParcel* parcel, AIBinder** binde * * This corresponds to the SDK's android.os.ParcelFileDescriptor. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param fd the value to write to the parcel (-1 to represent a null ParcelFileDescriptor). * * \return STATUS_OK on successful write. */ -binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd); +binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd) __INTRODUCED_IN(29); /** * Reads an int from the next location in a non-null parcel. @@ -427,13 +440,16 @@ binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd); * * This corresponds to the SDK's android.os.ParcelFileDescriptor. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param fd the out parameter for what is read from the parcel (or -1 to represent a null * ParcelFileDescriptor) * * \return STATUS_OK on successful write. */ -binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd); +binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd) + __INTRODUCED_IN(29); /** * Writes an AStatus object to the next location in a non-null parcel. @@ -444,6 +460,8 @@ binder_status_t AParcel_readParcelFileDescriptor(const AParcel* parcel, int* fd) * this happens or if writing the status object itself fails, the return value from this function * should be propagated to the client, and AParcel_readStatusHeader shouldn't be called. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param status the value to write to the parcel. * @@ -456,6 +474,8 @@ binder_status_t AParcel_writeStatusHeader(AParcel* parcel, const AStatus* status * Reads an AStatus from the next location in a non-null parcel. Ownership is passed to the caller * of this function. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param status the out parameter for what is read from the parcel. * @@ -469,6 +489,8 @@ binder_status_t AParcel_readStatusHeader(const AParcel* parcel, AStatus** status * * If length is -1, and string is nullptr, this will write a 'null' string to the parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param string the null-terminated string to write to the parcel, at least of size 'length'. * \param length the length of the string to be written. @@ -486,6 +508,8 @@ binder_status_t AParcel_writeString(AParcel* parcel, const char* string, int32_t * the output buffer from this read. If there is a 'null' string on the binder buffer, the allocator * will be called with length -1. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param stringData some external representation of a string. * \param allocator allocator that will be called once the size of the string is known. @@ -503,6 +527,8 @@ binder_status_t AParcel_readString(const AParcel* parcel, void* stringData, * returned from this function will be used to fill out the data from the parcel. If length is -1, * this will write a 'null' string array to the binder buffer. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData some external representation of an array. * \param length the length of the array to be written. @@ -525,6 +551,8 @@ binder_status_t AParcel_writeStringArray(AParcel* parcel, const void* arrayData, * the contents of the string that is read. If the string array being read is 'null', this will * instead just pass -1 to AParcel_stringArrayAllocator. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called with arrayData once the size of the output @@ -542,6 +570,8 @@ binder_status_t AParcel_readStringArray(const AParcel* parcel, void* arrayData, /** * Writes an array of parcelables (user-defined types) to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -561,6 +591,8 @@ binder_status_t AParcel_writeParcelableArray(AParcel* parcel, const void* arrayD * length is greater than zero, elementReader will be called for every index to read the * corresponding parcelable. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -577,6 +609,8 @@ binder_status_t AParcel_readParcelableArray(const AParcel* parcel, void* arrayDa /** * Writes int32_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -587,6 +621,8 @@ binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value) __INTRODUCED_ /** * Writes uint32_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -597,6 +633,8 @@ binder_status_t AParcel_writeUint32(AParcel* parcel, uint32_t value) __INTRODUCE /** * Writes int64_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -607,6 +645,8 @@ binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value) __INTRODUCED_ /** * Writes uint64_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -617,6 +657,8 @@ binder_status_t AParcel_writeUint64(AParcel* parcel, uint64_t value) __INTRODUCE /** * Writes float value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -627,6 +669,8 @@ binder_status_t AParcel_writeFloat(AParcel* parcel, float value) __INTRODUCED_IN /** * Writes double value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -637,6 +681,8 @@ binder_status_t AParcel_writeDouble(AParcel* parcel, double value) __INTRODUCED_ /** * Writes bool value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -647,6 +693,8 @@ binder_status_t AParcel_writeBool(AParcel* parcel, bool value) __INTRODUCED_IN(2 /** * Writes char16_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -657,6 +705,8 @@ binder_status_t AParcel_writeChar(AParcel* parcel, char16_t value) __INTRODUCED_ /** * Writes int8_t value to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param value the value to write to the parcel. * @@ -667,6 +717,8 @@ binder_status_t AParcel_writeByte(AParcel* parcel, int8_t value) __INTRODUCED_IN /** * Reads into int32_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -677,6 +729,8 @@ binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value) __INTRO /** * Reads into uint32_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -687,6 +741,8 @@ binder_status_t AParcel_readUint32(const AParcel* parcel, uint32_t* value) __INT /** * Reads into int64_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -697,6 +753,8 @@ binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value) __INTRO /** * Reads into uint64_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -707,6 +765,8 @@ binder_status_t AParcel_readUint64(const AParcel* parcel, uint64_t* value) __INT /** * Reads into float value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -717,6 +777,8 @@ binder_status_t AParcel_readFloat(const AParcel* parcel, float* value) __INTRODU /** * Reads into double value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -727,6 +789,8 @@ binder_status_t AParcel_readDouble(const AParcel* parcel, double* value) __INTRO /** * Reads into bool value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -737,6 +801,8 @@ binder_status_t AParcel_readBool(const AParcel* parcel, bool* value) __INTRODUCE /** * Reads into char16_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -747,6 +813,8 @@ binder_status_t AParcel_readChar(const AParcel* parcel, char16_t* value) __INTRO /** * Reads into int8_t value from the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param value the value to read from the parcel. * @@ -757,6 +825,8 @@ binder_status_t AParcel_readByte(const AParcel* parcel, int8_t* value) __INTRODU /** * Writes an array of int32_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -769,6 +839,8 @@ binder_status_t AParcel_writeInt32Array(AParcel* parcel, const int32_t* arrayDat /** * Writes an array of uint32_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -781,6 +853,8 @@ binder_status_t AParcel_writeUint32Array(AParcel* parcel, const uint32_t* arrayD /** * Writes an array of int64_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -793,6 +867,8 @@ binder_status_t AParcel_writeInt64Array(AParcel* parcel, const int64_t* arrayDat /** * Writes an array of uint64_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -805,6 +881,8 @@ binder_status_t AParcel_writeUint64Array(AParcel* parcel, const uint64_t* arrayD /** * Writes an array of float to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -817,6 +895,8 @@ binder_status_t AParcel_writeFloatArray(AParcel* parcel, const float* arrayData, /** * Writes an array of double to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -832,6 +912,8 @@ binder_status_t AParcel_writeDoubleArray(AParcel* parcel, const double* arrayDat * getter(arrayData, i) will be called for each i in [0, length) in order to get the underlying * values to write to the parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData some external representation of an array. * \param length the length of arrayData (or -1 if this represents a null array). @@ -845,6 +927,8 @@ binder_status_t AParcel_writeBoolArray(AParcel* parcel, const void* arrayData, i /** * Writes an array of char16_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -857,6 +941,8 @@ binder_status_t AParcel_writeCharArray(AParcel* parcel, const char16_t* arrayDat /** * Writes an array of int8_t to the next location in a non-null parcel. * + * Available since API level 29. + * * \param parcel the parcel to write to. * \param arrayData an array of size 'length' (or null if length is -1, may be null if length is 0). * \param length the length of arrayData or -1 if this represents a null array. @@ -873,6 +959,8 @@ binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -889,6 +977,8 @@ binder_status_t AParcel_readInt32Array(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -905,6 +995,8 @@ binder_status_t AParcel_readUint32Array(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -921,6 +1013,8 @@ binder_status_t AParcel_readInt64Array(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -937,6 +1031,8 @@ binder_status_t AParcel_readUint64Array(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -953,6 +1049,8 @@ binder_status_t AParcel_readFloatArray(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -968,6 +1066,8 @@ binder_status_t AParcel_readDoubleArray(const AParcel* parcel, void* arrayData, * First, allocator will be called with the length of the array. Then, for every i in [0, length), * setter(arrayData, i, x) will be called where x is the value at the associated index. * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -987,6 +1087,8 @@ binder_status_t AParcel_readBoolArray(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -1003,6 +1105,8 @@ binder_status_t AParcel_readCharArray(const AParcel* parcel, void* arrayData, * length is greater than zero, the buffer returned by the allocator will be filled with the * corresponding data * + * Available since API level 29. + * * \param parcel the parcel to read from. * \param arrayData some external representation of an array. * \param allocator the callback that will be called to allocate the array. @@ -1014,7 +1118,7 @@ binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, // @END-PRIMITIVE-READ-WRITE -#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +#endif //__ANDROID_API__ >= 29 __END_DECLS /** @} */ diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h new file mode 100644 index 0000000000..65e1704439 --- /dev/null +++ b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +/** + * @addtogroup NdkBinder + * @{ + */ + +/** + * @file binder_parcel_jni.h + * @brief Conversions between AParcel and android.os.Parcel + */ + +#pragma once + +#include <android/binder_parcel.h> + +#include <jni.h> + +__BEGIN_DECLS +#if __ANDROID_API__ >= 30 + +/** + * Converts an android.os.Parcel object into an AParcel* object. + * + * If the parcel is null, null is returned. + * + * Available since API level 30. + * + * \param env Java environment. Must not be null. + * \param parcel android.os.Parcel java object. + * + * \return an AParcel object representing the Java parcel object. If either parameter is null, this + * will return null. This must be deleted with AParcel_delete. This does not take ownership of the + * jobject and is only good for as long as the jobject is alive. + */ +__attribute__((warn_unused_result)) AParcel* AParcel_fromJavaParcel(JNIEnv* env, jobject parcel) + __INTRODUCED_IN(30); + +#endif //__ANDROID_API__ >= 30 +__END_DECLS + +/** @} */ diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h index f3bc31b0bb..df5df13c19 100644 --- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h +++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h @@ -421,13 +421,76 @@ static inline binder_status_t AParcel_readVector( } /** + * Convenience API for writing a non-null parcelable. + */ +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; + } + return p.writeToParcel(parcel); +} + +/** + * Convenience API for reading a non-null parcelable. + */ +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; + } + 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; + } + return p->writeToParcel(parcel); +} + +/** + * Convenience API for reading a nullable parcelable. + */ +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); +} + +/** * Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'. */ template <typename P> binder_status_t AParcel_writeStdVectorParcelableElement(AParcel* parcel, const void* vectorData, size_t index) { const std::vector<P>* vector = static_cast<const std::vector<P>*>(vectorData); - return vector->at(index).writeToParcel(parcel); + return AParcel_writeParcelable(parcel, vector->at(index)); } /** @@ -437,7 +500,43 @@ template <typename P> binder_status_t AParcel_readStdVectorParcelableElement(const AParcel* parcel, void* vectorData, size_t index) { std::vector<P>* vector = static_cast<std::vector<P>*>(vectorData); - return vector->at(index).readFromParcel(parcel); + return AParcel_readParcelable(parcel, &vector->at(index)); +} + +/** + * Writes a ScopedFileDescriptor object inside a std::vector<ScopedFileDescriptor> at index 'index' + * to 'parcel'. + */ +template <> +inline binder_status_t AParcel_writeStdVectorParcelableElement<ScopedFileDescriptor>( + AParcel* parcel, const void* vectorData, size_t index) { + const std::vector<ScopedFileDescriptor>* vector = + static_cast<const std::vector<ScopedFileDescriptor>*>(vectorData); + int writeFd = vector->at(index).get(); + if (writeFd < 0) { + return STATUS_UNEXPECTED_NULL; + } + return AParcel_writeParcelFileDescriptor(parcel, writeFd); +} + +/** + * Reads a ScopedFileDescriptor object inside a std::vector<ScopedFileDescriptor> at index 'index' + * from 'parcel'. + */ +template <> +inline binder_status_t AParcel_readStdVectorParcelableElement<ScopedFileDescriptor>( + const AParcel* parcel, void* vectorData, size_t index) { + std::vector<ScopedFileDescriptor>* vector = + static_cast<std::vector<ScopedFileDescriptor>*>(vectorData); + int readFd; + binder_status_t status = AParcel_readParcelFileDescriptor(parcel, &readFd); + if (status == STATUS_OK) { + if (readFd < 0) { + return STATUS_UNEXPECTED_NULL; + } + vector->at(index).set(readFd); + } + return status; } /** diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h index 2671b9b6fc..ab9a144c53 100644 --- a/libs/binder/ndk/include_ndk/android/binder_status.h +++ b/libs/binder/ndk/include_ndk/android/binder_status.h @@ -30,7 +30,7 @@ #include <sys/cdefs.h> __BEGIN_DECLS -#if __ANDROID_API__ >= __ANDROID_API_Q__ +#if __ANDROID_API__ >= 29 enum { STATUS_OK = 0, @@ -105,6 +105,8 @@ typedef struct AStatus AStatus; /** * New status which is considered a success. * + * Available since API level 29. + * * \return a newly constructed status object that the caller owns. */ __attribute__((warn_unused_result)) AStatus* AStatus_newOk() __INTRODUCED_IN(29); @@ -112,6 +114,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_newOk() __INTRODUCED_IN(29) /** * New status with exception code. * + * Available since API level 29. + * * \param exception the code that this status should represent. If this is EX_NONE, then this * constructs an non-error status object. * @@ -123,6 +127,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_fromExceptionCode(binder_ex /** * New status with exception code and message. * + * Available since API level 29. + * * \param exception the code that this status should represent. If this is EX_NONE, then this * constructs an non-error status object. * \param message the error message to associate with this status object. @@ -137,6 +143,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_fromExceptionCodeWithMessag * * This is considered to be EX_TRANSACTION_FAILED with extra information. * + * Available since API level 29. + * * \param serviceSpecific an implementation defined error code. * * \return a newly constructed status object that the caller owns. @@ -149,6 +157,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_fromServiceSpecificError( * * This is considered to be EX_TRANSACTION_FAILED with extra information. * + * Available since API level 29. + * * \param serviceSpecific an implementation defined error code. * \param message the error message to associate with this status object. * @@ -162,6 +172,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_fromServiceSpecificErrorWit * is returned by an API on AIBinder or AParcel, and that is to be returned from a method returning * an AStatus instance. * + * Available since API level 29. + * * \param a low-level error to associate with this status object. * * \return a newly constructed status object that the caller owns. @@ -173,6 +185,8 @@ __attribute__((warn_unused_result)) AStatus* AStatus_fromStatus(binder_status_t * Whether this object represents a successful transaction. If this function returns true, then * AStatus_getExceptionCode will return EX_NONE. * + * Available since API level 29. + * * \param status the status being queried. * * \return whether the status represents a successful transaction. For more details, see below. @@ -182,6 +196,8 @@ bool AStatus_isOk(const AStatus* status) __INTRODUCED_IN(29); /** * The exception that this status object represents. * + * Available since API level 29. + * * \param status the status being queried. * * \return the exception code that this object represents. @@ -194,6 +210,8 @@ binder_exception_t AStatus_getExceptionCode(const AStatus* status) __INTRODUCED_ * 0, the status object may still represent a different exception or status. To find out if this * transaction as a whole is okay, use AStatus_isOk instead. * + * Available since API level 29. + * * \param status the status being queried. * * \return the service-specific error code if the exception code is EX_SERVICE_SPECIFIC or 0. @@ -206,6 +224,8 @@ int32_t AStatus_getServiceSpecificError(const AStatus* status) __INTRODUCED_IN(2 * object may represent a different exception or a service specific error. To find out if this * transaction as a whole is okay, use AStatus_isOk instead. * + * Available since API level 29. + * * \param status the status being queried. * * \return the status code if the exception code is EX_TRANSACTION_FAILED or 0. @@ -218,6 +238,8 @@ binder_status_t AStatus_getStatus(const AStatus* status) __INTRODUCED_IN(29); * * The returned string has the lifetime of the status object passed into this function. * + * Available since API level 29. + * * \param status the status being queried. * * \return the message associated with this error. @@ -225,13 +247,34 @@ binder_status_t AStatus_getStatus(const AStatus* status) __INTRODUCED_IN(29); const char* AStatus_getMessage(const AStatus* status) __INTRODUCED_IN(29); /** + * Get human-readable description for debugging. + * + * Available since API level 30. + * + * \param status the status being queried. + * + * \return a description, must be deleted with AStatus_deleteDescription. + */ +__attribute__((warn_unused_result)) const char* AStatus_getDescription(const AStatus* status) + __INTRODUCED_IN(30); + +/** + * Delete description. + * + * \param description value from AStatus_getDescription + */ +void AStatus_deleteDescription(const char* description) __INTRODUCED_IN(30); + +/** * Deletes memory associated with the status instance. * + * Available since API level 29. + * * \param status the status to delete, returned from AStatus_newOk or one of the AStatus_from* APIs. */ void AStatus_delete(AStatus* status) __INTRODUCED_IN(29); -#endif //__ANDROID_API__ >= __ANDROID_API_Q__ +#endif //__ANDROID_API__ >= 29 __END_DECLS /** @} */ diff --git a/libs/binder/ndk/include_apex/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 055c79bca1..055c79bca1 100644 --- a/libs/binder/ndk/include_apex/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h new file mode 100644 index 0000000000..ac46cb80f4 --- /dev/null +++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/binder_parcel.h> + +__BEGIN_DECLS + +/** + * Gets whether or not FDs are allowed by this AParcel + * + * \return true if FDs are allowed, false if they are not. That is + * if this returns false then AParcel_writeParcelFileDescriptor will + * return STATUS_FDS_NOT_ALLOWED. + */ +bool AParcel_getAllowFds(const AParcel*); + +__END_DECLS
\ No newline at end of file diff --git a/libs/binder/ndk/include_apex/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h index 69e6387bcb..fdefbb4b8a 100644 --- a/libs/binder/ndk/include_apex/android/binder_process.h +++ b/libs/binder/ndk/include_platform/android/binder_process.h @@ -27,7 +27,7 @@ __BEGIN_DECLS void ABinderProcess_startThreadPool(); /** * This sets the maximum number of threads that can be started in the threadpool. By default, after - * startThreadPool is called, this is one. If it is called additional times, it will only prevent + * startThreadPool is called, this is 15. If it is called additional times, it will only prevent * the kernel from starting new threads and will not delete already existing threads. */ bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads); diff --git a/libs/binder/ndk/include_platform/android/binder_shell.h b/libs/binder/ndk/include_platform/android/binder_shell.h new file mode 100644 index 0000000000..17b38b0dae --- /dev/null +++ b/libs/binder/ndk/include_platform/android/binder_shell.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/binder_ibinder.h> + +__BEGIN_DECLS + +/** + * Function to execute a shell command. + * + * Available since API level 30. + * + * \param binder the binder executing the command + * \param in input file descriptor, should be flushed, ownership is not passed + * \param out output file descriptor, should be flushed, ownership is not passed + * \param err error file descriptor, should be flushed, ownership is not passed + * \param argv array of null-terminated strings for command (may be null if argc + * is 0) + * \param argc length of argv array + * + * \return binder_status_t result of transaction + */ +typedef binder_status_t (*AIBinder_handleShellCommand)(AIBinder* binder, int in, int out, int err, + const char** argv, uint32_t argc); + +/** + * This sets the implementation of handleShellCommand for a class. + * + * If this isn't set, nothing will be executed when handleShellCommand is called. + * + * Available since API level 30. + * + * \param handleShellCommand function to call when a shell transaction is + * received + */ +void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz, + AIBinder_handleShellCommand handleShellCommand) + __INTRODUCED_IN(30); + +__END_DECLS diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h new file mode 100644 index 0000000000..f5e8bf60ef --- /dev/null +++ b/libs/binder/ndk/include_platform/android/binder_stability.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android/binder_ibinder.h> + +__BEGIN_DECLS + +/** + * Private addition to binder_flag_t. + */ +enum { + /** + * Indicates that this transaction is coupled w/ vendor.img + */ + FLAG_PRIVATE_VENDOR = 0x10000000, +}; + +#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) + +enum { + FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_VENDOR, +}; + +/** + * This interface has the stability of the vendor image. + */ +void AIBinder_markVendorStability(AIBinder* binder); + +static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) { + AIBinder_markVendorStability(binder); +} + +#else // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) + +enum { + FLAG_PRIVATE_LOCAL = 0, +}; + +/** + * This interface has the stability of the system image. + */ +__attribute__((weak)) void AIBinder_markSystemStability(AIBinder* binder); + +static inline void AIBinder_markCompilationUnitStability(AIBinder* binder) { + if (AIBinder_markSystemStability == nullptr) return; + + AIBinder_markSystemStability(binder); +} + +#endif // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) + +/** + * This interface has system<->vendor stability + */ +void AIBinder_markVintfStability(AIBinder* binder); + +__END_DECLS diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 7e6581736f..a9eba47380 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -89,12 +89,33 @@ LIBBINDER_NDK { # introduced=29 AStatus_getStatus; AStatus_isOk; AStatus_newOk; - ABinderProcess_joinThreadPool; # apex - ABinderProcess_setThreadPoolMaxThreadCount; # apex - ABinderProcess_startThreadPool; # apex - AServiceManager_addService; # apex - AServiceManager_checkService; # apex - AServiceManager_getService; # apex + ABinderProcess_joinThreadPool; # apex llndk + ABinderProcess_setThreadPoolMaxThreadCount; # apex llndk + ABinderProcess_startThreadPool; # apex llndk + AServiceManager_addService; # apex llndk + AServiceManager_checkService; # apex llndk + AServiceManager_getService; # apex llndk local: *; }; + +LIBBINDER_NDK30 { # introduced=30 + global: + AIBinder_getExtension; + AIBinder_setExtension; + AStatus_getDescription; + AStatus_deleteDescription; + AParcel_fromJavaParcel; + + AIBinder_markSystemStability; # apex + AIBinder_markVendorStability; # llndk + AIBinder_markVintfStability; # apex llndk + AIBinder_Class_setHandleShellCommand; # apex llndk + local: + *; +}; + +LIBBINDER_NDK_PLATFORM { + global: + AParcel_getAllowFds; +}; diff --git a/libs/binder/ndk/parcel.cpp b/libs/binder/ndk/parcel.cpp index ae2276e794..703ceaed54 100644 --- a/libs/binder/ndk/parcel.cpp +++ b/libs/binder/ndk/parcel.cpp @@ -15,6 +15,7 @@ */ #include <android/binder_parcel.h> +#include <android/binder_parcel_platform.h> #include "parcel_internal.h" #include "ibinder_internal.h" @@ -50,7 +51,7 @@ binder_status_t WriteAndValidateArraySize(AParcel* parcel, bool isNullArray, int if (length < -1) return STATUS_BAD_VALUE; if (!isNullArray && length < 0) { - LOG(ERROR) << __func__ << ": null array must be used with length == -1."; + LOG(ERROR) << __func__ << ": non-null array but length is " << length; return STATUS_BAD_VALUE; } if (isNullArray && length > 0) { @@ -242,23 +243,18 @@ binder_status_t AParcel_readStrongBinder(const AParcel* parcel, AIBinder** binde } binder_status_t AParcel_writeParcelFileDescriptor(AParcel* parcel, int fd) { - std::unique_ptr<ParcelFileDescriptor> parcelFd; - if (fd < 0) { if (fd != -1) { return STATUS_UNKNOWN_ERROR; } - // parcelFd = nullptr - } else { // fd >= 0 - parcelFd = std::make_unique<ParcelFileDescriptor>(unique_fd(fd)); + return parcel->get()->writeInt32(0); // null } - status_t status = parcel->get()->writeNullableParcelable(parcelFd); + ParcelFileDescriptor parcelFd = ParcelFileDescriptor(unique_fd(fd)); + status_t status = parcel->get()->writeParcelable(parcelFd); // ownership is retained by caller - if (parcelFd != nullptr) { - (void)parcelFd->release().release(); - } + (void)parcelFd.release().release(); return PruneStatusT(status); } @@ -650,4 +646,8 @@ binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData, return ReadArray<int8_t>(parcel, arrayData, allocator); } +bool AParcel_getAllowFds(const AParcel* parcel) { + return parcel->get()->allowFds(); +} + // @END diff --git a/libs/binder/ndk/parcel_jni.cpp b/libs/binder/ndk/parcel_jni.cpp new file mode 100644 index 0000000000..53b2d7c4da --- /dev/null +++ b/libs/binder/ndk/parcel_jni.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_parcel_jni.h> +#include "parcel_internal.h" + +#include <android_os_Parcel.h> + +using ::android::Parcel; +using ::android::parcelForJavaObject; + +AParcel* AParcel_fromJavaParcel(JNIEnv* env, jobject jbinder) { + if (jbinder == nullptr) { + return nullptr; + } + + Parcel* parcel = parcelForJavaObject(env, jbinder); + + if (parcel == nullptr) { + return nullptr; + } + + return new AParcel(nullptr /*binder*/, parcel, false /*shouldOwn*/); +} diff --git a/libs/binder/ndk/runtests.sh b/libs/binder/ndk/runtests.sh deleted file mode 100755 index a0c49fb167..0000000000 --- a/libs/binder/ndk/runtests.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (C) 2018 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ -z $ANDROID_BUILD_TOP ]; then - echo "You need to source and lunch before you can use this script" - exit 1 -fi - -set -ex - -function run_libbinder_ndk_test() { - adb shell /data/nativetest64/libbinder_ndk_test_server/libbinder_ndk_test_server & - - # avoid getService 1s delay for most runs, non-critical - sleep 0.1 - - adb shell /data/nativetest64/libbinder_ndk_test_client/libbinder_ndk_test_client; \ - adb shell killall libbinder_ndk_test_server -} - -[ "$1" != "--skip-build" ] && $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \ - MODULES-IN-frameworks-native-libs-binder-ndk - -adb root -adb wait-for-device -adb sync data - -# very simple unit tests, tests things outside of the NDK as well -run_libbinder_ndk_test - -# CTS tests (much more comprehensive, new tests should ideally go here) -atest android.binder.cts diff --git a/libs/binder/ndk/scripts/init_map.sh b/libs/binder/ndk/scripts/init_map.sh deleted file mode 100755 index 3529b725ce..0000000000 --- a/libs/binder/ndk/scripts/init_map.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -# Simple helper for ease of development until this API is frozen. - -echo "LIBBINDER_NDK { # introduced=29" -echo " global:" -{ - grep -oP "AIBinder_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_ibinder.h; - grep -oP "AIBinder_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_ibinder_jni.h; - grep -oP "AParcel_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_parcel.h; - grep -oP "AStatus_[a-zA-Z0-9_]+(?=\()" include_ndk/android/binder_status.h; -} | sort | uniq | awk '{ print " " $0 ";"; }' -{ - grep -oP "AServiceManager_[a-zA-Z0-9_]+(?=\()" include_apex/android/binder_manager.h; - grep -oP "ABinderProcess_[a-zA-Z0-9_]+(?=\()" include_apex/android/binder_process.h; -} | sort | uniq | awk '{ print " " $0 "; # apex"; }' -echo " local:" -echo " *;" -echo "};" diff --git a/libs/binder/ndk/stability.cpp b/libs/binder/ndk/stability.cpp new file mode 100644 index 0000000000..a5b3ecea4a --- /dev/null +++ b/libs/binder/ndk/stability.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_stability.h> + +#include <binder/Stability.h> +#include "ibinder_internal.h" + +#include <log/log.h> + +using ::android::internal::Stability; + +#ifdef __ANDROID_VNDK__ +#error libbinder_ndk should only be built in a system context +#endif + +#ifdef __ANDROID_NDK__ +#error libbinder_ndk should only be built in a system context +#endif + +// explicit extern because symbol is only declared in header when __ANDROID_VNDK__ +extern "C" void AIBinder_markVendorStability(AIBinder* binder) { + Stability::markVndk(binder->getBinder().get()); +} + +void AIBinder_markSystemStability(AIBinder* binder) { + Stability::markCompilationUnit(binder->getBinder().get()); +} + +void AIBinder_markVintfStability(AIBinder* binder) { + Stability::markVintf(binder->getBinder().get()); +} diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp index 1f75b0b05d..87e1341f36 100644 --- a/libs/binder/ndk/status.cpp +++ b/libs/binder/ndk/status.cpp @@ -66,6 +66,17 @@ const char* AStatus_getMessage(const AStatus* status) { return status->get()->exceptionMessage().c_str(); } +const char* AStatus_getDescription(const AStatus* status) { + android::String8 description = status->get()->toString8(); + char* cStr = new char[description.size() + 1]; + memcpy(cStr, description.c_str(), description.size() + 1); + return cStr; +} + +void AStatus_deleteDescription(const char* description) { + delete[] const_cast<char*>(description); +} + void AStatus_delete(AStatus* status) { delete status; } diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp index 8cd4e033df..cb4b20ff9d 100644 --- a/libs/binder/ndk/test/Android.bp +++ b/libs/binder/ndk/test/Android.bp @@ -44,10 +44,10 @@ cc_defaults { "libandroid_runtime_lazy", "libbase", "libbinder", + "libbinder_ndk", "libutils", ], static_libs: [ - "libbinder_ndk", "test_libbinder_ndk_library", ], } @@ -56,14 +56,52 @@ cc_defaults { // specifically the parts which are outside of the NDK. Actual users should // also instead use AIDL to generate these stubs. See android.binder.cts. cc_test { - name: "libbinder_ndk_test_client", + name: "libbinder_ndk_unit_test", defaults: ["test_libbinder_ndk_test_defaults"], - srcs: ["main_client.cpp"], + srcs: ["libbinder_ndk_unit_test.cpp"], + static_libs: [ + "IBinderNdkUnitTest-cpp", + "IBinderNdkUnitTest-ndk_platform", + ], + test_suites: ["general-tests"], + require_root: true, + + // force since binderVendorDoubleLoadTest has its own + auto_gen_config: true, } cc_test { - name: "libbinder_ndk_test_server", - defaults: ["test_libbinder_ndk_test_defaults"], - srcs: ["main_server.cpp"], - gtest: false, + name: "binderVendorDoubleLoadTest", + vendor: true, + srcs: [ + "binderVendorDoubleLoadTest.cpp", + ], + static_libs: [ + "IBinderVendorDoubleLoadTest-cpp", + "IBinderVendorDoubleLoadTest-ndk_platform", + "libbinder_aidl_test_stub-ndk_platform", + ], + shared_libs: [ + "libbase", + "libbinder", + "libbinder_ndk", + "libutils", + ], + test_suites: ["general-tests"], +} + +aidl_interface { + name: "IBinderVendorDoubleLoadTest", + vendor: true, + srcs: [ + "IBinderVendorDoubleLoadTest.aidl", + ], +} + +aidl_interface { + name: "IBinderNdkUnitTest", + srcs: [ + "IBinderNdkUnitTest.aidl", + "IEmpty.aidl", + ], } diff --git a/libs/binder/ndk/test/AndroidTest.xml b/libs/binder/ndk/test/AndroidTest.xml new file mode 100644 index 0000000000..89646f7776 --- /dev/null +++ b/libs/binder/ndk/test/AndroidTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs binderVendorDoubleLoadTest."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true" /> + <option name="push" value="binderVendorDoubleLoadTest->/data/nativetest/vendor/binderVendorDoubleLoadTest" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/nativetest/vendor" /> + <option name="module-name" value="binderVendorDoubleLoadTest" /> + </test> +</configuration> + diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl new file mode 100644 index 0000000000..6e8e463ff1 --- /dev/null +++ b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl @@ -0,0 +1,27 @@ +/* + * 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. + */ + +// This AIDL is to test things that can't be tested in CtsNdkBinderTestCases +// because it requires libbinder_ndk implementation details or APIs not +// available to apps. Please prefer adding tests to CtsNdkBinderTestCases +// over here. + +import IEmpty; + +interface IBinderNdkUnitTest { + void takeInterface(IEmpty test); + void forceFlushCommands(); +} diff --git a/libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl b/libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl new file mode 100644 index 0000000000..3a5bd9cc56 --- /dev/null +++ b/libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +interface IBinderVendorDoubleLoadTest { + @utf8InCpp String RepeatString(@utf8InCpp String toRepeat); +} diff --git a/libs/binder/ndk/test/IEmpty.aidl b/libs/binder/ndk/test/IEmpty.aidl new file mode 100644 index 0000000000..95e4341531 --- /dev/null +++ b/libs/binder/ndk/test/IEmpty.aidl @@ -0,0 +1,17 @@ +/* + * 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. + */ + +interface IEmpty { } diff --git a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp new file mode 100644 index 0000000000..ad78e319a2 --- /dev/null +++ b/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <BnBinderVendorDoubleLoadTest.h> +#include <aidl/BnBinderVendorDoubleLoadTest.h> +#include <aidl/android/os/IServiceManager.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/strings.h> +#include <android/binder_ibinder.h> +#include <android/binder_manager.h> +#include <android/binder_process.h> +#include <android/binder_stability.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <binder/Stability.h> +#include <binder/Status.h> +#include <gtest/gtest.h> + +#include <sys/prctl.h> + +using namespace android; +using ::android::base::EndsWith; +using ::android::base::GetProperty; +using ::android::base::Split; +using ::android::binder::Status; +using ::android::internal::Stability; +using ::ndk::ScopedAStatus; +using ::ndk::SharedRefBase; +using ::ndk::SpAIBinder; + +static const std::string kLocalNdkServerName = "NdkServer-local-IBinderVendorDoubleLoadTest"; +static const std::string kRemoteNdkServerName = "NdkServer-remote-IBinderVendorDoubleLoadTest"; + +class NdkServer : public aidl::BnBinderVendorDoubleLoadTest { + ScopedAStatus RepeatString(const std::string& in, std::string* out) override { + *out = in; + return ScopedAStatus::ok(); + } +}; +class CppServer : public BnBinderVendorDoubleLoadTest { + Status RepeatString(const std::string& in, std::string* out) override { + *out = in; + return Status::ok(); + } +}; + +TEST(DoubleBinder, VendorCppCantCallIntoSystem) { + Vector<String16> services = defaultServiceManager()->listServices(); + EXPECT_TRUE(services.empty()); +} + +TEST(DoubleBinder, VendorCppCantRegisterService) { + sp<CppServer> cppServer = new CppServer; + status_t status = defaultServiceManager()->addService(String16("anything"), cppServer); + EXPECT_EQ(EX_TRANSACTION_FAILED, status); +} + +TEST(DoubleBinder, CppVendorCantManuallyMarkVintfStability) { + // this test also implies that stability logic is turned on in vendor + ASSERT_DEATH( + { + sp<IBinder> binder = new CppServer(); + Stability::markVintf(binder.get()); + }, + "Should only mark known object."); +} + +TEST(DoubleBinder, NdkVendorCantManuallyMarkVintfStability) { + // this test also implies that stability logic is turned on in vendor + ASSERT_DEATH( + { + std::shared_ptr<NdkServer> ndkServer = SharedRefBase::make<NdkServer>(); + AIBinder_markVintfStability(ndkServer->asBinder().get()); + }, + "Should only mark known object."); +} + +TEST(DoubleBinder, CallIntoNdk) { + for (const std::string& serviceName : {kLocalNdkServerName, kRemoteNdkServerName}) { + SpAIBinder binder = SpAIBinder(AServiceManager_checkService(serviceName.c_str())); + ASSERT_NE(nullptr, binder.get()) << serviceName; + EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())) << serviceName; + + std::shared_ptr<aidl::IBinderVendorDoubleLoadTest> server = + aidl::IBinderVendorDoubleLoadTest::fromBinder(binder); + + ASSERT_NE(nullptr, server.get()) << serviceName; + + EXPECT_EQ(STATUS_OK, AIBinder_ping(server->asBinder().get())); + + std::string outString; + ScopedAStatus status = server->RepeatString("foo", &outString); + EXPECT_EQ(STATUS_OK, AStatus_getExceptionCode(status.get())) + << serviceName << " " << status.getDescription(); + EXPECT_EQ("foo", outString) << serviceName; + } +} + +TEST(DoubleBinder, CallIntoSystemStabilityNdk) { + // picking an arbitrary system service + SpAIBinder binder = SpAIBinder(AServiceManager_checkService("manager")); + ASSERT_NE(nullptr, binder.get()); + + // can make stable transaction to system server + EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); + + using aidl::android::os::IServiceManager; + std::shared_ptr<IServiceManager> manager = IServiceManager::fromBinder(binder); + ASSERT_NE(nullptr, manager.get()); + + std::vector<std::string> services; + ASSERT_EQ( + STATUS_BAD_TYPE, + manager->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL, &services).getStatus()); +} + +void initDrivers() { + // Explicitly instantiated with the same driver that system would use. + // __ANDROID_VNDK__ right now uses /dev/vndbinder by default. + ProcessState::initWithDriver("/dev/binder"); + ProcessState::self()->startThreadPool(); + ABinderProcess_startThreadPool(); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + if (fork() == 0) { + // child process + + prctl(PR_SET_PDEATHSIG, SIGHUP); + + initDrivers(); + + // REMOTE SERVERS + std::shared_ptr<NdkServer> ndkServer = SharedRefBase::make<NdkServer>(); + CHECK(STATUS_OK == AServiceManager_addService(ndkServer->asBinder().get(), + kRemoteNdkServerName.c_str())); + + // OR sleep forever or whatever, it doesn't matter + IPCThreadState::self()->joinThreadPool(true); + exit(1); // should not reach + } + + sleep(1); + + initDrivers(); + + // LOCAL SERVERS + std::shared_ptr<NdkServer> ndkServer = SharedRefBase::make<NdkServer>(); + AServiceManager_addService(ndkServer->asBinder().get(), kLocalNdkServerName.c_str()); + + return RUN_ALL_TESTS(); +} diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp index 8467734c75..fd30d87c76 100644 --- a/libs/binder/ndk/test/main_client.cpp +++ b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#include <IBinderNdkUnitTest.h> +#include <aidl/BnBinderNdkUnitTest.h> +#include <aidl/BnEmpty.h> #include <android-base/logging.h> #include <android/binder_ibinder_jni.h> #include <android/binder_manager.h> @@ -21,13 +24,88 @@ #include <gtest/gtest.h> #include <iface/iface.h> +// warning: this is assuming that libbinder_ndk is using the same copy +// of libbinder that we are. +#include <binder/IPCThreadState.h> +#include <binder/IResultReceiver.h> +#include <binder/IServiceManager.h> +#include <binder/IShellCallback.h> + +#include <sys/prctl.h> #include <chrono> #include <condition_variable> #include <mutex> -using ::android::sp; +using namespace android; constexpr char kExistingNonNdkService[] = "SurfaceFlinger"; +constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest"; + +class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { + ndk::ScopedAStatus takeInterface(const std::shared_ptr<aidl::IEmpty>& empty) { + (void)empty; + return ndk::ScopedAStatus::ok(); + } + ndk::ScopedAStatus forceFlushCommands() { + // warning: this is assuming that libbinder_ndk is using the same copy + // of libbinder that we are. + android::IPCThreadState::self()->flushCommands(); + return ndk::ScopedAStatus::ok(); + } + binder_status_t handleShellCommand(int /*in*/, int out, int /*err*/, const char** args, + uint32_t numArgs) override { + for (uint32_t i = 0; i < numArgs; i++) { + dprintf(out, "%s", args[i]); + } + fsync(out); + return STATUS_OK; + } +}; + +int generatedService() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + + auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>(); + binder_status_t status = + AServiceManager_addService(service->asBinder().get(), kBinderNdkUnitTestService); + + if (status != STATUS_OK) { + LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService; + } + + ABinderProcess_joinThreadPool(); + + return 1; // should not return +} + +// manually-written parceling class considered bad practice +class MyFoo : public IFoo { + binder_status_t doubleNumber(int32_t in, int32_t* out) override { + *out = 2 * in; + LOG(INFO) << "doubleNumber (" << in << ") => " << *out; + return STATUS_OK; + } + + binder_status_t die() override { + LOG(FATAL) << "IFoo::die called!"; + return STATUS_UNKNOWN_ERROR; + } +}; + +int manualService(const char* instance) { + ABinderProcess_setThreadPoolMaxThreadCount(0); + + // Strong reference to MyFoo kept by service manager. + binder_status_t status = (new MyFoo)->addService(instance); + + if (status != STATUS_OK) { + LOG(FATAL) << "Could not register: " << status << " " << instance; + } + + ABinderProcess_joinThreadPool(); + + return 1; // should not return +} // This is too slow // TEST(NdkBinder, GetServiceThatDoesntExist) { @@ -87,14 +165,14 @@ TEST(NdkBinder, DeathRecipient) { EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die()); foo = nullptr; - AIBinder_decStrong(binder); - binder = nullptr; std::unique_lock<std::mutex> lock(deathMutex); EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; })); EXPECT_TRUE(deathRecieved); AIBinder_DeathRecipient_delete(recipient); + AIBinder_decStrong(binder); + binder = nullptr; } TEST(NdkBinder, RetrieveNonNdkService) { @@ -196,9 +274,142 @@ TEST(NdkBinder, AddServiceMultipleTimes) { EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2)); } +TEST(NdkBinder, SentAidlBinderCanBeDestroyed) { + static volatile bool destroyed = false; + static std::mutex dMutex; + static std::condition_variable cv; + + class MyEmpty : public aidl::BnEmpty { + virtual ~MyEmpty() { + destroyed = true; + cv.notify_one(); + } + }; + + std::shared_ptr<MyEmpty> empty = ndk::SharedRefBase::make<MyEmpty>(); + + ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService)); + std::shared_ptr<aidl::IBinderNdkUnitTest> service = + aidl::IBinderNdkUnitTest::fromBinder(binder); + + EXPECT_FALSE(destroyed); + + service->takeInterface(empty); + service->forceFlushCommands(); + empty = nullptr; + + // give other binder thread time to process commands + { + using namespace std::chrono_literals; + std::unique_lock<std::mutex> lk(dMutex); + cv.wait_for(lk, 1s, [] { return destroyed; }); + } + + EXPECT_TRUE(destroyed); +} + +class MyResultReceiver : public BnResultReceiver { + public: + Mutex mMutex; + Condition mCondition; + bool mHaveResult = false; + int32_t mResult = 0; + + virtual void send(int32_t resultCode) { + AutoMutex _l(mMutex); + mResult = resultCode; + mHaveResult = true; + mCondition.signal(); + } + + int32_t waitForResult() { + AutoMutex _l(mMutex); + while (!mHaveResult) { + mCondition.wait(mMutex); + } + return mResult; + } +}; + +class MyShellCallback : public BnShellCallback { + public: + virtual int openFile(const String16& /*path*/, const String16& /*seLinuxContext*/, + const String16& /*mode*/) { + // Empty implementation. + return 0; + } +}; + +bool ReadFdToString(int fd, std::string* content) { + char buf[64]; + ssize_t n; + while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) { + content->append(buf, n); + } + return (n == 0) ? true : false; +} + +std::string shellCmdToString(sp<IBinder> unitTestService, const std::vector<const char*>& args) { + int inFd[2] = {-1, -1}; + int outFd[2] = {-1, -1}; + int errFd[2] = {-1, -1}; + + EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, inFd)); + EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, outFd)); + EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, errFd)); + + sp<MyShellCallback> cb = new MyShellCallback(); + sp<MyResultReceiver> resultReceiver = new MyResultReceiver(); + + Vector<String16> argsVec; + for (int i = 0; i < args.size(); i++) { + argsVec.add(String16(args[i])); + } + status_t error = IBinder::shellCommand(unitTestService, inFd[0], outFd[0], errFd[0], argsVec, + cb, resultReceiver); + EXPECT_EQ(error, android::OK); + + status_t res = resultReceiver->waitForResult(); + EXPECT_EQ(res, android::OK); + + close(inFd[0]); + close(inFd[1]); + close(outFd[0]); + close(errFd[0]); + close(errFd[1]); + + std::string ret; + EXPECT_TRUE(ReadFdToString(outFd[1], &ret)); + close(outFd[1]); + return ret; +} + +TEST(NdkBinder, UseHandleShellCommand) { + static const sp<android::IServiceManager> sm(android::defaultServiceManager()); + sp<IBinder> testService = sm->getService(String16(kBinderNdkUnitTestService)); + + EXPECT_EQ("", shellCmdToString(testService, {})); + EXPECT_EQ("", shellCmdToString(testService, {"", ""})); + EXPECT_EQ("Hello world!", shellCmdToString(testService, {"Hello ", "world!"})); + EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"})); +} + int main(int argc, char* argv[]) { ::testing::InitGoogleTest(&argc, argv); + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return manualService(IFoo::kInstanceNameToDieFor); + } + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return manualService(IFoo::kSomeInstanceName); + } + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return generatedService(); + } + ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks ABinderProcess_startThreadPool(); diff --git a/libs/binder/ndk/test/main_server.cpp b/libs/binder/ndk/test/main_server.cpp deleted file mode 100644 index a6e17e8d98..0000000000 --- a/libs/binder/ndk/test/main_server.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <android-base/logging.h> -#include <android/binder_process.h> -#include <iface/iface.h> - -using ::android::sp; - -class MyFoo : public IFoo { - binder_status_t doubleNumber(int32_t in, int32_t* out) override { - *out = 2 * in; - LOG(INFO) << "doubleNumber (" << in << ") => " << *out; - return STATUS_OK; - } - - binder_status_t die() override { - LOG(FATAL) << "IFoo::die called!"; - return STATUS_UNKNOWN_ERROR; - } -}; - -int service(const char* instance) { - ABinderProcess_setThreadPoolMaxThreadCount(0); - - // Strong reference to MyFoo kept by service manager. - binder_status_t status = (new MyFoo)->addService(instance); - - if (status != STATUS_OK) { - LOG(FATAL) << "Could not register: " << status << " " << instance; - } - - ABinderProcess_joinThreadPool(); - - return 1; // should not return -} - -int main() { - if (fork() == 0) { - return service(IFoo::kInstanceNameToDieFor); - } - - return service(IFoo::kSomeInstanceName); -} diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index c451780dd7..3ee818734a 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -19,45 +19,34 @@ cc_defaults { cflags: [ "-Wall", "-Werror", - "-Wno-unused-private-field", - "-Wno-unused-variable", ], } cc_test { name: "binderDriverInterfaceTest_IPC_32", - srcs: ["binderDriverInterfaceTest.cpp"], defaults: ["binder_test_defaults"], + srcs: ["binderDriverInterfaceTest.cpp"], compile_multilib: "32", cflags: ["-DBINDER_IPC_32BIT=1"], } cc_test { + name: "binderDriverInterfaceTest", + defaults: ["binder_test_defaults"], product_variables: { binder32bit: { cflags: ["-DBINDER_IPC_32BIT=1"], }, }, - name: "binderDriverInterfaceTest", srcs: ["binderDriverInterfaceTest.cpp"], - defaults: ["binder_test_defaults"], -} - -cc_test { - name: "binderValueTypeTest", - srcs: ["binderValueTypeTest.cpp"], - defaults: ["binder_test_defaults"], - shared_libs: [ - "libbinder", - "libutils", - ], + test_suites: ["device-tests", "vts-core"], } cc_test { name: "binderLibTest_IPC_32", - srcs: ["binderLibTest.cpp"], defaults: ["binder_test_defaults"], + srcs: ["binderLibTest.cpp"], shared_libs: [ "libbinder", "libutils", @@ -67,25 +56,27 @@ cc_test { } cc_test { + name: "binderLibTest", + defaults: ["binder_test_defaults"], product_variables: { binder32bit: { cflags: ["-DBINDER_IPC_32BIT=1"], }, }, - defaults: ["binder_test_defaults"], - name: "binderLibTest", srcs: ["binderLibTest.cpp"], shared_libs: [ "libbinder", "libutils", ], + test_suites: ["device-tests", "vts-core"], + require_root: true, } cc_test { name: "binderThroughputTest", - srcs: ["binderThroughputTest.cpp"], defaults: ["binder_test_defaults"], + srcs: ["binderThroughputTest.cpp"], shared_libs: [ "libbinder", "libutils", @@ -101,19 +92,20 @@ cc_test { cc_test { name: "binderTextOutputTest", - srcs: ["binderTextOutputTest.cpp"], defaults: ["binder_test_defaults"], + srcs: ["binderTextOutputTest.cpp"], shared_libs: [ "libbinder", "libutils", "libbase", ], + test_suites: ["device-tests"], } cc_test { name: "schd-dbg", - srcs: ["schd-dbg.cpp"], defaults: ["binder_test_defaults"], + srcs: ["schd-dbg.cpp"], shared_libs: [ "libbinder", "libutils", @@ -123,16 +115,11 @@ cc_test { cc_test { name: "binderSafeInterfaceTest", - srcs: ["binderSafeInterfaceTest.cpp"], defaults: ["binder_test_defaults"], + srcs: ["binderSafeInterfaceTest.cpp"], cppflags: [ - "-Weverything", - "-Wno-c++98-compat", - "-Wno-c++98-compat-pedantic", - "-Wno-global-constructors", - "-Wno-padded", - "-Wno-weak-vtables", + "-Wextra", ], cpp_std: "experimental", @@ -144,4 +131,35 @@ cc_test { "liblog", "libutils", ], + test_suites: ["device-tests", "vts-core"], + require_root: true, +} + +aidl_interface { + name: "binderStabilityTestIface", + srcs: [ + "IBinderStabilityTest.aidl", + ], +} + +cc_test { + name: "binderStabilityTest", + defaults: ["binder_test_defaults"], + srcs: [ + "binderStabilityTest.cpp", + ], + + shared_libs: [ + "libbinder_ndk", + "libbinder", + "liblog", + "libutils", + ], + static_libs: [ + "binderStabilityTestIface-cpp", + "binderStabilityTestIface-ndk_platform", + ], + + test_suites: ["device-tests"], + require_root: true, } diff --git a/libs/binder/tests/IBinderStabilityTest.aidl b/libs/binder/tests/IBinderStabilityTest.aidl new file mode 100644 index 0000000000..36e1c2cbc4 --- /dev/null +++ b/libs/binder/tests/IBinderStabilityTest.aidl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! +// THIS IS ONLY FOR TESTING! +interface IBinderStabilityTest { + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + void sendBinder(IBinder binder); + + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + void sendAndCallBinder(IBinder binder); + + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + IBinder returnNoStabilityBinder(); + + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + IBinder returnLocalStabilityBinder(); + + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + IBinder returnVintfStabilityBinder(); + + // DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! + // THIS IS ONLY FOR TESTING! + IBinder returnVendorStabilityBinder(); +} +// DO NOT EVER IN A MILLION YEARS WRITE AN INTERFACE LIKE THIS! +// THIS IS ONLY FOR TESTING! +// Construct and return a binder with a specific stability diff --git a/libs/binder/tests/binderDriverInterfaceTest.cpp b/libs/binder/tests/binderDriverInterfaceTest.cpp index 77ebac8f5a..f3ed6a613c 100644 --- a/libs/binder/tests/binderDriverInterfaceTest.cpp +++ b/libs/binder/tests/binderDriverInterfaceTest.cpp @@ -230,7 +230,6 @@ TEST_F(BinderDriverInterfaceTest, IncRefsAcquireReleaseDecRefs) { } TEST_F(BinderDriverInterfaceTest, Transaction) { - binder_uintptr_t cookie = 1234; struct { uint32_t cmd1; struct binder_transaction_data arg1; diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 78f11594b9..5e0574ad8a 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -28,6 +28,7 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <private/binder/binder_module.h> #include <sys/epoll.h> #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) @@ -66,7 +67,6 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_LINK_DEATH_TRANSACTION, BINDER_LIB_TEST_WRITE_FILE_TRANSACTION, BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION, - BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION, BINDER_LIB_TEST_EXIT_TRANSACTION, BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION, BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION, @@ -553,50 +553,6 @@ TEST_F(BinderLibTest, AddServer) ASSERT_TRUE(server != nullptr); } -TEST_F(BinderLibTest, DeathNotificationNoRefs) -{ - status_t ret; - - sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient(); - - { - sp<IBinder> binder = addServer(); - ASSERT_TRUE(binder != nullptr); - ret = binder->linkToDeath(testDeathRecipient); - EXPECT_EQ(NO_ERROR, ret); - } - IPCThreadState::self()->flushCommands(); - ret = testDeathRecipient->waitEvent(5); - EXPECT_EQ(NO_ERROR, ret); -#if 0 /* Is there an unlink api that does not require a strong reference? */ - ret = binder->unlinkToDeath(testDeathRecipient); - EXPECT_EQ(NO_ERROR, ret); -#endif -} - -TEST_F(BinderLibTest, DeathNotificationWeakRef) -{ - status_t ret; - wp<IBinder> wbinder; - - sp<TestDeathRecipient> testDeathRecipient = new TestDeathRecipient(); - - { - sp<IBinder> binder = addServer(); - ASSERT_TRUE(binder != nullptr); - ret = binder->linkToDeath(testDeathRecipient); - EXPECT_EQ(NO_ERROR, ret); - wbinder = binder; - } - IPCThreadState::self()->flushCommands(); - ret = testDeathRecipient->waitEvent(5); - EXPECT_EQ(NO_ERROR, ret); -#if 0 /* Is there an unlink api that does not require a strong reference? */ - ret = binder->unlinkToDeath(testDeathRecipient); - EXPECT_EQ(NO_ERROR, ret); -#endif -} - TEST_F(BinderLibTest, DeathNotificationStrongRef) { status_t ret; @@ -814,20 +770,22 @@ TEST_F(BinderLibTest, PromoteLocal) { EXPECT_TRUE(strong_from_weak == nullptr); } -TEST_F(BinderLibTest, PromoteRemote) { - int ret; - Parcel data, reply; - sp<IBinder> strong = new BBinder(); - sp<IBinder> server = addServer(); +TEST_F(BinderLibTest, LocalGetExtension) { + sp<BBinder> binder = new BBinder(); + sp<IBinder> ext = new BBinder(); + binder->setExtension(ext); + EXPECT_EQ(ext, binder->getExtension()); +} +TEST_F(BinderLibTest, RemoteGetExtension) { + sp<IBinder> server = addServer(); ASSERT_TRUE(server != nullptr); - ASSERT_TRUE(strong != nullptr); - ret = data.writeWeakBinder(strong); - EXPECT_EQ(NO_ERROR, ret); + sp<IBinder> extension; + EXPECT_EQ(NO_ERROR, server->getExtension(&extension)); + ASSERT_NE(nullptr, extension.get()); - ret = server->transact(BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION, data, &reply); - EXPECT_GE(ret, 0); + EXPECT_EQ(NO_ERROR, extension->pingBinder()); } TEST_F(BinderLibTest, CheckHandleZeroBinderHighBitsZeroCookie) { @@ -855,7 +813,6 @@ TEST_F(BinderLibTest, FreedBinder) { wp<IBinder> keepFreedBinder; { Parcel data, reply; - data.writeBool(false); /* request weak reference */ ret = server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply); ASSERT_EQ(NO_ERROR, ret); struct flat_binder_object *freed = (struct flat_binder_object *)(reply.data()); @@ -864,8 +821,9 @@ TEST_F(BinderLibTest, FreedBinder) { * delete its reference to it - otherwise the transaction * fails regardless of whether the driver is fixed. */ - keepFreedBinder = reply.readWeakBinder(); + keepFreedBinder = reply.readStrongBinder(); } + IPCThreadState::self()->flushCommands(); { Parcel data, reply; data.writeStrongBinder(server); @@ -1015,9 +973,6 @@ TEST_F(BinderLibTest, WorkSourceRestored) TEST_F(BinderLibTest, PropagateFlagSet) { - status_t ret; - Parcel data, reply; - IPCThreadState::self()->clearPropagateWorkSource(); IPCThreadState::self()->setCallingWorkSourceUid(100); EXPECT_EQ(true, IPCThreadState::self()->shouldPropagateWorkSource()); @@ -1025,9 +980,6 @@ TEST_F(BinderLibTest, PropagateFlagSet) TEST_F(BinderLibTest, PropagateFlagCleared) { - status_t ret; - Parcel data, reply; - IPCThreadState::self()->setCallingWorkSourceUid(100); IPCThreadState::self()->clearPropagateWorkSource(); EXPECT_EQ(false, IPCThreadState::self()->shouldPropagateWorkSource()); @@ -1035,9 +987,6 @@ TEST_F(BinderLibTest, PropagateFlagCleared) TEST_F(BinderLibTest, PropagateFlagRestored) { - status_t ret; - Parcel data, reply; - int token = IPCThreadState::self()->setCallingWorkSourceUid(100); IPCThreadState::self()->restoreCallingWorkSource(token); @@ -1136,7 +1085,6 @@ class BinderLibTestService : public BBinder case BINDER_LIB_TEST_ADD_POLL_SERVER: case BINDER_LIB_TEST_ADD_SERVER: { int ret; - uint8_t buf[1] = { 0 }; int serverid; if (m_id != 0) { @@ -1334,29 +1282,6 @@ class BinderLibTestService : public BBinder if (ret != size) return UNKNOWN_ERROR; return NO_ERROR; } - case BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION: { - int ret; - wp<IBinder> weak; - sp<IBinder> strong; - Parcel data2, reply2; - sp<IServiceManager> sm = defaultServiceManager(); - sp<IBinder> server = sm->getService(binderLibTestServiceName); - - weak = data.readWeakBinder(); - if (weak == nullptr) { - return BAD_VALUE; - } - strong = weak.promote(); - - ret = server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data2, &reply2); - if (ret != NO_ERROR) - exit(EXIT_FAILURE); - - if (strong == nullptr) { - reply->setError(1); - } - return NO_ERROR; - } case BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION: alarm(10); return NO_ERROR; @@ -1365,13 +1290,8 @@ class BinderLibTestService : public BBinder ; exit(EXIT_SUCCESS); case BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION: { - bool strongRef = data.readBool(); sp<IBinder> binder = new BBinder(); - if (strongRef) { - reply->writeStrongBinder(binder); - } else { - reply->writeWeakBinder(binder); - } + reply->writeStrongBinder(binder); return NO_ERROR; } case BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION: { @@ -1399,7 +1319,6 @@ class BinderLibTestService : public BBinder bool m_serverStartRequested; sp<IBinder> m_serverStarted; sp<IBinder> m_strongRef; - bool m_callbackPending; sp<IBinder> m_callback; }; @@ -1412,6 +1331,13 @@ int run_server(int index, int readypipefd, bool usePoll) BinderLibTestService* testServicePtr; { sp<BinderLibTestService> testService = new BinderLibTestService(index); + + /* + * Normally would also contain functionality as well, but we are only + * testing the extension mechanism. + */ + testService->setExtension(new BBinder()); + /* * We need this below, but can't hold a sp<> because it prevents the * node from being cleaned up automatically. It's safe in this case @@ -1461,7 +1387,7 @@ int run_server(int index, int readypipefd, bool usePoll) * We simulate a single-threaded process using the binder poll * interface; besides handling binder commands, it can also * issue outgoing transactions, by storing a callback in - * m_callback and setting m_callbackPending. + * m_callback. * * processPendingCall() will then issue that transaction. */ @@ -1488,8 +1414,6 @@ int run_server(int index, int readypipefd, bool usePoll) } int main(int argc, char **argv) { - int ret; - if (argc == 4 && !strcmp(argv[1], "--servername")) { binderservername = argv[2]; } else { diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp index 3b1db27749..09f58cc833 100644 --- a/libs/binder/tests/binderSafeInterfaceTest.cpp +++ b/libs/binder/tests/binderSafeInterfaceTest.cpp @@ -75,7 +75,7 @@ public: private: int32_t mValue = 0; - uint8_t mPadding[4] = {}; // Avoids a warning from -Wpadded + __attribute__((unused)) uint8_t mPadding[4] = {}; // Avoids a warning from -Wpadded }; struct TestFlattenable : Flattenable<TestFlattenable> { @@ -743,6 +743,7 @@ TEST_F(SafeInterfaceTest, TestIncremementParcelableVector) { const std::vector<TestParcelable> a{TestParcelable{1}, TestParcelable{2}}; std::vector<TestParcelable> aPlusOne; status_t result = mSafeInterfaceTest->increment(a, &aPlusOne); + ASSERT_EQ(NO_ERROR, result); ASSERT_EQ(a.size(), aPlusOne.size()); for (size_t i = 0; i < a.size(); ++i) { ASSERT_EQ(a[i].getValue() + 1, aPlusOne[i].getValue()); diff --git a/libs/binder/tests/binderStabilityTest.cpp b/libs/binder/tests/binderStabilityTest.cpp new file mode 100644 index 0000000000..1f2779abf0 --- /dev/null +++ b/libs/binder/tests/binderStabilityTest.cpp @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/binder_manager.h> +#include <android/binder_stability.h> +#include <binder/Binder.h> +#include <binder/IBinder.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/Stability.h> +#include <gtest/gtest.h> + +#include <sys/prctl.h> + +#include "aidl/BnBinderStabilityTest.h" +#include "BnBinderStabilityTest.h" + +using namespace android; +using namespace ndk; +using android::binder::Status; +using android::internal::Stability; // for testing only! + +const String16 kSystemStabilityServer = String16("binder_stability_test_service_system"); + +// This is handwritten so that we can test different stability levels w/o having the AIDL +// compiler assign them. Hand-writing binder interfaces is considered a bad practice +// sanity reasons. YOU SHOULD DEFINE AN AIDL INTERFACE INSTEAD! +class BadStableBinder : public BBinder { +public: + static constexpr uint32_t USER_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION; + static String16 kDescriptor; + + bool gotUserTransaction = false; + + static status_t doUserTransaction(const sp<IBinder>& binder) { + Parcel data, reply; + data.writeInterfaceToken(kDescriptor); + return binder->transact(USER_TRANSACTION, data, &reply, 0/*flags*/); + } + + status_t onTransact(uint32_t code, + const Parcel& data, Parcel* reply, uint32_t flags) override { + if (code == USER_TRANSACTION) { + // not interested in this kind of stability. Make sure + // we have a test failure + LOG_ALWAYS_FATAL_IF(!data.enforceInterface(kDescriptor)); + + gotUserTransaction = true; + + ALOGE("binder stability: Got user transaction"); + return OK; + } + return BBinder::onTransact(code, data, reply, flags); + } + + static sp<BadStableBinder> undef() { + sp<BadStableBinder> iface = new BadStableBinder(); + return iface; + } + + static sp<BadStableBinder> system() { + sp<BadStableBinder> iface = new BadStableBinder(); + Stability::markCompilationUnit(iface.get()); // <- for test only + return iface; + } + + static sp<BadStableBinder> vintf() { + sp<BadStableBinder> iface = new BadStableBinder(); + Stability::markVintf(iface.get()); // <- for test only + return iface; + } + + static sp<BadStableBinder> vendor() { + sp<BadStableBinder> iface = new BadStableBinder(); + Stability::markVndk(iface.get()); // <- for test only + return iface; + } +}; +String16 BadStableBinder::kDescriptor = String16("BadStableBinder.test"); + +// NO! NO! NO! Do not even think of doing something like this! +// This is for testing! If a class like this was actually used in production, +// it would ruin everything! +class MyBinderStabilityTest : public BnBinderStabilityTest { +public: + Status sendBinder(const sp<IBinder>& /*binder*/) override { + return Status::ok(); + } + Status sendAndCallBinder(const sp<IBinder>& binder) override { + Stability::debugLogStability("sendAndCallBinder got binder", binder); + return Status::fromExceptionCode(BadStableBinder::doUserTransaction(binder)); + } + Status returnNoStabilityBinder(sp<IBinder>* _aidl_return) override { + *_aidl_return = BadStableBinder::undef(); + return Status::ok(); + } + Status returnLocalStabilityBinder(sp<IBinder>* _aidl_return) override { + *_aidl_return = BadStableBinder::system(); + return Status::ok(); + } + Status returnVintfStabilityBinder(sp<IBinder>* _aidl_return) override { + *_aidl_return = BadStableBinder::vintf(); + return Status::ok(); + } + Status returnVendorStabilityBinder(sp<IBinder>* _aidl_return) override { + *_aidl_return = BadStableBinder::vendor(); + return Status::ok(); + } +}; + +TEST(BinderStability, OnlyVintfStabilityBinderNeedsVintfDeclaration) { + EXPECT_FALSE(Stability::requiresVintfDeclaration(nullptr)); + EXPECT_FALSE(Stability::requiresVintfDeclaration(BadStableBinder::undef())); + EXPECT_FALSE(Stability::requiresVintfDeclaration(BadStableBinder::system())); + EXPECT_FALSE(Stability::requiresVintfDeclaration(BadStableBinder::vendor())); + + EXPECT_TRUE(Stability::requiresVintfDeclaration(BadStableBinder::vintf())); +} + +TEST(BinderStability, VintfStabilityServerMustBeDeclaredInManifest) { + sp<IBinder> vintfServer = BadStableBinder::vintf(); + + for (const char* instance8 : { + ".", "/", "/.", "a.d.IFoo", "foo", "a.d.IFoo/foo" + }) { + String16 instance (instance8); + + EXPECT_EQ(Status::EX_ILLEGAL_ARGUMENT, + android::defaultServiceManager()->addService(String16("."), vintfServer)) << instance8; + EXPECT_FALSE(android::defaultServiceManager()->isDeclared(instance)) << instance8; + } +} + +TEST(BinderStability, CantCallVendorBinderInSystemContext) { + sp<IBinder> serverBinder = android::defaultServiceManager()->getService(kSystemStabilityServer); + auto server = interface_cast<IBinderStabilityTest>(serverBinder); + + ASSERT_NE(nullptr, server.get()); + ASSERT_NE(nullptr, IInterface::asBinder(server)->remoteBinder()); + + EXPECT_TRUE(server->sendBinder(BadStableBinder::undef()).isOk()); + EXPECT_TRUE(server->sendBinder(BadStableBinder::system()).isOk()); + EXPECT_TRUE(server->sendBinder(BadStableBinder::vintf()).isOk()); + EXPECT_TRUE(server->sendBinder(BadStableBinder::vendor()).isOk()); + + { + sp<BadStableBinder> binder = BadStableBinder::undef(); + EXPECT_TRUE(server->sendAndCallBinder(binder).isOk()); + EXPECT_TRUE(binder->gotUserTransaction); + } + { + sp<BadStableBinder> binder = BadStableBinder::system(); + EXPECT_TRUE(server->sendAndCallBinder(binder).isOk()); + EXPECT_TRUE(binder->gotUserTransaction); + } + { + sp<BadStableBinder> binder = BadStableBinder::vintf(); + EXPECT_TRUE(server->sendAndCallBinder(binder).isOk()); + EXPECT_TRUE(binder->gotUserTransaction); + } + { + // !!! user-defined transaction may not be stable for remote server !!! + // !!! so, it does not work !!! + sp<BadStableBinder> binder = BadStableBinder::vendor(); + EXPECT_EQ(BAD_TYPE, server->sendAndCallBinder(binder).exceptionCode()); + EXPECT_FALSE(binder->gotUserTransaction); + } + + sp<IBinder> out; + EXPECT_TRUE(server->returnNoStabilityBinder(&out).isOk()); + ASSERT_NE(nullptr, out.get()); + EXPECT_EQ(OK, out->pingBinder()); + EXPECT_EQ(OK, BadStableBinder::doUserTransaction(out)); + + EXPECT_TRUE(server->returnLocalStabilityBinder(&out).isOk()); + ASSERT_NE(nullptr, out.get()); + EXPECT_EQ(OK, out->pingBinder()); + EXPECT_EQ(OK, BadStableBinder::doUserTransaction(out)); + + EXPECT_TRUE(server->returnVintfStabilityBinder(&out).isOk()); + ASSERT_NE(nullptr, out.get()); + EXPECT_EQ(OK, out->pingBinder()); + EXPECT_EQ(OK, BadStableBinder::doUserTransaction(out)); + + EXPECT_TRUE(server->returnVendorStabilityBinder(&out).isOk()); + ASSERT_NE(nullptr, out.get()); + + // !!! libbinder-defined transaction works !!! + EXPECT_EQ(OK, out->pingBinder()); + + // !!! user-defined transaction may not be stable !!! + // !!! so, it does not work !!! + EXPECT_EQ(BAD_TYPE, BadStableBinder::doUserTransaction(out)); +} + +// This is handwritten so that we can test different stability levels w/o having the AIDL +// compiler assign them. Hand-writing binder interfaces is considered a bad practice +// sanity reasons. YOU SHOULD DEFINE AN AIDL INTERFACE INSTEAD! + +struct NdkBinderStable_DataClass { + bool gotUserTransaction = false; +}; +void* NdkBadStableBinder_Class_onCreate(void* args) { + LOG_ALWAYS_FATAL_IF(args != nullptr, "Takes no args"); + return static_cast<void*>(new NdkBinderStable_DataClass); +} +void NdkBadStableBinder_Class_onDestroy(void* userData) { + delete static_cast<NdkBinderStable_DataClass*>(userData); +} +NdkBinderStable_DataClass* NdkBadStableBinder_getUserData(AIBinder* binder) { + LOG_ALWAYS_FATAL_IF(binder == nullptr); + void* userData = AIBinder_getUserData(binder); + LOG_ALWAYS_FATAL_IF(userData == nullptr, "null data - binder is remote?"); + + return static_cast<NdkBinderStable_DataClass*>(userData); +} +binder_status_t NdkBadStableBinder_Class_onTransact( + AIBinder* binder, transaction_code_t code, const AParcel* /*in*/, AParcel* /*out*/) { + + if (code == BadStableBinder::USER_TRANSACTION) { + ALOGE("ndk binder stability: Got user transaction"); + NdkBadStableBinder_getUserData(binder)->gotUserTransaction = true; + return STATUS_OK; + } + + return STATUS_UNKNOWN_TRANSACTION; +} + +static AIBinder_Class* kNdkBadStableBinder = + AIBinder_Class_define(String8(BadStableBinder::kDescriptor).c_str(), + NdkBadStableBinder_Class_onCreate, + NdkBadStableBinder_Class_onDestroy, + NdkBadStableBinder_Class_onTransact); + +// for testing only to get around __ANDROID_VNDK__ guard. +extern "C" void AIBinder_markVendorStability(AIBinder* binder); // <- BAD DO NOT COPY + +TEST(BinderStability, NdkCantCallVendorBinderInSystemContext) { + SpAIBinder binder = SpAIBinder(AServiceManager_getService( + String8(kSystemStabilityServer).c_str())); + + std::shared_ptr<aidl::IBinderStabilityTest> remoteServer = + aidl::IBinderStabilityTest::fromBinder(binder); + + ASSERT_NE(nullptr, remoteServer.get()); + + SpAIBinder comp = SpAIBinder(AIBinder_new(kNdkBadStableBinder, nullptr /*args*/)); + EXPECT_TRUE(remoteServer->sendBinder(comp).isOk()); + EXPECT_TRUE(remoteServer->sendAndCallBinder(comp).isOk()); + EXPECT_TRUE(NdkBadStableBinder_getUserData(comp.get())->gotUserTransaction); + + SpAIBinder vendor = SpAIBinder(AIBinder_new(kNdkBadStableBinder, nullptr /*args*/)); + AIBinder_markVendorStability(vendor.get()); + EXPECT_TRUE(remoteServer->sendBinder(vendor).isOk()); + EXPECT_FALSE(remoteServer->sendAndCallBinder(vendor).isOk()); + EXPECT_FALSE(NdkBadStableBinder_getUserData(vendor.get())->gotUserTransaction); +} + +class MarksStabilityInConstructor : public BBinder { +public: + static bool gDestructed; + + MarksStabilityInConstructor() { + Stability::markCompilationUnit(this); + } + ~MarksStabilityInConstructor() { + gDestructed = true; + } +}; +bool MarksStabilityInConstructor::gDestructed = false; + +TEST(BinderStability, MarkingObjectNoDestructTest) { + ASSERT_FALSE(MarksStabilityInConstructor::gDestructed); + + // best practice is to put this directly in an sp, but for this test, we + // want to explicitly check what happens before that happens + MarksStabilityInConstructor* binder = new MarksStabilityInConstructor(); + ASSERT_FALSE(MarksStabilityInConstructor::gDestructed); + + sp<MarksStabilityInConstructor> binderSp = binder; + ASSERT_FALSE(MarksStabilityInConstructor::gDestructed); + + binderSp = nullptr; + ASSERT_TRUE(MarksStabilityInConstructor::gDestructed); +} + +TEST(BinderStability, RemarkDies) { + ASSERT_DEATH({ + sp<IBinder> binder = new BBinder(); + Stability::markCompilationUnit(binder.get()); // <-- only called for tests + Stability::markVndk(binder.get()); // <-- only called for tests + }, "Should only mark known object."); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + if (fork() == 0) { + // child process + prctl(PR_SET_PDEATHSIG, SIGHUP); + + sp<IBinder> server = new MyBinderStabilityTest; + android::defaultServiceManager()->addService(kSystemStabilityServer, server); + + IPCThreadState::self()->joinThreadPool(true); + exit(1); // should not reach + } + + // This is not racey. Just giving these services some time to register before we call + // getService which sleeps for much longer... + usleep(10000); + + return RUN_ALL_TESTS(); +} diff --git a/libs/binder/tests/binderValueTypeTest.cpp b/libs/binder/tests/binderValueTypeTest.cpp deleted file mode 100644 index f8922b0784..0000000000 --- a/libs/binder/tests/binderValueTypeTest.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <limits> -#include <cstddef> -#include <vector> - -#include "android-base/file.h" -#include <gtest/gtest.h> - -#include <binder/Parcel.h> -#include <binder/Value.h> -#include <binder/Debug.h> - -using ::android::binder::Value; -using ::android::os::PersistableBundle; -using ::android::String16; -using ::std::vector; - -#define VALUE_TYPE_TEST(T, TYPENAME, VAL) \ - TEST(ValueType, Handles ## TYPENAME) { \ - T x = VAL; \ - T y = T(); \ - Value value = VAL; \ - ASSERT_FALSE(value.empty()); \ - ASSERT_TRUE(value.is ## TYPENAME ()); \ - ASSERT_TRUE(value.get ## TYPENAME (&y)); \ - ASSERT_EQ(x, y); \ - ASSERT_EQ(value, Value(y)); \ - value.put ## TYPENAME (x); \ - ASSERT_EQ(value, Value(y)); \ - value = Value(); \ - ASSERT_TRUE(value.empty()); \ - ASSERT_NE(value, Value(y)); \ - value = y; \ - ASSERT_EQ(value, Value(x)); \ - } - -#define VALUE_TYPE_VECTOR_TEST(T, TYPENAME, VAL) \ - TEST(ValueType, Handles ## TYPENAME ## Vector) { \ - vector<T> x; \ - vector<T> y; \ - x.push_back(VAL); \ - x.push_back(T()); \ - Value value(x); \ - ASSERT_FALSE(value.empty()); \ - ASSERT_TRUE(value.is ## TYPENAME ## Vector()); \ - ASSERT_TRUE(value.get ## TYPENAME ## Vector(&y)); \ - ASSERT_EQ(x, y); \ - ASSERT_EQ(value, Value(y)); \ - value.put ## TYPENAME ## Vector(x); \ - ASSERT_EQ(value, Value(y)); \ - value = Value(); \ - ASSERT_TRUE(value.empty()); \ - ASSERT_NE(value, Value(y)); \ - value = y; \ - ASSERT_EQ(value, Value(x)); \ - } - -VALUE_TYPE_TEST(bool, Boolean, true) -VALUE_TYPE_TEST(int32_t, Int, 31337) -VALUE_TYPE_TEST(int64_t, Long, 13370133701337L) -VALUE_TYPE_TEST(double, Double, 3.14159265358979323846) -VALUE_TYPE_TEST(String16, String, String16("Lovely")) - -VALUE_TYPE_VECTOR_TEST(bool, Boolean, true) -VALUE_TYPE_VECTOR_TEST(int32_t, Int, 31337) -VALUE_TYPE_VECTOR_TEST(int64_t, Long, 13370133701337L) -VALUE_TYPE_VECTOR_TEST(double, Double, 3.14159265358979323846) -VALUE_TYPE_VECTOR_TEST(String16, String, String16("Lovely")) - -VALUE_TYPE_TEST(PersistableBundle, PersistableBundle, PersistableBundle()) - -TEST(ValueType, HandlesClear) { - Value value; - ASSERT_TRUE(value.empty()); - value.putInt(31337); - ASSERT_FALSE(value.empty()); - value.clear(); - ASSERT_TRUE(value.empty()); -} - -TEST(ValueType, HandlesSwap) { - Value value_a, value_b; - int32_t int_x; - value_a.putInt(31337); - ASSERT_FALSE(value_a.empty()); - ASSERT_TRUE(value_b.empty()); - value_a.swap(value_b); - ASSERT_FALSE(value_b.empty()); - ASSERT_TRUE(value_a.empty()); - ASSERT_TRUE(value_b.getInt(&int_x)); - ASSERT_EQ(31337, int_x); -} diff --git a/libs/binder/tests/schd-dbg.cpp b/libs/binder/tests/schd-dbg.cpp index ec9534abac..ab4c56a6af 100644 --- a/libs/binder/tests/schd-dbg.cpp +++ b/libs/binder/tests/schd-dbg.cpp @@ -290,6 +290,7 @@ static void* thread_start(void* p) { sta = tickNow(); status_t ret = workers[target]->transact(BINDER_NOP, data, &reply); + ASSERT(ret == NO_ERROR); end = tickNow(); results_fifo->add_time(tickNano(sta, end)); diff --git a/libs/binderthreadstate/1.0/Android.bp b/libs/binderthreadstate/1.0/Android.bp new file mode 100644 index 0000000000..ebdc932591 --- /dev/null +++ b/libs/binderthreadstate/1.0/Android.bp @@ -0,0 +1,14 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "binderthreadstateutilstest@1.0", + root: "binderthreadstateutilstest", + system_ext_specific: true, + srcs: [ + "IHidlStuff.hal", + ], + interfaces: [ + "android.hidl.base@1.0", + ], + gen_java: true, +} diff --git a/libs/binderthreadstate/1.0/IHidlStuff.hal b/libs/binderthreadstate/1.0/IHidlStuff.hal new file mode 100644 index 0000000000..ffb64998ce --- /dev/null +++ b/libs/binderthreadstate/1.0/IHidlStuff.hal @@ -0,0 +1,22 @@ +/* + * 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. + */ + +package binderthreadstateutilstest@1.0; + +interface IHidlStuff { + callLocal(); + call(int32_t idx); +}; diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp index 512b0698d3..c1861104d3 100644 --- a/libs/binderthreadstate/Android.bp +++ b/libs/binderthreadstate/Android.bp @@ -12,35 +12,54 @@ // See the License for the specific language governing permissions and // limitations under the License. -cc_library { - name: "libbinderthreadstate", - recovery_available: true, - vendor_available: false, - vndk: { - enabled: true, - support_system_process: true, - }, - srcs: [ - "IPCThreadStateBase.cpp", - ], - - header_libs: [ - "libbase_headers", - "libutils_headers", - ], +// DO NOT ADD NEW USAGES OF THIS +// See comments in header file. +cc_library_static { + name: "libbinderthreadstateutils", + double_loadable: true, + vendor_available: true, + host_supported: true, shared_libs: [ - "liblog", + "libbinder", + "libhidlbase", // libhwbinder is in here ], export_include_dirs: ["include"], - sanitize: { - misc_undefined: ["integer"], - }, - cflags: [ "-Wall", "-Werror", ], } + +hidl_package_root { + name: "binderthreadstateutilstest", +} + +aidl_interface { + name: "binderthreadstateutilstest.aidl", + srcs: ["IAidlStuff.aidl"], +} + +cc_test { + name: "libbinderthreadstateutils_test", + srcs: ["test.cpp"], + static_libs: [ + "binderthreadstateutilstest@1.0", + "binderthreadstateutilstest.aidl-cpp", + "libbinderthreadstateutils", + ], + shared_libs: [ + "libbase", + "libbinder", + "libcutils", + "libhidlbase", + "libutils", + "liblog", + ], + test_suites: [ + "general-tests", + ], + require_root: true, +} diff --git a/libs/binderthreadstate/IAidlStuff.aidl b/libs/binderthreadstate/IAidlStuff.aidl new file mode 100644 index 0000000000..0c81c42a79 --- /dev/null +++ b/libs/binderthreadstate/IAidlStuff.aidl @@ -0,0 +1,21 @@ +/* + * 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. + */ + + +interface IAidlStuff { + void callLocal(); + void call(int idx); +} diff --git a/libs/binderthreadstate/IPCThreadStateBase.cpp b/libs/binderthreadstate/IPCThreadStateBase.cpp deleted file mode 100644 index fede151774..0000000000 --- a/libs/binderthreadstate/IPCThreadStateBase.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "IPCThreadStateBase" - -#include <binderthreadstate/IPCThreadStateBase.h> -#include <android-base/macros.h> - -#include <utils/Log.h> - -#include <errno.h> -#include <inttypes.h> -#include <pthread.h> - -namespace android { - -static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; -static bool gHaveTLS = false; -static pthread_key_t gTLS = 0; - -IPCThreadStateBase::IPCThreadStateBase() { - pthread_setspecific(gTLS, this); -} - -IPCThreadStateBase* IPCThreadStateBase::self() -{ - if (gHaveTLS) { -restart: - const pthread_key_t k = gTLS; - IPCThreadStateBase* st = (IPCThreadStateBase*)pthread_getspecific(k); - if (st) return st; - return new IPCThreadStateBase; - } - - pthread_mutex_lock(&gTLSMutex); - if (!gHaveTLS) { - int key_create_value = pthread_key_create(&gTLS, threadDestructor); - if (key_create_value != 0) { - pthread_mutex_unlock(&gTLSMutex); - ALOGW("IPCThreadStateBase::self() unable to create TLS key, expect a crash: %s\n", - strerror(key_create_value)); - return nullptr; - } - gHaveTLS = true; - } - pthread_mutex_unlock(&gTLSMutex); - goto restart; -} - -void IPCThreadStateBase::pushCurrentState(CallState callState) { - mCallStateStack.emplace(callState); -} - -IPCThreadStateBase::CallState IPCThreadStateBase::popCurrentState() { - ALOG_ASSERT(mCallStateStack.size > 0); - CallState val = mCallStateStack.top(); - mCallStateStack.pop(); - return val; -} - -IPCThreadStateBase::CallState IPCThreadStateBase::getCurrentBinderCallState() { - if (mCallStateStack.size() > 0) { - return mCallStateStack.top(); - } - return CallState::NONE; -} - -void IPCThreadStateBase::threadDestructor(void *st) -{ - IPCThreadStateBase* const self = static_cast<IPCThreadStateBase*>(st); - if (self) { - delete self; - } -} - -}; // namespace android diff --git a/libs/binderthreadstate/TEST_MAPPING b/libs/binderthreadstate/TEST_MAPPING new file mode 100644 index 0000000000..2bd046339f --- /dev/null +++ b/libs/binderthreadstate/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libbinderthreadstateutils_test" + } + ] +} diff --git a/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h new file mode 100644 index 0000000000..a3e5026a07 --- /dev/null +++ b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h @@ -0,0 +1,50 @@ +/* + * 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 + +// WARNING: DO NOT USE THIS +// You should: +// - have code know how it is handling things. Pass in caller information rather +// than assuming that code is running in a specific global context +// - use AIDL exclusively in your stack (HIDL is no longer required anywhere) + +#include <binder/IPCThreadState.h> +#include <hwbinder/IPCThreadState.h> + +namespace android { + +enum class BinderCallType { + NONE, + BINDER, + HWBINDER, +}; + +// Based on where we are in recursion of nested binder/hwbinder calls, determine +// which one we are closer to. +inline static BinderCallType getCurrentServingCall() { + const void* hwbinderSp = android::hardware::IPCThreadState::self()->getServingStackPointer(); + const void* binderSp = android::IPCThreadState::self()->getServingStackPointer(); + + if (hwbinderSp == nullptr && binderSp == nullptr) return BinderCallType::NONE; + if (hwbinderSp == nullptr) return BinderCallType::BINDER; + if (binderSp == nullptr) return BinderCallType::HWBINDER; + + if (hwbinderSp < binderSp) return BinderCallType::HWBINDER; + return BinderCallType::BINDER; +} + +} // namespace android diff --git a/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h b/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h deleted file mode 100644 index 6fdcc84054..0000000000 --- a/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H -#define BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H - -#include <stack> -namespace android { - -class IPCThreadStateBase { -public: - enum CallState { - HWBINDER, - BINDER, - NONE, - }; - static IPCThreadStateBase* self(); - void pushCurrentState(CallState callState); - CallState popCurrentState(); - CallState getCurrentBinderCallState(); - -private: - IPCThreadStateBase(); - static void threadDestructor(void *st); - - std::stack<CallState> mCallStateStack; -}; - -}; // namespace android - -#endif // BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp new file mode 100644 index 0000000000..68cc225057 --- /dev/null +++ b/libs/binderthreadstate/test.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <BnAidlStuff.h> +#include <android-base/logging.h> +#include <binder/IServiceManager.h> +#include <binderthreadstate/CallerUtils.h> +#include <binderthreadstateutilstest/1.0/IHidlStuff.h> +#include <gtest/gtest.h> +#include <hidl/HidlTransportSupport.h> +#include <linux/prctl.h> +#include <sys/prctl.h> + +using android::BinderCallType; +using android::defaultServiceManager; +using android::getCurrentServingCall; +using android::getService; +using android::OK; +using android::sp; +using android::String16; +using android::binder::Status; +using android::hardware::Return; +using binderthreadstateutilstest::V1_0::IHidlStuff; + +constexpr size_t kP1Id = 1; +constexpr size_t kP2Id = 2; + +// AIDL and HIDL are in separate namespaces so using same service names +std::string id2name(size_t id) { + return "libbinderthreadstateutils-" + std::to_string(id); +} + +// There are two servers calling each other recursively like this. +// +// P1 P2 +// | --HIDL--> | +// | <--HIDL-- | +// | --AIDL--> | +// | <--AIDL-- | +// | --HIDL--> | +// | <--HIDL-- | +// | --AIDL--> | +// | <--AIDL-- | +// .......... +// +// Calls always come in pairs (AIDL returns AIDL, HIDL returns HIDL) because +// this means that P1 always has a 'waitForResponse' call which can service the +// returning call and continue the recursion. Of course, with more threads, more +// complicated calls are possible, but this should do here. + +static void callHidl(size_t id, int32_t idx) { + auto stuff = IHidlStuff::getService(id2name(id)); + CHECK(stuff->call(idx).isOk()); +} + +static void callAidl(size_t id, int32_t idx) { + sp<IAidlStuff> stuff; + CHECK(OK == android::getService<IAidlStuff>(String16(id2name(id).c_str()), &stuff)); + CHECK(stuff->call(idx).isOk()); +} + +class HidlServer : public IHidlStuff { +public: + HidlServer(size_t thisId, size_t otherId) : thisId(thisId), otherId(otherId) {} + size_t thisId; + size_t otherId; + + Return<void> callLocal() { + CHECK(BinderCallType::NONE == getCurrentServingCall()); + return android::hardware::Status::ok(); + } + Return<void> call(int32_t idx) { + LOG(INFO) << "HidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx + << " with tid: " << gettid(); + CHECK(BinderCallType::HWBINDER == getCurrentServingCall()); + if (idx > 0) { + if (thisId == kP1Id && idx % 4 < 2) { + callHidl(otherId, idx - 1); + } else { + callAidl(otherId, idx - 1); + } + } + CHECK(BinderCallType::HWBINDER == getCurrentServingCall()); + return android::hardware::Status::ok(); + } +}; +class AidlServer : public BnAidlStuff { +public: + AidlServer(size_t thisId, size_t otherId) : thisId(thisId), otherId(otherId) {} + size_t thisId; + size_t otherId; + + Status callLocal() { + CHECK(BinderCallType::NONE == getCurrentServingCall()); + return Status::ok(); + } + Status call(int32_t idx) { + LOG(INFO) << "AidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx + << " with tid: " << gettid(); + CHECK(BinderCallType::BINDER == getCurrentServingCall()); + if (idx > 0) { + if (thisId == kP2Id && idx % 4 < 2) { + callHidl(otherId, idx - 1); + } else { + callAidl(otherId, idx - 1); + } + } + CHECK(BinderCallType::BINDER == getCurrentServingCall()); + return Status::ok(); + } +}; + +TEST(BinderThreadState, LocalHidlCall) { + sp<IHidlStuff> server = new HidlServer(0, 0); + EXPECT_TRUE(server->callLocal().isOk()); +} + +TEST(BinderThreadState, LocalAidlCall) { + sp<IAidlStuff> server = new AidlServer(0, 0); + EXPECT_TRUE(server->callLocal().isOk()); +} + +TEST(BindThreadState, RemoteHidlCall) { + auto stuff = IHidlStuff::getService(id2name(kP1Id)); + ASSERT_NE(nullptr, stuff); + ASSERT_TRUE(stuff->call(0).isOk()); +} +TEST(BindThreadState, RemoteAidlCall) { + sp<IAidlStuff> stuff; + ASSERT_EQ(OK, android::getService<IAidlStuff>(String16(id2name(kP1Id).c_str()), &stuff)); + ASSERT_NE(nullptr, stuff); + ASSERT_TRUE(stuff->call(0).isOk()); +} + +TEST(BindThreadState, RemoteNestedStartHidlCall) { + auto stuff = IHidlStuff::getService(id2name(kP1Id)); + ASSERT_NE(nullptr, stuff); + ASSERT_TRUE(stuff->call(100).isOk()); +} +TEST(BindThreadState, RemoteNestedStartAidlCall) { + sp<IAidlStuff> stuff; + ASSERT_EQ(OK, android::getService<IAidlStuff>(String16(id2name(kP1Id).c_str()), &stuff)); + ASSERT_NE(nullptr, stuff); + EXPECT_TRUE(stuff->call(100).isOk()); +} + +int server(size_t thisId, size_t otherId) { + // AIDL + android::ProcessState::self()->setThreadPoolMaxThreadCount(1); + sp<AidlServer> aidlServer = new AidlServer(thisId, otherId); + CHECK(OK == defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer)); + android::ProcessState::self()->startThreadPool(); + + // HIDL + setenv("TREBLE_TESTING_OVERRIDE", "true", true); + android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/); + sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId); + CHECK(OK == hidlServer->registerAsService(id2name(thisId).c_str())); + android::hardware::joinRpcThreadpool(); + + return EXIT_FAILURE; +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + setenv("TREBLE_TESTING_OVERRIDE", "true", true); + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return server(kP1Id, kP2Id); + } + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return server(kP2Id, kP1Id); + } + + android::waitForService<IAidlStuff>(String16(id2name(kP1Id).c_str())); + android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP1Id).c_str()); + android::waitForService<IAidlStuff>(String16(id2name(kP2Id).c_str())); + android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP2Id).c_str()); + + return RUN_ALL_TESTS(); +} diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp index 28cb13827d..b1943a4afd 100644 --- a/libs/cputimeinstate/Android.bp +++ b/libs/cputimeinstate/Android.bp @@ -8,19 +8,26 @@ cc_library { "liblog", "libnetdutils" ], + header_libs: ["bpf_prog_headers"], cflags: [ "-Werror", "-Wall", "-Wextra", ], + export_include_dirs: ["."], } cc_test { name: "libtimeinstate_test", srcs: ["testtimeinstate.cpp"], shared_libs: [ + "libbase", + "libbpf", + "libbpf_android", "libtimeinstate", + "libnetdutils", ], + header_libs: ["bpf_prog_headers"], cflags: [ "-Werror", "-Wall", diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 5fd4a95d7b..05a462e026 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -17,12 +17,16 @@ #define LOG_TAG "libtimeinstate" #include "cputimeinstate.h" +#include <bpf_timeinstate.h> #include <dirent.h> #include <errno.h> #include <inttypes.h> +#include <sys/sysinfo.h> #include <mutex> +#include <numeric> +#include <optional> #include <set> #include <string> #include <unordered_map> @@ -37,44 +41,39 @@ #include <libbpf.h> #include <log/log.h> -#define BPF_FS_PATH "/sys/fs/bpf/" - using android::base::StringPrintf; using android::base::unique_fd; namespace android { namespace bpf { -struct time_key_t { - uint32_t uid; - uint32_t freq; -}; - -struct val_t { - uint64_t ar[100]; -}; - static std::mutex gInitializedMutex; static bool gInitialized = false; +static std::mutex gTrackingMutex; +static bool gTracking = false; static uint32_t gNPolicies = 0; +static uint32_t gNCpus = 0; static std::vector<std::vector<uint32_t>> gPolicyFreqs; static std::vector<std::vector<uint32_t>> gPolicyCpus; static std::set<uint32_t> gAllFreqs; -static unique_fd gMapFd; +static unique_fd gTisMapFd; +static unique_fd gConcurrentMapFd; +static unique_fd gUidLastUpdateMapFd; -static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) { +static std::optional<std::vector<uint32_t>> readNumbersFromFile(const std::string &path) { std::string data; - if (!android::base::ReadFileToString(path, &data)) return false; + if (!android::base::ReadFileToString(path, &data)) return {}; auto strings = android::base::Split(data, " \n"); + std::vector<uint32_t> ret; for (const auto &s : strings) { if (s.empty()) continue; uint32_t n; - if (!android::base::ParseUint(s, &n)) return false; - out->emplace_back(n); + if (!android::base::ParseUint(s, &n)) return {}; + ret.emplace_back(n); } - return true; + return ret; } static int isPolicyFile(const struct dirent *d) { @@ -89,10 +88,22 @@ static int comparePolicyFiles(const struct dirent **d1, const struct dirent **d2 return policyN1 - policyN2; } +static int bpf_obj_get_wronly(const char *pathname) { + union bpf_attr attr; + + memset(&attr, 0, sizeof(attr)); + attr.pathname = ptr_to_u64((void *)pathname); + attr.file_flags = BPF_F_WRONLY; + + return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr)); +} + static bool initGlobals() { std::lock_guard<std::mutex> guard(gInitializedMutex); if (gInitialized) return true; + gNCpus = get_nprocs_conf(); + struct dirent **dirlist; const char basepath[] = "/sys/devices/system/cpu/cpufreq"; int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles); @@ -111,21 +122,32 @@ static bool initGlobals() { for (const auto &name : {"available", "boost"}) { std::string path = StringPrintf("%s/%s/scaling_%s_frequencies", basepath, policy.c_str(), name); - if (!readNumbersFromFile(path, &freqs)) return false; + auto nums = readNumbersFromFile(path); + if (!nums) continue; + freqs.insert(freqs.end(), nums->begin(), nums->end()); } + if (freqs.empty()) return false; std::sort(freqs.begin(), freqs.end()); gPolicyFreqs.emplace_back(freqs); for (auto freq : freqs) gAllFreqs.insert(freq); - std::vector<uint32_t> cpus; std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus"); - if (!readNumbersFromFile(path, &cpus)) return false; - gPolicyCpus.emplace_back(cpus); + auto cpus = readNumbersFromFile(path); + if (!cpus) return false; + gPolicyCpus.emplace_back(*cpus); } - gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times")}; - if (gMapFd < 0) return false; + gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + if (gTisMapFd < 0) return false; + + gConcurrentMapFd = + unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + if (gConcurrentMapFd < 0) return false; + + gUidLastUpdateMapFd = + unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_last_update_map")}; + if (gUidLastUpdateMapFd < 0) return false; gInitialized = true; return true; @@ -145,98 +167,334 @@ static bool attachTracepointProgram(const std::string &eventType, const std::str // process dies then it must be called again to resume tracking. // This function should *not* be called while tracking is already active; doing so is unnecessary // and can lead to accounting errors. -bool startTrackingUidCpuFreqTimes() { - return attachTracepointProgram("sched", "sched_switch") && +bool startTrackingUidTimes() { + std::lock_guard<std::mutex> guard(gTrackingMutex); + if (!initGlobals()) return false; + if (gTracking) return true; + + unique_fd cpuPolicyFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_policy_map")); + if (cpuPolicyFd < 0) return false; + + for (uint32_t i = 0; i < gPolicyCpus.size(); ++i) { + for (auto &cpu : gPolicyCpus[i]) { + if (writeToMapEntry(cpuPolicyFd, &cpu, &i, BPF_ANY)) return false; + } + } + + unique_fd freqToIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_freq_to_idx_map")); + if (freqToIdxFd < 0) return false; + freq_idx_key_t key; + for (uint32_t i = 0; i < gNPolicies; ++i) { + key.policy = i; + for (uint32_t j = 0; j < gPolicyFreqs[i].size(); ++j) { + key.freq = gPolicyFreqs[i][j]; + // Start indexes at 1 so that uninitialized state is distinguishable from lowest freq. + // The uid_times map still uses 0-based indexes, and the sched_switch program handles + // conversion between them, so this does not affect our map reading code. + uint32_t idx = j + 1; + if (writeToMapEntry(freqToIdxFd, &key, &idx, BPF_ANY)) return false; + } + } + + unique_fd cpuLastUpdateFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_last_update_map")); + if (cpuLastUpdateFd < 0) return false; + std::vector<uint64_t> zeros(get_nprocs_conf(), 0); + uint32_t zero = 0; + if (writeToMapEntry(cpuLastUpdateFd, &zero, zeros.data(), BPF_ANY)) return false; + + unique_fd nrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_nr_active_map")); + if (nrActiveFd < 0) return false; + if (writeToMapEntry(nrActiveFd, &zero, &zero, BPF_ANY)) return false; + + unique_fd policyNrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_nr_active_map")); + if (policyNrActiveFd < 0) return false; + for (uint32_t i = 0; i < gNPolicies; ++i) { + if (writeToMapEntry(policyNrActiveFd, &i, &zero, BPF_ANY)) return false; + } + + unique_fd policyFreqIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map")); + if (policyFreqIdxFd < 0) return false; + for (uint32_t i = 0; i < gNPolicies; ++i) { + if (writeToMapEntry(policyFreqIdxFd, &i, &zero, BPF_ANY)) return false; + } + + gTracking = attachTracepointProgram("sched", "sched_switch") && attachTracepointProgram("power", "cpu_frequency"); + return gTracking; } -// Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes. -// Returns false on error. Otherwise, returns true and populates freqTimes with a vector of vectors -// using the format: +std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs() { + if (!gInitialized && !initGlobals()) return {}; + return gPolicyFreqs; +} + +// Retrieve the times in ns that uid spent running at each CPU frequency. +// Return contains no value on error, otherwise it contains a vector of vectors using the format: // [[t0_0, t0_1, ...], // [t1_0, t1_1, ...], ...] // where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq. -bool getUidCpuFreqTimes(uint32_t uid, std::vector<std::vector<uint64_t>> *freqTimes) { - if (!gInitialized && !initGlobals()) return false; - time_key_t key = {.uid = uid, .freq = 0}; - - freqTimes->clear(); - freqTimes->resize(gNPolicies); - std::vector<uint32_t> idxs(gNPolicies, 0); - - val_t value; - for (uint32_t freq : gAllFreqs) { - key.freq = freq; - int ret = findMapEntry(gMapFd, &key, &value); - if (ret) { - if (errno == ENOENT) - memset(&value.ar, 0, sizeof(value.ar)); - else - return false; +std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid) { + if (!gInitialized && !initGlobals()) return {}; + + std::vector<std::vector<uint64_t>> out; + uint32_t maxFreqCount = 0; + for (const auto &freqList : gPolicyFreqs) { + if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size(); + out.emplace_back(freqList.size(), 0); + } + + std::vector<tis_val_t> vals(gNCpus); + time_key_t key = {.uid = uid}; + for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) { + key.bucket = i; + if (findMapEntry(gTisMapFd, &key, vals.data())) { + if (errno != ENOENT) return {}; + continue; } - for (uint32_t i = 0; i < gNPolicies; ++i) { - if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) continue; - uint64_t time = 0; - for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu]; - idxs[i] += 1; - (*freqTimes)[i].emplace_back(time); + + auto offset = i * FREQS_PER_ENTRY; + auto nextOffset = (i + 1) * FREQS_PER_ENTRY; + for (uint32_t j = 0; j < gNPolicies; ++j) { + if (offset >= gPolicyFreqs[j].size()) continue; + auto begin = out[j].begin() + offset; + auto end = nextOffset < gPolicyFreqs[j].size() ? begin + FREQS_PER_ENTRY : out[j].end(); + + for (const auto &cpu : gPolicyCpus[j]) { + std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>()); + } } } + return out; +} + +static std::optional<bool> uidUpdatedSince(uint32_t uid, uint64_t lastUpdate, + uint64_t *newLastUpdate) { + uint64_t uidLastUpdate; + if (findMapEntry(gUidLastUpdateMapFd, &uid, &uidLastUpdate)) return {}; + // Updates that occurred during the previous read may have been missed. To mitigate + // this, don't ignore entries updated up to 1s before *lastUpdate + constexpr uint64_t NSEC_PER_SEC = 1000000000; + if (uidLastUpdate + NSEC_PER_SEC < lastUpdate) return false; + if (uidLastUpdate > *newLastUpdate) *newLastUpdate = uidLastUpdate; return true; } -// Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap. -// Returns false on error. Otherwise, returns true and populates freqTimeMap with a map from uids to -// vectors of vectors using the format: +// Retrieve the times in ns that each uid spent running at each CPU freq. +// Return contains no value on error, otherwise it contains a map from uids to vectors of vectors +// using the format: // { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...], // uid1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... } // where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq. -bool getUidsCpuFreqTimes( - std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *freqTimeMap) { - if (!gInitialized && !initGlobals()) return false; +std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> +getUidsCpuFreqTimes() { + return getUidsUpdatedCpuFreqTimes(nullptr); +} - int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times"); - if (fd < 0) return false; - BpfMap<time_key_t, val_t> m(fd); +// Retrieve the times in ns that each uid spent running at each CPU freq, excluding UIDs that have +// not run since before lastUpdate. +// Return format is the same as getUidsCpuFreqTimes() +std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> +getUidsUpdatedCpuFreqTimes(uint64_t *lastUpdate) { + if (!gInitialized && !initGlobals()) return {}; + time_key_t key, prevKey; + std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> map; + if (getFirstMapKey(gTisMapFd, &key)) { + if (errno == ENOENT) return map; + return std::nullopt; + } - std::vector<std::unordered_map<uint32_t, uint32_t>> policyFreqIdxs; - for (uint32_t i = 0; i < gNPolicies; ++i) { - std::unordered_map<uint32_t, uint32_t> freqIdxs; - for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j; - policyFreqIdxs.emplace_back(freqIdxs); + std::vector<std::vector<uint64_t>> mapFormat; + for (const auto &freqList : gPolicyFreqs) mapFormat.emplace_back(freqList.size(), 0); + + uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0; + std::vector<tis_val_t> vals(gNCpus); + do { + if (lastUpdate) { + auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate); + if (!uidUpdated.has_value()) return {}; + if (!*uidUpdated) continue; + } + if (findMapEntry(gTisMapFd, &key, vals.data())) return {}; + if (map.find(key.uid) == map.end()) map.emplace(key.uid, mapFormat); + + auto offset = key.bucket * FREQS_PER_ENTRY; + auto nextOffset = (key.bucket + 1) * FREQS_PER_ENTRY; + for (uint32_t i = 0; i < gNPolicies; ++i) { + if (offset >= gPolicyFreqs[i].size()) continue; + auto begin = map[key.uid][i].begin() + offset; + auto end = nextOffset < gPolicyFreqs[i].size() ? begin + FREQS_PER_ENTRY : + map[key.uid][i].end(); + for (const auto &cpu : gPolicyCpus[i]) { + std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>()); + } + } + prevKey = key; + } while (prevKey = key, !getNextMapKey(gTisMapFd, &prevKey, &key)); + if (errno != ENOENT) return {}; + if (lastUpdate && newLastUpdate > *lastUpdate) *lastUpdate = newLastUpdate; + return map; +} + +static bool verifyConcurrentTimes(const concurrent_time_t &ct) { + uint64_t activeSum = std::accumulate(ct.active.begin(), ct.active.end(), (uint64_t)0); + uint64_t policySum = 0; + for (const auto &vec : ct.policy) { + policySum += std::accumulate(vec.begin(), vec.end(), (uint64_t)0); } + return activeSum == policySum; +} - auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val, - const BpfMap<time_key_t, val_t> &) { - if (freqTimeMap->find(key.uid) == freqTimeMap->end()) { - (*freqTimeMap)[key.uid].resize(gNPolicies); - for (uint32_t i = 0; i < gNPolicies; ++i) { - (*freqTimeMap)[key.uid][i].resize(gPolicyFreqs[i].size(), 0); +// Retrieve the times in ns that uid spent running concurrently with each possible number of other +// tasks on each cluster (policy times) and overall (active times). +// Return contains no value on error, otherwise it contains a concurrent_time_t with the format: +// {.active = [a0, a1, ...], .policy = [[p0_0, p0_1, ...], [p1_0, p1_1, ...], ...]} +// where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent +// running on the ith cluster, concurrently with tasks on j other cpus in the same cluster +std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) { + if (!gInitialized && !initGlobals()) return {}; + concurrent_time_t ret = {.active = std::vector<uint64_t>(gNCpus, 0)}; + for (const auto &cpuList : gPolicyCpus) ret.policy.emplace_back(cpuList.size(), 0); + std::vector<concurrent_val_t> vals(gNCpus); + time_key_t key = {.uid = uid}; + for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) { + if (findMapEntry(gConcurrentMapFd, &key, vals.data())) { + if (errno != ENOENT) return {}; + continue; + } + auto offset = key.bucket * CPUS_PER_ENTRY; + auto nextOffset = (key.bucket + 1) * CPUS_PER_ENTRY; + + auto activeBegin = ret.active.begin() + offset; + auto activeEnd = nextOffset < gNCpus ? activeBegin + CPUS_PER_ENTRY : ret.active.end(); + + for (uint32_t cpu = 0; cpu < gNCpus; ++cpu) { + std::transform(activeBegin, activeEnd, std::begin(vals[cpu].active), activeBegin, + std::plus<uint64_t>()); + } + + for (uint32_t policy = 0; policy < gNPolicies; ++policy) { + if (offset >= gPolicyCpus[policy].size()) continue; + auto policyBegin = ret.policy[policy].begin() + offset; + auto policyEnd = nextOffset < gPolicyCpus[policy].size() ? policyBegin + CPUS_PER_ENTRY + : ret.policy[policy].end(); + + for (const auto &cpu : gPolicyCpus[policy]) { + std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin, + std::plus<uint64_t>()); } } + } + if (!verifyConcurrentTimes(ret) && retry) return getUidConcurrentTimes(uid, false); + return ret; +} + +// Retrieve the times in ns that each uid spent running concurrently with each possible number of +// other tasks on each cluster (policy times) and overall (active times). +// Return contains no value on error, otherwise it contains a map from uids to concurrent_time_t's +// using the format: +// { uid0 -> {.active = [a0, a1, ...], .policy = [[p0_0, p0_1, ...], [p1_0, p1_1, ...], ...] }, ...} +// where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent +// running on the ith cluster, concurrently with tasks on j other cpus in the same cluster. +std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes() { + return getUidsUpdatedConcurrentTimes(nullptr); +} + +// Retrieve the times in ns that each uid spent running concurrently with each possible number of +// other tasks on each cluster (policy times) and overall (active times), excluding UIDs that have +// not run since before lastUpdate. +// Return format is the same as getUidsConcurrentTimes() +std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsUpdatedConcurrentTimes( + uint64_t *lastUpdate) { + if (!gInitialized && !initGlobals()) return {}; + time_key_t key, prevKey; + std::unordered_map<uint32_t, concurrent_time_t> ret; + if (getFirstMapKey(gConcurrentMapFd, &key)) { + if (errno == ENOENT) return ret; + return {}; + } + + concurrent_time_t retFormat = {.active = std::vector<uint64_t>(gNCpus, 0)}; + for (const auto &cpuList : gPolicyCpus) retFormat.policy.emplace_back(cpuList.size(), 0); + + std::vector<concurrent_val_t> vals(gNCpus); + std::vector<uint64_t>::iterator activeBegin, activeEnd, policyBegin, policyEnd; + + uint64_t newLastUpdate = lastUpdate ? *lastUpdate : 0; + do { + if (lastUpdate) { + auto uidUpdated = uidUpdatedSince(key.uid, *lastUpdate, &newLastUpdate); + if (!uidUpdated.has_value()) return {}; + if (!*uidUpdated) continue; + } + if (findMapEntry(gConcurrentMapFd, &key, vals.data())) return {}; + if (ret.find(key.uid) == ret.end()) ret.emplace(key.uid, retFormat); + + auto offset = key.bucket * CPUS_PER_ENTRY; + auto nextOffset = (key.bucket + 1) * CPUS_PER_ENTRY; + + activeBegin = ret[key.uid].active.begin(); + activeEnd = nextOffset < gNCpus ? activeBegin + CPUS_PER_ENTRY : ret[key.uid].active.end(); + + for (uint32_t cpu = 0; cpu < gNCpus; ++cpu) { + std::transform(activeBegin, activeEnd, std::begin(vals[cpu].active), activeBegin, + std::plus<uint64_t>()); + } + + for (uint32_t policy = 0; policy < gNPolicies; ++policy) { + if (offset >= gPolicyCpus[policy].size()) continue; + policyBegin = ret[key.uid].policy[policy].begin() + offset; + policyEnd = nextOffset < gPolicyCpus[policy].size() ? policyBegin + CPUS_PER_ENTRY + : ret[key.uid].policy[policy].end(); - for (size_t policy = 0; policy < gNPolicies; ++policy) { for (const auto &cpu : gPolicyCpus[policy]) { - auto freqIdx = policyFreqIdxs[policy][key.freq]; - (*freqTimeMap)[key.uid][policy][freqIdx] += val.ar[cpu]; + std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin, + std::plus<uint64_t>()); } } - return android::netdutils::status::ok; - }; - return isOk(m.iterateWithValue(fn)); + } while (prevKey = key, !getNextMapKey(gConcurrentMapFd, &prevKey, &key)); + if (errno != ENOENT) return {}; + for (const auto &[key, value] : ret) { + if (!verifyConcurrentTimes(value)) { + auto val = getUidConcurrentTimes(key, false); + if (val.has_value()) ret[key] = val.value(); + } + } + if (lastUpdate && newLastUpdate > *lastUpdate) *lastUpdate = newLastUpdate; + return ret; } // Clear all time in state data for a given uid. Returns false on error, true otherwise. -bool clearUidCpuFreqTimes(uint32_t uid) { +// This is only suitable for clearing data when an app is uninstalled; if called on a UID with +// running tasks it will cause time in state vs. concurrent time totals to be inconsistent for that +// UID. +bool clearUidTimes(uint32_t uid) { if (!gInitialized && !initGlobals()) return false; - time_key_t key = {.uid = uid, .freq = 0}; - std::vector<uint32_t> idxs(gNPolicies, 0); - for (auto freq : gAllFreqs) { - key.freq = freq; - if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false; + time_key_t key = {.uid = uid}; + + uint32_t maxFreqCount = 0; + for (const auto &freqList : gPolicyFreqs) { + if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size(); } + + tis_val_t zeros = {0}; + std::vector<tis_val_t> vals(gNCpus, zeros); + for (key.bucket = 0; key.bucket <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++key.bucket) { + if (writeToMapEntry(gTisMapFd, &key, vals.data(), BPF_EXIST) && errno != ENOENT) + return false; + if (deleteMapEntry(gTisMapFd, &key) && errno != ENOENT) return false; + } + + concurrent_val_t czeros = {.policy = {0}, .active = {0}}; + std::vector<concurrent_val_t> cvals(gNCpus, czeros); + for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) { + if (writeToMapEntry(gConcurrentMapFd, &key, cvals.data(), BPF_EXIST) && errno != ENOENT) + return false; + if (deleteMapEntry(gConcurrentMapFd, &key) && errno != ENOENT) return false; + } + + if (deleteMapEntry(gUidLastUpdateMapFd, &uid) && errno != ENOENT) return false; return true; } diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h index 9f6103ed9b..b7600f59e0 100644 --- a/libs/cputimeinstate/cputimeinstate.h +++ b/libs/cputimeinstate/cputimeinstate.h @@ -22,10 +22,24 @@ namespace android { namespace bpf { -bool startTrackingUidCpuFreqTimes(); -bool getUidCpuFreqTimes(unsigned int uid, std::vector<std::vector<uint64_t>> *freqTimes); -bool getUidsCpuFreqTimes(std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *tisMap); -bool clearUidCpuFreqTimes(unsigned int uid); +bool startTrackingUidTimes(); +std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid); +std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> + getUidsCpuFreqTimes(); +std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> + getUidsUpdatedCpuFreqTimes(uint64_t *lastUpdate); +std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs(); + +struct concurrent_time_t { + std::vector<uint64_t> active; + std::vector<std::vector<uint64_t>> policy; +}; + +std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry = true); +std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes(); +std::optional<std::unordered_map<uint32_t, concurrent_time_t>> + getUidsUpdatedConcurrentTimes(uint64_t *lastUpdate); +bool clearUidTimes(unsigned int uid); } // namespace bpf } // namespace android diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 9837865dfb..ea2a2008b7 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -1,57 +1,485 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <bpf_timeinstate.h> + +#include <sys/sysinfo.h> + +#include <numeric> #include <unordered_map> #include <vector> #include <gtest/gtest.h> +#include <android-base/unique_fd.h> +#include <bpf/BpfMap.h> #include <cputimeinstate.h> +#include <libbpf.h> namespace android { namespace bpf { +static constexpr uint64_t NSEC_PER_SEC = 1000000000; +static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365; + using std::vector; -TEST(TimeInStateTest, SingleUid) { - vector<vector<uint64_t>> times; - ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); - EXPECT_FALSE(times.empty()); +TEST(TimeInStateTest, SingleUidTimeInState) { + auto times = getUidCpuFreqTimes(0); + ASSERT_TRUE(times.has_value()); + EXPECT_FALSE(times->empty()); +} + +TEST(TimeInStateTest, SingleUidConcurrentTimes) { + auto concurrentTimes = getUidConcurrentTimes(0); + ASSERT_TRUE(concurrentTimes.has_value()); + ASSERT_FALSE(concurrentTimes->active.empty()); + ASSERT_FALSE(concurrentTimes->policy.empty()); + + uint64_t policyEntries = 0; + for (const auto &policyTimeVec : concurrentTimes->policy) policyEntries += policyTimeVec.size(); + ASSERT_EQ(concurrentTimes->active.size(), policyEntries); +} + +static void TestConcurrentTimesConsistent(const struct concurrent_time_t &concurrentTime) { + size_t maxPolicyCpus = 0; + for (const auto &vec : concurrentTime.policy) { + maxPolicyCpus = std::max(maxPolicyCpus, vec.size()); + } + uint64_t policySum = 0; + for (size_t i = 0; i < maxPolicyCpus; ++i) { + for (const auto &vec : concurrentTime.policy) { + if (i < vec.size()) policySum += vec[i]; + } + ASSERT_LE(concurrentTime.active[i], policySum); + policySum -= concurrentTime.active[i]; + } + policySum = 0; + for (size_t i = 0; i < concurrentTime.active.size(); ++i) { + for (const auto &vec : concurrentTime.policy) { + if (i < vec.size()) policySum += vec[vec.size() - 1 - i]; + } + auto activeSum = concurrentTime.active[concurrentTime.active.size() - 1 - i]; + // This check is slightly flaky because we may read a map entry in the middle of an update + // when active times have been updated but policy times have not. This happens infrequently + // and can be distinguished from more serious bugs by re-running the test: if the underlying + // data itself is inconsistent, the test will fail every time. + ASSERT_LE(activeSum, policySum); + policySum -= activeSum; + } +} + +static void TestUidTimesConsistent(const std::vector<std::vector<uint64_t>> &timeInState, + const struct concurrent_time_t &concurrentTime) { + ASSERT_NO_FATAL_FAILURE(TestConcurrentTimesConsistent(concurrentTime)); + ASSERT_EQ(timeInState.size(), concurrentTime.policy.size()); + uint64_t policySum = 0; + for (uint32_t i = 0; i < timeInState.size(); ++i) { + uint64_t tisSum = + std::accumulate(timeInState[i].begin(), timeInState[i].end(), (uint64_t)0); + uint64_t concurrentSum = std::accumulate(concurrentTime.policy[i].begin(), + concurrentTime.policy[i].end(), (uint64_t)0); + if (tisSum < concurrentSum) + ASSERT_LE(concurrentSum - tisSum, NSEC_PER_SEC); + else + ASSERT_LE(tisSum - concurrentSum, NSEC_PER_SEC); + policySum += concurrentSum; + } + uint64_t activeSum = std::accumulate(concurrentTime.active.begin(), concurrentTime.active.end(), + (uint64_t)0); + EXPECT_EQ(activeSum, policySum); +} + +TEST(TimeInStateTest, SingleUidTimesConsistent) { + auto times = getUidCpuFreqTimes(0); + ASSERT_TRUE(times.has_value()); + + auto concurrentTimes = getUidConcurrentTimes(0); + ASSERT_TRUE(concurrentTimes.has_value()); + + ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes)); +} + +TEST(TimeInStateTest, AllUidTimeInState) { + uint64_t zero = 0; + auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + + ASSERT_FALSE(map->empty()); + + vector<size_t> sizes; + auto firstEntry = map->begin()->second; + for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size()); + + for (const auto &vec : *map) { + ASSERT_EQ(vec.second.size(), sizes.size()); + for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]); + } + } +} + +void TestCheckUpdate(const std::vector<std::vector<uint64_t>> &before, + const std::vector<std::vector<uint64_t>> &after) { + ASSERT_EQ(before.size(), after.size()); + uint64_t sumBefore = 0, sumAfter = 0; + for (size_t i = 0; i < before.size(); ++i) { + ASSERT_EQ(before[i].size(), after[i].size()); + for (size_t j = 0; j < before[i].size(); ++j) { + // Times should never decrease + ASSERT_LE(before[i][j], after[i][j]); + } + sumBefore += std::accumulate(before[i].begin(), before[i].end(), (uint64_t)0); + sumAfter += std::accumulate(after[i].begin(), after[i].end(), (uint64_t)0); + } + ASSERT_LE(sumBefore, sumAfter); + ASSERT_LE(sumAfter - sumBefore, NSEC_PER_SEC); +} + +TEST(TimeInStateTest, AllUidUpdatedTimeInState) { + uint64_t lastUpdate = 0; + auto map1 = getUidsUpdatedCpuFreqTimes(&lastUpdate); + ASSERT_TRUE(map1.has_value()); + ASSERT_FALSE(map1->empty()); + ASSERT_NE(lastUpdate, (uint64_t)0); + uint64_t oldLastUpdate = lastUpdate; + + // Sleep briefly to trigger a context switch, ensuring we see at least one update. + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep (&ts, NULL); + + auto map2 = getUidsUpdatedCpuFreqTimes(&lastUpdate); + ASSERT_TRUE(map2.has_value()); + ASSERT_FALSE(map2->empty()); + ASSERT_NE(lastUpdate, oldLastUpdate); + + bool someUidsExcluded = false; + for (const auto &[uid, v] : *map1) { + if (map2->find(uid) == map2->end()) { + someUidsExcluded = true; + break; + } + } + ASSERT_TRUE(someUidsExcluded); + + for (const auto &[uid, newTimes] : *map2) { + ASSERT_NE(map1->find(uid), map1->end()); + ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid], newTimes)); + } +} + +TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { + uint64_t zero = 0; + auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + ASSERT_FALSE(map->empty()); + + for (const auto &kv : *map) { + uint32_t uid = kv.first; + auto times1 = kv.second; + auto times2 = getUidCpuFreqTimes(uid); + ASSERT_TRUE(times2.has_value()); + + ASSERT_EQ(times1.size(), times2->size()); + for (uint32_t i = 0; i < times1.size(); ++i) { + ASSERT_EQ(times1[i].size(), (*times2)[i].size()); + for (uint32_t j = 0; j < times1[i].size(); ++j) { + ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC); + } + } + } + } +} + +TEST(TimeInStateTest, AllUidConcurrentTimes) { + uint64_t zero = 0; + auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + ASSERT_FALSE(map->empty()); + + auto firstEntry = map->begin()->second; + for (const auto &kv : *map) { + ASSERT_EQ(kv.second.active.size(), firstEntry.active.size()); + ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size()); + for (size_t i = 0; i < kv.second.policy.size(); ++i) { + ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size()); + } + } + } +} + +TEST(TimeInStateTest, AllUidUpdatedConcurrentTimes) { + uint64_t lastUpdate = 0; + auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate); + ASSERT_TRUE(map1.has_value()); + ASSERT_FALSE(map1->empty()); + ASSERT_NE(lastUpdate, (uint64_t)0); + + // Sleep briefly to trigger a context switch, ensuring we see at least one update. + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep (&ts, NULL); + + uint64_t oldLastUpdate = lastUpdate; + auto map2 = getUidsUpdatedConcurrentTimes(&lastUpdate); + ASSERT_TRUE(map2.has_value()); + ASSERT_FALSE(map2->empty()); + ASSERT_NE(lastUpdate, oldLastUpdate); + + bool someUidsExcluded = false; + for (const auto &[uid, v] : *map1) { + if (map2->find(uid) == map2->end()) { + someUidsExcluded = true; + break; + } + } + ASSERT_TRUE(someUidsExcluded); + + for (const auto &[uid, newTimes] : *map2) { + ASSERT_NE(map1->find(uid), map1->end()); + ASSERT_NO_FATAL_FAILURE(TestCheckUpdate({(*map1)[uid].active},{newTimes.active})); + ASSERT_NO_FATAL_FAILURE(TestCheckUpdate((*map1)[uid].policy, newTimes.policy)); + } +} + +TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) { + uint64_t zero = 0; + auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + for (const auto &kv : *map) { + uint32_t uid = kv.first; + auto times1 = kv.second; + auto times2 = getUidConcurrentTimes(uid); + ASSERT_TRUE(times2.has_value()); + for (uint32_t i = 0; i < times1.active.size(); ++i) { + ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC); + } + for (uint32_t i = 0; i < times1.policy.size(); ++i) { + for (uint32_t j = 0; j < times1.policy[i].size(); ++j) { + ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC); + } + } + } + } +} + +void TestCheckDelta(uint64_t before, uint64_t after) { + // Times should never decrease + ASSERT_LE(before, after); + // UID can't have run for more than ~1s on each CPU + ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf()); } -TEST(TimeInStateTest, AllUid) { - vector<size_t> sizes; - std::unordered_map<uint32_t, vector<vector<uint64_t>>> map; - ASSERT_TRUE(getUidsCpuFreqTimes(&map)); +TEST(TimeInStateTest, AllUidTimeInStateMonotonic) { + auto map1 = getUidsCpuFreqTimes(); + ASSERT_TRUE(map1.has_value()); + sleep(1); + auto map2 = getUidsCpuFreqTimes(); + ASSERT_TRUE(map2.has_value()); - ASSERT_FALSE(map.empty()); + for (const auto &kv : *map1) { + uint32_t uid = kv.first; + auto times = kv.second; + ASSERT_NE(map2->find(uid), map2->end()); + for (uint32_t policy = 0; policy < times.size(); ++policy) { + for (uint32_t freqIdx = 0; freqIdx < times[policy].size(); ++freqIdx) { + auto before = times[policy][freqIdx]; + auto after = (*map2)[uid][policy][freqIdx]; + ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after)); + } + } + } +} - auto firstEntry = map.begin()->second; - for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size()); +TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) { + auto map1 = getUidsConcurrentTimes(); + ASSERT_TRUE(map1.has_value()); + ASSERT_FALSE(map1->empty()); + sleep(1); + auto map2 = getUidsConcurrentTimes(); + ASSERT_TRUE(map2.has_value()); + ASSERT_FALSE(map2->empty()); - for (const auto &vec : map) { - ASSERT_EQ(vec.second.size(), sizes.size()); - for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]); + for (const auto &kv : *map1) { + uint32_t uid = kv.first; + auto times = kv.second; + ASSERT_NE(map2->find(uid), map2->end()); + for (uint32_t i = 0; i < times.active.size(); ++i) { + auto before = times.active[i]; + auto after = (*map2)[uid].active[i]; + ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after)); + } + for (uint32_t policy = 0; policy < times.policy.size(); ++policy) { + for (uint32_t idx = 0; idx < times.policy[policy].size(); ++idx) { + auto before = times.policy[policy][idx]; + auto after = (*map2)[uid].policy[policy][idx]; + ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after)); + } + } + } +} + +TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) { + uint64_t zero = 0; + auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; + for (const auto &map : maps) { + ASSERT_TRUE(map.has_value()); + + bool foundLargeValue = false; + for (const auto &kv : *map) { + for (const auto &timeVec : kv.second) { + for (const auto &time : timeVec) { + ASSERT_LE(time, NSEC_PER_YEAR); + if (time > UINT32_MAX) foundLargeValue = true; + } + } + } + // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using + // uint64_t as expected, we should have some times higher than that. + ASSERT_TRUE(foundLargeValue); + } +} + +TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) { + uint64_t zero = 0; + auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; + for (const auto &concurrentMap : maps) { + ASSERT_TRUE(concurrentMap); + + bool activeFoundLargeValue = false; + bool policyFoundLargeValue = false; + for (const auto &kv : *concurrentMap) { + for (const auto &time : kv.second.active) { + ASSERT_LE(time, NSEC_PER_YEAR); + if (time > UINT32_MAX) activeFoundLargeValue = true; + } + for (const auto &policyTimeVec : kv.second.policy) { + for (const auto &time : policyTimeVec) { + ASSERT_LE(time, NSEC_PER_YEAR); + if (time > UINT32_MAX) policyFoundLargeValue = true; + } + } + } + // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using + // uint64_t as expected, we should have some times higher than that. + ASSERT_TRUE(activeFoundLargeValue); + ASSERT_TRUE(policyFoundLargeValue); + } +} + +TEST(TimeInStateTest, AllUidTimesConsistent) { + auto tisMap = getUidsCpuFreqTimes(); + ASSERT_TRUE(tisMap.has_value()); + + auto concurrentMap = getUidsConcurrentTimes(); + ASSERT_TRUE(concurrentMap.has_value()); + + ASSERT_EQ(tisMap->size(), concurrentMap->size()); + for (const auto &kv : *tisMap) { + uint32_t uid = kv.first; + auto times = kv.second; + ASSERT_NE(concurrentMap->find(uid), concurrentMap->end()); + + auto concurrentTimes = (*concurrentMap)[uid]; + ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(times, concurrentTimes)); } } TEST(TimeInStateTest, RemoveUid) { - vector<vector<uint64_t>> times, times2; - ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); - ASSERT_FALSE(times.empty()); + uint32_t uid = 0; + { + // Find an unused UID + auto times = getUidsCpuFreqTimes(); + ASSERT_TRUE(times.has_value()); + ASSERT_FALSE(times->empty()); + for (const auto &kv : *times) uid = std::max(uid, kv.first); + ++uid; + } + { + // Add a map entry for our fake UID by copying a real map entry + android::base::unique_fd fd{ + bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + ASSERT_GE(fd, 0); + time_key_t k; + ASSERT_FALSE(getFirstMapKey(fd, &k)); + std::vector<tis_val_t> vals(get_nprocs_conf()); + ASSERT_FALSE(findMapEntry(fd, &k, vals.data())); + uint32_t copiedUid = k.uid; + k.uid = uid; + ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST)); + + android::base::unique_fd fd2{ + bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + k.uid = copiedUid; + k.bucket = 0; + std::vector<concurrent_val_t> cvals(get_nprocs_conf()); + ASSERT_FALSE(findMapEntry(fd2, &k, cvals.data())); + k.uid = uid; + ASSERT_FALSE(writeToMapEntry(fd2, &k, cvals.data(), BPF_NOEXIST)); + } + auto times = getUidCpuFreqTimes(uid); + ASSERT_TRUE(times.has_value()); + ASSERT_FALSE(times->empty()); + + auto concurrentTimes = getUidConcurrentTimes(0); + ASSERT_TRUE(concurrentTimes.has_value()); + ASSERT_FALSE(concurrentTimes->active.empty()); + ASSERT_FALSE(concurrentTimes->policy.empty()); uint64_t sum = 0; - for (size_t i = 0; i < times.size(); ++i) { - for (auto x : times[i]) sum += x; + for (size_t i = 0; i < times->size(); ++i) { + for (auto x : (*times)[i]) sum += x; } ASSERT_GT(sum, (uint64_t)0); - ASSERT_TRUE(clearUidCpuFreqTimes(0)); - - ASSERT_TRUE(getUidCpuFreqTimes(0, ×2)); - ASSERT_EQ(times2.size(), times.size()); - for (size_t i = 0; i < times.size(); ++i) { - ASSERT_EQ(times2[i].size(), times[i].size()); - for (size_t j = 0; j < times[i].size(); ++j) ASSERT_LE(times2[i][j], times[i][j]); + uint64_t activeSum = 0; + for (size_t i = 0; i < concurrentTimes->active.size(); ++i) { + activeSum += concurrentTimes->active[i]; } + ASSERT_GT(activeSum, (uint64_t)0); + + ASSERT_TRUE(clearUidTimes(uid)); + + auto allTimes = getUidsCpuFreqTimes(); + ASSERT_TRUE(allTimes.has_value()); + ASSERT_FALSE(allTimes->empty()); + ASSERT_EQ(allTimes->find(uid), allTimes->end()); + + auto allConcurrentTimes = getUidsConcurrentTimes(); + ASSERT_TRUE(allConcurrentTimes.has_value()); + ASSERT_FALSE(allConcurrentTimes->empty()); + ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end()); +} + +TEST(TimeInStateTest, GetCpuFreqs) { + auto freqs = getCpuFreqs(); + ASSERT_TRUE(freqs.has_value()); + + auto times = getUidCpuFreqTimes(0); + ASSERT_TRUE(times.has_value()); + + ASSERT_EQ(freqs->size(), times->size()); + for (size_t i = 0; i < freqs->size(); ++i) EXPECT_EQ((*freqs)[i].size(), (*times)[i].size()); } } // namespace bpf diff --git a/libs/dumputils/Android.bp b/libs/dumputils/Android.bp index 3412e14f17..e403d36da1 100644 --- a/libs/dumputils/Android.bp +++ b/libs/dumputils/Android.bp @@ -17,9 +17,7 @@ cc_library { shared_libs: [ "libbase", - "libbinder", "libhidlbase", - "libhidltransport", "liblog", "libutils", ], diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 047780171e..90fded0e85 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -34,7 +34,6 @@ static const char* native_processes_to_dump[] = { "/system/bin/mediametrics", // media.metrics "/system/bin/mediaserver", "/system/bin/netd", - "/system/bin/vold", "/system/bin/sdcard", "/system/bin/statsd", "/system/bin/surfaceflinger", @@ -44,11 +43,19 @@ static const char* native_processes_to_dump[] = { NULL, }; + +// Native processes to dump on debuggable builds. +static const char* debuggable_native_processes_to_dump[] = { + "/system/bin/vold", + NULL, +}; + /* list of hal interface to dump containing process during native dumps */ static const char* hal_interfaces_to_dump[] { "android.hardware.audio@2.0::IDevicesFactory", "android.hardware.audio@4.0::IDevicesFactory", "android.hardware.audio@5.0::IDevicesFactory", + "android.hardware.audio@6.0::IDevicesFactory", "android.hardware.biometrics.face@1.0::IBiometricsFace", "android.hardware.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", @@ -106,6 +113,15 @@ bool should_dump_native_traces(const char* path) { return true; } } + + if (android::base::GetBoolProperty("ro.debuggable", false)) { + for (const char** p = debuggable_native_processes_to_dump; *p; p++) { + if (!strcmp(*p, path)) { + return true; + } + } + } + return false; } diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp new file mode 100644 index 0000000000..de32ff4e64 --- /dev/null +++ b/libs/fakeservicemanager/Android.bp @@ -0,0 +1,25 @@ +cc_defaults { + name: "fakeservicemanager_defaults", + srcs: [ + "ServiceManager.cpp", + ], + + shared_libs: [ + "libbinder", + "libutils", + ], +} + +cc_library { + name: "libfakeservicemanager", + defaults: ["fakeservicemanager_defaults"], +} + +cc_test_host { + name: "fakeservicemanager_test", + defaults: ["fakeservicemanager_defaults"], + srcs: [ + "test_sm.cpp", + ], + static_libs: ["libgmock"], +} diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp new file mode 100644 index 0000000000..69643249e3 --- /dev/null +++ b/libs/fakeservicemanager/ServiceManager.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ServiceManager.h" + +namespace android { + +ServiceManager::ServiceManager() {} + +sp<IBinder> ServiceManager::getService( const String16& name) const { + // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons. + return checkService(name); +} + +sp<IBinder> ServiceManager::checkService( const String16& name) const { + auto it = mNameToService.find(name); + if (it == mNameToService.end()) { + return nullptr; + } + return it->second; +} + +status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service, + bool /*allowIsolated*/, + int /*dumpsysFlags*/) { + mNameToService[name] = service; + return NO_ERROR; +} + +Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) { + Vector<String16> services; + for (auto const& [name, service] : mNameToService) { + (void) service; + services.push_back(name); + } + return services; +} + +IBinder* ServiceManager::onAsBinder() { + return nullptr; +} + +sp<IBinder> ServiceManager::waitForService(const String16& name) { + return checkService(name); +} + +bool ServiceManager::isDeclared(const String16& name) { + return mNameToService.find(name) != mNameToService.end(); +} + +} // namespace android diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h new file mode 100644 index 0000000000..62311d4727 --- /dev/null +++ b/libs/fakeservicemanager/ServiceManager.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IServiceManager.h> + +#include <map> + +namespace android { + +/** + * A local host simple implementation of IServiceManager, that does not + * communicate over binder. +*/ +class ServiceManager : public IServiceManager { +public: + ServiceManager(); + + /** + * Equivalent of checkService. + */ + sp<IBinder> getService( const String16& name) const override; + + /** + * Retrieve an existing service, non-blocking. + */ + sp<IBinder> checkService( const String16& name) const override; + + /** + * Register a service. + */ + status_t addService(const String16& name, const sp<IBinder>& service, + bool allowIsolated = false, + int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) override; + + /** + * Return list of all existing services. + */ + Vector<String16> listServices(int dumpsysFlags = 0) override; + + IBinder* onAsBinder() override; + + /** + * Effectively no-oped in this implementation - equivalent to checkService. + */ + sp<IBinder> waitForService(const String16& name) override; + + /** + * Check if a service is declared (e.g. VINTF manifest). + * + * If this returns true, waitForService should always be able to return the + * service. + */ + bool isDeclared(const String16& name) override; + +private: + std::map<String16, sp<IBinder>> mNameToService; +}; + +} // namespace android diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp new file mode 100644 index 0000000000..71e5abe126 --- /dev/null +++ b/libs/fakeservicemanager/test_sm.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <gmock/gmock.h> + +#include <binder/Binder.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> + +#include "ServiceManager.h" + +using android::sp; +using android::BBinder; +using android::IBinder; +using android::OK; +using android::status_t; +using android::ServiceManager; +using android::String16; +using android::IServiceManager; +using testing::ElementsAre; + +static sp<IBinder> getBinder() { + class LinkableBinder : public BBinder { + status_t linkToDeath(const sp<DeathRecipient>&, void*, uint32_t) override { + // let SM linkToDeath + return OK; + } + }; + + return new LinkableBinder; +} + +TEST(AddService, HappyHappy) { + auto sm = new ServiceManager(); + EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); +} + +TEST(AddService, HappyOverExistingService) { + auto sm = new ServiceManager(); + EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); +} + +TEST(GetService, HappyHappy) { + auto sm = new ServiceManager(); + sp<IBinder> service = getBinder(); + + EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + + EXPECT_EQ(sm->getService(String16("foo")), service); +} + +TEST(GetService, NonExistant) { + auto sm = new ServiceManager(); + + EXPECT_EQ(sm->getService(String16("foo")), nullptr); +} + +TEST(ListServices, AllServices) { + auto sm = new ServiceManager(); + + EXPECT_EQ(sm->addService(String16("sd"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + EXPECT_EQ(sm->addService(String16("sc"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_NORMAL), OK); + EXPECT_EQ(sm->addService(String16("sb"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_HIGH), OK); + EXPECT_EQ(sm->addService(String16("sa"), getBinder(), false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL), OK); + + android::Vector<String16> out = sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL); + + // all there and in the right order + EXPECT_THAT(out, ElementsAre(String16("sa"), String16("sb"), String16("sc"), + String16("sd"))); +} + +TEST(WaitForService, NonExistant) { + auto sm = new ServiceManager(); + + EXPECT_EQ(sm->waitForService(String16("foo")), nullptr); +} + +TEST(WaitForService, HappyHappy) { + auto sm = new ServiceManager(); + sp<IBinder> service = getBinder(); + + EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + + EXPECT_EQ(sm->waitForService(String16("foo")), service); +} + +TEST(IsDeclared, NonExistant) { + auto sm = new ServiceManager(); + + EXPECT_FALSE(sm->isDeclared(String16("foo"))); +} + +TEST(IsDeclared, HappyHappy) { + auto sm = new ServiceManager(); + sp<IBinder> service = getBinder(); + + EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/, + IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); + + EXPECT_TRUE(sm->isDeclared(String16("foo"))); +} diff --git a/libs/gralloc/OWNERS b/libs/gralloc/OWNERS new file mode 100644 index 0000000000..67743cdb0e --- /dev/null +++ b/libs/gralloc/OWNERS @@ -0,0 +1,2 @@ +marissaw@google.com +vhau@google.com diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS new file mode 100644 index 0000000000..c0bb75f982 --- /dev/null +++ b/libs/graphicsenv/OWNERS @@ -0,0 +1,6 @@ +chrisforbes@google.com +cnorthrop@google.com +courtneygo@google.com +lpy@google.com +timvp@google.com +zzyiwei@google.com diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index e3e63ee54e..f3d5aab089 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -15,6 +15,18 @@ cc_library_headers { name: "libgui_headers", vendor_available: true, export_include_dirs: ["include"], + + // we must build this module to get the required header as that is generated + export_shared_lib_headers: [ + "android.hidl.token@1.0-utils", + "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", + ], + shared_libs: [ + "android.hidl.token@1.0-utils", + "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", + ], } cc_library_shared { @@ -154,6 +166,10 @@ cc_defaults { "bufferqueue/2.0/types.cpp", ], + whole_static_libs: [ + "LibGuiProperties", + ], + shared_libs: [ "android.hardware.graphics.bufferqueue@1.0", "android.hardware.graphics.bufferqueue@2.0", @@ -166,8 +182,6 @@ cc_defaults { "libEGL", "libGLESv2", "libhidlbase", - "libhidltransport", - "libhwbinder", "liblog", "libnativewindow", "libsync", @@ -176,6 +190,10 @@ cc_defaults { "libvndksupport", ], + static_libs: [ + "libbinderthreadstateutils", + ], + header_libs: [ "libgui_headers", "libnativebase_headers", diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index a317aaf2f2..4f8eb6b5e8 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -981,6 +981,17 @@ status_t BufferQueueProducer::queueBuffer(int slot, item.mGraphicBuffer.clear(); } + // Update and get FrameEventHistory. + nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC); + NewFrameEventsEntry newFrameEventsEntry = { + currentFrameNumber, + postedTime, + requestedPresentTimestamp, + std::move(acquireFenceTime) + }; + addAndGetFrameTimestamps(&newFrameEventsEntry, + getFrameTimestamps ? &output->frameTimestamps : nullptr); + // Call back without the main BufferQueue lock held, but with the callback // lock held so we can ensure that callbacks occur in order @@ -1010,17 +1021,6 @@ status_t BufferQueueProducer::queueBuffer(int slot, mCallbackCondition.notify_all(); } - // Update and get FrameEventHistory. - nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC); - NewFrameEventsEntry newFrameEventsEntry = { - currentFrameNumber, - postedTime, - requestedPresentTimestamp, - std::move(acquireFenceTime) - }; - addAndGetFrameTimestamps(&newFrameEventsEntry, - getFrameTimestamps ? &output->frameTimestamps : nullptr); - // Wait without lock held if (connectedApi == NATIVE_WINDOW_API_EGL) { // Waiting here allows for two full buffers to be queued but not a diff --git a/libs/gui/BufferQueueThreadState.cpp b/libs/gui/BufferQueueThreadState.cpp index 3b531ec752..c13030b1ed 100644 --- a/libs/gui/BufferQueueThreadState.cpp +++ b/libs/gui/BufferQueueThreadState.cpp @@ -15,6 +15,7 @@ */ #include <binder/IPCThreadState.h> +#include <binderthreadstate/CallerUtils.h> #include <hwbinder/IPCThreadState.h> #include <private/gui/BufferQueueThreadState.h> #include <unistd.h> @@ -22,14 +23,14 @@ namespace android { uid_t BufferQueueThreadState::getCallingUid() { - if (hardware::IPCThreadState::self()->isServingCall()) { + if (getCurrentServingCall() == BinderCallType::HWBINDER) { return hardware::IPCThreadState::self()->getCallingUid(); } return IPCThreadState::self()->getCallingUid(); } pid_t BufferQueueThreadState::getCallingPid() { - if (hardware::IPCThreadState::self()->isServingCall()) { + if (getCurrentServingCall() == BinderCallType::HWBINDER) { return hardware::IPCThreadState::self()->getCallingPid(); } return IPCThreadState::self()->getCallingPid(); diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp index c04d9072bb..3215eca50f 100644 --- a/libs/gui/FrameTimestamps.cpp +++ b/libs/gui/FrameTimestamps.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "FrameEvents" +#include <LibGuiProperties.sysprop.h> #include <android-base/stringprintf.h> #include <cutils/compiler.h> // For CC_[UN]LIKELY #include <inttypes.h> @@ -167,6 +168,11 @@ struct FrameNumberEqual { } // namespace +const size_t FrameEventHistory::MAX_FRAME_HISTORY = + sysprop::LibGuiProperties::frame_event_history_size().value_or(8); + +FrameEventHistory::FrameEventHistory() : mFrames(std::vector<FrameEvents>(MAX_FRAME_HISTORY)) {} + FrameEventHistory::~FrameEventHistory() = default; FrameEvents* FrameEventHistory::getFrame(uint64_t frameNumber) { @@ -348,6 +354,9 @@ std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime( // ConsumerFrameEventHistory // ============================================================================ +ConsumerFrameEventHistory::ConsumerFrameEventHistory() + : mFramesDirty(std::vector<FrameEventDirtyFields>(MAX_FRAME_HISTORY)) {} + ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default; void ConsumerFrameEventHistory::onDisconnect() { @@ -443,9 +452,8 @@ void ConsumerFrameEventHistory::addRelease(uint64_t frameNumber, mFramesDirty[mReleaseOffset].setDirty<FrameEvent::RELEASE>(); } -void ConsumerFrameEventHistory::getFrameDelta( - FrameEventHistoryDelta* delta, - const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame) { +void ConsumerFrameEventHistory::getFrameDelta(FrameEventHistoryDelta* delta, + const std::vector<FrameEvents>::iterator& frame) { mProducerWantsEvents = true; size_t i = static_cast<size_t>(std::distance(mFrames.begin(), frame)); if (mFramesDirty[i].anyDirty()) { diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index e487792c87..12deaf0bd6 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -88,7 +88,7 @@ public: data.writeStrongBinder(applyToken); commands.write(data); data.writeInt64(desiredPresentTime); - data.writeWeakBinder(uncacheBuffer.token); + data.writeStrongBinder(uncacheBuffer.token.promote()); data.writeUint64(uncacheBuffer.id); if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) { @@ -1036,7 +1036,7 @@ status_t BnSurfaceComposer::onTransact( int64_t desiredPresentTime = data.readInt64(); client_cache_t uncachedBuffer; - uncachedBuffer.token = data.readWeakBinder(); + uncachedBuffer.token = data.readStrongBinder(); uncachedBuffer.id = data.readUint64(); std::vector<ListenerCallbacks> listenerCallbacks; diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 6066421faf..42eb9213d6 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -87,7 +87,7 @@ status_t layer_state_t::write(Parcel& output) const colorTransform.asArray(), 16 * sizeof(float)); output.writeFloat(cornerRadius); output.writeBool(hasListenerCallbacks); - output.writeWeakBinder(cachedBuffer.token); + output.writeStrongBinder(cachedBuffer.token.promote()); output.writeUint64(cachedBuffer.id); output.writeParcelable(metadata); @@ -157,7 +157,7 @@ status_t layer_state_t::read(const Parcel& input) colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float)))); cornerRadius = input.readFloat(); hasListenerCallbacks = input.readBool(); - cachedBuffer.token = input.readWeakBinder(); + cachedBuffer.token = input.readStrongBinder(); cachedBuffer.id = input.readUint64(); input.readParcelable(&metadata); diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS index 73150dcb58..c13401dc5c 100644 --- a/libs/gui/OWNERS +++ b/libs/gui/OWNERS @@ -1,7 +1,12 @@ +adyabr@google.com +akrulec@google.com +alecmouri@google.com jessehall@google.com jwcai@google.com lpy@google.com marissaw@google.com mathias@google.com racarr@google.com +steventhomas@google.com stoza@google.com +vhau@google.com diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h index df02494bf4..4670edda99 100644 --- a/libs/gui/include/gui/FrameTimestamps.h +++ b/libs/gui/include/gui/FrameTimestamps.h @@ -106,6 +106,7 @@ struct CompositorTiming { // producer via deltas. class FrameEventHistory { public: + FrameEventHistory(); virtual ~FrameEventHistory(); FrameEvents* getFrame(uint64_t frameNumber); @@ -113,10 +114,10 @@ public: void checkFencesForCompletion(); void dump(std::string& outString) const; - static constexpr size_t MAX_FRAME_HISTORY = 8; + static const size_t MAX_FRAME_HISTORY; protected: - std::array<FrameEvents, MAX_FRAME_HISTORY> mFrames; + std::vector<FrameEvents> mFrames; CompositorTiming mCompositorTiming; }; @@ -204,6 +205,7 @@ private: // The consumer's interface to FrameEventHistory class ConsumerFrameEventHistory : public FrameEventHistory { public: + ConsumerFrameEventHistory(); ~ConsumerFrameEventHistory() override; void onDisconnect(); @@ -224,9 +226,9 @@ public: private: void getFrameDelta(FrameEventHistoryDelta* delta, - const std::array<FrameEvents, MAX_FRAME_HISTORY>::iterator& frame); + const std::vector<FrameEvents>::iterator& frame); - std::array<FrameEventDirtyFields, MAX_FRAME_HISTORY> mFramesDirty; + std::vector<FrameEventDirtyFields> mFramesDirty; size_t mQueueOffset{0}; size_t mCompositionOffset{0}; diff --git a/libs/gui/sysprop/Android.bp b/libs/gui/sysprop/Android.bp new file mode 100644 index 0000000000..e7f7c1fc86 --- /dev/null +++ b/libs/gui/sysprop/Android.bp @@ -0,0 +1,7 @@ +sysprop_library { + name: "LibGuiProperties", + srcs: ["*.sysprop"], + api_packages: ["android.sysprop"], + property_owner: "Platform", + vendor_available: true, +} diff --git a/libs/gui/sysprop/LibGuiProperties.sysprop b/libs/gui/sysprop/LibGuiProperties.sysprop new file mode 100644 index 0000000000..0d54711095 --- /dev/null +++ b/libs/gui/sysprop/LibGuiProperties.sysprop @@ -0,0 +1,25 @@ +# 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. + +module: "android.sysprop.LibGuiProperties" +owner: Platform + +# Indicates how many elements should be present in the frame event histories. +prop { + api_name: "frame_event_history_size" + type: Integer + scope: Public + access: Readonly + prop_name: "ro.lib_gui.frame_event_history_size" +} diff --git a/libs/gui/sysprop/api/LibGuiProperties-current.txt b/libs/gui/sysprop/api/LibGuiProperties-current.txt new file mode 100644 index 0000000000..5b7f74e03e --- /dev/null +++ b/libs/gui/sysprop/api/LibGuiProperties-current.txt @@ -0,0 +1,8 @@ +props { + module: "android.sysprop.LibGuiProperties" + prop { + api_name: "frame_event_history_size" + type: Integer + prop_name: "ro.lib_gui.frame_event_history_size" + } +} diff --git a/libs/gui/sysprop/api/LibGuiProperties-latest.txt b/libs/gui/sysprop/api/LibGuiProperties-latest.txt new file mode 100644 index 0000000000..5b7f74e03e --- /dev/null +++ b/libs/gui/sysprop/api/LibGuiProperties-latest.txt @@ -0,0 +1,8 @@ +props { + module: "android.sysprop.LibGuiProperties" + prop { + api_name: "frame_event_history_size" + type: Integer + prop_name: "ro.lib_gui.frame_event_history_size" + } +} diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index ab6dcaa3f6..cbda6b23ff 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -47,7 +47,6 @@ cc_test { "libcutils", "libgui", "libhidlbase", - "libhidltransport", "libinput", "libui", "libutils", diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 66a8a04c82..03b9cd75db 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -380,6 +380,19 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { bgSurface->expectTap(1, 1); } +TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { + std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); + // In case we pass the very big inset without any checking. + fgSurface->mInputInfo.surfaceInset = INT32_MAX; + fgSurface->showAt(100, 100); + + fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); + + // expect no crash for overflow, and inset size to be clamped to surface size + injectTap(202, 202); + fgSurface->expectTap(1, 1); +} + // Ensure we ignore transparent region when getting screen bounds when positioning input frame. TEST_F(InputSurfacesTest, input_ignores_transparent_region) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 5a60347ed3..ec28757933 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -99,7 +99,7 @@ status_t InputWindowInfo::write(Parcel& output) const { applicationInfo.write(output); output.write(touchableRegion); output.writeBool(replaceTouchableRegionWithCrop); - output.writeWeakBinder(touchableRegionCropHandle); + output.writeStrongBinder(touchableRegionCropHandle.promote()); return OK; } @@ -142,7 +142,7 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { ret.applicationInfo = InputApplicationInfo::read(from); from.read(ret.touchableRegion); ret.replaceTouchableRegionWithCrop = from.readBool(); - ret.touchableRegionCropHandle = from.readWeakBinder(); + ret.touchableRegionCropHandle = from.readStrongBinder(); return ret; } diff --git a/libs/math/include/math/quat.h b/libs/math/include/math/quat.h index 1936a2baec..07573c5ecf 100644 --- a/libs/math/include/math/quat.h +++ b/libs/math/include/math/quat.h @@ -109,7 +109,7 @@ public: // initialize from 4 values to w + xi + yj + zk template<typename A, typename B, typename C, typename D> - constexpr TQuaternion(A w, B x, C y, D z) : x(x), y(y), z(z), w(w) { } + constexpr TQuaternion(A w, B x, C y, D z) : x(static_cast<T>(x)), y(static_cast<T>(y)), z(static_cast<T>(z)), w(static_cast<T>(w)) { } // initialize from a vec3 + a value to : v.xi + v.yj + v.zk + w template<typename A, typename B> diff --git a/libs/math/include/math/vec2.h b/libs/math/include/math/vec2.h index a34763347c..e0adb7f6cc 100644 --- a/libs/math/include/math/vec2.h +++ b/libs/math/include/math/vec2.h @@ -89,7 +89,7 @@ public: constexpr TVec2(A v) : x(v), y(v) { } template<typename A, typename B> - constexpr TVec2(A x, B y) : x(x), y(y) { } + constexpr TVec2(A x, B y) : x(static_cast<T>(x)), y(static_cast<T>(y)) { } template<typename A> explicit diff --git a/libs/math/include/math/vec3.h b/libs/math/include/math/vec3.h index 009fd84e3b..21fb684efc 100644 --- a/libs/math/include/math/vec3.h +++ b/libs/math/include/math/vec3.h @@ -86,13 +86,13 @@ public: // handles implicit conversion to a tvec4. must not be explicit. template<typename A, typename = typename std::enable_if<std::is_arithmetic<A>::value >::type> - constexpr TVec3(A v) : x(v), y(v), z(v) { } + constexpr TVec3(A v) : x(static_cast<T>(v)), y(static_cast<T>(v)), z(static_cast<T>(v)) { } template<typename A, typename B, typename C> - constexpr TVec3(A x, B y, C z) : x(x), y(y), z(z) { } + constexpr TVec3(A x, B y, C z) : x(static_cast<T>(x)), y(static_cast<T>(y)), z(static_cast<T>(z)) { } template<typename A, typename B> - constexpr TVec3(const TVec2<A>& v, B z) : x(v.x), y(v.y), z(z) { } + constexpr TVec3(const TVec2<A>& v, B z) : x(v.x), y(v.y), z(static_cast<T>(z)) { } template<typename A> explicit diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index 9bd30955f3..1ec73ce961 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -266,10 +266,10 @@ int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int so char buf[CMSG_SPACE(kFdBufferSize)]; struct msghdr msg = { - .msg_control = buf, - .msg_controllen = sizeof(buf), .msg_iov = &iov[0], .msg_iovlen = 1, + .msg_control = buf, + .msg_controllen = sizeof(buf), }; struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); @@ -306,10 +306,10 @@ int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** out iov[0].iov_len = kMessageBufferSize; struct msghdr msg = { - .msg_control = fdBuf, - .msg_controllen = sizeof(fdBuf), .msg_iov = &iov[0], .msg_iovlen = 1, + .msg_control = fdBuf, + .msg_controllen = sizeof(fdBuf), }; int result; diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index 27ab482676..55400c7b32 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -85,6 +85,11 @@ cc_library { export_header_lib_headers: [ "libnativebase_headers", ], + + stubs: { + symbol_file: "libnativewindow.map.txt", + versions: ["29"], + }, } llndk_library { diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h index da959e36d2..ae5e47ba97 100644 --- a/libs/nativewindow/include/android/hardware_buffer.h +++ b/libs/nativewindow/include/android/hardware_buffer.h @@ -342,6 +342,8 @@ typedef struct AHardwareBuffer AHardwareBuffer; * not compatible with its usage flags, the results are undefined and * may include program termination. * + * Available since API level 26. + * * \return 0 on success, or an error number of the allocation fails for * any reason. The returned buffer has a reference count of 1. */ @@ -352,18 +354,24 @@ int AHardwareBuffer_allocate(const AHardwareBuffer_Desc* desc, * * This prevents the object from being deleted until the last reference * is removed. + * + * Available since API level 26. */ void AHardwareBuffer_acquire(AHardwareBuffer* buffer) __INTRODUCED_IN(26); /** * Remove a reference that was previously acquired with * AHardwareBuffer_acquire() or AHardwareBuffer_allocate(). + * + * Available since API level 26. */ void AHardwareBuffer_release(AHardwareBuffer* buffer) __INTRODUCED_IN(26); /** * Return a description of the AHardwareBuffer in the passed * AHardwareBuffer_Desc struct. + * + * Available since API level 26. */ void AHardwareBuffer_describe(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc) __INTRODUCED_IN(26); @@ -413,6 +421,8 @@ void AHardwareBuffer_describe(const AHardwareBuffer* buffer, * simultaneously, and the contents of the buffer behave like shared * memory. * + * Available since API level 26. + * * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer * has more than one layer. Error number if the lock fails for any other @@ -441,6 +451,8 @@ int AHardwareBuffer_lock(AHardwareBuffer* buffer, uint64_t usage, * * See the AHardwareBuffer_lock documentation for all other locking semantics. * + * Available since API level 29. + * * \return 0 on success. -EINVAL if \a buffer is NULL, the usage flags * are not a combination of AHARDWAREBUFFER_USAGE_CPU_*, or the buffer * has more than one layer. Error number if the lock fails for any other @@ -462,6 +474,8 @@ int AHardwareBuffer_lockPlanes(AHardwareBuffer* buffer, uint64_t usage, * completed before the function returned and no further operations are * necessary. * + * Available since API level 26. + * * \return 0 on success. -EINVAL if \a buffer is NULL. Error number if * the unlock fails for any reason. */ @@ -470,6 +484,8 @@ int AHardwareBuffer_unlock(AHardwareBuffer* buffer, int32_t* fence) __INTRODUCED /** * Send the AHardwareBuffer to an AF_UNIX socket. * + * Available since API level 26. + * * \return 0 on success, -EINVAL if \a buffer is NULL, or an error * number if the operation fails for any reason. */ @@ -478,6 +494,8 @@ int AHardwareBuffer_sendHandleToUnixSocket(const AHardwareBuffer* buffer, int so /** * Receive an AHardwareBuffer from an AF_UNIX socket. * + * Available since API level 26. + * * \return 0 on success, -EINVAL if \a outBuffer is NULL, or an error * number if the operation fails for any reason. */ @@ -501,6 +519,8 @@ int AHardwareBuffer_recvHandleFromUnixSocket(int socketFd, AHardwareBuffer** out * some implementations have implementation-defined limits on texture * size and layer count. * + * Available since API level 29. + * * \return 1 if the format and usage flag combination is allocatable, * 0 otherwise. */ @@ -514,6 +534,8 @@ int AHardwareBuffer_isSupported(const AHardwareBuffer_Desc* desc) __INTRODUCED_I * of the locked buffer. If the bytes per pixel or bytes per stride are unknown * or variable, or if the underlying mapper implementation does not support returning * additional information, then this call will fail with INVALID_OPERATION + * + * Available since API level 29. */ int AHardwareBuffer_lockAndGetInfo(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, void** outVirtualAddress, diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index 6730596ec7..3e436e3b07 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -189,6 +189,8 @@ int32_t ANativeWindow_unlockAndPost(ANativeWindow* window); /** * Set a transform that will be applied to future buffers posted to the window. * + * Available since API level 26. + * * \param transform combination of {@link ANativeWindowTransform} flags * \return 0 for success, or -EINVAL if \p transform is invalid */ @@ -208,6 +210,8 @@ int32_t ANativeWindow_setBuffersTransform(ANativeWindow* window, int32_t transfo * measurement data instead of color images. The default dataSpace is 0, * ADATASPACE_UNKNOWN, unless it has been overridden by the producer. * + * Available since API level 28. + * * \param dataSpace data space of all buffers queued after this call. * \return 0 for success, -EINVAL if window is invalid or the dataspace is not * supported. @@ -216,6 +220,9 @@ int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpa /** * Get the dataspace of the buffers in window. + * + * Available since API level 28. + * * \return the dataspace of buffers in window, ADATASPACE_UNKNOWN is returned if * dataspace is unknown, or -EINVAL if window is invalid. */ diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index bad8b11540..db1c9b784e 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -2,9 +2,9 @@ LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; - AHardwareBuffer_createFromHandle; # vndk + AHardwareBuffer_createFromHandle; # llndk # apex AHardwareBuffer_describe; - AHardwareBuffer_getNativeHandle; # vndk + AHardwareBuffer_getNativeHandle; # llndk # apex AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; AHardwareBuffer_lockAndGetInfo; # introduced=29 @@ -13,32 +13,32 @@ LIBNATIVEWINDOW { AHardwareBuffer_release; AHardwareBuffer_sendHandleToUnixSocket; AHardwareBuffer_unlock; - ANativeWindowBuffer_getHardwareBuffer; # vndk - ANativeWindow_OemStorageGet; # vndk - ANativeWindow_OemStorageSet; # vndk + ANativeWindowBuffer_getHardwareBuffer; # llndk + ANativeWindow_OemStorageGet; # llndk + ANativeWindow_OemStorageSet; # llndk ANativeWindow_acquire; - ANativeWindow_cancelBuffer; # vndk - ANativeWindow_dequeueBuffer; # vndk + ANativeWindow_cancelBuffer; # llndk + ANativeWindow_dequeueBuffer; # llndk ANativeWindow_getBuffersDataSpace; # introduced=28 ANativeWindow_getFormat; ANativeWindow_getHeight; ANativeWindow_getWidth; ANativeWindow_lock; - ANativeWindow_query; # vndk - ANativeWindow_queryf; # vndk - ANativeWindow_queueBuffer; # vndk + ANativeWindow_query; # llndk + ANativeWindow_queryf; # llndk + ANativeWindow_queueBuffer; # llndk ANativeWindow_release; - ANativeWindow_setAutoRefresh; # vndk - ANativeWindow_setBufferCount; # vndk + ANativeWindow_setAutoRefresh; # llndk + ANativeWindow_setBufferCount; # llndk ANativeWindow_setBuffersDataSpace; # introduced=28 - ANativeWindow_setBuffersDimensions; # vndk - ANativeWindow_setBuffersFormat; # vndk + ANativeWindow_setBuffersDimensions; # llndk + ANativeWindow_setBuffersFormat; # llndk ANativeWindow_setBuffersGeometry; - ANativeWindow_setBuffersTimestamp; # vndk + ANativeWindow_setBuffersTimestamp; # llndk ANativeWindow_setBuffersTransform; - ANativeWindow_setSharedBufferMode; # vndk - ANativeWindow_setSwapInterval; # vndk - ANativeWindow_setUsage; # vndk + ANativeWindow_setSharedBufferMode; # llndk + ANativeWindow_setSwapInterval; # llndk + ANativeWindow_setUsage; # llndk ANativeWindow_unlockAndPost; local: *; diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp index cc2731d908..71b1f9f021 100644 --- a/libs/nativewindow/tests/AHardwareBufferTest.cpp +++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp @@ -20,6 +20,7 @@ #include <android/hardware_buffer.h> #include <private/android/AHardwareBufferHelpers.h> #include <android/hardware/graphics/common/1.0/types.h> +#include <vndk/hardware_buffer.h> #include <gtest/gtest.h> @@ -100,9 +101,33 @@ TEST(AHardwareBufferTest, ConvertToAndFromGrallocBits) { (uint64_t)BufferUsage::CPU_WRITE_RARELY, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY)); -EXPECT_TRUE(TestUsageConversion( + EXPECT_TRUE(TestUsageConversion( (uint64_t)BufferUsage::GPU_RENDER_TARGET | (uint64_t)BufferUsage::GPU_TEXTURE | - 1ull << 29 | 1ull << 57, + 1ull << 29 | 1ull << 57, AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_VENDOR_1 | AHARDWAREBUFFER_USAGE_VENDOR_13)); } + +TEST(AHardwareBufferTest, GetCreateHandleTest) { + AHardwareBuffer_Desc desc{ + .width = 64, + .height = 1, + .layers = 1, + .format = AHARDWAREBUFFER_FORMAT_BLOB, + .usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, + .stride = 64, + }; + + AHardwareBuffer* buffer = nullptr; + EXPECT_EQ(0, AHardwareBuffer_allocate(&desc, &buffer)); + const native_handle_t* handle = AHardwareBuffer_getNativeHandle(buffer); + EXPECT_NE(nullptr, handle); + + AHardwareBuffer* otherBuffer = nullptr; + EXPECT_EQ(0, AHardwareBuffer_createFromHandle( + &desc, handle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, &otherBuffer)); + EXPECT_NE(nullptr, otherBuffer); + + AHardwareBuffer_release(buffer); + AHardwareBuffer_release(otherBuffer); +} diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index 940ff5afbc..e8154a6931 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -21,22 +21,7 @@ cc_library_shared { "-Werror", ], cppflags: [ - "-Weverything", - - // The static constructors and destructors in this library have not been noted to - // introduce significant overheads - "-Wno-exit-time-destructors", - "-Wno-global-constructors", - - // We only care about compiling as C++14 - "-Wno-c++98-compat-pedantic", - - // android/sensors.h uses nested anonymous unions and anonymous structs - "-Wno-nested-anon-types", - "-Wno-gnu-anonymous-struct", - - // Don't warn about struct padding - "-Wno-padded", + "-Wextra", ], srcs: [ diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS index 81099e895c..90c233030e 100644 --- a/libs/sensor/OWNERS +++ b/libs/sensor/OWNERS @@ -1,3 +1,3 @@ arthuri@google.com bduddie@google.com -bstack@google.com +stange@google.com diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index 96d5eb9d1f..bf8b9f73fe 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -150,7 +150,7 @@ status_t SensorManager::assertStateLocked() { class DeathObserver : public IBinder::DeathRecipient { SensorManager& mSensorManager; virtual void binderDied(const wp<IBinder>& who) { - ALOGW("sensorservice died [%p]", who.unsafe_get()); + ALOGW("sensorservice died [%p]", static_cast<void*>(who.unsafe_get())); mSensorManager.sensorManagerDied(); } public: diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp index e0e3469fbb..4a606ffec2 100644 --- a/libs/sensorprivacy/Android.bp +++ b/libs/sensorprivacy/Android.bp @@ -28,8 +28,7 @@ cc_library_shared { ], srcs: [ - "aidl/android/hardware/ISensorPrivacyListener.aidl", - "aidl/android/hardware/ISensorPrivacyManager.aidl", + ":libsensorprivacy_aidl", "SensorPrivacyManager.cpp", ], @@ -45,3 +44,12 @@ cc_library_shared { export_shared_lib_headers: ["libbinder"], } + +filegroup { + name: "libsensorprivacy_aidl", + srcs: [ + "aidl/android/hardware/ISensorPrivacyListener.aidl", + "aidl/android/hardware/ISensorPrivacyManager.aidl", + ], + path: "aidl", +} diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 0407d8802c..080336b890 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -26,31 +26,12 @@ cc_library_shared { "-Werror", ], cppflags: [ - "-Weverything", - - // The static constructors and destructors in this library have not been noted to - // introduce significant overheads - "-Wno-exit-time-destructors", - "-Wno-global-constructors", - - // We only care about compiling as C++14 - "-Wno-c++98-compat-pedantic", - - // We are aware of the risks inherent in comparing floats for equality - "-Wno-float-equal", - - // We use four-character constants for the GraphicBuffer header, and don't care - // that they're non-portable as long as they're consistent within one execution - "-Wno-four-char-constants", - - // Don't warn about struct padding - "-Wno-padded", - - "-Wno-switch-enum", + "-Wextra", ], sanitize: { integer_overflow: true, + misc_undefined: ["bounds"], }, srcs: [ @@ -95,10 +76,7 @@ cc_library_shared { "android.hardware.graphics.mapper@3.0", "libbase", "libcutils", - "libhardware", "libhidlbase", - "libhidltransport", - "libhwbinder", "libsync", "libutils", "liblog", diff --git a/libs/ui/ColorSpace.cpp b/libs/ui/ColorSpace.cpp index 7a14af1c9d..df390e2588 100644 --- a/libs/ui/ColorSpace.cpp +++ b/libs/ui/ColorSpace.cpp @@ -364,7 +364,11 @@ std::unique_ptr<float3[]> ColorSpace::createLUT(uint32_t size, const ColorSpace& for (uint32_t z = 0; z < size; z++) { for (int32_t y = int32_t(size - 1); y >= 0; y--) { for (uint32_t x = 0; x < size; x++) { - *data++ = connector.transform({x * m, y * m, z * m}); + *data++ = connector.transform({ + static_cast<float>(x) * m, + static_cast<float>(y) * m, + static_cast<float>(z) * m, + }); } } } diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index d588346dfb..b54ce0fcf7 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -61,9 +61,9 @@ GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper:: GraphicBufferAllocator::~GraphicBufferAllocator() {} -size_t GraphicBufferAllocator::getTotalSize() const { +uint64_t GraphicBufferAllocator::getTotalSize() const { Mutex::Autolock _l(sLock); - size_t total = 0; + uint64_t total = 0; for (size_t i = 0; i < sAllocList.size(); ++i) { total += sAllocList.valueAt(i).size; } @@ -73,7 +73,7 @@ size_t GraphicBufferAllocator::getTotalSize() const { void GraphicBufferAllocator::dump(std::string& result) const { Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); - size_t total = 0; + uint64_t total = 0; result.append("Allocated buffers:\n"); const size_t c = list.size(); for (size_t i=0 ; i<c ; i++) { @@ -81,7 +81,7 @@ void GraphicBufferAllocator::dump(std::string& result) const { if (rec.size) { StringAppendF(&result, "%10p: %7.2f KiB | %4u (%4u) x %4u | %4u | %8X | 0x%" PRIx64 " | %s\n", - list.keyAt(i), rec.size / 1024.0, rec.width, rec.stride, rec.height, + list.keyAt(i), static_cast<double>(rec.size) / 1024.0, rec.width, rec.stride, rec.height, rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str()); } else { StringAppendF(&result, @@ -91,7 +91,7 @@ void GraphicBufferAllocator::dump(std::string& result) const { } total += rec.size; } - StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", total / 1024.0); + StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", static_cast<double>(total) / 1024.0); result.append(mAllocator->dumpDebugInfo()); } diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index 55e3b99aa1..1222cd6fad 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -339,10 +339,10 @@ Region& Region::scaleSelf(float sx, float sy) { size_t count = mStorage.size(); Rect* rects = mStorage.editArray(); while (count) { - rects->left = static_cast<int32_t>(rects->left * sx + 0.5f); - rects->right = static_cast<int32_t>(rects->right * sx + 0.5f); - rects->top = static_cast<int32_t>(rects->top * sy + 0.5f); - rects->bottom = static_cast<int32_t>(rects->bottom * sy + 0.5f); + rects->left = static_cast<int32_t>(static_cast<float>(rects->left) * sx + 0.5f); + rects->right = static_cast<int32_t>(static_cast<float>(rects->right) * sx + 0.5f); + rects->top = static_cast<int32_t>(static_cast<float>(rects->top) * sy + 0.5f); + rects->bottom = static_cast<int32_t>(static_cast<float>(rects->bottom) * sy + 0.5f); rects++; count--; } diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h index 324d9e143b..06357270f5 100644 --- a/libs/ui/include/ui/GraphicBufferAllocator.h +++ b/libs/ui/include/ui/GraphicBufferAllocator.h @@ -49,7 +49,7 @@ public: status_t free(buffer_handle_t handle); - size_t getTotalSize() const; + uint64_t getTotalSize() const; void dump(std::string& res) const; static void dumpToSystemLog(); diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h index c39d8af1d6..d9b713df4c 100644 --- a/libs/ui/include/ui/Size.h +++ b/libs/ui/include/ui/Size.h @@ -132,7 +132,7 @@ struct Size { // Otherwise we leverage implicit conversion to safely compare values of // different types, to ensure we return a value clamped to the range of // ToType. - return v < toLowest ? toLowest : (v > toHighest ? toHighest : static_cast<ToType>(v)); + return v < toLowest ? toLowest : (static_cast<ToType>(v) > toHighest ? toHighest : static_cast<ToType>(v)); } }; diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 15f4b6407c..0a07a48bd1 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -58,7 +58,6 @@ cc_test { "android.frameworks.bufferhub@1.0", "libcutils", "libhidlbase", - "libhwbinder", "libui", "libutils", ], @@ -79,7 +78,6 @@ cc_test { "android.frameworks.bufferhub@1.0", "libcutils", "libhidlbase", - "libhwbinder", "liblog", "libui", "libutils" diff --git a/libs/vr/OWNERS b/libs/vr/OWNERS index ec2d712ffd..098c80e557 100644 --- a/libs/vr/OWNERS +++ b/libs/vr/OWNERS @@ -1,4 +1,4 @@ hendrikw@google.com jwcai@google.com steventhomas@google.com - +patplunkett@google.com diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp index 6d202aec05..2fcee7bee6 100644 --- a/libs/vr/libbufferhub/Android.bp +++ b/libs/vr/libbufferhub/Android.bp @@ -29,11 +29,9 @@ sourceFiles = [ sharedLibraries = [ "libbase", "libcutils", - "libhardware", "liblog", "libui", "libutils", - "libnativewindow", "libpdx_default_transport", ] diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp index 9f72c05f0c..77c79112de 100644 --- a/libs/vr/libbufferhubqueue/Android.bp +++ b/libs/vr/libbufferhubqueue/Android.bp @@ -26,10 +26,8 @@ staticLibraries = [ ] sharedLibraries = [ - "libbase", "libbinder", "libcutils", - "libhardware", "liblog", "libui", "libutils", diff --git a/libs/vr/libdisplay/vsync_service.cpp b/libs/vr/libdisplay/vsync_service.cpp index 4668b9836c..04d4f30140 100644 --- a/libs/vr/libdisplay/vsync_service.cpp +++ b/libs/vr/libdisplay/vsync_service.cpp @@ -45,8 +45,8 @@ public: ALOGE("onVsync failed to writeInt64: %d", result); return result; } - result = remote()->transact( - BnVsyncCallback::ON_VSYNC, data, &reply, TF_ONE_WAY); + result = remote()->transact(BnVsyncCallback::ON_VSYNC, data, &reply, + IBinder::FLAG_ONEWAY); if (result != OK) { ALOGE("onVsync failed to transact: %d", result); return result; diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp index 1a9d7274a1..23a4224e05 100644 --- a/libs/vr/libpdx/Android.bp +++ b/libs/vr/libpdx/Android.bp @@ -53,6 +53,8 @@ cc_test { "libpdx", "liblog", "libutils", + ], + shared_libs: [ "libvndksupport", ], } diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp index 1176abf552..1ce9c991d5 100644 --- a/libs/vr/libpdx_default_transport/Android.bp +++ b/libs/vr/libpdx_default_transport/Android.bp @@ -39,7 +39,6 @@ cc_library_shared { "libcutils", "liblog", "libutils", - "libcrypto", "libselinux", ], } diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp index 282935307c..12ce74b1ea 100644 --- a/libs/vr/libvrflinger/Android.bp +++ b/libs/vr/libvrflinger/Android.bp @@ -57,7 +57,6 @@ sharedLibraries = [ "libgui", "libsync", "libhidlbase", - "libhidltransport", "libfmq", "libpdx_default_transport", ] diff --git a/opengl/OWNERS b/opengl/OWNERS index 881f1b8f35..b505712eb5 100644 --- a/opengl/OWNERS +++ b/opengl/OWNERS @@ -1,16 +1,7 @@ -# alanward@google.com -chiur@google.com chrisforbes@google.com cnorthrop@google.com courtneygo@google.com -hliatis@google.com ianelliott@google.com jessehall@google.com lpy@google.com -marissaw@google.com -nduca@google.com -pmuetschard@google.com -timvp@google.com -tobine@google.com -vhau@google.com zzyiwei@google.com diff --git a/opengl/libagl/Android.bp b/opengl/libagl/Android.bp index 6ec24b3712..f5bf015bad 100644 --- a/opengl/libagl/Android.bp +++ b/opengl/libagl/Android.bp @@ -25,8 +25,9 @@ cc_defaults { "libnativewindow", ], - // we need to access the private Bionic header <bionic_tls.h> - include_dirs: ["bionic/libc/private"], + header_libs: [ + "bionic_libc_platform_headers", + ], arch: { arm: { diff --git a/opengl/libagl/context.h b/opengl/libagl/context.h index 18ef7d5716..6e77a2366f 100644 --- a/opengl/libagl/context.h +++ b/opengl/libagl/context.h @@ -22,7 +22,7 @@ #include <sys/types.h> #include <pthread.h> #ifdef __ANDROID__ -#include <bionic_tls.h> +#include <bionic/tls.h> #endif #include <private/pixelflinger/ggl_context.h> diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index abc7a72716..3c741ab4fb 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -72,15 +72,13 @@ cc_defaults { "libarect", ], header_libs: [ + "bionic_libc_platform_headers", "gl_headers", "libsystem_headers", "libhardware_headers", "libnativebase_headers", ], export_header_lib_headers: ["gl_headers"], - - // we need to access the private Bionic header <bionic_tls.h> - include_dirs: ["bionic/libc/private"], } //############################################################################## @@ -153,7 +151,6 @@ cc_library_shared { "android.hardware.configstore-utils", "libbase", "libhidlbase", - "libhidltransport", "libnativebridge_lazy", "libnativeloader_lazy", "libutils", @@ -164,6 +161,10 @@ cc_library_shared { ], ldflags: ["-Wl,--exclude-libs=ALL"], export_include_dirs: ["EGL/include"], + stubs: { + symbol_file: "libEGL.map.txt", + versions: ["29"], + }, } cc_test { diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 29a966d348..c51a1295e7 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -40,7 +40,6 @@ static inline void clearError() { EGLDisplay eglGetDisplay(EGLNativeDisplayType display) { ATRACE_CALL(); - clearError(); if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); @@ -48,6 +47,7 @@ EGLDisplay eglGetDisplay(EGLNativeDisplayType display) { // Call down the chain, which usually points directly to the impl // but may also be routed through layers + clearError(); egl_connection_t* const cnx = &gEGLImpl; return cnx->platform.eglGetDisplay(display); } @@ -55,7 +55,6 @@ EGLDisplay eglGetDisplay(EGLNativeDisplayType display) { EGLDisplay eglGetPlatformDisplay(EGLenum platform, EGLNativeDisplayType display, const EGLAttrib* attrib_list) { ATRACE_CALL(); - clearError(); if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY); @@ -63,6 +62,7 @@ EGLDisplay eglGetPlatformDisplay(EGLenum platform, EGLNativeDisplayType display, // Call down the chain, which usually points directly to the impl // but may also be routed through layers + clearError(); egl_connection_t* const cnx = &gEGLImpl; return cnx->platform.eglGetPlatformDisplay(platform, display, attrib_list); } @@ -239,13 +239,12 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char* procname) // in which case we must make sure we've initialized ourselves, this // happens the first time egl_get_display() is called. - clearError(); - if (egl_init_drivers() == EGL_FALSE) { setError(EGL_BAD_PARAMETER, NULL); return nullptr; } + clearError(); egl_connection_t* const cnx = &gEGLImpl; return cnx->platform.eglGetProcAddress(procname); } @@ -324,23 +323,21 @@ EGLBoolean eglWaitClient(void) { } EGLBoolean eglBindAPI(EGLenum api) { - clearError(); - if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); } + clearError(); egl_connection_t* const cnx = &gEGLImpl; return cnx->platform.eglBindAPI(api); } EGLenum eglQueryAPI(void) { - clearError(); - if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); } + clearError(); egl_connection_t* const cnx = &gEGLImpl; return cnx->platform.eglQueryAPI(); } @@ -595,23 +592,21 @@ EGLClientBuffer eglGetNativeClientBufferANDROID(const AHardwareBuffer* buffer) { } EGLuint64NV eglGetSystemTimeFrequencyNV() { - clearError(); - if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE); } + clearError(); egl_connection_t* const cnx = &gEGLImpl; return cnx->platform.eglGetSystemTimeFrequencyNV(); } EGLuint64NV eglGetSystemTimeNV() { - clearError(); - if (egl_init_drivers() == EGL_FALSE) { return setError(EGL_BAD_PARAMETER, (EGLuint64NV)EGL_FALSE); } + clearError(); egl_connection_t* const cnx = &gEGLImpl; return cnx->platform.eglGetSystemTimeNV(); } diff --git a/opengl/libs/EGL/egl_layers.cpp b/opengl/libs/EGL/egl_layers.cpp index ba7cacdbf2..408e76b0ce 100644 --- a/opengl/libs/EGL/egl_layers.cpp +++ b/opengl/libs/EGL/egl_layers.cpp @@ -379,14 +379,12 @@ void LayerLoader::LoadLayers() { // any symbol dependencies will be resolved by system libraries. They // can't safely use libc++_shared, for example. Which is one reason // (among several) we only allow them in non-user builds. - void* handle = nullptr; auto app_namespace = android::GraphicsEnv::getInstance().getAppNamespace(); if (app_namespace && !android::base::StartsWith(layer, kSystemLayerLibraryDir)) { - bool native_bridge = false; char* error_message = nullptr; - handle = OpenNativeLibraryInNamespace( - app_namespace, layer.c_str(), &native_bridge, &error_message); - if (!handle) { + dlhandle_ = OpenNativeLibraryInNamespace( + app_namespace, layer.c_str(), &native_bridge_, &error_message); + if (!dlhandle_) { ALOGE("Failed to load layer %s with error: %s", layer.c_str(), error_message); android::NativeLoaderFreeErrorMessage(error_message); @@ -394,11 +392,11 @@ void LayerLoader::LoadLayers() { } } else { - handle = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL); + dlhandle_ = dlopen(layer.c_str(), RTLD_NOW | RTLD_LOCAL); } - if (handle) { - ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)handle, + if (dlhandle_) { + ALOGV("Loaded layer handle (%llu) for layer %s", (unsigned long long)dlhandle_, layers[i].c_str()); } else { // If the layer is found but can't be loaded, try setenforce 0 @@ -411,8 +409,7 @@ void LayerLoader::LoadLayers() { std::string init_func = "AndroidGLESLayer_Initialize"; ALOGV("Looking for entrypoint %s", init_func.c_str()); - layer_init_func LayerInit = - reinterpret_cast<layer_init_func>(dlsym(handle, init_func.c_str())); + layer_init_func LayerInit = GetTrampoline<layer_init_func>(init_func.c_str()); if (LayerInit) { ALOGV("Found %s for layer %s", init_func.c_str(), layer.c_str()); layer_init_.push_back(LayerInit); @@ -425,8 +422,7 @@ void LayerLoader::LoadLayers() { std::string setup_func = "AndroidGLESLayer_GetProcAddress"; ALOGV("Looking for entrypoint %s", setup_func.c_str()); - layer_setup_func LayerSetup = - reinterpret_cast<layer_setup_func>(dlsym(handle, setup_func.c_str())); + layer_setup_func LayerSetup = GetTrampoline<layer_setup_func>(setup_func.c_str()); if (LayerSetup) { ALOGV("Found %s for layer %s", setup_func.c_str(), layer.c_str()); layer_setup_.push_back(LayerSetup); diff --git a/opengl/libs/EGL/egl_layers.h b/opengl/libs/EGL/egl_layers.h index e401b448cf..1e2783fc98 100644 --- a/opengl/libs/EGL/egl_layers.h +++ b/opengl/libs/EGL/egl_layers.h @@ -21,10 +21,15 @@ #include <unordered_map> #include <vector> -#include <EGL/egldefs.h> +#include <android/dlext.h> +#include <dlfcn.h> +#include <EGL/egldefs.h> #include "egl_platform_entries.h" +#include <nativebridge/native_bridge.h> +#include <nativeloader/native_loader.h> + typedef __eglMustCastToProperFunctionPointerType EGLFuncPointer; namespace android { @@ -54,10 +59,21 @@ public: std::vector<layer_setup_func> layer_setup_; private: - LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0){}; + LayerLoader() : layers_loaded_(false), initialized_(false), current_layer_(0), dlhandle_(nullptr), native_bridge_(false){}; bool layers_loaded_; bool initialized_; unsigned current_layer_; + void* dlhandle_; + bool native_bridge_; + + template<typename Func = void*> + Func GetTrampoline(const char* name) const { + if (native_bridge_) { + return reinterpret_cast<Func>(android::NativeBridgeGetTrampoline( + dlhandle_, name, nullptr, 0)); + } + return reinterpret_cast<Func>(dlsym(dlhandle_, name)); + } }; }; // namespace android diff --git a/opengl/libs/ETC1/etc1.cpp b/opengl/libs/ETC1/etc1.cpp index 97d10851de..19d428a394 100644 --- a/opengl/libs/ETC1/etc1.cpp +++ b/opengl/libs/ETC1/etc1.cpp @@ -378,34 +378,30 @@ static void etc_encodeBaseColors(etc1_byte* pBaseColors, const etc1_byte* pColors, etc_compressed* pCompressed) { int r1, g1, b1, r2, g2, b2; // 8 bit base colors for sub-blocks bool differential; - { - int r51 = convert8To5(pColors[0]); - int g51 = convert8To5(pColors[1]); - int b51 = convert8To5(pColors[2]); - int r52 = convert8To5(pColors[3]); - int g52 = convert8To5(pColors[4]); - int b52 = convert8To5(pColors[5]); - - r1 = convert5To8(r51); - g1 = convert5To8(g51); - b1 = convert5To8(b51); - - int dr = r52 - r51; - int dg = g52 - g51; - int db = b52 - b51; - - differential = inRange4bitSigned(dr) && inRange4bitSigned(dg) - && inRange4bitSigned(db); - if (differential) { - r2 = convert5To8(r51 + dr); - g2 = convert5To8(g51 + dg); - b2 = convert5To8(b51 + db); - pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19) - | ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2; - } - } - - if (!differential) { + int r51 = convert8To5(pColors[0]); + int g51 = convert8To5(pColors[1]); + int b51 = convert8To5(pColors[2]); + int r52 = convert8To5(pColors[3]); + int g52 = convert8To5(pColors[4]); + int b52 = convert8To5(pColors[5]); + + r1 = convert5To8(r51); + g1 = convert5To8(g51); + b1 = convert5To8(b51); + + int dr = r52 - r51; + int dg = g52 - g51; + int db = b52 - b51; + + differential = inRange4bitSigned(dr) && inRange4bitSigned(dg) + && inRange4bitSigned(db); + if (differential) { + r2 = convert5To8(r51 + dr); + g2 = convert5To8(g51 + dg); + b2 = convert5To8(b51 + db); + pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19) + | ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2; + } else { int r41 = convert8To4(pColors[0]); int g41 = convert8To4(pColors[1]); int b41 = convert8To4(pColors[2]); diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h index 63a0e140cc..86fec21bae 100644 --- a/opengl/libs/hooks.h +++ b/opengl/libs/hooks.h @@ -46,7 +46,7 @@ #define MAX_NUMBER_OF_GL_EXTENSIONS 256 -#include <bionic_tls.h> /* special private C library header */ +#include <bionic/tls.h> /* special private C library header */ // ---------------------------------------------------------------------------- namespace android { diff --git a/opengl/libs/libEGL.map.txt b/opengl/libs/libEGL.map.txt index b2d795745f..0c14e01c7b 100644 --- a/opengl/libs/libEGL.map.txt +++ b/opengl/libs/libEGL.map.txt @@ -28,7 +28,7 @@ LIBEGL { eglDestroySurface; eglDestroySync; # introduced=29 eglDestroySyncKHR; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21 - eglDupNativeFenceFDANDROID; # vndk + eglDupNativeFenceFDANDROID; # llndk eglGetConfigAttrib; eglGetConfigs; eglGetCurrentContext; @@ -54,7 +54,7 @@ LIBEGL { eglQueryStreamTimeKHR; # introduced=23 eglQueryStreamu64KHR; # introduced=23 eglQueryString; - eglQueryStringImplementationANDROID; # vndk + eglQueryStringImplementationANDROID; # llndk eglQuerySurface; eglReleaseTexImage; eglReleaseThread; diff --git a/opengl/tests/EGLTest/Android.bp b/opengl/tests/EGLTest/Android.bp index a9e873ac43..8bfe517812 100644 --- a/opengl/tests/EGLTest/Android.bp +++ b/opengl/tests/EGLTest/Android.bp @@ -22,16 +22,17 @@ cc_test { "libbinder", "libgui", "libhidlbase", - "libhidltransport", "liblog", "libutils", "libnativewindow", ], include_dirs: [ - "bionic/libc/private", "frameworks/native/opengl/libs", "frameworks/native/opengl/libs/EGL", ], + header_libs: [ + "bionic_libc_platform_headers", + ], } diff --git a/opengl/tests/gl_perf/fill_common.cpp b/opengl/tests/gl_perf/fill_common.cpp index fefedc0939..613f1c6aa1 100644 --- a/opengl/tests/gl_perf/fill_common.cpp +++ b/opengl/tests/gl_perf/fill_common.cpp @@ -191,10 +191,10 @@ static void setupVA() { static void randUniform(int pgm, const char *var) { GLint loc = glGetUniformLocation(pgm, var); if (loc >= 0) { - float x = ((float)rand()) / RAND_MAX; - float y = ((float)rand()) / RAND_MAX; - float z = ((float)rand()) / RAND_MAX; - float w = ((float)rand()) / RAND_MAX; + float x = ((float)rand()) / (float)RAND_MAX; + float y = ((float)rand()) / (float)RAND_MAX; + float z = ((float)rand()) / (float)RAND_MAX; + float w = ((float)rand()) / (float)RAND_MAX; glUniform4f(loc, x, y, z, w); } } diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen index da9bb49495..41fcf1bd95 100755 --- a/opengl/tools/glgen/gen +++ b/opengl/tools/glgen/gen @@ -91,8 +91,8 @@ fi rm src/*.class # Add UnsupportedAppUsage.java to known sources. -mkdir -p out/android/annotation -cp ../../../../base/core/java/android/annotation/UnsupportedAppUsage.java out/android/annotation +mkdir -p out/android/compat/annotation +cp ../../../../../tools/platform-compat/annotation/src/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation pushd out > /dev/null mkdir classes @@ -114,7 +114,7 @@ javac -d classes android/opengl/EGL14.java \ android/opengl/GLES31.java \ android/opengl/GLES31Ext.java \ android/opengl/GLES32.java \ - android/annotation/UnsupportedAppUsage.java + android/compat/annotation/UnsupportedAppUsage.java popd > /dev/null JAVA_RESULT=$? if [ $JAVA_RESULT -ne 0 ]; then diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if index 12728f588f..9932556d27 100644 --- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if +++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if @@ -18,7 +18,7 @@ package android.opengl; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; import android.graphics.SurfaceTexture; import android.view.Surface; import android.view.SurfaceView; diff --git a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if index c2711aa4db..7db11015ad 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if +++ b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if @@ -19,7 +19,7 @@ package android.opengl; -import android.annotation.UnsupportedAppUsage; +import android.compat.annotation.UnsupportedAppUsage; /** OpenGL ES 2.0 */ diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp index cd03bc2be7..2bb6aef3a5 100644 --- a/services/bufferhub/Android.bp +++ b/services/bufferhub/Android.bp @@ -37,8 +37,6 @@ cc_library_shared { "libcrypto", "libcutils", "libhidlbase", - "libhidltransport", - "libhwbinder", "liblog", "libui", "libutils", @@ -64,8 +62,6 @@ cc_binary { "libcrypto", "libcutils", "libhidlbase", - "libhidltransport", - "libhwbinder", "liblog", "libui", "libutils", diff --git a/services/displayservice/Android.bp b/services/displayservice/Android.bp index 3442cb2ff7..4d2d87352b 100644 --- a/services/displayservice/Android.bp +++ b/services/displayservice/Android.bp @@ -27,7 +27,6 @@ cc_library_shared { "liblog", "libgui", "libhidlbase", - "libhidltransport", "libutils", "android.frameworks.displayservice@1.0", ], diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index dbb6ba6719..7b8e0f835c 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -59,9 +59,6 @@ cc_defaults { cc_defaults { name: "gpuservice_binary", defaults: ["gpuservice_defaults"], - whole_static_libs: [ - "libsigchain", - ], shared_libs: [ "libbinder", "libcutils", diff --git a/services/gpuservice/OWNERS b/services/gpuservice/OWNERS new file mode 100644 index 0000000000..5d028393f6 --- /dev/null +++ b/services/gpuservice/OWNERS @@ -0,0 +1,3 @@ +chrisforbes@google.com +lpy@google.com +zzyiwei@google.com diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 8dd4d1df63..11578c393e 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -30,7 +30,6 @@ cc_library_shared { srcs: [ "InputClassifier.cpp", "InputClassifierConverter.cpp", - "InputDispatcher.cpp", "InputManager.cpp", ], @@ -50,6 +49,10 @@ cc_library_shared { "server_configurable_flags", ], + static_libs: [ + "libinputdispatcher", + ], + cflags: [ // TODO(b/23084678): Move inputflinger to its own process and mark it hidden //-fvisibility=hidden @@ -60,44 +63,16 @@ cc_library_shared { "include", ], + export_static_lib_headers: [ + "libinputdispatcher", + ], } cc_library_headers { name: "libinputflinger_headers", + header_libs: ["libinputreporter_headers"], export_include_dirs: ["include"], -} - -cc_library_shared { - name: "libinputreader", - defaults: ["inputflinger_defaults"], - - srcs: [ - "EventHub.cpp", - "InputReader.cpp", - "InputReaderFactory.cpp", - "TouchVideoDevice.cpp", - ], - - shared_libs: [ - "libbase", - "libinputflinger_base", - "libcrypto", - "libcutils", - "libinput", - "liblog", - "libui", - "libutils", - "libhardware_legacy", - "libstatslog", - ], - - header_libs: [ - "libinputflinger_headers", - ], - - export_header_lib_headers: [ - "libinputflinger_headers", - ], + export_header_lib_headers: ["libinputreporter_headers"], } cc_library_shared { @@ -125,28 +100,6 @@ cc_library_shared { ], } -cc_library_shared { - name: "libinputreporter", - defaults: ["inputflinger_defaults"], - - srcs: [ - "InputReporter.cpp", - ], - - shared_libs: [ - "liblog", - "libutils", - ], - - header_libs: [ - "libinputflinger_headers", - ], - - export_header_lib_headers: [ - "libinputflinger_headers", - ], -} - subdirs = [ "host", "tests", diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h deleted file mode 100644 index 753b748884..0000000000 --- a/services/inputflinger/InputDispatcher.h +++ /dev/null @@ -1,1305 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUT_DISPATCHER_H -#define _UI_INPUT_DISPATCHER_H - -#include <condition_variable> -#include <input/Input.h> -#include <input/InputApplication.h> -#include <input/InputTransport.h> -#include <input/InputWindow.h> -#include <input/ISetInputWindowsListener.h> -#include <optional> -#include <ui/Region.h> -#include <utils/threads.h> -#include <utils/Timers.h> -#include <utils/RefBase.h> -#include <utils/Looper.h> -#include <utils/BitSet.h> -#include <cutils/atomic.h> -#include <unordered_map> - -#include <stddef.h> -#include <unistd.h> -#include <limits.h> -#include <unordered_map> - -#include "InputListener.h" -#include "InputReporterInterface.h" - -namespace android { - -/* - * Constants used to report the outcome of input event injection. - */ -enum { - /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ - INPUT_EVENT_INJECTION_PENDING = -1, - - /* Injection succeeded. */ - INPUT_EVENT_INJECTION_SUCCEEDED = 0, - - /* Injection failed because the injector did not have permission to inject - * into the application with input focus. */ - INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1, - - /* Injection failed because there were no available input targets. */ - INPUT_EVENT_INJECTION_FAILED = 2, - - /* Injection failed due to a timeout. */ - INPUT_EVENT_INJECTION_TIMED_OUT = 3 -}; - -/* - * Constants used to determine the input event injection synchronization mode. - */ -enum { - /* Injection is asynchronous and is assumed always to be successful. */ - INPUT_EVENT_INJECTION_SYNC_NONE = 0, - - /* Waits for previous events to be dispatched so that the input dispatcher can determine - * whether input event injection willbe permitted based on the current input focus. - * Does not wait for the input event to finish processing. */ - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1, - - /* Waits for the input event to be completely processed. */ - INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2, -}; - - -/* - * An input target specifies how an input event is to be dispatched to a particular window - * including the window's input channel, control flags, a timeout, and an X / Y offset to - * be added to input event coordinates to compensate for the absolute position of the - * window area. - */ -struct InputTarget { - enum { - /* This flag indicates that the event is being delivered to a foreground application. */ - FLAG_FOREGROUND = 1 << 0, - - /* This flag indicates that the MotionEvent falls within the area of the target - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ - FLAG_WINDOW_IS_OBSCURED = 1 << 1, - - /* This flag indicates that a motion event is being split across multiple windows. */ - FLAG_SPLIT = 1 << 2, - - /* This flag indicates that the pointer coordinates dispatched to the application - * will be zeroed out to avoid revealing information to an application. This is - * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing - * the same UID from watching all touches. */ - FLAG_ZERO_COORDS = 1 << 3, - - /* This flag indicates that the event should be sent as is. - * Should always be set unless the event is to be transmuted. */ - FLAG_DISPATCH_AS_IS = 1 << 8, - - /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside - * of the area of this target and so should instead be delivered as an - * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ - FLAG_DISPATCH_AS_OUTSIDE = 1 << 9, - - /* This flag indicates that a hover sequence is starting in the given window. - * The event is transmuted into ACTION_HOVER_ENTER. */ - FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10, - - /* This flag indicates that a hover event happened outside of a window which handled - * previous hover events, signifying the end of the current hover sequence for that - * window. - * The event is transmuted into ACTION_HOVER_ENTER. */ - FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11, - - /* This flag indicates that the event should be canceled. - * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips - * outside of a window. */ - FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12, - - /* This flag indicates that the event should be dispatched as an initial down. - * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips - * into a new window. */ - FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13, - - /* Mask for all dispatch modes. */ - FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS - | FLAG_DISPATCH_AS_OUTSIDE - | FLAG_DISPATCH_AS_HOVER_ENTER - | FLAG_DISPATCH_AS_HOVER_EXIT - | FLAG_DISPATCH_AS_SLIPPERY_EXIT - | FLAG_DISPATCH_AS_SLIPPERY_ENTER, - - /* This flag indicates that the target of a MotionEvent is partly or wholly - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ - FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, - - }; - - // The input channel to be targeted. - sp<InputChannel> inputChannel; - - // Flags for the input target. - int32_t flags; - - // The x and y offset to add to a MotionEvent as it is delivered. - // (ignored for KeyEvents) - float xOffset, yOffset; - - // Scaling factor to apply to MotionEvent as it is delivered. - // (ignored for KeyEvents) - float globalScaleFactor; - float windowXScale = 1.0f; - float windowYScale = 1.0f; - - // The subset of pointer ids to include in motion events dispatched to this input target - // if FLAG_SPLIT is set. - BitSet32 pointerIds; -}; - - -/* - * Input dispatcher configuration. - * - * Specifies various options that modify the behavior of the input dispatcher. - * The values provided here are merely defaults. The actual values will come from ViewConfiguration - * and are passed into the dispatcher during initialization. - */ -struct InputDispatcherConfiguration { - // The key repeat initial timeout. - nsecs_t keyRepeatTimeout; - - // The key repeat inter-key delay. - nsecs_t keyRepeatDelay; - - InputDispatcherConfiguration() : - keyRepeatTimeout(500 * 1000000LL), - keyRepeatDelay(50 * 1000000LL) { } -}; - - -/* - * Input dispatcher policy interface. - * - * The input reader policy is used by the input reader to interact with the Window Manager - * and other system components. - * - * The actual implementation is partially supported by callbacks into the DVM - * via JNI. This interface is also mocked in the unit tests. - */ -class InputDispatcherPolicyInterface : public virtual RefBase { -protected: - InputDispatcherPolicyInterface() { } - virtual ~InputDispatcherPolicyInterface() { } - -public: - /* Notifies the system that a configuration change has occurred. */ - virtual void notifyConfigurationChanged(nsecs_t when) = 0; - - /* Notifies the system that an application is not responding. - * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ - virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, - const sp<IBinder>& token, - const std::string& reason) = 0; - - /* Notifies the system that an input channel is unrecoverably broken. */ - virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0; - virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0; - - /* Gets the input dispatcher configuration. */ - virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0; - - /* Filters an input event. - * Return true to dispatch the event unmodified, false to consume the event. - * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED - * to injectInputEvent. - */ - virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0; - - /* Intercepts a key event immediately before queueing it. - * The policy can use this method as an opportunity to perform power management functions - * and early event preprocessing such as updating policy flags. - * - * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event - * should be dispatched to applications. - */ - virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0; - - /* Intercepts a touch, trackball or other motion event before queueing it. - * The policy can use this method as an opportunity to perform power management functions - * and early event preprocessing such as updating policy flags. - * - * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event - * should be dispatched to applications. - */ - virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, - uint32_t& policyFlags) = 0; - - /* Allows the policy a chance to intercept a key before dispatching. */ - virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, - const KeyEvent* keyEvent, uint32_t policyFlags) = 0; - - /* Allows the policy a chance to perform default processing for an unhandled key. - * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */ - virtual bool dispatchUnhandledKey(const sp<IBinder>& token, - const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0; - - /* Notifies the policy about switch events. - */ - virtual void notifySwitch(nsecs_t when, - uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) = 0; - - /* Poke user activity for an event dispatched to a window. */ - virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0; - - /* Checks whether a given application pid/uid has permission to inject input events - * into other applications. - * - * This method is special in that its implementation promises to be non-reentrant and - * is safe to call while holding other locks. (Most other methods make no such guarantees!) - */ - virtual bool checkInjectEventsPermissionNonReentrant( - int32_t injectorPid, int32_t injectorUid) = 0; - - /* Notifies the policy that a pointer down event has occurred outside the current focused - * window. - * - * The touchedToken passed as an argument is the window that received the input event. - */ - virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0; -}; - - -/* Notifies the system about input events generated by the input reader. - * The dispatcher is expected to be mostly asynchronous. */ -class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface { -protected: - InputDispatcherInterface() { } - virtual ~InputDispatcherInterface() { } - -public: - /* Dumps the state of the input dispatcher. - * - * This method may be called on any thread (usually by the input manager). */ - virtual void dump(std::string& dump) = 0; - - /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ - virtual void monitor() = 0; - - /* Runs a single iteration of the dispatch loop. - * Nominally processes one queued event, a timeout, or a response from an input consumer. - * - * This method should only be called on the input dispatcher thread. - */ - virtual void dispatchOnce() = 0; - - /* Injects an input event and optionally waits for sync. - * The synchronization mode determines whether the method blocks while waiting for - * input injection to proceed. - * Returns one of the INPUT_EVENT_INJECTION_XXX constants. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual int32_t injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, - uint32_t policyFlags) = 0; - - /* Sets the list of input windows. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setInputWindows(const std::vector<sp<InputWindowHandle> >& inputWindowHandles, - int32_t displayId, - const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) = 0; - - /* Sets the focused application on the given display. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setFocusedApplication( - int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0; - - /* Sets the focused display. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setFocusedDisplay(int32_t displayId) = 0; - - /* Sets the input dispatching mode. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual void setInputDispatchMode(bool enabled, bool frozen) = 0; - - /* Sets whether input event filtering is enabled. - * When enabled, incoming input events are sent to the policy's filterInputEvent - * method instead of being dispatched. The filter is expected to use - * injectInputEvent to inject the events it would like to have dispatched. - * It should include POLICY_FLAG_FILTERED in the policy flags during injection. - */ - virtual void setInputFilterEnabled(bool enabled) = 0; - - /* Transfers touch focus from one window to another window. - * - * Returns true on success. False if the window did not actually have touch focus. - */ - virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0; - - /* Registers input channels that may be used as targets for input events. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t registerInputChannel( - const sp<InputChannel>& inputChannel, int32_t displayId) = 0; - - /* Registers input channels to be used to monitor input events. - * - * Each monitor must target a specific display and will only receive input events sent to that - * display. If the monitor is a gesture monitor, it will only receive pointer events on the - * targeted display. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t registerInputMonitor( - const sp<InputChannel>& inputChannel, int32_t displayId, bool gestureMonitor) = 0; - - /* Unregister input channels that will no longer receive input events. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; - - /* Allows an input monitor steal the current pointer stream away from normal input windows. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t pilferPointers(const sp<IBinder>& token) = 0; - -}; - -/* Dispatches events to input targets. Some functions of the input dispatcher, such as - * identifying input targets, are controlled by a separate policy object. - * - * IMPORTANT INVARIANT: - * Because the policy can potentially block or cause re-entrance into the input dispatcher, - * the input dispatcher never calls into the policy while holding its internal locks. - * The implementation is also carefully designed to recover from scenarios such as an - * input channel becoming unregistered while identifying input targets or processing timeouts. - * - * Methods marked 'Locked' must be called with the lock acquired. - * - * Methods marked 'LockedInterruptible' must be called with the lock acquired but - * may during the course of their execution release the lock, call into the policy, and - * then reacquire the lock. The caller is responsible for recovering gracefully. - * - * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa. - */ -class InputDispatcher : public InputDispatcherInterface { -protected: - virtual ~InputDispatcher(); - -public: - explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy); - - virtual void dump(std::string& dump) override; - virtual void monitor() override; - - virtual void dispatchOnce() override; - - virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; - virtual void notifyKey(const NotifyKeyArgs* args) override; - virtual void notifyMotion(const NotifyMotionArgs* args) override; - virtual void notifySwitch(const NotifySwitchArgs* args) override; - virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; - - virtual int32_t injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, - uint32_t policyFlags) override; - - virtual void setInputWindows(const std::vector<sp<InputWindowHandle> >& inputWindowHandles, - int32_t displayId, - const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override; - virtual void setFocusedApplication(int32_t displayId, - const sp<InputApplicationHandle>& inputApplicationHandle) override; - virtual void setFocusedDisplay(int32_t displayId) override; - virtual void setInputDispatchMode(bool enabled, bool frozen) override; - virtual void setInputFilterEnabled(bool enabled) override; - - virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) - override; - - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, - int32_t displayId) override; - virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, - int32_t displayId, bool isGestureMonitor) override; - virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override; - virtual status_t pilferPointers(const sp<IBinder>& token) override; - -private: - template <typename T> - struct Link { - T* next; - T* prev; - - protected: - inline Link() : next(nullptr), prev(nullptr) { } - }; - - struct InjectionState { - mutable int32_t refCount; - - int32_t injectorPid; - int32_t injectorUid; - int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING - bool injectionIsAsync; // set to true if injection is not waiting for the result - int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress - - InjectionState(int32_t injectorPid, int32_t injectorUid); - void release(); - - private: - ~InjectionState(); - }; - - struct EventEntry : Link<EventEntry> { - enum { - TYPE_CONFIGURATION_CHANGED, - TYPE_DEVICE_RESET, - TYPE_KEY, - TYPE_MOTION - }; - - uint32_t sequenceNum; - mutable int32_t refCount; - int32_t type; - nsecs_t eventTime; - uint32_t policyFlags; - InjectionState* injectionState; - - bool dispatchInProgress; // initially false, set to true while dispatching - - inline bool isInjected() const { return injectionState != nullptr; } - - void release(); - - virtual void appendDescription(std::string& msg) const = 0; - - protected: - EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags); - virtual ~EventEntry(); - void releaseInjectionState(); - }; - - struct ConfigurationChangedEntry : EventEntry { - explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime); - virtual void appendDescription(std::string& msg) const; - - protected: - virtual ~ConfigurationChangedEntry(); - }; - - struct DeviceResetEntry : EventEntry { - int32_t deviceId; - - DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId); - virtual void appendDescription(std::string& msg) const; - - protected: - virtual ~DeviceResetEntry(); - }; - - struct KeyEntry : EventEntry { - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t action; - int32_t flags; - int32_t keyCode; - int32_t scanCode; - int32_t metaState; - int32_t repeatCount; - nsecs_t downTime; - - bool syntheticRepeat; // set to true for synthetic key repeats - - enum InterceptKeyResult { - INTERCEPT_KEY_RESULT_UNKNOWN, - INTERCEPT_KEY_RESULT_SKIP, - INTERCEPT_KEY_RESULT_CONTINUE, - INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER, - }; - InterceptKeyResult interceptKeyResult; // set based on the interception result - nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER - - KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, - int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, - int32_t repeatCount, nsecs_t downTime); - virtual void appendDescription(std::string& msg) const; - void recycle(); - - protected: - virtual ~KeyEntry(); - }; - - struct MotionEntry : EventEntry { - nsecs_t eventTime; - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t action; - int32_t actionButton; - int32_t flags; - int32_t metaState; - int32_t buttonState; - MotionClassification classification; - int32_t edgeFlags; - float xPrecision; - float yPrecision; - nsecs_t downTime; - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - - MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, - int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, float xPrecision, float yPrecision, - nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset); - virtual void appendDescription(std::string& msg) const; - - protected: - virtual ~MotionEntry(); - }; - - // Tracks the progress of dispatching a particular event to a particular connection. - struct DispatchEntry : Link<DispatchEntry> { - const uint32_t seq; // unique sequence number, never 0 - - EventEntry* eventEntry; // the event to dispatch - int32_t targetFlags; - float xOffset; - float yOffset; - float globalScaleFactor; - float windowXScale = 1.0f; - float windowYScale = 1.0f; - nsecs_t deliveryTime; // time when the event was actually delivered - - // Set to the resolved action and flags when the event is enqueued. - int32_t resolvedAction; - int32_t resolvedFlags; - - DispatchEntry(EventEntry* eventEntry, - int32_t targetFlags, float xOffset, float yOffset, - float globalScaleFactor, float windowXScale, float windowYScale); - ~DispatchEntry(); - - inline bool hasForegroundTarget() const { - return targetFlags & InputTarget::FLAG_FOREGROUND; - } - - inline bool isSplit() const { - return targetFlags & InputTarget::FLAG_SPLIT; - } - - private: - static volatile int32_t sNextSeqAtomic; - - static uint32_t nextSeq(); - }; - - // A command entry captures state and behavior for an action to be performed in the - // dispatch loop after the initial processing has taken place. It is essentially - // a kind of continuation used to postpone sensitive policy interactions to a point - // in the dispatch loop where it is safe to release the lock (generally after finishing - // the critical parts of the dispatch cycle). - // - // The special thing about commands is that they can voluntarily release and reacquire - // the dispatcher lock at will. Initially when the command starts running, the - // dispatcher lock is held. However, if the command needs to call into the policy to - // do some work, it can release the lock, do the work, then reacquire the lock again - // before returning. - // - // This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch - // never calls into the policy while holding its lock. - // - // Commands are implicitly 'LockedInterruptible'. - struct CommandEntry; - typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry); - - class Connection; - struct CommandEntry : Link<CommandEntry> { - explicit CommandEntry(Command command); - ~CommandEntry(); - - Command command; - - // parameters for the command (usage varies by command) - sp<Connection> connection; - nsecs_t eventTime; - KeyEntry* keyEntry; - sp<InputApplicationHandle> inputApplicationHandle; - std::string reason; - int32_t userActivityEventType; - uint32_t seq; - bool handled; - sp<InputChannel> inputChannel; - sp<IBinder> oldToken; - sp<IBinder> newToken; - }; - - // Generic queue implementation. - template <typename T> - struct Queue { - T* head; - T* tail; - uint32_t entryCount; - - inline Queue() : head(nullptr), tail(nullptr), entryCount(0) { - } - - inline bool isEmpty() const { - return !head; - } - - inline void enqueueAtTail(T* entry) { - entryCount++; - entry->prev = tail; - if (tail) { - tail->next = entry; - } else { - head = entry; - } - entry->next = nullptr; - tail = entry; - } - - inline void enqueueAtHead(T* entry) { - entryCount++; - entry->next = head; - if (head) { - head->prev = entry; - } else { - tail = entry; - } - entry->prev = nullptr; - head = entry; - } - - inline void dequeue(T* entry) { - entryCount--; - if (entry->prev) { - entry->prev->next = entry->next; - } else { - head = entry->next; - } - if (entry->next) { - entry->next->prev = entry->prev; - } else { - tail = entry->prev; - } - } - - inline T* dequeueAtHead() { - entryCount--; - T* entry = head; - head = entry->next; - if (head) { - head->prev = nullptr; - } else { - tail = nullptr; - } - return entry; - } - - uint32_t count() const { - return entryCount; - } - }; - - /* Specifies which events are to be canceled and why. */ - struct CancelationOptions { - enum Mode { - CANCEL_ALL_EVENTS = 0, - CANCEL_POINTER_EVENTS = 1, - CANCEL_NON_POINTER_EVENTS = 2, - CANCEL_FALLBACK_EVENTS = 3, - }; - - // The criterion to use to determine which events should be canceled. - Mode mode; - - // Descriptive reason for the cancelation. - const char* reason; - - // The specific keycode of the key event to cancel, or nullopt to cancel any key event. - std::optional<int32_t> keyCode = std::nullopt; - - // The specific device id of events to cancel, or nullopt to cancel events from any device. - std::optional<int32_t> deviceId = std::nullopt; - - // The specific display id of events to cancel, or nullopt to cancel events on any display. - std::optional<int32_t> displayId = std::nullopt; - - CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) { } - }; - - /* Tracks dispatched key and motion event state so that cancelation events can be - * synthesized when events are dropped. */ - class InputState { - public: - InputState(); - ~InputState(); - - // Returns true if there is no state to be canceled. - bool isNeutral() const; - - // Returns true if the specified source is known to have received a hover enter - // motion event. - bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const; - - // Records tracking information for a key event that has just been published. - // Returns true if the event should be delivered, false if it is inconsistent - // and should be skipped. - bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags); - - // Records tracking information for a motion event that has just been published. - // Returns true if the event should be delivered, false if it is inconsistent - // and should be skipped. - bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags); - - // Synthesizes cancelation events for the current state and resets the tracked state. - void synthesizeCancelationEvents(nsecs_t currentTime, - std::vector<EventEntry*>& outEvents, const CancelationOptions& options); - - // Clears the current state. - void clear(); - - // Copies pointer-related parts of the input state to another instance. - void copyPointerStateTo(InputState& other) const; - - // Gets the fallback key associated with a keycode. - // Returns -1 if none. - // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy. - int32_t getFallbackKey(int32_t originalKeyCode); - - // Sets the fallback key for a particular keycode. - void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode); - - // Removes the fallback key for a particular keycode. - void removeFallbackKey(int32_t originalKeyCode); - - inline const KeyedVector<int32_t, int32_t>& getFallbackKeys() const { - return mFallbackKeys; - } - - private: - struct KeyMemento { - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t keyCode; - int32_t scanCode; - int32_t metaState; - int32_t flags; - nsecs_t downTime; - uint32_t policyFlags; - }; - - struct MotionMemento { - int32_t deviceId; - uint32_t source; - int32_t displayId; - int32_t flags; - float xPrecision; - float yPrecision; - nsecs_t downTime; - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - bool hovering; - uint32_t policyFlags; - - void setPointers(const MotionEntry* entry); - }; - - std::vector<KeyMemento> mKeyMementos; - std::vector<MotionMemento> mMotionMementos; - KeyedVector<int32_t, int32_t> mFallbackKeys; - - ssize_t findKeyMemento(const KeyEntry* entry) const; - ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const; - - void addKeyMemento(const KeyEntry* entry, int32_t flags); - void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering); - - static bool shouldCancelKey(const KeyMemento& memento, - const CancelationOptions& options); - static bool shouldCancelMotion(const MotionMemento& memento, - const CancelationOptions& options); - }; - - /* Manages the dispatch state associated with a single input channel. */ - class Connection : public RefBase { - protected: - virtual ~Connection(); - - public: - enum Status { - // Everything is peachy. - STATUS_NORMAL, - // An unrecoverable communication error has occurred. - STATUS_BROKEN, - // The input channel has been unregistered. - STATUS_ZOMBIE - }; - - Status status; - sp<InputChannel> inputChannel; // never null - bool monitor; - InputPublisher inputPublisher; - InputState inputState; - - // True if the socket is full and no further events can be published until - // the application consumes some of the input. - bool inputPublisherBlocked; - - // Queue of events that need to be published to the connection. - Queue<DispatchEntry> outboundQueue; - - // Queue of events that have been published to the connection but that have not - // yet received a "finished" response from the application. - Queue<DispatchEntry> waitQueue; - - explicit Connection(const sp<InputChannel>& inputChannel, bool monitor); - - inline const std::string getInputChannelName() const { return inputChannel->getName(); } - - const std::string getWindowName() const; - const char* getStatusLabel() const; - - DispatchEntry* findWaitQueueEntry(uint32_t seq); - }; - - struct Monitor { - sp<InputChannel> inputChannel; // never null - - explicit Monitor(const sp<InputChannel>& inputChannel); - }; - - enum DropReason { - DROP_REASON_NOT_DROPPED = 0, - DROP_REASON_POLICY = 1, - DROP_REASON_APP_SWITCH = 2, - DROP_REASON_DISABLED = 3, - DROP_REASON_BLOCKED = 4, - DROP_REASON_STALE = 5, - }; - - sp<InputDispatcherPolicyInterface> mPolicy; - InputDispatcherConfiguration mConfig; - - std::mutex mLock; - - std::condition_variable mDispatcherIsAlive; - - sp<Looper> mLooper; - - EventEntry* mPendingEvent GUARDED_BY(mLock); - Queue<EventEntry> mInboundQueue GUARDED_BY(mLock); - Queue<EventEntry> mRecentQueue GUARDED_BY(mLock); - Queue<CommandEntry> mCommandQueue GUARDED_BY(mLock); - - DropReason mLastDropReason GUARDED_BY(mLock); - - void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock); - - // Enqueues an inbound event. Returns true if mLooper->wake() should be called. - bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock); - - // Cleans up input state when dropping an inbound event. - void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock); - - // Adds an event to a queue of recent events for debugging purposes. - void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock); - - // App switch latency optimization. - bool mAppSwitchSawKeyDown GUARDED_BY(mLock); - nsecs_t mAppSwitchDueTime GUARDED_BY(mLock); - - bool isAppSwitchKeyEvent(KeyEntry* keyEntry); - bool isAppSwitchPendingLocked() REQUIRES(mLock); - void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock); - - // Stale event latency optimization. - static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry); - - // Blocked event latency optimization. Drops old events when the user intends - // to transfer focus to a new application. - EventEntry* mNextUnblockedEvent GUARDED_BY(mLock); - - sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, - bool addOutsideTargets = false, bool addPortalWindows = false) REQUIRES(mLock); - - // All registered connections mapped by channel file descriptor. - KeyedVector<int, sp<Connection> > mConnectionsByFd GUARDED_BY(mLock); - - struct IBinderHash { - std::size_t operator()(const sp<IBinder>& b) const { - return std::hash<IBinder *>{}(b.get()); - } - }; - std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken - GUARDED_BY(mLock); - - // Finds the display ID of the gesture monitor identified by the provided token. - std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token) - REQUIRES(mLock); - - ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock); - - // Input channels that will receive a copy of all input events sent to the provided display. - std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay - GUARDED_BY(mLock); - - // Input channels that will receive pointer events that start within the corresponding display. - // These are a bit special when compared to global monitors since they'll cause gesture streams - // to continue even when there isn't a touched window,and have the ability to steal the rest of - // the pointer stream in order to claim it for a system gesture. - std::unordered_map<int32_t, std::vector<Monitor>> mGestureMonitorsByDisplay - GUARDED_BY(mLock); - - - // Event injection and synchronization. - std::condition_variable mInjectionResultAvailable; - bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid); - void setInjectionResult(EventEntry* entry, int32_t injectionResult); - - std::condition_variable mInjectionSyncFinished; - void incrementPendingForegroundDispatches(EventEntry* entry); - void decrementPendingForegroundDispatches(EventEntry* entry); - - // Key repeat tracking. - struct KeyRepeatState { - KeyEntry* lastKeyEntry; // or null if no repeat - nsecs_t nextRepeatTime; - } mKeyRepeatState GUARDED_BY(mLock); - - void resetKeyRepeatLocked() REQUIRES(mLock); - KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock); - - // Key replacement tracking - struct KeyReplacement { - int32_t keyCode; - int32_t deviceId; - bool operator==(const KeyReplacement& rhs) const { - return keyCode == rhs.keyCode && deviceId == rhs.deviceId; - } - bool operator<(const KeyReplacement& rhs) const { - return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId; - } - }; - // Maps the key code replaced, device id tuple to the key code it was replaced with - KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock); - // Process certain Meta + Key combinations - void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, - int32_t& keyCode, int32_t& metaState); - - // Deferred command processing. - bool haveCommandsLocked() const REQUIRES(mLock); - bool runCommandsLockedInterruptible() REQUIRES(mLock); - CommandEntry* postCommandLocked(Command command) REQUIRES(mLock); - - // Input filter processing. - bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); - bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock); - - // Inbound event processing. - void drainInboundQueueLocked() REQUIRES(mLock); - void releasePendingEventLocked() REQUIRES(mLock); - void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock); - - // Dispatch state. - bool mDispatchEnabled GUARDED_BY(mLock); - bool mDispatchFrozen GUARDED_BY(mLock); - bool mInputFilterEnabled GUARDED_BY(mLock); - - std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay - GUARDED_BY(mLock); - // Get window handles by display, return an empty vector if not found. - std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const - REQUIRES(mLock); - sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const - REQUIRES(mLock); - sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock); - bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock); - - // Focus tracking for keys, trackball, etc. - std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay - GUARDED_BY(mLock); - - // Focus tracking for touch. - struct TouchedWindow { - sp<InputWindowHandle> windowHandle; - int32_t targetFlags; - BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set - }; - - // For tracking the offsets we need to apply when adding gesture monitor targets. - struct TouchedMonitor { - Monitor monitor; - float xOffset = 0.f; - float yOffset = 0.f; - - explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset); - }; - - struct TouchState { - bool down; - bool split; - int32_t deviceId; // id of the device that is currently down, others are rejected - uint32_t source; // source of the device that is current down, others are rejected - int32_t displayId; // id to the display that currently has a touch, others are rejected - std::vector<TouchedWindow> windows; - - // This collects the portal windows that the touch has gone through. Each portal window - // targets a display (embedded display for most cases). With this info, we can add the - // monitoring channels of the displays touched. - std::vector<sp<InputWindowHandle>> portalWindows; - - std::vector<TouchedMonitor> gestureMonitors; - - TouchState(); - ~TouchState(); - void reset(); - void copyFrom(const TouchState& other); - void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, - int32_t targetFlags, BitSet32 pointerIds); - void addPortalWindow(const sp<InputWindowHandle>& windowHandle); - void addGestureMonitors(const std::vector<TouchedMonitor>& monitors); - void removeWindow(const sp<InputWindowHandle>& windowHandle); - void removeWindowByToken(const sp<IBinder>& token); - void filterNonAsIsTouchWindows(); - void filterNonMonitors(); - sp<InputWindowHandle> getFirstForegroundWindowHandle() const; - bool isSlippery() const; - }; - - KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock); - TouchState mTempTouchState GUARDED_BY(mLock); - - // Focused applications. - std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay - GUARDED_BY(mLock); - - // Top focused display. - int32_t mFocusedDisplayId GUARDED_BY(mLock); - - // Dispatcher state at time of last ANR. - std::string mLastANRState GUARDED_BY(mLock); - - // Dispatch inbound events. - bool dispatchConfigurationChangedLocked( - nsecs_t currentTime, ConfigurationChangedEntry* entry) REQUIRES(mLock); - bool dispatchDeviceResetLocked( - nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock); - bool dispatchKeyLocked( - nsecs_t currentTime, KeyEntry* entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); - bool dispatchMotionLocked( - nsecs_t currentTime, MotionEntry* entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); - void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry, - const std::vector<InputTarget>& inputTargets) REQUIRES(mLock); - - void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry); - void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry); - - // Keeping track of ANR timeouts. - enum InputTargetWaitCause { - INPUT_TARGET_WAIT_CAUSE_NONE, - INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY, - INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY, - }; - - InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock); - nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock); - nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock); - bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock); - sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock); - - // Contains the last window which received a hover event. - sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock); - - // Finding targets for input events. - int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, - const sp<InputApplicationHandle>& applicationHandle, - const sp<InputWindowHandle>& windowHandle, - nsecs_t* nextWakeupTime, const char* reason) REQUIRES(mLock); - - void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock); - - void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, - const sp<InputChannel>& inputChannel) REQUIRES(mLock); - nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); - void resetANRTimeoutsLocked() REQUIRES(mLock); - - int32_t getTargetDisplayId(const EventEntry* entry); - int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, - std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) REQUIRES(mLock); - int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, - std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime, - bool* outConflictingPointerActions) REQUIRES(mLock); - std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked(int32_t displayId, - const std::vector<sp<InputWindowHandle>>& portalWindows) REQUIRES(mLock); - void addGestureMonitors(const std::vector<Monitor>& monitors, - std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0, float yOffset = 0); - - void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, - int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) - REQUIRES(mLock); - void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset, - std::vector<InputTarget>& inputTargets) REQUIRES(mLock); - void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, - int32_t displayId, float xOffset = 0, float yOffset = 0) REQUIRES(mLock); - - void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock); - bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, - const InjectionState* injectionState); - bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, - int32_t x, int32_t y) const REQUIRES(mLock); - bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock); - std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle, - const sp<InputWindowHandle>& windowHandle); - - std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime, - const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry, - const char* targetType) REQUIRES(mLock); - - // Manage the dispatch cycle for a single connection. - // These methods are deliberately not Interruptible because doing all of the work - // with the mutex held makes it easier to ensure that connection invariants are maintained. - // If needed, the methods post commands to run later once the critical bits are done. - void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, - EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock); - void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, - EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock); - void enqueueDispatchEntryLocked(const sp<Connection>& connection, - EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode) - REQUIRES(mLock); - void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) - REQUIRES(mLock); - void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, - uint32_t seq, bool handled) REQUIRES(mLock); - void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, - bool notify) REQUIRES(mLock); - void drainDispatchQueue(Queue<DispatchEntry>* queue); - void releaseDispatchEntry(DispatchEntry* dispatchEntry); - static int handleReceiveCallback(int fd, int events, void* data); - // The action sent should only be of type AMOTION_EVENT_* - void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action, - const sp<IBinder>& newToken) REQUIRES(mLock); - - void synthesizeCancelationEventsForAllConnectionsLocked( - const CancelationOptions& options) REQUIRES(mLock); - void synthesizeCancelationEventsForMonitorsLocked( - const CancelationOptions& options) REQUIRES(mLock); - void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options, - std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock); - void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel, - const CancelationOptions& options) REQUIRES(mLock); - void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection, - const CancelationOptions& options) REQUIRES(mLock); - - // Splitting motion events across windows. - MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds); - - // Reset and drop everything the dispatcher is doing. - void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock); - - // Dump state. - void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock); - void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors); - void logDispatchStateLocked() REQUIRES(mLock); - - // Registration. - void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock); - void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel, - std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) - REQUIRES(mLock); - status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify) - REQUIRES(mLock); - - // Interesting events that we might like to log or tell the framework about. - void onDispatchCycleFinishedLocked( - nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) - REQUIRES(mLock); - void onDispatchCycleBrokenLocked( - nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock); - void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus, - const sp<InputWindowHandle>& newFocus) REQUIRES(mLock); - void onANRLocked( - nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle, - const sp<InputWindowHandle>& windowHandle, - nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) REQUIRES(mLock); - - // Outbound policy interactions. - void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) - REQUIRES(mLock); - void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) - REQUIRES(mLock); - void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - bool afterKeyEventLockedInterruptible(const sp<Connection>& connection, - DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) REQUIRES(mLock); - bool afterMotionEventLockedInterruptible(const sp<Connection>& connection, - DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock); - void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry); - void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) - REQUIRES(mLock); - - // Statistics gathering. - void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, - int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); - void traceInboundQueueLengthLocked() REQUIRES(mLock); - void traceOutboundQueueLength(const sp<Connection>& connection); - void traceWaitQueueLength(const sp<Connection>& connection); - - sp<InputReporterInterface> mReporter; -}; - -/* Enqueues and dispatches input events, endlessly. */ -class InputDispatcherThread : public Thread { -public: - explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher); - ~InputDispatcherThread(); - -private: - virtual bool threadLoop(); - - sp<InputDispatcherInterface> mDispatcher; -}; - -} // namespace android - -#endif // _UI_INPUT_DISPATCHER_H diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index acb53be69e..7d3067242a 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -19,6 +19,8 @@ //#define LOG_NDEBUG 0 #include "InputManager.h" +#include "InputDispatcherFactory.h" +#include "InputDispatcherThread.h" #include "InputReaderFactory.h" #include <binder/IPCThreadState.h> @@ -33,7 +35,7 @@ namespace android { InputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { - mDispatcher = new InputDispatcher(dispatcherPolicy); + mDispatcher = createInputDispatcher(dispatcherPolicy); mClassifier = new InputClassifier(mDispatcher); mReader = createInputReader(readerPolicy, mClassifier); initialize(); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 32f7ae0058..40f66d82f4 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -21,15 +21,14 @@ * Native input manager. */ -#include "EventHub.h" -#include "InputReaderBase.h" #include "InputClassifier.h" -#include "InputDispatcher.h" -#include "InputReader.h" +#include "InputReaderBase.h" +#include <InputDispatcherInterface.h> +#include <InputDispatcherPolicyInterface.h> +#include <input/ISetInputWindowsListener.h> #include <input/Input.h> #include <input/InputTransport.h> -#include <input/ISetInputWindowsListener.h> #include <input/IInputFlinger.h> #include <utils/Errors.h> @@ -39,6 +38,7 @@ namespace android { class InputChannel; +class InputDispatcherThread; /* * The input manager is the core of the system event processing. diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp deleted file mode 100644 index a45b8a56ce..0000000000 --- a/services/inputflinger/InputReader.cpp +++ /dev/null @@ -1,7534 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "InputReader" - -//#define LOG_NDEBUG 0 - -// Log debug messages for each raw event received from the EventHub. -#define DEBUG_RAW_EVENTS 0 - -// Log debug messages about touch screen filtering hacks. -#define DEBUG_HACKS 0 - -// Log debug messages about virtual key processing. -#define DEBUG_VIRTUAL_KEYS 0 - -// Log debug messages about pointers. -#define DEBUG_POINTERS 0 - -// Log debug messages about pointer assignment calculations. -#define DEBUG_POINTER_ASSIGNMENT 0 - -// Log debug messages about gesture detection. -#define DEBUG_GESTURES 0 - -// Log debug messages about the vibrator. -#define DEBUG_VIBRATOR 0 - -// Log debug messages about fusing stylus data. -#define DEBUG_STYLUS_FUSION 0 - -#include "InputReader.h" - -#include <errno.h> -#include <inttypes.h> -#include <limits.h> -#include <math.h> -#include <stddef.h> -#include <stdlib.h> -#include <unistd.h> - -#include <log/log.h> - -#include <android-base/stringprintf.h> -#include <input/Keyboard.h> -#include <input/VirtualKeyMap.h> -#include <statslog.h> - -#define INDENT " " -#define INDENT2 " " -#define INDENT3 " " -#define INDENT4 " " -#define INDENT5 " " - -using android::base::StringPrintf; - -namespace android { - -// --- Constants --- - -// Maximum number of slots supported when using the slot-based Multitouch Protocol B. -static constexpr size_t MAX_SLOTS = 32; - -// Maximum amount of latency to add to touch events while waiting for data from an -// external stylus. -static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); - -// Maximum amount of time to wait on touch data before pushing out new pressure data. -static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); - -// Artificial latency on synthetic events created from stylus data without corresponding touch -// data. -static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); - -// How often to report input event statistics -static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60); - -// --- Static Functions --- - -template<typename T> -inline static T abs(const T& value) { - return value < 0 ? - value : value; -} - -template<typename T> -inline static T min(const T& a, const T& b) { - return a < b ? a : b; -} - -template<typename T> -inline static void swap(T& a, T& b) { - T temp = a; - a = b; - b = temp; -} - -inline static float avg(float x, float y) { - return (x + y) / 2; -} - -inline static float distance(float x1, float y1, float x2, float y2) { - return hypotf(x1 - x2, y1 - y2); -} - -inline static int32_t signExtendNybble(int32_t value) { - return value >= 8 ? value - 16 : value; -} - -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - -static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation, - const int32_t map[][4], size_t mapSize) { - if (orientation != DISPLAY_ORIENTATION_0) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][orientation]; - } - } - } - return value; -} - -static const int32_t keyCodeRotationMap[][4] = { - // key codes enumerated counter-clockwise with the original (unrotated) key first - // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation - { AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT }, - { AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN }, - { AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT }, - { AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP }, - { AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, - AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT }, - { AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, - AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN }, - { AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, - AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT }, - { AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, - AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP }, -}; -static const size_t keyCodeRotationMapSize = - sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); - -static int32_t rotateStemKey(int32_t value, int32_t orientation, - const int32_t map[][2], size_t mapSize) { - if (orientation == DISPLAY_ORIENTATION_180) { - for (size_t i = 0; i < mapSize; i++) { - if (value == map[i][0]) { - return map[i][1]; - } - } - } - return value; -} - -// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X -static int32_t stemKeyRotationMap[][2] = { - // key codes enumerated with the original (unrotated) key first - // no rotation, 180 degree rotation - { AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY }, - { AKEYCODE_STEM_1, AKEYCODE_STEM_1 }, - { AKEYCODE_STEM_2, AKEYCODE_STEM_2 }, - { AKEYCODE_STEM_3, AKEYCODE_STEM_3 }, -}; -static const size_t stemKeyRotationMapSize = - sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]); - -static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { - keyCode = rotateStemKey(keyCode, orientation, - stemKeyRotationMap, stemKeyRotationMapSize); - return rotateValueUsingRotationMap(keyCode, orientation, - keyCodeRotationMap, keyCodeRotationMapSize); -} - -static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { - float temp; - switch (orientation) { - case DISPLAY_ORIENTATION_90: - temp = *deltaX; - *deltaX = *deltaY; - *deltaY = -temp; - break; - - case DISPLAY_ORIENTATION_180: - *deltaX = -*deltaX; - *deltaY = -*deltaY; - break; - - case DISPLAY_ORIENTATION_270: - temp = *deltaX; - *deltaX = -*deltaY; - *deltaY = temp; - break; - } -} - -static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { - return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0; -} - -// Returns true if the pointer should be reported as being down given the specified -// button states. This determines whether the event is reported as a touch event. -static bool isPointerDown(int32_t buttonState) { - return buttonState & - (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY - | AMOTION_EVENT_BUTTON_TERTIARY); -} - -static float calculateCommonVector(float a, float b) { - if (a > 0 && b > 0) { - return a < b ? a : b; - } else if (a < 0 && b < 0) { - return a > b ? a : b; - } else { - return 0; - } -} - -static void synthesizeButtonKey(InputReaderContext* context, int32_t action, - nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState, - int32_t buttonState, int32_t keyCode) { - if ( - (action == AKEY_EVENT_ACTION_DOWN - && !(lastButtonState & buttonState) - && (currentButtonState & buttonState)) - || (action == AKEY_EVENT_ACTION_UP - && (lastButtonState & buttonState) - && !(currentButtonState & buttonState))) { - NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId, - policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); - context->getListener()->notifyKey(&args); - } -} - -static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, - nsecs_t when, int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t lastButtonState, int32_t currentButtonState) { - synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, - lastButtonState, currentButtonState, - AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK); - synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, - lastButtonState, currentButtonState, - AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD); -} - - -// --- InputReader --- - -InputReader::InputReader(const sp<EventHubInterface>& eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener) : - mContext(this), mEventHub(eventHub), mPolicy(policy), - mNextSequenceNum(1), mGlobalMetaState(0), mGeneration(1), - mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), - mConfigurationChangesToRefresh(0) { - mQueuedListener = new QueuedInputListener(listener); - - { // acquire lock - AutoMutex _l(mLock); - - refreshConfigurationLocked(0); - updateGlobalMetaStateLocked(); - } // release lock -} - -InputReader::~InputReader() { - for (size_t i = 0; i < mDevices.size(); i++) { - delete mDevices.valueAt(i); - } -} - -void InputReader::loopOnce() { - int32_t oldGeneration; - int32_t timeoutMillis; - bool inputDevicesChanged = false; - std::vector<InputDeviceInfo> inputDevices; - { // acquire lock - AutoMutex _l(mLock); - - oldGeneration = mGeneration; - timeoutMillis = -1; - - uint32_t changes = mConfigurationChangesToRefresh; - if (changes) { - mConfigurationChangesToRefresh = 0; - timeoutMillis = 0; - refreshConfigurationLocked(changes); - } else if (mNextTimeout != LLONG_MAX) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); - } - } // release lock - - size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); - - { // acquire lock - AutoMutex _l(mLock); - mReaderIsAliveCondition.broadcast(); - - if (count) { - processEventsLocked(mEventBuffer, count); - } - - if (mNextTimeout != LLONG_MAX) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - if (now >= mNextTimeout) { -#if DEBUG_RAW_EVENTS - ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); -#endif - mNextTimeout = LLONG_MAX; - timeoutExpiredLocked(now); - } - } - - if (oldGeneration != mGeneration) { - inputDevicesChanged = true; - getInputDevicesLocked(inputDevices); - } - } // release lock - - // Send out a message that the describes the changed input devices. - if (inputDevicesChanged) { - mPolicy->notifyInputDevicesChanged(inputDevices); - } - - // Flush queued events out to the listener. - // This must happen outside of the lock because the listener could potentially call - // back into the InputReader's methods, such as getScanCodeState, or become blocked - // on another thread similarly waiting to acquire the InputReader lock thereby - // resulting in a deadlock. This situation is actually quite plausible because the - // listener is actually the input dispatcher, which calls into the window manager, - // which occasionally calls into the input reader. - mQueuedListener->flush(); -} - -void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { - for (const RawEvent* rawEvent = rawEvents; count;) { - int32_t type = rawEvent->type; - size_t batchSize = 1; - if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { - int32_t deviceId = rawEvent->deviceId; - while (batchSize < count) { - if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT - || rawEvent[batchSize].deviceId != deviceId) { - break; - } - batchSize += 1; - } -#if DEBUG_RAW_EVENTS - ALOGD("BatchSize: %zu Count: %zu", batchSize, count); -#endif - processEventsForDeviceLocked(deviceId, rawEvent, batchSize); - } else { - switch (rawEvent->type) { - case EventHubInterface::DEVICE_ADDED: - addDeviceLocked(rawEvent->when, rawEvent->deviceId); - break; - case EventHubInterface::DEVICE_REMOVED: - removeDeviceLocked(rawEvent->when, rawEvent->deviceId); - break; - case EventHubInterface::FINISHED_DEVICE_SCAN: - handleConfigurationChangedLocked(rawEvent->when); - break; - default: - ALOG_ASSERT(false); // can't happen - break; - } - } - count -= batchSize; - rawEvent += batchSize; - } -} - -void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); - return; - } - - InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId); - uint32_t classes = mEventHub->getDeviceClasses(deviceId); - int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId); - - InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes); - device->configure(when, &mConfig, 0); - device->reset(when); - - if (device->isIgnored()) { - ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, - identifier.name.c_str()); - } else { - ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, - identifier.name.c_str(), device->getSources()); - } - - mDevices.add(deviceId, device); - bumpGenerationLocked(); - - if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { - notifyExternalStylusPresenceChanged(); - } -} - -void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { - InputDevice* device = nullptr; - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); - return; - } - - device = mDevices.valueAt(deviceIndex); - mDevices.removeItemsAt(deviceIndex, 1); - bumpGenerationLocked(); - - if (device->isIgnored()) { - ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", - device->getId(), device->getName().c_str()); - } else { - ALOGI("Device removed: id=%d, name='%s', sources=0x%08x", - device->getId(), device->getName().c_str(), device->getSources()); - } - - if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { - notifyExternalStylusPresenceChanged(); - } - - device->reset(when); - delete device; -} - -InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, uint32_t classes) { - InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(), - controllerNumber, identifier, classes); - - // External devices. - if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { - device->setExternal(true); - } - - // Devices with mics. - if (classes & INPUT_DEVICE_CLASS_MIC) { - device->setMic(true); - } - - // Switch-like devices. - if (classes & INPUT_DEVICE_CLASS_SWITCH) { - device->addMapper(new SwitchInputMapper(device)); - } - - // Scroll wheel-like devices. - if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) { - device->addMapper(new RotaryEncoderInputMapper(device)); - } - - // Vibrator-like devices. - if (classes & INPUT_DEVICE_CLASS_VIBRATOR) { - device->addMapper(new VibratorInputMapper(device)); - } - - // Keyboard-like devices. - uint32_t keyboardSource = 0; - int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; - if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { - keyboardSource |= AINPUT_SOURCE_KEYBOARD; - } - if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { - keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; - } - if (classes & INPUT_DEVICE_CLASS_DPAD) { - keyboardSource |= AINPUT_SOURCE_DPAD; - } - if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { - keyboardSource |= AINPUT_SOURCE_GAMEPAD; - } - - if (keyboardSource != 0) { - device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType)); - } - - // Cursor-like devices. - if (classes & INPUT_DEVICE_CLASS_CURSOR) { - device->addMapper(new CursorInputMapper(device)); - } - - // Touchscreens and touchpad devices. - if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) { - device->addMapper(new MultiTouchInputMapper(device)); - } else if (classes & INPUT_DEVICE_CLASS_TOUCH) { - device->addMapper(new SingleTouchInputMapper(device)); - } - - // Joystick-like devices. - if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { - device->addMapper(new JoystickInputMapper(device)); - } - - // External stylus-like devices. - if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { - device->addMapper(new ExternalStylusInputMapper(device)); - } - - return device; -} - -void InputReader::processEventsForDeviceLocked(int32_t deviceId, - const RawEvent* rawEvents, size_t count) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - ALOGW("Discarding event for unknown deviceId %d.", deviceId); - return; - } - - InputDevice* device = mDevices.valueAt(deviceIndex); - if (device->isIgnored()) { - //ALOGD("Discarding event for ignored deviceId %d.", deviceId); - return; - } - - device->process(rawEvents, count); -} - -void InputReader::timeoutExpiredLocked(nsecs_t when) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - if (!device->isIgnored()) { - device->timeoutExpired(when); - } - } -} - -void InputReader::handleConfigurationChangedLocked(nsecs_t when) { - // Reset global meta state because it depends on the list of all configured devices. - updateGlobalMetaStateLocked(); - - // Enqueue configuration changed. - NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when); - mQueuedListener->notifyConfigurationChanged(&args); -} - -void InputReader::refreshConfigurationLocked(uint32_t changes) { - mPolicy->getReaderConfiguration(&mConfig); - mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); - - if (changes) { - ALOGI("Reconfiguring input devices. changes=0x%08x", changes); - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - - if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { - mEventHub->requestReopenDevices(); - } else { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - device->configure(now, &mConfig, changes); - } - } - } -} - -void InputReader::updateGlobalMetaStateLocked() { - mGlobalMetaState = 0; - - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - mGlobalMetaState |= device->getMetaState(); - } -} - -int32_t InputReader::getGlobalMetaStateLocked() { - return mGlobalMetaState; -} - -void InputReader::notifyExternalStylusPresenceChanged() { - refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE); -} - -void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) { - InputDeviceInfo info; - device->getDeviceInfo(&info); - outDevices.push_back(info); - } - } -} - -void InputReader::dispatchExternalStylusState(const StylusState& state) { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - device->updateExternalStylusState(state); - } -} - -void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) { - mDisableVirtualKeysTimeout = time; -} - -bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode) { - if (now < mDisableVirtualKeysTimeout) { - ALOGI("Dropping virtual key from device %s because virtual keys are " - "temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d", - device->getName().c_str(), - (mDisableVirtualKeysTimeout - now) * 0.000001, - keyCode, scanCode); - return true; - } else { - return false; - } -} - -void InputReader::fadePointerLocked() { - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - device->fadePointer(); - } -} - -void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) { - if (when < mNextTimeout) { - mNextTimeout = when; - mEventHub->wake(); - } -} - -int32_t InputReader::bumpGenerationLocked() { - return ++mGeneration; -} - -void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) { - AutoMutex _l(mLock); - getInputDevicesLocked(outInputDevices); -} - -void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) { - outInputDevices.clear(); - - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); - if (!device->isIgnored()) { - InputDeviceInfo info; - device->getDeviceInfo(&info); - outInputDevices.push_back(info); - } - } -} - -int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t keyCode) { - AutoMutex _l(mLock); - - return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState); -} - -int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t scanCode) { - AutoMutex _l(mLock); - - return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState); -} - -int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) { - AutoMutex _l(mLock); - - return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState); -} - -int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code, - GetStateFunc getStateFunc) { - int32_t result = AKEY_STATE_UNKNOWN; - if (deviceId >= 0) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = (device->*getStateFunc)(sourceMask, code); - } - } - } else { - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that - // value. Otherwise, return AKEY_STATE_UP as long as one device reports it. - int32_t currentResult = (device->*getStateFunc)(sourceMask, code); - if (currentResult >= AKEY_STATE_DOWN) { - return currentResult; - } else if (currentResult == AKEY_STATE_UP) { - result = currentResult; - } - } - } - } - return result; -} - -void InputReader::toggleCapsLockState(int32_t deviceId) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId); - return; - } - - InputDevice* device = mDevices.valueAt(deviceIndex); - if (device->isIgnored()) { - return; - } - - device->updateMetaState(AKEYCODE_CAPS_LOCK); -} - -bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { - AutoMutex _l(mLock); - - memset(outFlags, 0, numCodes); - return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags); -} - -bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { - bool result = false; - if (deviceId >= 0) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = device->markSupportedKeyCodes(sourceMask, - numCodes, keyCodes, outFlags); - } - } - } else { - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result |= device->markSupportedKeyCodes(sourceMask, - numCodes, keyCodes, outFlags); - } - } - } - return result; -} - -void InputReader::requestRefreshConfiguration(uint32_t changes) { - AutoMutex _l(mLock); - - if (changes) { - bool needWake = !mConfigurationChangesToRefresh; - mConfigurationChangesToRefresh |= changes; - - if (needWake) { - mEventHub->wake(); - } - } -} - -void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, - ssize_t repeat, int32_t token) { - AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - device->vibrate(pattern, patternSize, repeat, token); - } -} - -void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { - AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - device->cancelVibrate(token); - } -} - -bool InputReader::isInputDeviceEnabled(int32_t deviceId) { - AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - return device->isEnabled(); - } - ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); - return false; -} - -bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) { - AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); - return false; - } - - InputDevice* device = mDevices.valueAt(deviceIndex); - std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplay(); - // No associated display. By default, can dispatch to all displays. - if (!associatedDisplayId) { - return true; - } - - if (*associatedDisplayId == ADISPLAY_ID_NONE) { - ALOGW("Device has associated, but no associated display id."); - return true; - } - - return *associatedDisplayId == displayId; -} - -void InputReader::dump(std::string& dump) { - AutoMutex _l(mLock); - - mEventHub->dump(dump); - dump += "\n"; - - dump += "Input Reader State:\n"; - - for (size_t i = 0; i < mDevices.size(); i++) { - mDevices.valueAt(i)->dump(dump); - } - - dump += INDENT "Configuration:\n"; - dump += INDENT2 "ExcludedDeviceNames: ["; - for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) { - if (i != 0) { - dump += ", "; - } - dump += mConfig.excludedDeviceNames[i]; - } - dump += "]\n"; - dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n", - mConfig.virtualKeyQuietTime * 0.000001f); - - dump += StringPrintf(INDENT2 "PointerVelocityControlParameters: " - "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n", - mConfig.pointerVelocityControlParameters.scale, - mConfig.pointerVelocityControlParameters.lowThreshold, - mConfig.pointerVelocityControlParameters.highThreshold, - mConfig.pointerVelocityControlParameters.acceleration); - - dump += StringPrintf(INDENT2 "WheelVelocityControlParameters: " - "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, acceleration=%0.3f\n", - mConfig.wheelVelocityControlParameters.scale, - mConfig.wheelVelocityControlParameters.lowThreshold, - mConfig.wheelVelocityControlParameters.highThreshold, - mConfig.wheelVelocityControlParameters.acceleration); - - dump += StringPrintf(INDENT2 "PointerGesture:\n"); - dump += StringPrintf(INDENT3 "Enabled: %s\n", - toString(mConfig.pointerGesturesEnabled)); - dump += StringPrintf(INDENT3 "QuietInterval: %0.1fms\n", - mConfig.pointerGestureQuietInterval * 0.000001f); - dump += StringPrintf(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n", - mConfig.pointerGestureDragMinSwitchSpeed); - dump += StringPrintf(INDENT3 "TapInterval: %0.1fms\n", - mConfig.pointerGestureTapInterval * 0.000001f); - dump += StringPrintf(INDENT3 "TapDragInterval: %0.1fms\n", - mConfig.pointerGestureTapDragInterval * 0.000001f); - dump += StringPrintf(INDENT3 "TapSlop: %0.1fpx\n", - mConfig.pointerGestureTapSlop); - dump += StringPrintf(INDENT3 "MultitouchSettleInterval: %0.1fms\n", - mConfig.pointerGestureMultitouchSettleInterval * 0.000001f); - dump += StringPrintf(INDENT3 "MultitouchMinDistance: %0.1fpx\n", - mConfig.pointerGestureMultitouchMinDistance); - dump += StringPrintf(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n", - mConfig.pointerGestureSwipeTransitionAngleCosine); - dump += StringPrintf(INDENT3 "SwipeMaxWidthRatio: %0.1f\n", - mConfig.pointerGestureSwipeMaxWidthRatio); - dump += StringPrintf(INDENT3 "MovementSpeedRatio: %0.1f\n", - mConfig.pointerGestureMovementSpeedRatio); - dump += StringPrintf(INDENT3 "ZoomSpeedRatio: %0.1f\n", - mConfig.pointerGestureZoomSpeedRatio); - - dump += INDENT3 "Viewports:\n"; - mConfig.dump(dump); -} - -void InputReader::monitor() { - // Acquire and release the lock to ensure that the reader has not deadlocked. - mLock.lock(); - mEventHub->wake(); - mReaderIsAliveCondition.wait(mLock); - mLock.unlock(); - - // Check the EventHub - mEventHub->monitor(); -} - - -// --- InputReader::ContextImpl --- - -InputReader::ContextImpl::ContextImpl(InputReader* reader) : - mReader(reader) { -} - -void InputReader::ContextImpl::updateGlobalMetaState() { - // lock is already held by the input loop - mReader->updateGlobalMetaStateLocked(); -} - -int32_t InputReader::ContextImpl::getGlobalMetaState() { - // lock is already held by the input loop - return mReader->getGlobalMetaStateLocked(); -} - -void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) { - // lock is already held by the input loop - mReader->disableVirtualKeysUntilLocked(time); -} - -bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode) { - // lock is already held by the input loop - return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode); -} - -void InputReader::ContextImpl::fadePointer() { - // lock is already held by the input loop - mReader->fadePointerLocked(); -} - -void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) { - // lock is already held by the input loop - mReader->requestTimeoutAtTimeLocked(when); -} - -int32_t InputReader::ContextImpl::bumpGeneration() { - // lock is already held by the input loop - return mReader->bumpGenerationLocked(); -} - -void InputReader::ContextImpl::getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) { - // lock is already held by whatever called refreshConfigurationLocked - mReader->getExternalStylusDevicesLocked(outDevices); -} - -void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) { - mReader->dispatchExternalStylusState(state); -} - -InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { - return mReader->mPolicy.get(); -} - -InputListenerInterface* InputReader::ContextImpl::getListener() { - return mReader->mQueuedListener.get(); -} - -EventHubInterface* InputReader::ContextImpl::getEventHub() { - return mReader->mEventHub.get(); -} - -uint32_t InputReader::ContextImpl::getNextSequenceNum() { - return (mReader->mNextSequenceNum)++; -} - -// --- InputDevice --- - -InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, - int32_t controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes) : - mContext(context), mId(id), mGeneration(generation), mControllerNumber(controllerNumber), - mIdentifier(identifier), mClasses(classes), - mSources(0), mIsExternal(false), mHasMic(false), mDropUntilNextSync(false) { -} - -InputDevice::~InputDevice() { - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - delete mMappers[i]; - } - mMappers.clear(); -} - -bool InputDevice::isEnabled() { - return getEventHub()->isDeviceEnabled(mId); -} - -void InputDevice::setEnabled(bool enabled, nsecs_t when) { - if (isEnabled() == enabled) { - return; - } - - if (enabled) { - getEventHub()->enableDevice(mId); - reset(when); - } else { - reset(when); - getEventHub()->disableDevice(mId); - } - // Must change generation to flag this device as changed - bumpGeneration(); -} - -void InputDevice::dump(std::string& dump) { - InputDeviceInfo deviceInfo; - getDeviceInfo(&deviceInfo); - - dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(), - deviceInfo.getDisplayName().c_str()); - dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration); - dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal)); - dump += StringPrintf(INDENT2 "AssociatedDisplayPort: "); - if (mAssociatedDisplayPort) { - dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort); - } else { - dump += "<none>\n"; - } - dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic)); - dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); - dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); - - const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges(); - if (!ranges.empty()) { - dump += INDENT2 "Motion Ranges:\n"; - for (size_t i = 0; i < ranges.size(); i++) { - const InputDeviceInfo::MotionRange& range = ranges[i]; - const char* label = getAxisLabel(range.axis); - char name[32]; - if (label) { - strncpy(name, label, sizeof(name)); - name[sizeof(name) - 1] = '\0'; - } else { - snprintf(name, sizeof(name), "%d", range.axis); - } - dump += StringPrintf(INDENT3 "%s: source=0x%08x, " - "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n", - name, range.source, range.min, range.max, range.flat, range.fuzz, - range.resolution); - } - } - - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->dump(dump); - } -} - -void InputDevice::addMapper(InputMapper* mapper) { - mMappers.push_back(mapper); -} - -void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) { - mSources = 0; - - if (!isIgnored()) { - if (!changes) { // first time only - mContext->getEventHub()->getConfiguration(mId, &mConfiguration); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { - if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { - sp<KeyCharacterMap> keyboardLayout = - mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier); - if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) { - bumpGeneration(); - } - } - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) { - if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { - std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier); - if (mAlias != alias) { - mAlias = alias; - bumpGeneration(); - } - } - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { - ssize_t index = config->disabledDevices.indexOf(mId); - bool enabled = index < 0; - setEnabled(enabled, when); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - // In most situations, no port will be specified. - mAssociatedDisplayPort = std::nullopt; - // Find the display port that corresponds to the current input port. - const std::string& inputPort = mIdentifier.location; - if (!inputPort.empty()) { - const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations; - const auto& displayPort = ports.find(inputPort); - if (displayPort != ports.end()) { - mAssociatedDisplayPort = std::make_optional(displayPort->second); - } - } - } - - for (InputMapper* mapper : mMappers) { - mapper->configure(when, config, changes); - mSources |= mapper->getSources(); - } - } -} - -void InputDevice::reset(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->reset(when); - } - - mContext->updateGlobalMetaState(); - - notifyReset(when); -} - -void InputDevice::process(const RawEvent* rawEvents, size_t count) { - // Process all of the events in order for each mapper. - // We cannot simply ask each mapper to process them in bulk because mappers may - // have side-effects that must be interleaved. For example, joystick movement events and - // gamepad button presses are handled by different mappers but they should be dispatched - // in the order received. - for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) { -#if DEBUG_RAW_EVENTS - ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64, - rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, - rawEvent->when); -#endif - - if (mDropUntilNextSync) { - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - mDropUntilNextSync = false; -#if DEBUG_RAW_EVENTS - ALOGD("Recovered from input event buffer overrun."); -#endif - } else { -#if DEBUG_RAW_EVENTS - ALOGD("Dropped input event while waiting for next input sync."); -#endif - } - } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { - ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); - mDropUntilNextSync = true; - reset(rawEvent->when); - } else { - for (InputMapper* mapper : mMappers) { - mapper->process(rawEvent); - } - } - --count; - } -} - -void InputDevice::timeoutExpired(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->timeoutExpired(when); - } -} - -void InputDevice::updateExternalStylusState(const StylusState& state) { - for (InputMapper* mapper : mMappers) { - mapper->updateExternalStylusState(state); - } -} - -void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { - outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, - mIsExternal, mHasMic); - for (InputMapper* mapper : mMappers) { - mapper->populateDeviceInfo(outDeviceInfo); - } -} - -int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState); -} - -int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return getState(sourceMask, scanCode, & InputMapper::getScanCodeState); -} - -int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return getState(sourceMask, switchCode, & InputMapper::getSwitchState); -} - -int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { - int32_t result = AKEY_STATE_UNKNOWN; - for (InputMapper* mapper : mMappers) { - if (sourcesMatchMask(mapper->getSources(), sourceMask)) { - // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that - // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. - int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code); - if (currentResult >= AKEY_STATE_DOWN) { - return currentResult; - } else if (currentResult == AKEY_STATE_UP) { - result = currentResult; - } - } - } - return result; -} - -bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - bool result = false; - for (InputMapper* mapper : mMappers) { - if (sourcesMatchMask(mapper->getSources(), sourceMask)) { - result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); - } - } - return result; -} - -void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token) { - for (InputMapper* mapper : mMappers) { - mapper->vibrate(pattern, patternSize, repeat, token); - } -} - -void InputDevice::cancelVibrate(int32_t token) { - for (InputMapper* mapper : mMappers) { - mapper->cancelVibrate(token); - } -} - -void InputDevice::cancelTouch(nsecs_t when) { - for (InputMapper* mapper : mMappers) { - mapper->cancelTouch(when); - } -} - -int32_t InputDevice::getMetaState() { - int32_t result = 0; - for (InputMapper* mapper : mMappers) { - result |= mapper->getMetaState(); - } - return result; -} - -void InputDevice::updateMetaState(int32_t keyCode) { - for (InputMapper* mapper : mMappers) { - mapper->updateMetaState(keyCode); - } -} - -void InputDevice::fadePointer() { - for (InputMapper* mapper : mMappers) { - mapper->fadePointer(); - } -} - -void InputDevice::bumpGeneration() { - mGeneration = mContext->bumpGeneration(); -} - -void InputDevice::notifyReset(nsecs_t when) { - NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId); - mContext->getListener()->notifyDeviceReset(&args); -} - -std::optional<int32_t> InputDevice::getAssociatedDisplay() { - for (InputMapper* mapper : mMappers) { - std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplay(); - if (associatedDisplayId) { - return associatedDisplayId; - } - } - - return std::nullopt; -} - -// --- CursorButtonAccumulator --- - -CursorButtonAccumulator::CursorButtonAccumulator() { - clearButtons(); -} - -void CursorButtonAccumulator::reset(InputDevice* device) { - mBtnLeft = device->isKeyPressed(BTN_LEFT); - mBtnRight = device->isKeyPressed(BTN_RIGHT); - mBtnMiddle = device->isKeyPressed(BTN_MIDDLE); - mBtnBack = device->isKeyPressed(BTN_BACK); - mBtnSide = device->isKeyPressed(BTN_SIDE); - mBtnForward = device->isKeyPressed(BTN_FORWARD); - mBtnExtra = device->isKeyPressed(BTN_EXTRA); - mBtnTask = device->isKeyPressed(BTN_TASK); -} - -void CursorButtonAccumulator::clearButtons() { - mBtnLeft = 0; - mBtnRight = 0; - mBtnMiddle = 0; - mBtnBack = 0; - mBtnSide = 0; - mBtnForward = 0; - mBtnExtra = 0; - mBtnTask = 0; -} - -void CursorButtonAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_KEY) { - switch (rawEvent->code) { - case BTN_LEFT: - mBtnLeft = rawEvent->value; - break; - case BTN_RIGHT: - mBtnRight = rawEvent->value; - break; - case BTN_MIDDLE: - mBtnMiddle = rawEvent->value; - break; - case BTN_BACK: - mBtnBack = rawEvent->value; - break; - case BTN_SIDE: - mBtnSide = rawEvent->value; - break; - case BTN_FORWARD: - mBtnForward = rawEvent->value; - break; - case BTN_EXTRA: - mBtnExtra = rawEvent->value; - break; - case BTN_TASK: - mBtnTask = rawEvent->value; - break; - } - } -} - -uint32_t CursorButtonAccumulator::getButtonState() const { - uint32_t result = 0; - if (mBtnLeft) { - result |= AMOTION_EVENT_BUTTON_PRIMARY; - } - if (mBtnRight) { - result |= AMOTION_EVENT_BUTTON_SECONDARY; - } - if (mBtnMiddle) { - result |= AMOTION_EVENT_BUTTON_TERTIARY; - } - if (mBtnBack || mBtnSide) { - result |= AMOTION_EVENT_BUTTON_BACK; - } - if (mBtnForward || mBtnExtra) { - result |= AMOTION_EVENT_BUTTON_FORWARD; - } - return result; -} - - -// --- CursorMotionAccumulator --- - -CursorMotionAccumulator::CursorMotionAccumulator() { - clearRelativeAxes(); -} - -void CursorMotionAccumulator::reset(InputDevice* device) { - clearRelativeAxes(); -} - -void CursorMotionAccumulator::clearRelativeAxes() { - mRelX = 0; - mRelY = 0; -} - -void CursorMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_REL) { - switch (rawEvent->code) { - case REL_X: - mRelX = rawEvent->value; - break; - case REL_Y: - mRelY = rawEvent->value; - break; - } - } -} - -void CursorMotionAccumulator::finishSync() { - clearRelativeAxes(); -} - - -// --- CursorScrollAccumulator --- - -CursorScrollAccumulator::CursorScrollAccumulator() : - mHaveRelWheel(false), mHaveRelHWheel(false) { - clearRelativeAxes(); -} - -void CursorScrollAccumulator::configure(InputDevice* device) { - mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL); - mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL); -} - -void CursorScrollAccumulator::reset(InputDevice* device) { - clearRelativeAxes(); -} - -void CursorScrollAccumulator::clearRelativeAxes() { - mRelWheel = 0; - mRelHWheel = 0; -} - -void CursorScrollAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_REL) { - switch (rawEvent->code) { - case REL_WHEEL: - mRelWheel = rawEvent->value; - break; - case REL_HWHEEL: - mRelHWheel = rawEvent->value; - break; - } - } -} - -void CursorScrollAccumulator::finishSync() { - clearRelativeAxes(); -} - - -// --- TouchButtonAccumulator --- - -TouchButtonAccumulator::TouchButtonAccumulator() : - mHaveBtnTouch(false), mHaveStylus(false) { - clearButtons(); -} - -void TouchButtonAccumulator::configure(InputDevice* device) { - mHaveBtnTouch = device->hasKey(BTN_TOUCH); - mHaveStylus = device->hasKey(BTN_TOOL_PEN) - || device->hasKey(BTN_TOOL_RUBBER) - || device->hasKey(BTN_TOOL_BRUSH) - || device->hasKey(BTN_TOOL_PENCIL) - || device->hasKey(BTN_TOOL_AIRBRUSH); -} - -void TouchButtonAccumulator::reset(InputDevice* device) { - mBtnTouch = device->isKeyPressed(BTN_TOUCH); - mBtnStylus = device->isKeyPressed(BTN_STYLUS); - // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = - device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0); - mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER); - mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN); - mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER); - mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH); - mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL); - mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH); - mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE); - mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS); - mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP); - mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP); - mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP); -} - -void TouchButtonAccumulator::clearButtons() { - mBtnTouch = 0; - mBtnStylus = 0; - mBtnStylus2 = 0; - mBtnToolFinger = 0; - mBtnToolPen = 0; - mBtnToolRubber = 0; - mBtnToolBrush = 0; - mBtnToolPencil = 0; - mBtnToolAirbrush = 0; - mBtnToolMouse = 0; - mBtnToolLens = 0; - mBtnToolDoubleTap = 0; - mBtnToolTripleTap = 0; - mBtnToolQuadTap = 0; -} - -void TouchButtonAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_KEY) { - switch (rawEvent->code) { - case BTN_TOUCH: - mBtnTouch = rawEvent->value; - break; - case BTN_STYLUS: - mBtnStylus = rawEvent->value; - break; - case BTN_STYLUS2: - case BTN_0:// BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch - mBtnStylus2 = rawEvent->value; - break; - case BTN_TOOL_FINGER: - mBtnToolFinger = rawEvent->value; - break; - case BTN_TOOL_PEN: - mBtnToolPen = rawEvent->value; - break; - case BTN_TOOL_RUBBER: - mBtnToolRubber = rawEvent->value; - break; - case BTN_TOOL_BRUSH: - mBtnToolBrush = rawEvent->value; - break; - case BTN_TOOL_PENCIL: - mBtnToolPencil = rawEvent->value; - break; - case BTN_TOOL_AIRBRUSH: - mBtnToolAirbrush = rawEvent->value; - break; - case BTN_TOOL_MOUSE: - mBtnToolMouse = rawEvent->value; - break; - case BTN_TOOL_LENS: - mBtnToolLens = rawEvent->value; - break; - case BTN_TOOL_DOUBLETAP: - mBtnToolDoubleTap = rawEvent->value; - break; - case BTN_TOOL_TRIPLETAP: - mBtnToolTripleTap = rawEvent->value; - break; - case BTN_TOOL_QUADTAP: - mBtnToolQuadTap = rawEvent->value; - break; - } - } -} - -uint32_t TouchButtonAccumulator::getButtonState() const { - uint32_t result = 0; - if (mBtnStylus) { - result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; - } - if (mBtnStylus2) { - result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY; - } - return result; -} - -int32_t TouchButtonAccumulator::getToolType() const { - if (mBtnToolMouse || mBtnToolLens) { - return AMOTION_EVENT_TOOL_TYPE_MOUSE; - } - if (mBtnToolRubber) { - return AMOTION_EVENT_TOOL_TYPE_ERASER; - } - if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) { - return AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) { - return AMOTION_EVENT_TOOL_TYPE_FINGER; - } - return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - -bool TouchButtonAccumulator::isToolActive() const { - return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber - || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush - || mBtnToolMouse || mBtnToolLens - || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap; -} - -bool TouchButtonAccumulator::isHovering() const { - return mHaveBtnTouch && !mBtnTouch; -} - -bool TouchButtonAccumulator::hasStylus() const { - return mHaveStylus; -} - - -// --- RawPointerAxes --- - -RawPointerAxes::RawPointerAxes() { - clear(); -} - -void RawPointerAxes::clear() { - x.clear(); - y.clear(); - pressure.clear(); - touchMajor.clear(); - touchMinor.clear(); - toolMajor.clear(); - toolMinor.clear(); - orientation.clear(); - distance.clear(); - tiltX.clear(); - tiltY.clear(); - trackingId.clear(); - slot.clear(); -} - - -// --- RawPointerData --- - -RawPointerData::RawPointerData() { - clear(); -} - -void RawPointerData::clear() { - pointerCount = 0; - clearIdBits(); -} - -void RawPointerData::copyFrom(const RawPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointers[i] = other.pointers[i]; - - int id = pointers[i].id; - idToIndex[id] = other.idToIndex[id]; - } -} - -void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { - float x = 0, y = 0; - uint32_t count = touchingIdBits.count(); - if (count) { - for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - const Pointer& pointer = pointerForId(id); - x += pointer.x; - y += pointer.y; - } - x /= count; - y /= count; - } - *outX = x; - *outY = y; -} - - -// --- CookedPointerData --- - -CookedPointerData::CookedPointerData() { - clear(); -} - -void CookedPointerData::clear() { - pointerCount = 0; - hoveringIdBits.clear(); - touchingIdBits.clear(); -} - -void CookedPointerData::copyFrom(const CookedPointerData& other) { - pointerCount = other.pointerCount; - hoveringIdBits = other.hoveringIdBits; - touchingIdBits = other.touchingIdBits; - - for (uint32_t i = 0; i < pointerCount; i++) { - pointerProperties[i].copyFrom(other.pointerProperties[i]); - pointerCoords[i].copyFrom(other.pointerCoords[i]); - - int id = pointerProperties[i].id; - idToIndex[id] = other.idToIndex[id]; - } -} - - -// --- SingleTouchMotionAccumulator --- - -SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() { - clearAbsoluteAxes(); -} - -void SingleTouchMotionAccumulator::reset(InputDevice* device) { - mAbsX = device->getAbsoluteAxisValue(ABS_X); - mAbsY = device->getAbsoluteAxisValue(ABS_Y); - mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE); - mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH); - mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE); - mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X); - mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y); -} - -void SingleTouchMotionAccumulator::clearAbsoluteAxes() { - mAbsX = 0; - mAbsY = 0; - mAbsPressure = 0; - mAbsToolWidth = 0; - mAbsDistance = 0; - mAbsTiltX = 0; - mAbsTiltY = 0; -} - -void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - switch (rawEvent->code) { - case ABS_X: - mAbsX = rawEvent->value; - break; - case ABS_Y: - mAbsY = rawEvent->value; - break; - case ABS_PRESSURE: - mAbsPressure = rawEvent->value; - break; - case ABS_TOOL_WIDTH: - mAbsToolWidth = rawEvent->value; - break; - case ABS_DISTANCE: - mAbsDistance = rawEvent->value; - break; - case ABS_TILT_X: - mAbsTiltX = rawEvent->value; - break; - case ABS_TILT_Y: - mAbsTiltY = rawEvent->value; - break; - } - } -} - - -// --- MultiTouchMotionAccumulator --- - -MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() : - mCurrentSlot(-1), mSlots(nullptr), mSlotCount(0), mUsingSlotsProtocol(false), - mHaveStylus(false), mDeviceTimestamp(0) { -} - -MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { - delete[] mSlots; -} - -void MultiTouchMotionAccumulator::configure(InputDevice* device, - size_t slotCount, bool usingSlotsProtocol) { - mSlotCount = slotCount; - mUsingSlotsProtocol = usingSlotsProtocol; - mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE); - - delete[] mSlots; - mSlots = new Slot[slotCount]; -} - -void MultiTouchMotionAccumulator::reset(InputDevice* device) { - // Unfortunately there is no way to read the initial contents of the slots. - // So when we reset the accumulator, we must assume they are all zeroes. - if (mUsingSlotsProtocol) { - // Query the driver for the current slot index and use it as the initial slot - // before we start reading events from the device. It is possible that the - // current slot index will not be the same as it was when the first event was - // written into the evdev buffer, which means the input mapper could start - // out of sync with the initial state of the events in the evdev buffer. - // In the extremely unlikely case that this happens, the data from - // two slots will be confused until the next ABS_MT_SLOT event is received. - // This can cause the touch point to "jump", but at least there will be - // no stuck touches. - int32_t initialSlot; - status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), - ABS_MT_SLOT, &initialSlot); - if (status) { - ALOGD("Could not retrieve current multitouch slot index. status=%d", status); - initialSlot = -1; - } - clearSlots(initialSlot); - } else { - clearSlots(-1); - } - mDeviceTimestamp = 0; -} - -void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { - if (mSlots) { - for (size_t i = 0; i < mSlotCount; i++) { - mSlots[i].clear(); - } - } - mCurrentSlot = initialSlot; -} - -void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { - if (rawEvent->type == EV_ABS) { - bool newSlot = false; - if (mUsingSlotsProtocol) { - if (rawEvent->code == ABS_MT_SLOT) { - mCurrentSlot = rawEvent->value; - newSlot = true; - } - } else if (mCurrentSlot < 0) { - mCurrentSlot = 0; - } - - if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { -#if DEBUG_POINTERS - if (newSlot) { - ALOGW("MultiTouch device emitted invalid slot index %d but it " - "should be between 0 and %zd; ignoring this slot.", - mCurrentSlot, mSlotCount - 1); - } -#endif - } else { - Slot* slot = &mSlots[mCurrentSlot]; - - switch (rawEvent->code) { - case ABS_MT_POSITION_X: - slot->mInUse = true; - slot->mAbsMTPositionX = rawEvent->value; - break; - case ABS_MT_POSITION_Y: - slot->mInUse = true; - slot->mAbsMTPositionY = rawEvent->value; - break; - case ABS_MT_TOUCH_MAJOR: - slot->mInUse = true; - slot->mAbsMTTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - slot->mInUse = true; - slot->mAbsMTTouchMinor = rawEvent->value; - slot->mHaveAbsMTTouchMinor = true; - break; - case ABS_MT_WIDTH_MAJOR: - slot->mInUse = true; - slot->mAbsMTWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - slot->mInUse = true; - slot->mAbsMTWidthMinor = rawEvent->value; - slot->mHaveAbsMTWidthMinor = true; - break; - case ABS_MT_ORIENTATION: - slot->mInUse = true; - slot->mAbsMTOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - if (mUsingSlotsProtocol && rawEvent->value < 0) { - // The slot is no longer in use but it retains its previous contents, - // which may be reused for subsequent touches. - slot->mInUse = false; - } else { - slot->mInUse = true; - slot->mAbsMTTrackingId = rawEvent->value; - } - break; - case ABS_MT_PRESSURE: - slot->mInUse = true; - slot->mAbsMTPressure = rawEvent->value; - break; - case ABS_MT_DISTANCE: - slot->mInUse = true; - slot->mAbsMTDistance = rawEvent->value; - break; - case ABS_MT_TOOL_TYPE: - slot->mInUse = true; - slot->mAbsMTToolType = rawEvent->value; - slot->mHaveAbsMTToolType = true; - break; - } - } - } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { - // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - mCurrentSlot += 1; - } else if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) { - mDeviceTimestamp = rawEvent->value; - } -} - -void MultiTouchMotionAccumulator::finishSync() { - if (!mUsingSlotsProtocol) { - clearSlots(-1); - } -} - -bool MultiTouchMotionAccumulator::hasStylus() const { - return mHaveStylus; -} - - -// --- MultiTouchMotionAccumulator::Slot --- - -MultiTouchMotionAccumulator::Slot::Slot() { - clear(); -} - -void MultiTouchMotionAccumulator::Slot::clear() { - mInUse = false; - mHaveAbsMTTouchMinor = false; - mHaveAbsMTWidthMinor = false; - mHaveAbsMTToolType = false; - mAbsMTPositionX = 0; - mAbsMTPositionY = 0; - mAbsMTTouchMajor = 0; - mAbsMTTouchMinor = 0; - mAbsMTWidthMajor = 0; - mAbsMTWidthMinor = 0; - mAbsMTOrientation = 0; - mAbsMTTrackingId = -1; - mAbsMTPressure = 0; - mAbsMTDistance = 0; - mAbsMTToolType = 0; -} - -int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { - if (mHaveAbsMTToolType) { - switch (mAbsMTToolType) { - case MT_TOOL_FINGER: - return AMOTION_EVENT_TOOL_TYPE_FINGER; - case MT_TOOL_PEN: - return AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - } - return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; -} - - -// --- InputMapper --- - -InputMapper::InputMapper(InputDevice* device) : - mDevice(device), mContext(device->getContext()) { -} - -InputMapper::~InputMapper() { -} - -void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { - info->addSource(getSources()); -} - -void InputMapper::dump(std::string& dump) { -} - -void InputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { -} - -void InputMapper::reset(nsecs_t when) { -} - -void InputMapper::timeoutExpired(nsecs_t when) { -} - -int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return AKEY_STATE_UNKNOWN; -} - -int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return AKEY_STATE_UNKNOWN; -} - -int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return AKEY_STATE_UNKNOWN; -} - -bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - return false; -} - -void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token) { -} - -void InputMapper::cancelVibrate(int32_t token) { -} - -void InputMapper::cancelTouch(nsecs_t when) { -} - -int32_t InputMapper::getMetaState() { - return 0; -} - -void InputMapper::updateMetaState(int32_t keyCode) { -} - -void InputMapper::updateExternalStylusState(const StylusState& state) { - -} - -void InputMapper::fadePointer() { -} - -status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { - return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo); -} - -void InputMapper::bumpGeneration() { - mDevice->bumpGeneration(); -} - -void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, - const RawAbsoluteAxisInfo& axis, const char* name) { - if (axis.valid) { - dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n", - name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution); - } else { - dump += StringPrintf(INDENT4 "%s: unknown range\n", name); - } -} - -void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { - dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when); - dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure); - dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons); - dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType); -} - -// --- SwitchInputMapper --- - -SwitchInputMapper::SwitchInputMapper(InputDevice* device) : - InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) { -} - -SwitchInputMapper::~SwitchInputMapper() { -} - -uint32_t SwitchInputMapper::getSources() { - return AINPUT_SOURCE_SWITCH; -} - -void SwitchInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_SW: - processSwitch(rawEvent->code, rawEvent->value); - break; - - case EV_SYN: - if (rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } - } -} - -void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { - if (switchCode >= 0 && switchCode < 32) { - if (switchValue) { - mSwitchValues |= 1 << switchCode; - } else { - mSwitchValues &= ~(1 << switchCode); - } - mUpdatedSwitchMask |= 1 << switchCode; - } -} - -void SwitchInputMapper::sync(nsecs_t when) { - if (mUpdatedSwitchMask) { - uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; - NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues, - mUpdatedSwitchMask); - getListener()->notifySwitch(&args); - - mUpdatedSwitchMask = 0; - } -} - -int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return getEventHub()->getSwitchState(getDeviceId(), switchCode); -} - -void SwitchInputMapper::dump(std::string& dump) { - dump += INDENT2 "Switch Input Mapper:\n"; - dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues); -} - -// --- VibratorInputMapper --- - -VibratorInputMapper::VibratorInputMapper(InputDevice* device) : - InputMapper(device), mVibrating(false) { -} - -VibratorInputMapper::~VibratorInputMapper() { -} - -uint32_t VibratorInputMapper::getSources() { - return 0; -} - -void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - info->setVibrator(true); -} - -void VibratorInputMapper::process(const RawEvent* rawEvent) { - // TODO: Handle FF_STATUS, although it does not seem to be widely supported. -} - -void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token) { -#if DEBUG_VIBRATOR - std::string patternStr; - for (size_t i = 0; i < patternSize; i++) { - if (i != 0) { - patternStr += ", "; - } - patternStr += StringPrintf("%" PRId64, pattern[i]); - } - ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", - getDeviceId(), patternStr.c_str(), repeat, token); -#endif - - mVibrating = true; - memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t)); - mPatternSize = patternSize; - mRepeat = repeat; - mToken = token; - mIndex = -1; - - nextStep(); -} - -void VibratorInputMapper::cancelVibrate(int32_t token) { -#if DEBUG_VIBRATOR - ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); -#endif - - if (mVibrating && mToken == token) { - stopVibrating(); - } -} - -void VibratorInputMapper::timeoutExpired(nsecs_t when) { - if (mVibrating) { - if (when >= mNextStepTime) { - nextStep(); - } else { - getContext()->requestTimeoutAtTime(mNextStepTime); - } - } -} - -void VibratorInputMapper::nextStep() { - mIndex += 1; - if (size_t(mIndex) >= mPatternSize) { - if (mRepeat < 0) { - // We are done. - stopVibrating(); - return; - } - mIndex = mRepeat; - } - - bool vibratorOn = mIndex & 1; - nsecs_t duration = mPattern[mIndex]; - if (vibratorOn) { -#if DEBUG_VIBRATOR - ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration); -#endif - getEventHub()->vibrate(getDeviceId(), duration); - } else { -#if DEBUG_VIBRATOR - ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); -#endif - getEventHub()->cancelVibrate(getDeviceId()); - } - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - mNextStepTime = now + duration; - getContext()->requestTimeoutAtTime(mNextStepTime); -#if DEBUG_VIBRATOR - ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f); -#endif -} - -void VibratorInputMapper::stopVibrating() { - mVibrating = false; -#if DEBUG_VIBRATOR - ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); -#endif - getEventHub()->cancelVibrate(getDeviceId()); -} - -void VibratorInputMapper::dump(std::string& dump) { - dump += INDENT2 "Vibrator Input Mapper:\n"; - dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating)); -} - - -// --- KeyboardInputMapper --- - -KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, - uint32_t source, int32_t keyboardType) : - InputMapper(device), mSource(source), mKeyboardType(keyboardType) { -} - -KeyboardInputMapper::~KeyboardInputMapper() { -} - -uint32_t KeyboardInputMapper::getSources() { - return mSource; -} - -int32_t KeyboardInputMapper::getOrientation() { - if (mViewport) { - return mViewport->orientation; - } - return DISPLAY_ORIENTATION_0; -} - -int32_t KeyboardInputMapper::getDisplayId() { - if (mViewport) { - return mViewport->displayId; - } - return ADISPLAY_ID_NONE; -} - -void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - info->setKeyboardType(mKeyboardType); - info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId())); -} - -void KeyboardInputMapper::dump(std::string& dump) { - dump += INDENT2 "Keyboard Input Mapper:\n"; - dumpParameters(dump); - dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); - dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); - dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); - dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); - dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); -} - -void KeyboardInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - // Configure basic parameters. - configureParameters(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - if (mParameters.orientationAware) { - mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - } - } -} - -static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const *property) { - int32_t mapped = 0; - if (config.tryGetProperty(String8(property), mapped) && mapped > 0) { - for (size_t i = 0; i < stemKeyRotationMapSize; i++) { - if (stemKeyRotationMap[i][0] == keyCode) { - stemKeyRotationMap[i][1] = mapped; - return; - } - } - } -} - -void KeyboardInputMapper::configureParameters() { - mParameters.orientationAware = false; - const PropertyMap& config = getDevice()->getConfiguration(); - config.tryGetProperty(String8("keyboard.orientationAware"), - mParameters.orientationAware); - - if (mParameters.orientationAware) { - mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); - mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); - mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); - mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3"); - } - - mParameters.handlesKeyRepeat = false; - config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), - mParameters.handlesKeyRepeat); -} - -void KeyboardInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", - toString(mParameters.orientationAware)); - dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", - toString(mParameters.handlesKeyRepeat)); -} - -void KeyboardInputMapper::reset(nsecs_t when) { - mMetaState = AMETA_NONE; - mDownTime = 0; - mKeyDowns.clear(); - mCurrentHidUsage = 0; - - resetLedState(); - - InputMapper::reset(when); -} - -void KeyboardInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_KEY: { - int32_t scanCode = rawEvent->code; - int32_t usageCode = mCurrentHidUsage; - mCurrentHidUsage = 0; - - if (isKeyboardOrGamepadKey(scanCode)) { - processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode); - } - break; - } - case EV_MSC: { - if (rawEvent->code == MSC_SCAN) { - mCurrentHidUsage = rawEvent->value; - } - break; - } - case EV_SYN: { - if (rawEvent->code == SYN_REPORT) { - mCurrentHidUsage = 0; - } - } - } -} - -bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { - return scanCode < BTN_MOUSE - || scanCode >= KEY_OK - || (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) - || (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); -} - -bool KeyboardInputMapper::isMediaKey(int32_t keyCode) { - switch (keyCode) { - case AKEYCODE_MEDIA_PLAY: - case AKEYCODE_MEDIA_PAUSE: - case AKEYCODE_MEDIA_PLAY_PAUSE: - case AKEYCODE_MUTE: - case AKEYCODE_HEADSETHOOK: - case AKEYCODE_MEDIA_STOP: - case AKEYCODE_MEDIA_NEXT: - case AKEYCODE_MEDIA_PREVIOUS: - case AKEYCODE_MEDIA_REWIND: - case AKEYCODE_MEDIA_RECORD: - case AKEYCODE_MEDIA_FAST_FORWARD: - case AKEYCODE_MEDIA_SKIP_FORWARD: - case AKEYCODE_MEDIA_SKIP_BACKWARD: - case AKEYCODE_MEDIA_STEP_FORWARD: - case AKEYCODE_MEDIA_STEP_BACKWARD: - case AKEYCODE_MEDIA_AUDIO_TRACK: - case AKEYCODE_VOLUME_UP: - case AKEYCODE_VOLUME_DOWN: - case AKEYCODE_VOLUME_MUTE: - case AKEYCODE_TV_AUDIO_DESCRIPTION: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: - case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: - return true; - } - return false; -} - -void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, - int32_t usageCode) { - int32_t keyCode; - int32_t keyMetaState; - uint32_t policyFlags; - - if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, - &keyCode, &keyMetaState, &policyFlags)) { - keyCode = AKEYCODE_UNKNOWN; - keyMetaState = mMetaState; - policyFlags = 0; - } - - if (down) { - // Rotate key codes according to orientation if needed. - if (mParameters.orientationAware) { - keyCode = rotateKeyCode(keyCode, getOrientation()); - } - - // Add key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { - // key repeat, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; - } else { - // key down - if ((policyFlags & POLICY_FLAG_VIRTUAL) - && mContext->shouldDropVirtualKey(when, - getDevice(), keyCode, scanCode)) { - return; - } - if (policyFlags & POLICY_FLAG_GESTURE) { - mDevice->cancelTouch(when); - } - - KeyDown keyDown; - keyDown.keyCode = keyCode; - keyDown.scanCode = scanCode; - mKeyDowns.push_back(keyDown); - } - - mDownTime = when; - } else { - // Remove key down. - ssize_t keyDownIndex = findKeyDown(scanCode); - if (keyDownIndex >= 0) { - // key up, be sure to use same keycode as before in case of rotation - keyCode = mKeyDowns[keyDownIndex].keyCode; - mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex); - } else { - // key was not actually down - ALOGI("Dropping key up from device %s because the key was not down. " - "keyCode=%d, scanCode=%d", - getDeviceName().c_str(), keyCode, scanCode); - return; - } - } - - if (updateMetaStateIfNeeded(keyCode, down)) { - // If global meta state changed send it along with the key. - // If it has not changed then we'll use what keymap gave us, - // since key replacement logic might temporarily reset a few - // meta bits for given key. - keyMetaState = mMetaState; - } - - nsecs_t downTime = mDownTime; - - // Key down on external an keyboard should wake the device. - // We don't do this for internal keyboards to prevent them from waking up in your pocket. - // For internal keyboards, the key layout file should specify the policy flags for - // each wake key individually. - // TODO: Use the input device configuration to control this behavior more finely. - if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) { - policyFlags |= POLICY_FLAG_WAKE; - } - - if (mParameters.handlesKeyRepeat) { - policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; - } - - NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - getDisplayId(), policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); - getListener()->notifyKey(&args); -} - -ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { - size_t n = mKeyDowns.size(); - for (size_t i = 0; i < n; i++) { - if (mKeyDowns[i].scanCode == scanCode) { - return i; - } - } - return -1; -} - -int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); -} - -int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); -} - -bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); -} - -int32_t KeyboardInputMapper::getMetaState() { - return mMetaState; -} - -void KeyboardInputMapper::updateMetaState(int32_t keyCode) { - updateMetaStateIfNeeded(keyCode, false); -} - -bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) { - int32_t oldMetaState = mMetaState; - int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState); - bool metaStateChanged = oldMetaState != newMetaState; - if (metaStateChanged) { - mMetaState = newMetaState; - updateLedState(false); - - getContext()->updateGlobalMetaState(); - } - - return metaStateChanged; -} - -void KeyboardInputMapper::resetLedState() { - initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK); - initializeLedState(mNumLockLedState, ALED_NUM_LOCK); - initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK); - - updateLedState(true); -} - -void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) { - ledState.avail = getEventHub()->hasLed(getDeviceId(), led); - ledState.on = false; -} - -void KeyboardInputMapper::updateLedState(bool reset) { - updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, - AMETA_CAPS_LOCK_ON, reset); - updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, - AMETA_NUM_LOCK_ON, reset); - updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, - AMETA_SCROLL_LOCK_ON, reset); -} - -void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, - int32_t led, int32_t modifier, bool reset) { - if (ledState.avail) { - bool desiredState = (mMetaState & modifier) != 0; - if (reset || ledState.on != desiredState) { - getEventHub()->setLedState(getDeviceId(), led, desiredState); - ledState.on = desiredState; - } - } -} - - -// --- CursorInputMapper --- - -CursorInputMapper::CursorInputMapper(InputDevice* device) : - InputMapper(device) { -} - -CursorInputMapper::~CursorInputMapper() { -} - -uint32_t CursorInputMapper::getSources() { - return mSource; -} - -void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mParameters.mode == Parameters::MODE_POINTER) { - float minX, minY, maxX, maxY; - if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { - info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f); - info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f); - } - } else { - info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f); - info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f); - } - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); - - if (mCursorScrollAccumulator.haveRelativeVWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); - } - if (mCursorScrollAccumulator.haveRelativeHWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); - } -} - -void CursorInputMapper::dump(std::string& dump) { - dump += INDENT2 "Cursor Input Mapper:\n"; - dumpParameters(dump); - dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale); - dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale); - dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision); - dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision); - dump += StringPrintf(INDENT3 "HaveVWheel: %s\n", - toString(mCursorScrollAccumulator.haveRelativeVWheel())); - dump += StringPrintf(INDENT3 "HaveHWheel: %s\n", - toString(mCursorScrollAccumulator.haveRelativeHWheel())); - dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); - dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); - dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); - dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState); - dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState))); - dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); -} - -void CursorInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - mCursorScrollAccumulator.configure(getDevice()); - - // Configure basic parameters. - configureParameters(); - - // Configure device mode. - switch (mParameters.mode) { - case Parameters::MODE_POINTER_RELATIVE: - // Should not happen during first time configuration. - ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER"); - mParameters.mode = Parameters::MODE_POINTER; - [[fallthrough]]; - case Parameters::MODE_POINTER: - mSource = AINPUT_SOURCE_MOUSE; - mXPrecision = 1.0f; - mYPrecision = 1.0f; - mXScale = 1.0f; - mYScale = 1.0f; - mPointerController = getPolicy()->obtainPointerController(getDeviceId()); - break; - case Parameters::MODE_NAVIGATION: - mSource = AINPUT_SOURCE_TRACKBALL; - mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - break; - } - - mVWheelScale = 1.0f; - mHWheelScale = 1.0f; - } - - if ((!changes && config->pointerCapture) - || (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) { - if (config->pointerCapture) { - if (mParameters.mode == Parameters::MODE_POINTER) { - mParameters.mode = Parameters::MODE_POINTER_RELATIVE; - mSource = AINPUT_SOURCE_MOUSE_RELATIVE; - // Keep PointerController around in order to preserve the pointer position. - mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } else { - ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); - } - } else { - if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) { - mParameters.mode = Parameters::MODE_POINTER; - mSource = AINPUT_SOURCE_MOUSE; - } else { - ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE"); - } - } - bumpGeneration(); - if (changes) { - getDevice()->notifyReset(when); - } - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { - mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters); - mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters); - mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - mOrientation = DISPLAY_ORIENTATION_0; - if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { - std::optional<DisplayViewport> internalViewport = - config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - if (internalViewport) { - mOrientation = internalViewport->orientation; - } - } - - // Update the PointerController if viewports changed. - if (mParameters.mode == Parameters::MODE_POINTER) { - getPolicy()->obtainPointerController(getDeviceId()); - } - bumpGeneration(); - } -} - -void CursorInputMapper::configureParameters() { - mParameters.mode = Parameters::MODE_POINTER; - String8 cursorModeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) { - if (cursorModeString == "navigation") { - mParameters.mode = Parameters::MODE_NAVIGATION; - } else if (cursorModeString != "pointer" && cursorModeString != "default") { - ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string()); - } - } - - mParameters.orientationAware = false; - getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"), - mParameters.orientationAware); - - mParameters.hasAssociatedDisplay = false; - if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { - mParameters.hasAssociatedDisplay = true; - } -} - -void CursorInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n", - toString(mParameters.hasAssociatedDisplay)); - - switch (mParameters.mode) { - case Parameters::MODE_POINTER: - dump += INDENT4 "Mode: pointer\n"; - break; - case Parameters::MODE_POINTER_RELATIVE: - dump += INDENT4 "Mode: relative pointer\n"; - break; - case Parameters::MODE_NAVIGATION: - dump += INDENT4 "Mode: navigation\n"; - break; - default: - ALOG_ASSERT(false); - } - - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", - toString(mParameters.orientationAware)); -} - -void CursorInputMapper::reset(nsecs_t when) { - mButtonState = 0; - mDownTime = 0; - - mPointerVelocityControl.reset(); - mWheelXVelocityControl.reset(); - mWheelYVelocityControl.reset(); - - mCursorButtonAccumulator.reset(getDevice()); - mCursorMotionAccumulator.reset(getDevice()); - mCursorScrollAccumulator.reset(getDevice()); - - InputMapper::reset(when); -} - -void CursorInputMapper::process(const RawEvent* rawEvent) { - mCursorButtonAccumulator.process(rawEvent); - mCursorMotionAccumulator.process(rawEvent); - mCursorScrollAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void CursorInputMapper::sync(nsecs_t when) { - int32_t lastButtonState = mButtonState; - int32_t currentButtonState = mCursorButtonAccumulator.getButtonState(); - mButtonState = currentButtonState; - - bool wasDown = isPointerDown(lastButtonState); - bool down = isPointerDown(currentButtonState); - bool downChanged; - if (!wasDown && down) { - mDownTime = when; - downChanged = true; - } else if (wasDown && !down) { - downChanged = true; - } else { - downChanged = false; - } - nsecs_t downTime = mDownTime; - bool buttonsChanged = currentButtonState != lastButtonState; - int32_t buttonsPressed = currentButtonState & ~lastButtonState; - int32_t buttonsReleased = lastButtonState & ~currentButtonState; - - float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale; - float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale; - bool moved = deltaX != 0 || deltaY != 0; - - // Rotate delta according to orientation if needed. - if (mParameters.orientationAware && mParameters.hasAssociatedDisplay - && (deltaX != 0.0f || deltaY != 0.0f)) { - rotateDelta(mOrientation, &deltaX, &deltaY); - } - - // Move the pointer. - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE; - - PointerCoords pointerCoords; - pointerCoords.clear(); - - float vscroll = mCursorScrollAccumulator.getRelativeVWheel(); - float hscroll = mCursorScrollAccumulator.getRelativeHWheel(); - bool scrolled = vscroll != 0 || hscroll != 0; - - mWheelYVelocityControl.move(when, nullptr, &vscroll); - mWheelXVelocityControl.move(when, &hscroll, nullptr); - - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - int32_t displayId; - if (mSource == AINPUT_SOURCE_MOUSE) { - if (moved || scrolled || buttonsChanged) { - mPointerController->setPresentation( - PointerControllerInterface::PRESENTATION_POINTER); - - if (moved) { - mPointerController->move(deltaX, deltaY); - } - - if (buttonsChanged) { - mPointerController->setButtonState(currentButtonState); - } - - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } - - float x, y; - mPointerController->getPosition(&x, &y); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); - displayId = mPointerController->getDisplayId(); - } else { - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); - displayId = ADISPLAY_ID_NONE; - } - - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); - - // Moving an external trackball or mouse should wake the device. - // We don't do this for internal cursor devices to prevent them from waking up - // the device in your pocket. - // TODO: Use the input device configuration to control this behavior more finely. - uint32_t policyFlags = 0; - if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) { - policyFlags |= POLICY_FLAG_WAKE; - } - - // Synthesize key down from buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, - displayId, policyFlags, lastButtonState, currentButtonState); - - // Send motion event. - if (downChanged || moved || scrolled || buttonsChanged) { - int32_t metaState = mContext->getGlobalMetaState(); - int32_t buttonState = lastButtonState; - int32_t motionEventAction; - if (downChanged) { - motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) { - motionEventAction = AMOTION_EVENT_ACTION_MOVE; - } else { - motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE; - } - - if (buttonsReleased) { - BitSet32 released(buttonsReleased); - while (!released.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); - buttonState &= ~actionButton; - NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, - metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&releaseArgs); - } - } - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, - displayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - - if (buttonsPressed) { - BitSet32 pressed(buttonsPressed); - while (!pressed.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); - buttonState |= actionButton; - NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS, - actionButton, 0, metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&pressArgs); - } - } - - ALOG_ASSERT(buttonState == currentButtonState); - - // Send hover move after UP to tell the application that the mouse is hovering now. - if (motionEventAction == AMOTION_EVENT_ACTION_UP - && (mSource == AINPUT_SOURCE_MOUSE)) { - NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, currentButtonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&hoverArgs); - } - - // Send scroll events. - if (scrolled) { - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); - } - } - - // Synthesize key up from buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, - displayId, policyFlags, lastButtonState, currentButtonState); - - mCursorMotionAccumulator.finishSync(); - mCursorScrollAccumulator.finishSync(); -} - -int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); - } else { - return AKEY_STATE_UNKNOWN; - } -} - -void CursorInputMapper::fadePointer() { - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } -} - -std::optional<int32_t> CursorInputMapper::getAssociatedDisplay() { - if (mParameters.hasAssociatedDisplay) { - if (mParameters.mode == Parameters::MODE_POINTER) { - return std::make_optional(mPointerController->getDisplayId()); - } else { - // If the device is orientationAware and not a mouse, - // it expects to dispatch events to any display - return std::make_optional(ADISPLAY_ID_NONE); - } - } - return std::nullopt; -} - -// --- RotaryEncoderInputMapper --- - -RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) : - InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) { - mSource = AINPUT_SOURCE_ROTARY_ENCODER; -} - -RotaryEncoderInputMapper::~RotaryEncoderInputMapper() { -} - -uint32_t RotaryEncoderInputMapper::getSources() { - return mSource; -} - -void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { - float res = 0.0f; - if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) { - ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); - } - if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"), - mScalingFactor)) { - ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," - "default to 1.0!\n"); - mScalingFactor = 1.0f; - } - info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - res * mScalingFactor); - } -} - -void RotaryEncoderInputMapper::dump(std::string& dump) { - dump += INDENT2 "Rotary Encoder Input Mapper:\n"; - dump += StringPrintf(INDENT3 "HaveWheel: %s\n", - toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel())); -} - -void RotaryEncoderInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - InputMapper::configure(when, config, changes); - if (!changes) { - mRotaryEncoderScrollAccumulator.configure(getDevice()); - } - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - std::optional<DisplayViewport> internalViewport = - config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - if (internalViewport) { - mOrientation = internalViewport->orientation; - } else { - mOrientation = DISPLAY_ORIENTATION_0; - } - } -} - -void RotaryEncoderInputMapper::reset(nsecs_t when) { - mRotaryEncoderScrollAccumulator.reset(getDevice()); - - InputMapper::reset(when); -} - -void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { - mRotaryEncoderScrollAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void RotaryEncoderInputMapper::sync(nsecs_t when) { - PointerCoords pointerCoords; - pointerCoords.clear(); - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - - float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); - bool scrolled = scroll != 0; - - // This is not a pointer, so it's not associated with a display. - int32_t displayId = ADISPLAY_ID_NONE; - - // Moving the rotary encoder should wake the device (if specified). - uint32_t policyFlags = 0; - if (scrolled && getDevice()->isExternal()) { - policyFlags |= POLICY_FLAG_WAKE; - } - - if (mOrientation == DISPLAY_ORIENTATION_180) { - scroll = -scroll; - } - - // Send motion event. - if (scrolled) { - int32_t metaState = mContext->getGlobalMetaState(); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - - NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, /* buttonState */ 0, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - 0, 0, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); - } - - mRotaryEncoderScrollAccumulator.finishSync(); -} - -// --- TouchInputMapper --- - -TouchInputMapper::TouchInputMapper(InputDevice* device) : - InputMapper(device), - mSource(0), mDeviceMode(DEVICE_MODE_DISABLED), - mSurfaceWidth(-1), mSurfaceHeight(-1), mSurfaceLeft(0), mSurfaceTop(0), - mPhysicalWidth(-1), mPhysicalHeight(-1), mPhysicalLeft(0), mPhysicalTop(0), - mSurfaceOrientation(DISPLAY_ORIENTATION_0) { -} - -TouchInputMapper::~TouchInputMapper() { -} - -uint32_t TouchInputMapper::getSources() { - return mSource; -} - -void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - if (mDeviceMode != DEVICE_MODE_DISABLED) { - info->addMotionRange(mOrientedRanges.x); - info->addMotionRange(mOrientedRanges.y); - info->addMotionRange(mOrientedRanges.pressure); - - if (mOrientedRanges.haveSize) { - info->addMotionRange(mOrientedRanges.size); - } - - if (mOrientedRanges.haveTouchSize) { - info->addMotionRange(mOrientedRanges.touchMajor); - info->addMotionRange(mOrientedRanges.touchMinor); - } - - if (mOrientedRanges.haveToolSize) { - info->addMotionRange(mOrientedRanges.toolMajor); - info->addMotionRange(mOrientedRanges.toolMinor); - } - - if (mOrientedRanges.haveOrientation) { - info->addMotionRange(mOrientedRanges.orientation); - } - - if (mOrientedRanges.haveDistance) { - info->addMotionRange(mOrientedRanges.distance); - } - - if (mOrientedRanges.haveTilt) { - info->addMotionRange(mOrientedRanges.tilt); - } - - if (mCursorScrollAccumulator.haveRelativeVWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - 0.0f); - } - if (mCursorScrollAccumulator.haveRelativeHWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, - 0.0f); - } - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { - const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; - const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, - x.fuzz, x.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, - y.fuzz, y.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, - x.fuzz, x.resolution); - info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, - y.fuzz, y.resolution); - } - info->setButtonUnderPad(mParameters.hasButtonUnderPad); - } -} - -void TouchInputMapper::dump(std::string& dump) { - dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode)); - dumpParameters(dump); - dumpVirtualKeys(dump); - dumpRawPointerAxes(dump); - dumpCalibration(dump); - dumpAffineTransformation(dump); - dumpSurface(dump); - - dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n"); - dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate); - dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate); - dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale); - dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale); - dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision); - dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision); - dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale); - dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale); - dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale); - dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale); - dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale); - dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt)); - dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter); - dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale); - dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter); - dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale); - - dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState); - dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n", - mLastRawState.rawPointerData.pointerCount); - for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) { - const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i]; - dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " - "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " - "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, " - "toolType=%d, isHovering=%s\n", i, - pointer.id, pointer.x, pointer.y, pointer.pressure, - pointer.touchMajor, pointer.touchMinor, - pointer.toolMajor, pointer.toolMinor, - pointer.orientation, pointer.tiltX, pointer.tiltY, pointer.distance, - pointer.toolType, toString(pointer.isHovering)); - } - - dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n", mLastCookedState.buttonState); - dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n", - mLastCookedState.cookedPointerData.pointerCount); - for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) { - const PointerProperties& pointerProperties = - mLastCookedState.cookedPointerData.pointerProperties[i]; - const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i]; - dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, " - "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, toolMinor=%0.3f, " - "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, " - "toolType=%d, isHovering=%s\n", i, - pointerProperties.id, - pointerCoords.getX(), - pointerCoords.getY(), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT), - pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), - pointerProperties.toolType, - toString(mLastCookedState.cookedPointerData.isHovering(i))); - } - - dump += INDENT3 "Stylus Fusion:\n"; - dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", - toString(mExternalStylusConnected)); - dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId); - dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", - mExternalStylusFusionTimeout); - dump += INDENT3 "External Stylus State:\n"; - dumpStylusState(dump, mExternalStylusState); - - if (mDeviceMode == DEVICE_MODE_POINTER) { - dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n"); - dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", - mPointerXMovementScale); - dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", - mPointerYMovementScale); - dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n", - mPointerXZoomScale); - dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n", - mPointerYZoomScale); - dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n", - mPointerGestureMaxSwipeWidth); - } -} - -const char* TouchInputMapper::modeToString(DeviceMode deviceMode) { - switch (deviceMode) { - case DEVICE_MODE_DISABLED: - return "disabled"; - case DEVICE_MODE_DIRECT: - return "direct"; - case DEVICE_MODE_UNSCALED: - return "unscaled"; - case DEVICE_MODE_NAVIGATION: - return "navigation"; - case DEVICE_MODE_POINTER: - return "pointer"; - } - return "unknown"; -} - -void TouchInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - InputMapper::configure(when, config, changes); - - mConfig = *config; - - if (!changes) { // first time only - // Configure basic parameters. - configureParameters(); - - // Configure common accumulators. - mCursorScrollAccumulator.configure(getDevice()); - mTouchButtonAccumulator.configure(getDevice()); - - // Configure absolute axis information. - configureRawPointerAxes(); - - // Prepare input device calibration. - parseCalibration(); - resolveCalibration(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) { - // Update location calibration to reflect current settings - updateAffineTransformation(); - } - - if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { - // Update pointer speed. - mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters); - mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); - mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); - } - - bool resetNeeded = false; - if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO - | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT - | InputReaderConfiguration::CHANGE_SHOW_TOUCHES - | InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) { - // Configure device sources, surface dimensions, orientation and - // scaling factors. - configureSurface(when, &resetNeeded); - } - - if (changes && resetNeeded) { - // Send reset, unless this is the first time the device has been configured, - // in which case the reader will call reset itself after all mappers are ready. - getDevice()->notifyReset(when); - } -} - -void TouchInputMapper::resolveExternalStylusPresence() { - std::vector<InputDeviceInfo> devices; - mContext->getExternalStylusDevices(devices); - mExternalStylusConnected = !devices.empty(); - - if (!mExternalStylusConnected) { - resetExternalStylus(); - } -} - -void TouchInputMapper::configureParameters() { - // Use the pointer presentation mode for devices that do not support distinct - // multitouch. The spot-based presentation relies on being able to accurately - // locate two or more fingers on the touch pad. - mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT) - ? Parameters::GESTURE_MODE_SINGLE_TOUCH : Parameters::GESTURE_MODE_MULTI_TOUCH; - - String8 gestureModeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"), - gestureModeString)) { - if (gestureModeString == "single-touch") { - mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH; - } else if (gestureModeString == "multi-touch") { - mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH; - } else if (gestureModeString != "default") { - ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string()); - } - } - - if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { - // The device is a touch screen. - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; - } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) { - // The device is a pointing device like a track pad. - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) - || getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { - // The device is a cursor device with a touch pad attached. - // By default don't use the touch pad to move the pointer. - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; - } else { - // The device is a touch pad of unknown purpose. - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } - - mParameters.hasButtonUnderPad= - getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD); - - String8 deviceTypeString; - if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), - deviceTypeString)) { - if (deviceTypeString == "touchScreen") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; - } else if (deviceTypeString == "touchPad") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; - } else if (deviceTypeString == "touchNavigation") { - mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION; - } else if (deviceTypeString == "pointer") { - mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; - } else if (deviceTypeString != "default") { - ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); - } - } - - mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; - getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), - mParameters.orientationAware); - - mParameters.hasAssociatedDisplay = false; - mParameters.associatedDisplayIsExternal = false; - if (mParameters.orientationAware - || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN - || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { - mParameters.hasAssociatedDisplay = true; - if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { - mParameters.associatedDisplayIsExternal = getDevice()->isExternal(); - String8 uniqueDisplayId; - getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"), - uniqueDisplayId); - mParameters.uniqueDisplayId = uniqueDisplayId.c_str(); - } - } - if (getDevice()->getAssociatedDisplayPort()) { - mParameters.hasAssociatedDisplay = true; - } - - // Initial downs on external touch devices should wake the device. - // Normally we don't do this for internal touch screens to prevent them from waking - // up in your pocket but you can enable it using the input device configuration. - mParameters.wake = getDevice()->isExternal(); - getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), - mParameters.wake); -} - -void TouchInputMapper::dumpParameters(std::string& dump) { - dump += INDENT3 "Parameters:\n"; - - switch (mParameters.gestureMode) { - case Parameters::GESTURE_MODE_SINGLE_TOUCH: - dump += INDENT4 "GestureMode: single-touch\n"; - break; - case Parameters::GESTURE_MODE_MULTI_TOUCH: - dump += INDENT4 "GestureMode: multi-touch\n"; - break; - default: - assert(false); - } - - switch (mParameters.deviceType) { - case Parameters::DEVICE_TYPE_TOUCH_SCREEN: - dump += INDENT4 "DeviceType: touchScreen\n"; - break; - case Parameters::DEVICE_TYPE_TOUCH_PAD: - dump += INDENT4 "DeviceType: touchPad\n"; - break; - case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION: - dump += INDENT4 "DeviceType: touchNavigation\n"; - break; - case Parameters::DEVICE_TYPE_POINTER: - dump += INDENT4 "DeviceType: pointer\n"; - break; - default: - ALOG_ASSERT(false); - } - - dump += StringPrintf( - INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, displayId='%s'\n", - toString(mParameters.hasAssociatedDisplay), - toString(mParameters.associatedDisplayIsExternal), - mParameters.uniqueDisplayId.c_str()); - dump += StringPrintf(INDENT4 "OrientationAware: %s\n", - toString(mParameters.orientationAware)); -} - -void TouchInputMapper::configureRawPointerAxes() { - mRawPointerAxes.clear(); -} - -void TouchInputMapper::dumpRawPointerAxes(std::string& dump) { - dump += INDENT3 "Raw Touch Axes:\n"; - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId"); - dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot"); -} - -bool TouchInputMapper::hasExternalStylus() const { - return mExternalStylusConnected; -} - -/** - * Determine which DisplayViewport to use. - * 1. If display port is specified, return the matching viewport. If matching viewport not - * found, then return. - * 2. If a device has associated display, get the matching viewport by either unique id or by - * the display type (internal or external). - * 3. Otherwise, use a non-display viewport. - */ -std::optional<DisplayViewport> TouchInputMapper::findViewport() { - if (mParameters.hasAssociatedDisplay) { - const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort(); - if (displayPort) { - // Find the viewport that contains the same port - std::optional<DisplayViewport> v = mConfig.getDisplayViewportByPort(*displayPort); - if (!v) { - ALOGW("Input device %s should be associated with display on port %" PRIu8 ", " - "but the corresponding viewport is not found.", - getDeviceName().c_str(), *displayPort); - } - return v; - } - - if (!mParameters.uniqueDisplayId.empty()) { - return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId); - } - - ViewportType viewportTypeToUse; - if (mParameters.associatedDisplayIsExternal) { - viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL; - } else { - viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL; - } - - std::optional<DisplayViewport> viewport = - mConfig.getDisplayViewportByType(viewportTypeToUse); - if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) { - ALOGW("Input device %s should be associated with external display, " - "fallback to internal one for the external viewport is not found.", - getDeviceName().c_str()); - viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - } - - return viewport; - } - - DisplayViewport newViewport; - // Raw width and height in the natural orientation. - int32_t rawWidth = mRawPointerAxes.getRawWidth(); - int32_t rawHeight = mRawPointerAxes.getRawHeight(); - newViewport.setNonDisplayViewport(rawWidth, rawHeight); - return std::make_optional(newViewport); -} - -void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { - int32_t oldDeviceMode = mDeviceMode; - - resolveExternalStylusPresence(); - - // Determine device mode. - if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER - && mConfig.pointerGesturesEnabled) { - mSource = AINPUT_SOURCE_MOUSE; - mDeviceMode = DEVICE_MODE_POINTER; - if (hasStylus()) { - mSource |= AINPUT_SOURCE_STYLUS; - } - } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN - && mParameters.hasAssociatedDisplay) { - mSource = AINPUT_SOURCE_TOUCHSCREEN; - mDeviceMode = DEVICE_MODE_DIRECT; - if (hasStylus()) { - mSource |= AINPUT_SOURCE_STYLUS; - } - if (hasExternalStylus()) { - mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS; - } - } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) { - mSource = AINPUT_SOURCE_TOUCH_NAVIGATION; - mDeviceMode = DEVICE_MODE_NAVIGATION; - } else { - mSource = AINPUT_SOURCE_TOUCHPAD; - mDeviceMode = DEVICE_MODE_UNSCALED; - } - - // Ensure we have valid X and Y axes. - if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { - ALOGW("Touch device '%s' did not report support for X or Y axis! " - "The device will be inoperable.", getDeviceName().c_str()); - mDeviceMode = DEVICE_MODE_DISABLED; - return; - } - - // Get associated display dimensions. - std::optional<DisplayViewport> newViewport = findViewport(); - if (!newViewport) { - ALOGI("Touch device '%s' could not query the properties of its associated " - "display. The device will be inoperable until the display size " - "becomes available.", - getDeviceName().c_str()); - mDeviceMode = DEVICE_MODE_DISABLED; - return; - } - - // Raw width and height in the natural orientation. - int32_t rawWidth = mRawPointerAxes.getRawWidth(); - int32_t rawHeight = mRawPointerAxes.getRawHeight(); - - bool viewportChanged = mViewport != *newViewport; - if (viewportChanged) { - mViewport = *newViewport; - - if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) { - // Convert rotated viewport to natural surface coordinates. - int32_t naturalLogicalWidth, naturalLogicalHeight; - int32_t naturalPhysicalWidth, naturalPhysicalHeight; - int32_t naturalPhysicalLeft, naturalPhysicalTop; - int32_t naturalDeviceWidth, naturalDeviceHeight; - switch (mViewport.orientation) { - case DISPLAY_ORIENTATION_90: - naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; - naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; - naturalPhysicalTop = mViewport.physicalLeft; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_180: - naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; - naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; - naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - case DISPLAY_ORIENTATION_270: - naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; - naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; - naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalLeft = mViewport.physicalTop; - naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; - naturalDeviceWidth = mViewport.deviceHeight; - naturalDeviceHeight = mViewport.deviceWidth; - break; - case DISPLAY_ORIENTATION_0: - default: - naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; - naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; - naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; - naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; - naturalPhysicalLeft = mViewport.physicalLeft; - naturalPhysicalTop = mViewport.physicalTop; - naturalDeviceWidth = mViewport.deviceWidth; - naturalDeviceHeight = mViewport.deviceHeight; - break; - } - - if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) { - ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str()); - naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight; - naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth; - } - - mPhysicalWidth = naturalPhysicalWidth; - mPhysicalHeight = naturalPhysicalHeight; - mPhysicalLeft = naturalPhysicalLeft; - mPhysicalTop = naturalPhysicalTop; - - mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; - mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; - mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; - mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; - - mSurfaceOrientation = mParameters.orientationAware ? - mViewport.orientation : DISPLAY_ORIENTATION_0; - } else { - mPhysicalWidth = rawWidth; - mPhysicalHeight = rawHeight; - mPhysicalLeft = 0; - mPhysicalTop = 0; - - mSurfaceWidth = rawWidth; - mSurfaceHeight = rawHeight; - mSurfaceLeft = 0; - mSurfaceTop = 0; - mSurfaceOrientation = DISPLAY_ORIENTATION_0; - } - } - - // If moving between pointer modes, need to reset some state. - bool deviceModeChanged = mDeviceMode != oldDeviceMode; - if (deviceModeChanged) { - mOrientedRanges.clear(); - } - - // Create or update pointer controller if needed. - if (mDeviceMode == DEVICE_MODE_POINTER || - (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { - if (mPointerController == nullptr || viewportChanged) { - mPointerController = getPolicy()->obtainPointerController(getDeviceId()); - } - } else { - mPointerController.clear(); - } - - if (viewportChanged || deviceModeChanged) { - ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " - "display id %d", - getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight, - mSurfaceOrientation, mDeviceMode, mViewport.displayId); - - // Configure X and Y factors. - mXScale = float(mSurfaceWidth) / rawWidth; - mYScale = float(mSurfaceHeight) / rawHeight; - mXTranslate = -mSurfaceLeft; - mYTranslate = -mSurfaceTop; - mXPrecision = 1.0f / mXScale; - mYPrecision = 1.0f / mYScale; - - mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X; - mOrientedRanges.x.source = mSource; - mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y; - mOrientedRanges.y.source = mSource; - - configureVirtualKeys(); - - // Scale factor for terms that are not oriented in a particular axis. - // If the pixels are square then xScale == yScale otherwise we fake it - // by choosing an average. - mGeometricScale = avg(mXScale, mYScale); - - // Size of diagonal axis. - float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight); - - // Size factors. - if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { - if (mRawPointerAxes.touchMajor.valid - && mRawPointerAxes.touchMajor.maxValue != 0) { - mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue; - } else if (mRawPointerAxes.toolMajor.valid - && mRawPointerAxes.toolMajor.maxValue != 0) { - mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue; - } else { - mSizeScale = 0.0f; - } - - mOrientedRanges.haveTouchSize = true; - mOrientedRanges.haveToolSize = true; - mOrientedRanges.haveSize = true; - - mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR; - mOrientedRanges.touchMajor.source = mSource; - mOrientedRanges.touchMajor.min = 0; - mOrientedRanges.touchMajor.max = diagonalSize; - mOrientedRanges.touchMajor.flat = 0; - mOrientedRanges.touchMajor.fuzz = 0; - mOrientedRanges.touchMajor.resolution = 0; - - mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; - mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; - - mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR; - mOrientedRanges.toolMajor.source = mSource; - mOrientedRanges.toolMajor.min = 0; - mOrientedRanges.toolMajor.max = diagonalSize; - mOrientedRanges.toolMajor.flat = 0; - mOrientedRanges.toolMajor.fuzz = 0; - mOrientedRanges.toolMajor.resolution = 0; - - mOrientedRanges.toolMinor = mOrientedRanges.toolMajor; - mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR; - - mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE; - mOrientedRanges.size.source = mSource; - mOrientedRanges.size.min = 0; - mOrientedRanges.size.max = 1.0; - mOrientedRanges.size.flat = 0; - mOrientedRanges.size.fuzz = 0; - mOrientedRanges.size.resolution = 0; - } else { - mSizeScale = 0.0f; - } - - // Pressure factors. - mPressureScale = 0; - float pressureMax = 1.0; - if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL - || mCalibration.pressureCalibration - == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) { - if (mCalibration.havePressureScale) { - mPressureScale = mCalibration.pressureScale; - pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue; - } else if (mRawPointerAxes.pressure.valid - && mRawPointerAxes.pressure.maxValue != 0) { - mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue; - } - } - - mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE; - mOrientedRanges.pressure.source = mSource; - mOrientedRanges.pressure.min = 0; - mOrientedRanges.pressure.max = pressureMax; - mOrientedRanges.pressure.flat = 0; - mOrientedRanges.pressure.fuzz = 0; - mOrientedRanges.pressure.resolution = 0; - - // Tilt - mTiltXCenter = 0; - mTiltXScale = 0; - mTiltYCenter = 0; - mTiltYScale = 0; - mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid; - if (mHaveTilt) { - mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, - mRawPointerAxes.tiltX.maxValue); - mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, - mRawPointerAxes.tiltY.maxValue); - mTiltXScale = M_PI / 180; - mTiltYScale = M_PI / 180; - - mOrientedRanges.haveTilt = true; - - mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT; - mOrientedRanges.tilt.source = mSource; - mOrientedRanges.tilt.min = 0; - mOrientedRanges.tilt.max = M_PI_2; - mOrientedRanges.tilt.flat = 0; - mOrientedRanges.tilt.fuzz = 0; - mOrientedRanges.tilt.resolution = 0; - } - - // Orientation - mOrientationScale = 0; - if (mHaveTilt) { - mOrientedRanges.haveOrientation = true; - - mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; - mOrientedRanges.orientation.source = mSource; - mOrientedRanges.orientation.min = -M_PI; - mOrientedRanges.orientation.max = M_PI; - mOrientedRanges.orientation.flat = 0; - mOrientedRanges.orientation.fuzz = 0; - mOrientedRanges.orientation.resolution = 0; - } else if (mCalibration.orientationCalibration != - Calibration::ORIENTATION_CALIBRATION_NONE) { - if (mCalibration.orientationCalibration - == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) { - if (mRawPointerAxes.orientation.valid) { - if (mRawPointerAxes.orientation.maxValue > 0) { - mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue; - } else if (mRawPointerAxes.orientation.minValue < 0) { - mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue; - } else { - mOrientationScale = 0; - } - } - } - - mOrientedRanges.haveOrientation = true; - - mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; - mOrientedRanges.orientation.source = mSource; - mOrientedRanges.orientation.min = -M_PI_2; - mOrientedRanges.orientation.max = M_PI_2; - mOrientedRanges.orientation.flat = 0; - mOrientedRanges.orientation.fuzz = 0; - mOrientedRanges.orientation.resolution = 0; - } - - // Distance - mDistanceScale = 0; - if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) { - if (mCalibration.distanceCalibration - == Calibration::DISTANCE_CALIBRATION_SCALED) { - if (mCalibration.haveDistanceScale) { - mDistanceScale = mCalibration.distanceScale; - } else { - mDistanceScale = 1.0f; - } - } - - mOrientedRanges.haveDistance = true; - - mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE; - mOrientedRanges.distance.source = mSource; - mOrientedRanges.distance.min = - mRawPointerAxes.distance.minValue * mDistanceScale; - mOrientedRanges.distance.max = - mRawPointerAxes.distance.maxValue * mDistanceScale; - mOrientedRanges.distance.flat = 0; - mOrientedRanges.distance.fuzz = - mRawPointerAxes.distance.fuzz * mDistanceScale; - mOrientedRanges.distance.resolution = 0; - } - - // Compute oriented precision, scales and ranges. - // Note that the maximum value reported is an inclusive maximum value so it is one - // unit less than the total width or height of surface. - switch (mSurfaceOrientation) { - case DISPLAY_ORIENTATION_90: - case DISPLAY_ORIENTATION_270: - mOrientedXPrecision = mYPrecision; - mOrientedYPrecision = mXPrecision; - - mOrientedRanges.x.min = mYTranslate; - mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1; - mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = 0; - mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; - - mOrientedRanges.y.min = mXTranslate; - mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1; - mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = 0; - mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; - break; - - default: - mOrientedXPrecision = mXPrecision; - mOrientedYPrecision = mYPrecision; - - mOrientedRanges.x.min = mXTranslate; - mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1; - mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = 0; - mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; - - mOrientedRanges.y.min = mYTranslate; - mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1; - mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = 0; - mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; - break; - } - - // Location - updateAffineTransformation(); - - if (mDeviceMode == DEVICE_MODE_POINTER) { - // Compute pointer gesture detection parameters. - float rawDiagonal = hypotf(rawWidth, rawHeight); - float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight); - - // Scale movements such that one whole swipe of the touch pad covers a - // given area relative to the diagonal size of the display when no acceleration - // is applied. - // Assume that the touch pad has a square aspect ratio such that movements in - // X and Y of the same number of raw units cover the same physical distance. - mPointerXMovementScale = mConfig.pointerGestureMovementSpeedRatio - * displayDiagonal / rawDiagonal; - mPointerYMovementScale = mPointerXMovementScale; - - // Scale zooms to cover a smaller range of the display than movements do. - // This value determines the area around the pointer that is affected by freeform - // pointer gestures. - mPointerXZoomScale = mConfig.pointerGestureZoomSpeedRatio - * displayDiagonal / rawDiagonal; - mPointerYZoomScale = mPointerXZoomScale; - - // Max width between pointers to detect a swipe gesture is more than some fraction - // of the diagonal axis of the touch pad. Touches that are wider than this are - // translated into freeform gestures. - mPointerGestureMaxSwipeWidth = - mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; - - // Abort current pointer usages because the state has changed. - abortPointerUsage(when, 0 /*policyFlags*/); - } - - // Inform the dispatcher about the changes. - *outResetNeeded = true; - bumpGeneration(); - } -} - -void TouchInputMapper::dumpSurface(std::string& dump) { - dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); - dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth); - dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); - dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); - dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); - dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); - dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); - dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); - dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); - dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation); -} - -void TouchInputMapper::configureVirtualKeys() { - std::vector<VirtualKeyDefinition> virtualKeyDefinitions; - getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions); - - mVirtualKeys.clear(); - - if (virtualKeyDefinitions.size() == 0) { - return; - } - - int32_t touchScreenLeft = mRawPointerAxes.x.minValue; - int32_t touchScreenTop = mRawPointerAxes.y.minValue; - int32_t touchScreenWidth = mRawPointerAxes.getRawWidth(); - int32_t touchScreenHeight = mRawPointerAxes.getRawHeight(); - - for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) { - VirtualKey virtualKey; - - virtualKey.scanCode = virtualKeyDefinition.scanCode; - int32_t keyCode; - int32_t dummyKeyMetaState; - uint32_t flags; - if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, - &keyCode, &dummyKeyMetaState, &flags)) { - ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", - virtualKey.scanCode); - continue; // drop the key - } - - virtualKey.keyCode = keyCode; - virtualKey.flags = flags; - - // convert the key definition's display coordinates into touch coordinates for a hit box - int32_t halfWidth = virtualKeyDefinition.width / 2; - int32_t halfHeight = virtualKeyDefinition.height / 2; - - virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) - * touchScreenWidth / mSurfaceWidth + touchScreenLeft; - virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth) - * touchScreenWidth / mSurfaceWidth + touchScreenLeft; - virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) - * touchScreenHeight / mSurfaceHeight + touchScreenTop; - virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) - * touchScreenHeight / mSurfaceHeight + touchScreenTop; - mVirtualKeys.push_back(virtualKey); - } -} - -void TouchInputMapper::dumpVirtualKeys(std::string& dump) { - if (!mVirtualKeys.empty()) { - dump += INDENT3 "Virtual Keys:\n"; - - for (size_t i = 0; i < mVirtualKeys.size(); i++) { - const VirtualKey& virtualKey = mVirtualKeys[i]; - dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, " - "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n", - i, virtualKey.scanCode, virtualKey.keyCode, - virtualKey.hitLeft, virtualKey.hitRight, - virtualKey.hitTop, virtualKey.hitBottom); - } - } -} - -void TouchInputMapper::parseCalibration() { - const PropertyMap& in = getDevice()->getConfiguration(); - Calibration& out = mCalibration; - - // Size - out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT; - String8 sizeCalibrationString; - if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) { - if (sizeCalibrationString == "none") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; - } else if (sizeCalibrationString == "geometric") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; - } else if (sizeCalibrationString == "diameter") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER; - } else if (sizeCalibrationString == "box") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX; - } else if (sizeCalibrationString == "area") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA; - } else if (sizeCalibrationString != "default") { - ALOGW("Invalid value for touch.size.calibration: '%s'", - sizeCalibrationString.string()); - } - } - - out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), - out.sizeScale); - out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), - out.sizeBias); - out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), - out.sizeIsSummed); - - // Pressure - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT; - String8 pressureCalibrationString; - if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) { - if (pressureCalibrationString == "none") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; - } else if (pressureCalibrationString == "physical") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; - } else if (pressureCalibrationString == "amplitude") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; - } else if (pressureCalibrationString != "default") { - ALOGW("Invalid value for touch.pressure.calibration: '%s'", - pressureCalibrationString.string()); - } - } - - out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), - out.pressureScale); - - // Orientation - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT; - String8 orientationCalibrationString; - if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) { - if (orientationCalibrationString == "none") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; - } else if (orientationCalibrationString == "interpolated") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; - } else if (orientationCalibrationString == "vector") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR; - } else if (orientationCalibrationString != "default") { - ALOGW("Invalid value for touch.orientation.calibration: '%s'", - orientationCalibrationString.string()); - } - } - - // Distance - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT; - String8 distanceCalibrationString; - if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) { - if (distanceCalibrationString == "none") { - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; - } else if (distanceCalibrationString == "scaled") { - out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; - } else if (distanceCalibrationString != "default") { - ALOGW("Invalid value for touch.distance.calibration: '%s'", - distanceCalibrationString.string()); - } - } - - out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), - out.distanceScale); - - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT; - String8 coverageCalibrationString; - if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) { - if (coverageCalibrationString == "none") { - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; - } else if (coverageCalibrationString == "box") { - out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX; - } else if (coverageCalibrationString != "default") { - ALOGW("Invalid value for touch.coverage.calibration: '%s'", - coverageCalibrationString.string()); - } - } -} - -void TouchInputMapper::resolveCalibration() { - // Size - if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) { - if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) { - mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; - } - } else { - mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; - } - - // Pressure - if (mRawPointerAxes.pressure.valid) { - if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) { - mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; - } - } else { - mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; - } - - // Orientation - if (mRawPointerAxes.orientation.valid) { - if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) { - mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; - } - } else { - mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; - } - - // Distance - if (mRawPointerAxes.distance.valid) { - if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) { - mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; - } - } else { - mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; - } - - // Coverage - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) { - mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; - } -} - -void TouchInputMapper::dumpCalibration(std::string& dump) { - dump += INDENT3 "Calibration:\n"; - - // Size - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_NONE: - dump += INDENT4 "touch.size.calibration: none\n"; - break; - case Calibration::SIZE_CALIBRATION_GEOMETRIC: - dump += INDENT4 "touch.size.calibration: geometric\n"; - break; - case Calibration::SIZE_CALIBRATION_DIAMETER: - dump += INDENT4 "touch.size.calibration: diameter\n"; - break; - case Calibration::SIZE_CALIBRATION_BOX: - dump += INDENT4 "touch.size.calibration: box\n"; - break; - case Calibration::SIZE_CALIBRATION_AREA: - dump += INDENT4 "touch.size.calibration: area\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.haveSizeScale) { - dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", - mCalibration.sizeScale); - } - - if (mCalibration.haveSizeBias) { - dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", - mCalibration.sizeBias); - } - - if (mCalibration.haveSizeIsSummed) { - dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n", - toString(mCalibration.sizeIsSummed)); - } - - // Pressure - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_NONE: - dump += INDENT4 "touch.pressure.calibration: none\n"; - break; - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - dump += INDENT4 "touch.pressure.calibration: physical\n"; - break; - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - dump += INDENT4 "touch.pressure.calibration: amplitude\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.havePressureScale) { - dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", - mCalibration.pressureScale); - } - - // Orientation - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_NONE: - dump += INDENT4 "touch.orientation.calibration: none\n"; - break; - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - dump += INDENT4 "touch.orientation.calibration: interpolated\n"; - break; - case Calibration::ORIENTATION_CALIBRATION_VECTOR: - dump += INDENT4 "touch.orientation.calibration: vector\n"; - break; - default: - ALOG_ASSERT(false); - } - - // Distance - switch (mCalibration.distanceCalibration) { - case Calibration::DISTANCE_CALIBRATION_NONE: - dump += INDENT4 "touch.distance.calibration: none\n"; - break; - case Calibration::DISTANCE_CALIBRATION_SCALED: - dump += INDENT4 "touch.distance.calibration: scaled\n"; - break; - default: - ALOG_ASSERT(false); - } - - if (mCalibration.haveDistanceScale) { - dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", - mCalibration.distanceScale); - } - - switch (mCalibration.coverageCalibration) { - case Calibration::COVERAGE_CALIBRATION_NONE: - dump += INDENT4 "touch.coverage.calibration: none\n"; - break; - case Calibration::COVERAGE_CALIBRATION_BOX: - dump += INDENT4 "touch.coverage.calibration: box\n"; - break; - default: - ALOG_ASSERT(false); - } -} - -void TouchInputMapper::dumpAffineTransformation(std::string& dump) { - dump += INDENT3 "Affine Transformation:\n"; - - dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale); - dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix); - dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset); - dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix); - dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale); - dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset); -} - -void TouchInputMapper::updateAffineTransformation() { - mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(), - mSurfaceOrientation); -} - -void TouchInputMapper::reset(nsecs_t when) { - mCursorButtonAccumulator.reset(getDevice()); - mCursorScrollAccumulator.reset(getDevice()); - mTouchButtonAccumulator.reset(getDevice()); - - mPointerVelocityControl.reset(); - mWheelXVelocityControl.reset(); - mWheelYVelocityControl.reset(); - - mRawStatesPending.clear(); - mCurrentRawState.clear(); - mCurrentCookedState.clear(); - mLastRawState.clear(); - mLastCookedState.clear(); - mPointerUsage = POINTER_USAGE_NONE; - mSentHoverEnter = false; - mHavePointerIds = false; - mCurrentMotionAborted = false; - mDownTime = 0; - - mCurrentVirtualKey.down = false; - - mPointerGesture.reset(); - mPointerSimple.reset(); - resetExternalStylus(); - - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - mPointerController->clearSpots(); - } - - InputMapper::reset(when); -} - -void TouchInputMapper::resetExternalStylus() { - mExternalStylusState.clear(); - mExternalStylusId = -1; - mExternalStylusFusionTimeout = LLONG_MAX; - mExternalStylusDataPending = false; -} - -void TouchInputMapper::clearStylusDataPendingFlags() { - mExternalStylusDataPending = false; - mExternalStylusFusionTimeout = LLONG_MAX; -} - -void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) { - nsecs_t now = systemTime(CLOCK_MONOTONIC); - nsecs_t latency = now - evdevTime; - mStatistics.addValue(nanoseconds_to_microseconds(latency)); - nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime; - if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) { - android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, - mStatistics.min, mStatistics.max, - mStatistics.mean(), mStatistics.stdev(), mStatistics.count); - mStatistics.reset(now); - } -} - -void TouchInputMapper::process(const RawEvent* rawEvent) { - mCursorButtonAccumulator.process(rawEvent); - mCursorScrollAccumulator.process(rawEvent); - mTouchButtonAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - reportEventForStatistics(rawEvent->when); - sync(rawEvent->when); - } -} - -void TouchInputMapper::sync(nsecs_t when) { - const RawState* last = mRawStatesPending.empty() ? - &mCurrentRawState : &mRawStatesPending.back(); - - // Push a new state. - mRawStatesPending.emplace_back(); - - RawState* next = &mRawStatesPending.back(); - next->clear(); - next->when = when; - - // Sync button state. - next->buttonState = mTouchButtonAccumulator.getButtonState() - | mCursorButtonAccumulator.getButtonState(); - - // Sync scroll - next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); - next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel(); - mCursorScrollAccumulator.finishSync(); - - // Sync touch - syncTouch(when, next); - - // Assign pointer ids. - if (!mHavePointerIds) { - assignPointerIds(last, next); - } - -#if DEBUG_RAW_EVENTS - ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " - "hovering ids 0x%08x -> 0x%08x", - last->rawPointerData.pointerCount, - next->rawPointerData.pointerCount, - last->rawPointerData.touchingIdBits.value, - next->rawPointerData.touchingIdBits.value, - last->rawPointerData.hoveringIdBits.value, - next->rawPointerData.hoveringIdBits.value); -#endif - - processRawTouches(false /*timeout*/); -} - -void TouchInputMapper::processRawTouches(bool timeout) { - if (mDeviceMode == DEVICE_MODE_DISABLED) { - // Drop all input if the device is disabled. - mCurrentRawState.clear(); - mRawStatesPending.clear(); - return; - } - - // Drain any pending touch states. The invariant here is that the mCurrentRawState is always - // valid and must go through the full cook and dispatch cycle. This ensures that anything - // touching the current state will only observe the events that have been dispatched to the - // rest of the pipeline. - const size_t N = mRawStatesPending.size(); - size_t count; - for(count = 0; count < N; count++) { - const RawState& next = mRawStatesPending[count]; - - // A failure to assign the stylus id means that we're waiting on stylus data - // and so should defer the rest of the pipeline. - if (assignExternalStylusId(next, timeout)) { - break; - } - - // All ready to go. - clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(next); - if (mCurrentRawState.when < mLastRawState.when) { - mCurrentRawState.when = mLastRawState.when; - } - cookAndDispatch(mCurrentRawState.when); - } - if (count != 0) { - mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count); - } - - if (mExternalStylusDataPending) { - if (timeout) { - nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY; - clearStylusDataPendingFlags(); - mCurrentRawState.copyFrom(mLastRawState); -#if DEBUG_STYLUS_FUSION - ALOGD("Timeout expired, synthesizing event with new stylus data"); -#endif - cookAndDispatch(when); - } else if (mExternalStylusFusionTimeout == LLONG_MAX) { - mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT; - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - } - } -} - -void TouchInputMapper::cookAndDispatch(nsecs_t when) { - // Always start with a clean state. - mCurrentCookedState.clear(); - - // Apply stylus buttons to current raw state. - applyExternalStylusButtonState(when); - - // Handle policy on initial down or hover events. - bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 - && mCurrentRawState.rawPointerData.pointerCount != 0; - - uint32_t policyFlags = 0; - bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState; - if (initialDown || buttonsPressed) { - // If this is a touch screen, hide the pointer on an initial down. - if (mDeviceMode == DEVICE_MODE_DIRECT) { - getContext()->fadePointer(); - } - - if (mParameters.wake) { - policyFlags |= POLICY_FLAG_WAKE; - } - } - - // Consume raw off-screen touches before cooking pointer data. - // If touches are consumed, subsequent code will not receive any pointer data. - if (consumeRawTouches(when, policyFlags)) { - mCurrentRawState.rawPointerData.clear(); - } - - // Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure - // with cooked pointer data that has the same ids and indices as the raw data. - // The following code can use either the raw or cooked data, as needed. - cookPointerData(); - - // Apply stylus pressure to current cooked state. - applyExternalStylusTouchState(when); - - // Synthesize key down from raw buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, - mViewport.displayId, policyFlags, - mLastCookedState.buttonState, mCurrentCookedState.buttonState); - - // Dispatch the touches either directly or by translation through a pointer on screen. - if (mDeviceMode == DEVICE_MODE_POINTER) { - for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); - !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS - || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { - mCurrentCookedState.stylusIdBits.markBit(id); - } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER - || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - mCurrentCookedState.fingerIdBits.markBit(id); - } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) { - mCurrentCookedState.mouseIdBits.markBit(id); - } - } - for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); - !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS - || pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { - mCurrentCookedState.stylusIdBits.markBit(id); - } - } - - // Stylus takes precedence over all tools, then mouse, then finger. - PointerUsage pointerUsage = mPointerUsage; - if (!mCurrentCookedState.stylusIdBits.isEmpty()) { - mCurrentCookedState.mouseIdBits.clear(); - mCurrentCookedState.fingerIdBits.clear(); - pointerUsage = POINTER_USAGE_STYLUS; - } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) { - mCurrentCookedState.fingerIdBits.clear(); - pointerUsage = POINTER_USAGE_MOUSE; - } else if (!mCurrentCookedState.fingerIdBits.isEmpty() || - isPointerDown(mCurrentRawState.buttonState)) { - pointerUsage = POINTER_USAGE_GESTURES; - } - - dispatchPointerUsage(when, policyFlags, pointerUsage); - } else { - if (mDeviceMode == DEVICE_MODE_DIRECT - && mConfig.showTouches && mPointerController != nullptr) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - - mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.touchingIdBits, - mViewport.displayId); - } - - if (!mCurrentMotionAborted) { - dispatchButtonRelease(when, policyFlags); - dispatchHoverExit(when, policyFlags); - dispatchTouches(when, policyFlags); - dispatchHoverEnterAndMove(when, policyFlags); - dispatchButtonPress(when, policyFlags); - } - - if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { - mCurrentMotionAborted = false; - } - } - - // Synthesize key up from raw buttons if needed. - synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, - mViewport.displayId, policyFlags, - mLastCookedState.buttonState, mCurrentCookedState.buttonState); - - // Clear some transient state. - mCurrentRawState.rawVScroll = 0; - mCurrentRawState.rawHScroll = 0; - - // Copy current touch to last touch in preparation for the next cycle. - mLastRawState.copyFrom(mCurrentRawState); - mLastCookedState.copyFrom(mCurrentCookedState); -} - -void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { - if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) { - mCurrentRawState.buttonState |= mExternalStylusState.buttons; - } -} - -void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) { - CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData; - const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData; - - if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) { - float pressure = mExternalStylusState.pressure; - if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) { - const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId); - pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - } - PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId); - coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - - PointerProperties& properties = - currentPointerData.editPointerPropertiesWithId(mExternalStylusId); - if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - properties.toolType = mExternalStylusState.toolType; - } - } -} - -bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) { - if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) { - return false; - } - - const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 - && state.rawPointerData.pointerCount != 0; - if (initialDown) { - if (mExternalStylusState.pressure != 0.0f) { -#if DEBUG_STYLUS_FUSION - ALOGD("Have both stylus and touch data, beginning fusion"); -#endif - mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit(); - } else if (timeout) { -#if DEBUG_STYLUS_FUSION - ALOGD("Timeout expired, assuming touch is not a stylus."); -#endif - resetExternalStylus(); - } else { - if (mExternalStylusFusionTimeout == LLONG_MAX) { - mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; - } -#if DEBUG_STYLUS_FUSION - ALOGD("No stylus data but stylus is connected, requesting timeout " - "(%" PRId64 "ms)", mExternalStylusFusionTimeout); -#endif - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - return true; - } - } - - // Check if the stylus pointer has gone up. - if (mExternalStylusId != -1 && - !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) { -#if DEBUG_STYLUS_FUSION - ALOGD("Stylus pointer is going up"); -#endif - mExternalStylusId = -1; - } - - return false; -} - -void TouchInputMapper::timeoutExpired(nsecs_t when) { - if (mDeviceMode == DEVICE_MODE_POINTER) { - if (mPointerUsage == POINTER_USAGE_GESTURES) { - dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/); - } - } else if (mDeviceMode == DEVICE_MODE_DIRECT) { - if (mExternalStylusFusionTimeout < when) { - processRawTouches(true /*timeout*/); - } else if (mExternalStylusFusionTimeout != LLONG_MAX) { - getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); - } - } -} - -void TouchInputMapper::updateExternalStylusState(const StylusState& state) { - mExternalStylusState.copyFrom(state); - if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) { - // We're either in the middle of a fused stream of data or we're waiting on data before - // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus - // data. - mExternalStylusDataPending = true; - processRawTouches(false /*timeout*/); - } -} - -bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) { - // Check for release of a virtual key. - if (mCurrentVirtualKey.down) { - if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - // Pointer went up while virtual key was down. - mCurrentVirtualKey.down = false; - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, - AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - } - return true; - } - - if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { - uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); - if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { - // Pointer is still within the space of the virtual key. - return true; - } - } - - // Pointer left virtual key area or another pointer also went down. - // Send key cancellation but do not consume the touch yet. - // This is useful when the user swipes through from the virtual key area - // into the main display surface. - mCurrentVirtualKey.down = false; - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, - AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY - | AKEY_EVENT_FLAG_CANCELED); - } - } - - if (mLastRawState.rawPointerData.touchingIdBits.isEmpty() - && !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - // Pointer just went down. Check for virtual key press or off-screen touches. - uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); - const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); - if (!isPointInsideSurface(pointer.x, pointer.y)) { - // If exactly one pointer went down, check for virtual key hit. - // Otherwise we will drop the entire stroke. - if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { - const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); - if (virtualKey) { - mCurrentVirtualKey.down = true; - mCurrentVirtualKey.downTime = when; - mCurrentVirtualKey.keyCode = virtualKey->keyCode; - mCurrentVirtualKey.scanCode = virtualKey->scanCode; - mCurrentVirtualKey.ignored = mContext->shouldDropVirtualKey( - when, getDevice(), virtualKey->keyCode, virtualKey->scanCode); - - if (!mCurrentVirtualKey.ignored) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", - mCurrentVirtualKey.keyCode, - mCurrentVirtualKey.scanCode); -#endif - dispatchVirtualKey(when, policyFlags, - AKEY_EVENT_ACTION_DOWN, - AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); - } - } - } - return true; - } - } - - // Disable all virtual key touches that happen within a short time interval of the - // most recent touch within the screen area. The idea is to filter out stray - // virtual key presses when interacting with the touch screen. - // - // Problems we're trying to solve: - // - // 1. While scrolling a list or dragging the window shade, the user swipes down into a - // virtual key area that is implemented by a separate touch panel and accidentally - // triggers a virtual key. - // - // 2. While typing in the on screen keyboard, the user taps slightly outside the screen - // area and accidentally triggers a virtual key. This often happens when virtual keys - // are layed out below the screen near to where the on screen keyboard's space bar - // is displayed. - if (mConfig.virtualKeyQuietTime > 0 && - !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { - mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); - } - return false; -} - -void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, - int32_t keyEventAction, int32_t keyEventFlags) { - int32_t keyCode = mCurrentVirtualKey.keyCode; - int32_t scanCode = mCurrentVirtualKey.scanCode; - nsecs_t downTime = mCurrentVirtualKey.downTime; - int32_t metaState = mContext->getGlobalMetaState(); - policyFlags |= POLICY_FLAG_VIRTUAL; - - NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, - mViewport.displayId, - policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); - getListener()->notifyKey(&args); -} - -void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { - BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; - if (!currentIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.deviceTimestamp, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - currentIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - mCurrentMotionAborted = true; - } -} - -void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { - BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; - BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits; - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - - if (currentIdBits == lastIdBits) { - if (!currentIdBits.isEmpty()) { - // No pointer id changes so this is a move event. - // The listener takes care of batching moves so we don't have to deal with that here. - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, - AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.deviceTimestamp, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - currentIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - } else { - // There may be pointers going up and pointers going down and pointers moving - // all at the same time. - BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value); - BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value); - BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value); - BitSet32 dispatchedIdBits(lastIdBits.value); - - // Update last coordinates of pointers that have moved so that we observe the new - // pointer positions at the same time as other pointers that have just gone up. - bool moveNeeded = updateMovedPointers( - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, - moveIdBits); - if (buttonState != mLastCookedState.buttonState) { - moveNeeded = true; - } - - // Dispatch pointer up events. - while (!upIdBits.isEmpty()) { - uint32_t upId = upIdBits.clearFirstMarkedBit(); - - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, 0, - mCurrentCookedState.deviceTimestamp, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, - dispatchedIdBits, upId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); - dispatchedIdBits.clearBit(upId); - } - - // Dispatch move events if any of the remaining pointers moved from their old locations. - // Although applications receive new locations as part of individual pointer up - // events, they do not generally handle them except when presented in a move event. - if (moveNeeded && !moveIdBits.isEmpty()) { - ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, 0, - mCurrentCookedState.deviceTimestamp, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - - // Dispatch pointer down events using the new pointer locations. - while (!downIdBits.isEmpty()) { - uint32_t downId = downIdBits.clearFirstMarkedBit(); - dispatchedIdBits.markBit(downId); - - if (dispatchedIdBits.count() == 1) { - // First pointer is going down. Set down time. - mDownTime = when; - } - - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0, - mCurrentCookedState.deviceTimestamp, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - dispatchedIdBits, downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } - } -} - -void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) { - if (mSentHoverEnter && - (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() - || !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) { - int32_t metaState = getContext()->getGlobalMetaState(); - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastCookedState.buttonState, 0, - mLastCookedState.deviceTimestamp, - mLastCookedState.cookedPointerData.pointerProperties, - mLastCookedState.cookedPointerData.pointerCoords, - mLastCookedState.cookedPointerData.idToIndex, - mLastCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - mSentHoverEnter = false; - } -} - -void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) { - if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() - && !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - if (!mSentHoverEnter) { - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, - 0, 0, metaState, mCurrentRawState.buttonState, 0, - mCurrentCookedState.deviceTimestamp, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - mSentHoverEnter = true; - } - - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, 0, - mCurrentCookedState.deviceTimestamp, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) { - BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState); - const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData); - const int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mLastCookedState.buttonState; - while (!releasedButtons.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit()); - buttonState &= ~actionButton; - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, - 0, metaState, buttonState, 0, - mCurrentCookedState.deviceTimestamp, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) { - BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState); - const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData); - const int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mLastCookedState.buttonState; - while (!pressedButtons.isEmpty()) { - int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit()); - buttonState |= actionButton; - dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, - 0, metaState, buttonState, 0, - mCurrentCookedState.deviceTimestamp, - mCurrentCookedState.cookedPointerData.pointerProperties, - mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, - mOrientedXPrecision, mOrientedYPrecision, mDownTime); - } -} - -const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) { - if (!cookedPointerData.touchingIdBits.isEmpty()) { - return cookedPointerData.touchingIdBits; - } - return cookedPointerData.hoveringIdBits; -} - -void TouchInputMapper::cookPointerData() { - uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount; - - mCurrentCookedState.cookedPointerData.clear(); - mCurrentCookedState.deviceTimestamp = - mCurrentRawState.deviceTimestamp; - mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount; - mCurrentCookedState.cookedPointerData.hoveringIdBits = - mCurrentRawState.rawPointerData.hoveringIdBits; - mCurrentCookedState.cookedPointerData.touchingIdBits = - mCurrentRawState.rawPointerData.touchingIdBits; - - if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { - mCurrentCookedState.buttonState = 0; - } else { - mCurrentCookedState.buttonState = mCurrentRawState.buttonState; - } - - // Walk through the the active pointers and map device coordinates onto - // surface coordinates and adjust for display orientation. - for (uint32_t i = 0; i < currentPointerCount; i++) { - const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i]; - - // Size - float touchMajor, touchMinor, toolMajor, toolMinor, size; - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_GEOMETRIC: - case Calibration::SIZE_CALIBRATION_DIAMETER: - case Calibration::SIZE_CALIBRATION_BOX: - case Calibration::SIZE_CALIBRATION_AREA: - if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) { - touchMajor = in.touchMajor; - touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; - toolMajor = in.toolMajor; - toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; - size = mRawPointerAxes.touchMinor.valid - ? avg(in.touchMajor, in.touchMinor) : in.touchMajor; - } else if (mRawPointerAxes.touchMajor.valid) { - toolMajor = touchMajor = in.touchMajor; - toolMinor = touchMinor = mRawPointerAxes.touchMinor.valid - ? in.touchMinor : in.touchMajor; - size = mRawPointerAxes.touchMinor.valid - ? avg(in.touchMajor, in.touchMinor) : in.touchMajor; - } else if (mRawPointerAxes.toolMajor.valid) { - touchMajor = toolMajor = in.toolMajor; - touchMinor = toolMinor = mRawPointerAxes.toolMinor.valid - ? in.toolMinor : in.toolMajor; - size = mRawPointerAxes.toolMinor.valid - ? avg(in.toolMajor, in.toolMinor) : in.toolMajor; - } else { - ALOG_ASSERT(false, "No touch or tool axes. " - "Size calibration should have been resolved to NONE."); - touchMajor = 0; - touchMinor = 0; - toolMajor = 0; - toolMinor = 0; - size = 0; - } - - if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) { - uint32_t touchingCount = - mCurrentRawState.rawPointerData.touchingIdBits.count(); - if (touchingCount > 1) { - touchMajor /= touchingCount; - touchMinor /= touchingCount; - toolMajor /= touchingCount; - toolMinor /= touchingCount; - size /= touchingCount; - } - } - - if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) { - touchMajor *= mGeometricScale; - touchMinor *= mGeometricScale; - toolMajor *= mGeometricScale; - toolMinor *= mGeometricScale; - } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) { - touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0; - touchMinor = touchMajor; - toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0; - toolMinor = toolMajor; - } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) { - touchMinor = touchMajor; - toolMinor = toolMajor; - } - - mCalibration.applySizeScaleAndBias(&touchMajor); - mCalibration.applySizeScaleAndBias(&touchMinor); - mCalibration.applySizeScaleAndBias(&toolMajor); - mCalibration.applySizeScaleAndBias(&toolMinor); - size *= mSizeScale; - break; - default: - touchMajor = 0; - touchMinor = 0; - toolMajor = 0; - toolMinor = 0; - size = 0; - break; - } - - // Pressure - float pressure; - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - pressure = in.pressure * mPressureScale; - break; - default: - pressure = in.isHovering ? 0 : 1; - break; - } - - // Tilt and Orientation - float tilt; - float orientation; - if (mHaveTilt) { - float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale; - float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale; - orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)); - tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle)); - } else { - tilt = 0; - - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - orientation = in.orientation * mOrientationScale; - break; - case Calibration::ORIENTATION_CALIBRATION_VECTOR: { - int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); - int32_t c2 = signExtendNybble(in.orientation & 0x0f); - if (c1 != 0 || c2 != 0) { - orientation = atan2f(c1, c2) * 0.5f; - float confidence = hypotf(c1, c2); - float scale = 1.0f + confidence / 16.0f; - touchMajor *= scale; - touchMinor /= scale; - toolMajor *= scale; - toolMinor /= scale; - } else { - orientation = 0; - } - break; - } - default: - orientation = 0; - } - } - - // Distance - float distance; - switch (mCalibration.distanceCalibration) { - case Calibration::DISTANCE_CALIBRATION_SCALED: - distance = in.distance * mDistanceScale; - break; - default: - distance = 0; - } - - // Coverage - int32_t rawLeft, rawTop, rawRight, rawBottom; - switch (mCalibration.coverageCalibration) { - case Calibration::COVERAGE_CALIBRATION_BOX: - rawLeft = (in.toolMinor & 0xffff0000) >> 16; - rawRight = in.toolMinor & 0x0000ffff; - rawBottom = in.toolMajor & 0x0000ffff; - rawTop = (in.toolMajor & 0xffff0000) >> 16; - break; - default: - rawLeft = rawTop = rawRight = rawBottom = 0; - break; - } - - // Adjust X,Y coords for device calibration - // TODO: Adjust coverage coords? - float xTransformed = in.x, yTransformed = in.y; - mAffineTransform.applyTo(xTransformed, yTransformed); - - // Adjust X, Y, and coverage coords for surface orientation. - float x, y; - float left, top, right, bottom; - - switch (mSurfaceOrientation) { - case DISPLAY_ORIENTATION_90: - x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate; - left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - right = float(rawBottom- mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; - top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; - orientation -= M_PI_2; - if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) { - orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - case DISPLAY_ORIENTATION_180: - x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale; - y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate; - left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; - right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; - bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; - top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; - orientation -= M_PI; - if (mOrientedRanges.haveOrientation && orientation < mOrientedRanges.orientation.min) { - orientation += (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - case DISPLAY_ORIENTATION_270: - x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale; - y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; - right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; - bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - orientation += M_PI_2; - if (mOrientedRanges.haveOrientation && orientation > mOrientedRanges.orientation.max) { - orientation -= (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); - } - break; - default: - x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; - bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; - break; - } - - // Write output coords. - PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i]; - out.clear(); - out.setAxisValue(AMOTION_EVENT_AXIS_X, x); - out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); - out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); - out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); - out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt); - out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance); - if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right); - out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom); - } else { - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); - } - - // Write output properties. - PointerProperties& properties = - mCurrentCookedState.cookedPointerData.pointerProperties[i]; - uint32_t id = in.id; - properties.clear(); - properties.id = id; - properties.toolType = in.toolType; - - // Write id index. - mCurrentCookedState.cookedPointerData.idToIndex[id] = i; - } -} - -void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, - PointerUsage pointerUsage) { - if (pointerUsage != mPointerUsage) { - abortPointerUsage(when, policyFlags); - mPointerUsage = pointerUsage; - } - - switch (mPointerUsage) { - case POINTER_USAGE_GESTURES: - dispatchPointerGestures(when, policyFlags, false /*isTimeout*/); - break; - case POINTER_USAGE_STYLUS: - dispatchPointerStylus(when, policyFlags); - break; - case POINTER_USAGE_MOUSE: - dispatchPointerMouse(when, policyFlags); - break; - default: - break; - } -} - -void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) { - switch (mPointerUsage) { - case POINTER_USAGE_GESTURES: - abortPointerGestures(when, policyFlags); - break; - case POINTER_USAGE_STYLUS: - abortPointerStylus(when, policyFlags); - break; - case POINTER_USAGE_MOUSE: - abortPointerMouse(when, policyFlags); - break; - default: - break; - } - - mPointerUsage = POINTER_USAGE_NONE; -} - -void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, - bool isTimeout) { - // Update current gesture coordinates. - bool cancelPreviousGesture, finishPreviousGesture; - bool sendEvents = preparePointerGestures(when, - &cancelPreviousGesture, &finishPreviousGesture, isTimeout); - if (!sendEvents) { - return; - } - if (finishPreviousGesture) { - cancelPreviousGesture = false; - } - - // Update the pointer presentation and spots. - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - if (finishPreviousGesture || cancelPreviousGesture) { - mPointerController->clearSpots(); - } - - if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { - mPointerController->setSpots(mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, - mPointerController->getDisplayId()); - } - } else { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - } - - // Show or hide the pointer if needed. - switch (mPointerGesture.currentGestureMode) { - case PointerGesture::NEUTRAL: - case PointerGesture::QUIET: - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH - && mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) { - // Remind the user of where the pointer is after finishing a gesture with spots. - mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL); - } - break; - case PointerGesture::TAP: - case PointerGesture::TAP_DRAG: - case PointerGesture::BUTTON_CLICK_OR_DRAG: - case PointerGesture::HOVER: - case PointerGesture::PRESS: - case PointerGesture::SWIPE: - // Unfade the pointer when the current gesture manipulates the - // area directly under the pointer. - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - break; - case PointerGesture::FREEFORM: - // Fade the pointer when the current gesture manipulates a different - // area and there are spots to guide the user experience. - if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } else { - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } - break; - } - - // Send events! - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentCookedState.buttonState; - - // Update last coordinates of pointers that have moved so that we observe the new - // pointer positions at the same time as other pointers that have just gone up. - bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP - || mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG - || mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG - || mPointerGesture.currentGestureMode == PointerGesture::PRESS - || mPointerGesture.currentGestureMode == PointerGesture::SWIPE - || mPointerGesture.currentGestureMode == PointerGesture::FREEFORM; - bool moveNeeded = false; - if (down && !cancelPreviousGesture && !finishPreviousGesture - && !mPointerGesture.lastGestureIdBits.isEmpty() - && !mPointerGesture.currentGestureIdBits.isEmpty()) { - BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value - & mPointerGesture.lastGestureIdBits.value); - moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, - movedGestureIdBits); - if (buttonState != mLastCookedState.buttonState) { - moveNeeded = true; - } - } - - // Send motion events for all pointers that went up or were canceled. - BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); - if (!dispatchedGestureIdBits.isEmpty()) { - if (cancelPreviousGesture) { - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, - dispatchedGestureIdBits, -1, 0, - 0, mPointerGesture.downTime); - - dispatchedGestureIdBits.clear(); - } else { - BitSet32 upGestureIdBits; - if (finishPreviousGesture) { - upGestureIdBits = dispatchedGestureIdBits; - } else { - upGestureIdBits.value = dispatchedGestureIdBits.value - & ~mPointerGesture.currentGestureIdBits.value; - } - while (!upGestureIdBits.isEmpty()) { - uint32_t id = upGestureIdBits.clearFirstMarkedBit(); - - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, - dispatchedGestureIdBits, id, - 0, 0, mPointerGesture.downTime); - - dispatchedGestureIdBits.clearBit(id); - } - } - } - - // Send motion events for all pointers that moved. - if (moveNeeded) { - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, - dispatchedGestureIdBits, -1, - 0, 0, mPointerGesture.downTime); - } - - // Send motion events for all pointers that went down. - if (down) { - BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value - & ~dispatchedGestureIdBits.value); - while (!downGestureIdBits.isEmpty()) { - uint32_t id = downGestureIdBits.clearFirstMarkedBit(); - dispatchedGestureIdBits.markBit(id); - - if (dispatchedGestureIdBits.count() == 1) { - mPointerGesture.downTime = when; - } - - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, metaState, buttonState, 0, - /* deviceTimestamp */ 0, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, - dispatchedGestureIdBits, id, - 0, 0, mPointerGesture.downTime); - } - } - - // Send motion events for hover. - if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - mPointerGesture.currentGestureProperties, - mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, -1, - 0, 0, mPointerGesture.downTime); - } else if (dispatchedGestureIdBits.isEmpty() - && !mPointerGesture.lastGestureIdBits.isEmpty()) { - // Synthesize a hover move event after all pointers go up to indicate that - // the pointer is hovering again even if the user is not currently touching - // the touch pad. This ensures that a view will receive a fresh hover enter - // event after a tap. - float x, y; - mPointerController->getPosition(&x, &y); - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - - PointerCoords pointerCoords; - pointerCoords.clear(); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - - const int32_t displayId = mPointerController->getDisplayId(); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, - metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - 0, 0, mPointerGesture.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Update state. - mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode; - if (!down) { - mPointerGesture.lastGestureIdBits.clear(); - } else { - mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits; - for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; - mPointerGesture.lastGestureProperties[index].copyFrom( - mPointerGesture.currentGestureProperties[index]); - mPointerGesture.lastGestureCoords[index].copyFrom( - mPointerGesture.currentGestureCoords[index]); - mPointerGesture.lastGestureIdToIndex[id] = index; - } - } -} - -void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) { - // Cancel previously dispatches pointers. - if (!mPointerGesture.lastGestureIdBits.isEmpty()) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t buttonState = mCurrentRawState.buttonState; - dispatchMotion(when, policyFlags, mSource, - AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - mPointerGesture.lastGestureProperties, - mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, - mPointerGesture.lastGestureIdBits, -1, - 0, 0, mPointerGesture.downTime); - } - - // Reset the current pointer gesture. - mPointerGesture.reset(); - mPointerVelocityControl.reset(); - - // Remove any current spots. - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - mPointerController->clearSpots(); - } -} - -bool TouchInputMapper::preparePointerGestures(nsecs_t when, - bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout) { - *outCancelPreviousGesture = false; - *outFinishPreviousGesture = false; - - // Handle TAP timeout. - if (isTimeout) { -#if DEBUG_GESTURES - ALOGD("Gestures: Processing timeout"); -#endif - - if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { - if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - // The tap/drag timeout has not yet expired. - getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime - + mConfig.pointerGestureTapDragInterval); - } else { - // The tap is finished. -#if DEBUG_GESTURES - ALOGD("Gestures: TAP finished"); -#endif - *outFinishPreviousGesture = true; - - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; - mPointerGesture.currentGestureIdBits.clear(); - - mPointerVelocityControl.reset(); - return true; - } - } - - // We did not handle this timeout. - return false; - } - - const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count(); - const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count(); - - // Update the velocity tracker. - { - VelocityTracker::Position positions[MAX_POINTERS]; - uint32_t count = 0; - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) { - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(id); - positions[count].x = pointer.x * mPointerXMovementScale; - positions[count].y = pointer.y * mPointerYMovementScale; - } - mPointerGesture.velocityTracker.addMovement(when, - mCurrentCookedState.fingerIdBits, positions); - } - - // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning - // to NEUTRAL, then we should not generate tap event. - if (mPointerGesture.lastGestureMode != PointerGesture::HOVER - && mPointerGesture.lastGestureMode != PointerGesture::TAP - && mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) { - mPointerGesture.resetTap(); - } - - // Pick a new active touch id if needed. - // Choose an arbitrary pointer that just went down, if there is one. - // Otherwise choose an arbitrary remaining pointer. - // This guarantees we always have an active touch id when there is at least one pointer. - // We keep the same active touch id for as long as possible. - int32_t lastActiveTouchId = mPointerGesture.activeTouchId; - int32_t activeTouchId = lastActiveTouchId; - if (activeTouchId < 0) { - if (!mCurrentCookedState.fingerIdBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = - mCurrentCookedState.fingerIdBits.firstMarkedBit(); - mPointerGesture.firstTouchTime = when; - } - } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) { - if (!mCurrentCookedState.fingerIdBits.isEmpty()) { - activeTouchId = mPointerGesture.activeTouchId = - mCurrentCookedState.fingerIdBits.firstMarkedBit(); - } else { - activeTouchId = mPointerGesture.activeTouchId = -1; - } - } - - // Determine whether we are in quiet time. - bool isQuietTime = false; - if (activeTouchId < 0) { - mPointerGesture.resetQuietTime(); - } else { - isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval; - if (!isQuietTime) { - if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS - || mPointerGesture.lastGestureMode == PointerGesture::SWIPE - || mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) - && currentFingerCount < 2) { - // Enter quiet time when exiting swipe or freeform state. - // This is to prevent accidentally entering the hover state and flinging the - // pointer when finishing a swipe and there is still one pointer left onscreen. - isQuietTime = true; - } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG - && currentFingerCount >= 2 - && !isPointerDown(mCurrentRawState.buttonState)) { - // Enter quiet time when releasing the button and there are still two or more - // fingers down. This may indicate that one finger was used to press the button - // but it has not gone up yet. - isQuietTime = true; - } - if (isQuietTime) { - mPointerGesture.quietTime = when; - } - } - } - - // Switch states based on button and pointer state. - if (isQuietTime) { - // Case 1: Quiet time. (QUIET) -#if DEBUG_GESTURES - ALOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime - + mConfig.pointerGestureQuietInterval - when) * 0.000001f); -#endif - if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) { - *outFinishPreviousGesture = true; - } - - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::QUIET; - mPointerGesture.currentGestureIdBits.clear(); - - mPointerVelocityControl.reset(); - } else if (isPointerDown(mCurrentRawState.buttonState)) { - // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG) - // The pointer follows the active touch point. - // Emit DOWN, MOVE, UP events at the pointer location. - // - // Only the active touch matters; other fingers are ignored. This policy helps - // to handle the case where the user places a second finger on the touch pad - // to apply the necessary force to depress an integrated button below the surface. - // We don't want the second finger to be delivered to applications. - // - // For this to work well, we need to make sure to track the pointer that is really - // active. If the user first puts one finger down to click then adds another - // finger to drag then the active pointer should switch to the finger that is - // being dragged. -#if DEBUG_GESTURES - ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, " - "currentFingerCount=%d", activeTouchId, currentFingerCount); -#endif - // Reset state when just starting. - if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) { - *outFinishPreviousGesture = true; - mPointerGesture.activeGestureId = 0; - } - - // Switch pointers if needed. - // Find the fastest pointer and follow it. - if (activeTouchId >= 0 && currentFingerCount > 1) { - int32_t bestId = -1; - float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - float vx, vy; - if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { - float speed = hypotf(vx, vy); - if (speed > bestSpeed) { - bestId = id; - bestSpeed = speed; - } - } - } - if (bestId >= 0 && bestId != activeTouchId) { - mPointerGesture.activeTouchId = activeTouchId = bestId; -#if DEBUG_GESTURES - ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, " - "bestId=%d, bestSpeed=%0.3f", bestId, bestSpeed); -#endif - } - } - - float deltaX = 0, deltaY = 0; - if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { - const RawPointerData::Pointer& currentPointer = - mCurrentRawState.rawPointerData.pointerForId(activeTouchId); - const RawPointerData::Pointer& lastPointer = - mLastRawState.rawPointerData.pointerForId(activeTouchId); - deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; - deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - // Move the pointer using a relative motion. - // When using spots, the click will occur at the position of the anchor - // spot and all other spots will move there. - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - float x, y; - mPointerController->getPosition(&x, &y); - - mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG; - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } else if (currentFingerCount == 0) { - // Case 3. No fingers down and button is not pressed. (NEUTRAL) - if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) { - *outFinishPreviousGesture = true; - } - - // Watch for taps coming out of HOVER or TAP_DRAG mode. - // Checking for taps after TAP_DRAG allows us to detect double-taps. - bool tapped = false; - if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER - || mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) - && lastFingerCount == 1) { - if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { - float x, y; - mPointerController->getPosition(&x, &y); - if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop - && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { -#if DEBUG_GESTURES - ALOGD("Gestures: TAP"); -#endif - - mPointerGesture.tapUpTime = when; - getContext()->requestTimeoutAtTime(when - + mConfig.pointerGestureTapDragInterval); - - mPointerGesture.activeGestureId = 0; - mPointerGesture.currentGestureMode = PointerGesture::TAP; - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit( - mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[ - mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = - mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue( - AMOTION_EVENT_AXIS_X, mPointerGesture.tapX); - mPointerGesture.currentGestureCoords[0].setAxisValue( - AMOTION_EVENT_AXIS_Y, mPointerGesture.tapY); - mPointerGesture.currentGestureCoords[0].setAxisValue( - AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - - tapped = true; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", - x - mPointerGesture.tapX, - y - mPointerGesture.tapY); -#endif - } - } else { -#if DEBUG_GESTURES - if (mPointerGesture.tapDownTime != LLONG_MIN) { - ALOGD("Gestures: Not a TAP, %0.3fms since down", - (when - mPointerGesture.tapDownTime) * 0.000001f); - } else { - ALOGD("Gestures: Not a TAP, incompatible mode transitions"); - } -#endif - } - } - - mPointerVelocityControl.reset(); - - if (!tapped) { -#if DEBUG_GESTURES - ALOGD("Gestures: NEUTRAL"); -#endif - mPointerGesture.activeGestureId = -1; - mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; - mPointerGesture.currentGestureIdBits.clear(); - } - } else if (currentFingerCount == 1) { - // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG) - // The pointer follows the active touch point. - // When in HOVER, emit HOVER_MOVE events at the pointer location. - // When in TAP_DRAG, emit MOVE events at the pointer location. - ALOG_ASSERT(activeTouchId >= 0); - - mPointerGesture.currentGestureMode = PointerGesture::HOVER; - if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { - if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - float x, y; - mPointerController->getPosition(&x, &y); - if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop - && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { - mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f", - x - mPointerGesture.tapX, - y - mPointerGesture.tapY); -#endif - } - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up", - (when - mPointerGesture.tapUpTime) * 0.000001f); -#endif - } - } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) { - mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; - } - - float deltaX = 0, deltaY = 0; - if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { - const RawPointerData::Pointer& currentPointer = - mCurrentRawState.rawPointerData.pointerForId(activeTouchId); - const RawPointerData::Pointer& lastPointer = - mLastRawState.rawPointerData.pointerForId(activeTouchId); - deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; - deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - // Move the pointer using a relative motion. - // When using spots, the hover or drag will occur at the position of the anchor spot. - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - bool down; - if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) { -#if DEBUG_GESTURES - ALOGD("Gestures: TAP_DRAG"); -#endif - down = true; - } else { -#if DEBUG_GESTURES - ALOGD("Gestures: HOVER"); -#endif - if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) { - *outFinishPreviousGesture = true; - } - mPointerGesture.activeGestureId = 0; - down = false; - } - - float x, y; - mPointerController->getPosition(&x, &y); - - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - down ? 1.0f : 0.0f); - - if (lastFingerCount == 0 && currentFingerCount != 0) { - mPointerGesture.resetTap(); - mPointerGesture.tapDownTime = when; - mPointerGesture.tapX = x; - mPointerGesture.tapY = y; - } - } else { - // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) - // We need to provide feedback for each finger that goes down so we cannot wait - // for the fingers to move before deciding what to do. - // - // The ambiguous case is deciding what to do when there are two fingers down but they - // have not moved enough to determine whether they are part of a drag or part of a - // freeform gesture, or just a press or long-press at the pointer location. - // - // When there are two fingers we start with the PRESS hypothesis and we generate a - // down at the pointer location. - // - // When the two fingers move enough or when additional fingers are added, we make - // a decision to transition into SWIPE or FREEFORM mode accordingly. - ALOG_ASSERT(activeTouchId >= 0); - - bool settled = when >= mPointerGesture.firstTouchTime - + mConfig.pointerGestureMultitouchSettleInterval; - if (mPointerGesture.lastGestureMode != PointerGesture::PRESS - && mPointerGesture.lastGestureMode != PointerGesture::SWIPE - && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { - *outFinishPreviousGesture = true; - } else if (!settled && currentFingerCount > lastFingerCount) { - // Additional pointers have gone down but not yet settled. - // Reset the gesture. -#if DEBUG_GESTURES - ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " - "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime - + mConfig.pointerGestureMultitouchSettleInterval - when) - * 0.000001f); -#endif - *outCancelPreviousGesture = true; - } else { - // Continue previous gesture. - mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; - } - - if (*outFinishPreviousGesture || *outCancelPreviousGesture) { - mPointerGesture.currentGestureMode = PointerGesture::PRESS; - mPointerGesture.activeGestureId = 0; - mPointerGesture.referenceIdBits.clear(); - mPointerVelocityControl.reset(); - - // Use the centroid and pointer location as the reference points for the gesture. -#if DEBUG_GESTURES - ALOGD("Gestures: Using centroid as reference for MULTITOUCH, " - "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime - + mConfig.pointerGestureMultitouchSettleInterval - when) - * 0.000001f); -#endif - mCurrentRawState.rawPointerData.getCentroidOfTouchingPointers( - &mPointerGesture.referenceTouchX, - &mPointerGesture.referenceTouchY); - mPointerController->getPosition(&mPointerGesture.referenceGestureX, - &mPointerGesture.referenceGestureY); - } - - // Clear the reference deltas for fingers not yet included in the reference calculation. - for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value - & ~mPointerGesture.referenceIdBits.value); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - mPointerGesture.referenceDeltas[id].dx = 0; - mPointerGesture.referenceDeltas[id].dy = 0; - } - mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits; - - // Add delta for all fingers and calculate a common movement delta. - float commonDeltaX = 0, commonDeltaY = 0; - BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value - & mCurrentCookedState.fingerIdBits.value); - for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) { - bool first = (idBits == commonIdBits); - uint32_t id = idBits.clearFirstMarkedBit(); - const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id); - const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx += cpd.x - lpd.x; - delta.dy += cpd.y - lpd.y; - - if (first) { - commonDeltaX = delta.dx; - commonDeltaY = delta.dy; - } else { - commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); - commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); - } - } - - // Consider transitions from PRESS to SWIPE or MULTITOUCH. - if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { - float dist[MAX_POINTER_ID + 1]; - int32_t distOverThreshold = 0; - for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - dist[id] = hypotf(delta.dx * mPointerXZoomScale, - delta.dy * mPointerYZoomScale); - if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) { - distOverThreshold += 1; - } - } - - // Only transition when at least two pointers have moved further than - // the minimum distance threshold. - if (distOverThreshold >= 2) { - if (currentFingerCount > 2) { - // There are more than two pointers, switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", - currentFingerCount); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else { - // There are exactly two pointers. - BitSet32 idBits(mCurrentCookedState.fingerIdBits); - uint32_t id1 = idBits.clearFirstMarkedBit(); - uint32_t id2 = idBits.firstMarkedBit(); - const RawPointerData::Pointer& p1 = - mCurrentRawState.rawPointerData.pointerForId(id1); - const RawPointerData::Pointer& p2 = - mCurrentRawState.rawPointerData.pointerForId(id2); - float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y); - if (mutualDistance > mPointerGestureMaxSwipeWidth) { - // There are two pointers but they are too far apart for a SWIPE, - // switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", - mutualDistance, mPointerGestureMaxSwipeWidth); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else { - // There are two pointers. Wait for both pointers to start moving - // before deciding whether this is a SWIPE or FREEFORM gesture. - float dist1 = dist[id1]; - float dist2 = dist[id2]; - if (dist1 >= mConfig.pointerGestureMultitouchMinDistance - && dist2 >= mConfig.pointerGestureMultitouchMinDistance) { - // Calculate the dot product of the displacement vectors. - // When the vectors are oriented in approximately the same direction, - // the angle betweeen them is near zero and the cosine of the angle - // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2). - PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; - PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; - float dx1 = delta1.dx * mPointerXZoomScale; - float dy1 = delta1.dy * mPointerYZoomScale; - float dx2 = delta2.dx * mPointerXZoomScale; - float dy2 = delta2.dy * mPointerYZoomScale; - float dot = dx1 * dx2 + dy1 * dy2; - float cosine = dot / (dist1 * dist2); // denominator always > 0 - if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) { - // Pointers are moving in the same direction. Switch to SWIPE. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to SWIPE, " - "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " - "cosine %0.3f >= %0.3f", - dist1, mConfig.pointerGestureMultitouchMinDistance, - dist2, mConfig.pointerGestureMultitouchMinDistance, - cosine, mConfig.pointerGestureSwipeTransitionAngleCosine); -#endif - mPointerGesture.currentGestureMode = PointerGesture::SWIPE; - } else { - // Pointers are moving in different directions. Switch to FREEFORM. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS transitioned to FREEFORM, " - "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " - "cosine %0.3f < %0.3f", - dist1, mConfig.pointerGestureMultitouchMinDistance, - dist2, mConfig.pointerGestureMultitouchMinDistance, - cosine, mConfig.pointerGestureSwipeTransitionAngleCosine); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } - } - } - } - } - } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { - // Switch from SWIPE to FREEFORM if additional pointers go down. - // Cancel previous gesture. - if (currentFingerCount > 2) { -#if DEBUG_GESTURES - ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2", - currentFingerCount); -#endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } - } - - // Move the reference points based on the overall group motion of the fingers - // except in PRESS mode while waiting for a transition to occur. - if (mPointerGesture.currentGestureMode != PointerGesture::PRESS - && (commonDeltaX || commonDeltaY)) { - for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx = 0; - delta.dy = 0; - } - - mPointerGesture.referenceTouchX += commonDeltaX; - mPointerGesture.referenceTouchY += commonDeltaY; - - commonDeltaX *= mPointerXMovementScale; - commonDeltaY *= mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY); - mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); - - mPointerGesture.referenceGestureX += commonDeltaX; - mPointerGesture.referenceGestureY += commonDeltaY; - } - - // Report gestures. - if (mPointerGesture.currentGestureMode == PointerGesture::PRESS - || mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { - // PRESS or SWIPE mode. -#if DEBUG_GESTURES - ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d," - "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); -#endif - ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); - - mPointerGesture.currentGestureIdBits.clear(); - mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; - mPointerGesture.currentGestureProperties[0].clear(); - mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; - mPointerGesture.currentGestureProperties[0].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[0].clear(); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, - mPointerGesture.referenceGestureX); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, - mPointerGesture.referenceGestureY); - mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { - // FREEFORM mode. -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM activeTouchId=%d," - "activeGestureId=%d, currentTouchPointerCount=%d", - activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); -#endif - ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); - - mPointerGesture.currentGestureIdBits.clear(); - - BitSet32 mappedTouchIdBits; - BitSet32 usedGestureIdBits; - if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { - // Initially, assign the active gesture id to the active touch point - // if there is one. No other touch id bits are mapped yet. - if (!*outCancelPreviousGesture) { - mappedTouchIdBits.markBit(activeTouchId); - usedGestureIdBits.markBit(mPointerGesture.activeGestureId); - mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = - mPointerGesture.activeGestureId; - } else { - mPointerGesture.activeGestureId = -1; - } - } else { - // Otherwise, assume we mapped all touches from the previous frame. - // Reuse all mappings that are still applicable. - mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value - & mCurrentCookedState.fingerIdBits.value; - usedGestureIdBits = mPointerGesture.lastGestureIdBits; - - // Check whether we need to choose a new active gesture id because the - // current went went up. - for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value - & ~mCurrentCookedState.fingerIdBits.value); - !upTouchIdBits.isEmpty(); ) { - uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit(); - uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; - if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { - mPointerGesture.activeGestureId = -1; - break; - } - } - } - -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM follow up " - "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " - "activeGestureId=%d", - mappedTouchIdBits.value, usedGestureIdBits.value, - mPointerGesture.activeGestureId); -#endif - - BitSet32 idBits(mCurrentCookedState.fingerIdBits); - for (uint32_t i = 0; i < currentFingerCount; i++) { - uint32_t touchId = idBits.clearFirstMarkedBit(); - uint32_t gestureId; - if (!mappedTouchIdBits.hasBit(touchId)) { - gestureId = usedGestureIdBits.markFirstUnmarkedBit(); - mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM " - "new mapping for touch id %d -> gesture id %d", - touchId, gestureId); -#endif - } else { - gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM " - "existing mapping for touch id %d -> gesture id %d", - touchId, gestureId); -#endif - } - mPointerGesture.currentGestureIdBits.markBit(gestureId); - mPointerGesture.currentGestureIdToIndex[gestureId] = i; - - const RawPointerData::Pointer& pointer = - mCurrentRawState.rawPointerData.pointerForId(touchId); - float deltaX = (pointer.x - mPointerGesture.referenceTouchX) - * mPointerXZoomScale; - float deltaY = (pointer.y - mPointerGesture.referenceTouchY) - * mPointerYZoomScale; - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - - mPointerGesture.currentGestureProperties[i].clear(); - mPointerGesture.currentGestureProperties[i].id = gestureId; - mPointerGesture.currentGestureProperties[i].toolType = - AMOTION_EVENT_TOOL_TYPE_FINGER; - mPointerGesture.currentGestureCoords[i].clear(); - mPointerGesture.currentGestureCoords[i].setAxisValue( - AMOTION_EVENT_AXIS_X, mPointerGesture.referenceGestureX + deltaX); - mPointerGesture.currentGestureCoords[i].setAxisValue( - AMOTION_EVENT_AXIS_Y, mPointerGesture.referenceGestureY + deltaY); - mPointerGesture.currentGestureCoords[i].setAxisValue( - AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } - - if (mPointerGesture.activeGestureId < 0) { - mPointerGesture.activeGestureId = - mPointerGesture.currentGestureIdBits.firstMarkedBit(); -#if DEBUG_GESTURES - ALOGD("Gestures: FREEFORM new " - "activeGestureId=%d", mPointerGesture.activeGestureId); -#endif - } - } - } - - mPointerController->setButtonState(mCurrentRawState.buttonState); - -#if DEBUG_GESTURES - ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " - "currentGestureMode=%d, currentGestureIdBits=0x%08x, " - "lastGestureMode=%d, lastGestureIdBits=0x%08x", - toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), - mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value, - mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value); - for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; - const PointerProperties& properties = mPointerGesture.currentGestureProperties[index]; - const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; - ALOGD(" currentGesture[%d]: index=%d, toolType=%d, " - "x=%0.3f, y=%0.3f, pressure=%0.3f", - id, index, properties.toolType, - coords.getAxisValue(AMOTION_EVENT_AXIS_X), - coords.getAxisValue(AMOTION_EVENT_AXIS_Y), - coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); - } - for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty(); ) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; - const PointerProperties& properties = mPointerGesture.lastGestureProperties[index]; - const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; - ALOGD(" lastGesture[%d]: index=%d, toolType=%d, " - "x=%0.3f, y=%0.3f, pressure=%0.3f", - id, index, properties.toolType, - coords.getAxisValue(AMOTION_EVENT_AXIS_X), - coords.getAxisValue(AMOTION_EVENT_AXIS_Y), - coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); - } -#endif - return true; -} - -void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - bool down, hovering; - if (!mCurrentCookedState.stylusIdBits.isEmpty()) { - uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); - uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; - float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(); - float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY(); - mPointerController->setPosition(x, y); - - hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); - down = !hovering; - - mPointerController->getPosition(&x, &y); - mPointerSimple.currentCoords.copyFrom( - mCurrentCookedState.cookedPointerData.pointerCoords[index]); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerSimple.currentProperties.id = 0; - mPointerSimple.currentProperties.toolType = - mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType; - } else { - down = false; - hovering = false; - } - - dispatchPointerSimple(when, policyFlags, down, hovering); -} - -void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) { - abortPointerSimple(when, policyFlags); -} - -void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - bool down, hovering; - if (!mCurrentCookedState.mouseIdBits.isEmpty()) { - uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit(); - uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id]; - float deltaX = 0, deltaY = 0; - if (mLastCookedState.mouseIdBits.hasBit(id)) { - uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id]; - deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x - - mLastRawState.rawPointerData.pointers[lastIndex].x) - * mPointerXMovementScale; - deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y - - mLastRawState.rawPointerData.pointers[lastIndex].y) - * mPointerYMovementScale; - - rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); - mPointerVelocityControl.move(when, &deltaX, &deltaY); - - mPointerController->move(deltaX, deltaY); - } else { - mPointerVelocityControl.reset(); - } - - down = isPointerDown(mCurrentRawState.buttonState); - hovering = !down; - - float x, y; - mPointerController->getPosition(&x, &y); - mPointerSimple.currentCoords.copyFrom( - mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, - hovering ? 0.0f : 1.0f); - mPointerSimple.currentProperties.id = 0; - mPointerSimple.currentProperties.toolType = - mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType; - } else { - mPointerVelocityControl.reset(); - - down = false; - hovering = false; - } - - dispatchPointerSimple(when, policyFlags, down, hovering); -} - -void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) { - abortPointerSimple(when, policyFlags); - - mPointerVelocityControl.reset(); -} - -void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, - bool down, bool hovering) { - int32_t metaState = getContext()->getGlobalMetaState(); - int32_t displayId = mViewport.displayId; - - if (mPointerController != nullptr) { - if (down || hovering) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - mPointerController->clearSpots(); - mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } - displayId = mPointerController->getDisplayId(); - } - - if (mPointerSimple.down && !down) { - mPointerSimple.down = false; - - // Send up. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (mPointerSimple.hovering && !hovering) { - mPointerSimple.hovering = false; - - // Send hover exit. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (down) { - if (!mPointerSimple.down) { - mPointerSimple.down = true; - mPointerSimple.downTime = when; - - // Send down. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Send move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (hovering) { - if (!mPointerSimple.hovering) { - mPointerSimple.hovering = true; - - // Send hover enter. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Send hover move. - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { - float vscroll = mCurrentRawState.rawVScroll; - float hscroll = mCurrentRawState.rawHScroll; - mWheelYVelocityControl.move(when, nullptr, &vscroll); - mWheelXVelocityControl.move(when, &hscroll, nullptr); - - // Send scroll. - PointerCoords pointerCoords; - pointerCoords.copyFrom(mPointerSimple.currentCoords); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, - 1, &mPointerSimple.currentProperties, &pointerCoords, - mOrientedXPrecision, mOrientedYPrecision, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); - } - - // Save state. - if (down || hovering) { - mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords); - mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties); - } else { - mPointerSimple.reset(); - } -} - -void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) { - mPointerSimple.currentCoords.clear(); - mPointerSimple.currentProperties.clear(); - - dispatchPointerSimple(when, policyFlags, false, false); -} - -void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, - int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, int32_t edgeFlags, uint32_t deviceTimestamp, - const PointerProperties* properties, const PointerCoords* coords, - const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, - float xPrecision, float yPrecision, nsecs_t downTime) { - PointerCoords pointerCoords[MAX_POINTERS]; - PointerProperties pointerProperties[MAX_POINTERS]; - uint32_t pointerCount = 0; - while (!idBits.isEmpty()) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t index = idToIndex[id]; - pointerProperties[pointerCount].copyFrom(properties[index]); - pointerCoords[pointerCount].copyFrom(coords[index]); - - if (changedId >= 0 && id == uint32_t(changedId)) { - action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - } - - pointerCount += 1; - } - - ALOG_ASSERT(pointerCount != 0); - - if (changedId >= 0 && pointerCount == 1) { - // Replace initial down and final up action. - // We can compare the action without masking off the changed pointer index - // because we know the index is 0. - if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { - action = AMOTION_EVENT_ACTION_DOWN; - } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { - action = AMOTION_EVENT_ACTION_UP; - } else { - // Can't happen. - ALOG_ASSERT(false); - } - } - const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE); - const int32_t deviceId = getDeviceId(); - std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId); - std::for_each(frames.begin(), frames.end(), - [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, - source, displayId, policyFlags, - action, actionButton, flags, metaState, buttonState, MotionClassification::NONE, - edgeFlags, deviceTimestamp, pointerCount, pointerProperties, pointerCoords, - xPrecision, yPrecision, downTime, std::move(frames)); - getListener()->notifyMotion(&args); -} - -bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, - const PointerCoords* inCoords, const uint32_t* inIdToIndex, - PointerProperties* outProperties, PointerCoords* outCoords, const uint32_t* outIdToIndex, - BitSet32 idBits) const { - bool changed = false; - while (!idBits.isEmpty()) { - uint32_t id = idBits.clearFirstMarkedBit(); - uint32_t inIndex = inIdToIndex[id]; - uint32_t outIndex = outIdToIndex[id]; - - const PointerProperties& curInProperties = inProperties[inIndex]; - const PointerCoords& curInCoords = inCoords[inIndex]; - PointerProperties& curOutProperties = outProperties[outIndex]; - PointerCoords& curOutCoords = outCoords[outIndex]; - - if (curInProperties != curOutProperties) { - curOutProperties.copyFrom(curInProperties); - changed = true; - } - - if (curInCoords != curOutCoords) { - curOutCoords.copyFrom(curInCoords); - changed = true; - } - } - return changed; -} - -void TouchInputMapper::fadePointer() { - if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } -} - -void TouchInputMapper::cancelTouch(nsecs_t when) { - abortPointerUsage(when, 0 /*policyFlags*/); - abortTouches(when, 0 /* policyFlags*/); -} - -bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { - const float scaledX = x * mXScale; - const float scaledY = y * mYScale; - return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue - && scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth - && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue - && scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight; -} - -const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { - - for (const VirtualKey& virtualKey: mVirtualKeys) { -#if DEBUG_VIRTUAL_KEYS - ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " - "left=%d, top=%d, right=%d, bottom=%d", - x, y, - virtualKey.keyCode, virtualKey.scanCode, - virtualKey.hitLeft, virtualKey.hitTop, - virtualKey.hitRight, virtualKey.hitBottom); -#endif - - if (virtualKey.isHit(x, y)) { - return & virtualKey; - } - } - - return nullptr; -} - -void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) { - uint32_t currentPointerCount = current->rawPointerData.pointerCount; - uint32_t lastPointerCount = last->rawPointerData.pointerCount; - - current->rawPointerData.clearIdBits(); - - if (currentPointerCount == 0) { - // No pointers to assign. - return; - } - - if (lastPointerCount == 0) { - // All pointers are new. - for (uint32_t i = 0; i < currentPointerCount; i++) { - uint32_t id = i; - current->rawPointerData.pointers[i].id = id; - current->rawPointerData.idToIndex[id] = i; - current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i)); - } - return; - } - - if (currentPointerCount == 1 && lastPointerCount == 1 - && current->rawPointerData.pointers[0].toolType - == last->rawPointerData.pointers[0].toolType) { - // Only one pointer and no change in count so it must have the same id as before. - uint32_t id = last->rawPointerData.pointers[0].id; - current->rawPointerData.pointers[0].id = id; - current->rawPointerData.idToIndex[id] = 0; - current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0)); - return; - } - - // General case. - // We build a heap of squared euclidean distances between current and last pointers - // associated with the current and last pointer indices. Then, we find the best - // match (by distance) for each current pointer. - // The pointers must have the same tool type but it is possible for them to - // transition from hovering to touching or vice-versa while retaining the same id. - PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; - - uint32_t heapSize = 0; - for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; - currentPointerIndex++) { - for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; - lastPointerIndex++) { - const RawPointerData::Pointer& currentPointer = - current->rawPointerData.pointers[currentPointerIndex]; - const RawPointerData::Pointer& lastPointer = - last->rawPointerData.pointers[lastPointerIndex]; - if (currentPointer.toolType == lastPointer.toolType) { - int64_t deltaX = currentPointer.x - lastPointer.x; - int64_t deltaY = currentPointer.y - lastPointer.y; - - uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); - - // Insert new element into the heap (sift up). - heap[heapSize].currentPointerIndex = currentPointerIndex; - heap[heapSize].lastPointerIndex = lastPointerIndex; - heap[heapSize].distance = distance; - heapSize += 1; - } - } - } - - // Heapify - for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) { - startIndex -= 1; - for (uint32_t parentIndex = startIndex; ;) { - uint32_t childIndex = parentIndex * 2 + 1; - if (childIndex >= heapSize) { - break; - } - - if (childIndex + 1 < heapSize - && heap[childIndex + 1].distance < heap[childIndex].distance) { - childIndex += 1; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - } - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, - i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, - heap[i].distance); - } -#endif - - // Pull matches out by increasing order of distance. - // To avoid reassigning pointers that have already been matched, the loop keeps track - // of which last and current pointers have been matched using the matchedXXXBits variables. - // It also tracks the used pointer id bits. - BitSet32 matchedLastBits(0); - BitSet32 matchedCurrentBits(0); - BitSet32 usedIdBits(0); - bool first = true; - for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) { - while (heapSize > 0) { - if (first) { - // The first time through the loop, we just consume the root element of - // the heap (the one with smallest distance). - first = false; - } else { - // Previous iterations consumed the root element of the heap. - // Pop root element off of the heap (sift down). - heap[0] = heap[heapSize]; - for (uint32_t parentIndex = 0; ;) { - uint32_t childIndex = parentIndex * 2 + 1; - if (childIndex >= heapSize) { - break; - } - - if (childIndex + 1 < heapSize - && heap[childIndex + 1].distance < heap[childIndex].distance) { - childIndex += 1; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, - i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, - heap[i].distance); - } -#endif - } - - heapSize -= 1; - - uint32_t currentPointerIndex = heap[0].currentPointerIndex; - if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched - - uint32_t lastPointerIndex = heap[0].lastPointerIndex; - if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched - - matchedCurrentBits.markBit(currentPointerIndex); - matchedLastBits.markBit(lastPointerIndex); - - uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id; - current->rawPointerData.pointers[currentPointerIndex].id = id; - current->rawPointerData.idToIndex[id] = currentPointerIndex; - current->rawPointerData.markIdBit(id, - current->rawPointerData.isHovering(currentPointerIndex)); - usedIdBits.markBit(id); - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 - ", id=%" PRIu32 ", distance=%" PRIu64, - lastPointerIndex, currentPointerIndex, id, heap[0].distance); -#endif - break; - } - } - - // Assign fresh ids to pointers that were not matched in the process. - for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) { - uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit(); - uint32_t id = usedIdBits.markFirstUnmarkedBit(); - - current->rawPointerData.pointers[currentPointerIndex].id = id; - current->rawPointerData.idToIndex[id] = currentPointerIndex; - current->rawPointerData.markIdBit(id, - current->rawPointerData.isHovering(currentPointerIndex)); - -#if DEBUG_POINTER_ASSIGNMENT - ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id); -#endif - } -} - -int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) { - return AKEY_STATE_VIRTUAL; - } - - for (const VirtualKey& virtualKey : mVirtualKeys) { - if (virtualKey.keyCode == keyCode) { - return AKEY_STATE_UP; - } - } - - return AKEY_STATE_UNKNOWN; -} - -int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) { - return AKEY_STATE_VIRTUAL; - } - - for (const VirtualKey& virtualKey : mVirtualKeys) { - if (virtualKey.scanCode == scanCode) { - return AKEY_STATE_UP; - } - } - - return AKEY_STATE_UNKNOWN; -} - -bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - for (const VirtualKey& virtualKey : mVirtualKeys) { - for (size_t i = 0; i < numCodes; i++) { - if (virtualKey.keyCode == keyCodes[i]) { - outFlags[i] = 1; - } - } - } - - return true; -} - -std::optional<int32_t> TouchInputMapper::getAssociatedDisplay() { - if (mParameters.hasAssociatedDisplay) { - if (mDeviceMode == DEVICE_MODE_POINTER) { - return std::make_optional(mPointerController->getDisplayId()); - } else { - return std::make_optional(mViewport.displayId); - } - } - return std::nullopt; -} - -// --- SingleTouchInputMapper --- - -SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : - TouchInputMapper(device) { -} - -SingleTouchInputMapper::~SingleTouchInputMapper() { -} - -void SingleTouchInputMapper::reset(nsecs_t when) { - mSingleTouchMotionAccumulator.reset(getDevice()); - - TouchInputMapper::reset(when); -} - -void SingleTouchInputMapper::process(const RawEvent* rawEvent) { - TouchInputMapper::process(rawEvent); - - mSingleTouchMotionAccumulator.process(rawEvent); -} - -void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { - if (mTouchButtonAccumulator.isToolActive()) { - outState->rawPointerData.pointerCount = 1; - outState->rawPointerData.idToIndex[0] = 0; - - bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE - && (mTouchButtonAccumulator.isHovering() - || (mRawPointerAxes.pressure.valid - && mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0)); - outState->rawPointerData.markIdBit(0, isHovering); - - RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0]; - outPointer.id = 0; - outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX(); - outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY(); - outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); - outPointer.touchMajor = 0; - outPointer.touchMinor = 0; - outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); - outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); - outPointer.orientation = 0; - outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance(); - outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX(); - outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY(); - outPointer.toolType = mTouchButtonAccumulator.getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - } - outPointer.isHovering = isHovering; - } -} - -void SingleTouchInputMapper::configureRawPointerAxes() { - TouchInputMapper::configureRawPointerAxes(); - - getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x); - getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y); - getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure); - getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor); - getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance); - getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX); - getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY); -} - -bool SingleTouchInputMapper::hasStylus() const { - return mTouchButtonAccumulator.hasStylus(); -} - - -// --- MultiTouchInputMapper --- - -MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : - TouchInputMapper(device) { -} - -MultiTouchInputMapper::~MultiTouchInputMapper() { -} - -void MultiTouchInputMapper::reset(nsecs_t when) { - mMultiTouchMotionAccumulator.reset(getDevice()); - - mPointerIdBits.clear(); - - TouchInputMapper::reset(when); -} - -void MultiTouchInputMapper::process(const RawEvent* rawEvent) { - TouchInputMapper::process(rawEvent); - - mMultiTouchMotionAccumulator.process(rawEvent); -} - -void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { - size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); - size_t outCount = 0; - BitSet32 newPointerIdBits; - mHavePointerIds = true; - - for (size_t inIndex = 0; inIndex < inCount; inIndex++) { - const MultiTouchMotionAccumulator::Slot* inSlot = - mMultiTouchMotionAccumulator.getSlot(inIndex); - if (!inSlot->isInUse()) { - continue; - } - - if (outCount >= MAX_POINTERS) { -#if DEBUG_POINTERS - ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " - "ignoring the rest.", - getDeviceName().c_str(), MAX_POINTERS); -#endif - break; // too many fingers! - } - - RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; - outPointer.x = inSlot->getX(); - outPointer.y = inSlot->getY(); - outPointer.pressure = inSlot->getPressure(); - outPointer.touchMajor = inSlot->getTouchMajor(); - outPointer.touchMinor = inSlot->getTouchMinor(); - outPointer.toolMajor = inSlot->getToolMajor(); - outPointer.toolMinor = inSlot->getToolMinor(); - outPointer.orientation = inSlot->getOrientation(); - outPointer.distance = inSlot->getDistance(); - outPointer.tiltX = 0; - outPointer.tiltY = 0; - - outPointer.toolType = inSlot->getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = mTouchButtonAccumulator.getToolType(); - if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - } - } - - bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE - && (mTouchButtonAccumulator.isHovering() - || (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0)); - outPointer.isHovering = isHovering; - - // Assign pointer id using tracking id if available. - if (mHavePointerIds) { - int32_t trackingId = inSlot->getTrackingId(); - int32_t id = -1; - if (trackingId >= 0) { - for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty(); ) { - uint32_t n = idBits.clearFirstMarkedBit(); - if (mPointerTrackingIdMap[n] == trackingId) { - id = n; - } - } - - if (id < 0 && !mPointerIdBits.isFull()) { - id = mPointerIdBits.markFirstUnmarkedBit(); - mPointerTrackingIdMap[id] = trackingId; - } - } - if (id < 0) { - mHavePointerIds = false; - outState->rawPointerData.clearIdBits(); - newPointerIdBits.clear(); - } else { - outPointer.id = id; - outState->rawPointerData.idToIndex[id] = outCount; - outState->rawPointerData.markIdBit(id, isHovering); - newPointerIdBits.markBit(id); - } - } - outCount += 1; - } - - outState->deviceTimestamp = mMultiTouchMotionAccumulator.getDeviceTimestamp(); - outState->rawPointerData.pointerCount = outCount; - mPointerIdBits = newPointerIdBits; - - mMultiTouchMotionAccumulator.finishSync(); -} - -void MultiTouchInputMapper::configureRawPointerAxes() { - TouchInputMapper::configureRawPointerAxes(); - - getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x); - getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y); - getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor); - getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor); - getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor); - getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor); - getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation); - getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure); - getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance); - getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId); - getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot); - - if (mRawPointerAxes.trackingId.valid - && mRawPointerAxes.slot.valid - && mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) { - size_t slotCount = mRawPointerAxes.slot.maxValue + 1; - if (slotCount > MAX_SLOTS) { - ALOGW("MultiTouch Device %s reported %zu slots but the framework " - "only supports a maximum of %zu slots at this time.", - getDeviceName().c_str(), slotCount, MAX_SLOTS); - slotCount = MAX_SLOTS; - } - mMultiTouchMotionAccumulator.configure(getDevice(), - slotCount, true /*usingSlotsProtocol*/); - } else { - mMultiTouchMotionAccumulator.configure(getDevice(), - MAX_POINTERS, false /*usingSlotsProtocol*/); - } -} - -bool MultiTouchInputMapper::hasStylus() const { - return mMultiTouchMotionAccumulator.hasStylus() - || mTouchButtonAccumulator.hasStylus(); -} - -// --- ExternalStylusInputMapper - -ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : - InputMapper(device) { - -} - -uint32_t ExternalStylusInputMapper::getSources() { - return AINPUT_SOURCE_STYLUS; -} - -void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, - 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); -} - -void ExternalStylusInputMapper::dump(std::string& dump) { - dump += INDENT2 "External Stylus Input Mapper:\n"; - dump += INDENT3 "Raw Stylus Axes:\n"; - dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure"); - dump += INDENT3 "Stylus State:\n"; - dumpStylusState(dump, mStylusState); -} - -void ExternalStylusInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); - mTouchButtonAccumulator.configure(getDevice()); -} - -void ExternalStylusInputMapper::reset(nsecs_t when) { - InputDevice* device = getDevice(); - mSingleTouchMotionAccumulator.reset(device); - mTouchButtonAccumulator.reset(device); - InputMapper::reset(when); -} - -void ExternalStylusInputMapper::process(const RawEvent* rawEvent) { - mSingleTouchMotionAccumulator.process(rawEvent); - mTouchButtonAccumulator.process(rawEvent); - - if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - sync(rawEvent->when); - } -} - -void ExternalStylusInputMapper::sync(nsecs_t when) { - mStylusState.clear(); - - mStylusState.when = when; - - mStylusState.toolType = mTouchButtonAccumulator.getToolType(); - if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { - mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; - } - - int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); - if (mRawPressureAxis.valid) { - mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue; - } else if (mTouchButtonAccumulator.isToolActive()) { - mStylusState.pressure = 1.0f; - } else { - mStylusState.pressure = 0.0f; - } - - mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); - - mContext->dispatchExternalStylusState(mStylusState); -} - - -// --- JoystickInputMapper --- - -JoystickInputMapper::JoystickInputMapper(InputDevice* device) : - InputMapper(device) { -} - -JoystickInputMapper::~JoystickInputMapper() { -} - -uint32_t JoystickInputMapper::getSources() { - return AINPUT_SOURCE_JOYSTICK; -} - -void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - for (size_t i = 0; i < mAxes.size(); i++) { - const Axis& axis = mAxes.valueAt(i); - addMotionRange(axis.axisInfo.axis, axis, info); - - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - addMotionRange(axis.axisInfo.highAxis, axis, info); - - } - } -} - -void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, - InputDeviceInfo* info) { - info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, - axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); - /* In order to ease the transition for developers from using the old axes - * to the newer, more semantically correct axes, we'll continue to register - * the old axes as duplicates of their corresponding new ones. */ - int32_t compatAxis = getCompatAxis(axisId); - if (compatAxis >= 0) { - info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, - axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); - } -} - -/* A mapping from axes the joystick actually has to the axes that should be - * artificially created for compatibility purposes. - * Returns -1 if no compatibility axis is needed. */ -int32_t JoystickInputMapper::getCompatAxis(int32_t axis) { - switch(axis) { - case AMOTION_EVENT_AXIS_LTRIGGER: - return AMOTION_EVENT_AXIS_BRAKE; - case AMOTION_EVENT_AXIS_RTRIGGER: - return AMOTION_EVENT_AXIS_GAS; - } - return -1; -} - -void JoystickInputMapper::dump(std::string& dump) { - dump += INDENT2 "Joystick Input Mapper:\n"; - - dump += INDENT3 "Axes:\n"; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - const char* label = getAxisLabel(axis.axisInfo.axis); - if (label) { - dump += StringPrintf(INDENT4 "%s", label); - } else { - dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis); - } - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - label = getAxisLabel(axis.axisInfo.highAxis); - if (label) { - dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue); - } else { - dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis, - axis.axisInfo.splitValue); - } - } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) { - dump += " (invert)"; - } - - dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n", - axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); - dump += StringPrintf(INDENT4 " scale=%0.5f, offset=%0.5f, " - "highScale=%0.5f, highOffset=%0.5f\n", - axis.scale, axis.offset, axis.highScale, axis.highOffset); - dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, " - "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n", - mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, - axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, axis.rawAxisInfo.resolution); - } -} - -void JoystickInputMapper::configure(nsecs_t when, - const InputReaderConfiguration* config, uint32_t changes) { - InputMapper::configure(when, config, changes); - - if (!changes) { // first time only - // Collect all axes. - for (int32_t abs = 0; abs <= ABS_MAX; abs++) { - if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) - & INPUT_DEVICE_CLASS_JOYSTICK)) { - continue; // axis must be claimed by a different device - } - - RawAbsoluteAxisInfo rawAxisInfo; - getAbsoluteAxisInfo(abs, &rawAxisInfo); - if (rawAxisInfo.valid) { - // Map axis. - AxisInfo axisInfo; - bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo); - if (!explicitlyMapped) { - // Axis is not explicitly mapped, will choose a generic axis later. - axisInfo.mode = AxisInfo::MODE_NORMAL; - axisInfo.axis = -1; - } - - // Apply flat override. - int32_t rawFlat = axisInfo.flatOverride < 0 - ? rawAxisInfo.flat : axisInfo.flatOverride; - - // Calculate scaling factors and limits. - Axis axis; - if (axisInfo.mode == AxisInfo::MODE_SPLIT) { - float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); - float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, - scale, 0.0f, highScale, 0.0f, - 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } else if (isCenteredAxis(axisInfo.axis)) { - float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, - scale, offset, scale, offset, - -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } else { - float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, - scale, 0.0f, scale, 0.0f, - 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, - rawAxisInfo.resolution * scale); - } - - // To eliminate noise while the joystick is at rest, filter out small variations - // in axis values up front. - axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f; - - mAxes.add(abs, axis); - } - } - - // If there are too many axes, start dropping them. - // Prefer to keep explicitly mapped axes. - if (mAxes.size() > PointerCoords::MAX_AXES) { - ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.", - getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES); - pruneAxes(true); - pruneAxes(false); - } - - // Assign generic axis ids to remaining axes. - int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - if (axis.axisInfo.axis < 0) { - while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 - && haveAxis(nextGenericAxisId)) { - nextGenericAxisId += 1; - } - - if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { - axis.axisInfo.axis = nextGenericAxisId; - nextGenericAxisId += 1; - } else { - ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " - "have already been assigned to other axes.", - getDeviceName().c_str(), mAxes.keyAt(i)); - mAxes.removeItemsAt(i--); - numAxes -= 1; - } - } - } - } -} - -bool JoystickInputMapper::haveAxis(int32_t axisId) { - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - if (axis.axisInfo.axis == axisId - || (axis.axisInfo.mode == AxisInfo::MODE_SPLIT - && axis.axisInfo.highAxis == axisId)) { - return true; - } - } - return false; -} - -void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { - size_t i = mAxes.size(); - while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) { - if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) { - continue; - } - ALOGI("Discarding joystick '%s' axis %d because there are too many axes.", - getDeviceName().c_str(), mAxes.keyAt(i)); - mAxes.removeItemsAt(i); - } -} - -bool JoystickInputMapper::isCenteredAxis(int32_t axis) { - switch (axis) { - case AMOTION_EVENT_AXIS_X: - case AMOTION_EVENT_AXIS_Y: - case AMOTION_EVENT_AXIS_Z: - case AMOTION_EVENT_AXIS_RX: - case AMOTION_EVENT_AXIS_RY: - case AMOTION_EVENT_AXIS_RZ: - case AMOTION_EVENT_AXIS_HAT_X: - case AMOTION_EVENT_AXIS_HAT_Y: - case AMOTION_EVENT_AXIS_ORIENTATION: - case AMOTION_EVENT_AXIS_RUDDER: - case AMOTION_EVENT_AXIS_WHEEL: - return true; - default: - return false; - } -} - -void JoystickInputMapper::reset(nsecs_t when) { - // Recenter all axes. - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - axis.resetValue(); - } - - InputMapper::reset(when); -} - -void JoystickInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_ABS: { - ssize_t index = mAxes.indexOfKey(rawEvent->code); - if (index >= 0) { - Axis& axis = mAxes.editValueAt(index); - float newValue, highNewValue; - switch (axis.axisInfo.mode) { - case AxisInfo::MODE_INVERT: - newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) - * axis.scale + axis.offset; - highNewValue = 0.0f; - break; - case AxisInfo::MODE_SPLIT: - if (rawEvent->value < axis.axisInfo.splitValue) { - newValue = (axis.axisInfo.splitValue - rawEvent->value) - * axis.scale + axis.offset; - highNewValue = 0.0f; - } else if (rawEvent->value > axis.axisInfo.splitValue) { - newValue = 0.0f; - highNewValue = (rawEvent->value - axis.axisInfo.splitValue) - * axis.highScale + axis.highOffset; - } else { - newValue = 0.0f; - highNewValue = 0.0f; - } - break; - default: - newValue = rawEvent->value * axis.scale + axis.offset; - highNewValue = 0.0f; - break; - } - axis.newValue = newValue; - axis.highNewValue = highNewValue; - } - break; - } - - case EV_SYN: - switch (rawEvent->code) { - case SYN_REPORT: - sync(rawEvent->when, false /*force*/); - break; - } - break; - } -} - -void JoystickInputMapper::sync(nsecs_t when, bool force) { - if (!filterAxes(force)) { - return; - } - - int32_t metaState = mContext->getGlobalMetaState(); - int32_t buttonState = 0; - - PointerProperties pointerProperties; - pointerProperties.clear(); - pointerProperties.id = 0; - pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - - PointerCoords pointerCoords; - pointerCoords.clear(); - - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - const Axis& axis = mAxes.valueAt(i); - setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue); - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis, - axis.highCurrentValue); - } - } - - // Moving a joystick axis should not wake the device because joysticks can - // be fairly noisy even when not in use. On the other hand, pushing a gamepad - // button will likely wake the device. - // TODO: Use the input device configuration to control this behavior more finely. - uint32_t policyFlags = 0; - - NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), - AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, - AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &pointerProperties, &pointerCoords, 0, 0, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&args); -} - -void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, - int32_t axis, float value) { - pointerCoords->setAxisValue(axis, value); - /* In order to ease the transition for developers from using the old axes - * to the newer, more semantically correct axes, we'll continue to produce - * values for the old axes as mirrors of the value of their corresponding - * new axes. */ - int32_t compatAxis = getCompatAxis(axis); - if (compatAxis >= 0) { - pointerCoords->setAxisValue(compatAxis, value); - } -} - -bool JoystickInputMapper::filterAxes(bool force) { - bool atLeastOneSignificantChange = force; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - if (force || hasValueChangedSignificantly(axis.filter, - axis.newValue, axis.currentValue, axis.min, axis.max)) { - axis.currentValue = axis.newValue; - atLeastOneSignificantChange = true; - } - if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - if (force || hasValueChangedSignificantly(axis.filter, - axis.highNewValue, axis.highCurrentValue, axis.min, axis.max)) { - axis.highCurrentValue = axis.highNewValue; - atLeastOneSignificantChange = true; - } - } - } - return atLeastOneSignificantChange; -} - -bool JoystickInputMapper::hasValueChangedSignificantly( - float filter, float newValue, float currentValue, float min, float max) { - if (newValue != currentValue) { - // Filter out small changes in value unless the value is converging on the axis - // bounds or center point. This is intended to reduce the amount of information - // sent to applications by particularly noisy joysticks (such as PS3). - if (fabs(newValue - currentValue) > filter - || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min) - || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max) - || hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) { - return true; - } - } - return false; -} - -bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange( - float filter, float newValue, float currentValue, float thresholdValue) { - float newDistance = fabs(newValue - thresholdValue); - if (newDistance < filter) { - float oldDistance = fabs(currentValue - thresholdValue); - if (newDistance < oldDistance) { - return true; - } - } - return false; -} - -} // namespace android diff --git a/services/inputflinger/InputReader.h b/services/inputflinger/InputReader.h deleted file mode 100644 index 9777779e7d..0000000000 --- a/services/inputflinger/InputReader.h +++ /dev/null @@ -1,1809 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UI_INPUT_READER_H -#define _UI_INPUT_READER_H - -#include "EventHub.h" -#include "PointerControllerInterface.h" -#include "InputListener.h" -#include "InputReaderBase.h" - -#include <input/DisplayViewport.h> -#include <input/Input.h> -#include <input/VelocityControl.h> -#include <input/VelocityTracker.h> -#include <ui/DisplayInfo.h> -#include <utils/KeyedVector.h> -#include <utils/Condition.h> -#include <utils/Mutex.h> -#include <utils/Timers.h> -#include <utils/BitSet.h> - -#include <optional> -#include <stddef.h> -#include <unistd.h> -#include <vector> - -namespace android { - -class InputDevice; -class InputMapper; - - -struct StylusState { - /* Time the stylus event was received. */ - nsecs_t when; - /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */ - float pressure; - /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */ - uint32_t buttons; - /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */ - int32_t toolType; - - void copyFrom(const StylusState& other) { - when = other.when; - pressure = other.pressure; - buttons = other.buttons; - toolType = other.toolType; - } - - void clear() { - when = LLONG_MAX; - pressure = 0.f; - buttons = 0; - toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; - } -}; - - -/* Internal interface used by individual input devices to access global input device state - * and parameters maintained by the input reader. - */ -class InputReaderContext { -public: - InputReaderContext() { } - virtual ~InputReaderContext() { } - - virtual void updateGlobalMetaState() = 0; - virtual int32_t getGlobalMetaState() = 0; - - virtual void disableVirtualKeysUntil(nsecs_t time) = 0; - virtual bool shouldDropVirtualKey(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode) = 0; - - virtual void fadePointer() = 0; - - virtual void requestTimeoutAtTime(nsecs_t when) = 0; - virtual int32_t bumpGeneration() = 0; - - virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) = 0; - virtual void dispatchExternalStylusState(const StylusState& outState) = 0; - - virtual InputReaderPolicyInterface* getPolicy() = 0; - virtual InputListenerInterface* getListener() = 0; - virtual EventHubInterface* getEventHub() = 0; - - virtual uint32_t getNextSequenceNum() = 0; -}; - - -/* The input reader reads raw event data from the event hub and processes it into input events - * that it sends to the input listener. Some functions of the input reader, such as early - * event filtering in low power states, are controlled by a separate policy object. - * - * The InputReader owns a collection of InputMappers. Most of the work it does happens - * on the input reader thread but the InputReader can receive queries from other system - * components running on arbitrary threads. To keep things manageable, the InputReader - * uses a single Mutex to guard its state. The Mutex may be held while calling into the - * EventHub or the InputReaderPolicy but it is never held while calling into the - * InputListener. - */ -class InputReader : public InputReaderInterface { -public: - InputReader(const sp<EventHubInterface>& eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener); - virtual ~InputReader(); - - virtual void dump(std::string& dump); - virtual void monitor(); - - virtual void loopOnce(); - - virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices); - - virtual bool isInputDeviceEnabled(int32_t deviceId); - - virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t scanCode); - virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t keyCode); - virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, - int32_t sw); - - virtual void toggleCapsLockState(int32_t deviceId); - - virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); - - virtual void requestRefreshConfiguration(uint32_t changes); - - virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, - ssize_t repeat, int32_t token); - virtual void cancelVibrate(int32_t deviceId, int32_t token); - - virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId); -protected: - // These members are protected so they can be instrumented by test cases. - virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber, - const InputDeviceIdentifier& identifier, uint32_t classes); - - class ContextImpl : public InputReaderContext { - InputReader* mReader; - - public: - explicit ContextImpl(InputReader* reader); - - virtual void updateGlobalMetaState(); - virtual int32_t getGlobalMetaState(); - virtual void disableVirtualKeysUntil(nsecs_t time); - virtual bool shouldDropVirtualKey(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode); - virtual void fadePointer(); - virtual void requestTimeoutAtTime(nsecs_t when); - virtual int32_t bumpGeneration(); - virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices); - virtual void dispatchExternalStylusState(const StylusState& outState); - virtual InputReaderPolicyInterface* getPolicy(); - virtual InputListenerInterface* getListener(); - virtual EventHubInterface* getEventHub(); - virtual uint32_t getNextSequenceNum(); - } mContext; - - friend class ContextImpl; - -private: - Mutex mLock; - - Condition mReaderIsAliveCondition; - - sp<EventHubInterface> mEventHub; - sp<InputReaderPolicyInterface> mPolicy; - sp<QueuedInputListener> mQueuedListener; - - InputReaderConfiguration mConfig; - - // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers - uint32_t mNextSequenceNum; - - // The event queue. - static const int EVENT_BUFFER_SIZE = 256; - RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; - - KeyedVector<int32_t, InputDevice*> mDevices; - - // low-level input event decoding and device management - void processEventsLocked(const RawEvent* rawEvents, size_t count); - - void addDeviceLocked(nsecs_t when, int32_t deviceId); - void removeDeviceLocked(nsecs_t when, int32_t deviceId); - void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count); - void timeoutExpiredLocked(nsecs_t when); - - void handleConfigurationChangedLocked(nsecs_t when); - - int32_t mGlobalMetaState; - void updateGlobalMetaStateLocked(); - int32_t getGlobalMetaStateLocked(); - - void notifyExternalStylusPresenceChanged(); - void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices); - void dispatchExternalStylusState(const StylusState& state); - - void fadePointerLocked(); - - int32_t mGeneration; - int32_t bumpGenerationLocked(); - - void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices); - - nsecs_t mDisableVirtualKeysTimeout; - void disableVirtualKeysUntilLocked(nsecs_t time); - bool shouldDropVirtualKeyLocked(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode); - - nsecs_t mNextTimeout; - void requestTimeoutAtTimeLocked(nsecs_t when); - - uint32_t mConfigurationChangesToRefresh; - void refreshConfigurationLocked(uint32_t changes); - - // 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, - GetStateFunc getStateFunc); - bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); -}; - - -/* Represents the state of a single input device. */ -class InputDevice { -public: - InputDevice(InputReaderContext* context, int32_t id, int32_t generation, int32_t - controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes); - ~InputDevice(); - - inline InputReaderContext* getContext() { return mContext; } - inline int32_t getId() const { return mId; } - inline int32_t getControllerNumber() const { return mControllerNumber; } - inline int32_t getGeneration() const { return mGeneration; } - inline const std::string getName() const { return mIdentifier.name; } - inline const std::string getDescriptor() { return mIdentifier.descriptor; } - inline uint32_t getClasses() const { return mClasses; } - inline uint32_t getSources() const { return mSources; } - - inline bool isExternal() { return mIsExternal; } - inline void setExternal(bool external) { mIsExternal = external; } - inline std::optional<uint8_t> getAssociatedDisplayPort() const { - return mAssociatedDisplayPort; - } - - inline void setMic(bool hasMic) { mHasMic = hasMic; } - inline bool hasMic() const { return mHasMic; } - - inline bool isIgnored() { return mMappers.empty(); } - - bool isEnabled(); - void setEnabled(bool enabled, nsecs_t when); - - void dump(std::string& dump); - void addMapper(InputMapper* mapper); - void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - void reset(nsecs_t when); - void process(const RawEvent* rawEvents, size_t count); - void timeoutExpired(nsecs_t when); - void updateExternalStylusState(const StylusState& state); - - void getDeviceInfo(InputDeviceInfo* outDeviceInfo); - int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); - void cancelVibrate(int32_t token); - void cancelTouch(nsecs_t when); - - int32_t getMetaState(); - void updateMetaState(int32_t keyCode); - - void fadePointer(); - - void bumpGeneration(); - - void notifyReset(nsecs_t when); - - inline const PropertyMap& getConfiguration() { return mConfiguration; } - inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } - - bool hasKey(int32_t code) { - return getEventHub()->hasScanCode(mId, code); - } - - bool hasAbsoluteAxis(int32_t code) { - RawAbsoluteAxisInfo info; - getEventHub()->getAbsoluteAxisInfo(mId, code, &info); - return info.valid; - } - - bool isKeyPressed(int32_t code) { - return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN; - } - - int32_t getAbsoluteAxisValue(int32_t code) { - int32_t value; - getEventHub()->getAbsoluteAxisValue(mId, code, &value); - return value; - } - - std::optional<int32_t> getAssociatedDisplay(); -private: - InputReaderContext* mContext; - int32_t mId; - int32_t mGeneration; - int32_t mControllerNumber; - InputDeviceIdentifier mIdentifier; - std::string mAlias; - uint32_t mClasses; - - std::vector<InputMapper*> mMappers; - - uint32_t mSources; - bool mIsExternal; - std::optional<uint8_t> mAssociatedDisplayPort; - bool mHasMic; - bool mDropUntilNextSync; - - typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); - int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); - - PropertyMap mConfiguration; -}; - - -/* Keeps track of the state of mouse or touch pad buttons. */ -class CursorButtonAccumulator { -public: - CursorButtonAccumulator(); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - - uint32_t getButtonState() const; - -private: - bool mBtnLeft; - bool mBtnRight; - bool mBtnMiddle; - bool mBtnBack; - bool mBtnSide; - bool mBtnForward; - bool mBtnExtra; - bool mBtnTask; - - void clearButtons(); -}; - - -/* Keeps track of cursor movements. */ - -class CursorMotionAccumulator { -public: - CursorMotionAccumulator(); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - void finishSync(); - - inline int32_t getRelativeX() const { return mRelX; } - inline int32_t getRelativeY() const { return mRelY; } - -private: - int32_t mRelX; - int32_t mRelY; - - void clearRelativeAxes(); -}; - - -/* Keeps track of cursor scrolling motions. */ - -class CursorScrollAccumulator { -public: - CursorScrollAccumulator(); - void configure(InputDevice* device); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - void finishSync(); - - inline bool haveRelativeVWheel() const { return mHaveRelWheel; } - inline bool haveRelativeHWheel() const { return mHaveRelHWheel; } - - inline int32_t getRelativeX() const { return mRelX; } - inline int32_t getRelativeY() const { return mRelY; } - inline int32_t getRelativeVWheel() const { return mRelWheel; } - inline int32_t getRelativeHWheel() const { return mRelHWheel; } - -private: - bool mHaveRelWheel; - bool mHaveRelHWheel; - - int32_t mRelX; - int32_t mRelY; - int32_t mRelWheel; - int32_t mRelHWheel; - - void clearRelativeAxes(); -}; - - -/* Keeps track of the state of touch, stylus and tool buttons. */ -class TouchButtonAccumulator { -public: - TouchButtonAccumulator(); - void configure(InputDevice* device); - void reset(InputDevice* device); - - void process(const RawEvent* rawEvent); - - uint32_t getButtonState() const; - int32_t getToolType() const; - bool isToolActive() const; - bool isHovering() const; - bool hasStylus() const; - -private: - bool mHaveBtnTouch; - bool mHaveStylus; - - bool mBtnTouch; - bool mBtnStylus; - bool mBtnStylus2; - bool mBtnToolFinger; - bool mBtnToolPen; - bool mBtnToolRubber; - bool mBtnToolBrush; - bool mBtnToolPencil; - bool mBtnToolAirbrush; - bool mBtnToolMouse; - bool mBtnToolLens; - bool mBtnToolDoubleTap; - bool mBtnToolTripleTap; - bool mBtnToolQuadTap; - - void clearButtons(); -}; - - -/* Raw axis information from the driver. */ -struct RawPointerAxes { - RawAbsoluteAxisInfo x; - RawAbsoluteAxisInfo y; - RawAbsoluteAxisInfo pressure; - RawAbsoluteAxisInfo touchMajor; - RawAbsoluteAxisInfo touchMinor; - RawAbsoluteAxisInfo toolMajor; - RawAbsoluteAxisInfo toolMinor; - RawAbsoluteAxisInfo orientation; - RawAbsoluteAxisInfo distance; - RawAbsoluteAxisInfo tiltX; - RawAbsoluteAxisInfo tiltY; - RawAbsoluteAxisInfo trackingId; - RawAbsoluteAxisInfo slot; - - RawPointerAxes(); - inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; } - inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; } - void clear(); -}; - - -/* Raw data for a collection of pointers including a pointer id mapping table. */ -struct RawPointerData { - struct Pointer { - uint32_t id; - int32_t x; - int32_t y; - int32_t pressure; - int32_t touchMajor; - int32_t touchMinor; - int32_t toolMajor; - int32_t toolMinor; - int32_t orientation; - int32_t distance; - int32_t tiltX; - int32_t tiltY; - int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant - bool isHovering; - }; - - uint32_t pointerCount; - Pointer pointers[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; - - RawPointerData(); - void clear(); - void copyFrom(const RawPointerData& other); - void getCentroidOfTouchingPointers(float* outX, float* outY) const; - - inline void markIdBit(uint32_t id, bool isHovering) { - if (isHovering) { - hoveringIdBits.markBit(id); - } else { - touchingIdBits.markBit(id); - } - } - - inline void clearIdBits() { - hoveringIdBits.clear(); - touchingIdBits.clear(); - } - - inline const Pointer& pointerForId(uint32_t id) const { - return pointers[idToIndex[id]]; - } - - inline bool isHovering(uint32_t pointerIndex) { - return pointers[pointerIndex].isHovering; - } -}; - - -/* Cooked data for a collection of pointers including a pointer id mapping table. */ -struct CookedPointerData { - uint32_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - BitSet32 hoveringIdBits, touchingIdBits; - uint32_t idToIndex[MAX_POINTER_ID + 1]; - - CookedPointerData(); - void clear(); - void copyFrom(const CookedPointerData& other); - - inline const PointerCoords& pointerCoordsForId(uint32_t id) const { - return pointerCoords[idToIndex[id]]; - } - - inline PointerCoords& editPointerCoordsWithId(uint32_t id) { - return pointerCoords[idToIndex[id]]; - } - - inline PointerProperties& editPointerPropertiesWithId(uint32_t id) { - return pointerProperties[idToIndex[id]]; - } - - inline bool isHovering(uint32_t pointerIndex) const { - return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id); - } - - inline bool isTouching(uint32_t pointerIndex) const { - return touchingIdBits.hasBit(pointerProperties[pointerIndex].id); - } -}; - -/** - * Basic statistics information. - * Keep track of min, max, average, and standard deviation of the received samples. - * Used to report latency information about input events. - */ -struct LatencyStatistics { - float min; - float max; - // Sum of all samples - float sum; - // Sum of squares of all samples - float sum2; - // The number of samples - size_t count; - // The last time statistics were reported. - nsecs_t lastReportTime; - - LatencyStatistics() { - reset(systemTime(SYSTEM_TIME_MONOTONIC)); - } - - inline void addValue(float x) { - if (x < min) { - min = x; - } - if (x > max) { - max = x; - } - sum += x; - sum2 += x * x; - count++; - } - - // Get the average value. Should not be called if no samples have been added. - inline float mean() { - if (count == 0) { - return 0; - } - return sum / count; - } - - // Get the standard deviation. Should not be called if no samples have been added. - inline float stdev() { - if (count == 0) { - return 0; - } - float average = mean(); - return sqrt(sum2 / count - average * average); - } - - /** - * Reset internal state. The variable 'when' is the time when the data collection started. - * Call this to start a new data collection window. - */ - inline void reset(nsecs_t when) { - max = 0; - min = std::numeric_limits<float>::max(); - sum = 0; - sum2 = 0; - count = 0; - lastReportTime = when; - } -}; - -/* Keeps track of the state of single-touch protocol. */ -class SingleTouchMotionAccumulator { -public: - SingleTouchMotionAccumulator(); - - void process(const RawEvent* rawEvent); - void reset(InputDevice* device); - - inline int32_t getAbsoluteX() const { return mAbsX; } - inline int32_t getAbsoluteY() const { return mAbsY; } - inline int32_t getAbsolutePressure() const { return mAbsPressure; } - inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; } - inline int32_t getAbsoluteDistance() const { return mAbsDistance; } - inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; } - inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; } - -private: - int32_t mAbsX; - int32_t mAbsY; - int32_t mAbsPressure; - int32_t mAbsToolWidth; - int32_t mAbsDistance; - int32_t mAbsTiltX; - int32_t mAbsTiltY; - - void clearAbsoluteAxes(); -}; - - -/* Keeps track of the state of multi-touch protocol. */ -class MultiTouchMotionAccumulator { -public: - class Slot { - public: - inline bool isInUse() const { return mInUse; } - inline int32_t getX() const { return mAbsMTPositionX; } - inline int32_t getY() const { return mAbsMTPositionY; } - inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } - inline int32_t getTouchMinor() const { - return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; } - inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } - inline int32_t getToolMinor() const { - return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; } - inline int32_t getOrientation() const { return mAbsMTOrientation; } - inline int32_t getTrackingId() const { return mAbsMTTrackingId; } - inline int32_t getPressure() const { return mAbsMTPressure; } - inline int32_t getDistance() const { return mAbsMTDistance; } - inline int32_t getToolType() const; - - private: - friend class MultiTouchMotionAccumulator; - - bool mInUse; - bool mHaveAbsMTTouchMinor; - bool mHaveAbsMTWidthMinor; - bool mHaveAbsMTToolType; - - int32_t mAbsMTPositionX; - int32_t mAbsMTPositionY; - int32_t mAbsMTTouchMajor; - int32_t mAbsMTTouchMinor; - int32_t mAbsMTWidthMajor; - int32_t mAbsMTWidthMinor; - int32_t mAbsMTOrientation; - int32_t mAbsMTTrackingId; - int32_t mAbsMTPressure; - int32_t mAbsMTDistance; - int32_t mAbsMTToolType; - - Slot(); - void clear(); - }; - - MultiTouchMotionAccumulator(); - ~MultiTouchMotionAccumulator(); - - void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol); - void reset(InputDevice* device); - void process(const RawEvent* rawEvent); - void finishSync(); - bool hasStylus() const; - - inline size_t getSlotCount() const { return mSlotCount; } - inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } - inline uint32_t getDeviceTimestamp() const { return mDeviceTimestamp; } - -private: - int32_t mCurrentSlot; - Slot* mSlots; - size_t mSlotCount; - bool mUsingSlotsProtocol; - bool mHaveStylus; - uint32_t mDeviceTimestamp; - - void clearSlots(int32_t initialSlot); -}; - - -/* An input mapper transforms raw input events into cooked event data. - * A single input device can have multiple associated input mappers in order to interpret - * different classes of events. - * - * InputMapper lifecycle: - * - create - * - configure with 0 changes - * - reset - * - process, process, process (may occasionally reconfigure with non-zero changes or reset) - * - reset - * - destroy - */ -class InputMapper { -public: - explicit InputMapper(InputDevice* device); - virtual ~InputMapper(); - - inline InputDevice* getDevice() { return mDevice; } - inline int32_t getDeviceId() { return mDevice->getId(); } - inline const std::string getDeviceName() { return mDevice->getName(); } - inline InputReaderContext* getContext() { return mContext; } - inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } - inline InputListenerInterface* getListener() { return mContext->getListener(); } - inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } - - virtual uint32_t getSources() = 0; - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent) = 0; - virtual void timeoutExpired(nsecs_t when); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token); - virtual void cancelVibrate(int32_t token); - virtual void cancelTouch(nsecs_t when); - - virtual int32_t getMetaState(); - virtual void updateMetaState(int32_t keyCode); - - virtual void updateExternalStylusState(const StylusState& state); - - virtual void fadePointer(); - virtual std::optional<int32_t> getAssociatedDisplay() { - return std::nullopt; - } -protected: - InputDevice* mDevice; - InputReaderContext* mContext; - - status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); - void bumpGeneration(); - - static void dumpRawAbsoluteAxisInfo(std::string& dump, - const RawAbsoluteAxisInfo& axis, const char* name); - static void dumpStylusState(std::string& dump, const StylusState& state); -}; - - -class SwitchInputMapper : public InputMapper { -public: - explicit SwitchInputMapper(InputDevice* device); - virtual ~SwitchInputMapper(); - - virtual uint32_t getSources(); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - virtual void dump(std::string& dump); - -private: - uint32_t mSwitchValues; - uint32_t mUpdatedSwitchMask; - - void processSwitch(int32_t switchCode, int32_t switchValue); - void sync(nsecs_t when); -}; - - -class VibratorInputMapper : public InputMapper { -public: - explicit VibratorInputMapper(InputDevice* device); - virtual ~VibratorInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void process(const RawEvent* rawEvent); - - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, - int32_t token); - virtual void cancelVibrate(int32_t token); - virtual void timeoutExpired(nsecs_t when); - virtual void dump(std::string& dump); - -private: - bool mVibrating; - nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE]; - size_t mPatternSize; - ssize_t mRepeat; - int32_t mToken; - ssize_t mIndex; - nsecs_t mNextStepTime; - - void nextStep(); - void stopVibrating(); -}; - - -class KeyboardInputMapper : public InputMapper { -public: - KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType); - virtual ~KeyboardInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - - virtual int32_t getMetaState(); - virtual void updateMetaState(int32_t keyCode); - -private: - // The current viewport. - std::optional<DisplayViewport> mViewport; - - struct KeyDown { - int32_t keyCode; - int32_t scanCode; - }; - - uint32_t mSource; - int32_t mKeyboardType; - - std::vector<KeyDown> mKeyDowns; // keys that are down - int32_t mMetaState; - nsecs_t mDownTime; // time of most recent key down - - int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none - - struct LedState { - bool avail; // led is available - bool on; // we think the led is currently on - }; - LedState mCapsLockLedState; - LedState mNumLockLedState; - LedState mScrollLockLedState; - - // Immutable configuration parameters. - struct Parameters { - bool orientationAware; - bool handlesKeyRepeat; - } mParameters; - - void configureParameters(); - void dumpParameters(std::string& dump); - - int32_t getOrientation(); - int32_t getDisplayId(); - - bool isKeyboardOrGamepadKey(int32_t scanCode); - bool isMediaKey(int32_t keyCode); - - void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode); - - bool updateMetaStateIfNeeded(int32_t keyCode, bool down); - - ssize_t findKeyDown(int32_t scanCode); - - void resetLedState(); - void initializeLedState(LedState& ledState, int32_t led); - void updateLedState(bool reset); - void updateLedStateForModifier(LedState& ledState, int32_t led, - int32_t modifier, bool reset); -}; - - -class CursorInputMapper : public InputMapper { -public: - explicit CursorInputMapper(InputDevice* device); - virtual ~CursorInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - - virtual void fadePointer(); - - virtual std::optional<int32_t> getAssociatedDisplay(); -private: - // Amount that trackball needs to move in order to generate a key event. - static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; - - // Immutable configuration parameters. - struct Parameters { - enum Mode { - MODE_POINTER, - MODE_POINTER_RELATIVE, - MODE_NAVIGATION, - }; - - Mode mode; - bool hasAssociatedDisplay; - bool orientationAware; - } mParameters; - - CursorButtonAccumulator mCursorButtonAccumulator; - CursorMotionAccumulator mCursorMotionAccumulator; - CursorScrollAccumulator mCursorScrollAccumulator; - - int32_t mSource; - float mXScale; - float mYScale; - float mXPrecision; - float mYPrecision; - - float mVWheelScale; - float mHWheelScale; - - // Velocity controls for mouse pointer and wheel movements. - // The controls for X and Y wheel movements are separate to keep them decoupled. - VelocityControl mPointerVelocityControl; - VelocityControl mWheelXVelocityControl; - VelocityControl mWheelYVelocityControl; - - int32_t mOrientation; - - sp<PointerControllerInterface> mPointerController; - - int32_t mButtonState; - nsecs_t mDownTime; - - void configureParameters(); - void dumpParameters(std::string& dump); - - void sync(nsecs_t when); -}; - - -class RotaryEncoderInputMapper : public InputMapper { -public: - explicit RotaryEncoderInputMapper(InputDevice* device); - virtual ~RotaryEncoderInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -private: - CursorScrollAccumulator mRotaryEncoderScrollAccumulator; - - int32_t mSource; - float mScalingFactor; - int32_t mOrientation; - - void sync(nsecs_t when); -}; - -class TouchInputMapper : public InputMapper { -public: - explicit TouchInputMapper(InputDevice* device); - virtual ~TouchInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); - - virtual void fadePointer(); - virtual void cancelTouch(nsecs_t when); - virtual void timeoutExpired(nsecs_t when); - virtual void updateExternalStylusState(const StylusState& state); - virtual std::optional<int32_t> getAssociatedDisplay(); -protected: - CursorButtonAccumulator mCursorButtonAccumulator; - CursorScrollAccumulator mCursorScrollAccumulator; - TouchButtonAccumulator mTouchButtonAccumulator; - - struct VirtualKey { - int32_t keyCode; - int32_t scanCode; - uint32_t flags; - - // computed hit box, specified in touch screen coords based on known display size - int32_t hitLeft; - int32_t hitTop; - int32_t hitRight; - int32_t hitBottom; - - inline bool isHit(int32_t x, int32_t y) const { - return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom; - } - }; - - // Input sources and device mode. - uint32_t mSource; - - enum DeviceMode { - DEVICE_MODE_DISABLED, // input is disabled - DEVICE_MODE_DIRECT, // direct mapping (touchscreen) - DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad) - DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation) - DEVICE_MODE_POINTER, // pointer mapping (pointer) - }; - DeviceMode mDeviceMode; - - // The reader's configuration. - InputReaderConfiguration mConfig; - - // Immutable configuration parameters. - struct Parameters { - enum DeviceType { - DEVICE_TYPE_TOUCH_SCREEN, - DEVICE_TYPE_TOUCH_PAD, - DEVICE_TYPE_TOUCH_NAVIGATION, - DEVICE_TYPE_POINTER, - }; - - DeviceType deviceType; - bool hasAssociatedDisplay; - bool associatedDisplayIsExternal; - bool orientationAware; - bool hasButtonUnderPad; - std::string uniqueDisplayId; - - enum GestureMode { - GESTURE_MODE_SINGLE_TOUCH, - GESTURE_MODE_MULTI_TOUCH, - }; - GestureMode gestureMode; - - bool wake; - } mParameters; - - // Immutable calibration parameters in parsed form. - struct Calibration { - // Size - enum SizeCalibration { - SIZE_CALIBRATION_DEFAULT, - SIZE_CALIBRATION_NONE, - SIZE_CALIBRATION_GEOMETRIC, - SIZE_CALIBRATION_DIAMETER, - SIZE_CALIBRATION_BOX, - SIZE_CALIBRATION_AREA, - }; - - SizeCalibration sizeCalibration; - - bool haveSizeScale; - float sizeScale; - bool haveSizeBias; - float sizeBias; - bool haveSizeIsSummed; - bool sizeIsSummed; - - // Pressure - enum PressureCalibration { - PRESSURE_CALIBRATION_DEFAULT, - PRESSURE_CALIBRATION_NONE, - PRESSURE_CALIBRATION_PHYSICAL, - PRESSURE_CALIBRATION_AMPLITUDE, - }; - - PressureCalibration pressureCalibration; - bool havePressureScale; - float pressureScale; - - // Orientation - enum OrientationCalibration { - ORIENTATION_CALIBRATION_DEFAULT, - ORIENTATION_CALIBRATION_NONE, - ORIENTATION_CALIBRATION_INTERPOLATED, - ORIENTATION_CALIBRATION_VECTOR, - }; - - OrientationCalibration orientationCalibration; - - // Distance - enum DistanceCalibration { - DISTANCE_CALIBRATION_DEFAULT, - DISTANCE_CALIBRATION_NONE, - DISTANCE_CALIBRATION_SCALED, - }; - - DistanceCalibration distanceCalibration; - bool haveDistanceScale; - float distanceScale; - - enum CoverageCalibration { - COVERAGE_CALIBRATION_DEFAULT, - COVERAGE_CALIBRATION_NONE, - COVERAGE_CALIBRATION_BOX, - }; - - CoverageCalibration coverageCalibration; - - inline void applySizeScaleAndBias(float* outSize) const { - if (haveSizeScale) { - *outSize *= sizeScale; - } - if (haveSizeBias) { - *outSize += sizeBias; - } - if (*outSize < 0) { - *outSize = 0; - } - } - } mCalibration; - - // Affine location transformation/calibration - struct TouchAffineTransformation mAffineTransform; - - RawPointerAxes mRawPointerAxes; - - struct RawState { - nsecs_t when; - uint32_t deviceTimestamp; - - // Raw pointer sample data. - RawPointerData rawPointerData; - - int32_t buttonState; - - // Scroll state. - int32_t rawVScroll; - int32_t rawHScroll; - - void copyFrom(const RawState& other) { - when = other.when; - deviceTimestamp = other.deviceTimestamp; - rawPointerData.copyFrom(other.rawPointerData); - buttonState = other.buttonState; - rawVScroll = other.rawVScroll; - rawHScroll = other.rawHScroll; - } - - void clear() { - when = 0; - deviceTimestamp = 0; - rawPointerData.clear(); - buttonState = 0; - rawVScroll = 0; - rawHScroll = 0; - } - }; - - struct CookedState { - uint32_t deviceTimestamp; - // Cooked pointer sample data. - CookedPointerData cookedPointerData; - - // Id bits used to differentiate fingers, stylus and mouse tools. - BitSet32 fingerIdBits; - BitSet32 stylusIdBits; - BitSet32 mouseIdBits; - - int32_t buttonState; - - void copyFrom(const CookedState& other) { - deviceTimestamp = other.deviceTimestamp; - cookedPointerData.copyFrom(other.cookedPointerData); - fingerIdBits = other.fingerIdBits; - stylusIdBits = other.stylusIdBits; - mouseIdBits = other.mouseIdBits; - buttonState = other.buttonState; - } - - void clear() { - deviceTimestamp = 0; - cookedPointerData.clear(); - fingerIdBits.clear(); - stylusIdBits.clear(); - mouseIdBits.clear(); - buttonState = 0; - } - }; - - std::vector<RawState> mRawStatesPending; - RawState mCurrentRawState; - CookedState mCurrentCookedState; - RawState mLastRawState; - CookedState mLastCookedState; - - // State provided by an external stylus - StylusState mExternalStylusState; - int64_t mExternalStylusId; - nsecs_t mExternalStylusFusionTimeout; - bool mExternalStylusDataPending; - - // True if we sent a HOVER_ENTER event. - bool mSentHoverEnter; - - // Have we assigned pointer IDs for this stream - bool mHavePointerIds; - - // Is the current stream of direct touch events aborted - bool mCurrentMotionAborted; - - // The time the primary pointer last went down. - nsecs_t mDownTime; - - // The pointer controller, or null if the device is not a pointer. - sp<PointerControllerInterface> mPointerController; - - std::vector<VirtualKey> mVirtualKeys; - - virtual void configureParameters(); - virtual void dumpParameters(std::string& dump); - virtual void configureRawPointerAxes(); - virtual void dumpRawPointerAxes(std::string& dump); - virtual void configureSurface(nsecs_t when, bool* outResetNeeded); - virtual void dumpSurface(std::string& dump); - virtual void configureVirtualKeys(); - virtual void dumpVirtualKeys(std::string& dump); - virtual void parseCalibration(); - virtual void resolveCalibration(); - virtual void dumpCalibration(std::string& dump); - virtual void updateAffineTransformation(); - virtual void dumpAffineTransformation(std::string& dump); - virtual void resolveExternalStylusPresence(); - virtual bool hasStylus() const = 0; - virtual bool hasExternalStylus() const; - - virtual void syncTouch(nsecs_t when, RawState* outState) = 0; - -private: - // The current viewport. - // The components of the viewport are specified in the display's rotated orientation. - DisplayViewport mViewport; - - // The surface orientation, width and height set by configureSurface(). - // The width and height are derived from the viewport but are specified - // in the natural orientation. - // The surface origin specifies how the surface coordinates should be translated - // to align with the logical display coordinate space. - int32_t mSurfaceWidth; - int32_t mSurfaceHeight; - int32_t mSurfaceLeft; - int32_t mSurfaceTop; - - // Similar to the surface coordinates, but in the raw display coordinate space rather than in - // the logical coordinate space. - int32_t mPhysicalWidth; - int32_t mPhysicalHeight; - int32_t mPhysicalLeft; - int32_t mPhysicalTop; - - // The orientation may be different from the viewport orientation as it specifies - // the rotation of the surface coordinates required to produce the viewport's - // requested orientation, so it will depend on whether the device is orientation aware. - int32_t mSurfaceOrientation; - - // Translation and scaling factors, orientation-independent. - float mXTranslate; - float mXScale; - float mXPrecision; - - float mYTranslate; - float mYScale; - float mYPrecision; - - float mGeometricScale; - - float mPressureScale; - - float mSizeScale; - - float mOrientationScale; - - float mDistanceScale; - - bool mHaveTilt; - float mTiltXCenter; - float mTiltXScale; - float mTiltYCenter; - float mTiltYScale; - - bool mExternalStylusConnected; - - // Oriented motion ranges for input device info. - struct OrientedRanges { - InputDeviceInfo::MotionRange x; - InputDeviceInfo::MotionRange y; - InputDeviceInfo::MotionRange pressure; - - bool haveSize; - InputDeviceInfo::MotionRange size; - - bool haveTouchSize; - InputDeviceInfo::MotionRange touchMajor; - InputDeviceInfo::MotionRange touchMinor; - - bool haveToolSize; - InputDeviceInfo::MotionRange toolMajor; - InputDeviceInfo::MotionRange toolMinor; - - bool haveOrientation; - InputDeviceInfo::MotionRange orientation; - - bool haveDistance; - InputDeviceInfo::MotionRange distance; - - bool haveTilt; - InputDeviceInfo::MotionRange tilt; - - OrientedRanges() { - clear(); - } - - void clear() { - haveSize = false; - haveTouchSize = false; - haveToolSize = false; - haveOrientation = false; - haveDistance = false; - haveTilt = false; - } - } mOrientedRanges; - - // Oriented dimensions and precision. - float mOrientedXPrecision; - float mOrientedYPrecision; - - struct CurrentVirtualKeyState { - bool down; - bool ignored; - nsecs_t downTime; - int32_t keyCode; - int32_t scanCode; - } mCurrentVirtualKey; - - // Scale factor for gesture or mouse based pointer movements. - float mPointerXMovementScale; - float mPointerYMovementScale; - - // Scale factor for gesture based zooming and other freeform motions. - float mPointerXZoomScale; - float mPointerYZoomScale; - - // The maximum swipe width. - float mPointerGestureMaxSwipeWidth; - - struct PointerDistanceHeapElement { - uint32_t currentPointerIndex : 8; - uint32_t lastPointerIndex : 8; - uint64_t distance : 48; // squared distance - }; - - enum PointerUsage { - POINTER_USAGE_NONE, - POINTER_USAGE_GESTURES, - POINTER_USAGE_STYLUS, - POINTER_USAGE_MOUSE, - }; - PointerUsage mPointerUsage; - - struct PointerGesture { - enum Mode { - // No fingers, button is not pressed. - // Nothing happening. - NEUTRAL, - - // No fingers, button is not pressed. - // Tap detected. - // Emits DOWN and UP events at the pointer location. - TAP, - - // Exactly one finger dragging following a tap. - // Pointer follows the active finger. - // Emits DOWN, MOVE and UP events at the pointer location. - // - // Detect double-taps when the finger goes up while in TAP_DRAG mode. - TAP_DRAG, - - // Button is pressed. - // Pointer follows the active finger if there is one. Other fingers are ignored. - // Emits DOWN, MOVE and UP events at the pointer location. - BUTTON_CLICK_OR_DRAG, - - // Exactly one finger, button is not pressed. - // Pointer follows the active finger. - // Emits HOVER_MOVE events at the pointer location. - // - // Detect taps when the finger goes up while in HOVER mode. - HOVER, - - // Exactly two fingers but neither have moved enough to clearly indicate - // whether a swipe or freeform gesture was intended. We consider the - // pointer to be pressed so this enables clicking or long-pressing on buttons. - // Pointer does not move. - // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate. - PRESS, - - // Exactly two fingers moving in the same direction, button is not pressed. - // Pointer does not move. - // Emits DOWN, MOVE and UP events with a single pointer coordinate that - // follows the midpoint between both fingers. - SWIPE, - - // Two or more fingers moving in arbitrary directions, button is not pressed. - // Pointer does not move. - // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow - // each finger individually relative to the initial centroid of the finger. - FREEFORM, - - // Waiting for quiet time to end before starting the next gesture. - QUIET, - }; - - // Time the first finger went down. - nsecs_t firstTouchTime; - - // The active pointer id from the raw touch data. - int32_t activeTouchId; // -1 if none - - // The active pointer id from the gesture last delivered to the application. - int32_t activeGestureId; // -1 if none - - // Pointer coords and ids for the current and previous pointer gesture. - Mode currentGestureMode; - BitSet32 currentGestureIdBits; - uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties currentGestureProperties[MAX_POINTERS]; - PointerCoords currentGestureCoords[MAX_POINTERS]; - - Mode lastGestureMode; - BitSet32 lastGestureIdBits; - uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; - PointerProperties lastGestureProperties[MAX_POINTERS]; - PointerCoords lastGestureCoords[MAX_POINTERS]; - - // Time the pointer gesture last went down. - nsecs_t downTime; - - // Time when the pointer went down for a TAP. - nsecs_t tapDownTime; - - // Time when the pointer went up for a TAP. - nsecs_t tapUpTime; - - // Location of initial tap. - float tapX, tapY; - - // Time we started waiting for quiescence. - nsecs_t quietTime; - - // Reference points for multitouch gestures. - float referenceTouchX; // reference touch X/Y coordinates in surface units - float referenceTouchY; - float referenceGestureX; // reference gesture X/Y coordinates in pixels - float referenceGestureY; - - // Distance that each pointer has traveled which has not yet been - // subsumed into the reference gesture position. - BitSet32 referenceIdBits; - struct Delta { - float dx, dy; - }; - Delta referenceDeltas[MAX_POINTER_ID + 1]; - - // Describes how touch ids are mapped to gesture ids for freeform gestures. - uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; - - // A velocity tracker for determining whether to switch active pointers during drags. - VelocityTracker velocityTracker; - - void reset() { - firstTouchTime = LLONG_MIN; - activeTouchId = -1; - activeGestureId = -1; - currentGestureMode = NEUTRAL; - currentGestureIdBits.clear(); - lastGestureMode = NEUTRAL; - lastGestureIdBits.clear(); - downTime = 0; - velocityTracker.clear(); - resetTap(); - resetQuietTime(); - } - - void resetTap() { - tapDownTime = LLONG_MIN; - tapUpTime = LLONG_MIN; - } - - void resetQuietTime() { - quietTime = LLONG_MIN; - } - } mPointerGesture; - - struct PointerSimple { - PointerCoords currentCoords; - PointerProperties currentProperties; - PointerCoords lastCoords; - PointerProperties lastProperties; - - // True if the pointer is down. - bool down; - - // True if the pointer is hovering. - bool hovering; - - // Time the pointer last went down. - nsecs_t downTime; - - void reset() { - currentCoords.clear(); - currentProperties.clear(); - lastCoords.clear(); - lastProperties.clear(); - down = false; - hovering = false; - downTime = 0; - } - } mPointerSimple; - - // The pointer and scroll velocity controls. - VelocityControl mPointerVelocityControl; - VelocityControl mWheelXVelocityControl; - VelocityControl mWheelYVelocityControl; - - // Latency statistics for touch events - struct LatencyStatistics mStatistics; - - std::optional<DisplayViewport> findViewport(); - - void resetExternalStylus(); - void clearStylusDataPendingFlags(); - - void sync(nsecs_t when); - - bool consumeRawTouches(nsecs_t when, uint32_t policyFlags); - void processRawTouches(bool timeout); - void cookAndDispatch(nsecs_t when); - void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, - int32_t keyEventAction, int32_t keyEventFlags); - - void dispatchTouches(nsecs_t when, uint32_t policyFlags); - void dispatchHoverExit(nsecs_t when, uint32_t policyFlags); - void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags); - void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags); - void dispatchButtonPress(nsecs_t when, uint32_t policyFlags); - const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData); - void cookPointerData(); - void abortTouches(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage); - void abortPointerUsage(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout); - void abortPointerGestures(nsecs_t when, uint32_t policyFlags); - bool preparePointerGestures(nsecs_t when, - bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, - bool isTimeout); - - void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags); - void abortPointerStylus(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags); - void abortPointerMouse(nsecs_t when, uint32_t policyFlags); - - void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, - bool down, bool hovering); - void abortPointerSimple(nsecs_t when, uint32_t policyFlags); - - bool assignExternalStylusId(const RawState& state, bool timeout); - void applyExternalStylusButtonState(nsecs_t when); - void applyExternalStylusTouchState(nsecs_t when); - - // Dispatches a motion event. - // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the - // method will take care of setting the index and transmuting the action to DOWN or UP - // it is the first / last pointer to go down / up. - void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, - int32_t action, int32_t actionButton, - int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, - uint32_t deviceTimestamp, - const PointerProperties* properties, const PointerCoords* coords, - const uint32_t* idToIndex, BitSet32 idBits, - int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); - - // Updates pointer coords and properties for pointers with specified ids that have moved. - // Returns true if any of them changed. - bool updateMovedPointers(const PointerProperties* inProperties, - const PointerCoords* inCoords, const uint32_t* inIdToIndex, - PointerProperties* outProperties, PointerCoords* outCoords, - const uint32_t* outIdToIndex, BitSet32 idBits) const; - - bool isPointInsideSurface(int32_t x, int32_t y); - const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y); - - static void assignPointerIds(const RawState* last, RawState* current); - - void reportEventForStatistics(nsecs_t evdevTime); - - const char* modeToString(DeviceMode deviceMode); -}; - - -class SingleTouchInputMapper : public TouchInputMapper { -public: - explicit SingleTouchInputMapper(InputDevice* device); - virtual ~SingleTouchInputMapper(); - - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -protected: - virtual void syncTouch(nsecs_t when, RawState* outState); - virtual void configureRawPointerAxes(); - virtual bool hasStylus() const; - -private: - SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; -}; - - -class MultiTouchInputMapper : public TouchInputMapper { -public: - explicit MultiTouchInputMapper(InputDevice* device); - virtual ~MultiTouchInputMapper(); - - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -protected: - virtual void syncTouch(nsecs_t when, RawState* outState); - virtual void configureRawPointerAxes(); - virtual bool hasStylus() const; - -private: - MultiTouchMotionAccumulator mMultiTouchMotionAccumulator; - - // Specifies the pointer id bits that are in use, and their associated tracking id. - BitSet32 mPointerIdBits; - int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1]; -}; - -class ExternalStylusInputMapper : public InputMapper { -public: - explicit ExternalStylusInputMapper(InputDevice* device); - virtual ~ExternalStylusInputMapper() = default; - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - virtual void sync(nsecs_t when); - -private: - SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; - RawAbsoluteAxisInfo mRawPressureAxis; - TouchButtonAccumulator mTouchButtonAccumulator; - - StylusState mStylusState; -}; - - -class JoystickInputMapper : public InputMapper { -public: - explicit JoystickInputMapper(InputDevice* device); - virtual ~JoystickInputMapper(); - - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - -private: - struct Axis { - RawAbsoluteAxisInfo rawAxisInfo; - AxisInfo axisInfo; - - bool explicitlyMapped; // true if the axis was explicitly assigned an axis id - - float scale; // scale factor from raw to normalized values - float offset; // offset to add after scaling for normalization - float highScale; // scale factor from raw to normalized values of high split - float highOffset; // offset to add after scaling for normalization of high split - - float min; // normalized inclusive minimum - float max; // normalized inclusive maximum - float flat; // normalized flat region size - float fuzz; // normalized error tolerance - float resolution; // normalized resolution in units/mm - - float filter; // filter out small variations of this size - float currentValue; // current value - float newValue; // most recent value - float highCurrentValue; // current value of high split - float highNewValue; // most recent value of high split - - void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, - bool explicitlyMapped, float scale, float offset, - float highScale, float highOffset, - float min, float max, float flat, float fuzz, float resolution) { - this->rawAxisInfo = rawAxisInfo; - this->axisInfo = axisInfo; - this->explicitlyMapped = explicitlyMapped; - this->scale = scale; - this->offset = offset; - this->highScale = highScale; - this->highOffset = highOffset; - this->min = min; - this->max = max; - this->flat = flat; - this->fuzz = fuzz; - this->resolution = resolution; - this->filter = 0; - resetValue(); - } - - void resetValue() { - this->currentValue = 0; - this->newValue = 0; - this->highCurrentValue = 0; - this->highNewValue = 0; - } - }; - - // Axes indexed by raw ABS_* axis index. - KeyedVector<int32_t, Axis> mAxes; - - void sync(nsecs_t when, bool force); - - bool haveAxis(int32_t axisId); - void pruneAxes(bool ignoreExplicitlyMappedAxes); - bool filterAxes(bool force); - - static bool hasValueChangedSignificantly(float filter, - float newValue, float currentValue, float min, float max); - static bool hasMovedNearerToValueWithinFilteredRange(float filter, - float newValue, float currentValue, float thresholdValue); - - static bool isCenteredAxis(int32_t axis); - static int32_t getCompatAxis(int32_t axis); - - static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info); - static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, - float value); -}; - -} // namespace android - -#endif // _UI_INPUT_READER_H diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp new file mode 100644 index 0000000000..b8c3a808f1 --- /dev/null +++ b/services/inputflinger/dispatcher/Android.bp @@ -0,0 +1,42 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_static { + name: "libinputdispatcher", + defaults: ["inputflinger_defaults"], + srcs: [ + "Connection.cpp", + "Entry.cpp", + "InjectionState.cpp", + "InputDispatcher.cpp", + "InputDispatcherFactory.cpp", + "InputDispatcherThread.cpp", + "InputState.cpp", + "InputTarget.cpp", + "Monitor.cpp", + "TouchState.cpp" + ], + shared_libs: [ + "libbase", + "libcutils", + "libinput", + "libinputreporter", + "libinputflinger_base", + "liblog", + "libui", + "libutils", + ], + + export_include_dirs: ["include"], +} diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h new file mode 100644 index 0000000000..99e2108dbf --- /dev/null +++ b/services/inputflinger/dispatcher/CancelationOptions.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H +#define _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H + +#include <optional> + +namespace android::inputdispatcher { + +/* Specifies which events are to be canceled and why. */ +struct CancelationOptions { + enum Mode { + CANCEL_ALL_EVENTS = 0, + CANCEL_POINTER_EVENTS = 1, + CANCEL_NON_POINTER_EVENTS = 2, + CANCEL_FALLBACK_EVENTS = 3, + }; + + // The criterion to use to determine which events should be canceled. + Mode mode; + + // Descriptive reason for the cancelation. + const char* reason; + + // The specific keycode of the key event to cancel, or nullopt to cancel any key event. + std::optional<int32_t> keyCode = std::nullopt; + + // The specific device id of events to cancel, or nullopt to cancel events from any device. + std::optional<int32_t> deviceId = std::nullopt; + + // The specific display id of events to cancel, or nullopt to cancel events on any display. + std::optional<int32_t> displayId = std::nullopt; + + CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) {} +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_CANCELLATIONOPTIONS_H diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp new file mode 100644 index 0000000000..ef7f6509e4 --- /dev/null +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Connection.h" + +#include "Entry.h" + +namespace android::inputdispatcher { + +Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor) + : status(STATUS_NORMAL), + inputChannel(inputChannel), + monitor(monitor), + inputPublisher(inputChannel), + inputPublisherBlocked(false) {} + +Connection::~Connection() {} + +const std::string Connection::getWindowName() const { + if (inputChannel != nullptr) { + return inputChannel->getName(); + } + if (monitor) { + return "monitor"; + } + return "?"; +} + +const char* Connection::getStatusLabel() const { + switch (status) { + case STATUS_NORMAL: + return "NORMAL"; + case STATUS_BROKEN: + return "BROKEN"; + case STATUS_ZOMBIE: + return "ZOMBIE"; + default: + return "UNKNOWN"; + } +} + +DispatchEntry* Connection::findWaitQueueEntry(uint32_t seq) { + for (DispatchEntry* entry = waitQueue.head; entry != nullptr; entry = entry->next) { + if (entry->seq == seq) { + return entry; + } + } + return nullptr; +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h new file mode 100644 index 0000000000..ed4eebdff4 --- /dev/null +++ b/services/inputflinger/dispatcher/Connection.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_CONNECTION_H +#define _UI_INPUT_INPUTDISPATCHER_CONNECTION_H + +#include "InputState.h" +#include "Queue.h" + +#include <input/InputTransport.h> +#include <deque> + +namespace android::inputdispatcher { + +struct DispatchEntry; + +/* Manages the dispatch state associated with a single input channel. */ +class Connection : public RefBase { +protected: + virtual ~Connection(); + +public: + enum Status { + // Everything is peachy. + STATUS_NORMAL, + // An unrecoverable communication error has occurred. + STATUS_BROKEN, + // The input channel has been unregistered. + STATUS_ZOMBIE + }; + + Status status; + sp<InputChannel> inputChannel; // never null + bool monitor; + InputPublisher inputPublisher; + InputState inputState; + + // True if the socket is full and no further events can be published until + // the application consumes some of the input. + bool inputPublisherBlocked; + + // Queue of events that need to be published to the connection. + Queue<DispatchEntry> outboundQueue; + + // Queue of events that have been published to the connection but that have not + // yet received a "finished" response from the application. + Queue<DispatchEntry> waitQueue; + + explicit Connection(const sp<InputChannel>& inputChannel, bool monitor); + + inline const std::string getInputChannelName() const { return inputChannel->getName(); } + + const std::string getWindowName() const; + const char* getStatusLabel() const; + + DispatchEntry* findWaitQueueEntry(uint32_t seq); +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_CONNECTION_H diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp new file mode 100644 index 0000000000..a4d9e4edc2 --- /dev/null +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Entry.h" + +#include "Connection.h" + +#include <android-base/properties.h> +#include <android-base/stringprintf.h> +#include <cutils/atomic.h> +#include <inttypes.h> + +using android::base::GetBoolProperty; +using android::base::StringPrintf; + +namespace android::inputdispatcher { + +static std::string motionActionToString(int32_t action) { + // Convert MotionEvent action to string + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + return "DOWN"; + case AMOTION_EVENT_ACTION_MOVE: + return "MOVE"; + case AMOTION_EVENT_ACTION_UP: + return "UP"; + case AMOTION_EVENT_ACTION_POINTER_DOWN: + return "POINTER_DOWN"; + case AMOTION_EVENT_ACTION_POINTER_UP: + return "POINTER_UP"; + } + return StringPrintf("%" PRId32, action); +} + +static std::string keyActionToString(int32_t action) { + // Convert KeyEvent action to string + switch (action) { + case AKEY_EVENT_ACTION_DOWN: + return "DOWN"; + case AKEY_EVENT_ACTION_UP: + return "UP"; + case AKEY_EVENT_ACTION_MULTIPLE: + return "MULTIPLE"; + } + return StringPrintf("%" PRId32, action); +} + +// --- EventEntry --- + +EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags) + : sequenceNum(sequenceNum), + refCount(1), + type(type), + eventTime(eventTime), + policyFlags(policyFlags), + injectionState(nullptr), + dispatchInProgress(false) {} + +EventEntry::~EventEntry() { + releaseInjectionState(); +} + +void EventEntry::release() { + refCount -= 1; + if (refCount == 0) { + delete this; + } else { + ALOG_ASSERT(refCount > 0); + } +} + +void EventEntry::releaseInjectionState() { + if (injectionState) { + injectionState->release(); + injectionState = nullptr; + } +} + +// --- ConfigurationChangedEntry --- + +ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime) + : EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {} + +ConfigurationChangedEntry::~ConfigurationChangedEntry() {} + +void ConfigurationChangedEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags); +} + +// --- DeviceResetEntry --- + +DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) + : EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), deviceId(deviceId) {} + +DeviceResetEntry::~DeviceResetEntry() {} + +void DeviceResetEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags); +} + +// --- KeyEntry --- + +KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime) + : EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags), + deviceId(deviceId), + source(source), + displayId(displayId), + action(action), + flags(flags), + keyCode(keyCode), + scanCode(scanCode), + metaState(metaState), + repeatCount(repeatCount), + downTime(downTime), + syntheticRepeat(false), + interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN), + interceptKeyWakeupTime(0) {} + +KeyEntry::~KeyEntry() {} + +void KeyEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("KeyEvent"); + if (!GetBoolProperty("ro.debuggable", false)) { + return; + } + msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " + "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " + "repeatCount=%d), policyFlags=0x%08x", + deviceId, source, displayId, keyActionToString(action).c_str(), flags, + keyCode, scanCode, metaState, repeatCount, policyFlags); +} + +void KeyEntry::recycle() { + releaseInjectionState(); + + dispatchInProgress = false; + syntheticRepeat = false; + interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; + interceptKeyWakeupTime = 0; +} + +// --- MotionEntry --- + +MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, + int32_t buttonState, MotionClassification classification, + int32_t edgeFlags, float xPrecision, float yPrecision, nsecs_t downTime, + uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xOffset, float yOffset) + : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), + eventTime(eventTime), + deviceId(deviceId), + source(source), + displayId(displayId), + action(action), + actionButton(actionButton), + flags(flags), + metaState(metaState), + buttonState(buttonState), + classification(classification), + edgeFlags(edgeFlags), + xPrecision(xPrecision), + yPrecision(yPrecision), + downTime(downTime), + pointerCount(pointerCount) { + for (uint32_t i = 0; i < pointerCount; i++) { + this->pointerProperties[i].copyFrom(pointerProperties[i]); + this->pointerCoords[i].copyFrom(pointerCoords[i]); + if (xOffset || yOffset) { + this->pointerCoords[i].applyOffset(xOffset, yOffset); + } + } +} + +MotionEntry::~MotionEntry() {} + +void MotionEntry::appendDescription(std::string& msg) const { + msg += StringPrintf("MotionEvent"); + if (!GetBoolProperty("ro.debuggable", false)) { + return; + } + msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 + ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " + "buttonState=0x%08x, " + "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " + "pointers=[", + deviceId, source, displayId, motionActionToString(action).c_str(), + actionButton, flags, metaState, buttonState, + motionClassificationToString(classification), edgeFlags, xPrecision, + yPrecision); + + for (uint32_t i = 0; i < pointerCount; i++) { + if (i) { + msg += ", "; + } + msg += StringPrintf("%d: (%.1f, %.1f)", pointerProperties[i].id, pointerCoords[i].getX(), + pointerCoords[i].getY()); + } + msg += StringPrintf("]), policyFlags=0x%08x", policyFlags); +} + +// --- DispatchEntry --- + +volatile int32_t DispatchEntry::sNextSeqAtomic; + +DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, + float yOffset, float globalScaleFactor, float windowXScale, + float windowYScale) + : seq(nextSeq()), + eventEntry(eventEntry), + targetFlags(targetFlags), + xOffset(xOffset), + yOffset(yOffset), + globalScaleFactor(globalScaleFactor), + windowXScale(windowXScale), + windowYScale(windowYScale), + deliveryTime(0), + resolvedAction(0), + resolvedFlags(0) { + eventEntry->refCount += 1; +} + +DispatchEntry::~DispatchEntry() { + eventEntry->release(); +} + +uint32_t DispatchEntry::nextSeq() { + // Sequence number 0 is reserved and will never be returned. + uint32_t seq; + do { + seq = android_atomic_inc(&sNextSeqAtomic); + } while (!seq); + return seq; +} + +// --- CommandEntry --- + +CommandEntry::CommandEntry(Command command) + : command(command), + eventTime(0), + keyEntry(nullptr), + userActivityEventType(0), + seq(0), + handled(false) {} + +CommandEntry::~CommandEntry() {} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h new file mode 100644 index 0000000000..b904caf671 --- /dev/null +++ b/services/inputflinger/dispatcher/Entry.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_ENTRY_H +#define _UI_INPUT_INPUTDISPATCHER_ENTRY_H + +#include "InjectionState.h" +#include "InputTarget.h" + +#include <input/Input.h> +#include <input/InputApplication.h> +#include <stdint.h> +#include <utils/Timers.h> +#include <functional> +#include <string> + +namespace android::inputdispatcher { + +template <typename T> +struct Link { + T* next; + T* prev; + +protected: + inline Link() : next(nullptr), prev(nullptr) {} +}; + +struct EventEntry : Link<EventEntry> { + enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION }; + + uint32_t sequenceNum; + mutable int32_t refCount; + int32_t type; + nsecs_t eventTime; + uint32_t policyFlags; + InjectionState* injectionState; + + bool dispatchInProgress; // initially false, set to true while dispatching + + inline bool isInjected() const { return injectionState != nullptr; } + + void release(); + + virtual void appendDescription(std::string& msg) const = 0; + +protected: + EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags); + virtual ~EventEntry(); + void releaseInjectionState(); +}; + +struct ConfigurationChangedEntry : EventEntry { + explicit ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime); + virtual void appendDescription(std::string& msg) const; + +protected: + virtual ~ConfigurationChangedEntry(); +}; + +struct DeviceResetEntry : EventEntry { + int32_t deviceId; + + DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId); + virtual void appendDescription(std::string& msg) const; + +protected: + virtual ~DeviceResetEntry(); +}; + +struct KeyEntry : EventEntry { + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t flags; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t repeatCount; + nsecs_t downTime; + + bool syntheticRepeat; // set to true for synthetic key repeats + + enum InterceptKeyResult { + INTERCEPT_KEY_RESULT_UNKNOWN, + INTERCEPT_KEY_RESULT_SKIP, + INTERCEPT_KEY_RESULT_CONTINUE, + INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER, + }; + InterceptKeyResult interceptKeyResult; // set based on the interception result + nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER + + KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, + int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, + nsecs_t downTime); + virtual void appendDescription(std::string& msg) const; + void recycle(); + +protected: + virtual ~KeyEntry(); +}; + +struct MotionEntry : EventEntry { + nsecs_t eventTime; + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t actionButton; + int32_t flags; + int32_t metaState; + int32_t buttonState; + MotionClassification classification; + int32_t edgeFlags; + float xPrecision; + float yPrecision; + nsecs_t downTime; + uint32_t pointerCount; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + + MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, + MotionClassification classification, int32_t edgeFlags, float xPrecision, + float yPrecision, nsecs_t downTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xOffset, float yOffset); + virtual void appendDescription(std::string& msg) const; + +protected: + virtual ~MotionEntry(); +}; + +// Tracks the progress of dispatching a particular event to a particular connection. +struct DispatchEntry : Link<DispatchEntry> { + const uint32_t seq; // unique sequence number, never 0 + + EventEntry* eventEntry; // the event to dispatch + int32_t targetFlags; + float xOffset; + float yOffset; + float globalScaleFactor; + float windowXScale = 1.0f; + float windowYScale = 1.0f; + nsecs_t deliveryTime; // time when the event was actually delivered + + // Set to the resolved action and flags when the event is enqueued. + int32_t resolvedAction; + int32_t resolvedFlags; + + DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset, + float globalScaleFactor, float windowXScale, float windowYScale); + ~DispatchEntry(); + + inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; } + + inline bool isSplit() const { return targetFlags & InputTarget::FLAG_SPLIT; } + +private: + static volatile int32_t sNextSeqAtomic; + + static uint32_t nextSeq(); +}; + +class InputDispatcher; +// A command entry captures state and behavior for an action to be performed in the +// dispatch loop after the initial processing has taken place. It is essentially +// a kind of continuation used to postpone sensitive policy interactions to a point +// in the dispatch loop where it is safe to release the lock (generally after finishing +// the critical parts of the dispatch cycle). +// +// The special thing about commands is that they can voluntarily release and reacquire +// the dispatcher lock at will. Initially when the command starts running, the +// dispatcher lock is held. However, if the command needs to call into the policy to +// do some work, it can release the lock, do the work, then reacquire the lock again +// before returning. +// +// This mechanism is a bit clunky but it helps to preserve the invariant that the dispatch +// never calls into the policy while holding its lock. +// +// Commands are implicitly 'LockedInterruptible'. +struct CommandEntry; +typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry); + +class Connection; +struct CommandEntry : Link<CommandEntry> { + explicit CommandEntry(Command command); + ~CommandEntry(); + + Command command; + + // parameters for the command (usage varies by command) + sp<Connection> connection; + nsecs_t eventTime; + KeyEntry* keyEntry; + sp<InputApplicationHandle> inputApplicationHandle; + std::string reason; + int32_t userActivityEventType; + uint32_t seq; + bool handled; + sp<InputChannel> inputChannel; + sp<IBinder> oldToken; + sp<IBinder> newToken; +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_ENTRY_H diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp new file mode 100644 index 0000000000..b2d0a26a37 --- /dev/null +++ b/services/inputflinger/dispatcher/InjectionState.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InjectionState.h" + +#include <log/log.h> + +namespace android::inputdispatcher { + +InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) + : refCount(1), + injectorPid(injectorPid), + injectorUid(injectorUid), + injectionResult(INPUT_EVENT_INJECTION_PENDING), + injectionIsAsync(false), + pendingForegroundDispatches(0) {} + +InjectionState::~InjectionState() {} + +void InjectionState::release() { + refCount -= 1; + if (refCount == 0) { + delete this; + } else { + ALOG_ASSERT(refCount > 0); + } +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h new file mode 100644 index 0000000000..311a0f11ef --- /dev/null +++ b/services/inputflinger/dispatcher/InjectionState.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H +#define _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H + +#include "InputDispatcherInterface.h" + +#include <stdint.h> + +namespace android::inputdispatcher { + +/* + * Constants used to determine the input event injection synchronization mode. + */ +enum { + /* Injection is asynchronous and is assumed always to be successful. */ + INPUT_EVENT_INJECTION_SYNC_NONE = 0, + + /* Waits for previous events to be dispatched so that the input dispatcher can determine + * whether input event injection willbe permitted based on the current input focus. + * Does not wait for the input event to finish processing. */ + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_RESULT = 1, + + /* Waits for the input event to be completely processed. */ + INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED = 2, +}; + +struct InjectionState { + mutable int32_t refCount; + + int32_t injectorPid; + int32_t injectorUid; + int32_t injectionResult; // initially INPUT_EVENT_INJECTION_PENDING + bool injectionIsAsync; // set to true if injection is not waiting for the result + int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress + + InjectionState(int32_t injectorPid, int32_t injectorUid); + void release(); + +private: + ~InjectionState(); +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_INJECTIONSTATE_H diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index b921d954dc..44100086c4 100644 --- a/services/inputflinger/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -45,20 +45,22 @@ #include "InputDispatcher.h" +#include "Connection.h" + #include <errno.h> #include <inttypes.h> #include <limits.h> -#include <sstream> #include <stddef.h> #include <time.h> #include <unistd.h> +#include <sstream> #include <android-base/chrono_utils.h> #include <android-base/stringprintf.h> +#include <binder/Binder.h> #include <log/log.h> -#include <utils/Trace.h> #include <powermanager/PowerManager.h> -#include <binder/Binder.h> +#include <utils/Trace.h> #define INDENT " " #define INDENT2 " " @@ -67,7 +69,7 @@ using android::base::StringPrintf; -namespace android { +namespace android::inputdispatcher { // Default input dispatching timeout if there is no focused application or paused window // from which to determine an appropriate dispatching timeout. @@ -97,10 +99,6 @@ constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; -// Sequence number for synthesized or injected events. -constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; - - static inline nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); } @@ -109,41 +107,23 @@ static inline const char* toString(bool value) { return value ? "true" : "false"; } -static std::string dispatchModeToString(int32_t dispatchMode) { - switch (dispatchMode) { - case InputTarget::FLAG_DISPATCH_AS_IS: - return "DISPATCH_AS_IS"; - case InputTarget::FLAG_DISPATCH_AS_OUTSIDE: - return "DISPATCH_AS_OUTSIDE"; - case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER: - return "DISPATCH_AS_HOVER_ENTER"; - case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT: - return "DISPATCH_AS_HOVER_EXIT"; - case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT: - return "DISPATCH_AS_SLIPPERY_EXIT"; - case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER: - return "DISPATCH_AS_SLIPPERY_ENTER"; - } - return StringPrintf("%" PRId32, dispatchMode); -} - static inline int32_t getMotionEventActionPointerIndex(int32_t action) { - return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) - >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> + AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; } static bool isValidKeyAction(int32_t action) { switch (action) { - case AKEY_EVENT_ACTION_DOWN: - case AKEY_EVENT_ACTION_UP: - return true; - default: - return false; + case AKEY_EVENT_ACTION_DOWN: + case AKEY_EVENT_ACTION_UP: + return true; + default: + return false; } } static bool validateKeyEvent(int32_t action) { - if (! isValidKeyAction(action)) { + if (!isValidKeyAction(action)) { ALOGE("Key event has invalid action code 0x%x", action); return false; } @@ -152,46 +132,46 @@ static bool validateKeyEvent(int32_t action) { static bool isValidMotionAction(int32_t action, int32_t actionButton, int32_t pointerCount) { switch (action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: - case AMOTION_EVENT_ACTION_MOVE: - case AMOTION_EVENT_ACTION_OUTSIDE: - case AMOTION_EVENT_ACTION_HOVER_ENTER: - case AMOTION_EVENT_ACTION_HOVER_MOVE: - case AMOTION_EVENT_ACTION_HOVER_EXIT: - case AMOTION_EVENT_ACTION_SCROLL: - return true; - case AMOTION_EVENT_ACTION_POINTER_DOWN: - case AMOTION_EVENT_ACTION_POINTER_UP: { - int32_t index = getMotionEventActionPointerIndex(action); - return index >= 0 && index < pointerCount; - } - case AMOTION_EVENT_ACTION_BUTTON_PRESS: - case AMOTION_EVENT_ACTION_BUTTON_RELEASE: - return actionButton != 0; - default: - return false; + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_OUTSIDE: + case AMOTION_EVENT_ACTION_HOVER_ENTER: + case AMOTION_EVENT_ACTION_HOVER_MOVE: + case AMOTION_EVENT_ACTION_HOVER_EXIT: + case AMOTION_EVENT_ACTION_SCROLL: + return true; + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_POINTER_UP: { + int32_t index = getMotionEventActionPointerIndex(action); + return index >= 0 && index < pointerCount; + } + case AMOTION_EVENT_ACTION_BUTTON_PRESS: + case AMOTION_EVENT_ACTION_BUTTON_RELEASE: + return actionButton != 0; + default: + return false; } } static bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCount, - const PointerProperties* pointerProperties) { - if (! isValidMotionAction(action, actionButton, pointerCount)) { + const PointerProperties* pointerProperties) { + if (!isValidMotionAction(action, actionButton, pointerCount)) { ALOGE("Motion event has invalid action code 0x%x", action); return false; } if (pointerCount < 1 || pointerCount > MAX_POINTERS) { ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %d.", - pointerCount, MAX_POINTERS); + pointerCount, MAX_POINTERS); return false; } BitSet32 pointerIdBits; for (size_t i = 0; i < pointerCount; i++) { int32_t id = pointerProperties[i].id; if (id < 0 || id > MAX_POINTER_ID) { - ALOGE("Motion event has invalid pointer id %d; value must be between 0 and %d", - id, MAX_POINTER_ID); + ALOGE("Motion event has invalid pointer id %d; value must be between 0 and %d", id, + MAX_POINTER_ID); return false; } if (pointerIdBits.hasBit(id)) { @@ -223,23 +203,26 @@ static void dumpRegion(std::string& dump, const Region& region) { } } -template<typename T, typename U> +template <typename T, typename U> static T getValueByKey(std::unordered_map<U, T>& map, U key) { typename std::unordered_map<U, T>::const_iterator it = map.find(key); return it != map.end() ? it->second : T{}; } - // --- InputDispatcher --- -InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : - mPolicy(policy), - mPendingEvent(nullptr), mLastDropReason(DROP_REASON_NOT_DROPPED), - mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), - mNextUnblockedEvent(nullptr), - mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), - mFocusedDisplayId(ADISPLAY_ID_DEFAULT), - mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { +InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) + : mPolicy(policy), + mPendingEvent(nullptr), + mLastDropReason(DROP_REASON_NOT_DROPPED), + mAppSwitchSawKeyDown(false), + mAppSwitchDueTime(LONG_LONG_MAX), + mNextUnblockedEvent(nullptr), + mDispatchEnabled(false), + mDispatchFrozen(false), + mInputFilterEnabled(false), + mFocusedDisplayId(ADISPLAY_ID_DEFAULT), + mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { mLooper = new Looper(false); mReporter = createInputReporter(); @@ -315,7 +298,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // Ready to start a new event. // If we don't already have a pending event, go grab one. - if (! mPendingEvent) { + if (!mPendingEvent) { if (mInboundQueue.isEmpty()) { if (isAppSwitchDue) { // The inbound queue is empty so the app switch key we were waiting @@ -370,63 +353,59 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } switch (mPendingEvent->type) { - case EventEntry::TYPE_CONFIGURATION_CHANGED: { - ConfigurationChangedEntry* typedEntry = - static_cast<ConfigurationChangedEntry*>(mPendingEvent); - done = dispatchConfigurationChangedLocked(currentTime, typedEntry); - dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped - break; - } - - case EventEntry::TYPE_DEVICE_RESET: { - DeviceResetEntry* typedEntry = - static_cast<DeviceResetEntry*>(mPendingEvent); - done = dispatchDeviceResetLocked(currentTime, typedEntry); - dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped - break; - } - - case EventEntry::TYPE_KEY: { - KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); - if (isAppSwitchDue) { - if (isAppSwitchKeyEvent(typedEntry)) { - resetPendingAppSwitchLocked(true); - isAppSwitchDue = false; - } else if (dropReason == DROP_REASON_NOT_DROPPED) { - dropReason = DROP_REASON_APP_SWITCH; - } - } - if (dropReason == DROP_REASON_NOT_DROPPED - && isStaleEvent(currentTime, typedEntry)) { - dropReason = DROP_REASON_STALE; - } - if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { - dropReason = DROP_REASON_BLOCKED; + case EventEntry::TYPE_CONFIGURATION_CHANGED: { + ConfigurationChangedEntry* typedEntry = + static_cast<ConfigurationChangedEntry*>(mPendingEvent); + done = dispatchConfigurationChangedLocked(currentTime, typedEntry); + dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped + break; } - done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); - break; - } - case EventEntry::TYPE_MOTION: { - MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); - if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) { - dropReason = DROP_REASON_APP_SWITCH; + case EventEntry::TYPE_DEVICE_RESET: { + DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent); + done = dispatchDeviceResetLocked(currentTime, typedEntry); + dropReason = DROP_REASON_NOT_DROPPED; // device resets are never dropped + break; } - if (dropReason == DROP_REASON_NOT_DROPPED - && isStaleEvent(currentTime, typedEntry)) { - dropReason = DROP_REASON_STALE; + + case EventEntry::TYPE_KEY: { + KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); + if (isAppSwitchDue) { + if (isAppSwitchKeyEvent(typedEntry)) { + resetPendingAppSwitchLocked(true); + isAppSwitchDue = false; + } else if (dropReason == DROP_REASON_NOT_DROPPED) { + dropReason = DROP_REASON_APP_SWITCH; + } + } + if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) { + dropReason = DROP_REASON_STALE; + } + if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { + dropReason = DROP_REASON_BLOCKED; + } + done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); + break; } - if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { - dropReason = DROP_REASON_BLOCKED; + + case EventEntry::TYPE_MOTION: { + MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); + if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) { + dropReason = DROP_REASON_APP_SWITCH; + } + if (dropReason == DROP_REASON_NOT_DROPPED && isStaleEvent(currentTime, typedEntry)) { + dropReason = DROP_REASON_STALE; + } + if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { + dropReason = DROP_REASON_BLOCKED; + } + done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime); + break; } - done = dispatchMotionLocked(currentTime, typedEntry, - &dropReason, nextWakeupTime); - break; - } - default: - ALOG_ASSERT(false); - break; + default: + ALOG_ASSERT(false); + break; } if (done) { @@ -436,7 +415,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { mLastDropReason = dropReason; releasePendingEventLocked(); - *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately + *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately } } @@ -446,55 +425,56 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { traceInboundQueueLengthLocked(); switch (entry->type) { - case EventEntry::TYPE_KEY: { - // Optimize app switch latency. - // If the application takes too long to catch up then we drop all events preceding - // the app switch key. - KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); - if (isAppSwitchKeyEvent(keyEntry)) { - if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) { - mAppSwitchSawKeyDown = true; - } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) { - if (mAppSwitchSawKeyDown) { + case EventEntry::TYPE_KEY: { + // Optimize app switch latency. + // If the application takes too long to catch up then we drop all events preceding + // the app switch key. + KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); + if (isAppSwitchKeyEvent(keyEntry)) { + if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) { + mAppSwitchSawKeyDown = true; + } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) { + if (mAppSwitchSawKeyDown) { #if DEBUG_APP_SWITCH - ALOGD("App switch is pending!"); + ALOGD("App switch is pending!"); #endif - mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT; - mAppSwitchSawKeyDown = false; - needWake = true; + mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT; + mAppSwitchSawKeyDown = false; + needWake = true; + } } } + break; } - break; - } - - case EventEntry::TYPE_MOTION: { - // Optimize case where the current application is unresponsive and the user - // decides to touch a window in a different application. - // If the application takes too long to catch up then we drop all events preceding - // the touch into the other window. - MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); - if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN - && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) - && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY - && mInputTargetWaitApplicationToken != nullptr) { - int32_t displayId = motionEntry->displayId; - int32_t x = int32_t(motionEntry->pointerCoords[0]. - getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(motionEntry->pointerCoords[0]. - getAxisValue(AMOTION_EVENT_AXIS_Y)); - sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y); - if (touchedWindowHandle != nullptr - && touchedWindowHandle->getApplicationToken() - != mInputTargetWaitApplicationToken) { - // User touched a different application than the one we are waiting on. - // Flag the event, and start pruning the input queue. - mNextUnblockedEvent = motionEntry; - needWake = true; + + case EventEntry::TYPE_MOTION: { + // Optimize case where the current application is unresponsive and the user + // decides to touch a window in a different application. + // If the application takes too long to catch up then we drop all events preceding + // the touch into the other window. + MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); + if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN && + (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && + mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && + mInputTargetWaitApplicationToken != nullptr) { + int32_t displayId = motionEntry->displayId; + int32_t x = + int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = + int32_t(motionEntry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); + sp<InputWindowHandle> touchedWindowHandle = + findTouchedWindowAtLocked(displayId, x, y); + if (touchedWindowHandle != nullptr && + touchedWindowHandle->getApplicationToken() != + mInputTargetWaitApplicationToken) { + // User touched a different application than the one we are waiting on. + // Flag the event, and start pruning the input queue. + mNextUnblockedEvent = motionEntry; + needWake = true; + } } + break; } - break; - } } return needWake; @@ -508,8 +488,9 @@ void InputDispatcher::addRecentEventLocked(EventEntry* entry) { } } -sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, - int32_t x, int32_t y, bool addOutsideTargets, bool addPortalWindows) { +sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, + int32_t y, bool addOutsideTargets, + bool addPortalWindows) { // Traverse windows from front to back to find touched window. const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); for (const sp<InputWindowHandle>& windowHandle : windowHandles) { @@ -519,18 +500,19 @@ sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t display if (windowInfo->visible) { if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) { - bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE - | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0; + bool isTouchModal = (flags & + (InputWindowInfo::FLAG_NOT_FOCUSABLE | + InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0; if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) { int32_t portalToDisplayId = windowInfo->portalToDisplayId; - if (portalToDisplayId != ADISPLAY_ID_NONE - && portalToDisplayId != displayId) { + if (portalToDisplayId != ADISPLAY_ID_NONE && + portalToDisplayId != displayId) { if (addPortalWindows) { // For the monitoring channels of the display. mTempTouchState.addPortalWindow(windowHandle); } - return findTouchedWindowAtLocked( - portalToDisplayId, x, y, addOutsideTargets, addPortalWindows); + return findTouchedWindowAtLocked(portalToDisplayId, x, y, + addOutsideTargets, addPortalWindows); } // Found window. return windowHandle; @@ -538,8 +520,9 @@ sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t display } if (addOutsideTargets && (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) { - mTempTouchState.addOrUpdateWindow( - windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE, BitSet32(0)); + mTempTouchState.addOrUpdateWindow(windowHandle, + InputTarget::FLAG_DISPATCH_AS_OUTSIDE, + BitSet32(0)); } } } @@ -547,7 +530,7 @@ sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t display return nullptr; } -std::vector<InputDispatcher::TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked( +std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked( int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) { std::vector<TouchedMonitor> touchedMonitors; @@ -556,14 +539,15 @@ std::vector<InputDispatcher::TouchedMonitor> InputDispatcher::findTouchedGesture for (const sp<InputWindowHandle>& portalWindow : portalWindows) { const InputWindowInfo* windowInfo = portalWindow->getInfo(); monitors = getValueByKey(mGestureMonitorsByDisplay, windowInfo->portalToDisplayId); - addGestureMonitors(monitors, touchedMonitors, - -windowInfo->frameLeft, -windowInfo->frameTop); + addGestureMonitors(monitors, touchedMonitors, -windowInfo->frameLeft, + -windowInfo->frameTop); } return touchedMonitors; } void InputDispatcher::addGestureMonitors(const std::vector<Monitor>& monitors, - std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset, float yOffset) { + std::vector<TouchedMonitor>& outTouchedMonitors, + float xOffset, float yOffset) { if (monitors.empty()) { return; } @@ -576,68 +560,66 @@ void InputDispatcher::addGestureMonitors(const std::vector<Monitor>& monitors, void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) { const char* reason; switch (dropReason) { - case DROP_REASON_POLICY: + case DROP_REASON_POLICY: #if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("Dropped event because policy consumed it."); + ALOGD("Dropped event because policy consumed it."); #endif - reason = "inbound event was dropped because the policy consumed it"; - break; - case DROP_REASON_DISABLED: - if (mLastDropReason != DROP_REASON_DISABLED) { - ALOGI("Dropped event because input dispatch is disabled."); - } - reason = "inbound event was dropped because input dispatch is disabled"; - break; - case DROP_REASON_APP_SWITCH: - ALOGI("Dropped event because of pending overdue app switch."); - reason = "inbound event was dropped because of pending overdue app switch"; - break; - case DROP_REASON_BLOCKED: - ALOGI("Dropped event because the current application is not responding and the user " - "has started interacting with a different application."); - reason = "inbound event was dropped because the current application is not responding " - "and the user has started interacting with a different application"; - break; - case DROP_REASON_STALE: - ALOGI("Dropped event because it is stale."); - reason = "inbound event was dropped because it is stale"; - break; - default: - ALOG_ASSERT(false); - return; + reason = "inbound event was dropped because the policy consumed it"; + break; + case DROP_REASON_DISABLED: + if (mLastDropReason != DROP_REASON_DISABLED) { + ALOGI("Dropped event because input dispatch is disabled."); + } + reason = "inbound event was dropped because input dispatch is disabled"; + break; + case DROP_REASON_APP_SWITCH: + ALOGI("Dropped event because of pending overdue app switch."); + reason = "inbound event was dropped because of pending overdue app switch"; + break; + case DROP_REASON_BLOCKED: + ALOGI("Dropped event because the current application is not responding and the user " + "has started interacting with a different application."); + reason = "inbound event was dropped because the current application is not responding " + "and the user has started interacting with a different application"; + break; + case DROP_REASON_STALE: + ALOGI("Dropped event because it is stale."); + reason = "inbound event was dropped because it is stale"; + break; + default: + ALOG_ASSERT(false); + return; } switch (entry->type) { - case EventEntry::TYPE_KEY: { - CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); - synthesizeCancelationEventsForAllConnectionsLocked(options); - break; - } - case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); - if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason); - synthesizeCancelationEventsForAllConnectionsLocked(options); - } else { + case EventEntry::TYPE_KEY: { CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); + break; + } + case EventEntry::TYPE_MOTION: { + MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); + if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { + CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason); + synthesizeCancelationEventsForAllConnectionsLocked(options); + } else { + CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason); + synthesizeCancelationEventsForAllConnectionsLocked(options); + } + break; } - break; - } } } static bool isAppSwitchKeyCode(int32_t keyCode) { - return keyCode == AKEYCODE_HOME - || keyCode == AKEYCODE_ENDCALL - || keyCode == AKEYCODE_APP_SWITCH; + return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL || + keyCode == AKEYCODE_APP_SWITCH; } bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) { - return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) - && isAppSwitchKeyCode(keyEntry->keyCode) - && (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) - && (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER); + return !(keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry->keyCode) && + (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) && + (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER); } bool InputDispatcher::isAppSwitchPendingLocked() { @@ -677,18 +659,18 @@ bool InputDispatcher::runCommandsLockedInterruptible() { commandEntry->connection.clear(); delete commandEntry; - } while (! mCommandQueue.isEmpty()); + } while (!mCommandQueue.isEmpty()); return true; } -InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) { +CommandEntry* InputDispatcher::postCommandLocked(Command command) { CommandEntry* commandEntry = new CommandEntry(command); mCommandQueue.enqueueAtTail(commandEntry); return commandEntry; } void InputDispatcher::drainInboundQueueLocked() { - while (! mInboundQueue.isEmpty()) { + while (!mInboundQueue.isEmpty()) { EventEntry* entry = mInboundQueue.dequeueAtHead(); releaseInboundEventLocked(entry); } @@ -725,7 +707,7 @@ void InputDispatcher::resetKeyRepeatLocked() { } } -InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { +KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t currentTime) { KeyEntry* entry = mKeyRepeatState.lastKeyEntry; // Reuse the repeated key entry if it is otherwise unreferenced. @@ -737,10 +719,11 @@ InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t cu entry->policyFlags = policyFlags; entry->repeatCount += 1; } else { - KeyEntry* newEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - entry->deviceId, entry->source, entry->displayId, policyFlags, - entry->action, entry->flags, entry->keyCode, entry->scanCode, - entry->metaState, entry->repeatCount + 1, entry->downTime); + KeyEntry* newEntry = + new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, entry->deviceId, + entry->source, entry->displayId, policyFlags, entry->action, + entry->flags, entry->keyCode, entry->scanCode, entry->metaState, + entry->repeatCount + 1, entry->downTime); mKeyRepeatState.lastKeyEntry = newEntry; entry->release(); @@ -757,8 +740,8 @@ InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t cu return entry; } -bool InputDispatcher::dispatchConfigurationChangedLocked( - nsecs_t currentTime, ConfigurationChangedEntry* entry) { +bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime, + ConfigurationChangedEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("dispatchConfigurationChanged - eventTime=%" PRId64, entry->eventTime); #endif @@ -767,36 +750,33 @@ bool InputDispatcher::dispatchConfigurationChangedLocked( resetKeyRepeatLocked(); // Enqueue a command to run outside the lock to tell the policy that the configuration changed. - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); commandEntry->eventTime = entry->eventTime; return true; } -bool InputDispatcher::dispatchDeviceResetLocked( - nsecs_t currentTime, DeviceResetEntry* entry) { +bool InputDispatcher::dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("dispatchDeviceReset - eventTime=%" PRId64 ", deviceId=%d", entry->eventTime, - entry->deviceId); + entry->deviceId); #endif - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, - "device was reset"); + CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "device was reset"); options.deviceId = entry->deviceId; synthesizeCancelationEventsForAllConnectionsLocked(options); return true; } bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) { + DropReason* dropReason, nsecs_t* nextWakeupTime) { // Preprocessing. - if (! entry->dispatchInProgress) { - if (entry->repeatCount == 0 - && entry->action == AKEY_EVENT_ACTION_DOWN - && (entry->policyFlags & POLICY_FLAG_TRUSTED) - && (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) { - if (mKeyRepeatState.lastKeyEntry - && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { + if (!entry->dispatchInProgress) { + if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN && + (entry->policyFlags & POLICY_FLAG_TRUSTED) && + (!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) { + if (mKeyRepeatState.lastKeyEntry && + mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { // We have seen two identical key downs in a row which indicates that the device // driver is automatically generating key repeats itself. We take note of the // repeat here, but we disable our own next key repeat timer since it is clear that @@ -811,7 +791,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, } mKeyRepeatState.lastKeyEntry = entry; entry->refCount += 1; - } else if (! entry->syntheticRepeat) { + } else if (!entry->syntheticRepeat) { resetKeyRepeatLocked(); } @@ -842,12 +822,11 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); + &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); sp<InputWindowHandle> focusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry)); if (focusedWindowHandle != nullptr) { - commandEntry->inputChannel = - getInputChannelLocked(focusedWindowHandle->getToken()); + commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken()); } commandEntry->keyEntry = entry; entry->refCount += 1; @@ -863,16 +842,17 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, // Clean up if dropping the event. if (*dropReason != DROP_REASON_NOT_DROPPED) { - setInjectionResult(entry, *dropReason == DROP_REASON_POLICY - ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); + setInjectionResult(entry, + *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED + : INPUT_EVENT_INJECTION_FAILED); mReporter->reportDroppedKey(entry->sequenceNum); return true; } // Identify targets. std::vector<InputTarget> inputTargets; - int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, - entry, inputTargets, nextWakeupTime); + int32_t injectionResult = + findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { return false; } @@ -893,20 +873,19 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", " - "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, " - "metaState=0x%x, repeatCount=%d, downTime=%" PRId64, - prefix, - entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags, - entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, - entry->repeatCount, entry->downTime); + "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, " + "metaState=0x%x, repeatCount=%d, downTime=%" PRId64, + prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId, + entry->policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode, + entry->metaState, entry->repeatCount, entry->downTime); #endif } -bool InputDispatcher::dispatchMotionLocked( - nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { +bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, + DropReason* dropReason, nsecs_t* nextWakeupTime) { ATRACE_CALL(); // Preprocessing. - if (! entry->dispatchInProgress) { + if (!entry->dispatchInProgress) { entry->dispatchInProgress = true; logOutboundMotionDetails("dispatchMotion - ", entry); @@ -914,8 +893,9 @@ bool InputDispatcher::dispatchMotionLocked( // Clean up if dropping the event. if (*dropReason != DROP_REASON_NOT_DROPPED) { - setInjectionResult(entry, *dropReason == DROP_REASON_POLICY - ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); + setInjectionResult(entry, + *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED + : INPUT_EVENT_INJECTION_FAILED); return true; } @@ -928,12 +908,13 @@ bool InputDispatcher::dispatchMotionLocked( int32_t injectionResult; if (isPointerEvent) { // Pointer event. (eg. touchscreen) - injectionResult = findTouchedWindowTargetsLocked(currentTime, - entry, inputTargets, nextWakeupTime, &conflictingPointerActions); + injectionResult = + findTouchedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime, + &conflictingPointerActions); } else { // Non touch event. (eg. trackball) - injectionResult = findFocusedWindowTargetsLocked(currentTime, - entry, inputTargets, nextWakeupTime); + injectionResult = + findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); } if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { return false; @@ -942,9 +923,9 @@ bool InputDispatcher::dispatchMotionLocked( setInjectionResult(entry, injectionResult); if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) { - CancelationOptions::Mode mode(isPointerEvent ? - CancelationOptions::CANCEL_POINTER_EVENTS : - CancelationOptions::CANCEL_NON_POINTER_EVENTS); + CancelationOptions::Mode mode(isPointerEvent + ? CancelationOptions::CANCEL_POINTER_EVENTS + : CancelationOptions::CANCEL_NON_POINTER_EVENTS); CancelationOptions options(mode, "input event injection failed"); synthesizeCancelationEventsForMonitorsLocked(options); } @@ -964,7 +945,7 @@ bool InputDispatcher::dispatchMotionLocked( for (size_t i = 0; i < state.portalWindows.size(); i++) { const InputWindowInfo* windowInfo = state.portalWindows[i]->getInfo(); addGlobalMonitoringTargetsLocked(inputTargets, windowInfo->portalToDisplayId, - -windowInfo->frameLeft, -windowInfo->frameTop); + -windowInfo->frameLeft, -windowInfo->frameTop); } } } @@ -973,50 +954,46 @@ bool InputDispatcher::dispatchMotionLocked( // Dispatch the motion. if (conflictingPointerActions) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "conflicting pointer actions"); + "conflicting pointer actions"); synthesizeCancelationEventsForAllConnectionsLocked(options); } dispatchEventLocked(currentTime, entry, inputTargets); return true; } - void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 - ", policyFlags=0x%x, " - "action=0x%x, actionButton=0x%x, flags=0x%x, " - "metaState=0x%x, buttonState=0x%x," - "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, - prefix, - entry->eventTime, entry->deviceId, entry->source, entry->displayId, entry->policyFlags, - entry->action, entry->actionButton, entry->flags, - entry->metaState, entry->buttonState, - entry->edgeFlags, entry->xPrecision, entry->yPrecision, - entry->downTime); + ", policyFlags=0x%x, " + "action=0x%x, actionButton=0x%x, flags=0x%x, " + "metaState=0x%x, buttonState=0x%x," + "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, + prefix, entry->eventTime, entry->deviceId, entry->source, entry->displayId, + entry->policyFlags, entry->action, entry->actionButton, entry->flags, entry->metaState, + entry->buttonState, entry->edgeFlags, entry->xPrecision, entry->yPrecision, + entry->downTime); for (uint32_t i = 0; i < entry->pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " - "x=%f, y=%f, pressure=%f, size=%f, " - "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " - "orientation=%f", - i, entry->pointerProperties[i].id, - entry->pointerProperties[i].toolType, - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + "x=%f, y=%f, pressure=%f, size=%f, " + "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " + "orientation=%f", + i, entry->pointerProperties[i].id, entry->pointerProperties[i].toolType, + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } #endif } -void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, - EventEntry* eventEntry, const std::vector<InputTarget>& inputTargets) { +void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, + const std::vector<InputTarget>& inputTargets) { ATRACE_CALL(); #if DEBUG_DISPATCH_CYCLE ALOGD("dispatchEventToCurrentInputTargets"); @@ -1034,18 +1011,17 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, } else { #if DEBUG_FOCUS ALOGD("Dropping event delivery to target with channel '%s' because it " - "is no longer registered with the input dispatcher.", - inputTarget.inputChannel->getName().c_str()); + "is no longer registered with the input dispatcher.", + inputTarget.inputChannel->getName().c_str()); #endif } } } -int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, - const EventEntry* entry, +int32_t InputDispatcher::handleTargetsNotReadyLocked( + nsecs_t currentTime, const EventEntry* entry, const sp<InputApplicationHandle>& applicationHandle, - const sp<InputWindowHandle>& windowHandle, - nsecs_t* nextWakeupTime, const char* reason) { + const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) { if (applicationHandle == nullptr && windowHandle == nullptr) { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { #if DEBUG_FOCUS @@ -1061,15 +1037,14 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { #if DEBUG_FOCUS ALOGD("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), - reason); + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); #endif nsecs_t timeout; if (windowHandle != nullptr) { timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } else if (applicationHandle != nullptr) { - timeout = applicationHandle->getDispatchingTimeout( - DEFAULT_INPUT_DISPATCHING_TIMEOUT); + timeout = + applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } else { timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT; } @@ -1094,8 +1069,8 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, } if (currentTime >= mInputTargetWaitTimeoutTime) { - onANRLocked(currentTime, applicationHandle, windowHandle, - entry->eventTime, mInputTargetWaitStartTime, reason); + onANRLocked(currentTime, applicationHandle, windowHandle, entry->eventTime, + mInputTargetWaitStartTime, reason); // Force poll loop to wake up immediately on next iteration once we get the // ANR response back from the policy. @@ -1117,8 +1092,8 @@ void InputDispatcher::removeWindowByTokenLocked(const sp<IBinder>& token) { } } -void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, - const sp<InputChannel>& inputChannel) { +void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( + nsecs_t newTimeout, const sp<InputChannel>& inputChannel) { if (newTimeout > 0) { // Extend the timeout. mInputTargetWaitTimeoutTime = now() + newTimeout; @@ -1139,7 +1114,7 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout if (connection->status == Connection::STATUS_NORMAL) { CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, - "application not responding"); + "application not responding"); synthesizeCancelationEventsForConnectionLocked(connection, options); } } @@ -1147,8 +1122,7 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout } } -nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked( - nsecs_t currentTime) { +nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) { if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { return currentTime - mInputTargetWaitStartTime; } @@ -1157,7 +1131,7 @@ nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked( void InputDispatcher::resetANRTimeoutsLocked() { #if DEBUG_FOCUS - ALOGD("Resetting ANR timeouts."); + ALOGD("Resetting ANR timeouts."); #endif // Reset input target wait timeout. @@ -1173,26 +1147,28 @@ void InputDispatcher::resetANRTimeoutsLocked() { int32_t InputDispatcher::getTargetDisplayId(const EventEntry* entry) { int32_t displayId; switch (entry->type) { - case EventEntry::TYPE_KEY: { - const KeyEntry* typedEntry = static_cast<const KeyEntry*>(entry); - displayId = typedEntry->displayId; - break; - } - case EventEntry::TYPE_MOTION: { - const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry); - displayId = typedEntry->displayId; - break; - } - default: { - ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type); - return ADISPLAY_ID_NONE; - } + case EventEntry::TYPE_KEY: { + const KeyEntry* typedEntry = static_cast<const KeyEntry*>(entry); + displayId = typedEntry->displayId; + break; + } + case EventEntry::TYPE_MOTION: { + const MotionEntry* typedEntry = static_cast<const MotionEntry*>(entry); + displayId = typedEntry->displayId; + break; + } + default: { + ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry->type); + return ADISPLAY_ID_NONE; + } } return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId; } int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, - const EventEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) { + const EventEntry* entry, + std::vector<InputTarget>& inputTargets, + nsecs_t* nextWakeupTime) { int32_t injectionResult; std::string reason; @@ -1206,16 +1182,20 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, // then drop the event. if (focusedWindowHandle == nullptr) { if (focusedApplicationHandle != nullptr) { - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - focusedApplicationHandle, nullptr, nextWakeupTime, - "Waiting because no window has focus but there is a " - "focused application that may eventually add a window " - "when it finishes starting up."); + injectionResult = + handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, + nullptr, nextWakeupTime, + "Waiting because no window has focus but there is " + "a " + "focused application that may eventually add a " + "window " + "when it finishes starting up."); goto Unresponsive; } ALOGI("Dropping event because there is no focused window or focused application in display " - "%" PRId32 ".", displayId); + "%" PRId32 ".", + displayId); injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; } @@ -1227,19 +1207,19 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, } // Check whether the window is ready for more input. - reason = checkWindowReadyForMoreInputLocked(currentTime, - focusedWindowHandle, entry, "focused"); + reason = checkWindowReadyForMoreInputLocked(currentTime, focusedWindowHandle, entry, "focused"); if (!reason.empty()) { - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - focusedApplicationHandle, focusedWindowHandle, nextWakeupTime, reason.c_str()); + injectionResult = + handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, + focusedWindowHandle, nextWakeupTime, reason.c_str()); goto Unresponsive; } // Success! Output targets. injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; addWindowTargetLocked(focusedWindowHandle, - InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0), - inputTargets); + InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, + BitSet32(0), inputTargets); // Done. Failed: @@ -1248,15 +1228,17 @@ Unresponsive: updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); #if DEBUG_FOCUS ALOGD("findFocusedWindow finished: injectionResult=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, timeSpentWaitingForApplication / 1000000.0); + "timeSpentWaitingForApplication=%0.1fms", + injectionResult, timeSpentWaitingForApplication / 1000000.0); #endif return injectionResult; } int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, - const MotionEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime, - bool* outConflictingPointerActions) { + const MotionEntry* entry, + std::vector<InputTarget>& inputTargets, + nsecs_t* nextWakeupTime, + bool* outConflictingPointerActions) { ATRACE_CALL(); enum InjectionPermission { INJECTION_PERMISSION_UNKNOWN, @@ -1286,23 +1268,22 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } bool isSplit = mTempTouchState.split; - bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 - && (mTempTouchState.deviceId != entry->deviceId - || mTempTouchState.source != entry->source - || mTempTouchState.displayId != displayId); - bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE - || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER - || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); - bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN - || maskedAction == AMOTION_EVENT_ACTION_SCROLL - || isHoverAction); + bool switchedDevice = mTempTouchState.deviceId >= 0 && mTempTouchState.displayId >= 0 && + (mTempTouchState.deviceId != entry->deviceId || + mTempTouchState.source != entry->source || mTempTouchState.displayId != displayId); + bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || + maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || + maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); + bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || + maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction); bool wrongDevice = false; if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) { #if DEBUG_FOCUS ALOGD("Dropping event because a pointer for a different device is already down " - "in display %" PRId32, displayId); + "in display %" PRId32, + displayId); #endif // TODO: test multiple simultaneous input streams. injectionResult = INPUT_EVENT_INJECTION_FAILED; @@ -1319,7 +1300,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { #if DEBUG_FOCUS ALOGI("Dropping move event because a pointer for a different device is already active " - "in display %" PRId32, displayId); + "in display %" PRId32, + displayId); #endif // TODO: test multiple simultaneous input streams. injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; @@ -1332,21 +1314,20 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ int32_t pointerIndex = getMotionEventActionPointerIndex(action); - int32_t x = int32_t(entry->pointerCoords[pointerIndex]. - getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(entry->pointerCoords[pointerIndex]. - getAxisValue(AMOTION_EVENT_AXIS_Y)); + int32_t x = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; - sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked( - displayId, x, y, isDown /*addOutsideTargets*/, true /*addPortalWindows*/); + sp<InputWindowHandle> newTouchedWindowHandle = + findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/, + true /*addPortalWindows*/); std::vector<TouchedMonitor> newGestureMonitors = isDown ? findTouchedGestureMonitorsLocked(displayId, mTempTouchState.portalWindows) : std::vector<TouchedMonitor>{}; // Figure out whether splitting will be allowed for this window. - if (newTouchedWindowHandle != nullptr - && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { + if (newTouchedWindowHandle != nullptr && + newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { // New window supports splitting. isSplit = true; } else if (isSplit) { @@ -1363,7 +1344,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) { ALOGI("Dropping event because there is no touchable window or gesture monitor at " - "(%d, %d) in display %" PRId32 ".", x, y, displayId); + "(%d, %d) in display %" PRId32 ".", + x, y, displayId); injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; } @@ -1401,19 +1383,19 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ // If the pointer is not currently down, then ignore the event. - if (! mTempTouchState.down) { + if (!mTempTouchState.down) { #if DEBUG_FOCUS ALOGD("Dropping event because the pointer is not down or we previously " - "dropped the pointer down event in display %" PRId32, displayId); + "dropped the pointer down event in display %" PRId32, + displayId); #endif injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; } // Check whether touches should slip outside of the current foreground window. - if (maskedAction == AMOTION_EVENT_ACTION_MOVE - && entry->pointerCount == 1 - && mTempTouchState.isSlippery()) { + if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry->pointerCount == 1 && + mTempTouchState.isSlippery()) { int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); @@ -1421,26 +1403,25 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, mTempTouchState.getFirstForegroundWindowHandle(); sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y); - if (oldTouchedWindowHandle != newTouchedWindowHandle - && oldTouchedWindowHandle != nullptr - && newTouchedWindowHandle != nullptr) { + if (oldTouchedWindowHandle != newTouchedWindowHandle && + oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) { #if DEBUG_FOCUS ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32, - oldTouchedWindowHandle->getName().c_str(), - newTouchedWindowHandle->getName().c_str(), - displayId); + oldTouchedWindowHandle->getName().c_str(), + newTouchedWindowHandle->getName().c_str(), displayId); #endif // Make a slippery exit from the old window. mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, BitSet32(0)); + InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, + BitSet32(0)); // Make a slippery entrance into the new window. if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { isSplit = true; } - int32_t targetFlags = InputTarget::FLAG_FOREGROUND - | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER; + int32_t targetFlags = + InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER; if (isSplit) { targetFlags |= InputTarget::FLAG_SPLIT; } @@ -1462,20 +1443,22 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (mLastHoverWindowHandle != nullptr) { #if DEBUG_HOVER ALOGD("Sending hover exit event to window %s.", - mLastHoverWindowHandle->getName().c_str()); + mLastHoverWindowHandle->getName().c_str()); #endif mTempTouchState.addOrUpdateWindow(mLastHoverWindowHandle, - InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0)); + InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, + BitSet32(0)); } // Let the new window know that the hover sequence is starting. if (newHoverWindowHandle != nullptr) { #if DEBUG_HOVER ALOGD("Sending hover enter event to window %s.", - newHoverWindowHandle->getName().c_str()); + newHoverWindowHandle->getName().c_str()); #endif mTempTouchState.addOrUpdateWindow(newHoverWindowHandle, - InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, BitSet32(0)); + InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER, + BitSet32(0)); } } @@ -1486,8 +1469,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { haveForegroundWindow = true; - if (! checkInjectionPermission(touchedWindow.windowHandle, - entry->injectionState)) { + if (!checkInjectionPermission(touchedWindow.windowHandle, entry->injectionState)) { injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; injectionPermission = INJECTION_PERMISSION_DENIED; goto Failed; @@ -1497,8 +1479,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty(); if (!haveForegroundWindow && !hasGestureMonitor) { #if DEBUG_FOCUS - ALOGD("Dropping event because there is no touched foreground window in display %" - PRId32 " or gesture monitor to receive it.", displayId); + ALOGD("Dropping event because there is no touched foreground window in display %" PRId32 + " or gesture monitor to receive it.", + displayId); #endif injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; @@ -1520,7 +1503,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, sp<InputWindowHandle> inputWindowHandle = touchedWindow.windowHandle; if (inputWindowHandle->getInfo()->ownerUid != foregroundWindowUid) { mTempTouchState.addOrUpdateWindow(inputWindowHandle, - InputTarget::FLAG_ZERO_COORDS, BitSet32(0)); + InputTarget::FLAG_ZERO_COORDS, + BitSet32(0)); } } } @@ -1531,11 +1515,13 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { // Check whether the window is ready for more input. - std::string reason = checkWindowReadyForMoreInputLocked(currentTime, - touchedWindow.windowHandle, entry, "touched"); + std::string reason = + checkWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, + entry, "touched"); if (!reason.empty()) { - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - nullptr, touchedWindow.windowHandle, nextWakeupTime, reason.c_str()); + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, nullptr, + touchedWindow.windowHandle, + nextWakeupTime, reason.c_str()); goto Unresponsive; } } @@ -1555,14 +1541,15 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, getWindowHandlesLocked(displayId); for (const sp<InputWindowHandle>& windowHandle : windowHandles) { const InputWindowInfo* info = windowHandle->getInfo(); - if (info->displayId == displayId - && windowHandle->getInfo()->layoutParamsType - == InputWindowInfo::TYPE_WALLPAPER) { - mTempTouchState.addOrUpdateWindow(windowHandle, - InputTarget::FLAG_WINDOW_IS_OBSCURED - | InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED - | InputTarget::FLAG_DISPATCH_AS_IS, - BitSet32(0)); + if (info->displayId == displayId && + windowHandle->getInfo()->layoutParamsType == InputWindowInfo::TYPE_WALLPAPER) { + mTempTouchState + .addOrUpdateWindow(windowHandle, + InputTarget::FLAG_WINDOW_IS_OBSCURED | + InputTarget:: + FLAG_WINDOW_IS_PARTIALLY_OBSCURED | + InputTarget::FLAG_DISPATCH_AS_IS, + BitSet32(0)); } } } @@ -1573,12 +1560,12 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, for (const TouchedWindow& touchedWindow : mTempTouchState.windows) { addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, - touchedWindow.pointerIds, inputTargets); + touchedWindow.pointerIds, inputTargets); } for (const TouchedMonitor& touchedMonitor : mTempTouchState.gestureMonitors) { addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset, - touchedMonitor.yOffset, inputTargets); + touchedMonitor.yOffset, inputTargets); } // Drop the outside or hover touch windows since we will not care about them @@ -1614,14 +1601,14 @@ Failed: *outConflictingPointerActions = true; } mTempTouchState.reset(); - if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER - || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { + if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || + maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { mTempTouchState.deviceId = entry->deviceId; mTempTouchState.source = entry->source; mTempTouchState.displayId = displayId; } - } else if (maskedAction == AMOTION_EVENT_ACTION_UP - || maskedAction == AMOTION_EVENT_ACTION_CANCEL) { + } else if (maskedAction == AMOTION_EVENT_ACTION_UP || + maskedAction == AMOTION_EVENT_ACTION_CANCEL) { // All pointers up or canceled. mTempTouchState.reset(); } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { @@ -1638,7 +1625,7 @@ Failed: int32_t pointerIndex = getMotionEventActionPointerIndex(action); uint32_t pointerId = entry->pointerProperties[pointerIndex].id; - for (size_t i = 0; i < mTempTouchState.windows.size(); ) { + for (size_t i = 0; i < mTempTouchState.windows.size();) { TouchedWindow& touchedWindow = mTempTouchState.windows[i]; if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) { touchedWindow.pointerIds.clearBit(pointerId); @@ -1683,14 +1670,15 @@ Unresponsive: updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); #if DEBUG_FOCUS ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); + "timeSpentWaitingForApplication=%0.1fms", + injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); #endif return injectionResult; } void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, - int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) { + int32_t targetFlags, BitSet32 pointerIds, + std::vector<InputTarget>& inputTargets) { sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken()); if (inputChannel == nullptr) { ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str()); @@ -1701,8 +1689,8 @@ void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowH InputTarget target; target.inputChannel = inputChannel; target.flags = targetFlags; - target.xOffset = - windowInfo->frameLeft; - target.yOffset = - windowInfo->frameTop; + target.xOffset = -windowInfo->frameLeft; + target.yOffset = -windowInfo->frameTop; target.globalScaleFactor = windowInfo->globalScaleFactor; target.windowXScale = windowInfo->windowXScale; target.windowYScale = windowInfo->windowYScale; @@ -1711,8 +1699,8 @@ void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowH } void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, - int32_t displayId, float xOffset, float yOffset) { - + int32_t displayId, float xOffset, + float yOffset) { std::unordered_map<int32_t, std::vector<Monitor>>::const_iterator it = mGlobalMonitorsByDisplay.find(displayId); @@ -1724,8 +1712,9 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& } } -void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, - float xOffset, float yOffset, std::vector<InputTarget>& inputTargets) { +void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xOffset, + float yOffset, + std::vector<InputTarget>& inputTargets) { InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; @@ -1737,28 +1726,27 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, } bool InputDispatcher::checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, - const InjectionState* injectionState) { - if (injectionState - && (windowHandle == nullptr - || windowHandle->getInfo()->ownerUid != injectionState->injectorUid) - && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) { + const InjectionState* injectionState) { + if (injectionState && + (windowHandle == nullptr || + windowHandle->getInfo()->ownerUid != injectionState->injectorUid) && + !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) { if (windowHandle != nullptr) { ALOGW("Permission denied: injecting event from pid %d uid %d to window %s " - "owned by uid %d", - injectionState->injectorPid, injectionState->injectorUid, - windowHandle->getName().c_str(), - windowHandle->getInfo()->ownerUid); + "owned by uid %d", + injectionState->injectorPid, injectionState->injectorUid, + windowHandle->getName().c_str(), windowHandle->getInfo()->ownerUid); } else { ALOGW("Permission denied: injecting event from pid %d uid %d", - injectionState->injectorPid, injectionState->injectorUid); + injectionState->injectorPid, injectionState->injectorUid); } return false; } return true; } -bool InputDispatcher::isWindowObscuredAtPointLocked( - const sp<InputWindowHandle>& windowHandle, int32_t x, int32_t y) const { +bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, + int32_t x, int32_t y) const { int32_t displayId = windowHandle->getInfo()->displayId; const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); for (const sp<InputWindowHandle>& otherHandle : windowHandles) { @@ -1767,16 +1755,14 @@ bool InputDispatcher::isWindowObscuredAtPointLocked( } const InputWindowInfo* otherInfo = otherHandle->getInfo(); - if (otherInfo->displayId == displayId - && otherInfo->visible && !otherInfo->isTrustedOverlay() - && otherInfo->frameContainsPoint(x, y)) { + if (otherInfo->displayId == displayId && otherInfo->visible && + !otherInfo->isTrustedOverlay() && otherInfo->frameContainsPoint(x, y)) { return true; } } return false; } - bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const { int32_t displayId = windowHandle->getInfo()->displayId; const std::vector<sp<InputWindowHandle>> windowHandles = getWindowHandlesLocked(displayId); @@ -1787,45 +1773,47 @@ bool InputDispatcher::isWindowObscuredLocked(const sp<InputWindowHandle>& window } const InputWindowInfo* otherInfo = otherHandle->getInfo(); - if (otherInfo->displayId == displayId - && otherInfo->visible && !otherInfo->isTrustedOverlay() - && otherInfo->overlaps(windowInfo)) { + if (otherInfo->displayId == displayId && otherInfo->visible && + !otherInfo->isTrustedOverlay() && otherInfo->overlaps(windowInfo)) { return true; } } return false; } -std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime, - const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry, - const char* targetType) { +std::string InputDispatcher::checkWindowReadyForMoreInputLocked( + nsecs_t currentTime, const sp<InputWindowHandle>& windowHandle, + const EventEntry* eventEntry, const char* targetType) { // If the window is paused then keep waiting. if (windowHandle->getInfo()->paused) { return StringPrintf("Waiting because the %s window is paused.", targetType); } // If the window's connection is not registered then keep waiting. - ssize_t connectionIndex = getConnectionIndexLocked( - getInputChannelLocked(windowHandle->getToken())); + ssize_t connectionIndex = + getConnectionIndexLocked(getInputChannelLocked(windowHandle->getToken())); if (connectionIndex < 0) { return StringPrintf("Waiting because the %s window's input channel is not " - "registered with the input dispatcher. The window may be in the process " - "of being removed.", targetType); + "registered with the input dispatcher. The window may be in the " + "process " + "of being removed.", + targetType); } // If the connection is dead then keep waiting. sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); if (connection->status != Connection::STATUS_NORMAL) { return StringPrintf("Waiting because the %s window's input connection is %s." - "The window may be in the process of being removed.", targetType, - connection->getStatusLabel()); + "The window may be in the process of being removed.", + targetType, connection->getStatusLabel()); } // If the connection is backed up then keep waiting. if (connection->inputPublisherBlocked) { return StringPrintf("Waiting because the %s window's input channel is full. " - "Outbound queue length: %d. Wait queue length: %d.", - targetType, connection->outboundQueue.count(), connection->waitQueue.count()); + "Outbound queue length: %d. Wait queue length: %d.", + targetType, connection->outboundQueue.count(), + connection->waitQueue.count()); } // Ensure that the dispatch queues aren't too far backed up for this event. @@ -1843,9 +1831,11 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentT // prior input events. if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) { return StringPrintf("Waiting to send key event because the %s window has not " - "finished processing all of the input events that were previously " - "delivered to it. Outbound queue length: %d. Wait queue length: %d.", - targetType, connection->outboundQueue.count(), connection->waitQueue.count()); + "finished processing all of the input events that were previously " + "delivered to it. Outbound queue length: %d. Wait queue length: " + "%d.", + targetType, connection->outboundQueue.count(), + connection->waitQueue.count()); } } else { // Touch events can always be sent to a window immediately because the user intended @@ -1863,15 +1853,17 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentT // The one case where we pause input event delivery is when the wait queue is piling // up with lots of events because the application is not responding. // This condition ensures that ANRs are detected reliably. - if (!connection->waitQueue.isEmpty() - && currentTime >= connection->waitQueue.head->deliveryTime - + STREAM_AHEAD_EVENT_TIMEOUT) { + if (!connection->waitQueue.isEmpty() && + currentTime >= connection->waitQueue.head->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) { return StringPrintf("Waiting to send non-key event because the %s window has not " - "finished processing certain input events that were delivered to it over " - "%0.1fms ago. Wait queue length: %d. Wait queue head age: %0.1fms.", - targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f, - connection->waitQueue.count(), - (currentTime - connection->waitQueue.head->deliveryTime) * 0.000001f); + "finished processing certain input events that were delivered to " + "it over " + "%0.1fms ago. Wait queue length: %d. Wait queue head age: " + "%0.1fms.", + targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f, + connection->waitQueue.count(), + (currentTime - connection->waitQueue.head->deliveryTime) * + 0.000001f); } } return ""; @@ -1912,50 +1904,50 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { int32_t eventType = USER_ACTIVITY_EVENT_OTHER; switch (eventEntry->type) { - case EventEntry::TYPE_MOTION: { - const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry); - if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) { - return; - } + case EventEntry::TYPE_MOTION: { + const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry); + if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) { + return; + } - if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) { - eventType = USER_ACTIVITY_EVENT_TOUCH; + if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) { + eventType = USER_ACTIVITY_EVENT_TOUCH; + } + break; } - break; - } - case EventEntry::TYPE_KEY: { - const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry); - if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) { - return; + case EventEntry::TYPE_KEY: { + const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry); + if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) { + return; + } + eventType = USER_ACTIVITY_EVENT_BUTTON; + break; } - eventType = USER_ACTIVITY_EVENT_BUTTON; - break; - } } - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doPokeUserActivityLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doPokeUserActivityLockedInterruptible); commandEntry->eventTime = eventEntry->eventTime; commandEntry->userActivityEventType = eventType; } void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { + const sp<Connection>& connection, + EventEntry* eventEntry, + const InputTarget* inputTarget) { if (ATRACE_ENABLED()) { - std::string message = StringPrintf( - "prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", - connection->getInputChannelName().c_str(), eventEntry->sequenceNum); + std::string message = + StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", + connection->getInputChannelName().c_str(), eventEntry->sequenceNum); ATRACE_NAME(message.c_str()); } #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " - "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " - "windowScaleFactor=(%f, %f), pointerIds=0x%x", - connection->getInputChannelName().c_str(), inputTarget->flags, - inputTarget->xOffset, inputTarget->yOffset, - inputTarget->globalScaleFactor, - inputTarget->windowXScale, inputTarget->windowYScale, - inputTarget->pointerIds.value); + "xOffset=%f, yOffset=%f, globalScaleFactor=%f, " + "windowScaleFactor=(%f, %f), pointerIds=0x%x", + connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset, + inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale, + inputTarget->windowYScale, inputTarget->pointerIds.value); #endif // Skip this event if the connection status is not normal. @@ -1963,7 +1955,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, if (connection->status != Connection::STATUS_NORMAL) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Dropping event because the channel status is %s", - connection->getInputChannelName().c_str(), connection->getStatusLabel()); + connection->getInputChannelName().c_str(), connection->getStatusLabel()); #endif return; } @@ -1974,18 +1966,16 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry); if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) { - MotionEntry* splitMotionEntry = splitMotionEvent( - originalMotionEntry, inputTarget->pointerIds); + MotionEntry* splitMotionEntry = + splitMotionEvent(originalMotionEntry, inputTarget->pointerIds); if (!splitMotionEntry) { return; // split event was dropped } #if DEBUG_FOCUS - ALOGD("channel '%s' ~ Split motion event.", - connection->getInputChannelName().c_str()); + ALOGD("channel '%s' ~ Split motion event.", connection->getInputChannelName().c_str()); logOutboundMotionDetails(" ", splitMotionEntry); #endif - enqueueDispatchEntriesLocked(currentTime, connection, - splitMotionEntry, inputTarget); + enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget); splitMotionEntry->release(); return; } @@ -1996,11 +1986,14 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, - const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { + const sp<Connection>& connection, + EventEntry* eventEntry, + const InputTarget* inputTarget) { if (ATRACE_ENABLED()) { - std::string message = StringPrintf( - "enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")", - connection->getInputChannelName().c_str(), eventEntry->sequenceNum); + std::string message = + StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32 + ")", + connection->getInputChannelName().c_str(), eventEntry->sequenceNum); ATRACE_NAME(message.c_str()); } @@ -2008,17 +2001,17 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, // Enqueue dispatch entries for the requested modes. enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); + InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_OUTSIDE); + InputTarget::FLAG_DISPATCH_AS_OUTSIDE); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER); + InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_IS); + InputTarget::FLAG_DISPATCH_AS_IS); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT); + InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); + InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty && !connection->outboundQueue.isEmpty()) { @@ -2026,14 +2019,14 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, } } -void InputDispatcher::enqueueDispatchEntryLocked( - const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, - int32_t dispatchMode) { +void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection, + EventEntry* eventEntry, + const InputTarget* inputTarget, + int32_t dispatchMode) { if (ATRACE_ENABLED()) { - std::string message = StringPrintf( - "enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)", - connection->getInputChannelName().c_str(), - dispatchModeToString(dispatchMode).c_str()); + std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)", + connection->getInputChannelName().c_str(), + dispatchModeToString(dispatchMode).c_str()); ATRACE_NAME(message.c_str()); } int32_t inputTargetFlags = inputTarget->flags; @@ -2044,78 +2037,81 @@ void InputDispatcher::enqueueDispatchEntryLocked( // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. - DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref - inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, - inputTarget->globalScaleFactor, inputTarget->windowXScale, - inputTarget->windowYScale); + DispatchEntry* dispatchEntry = + new DispatchEntry(eventEntry, // increments ref + inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, + inputTarget->globalScaleFactor, inputTarget->windowXScale, + inputTarget->windowYScale); // Apply target flags and update the connection's input state. switch (eventEntry->type) { - case EventEntry::TYPE_KEY: { - KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); - dispatchEntry->resolvedAction = keyEntry->action; - dispatchEntry->resolvedFlags = keyEntry->flags; + case EventEntry::TYPE_KEY: { + KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); + dispatchEntry->resolvedAction = keyEntry->action; + dispatchEntry->resolvedFlags = keyEntry->flags; - if (!connection->inputState.trackKey(keyEntry, - dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { + if (!connection->inputState.trackKey(keyEntry, dispatchEntry->resolvedAction, + dispatchEntry->resolvedFlags)) { #if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", - connection->getInputChannelName().c_str()); + ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", + connection->getInputChannelName().c_str()); #endif - delete dispatchEntry; - return; // skip the inconsistent event - } - break; - } - - case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); - if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) { - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL; - } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) { - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN; - } else { - dispatchEntry->resolvedAction = motionEntry->action; + delete dispatchEntry; + return; // skip the inconsistent event + } + break; } - if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE - && !connection->inputState.isHovering( - motionEntry->deviceId, motionEntry->source, motionEntry->displayId)) { + + case EventEntry::TYPE_MOTION: { + MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); + if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL; + } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) { + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN; + } else { + dispatchEntry->resolvedAction = motionEntry->action; + } + if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE && + !connection->inputState.isHovering(motionEntry->deviceId, motionEntry->source, + motionEntry->displayId)) { #if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event", - connection->getInputChannelName().c_str()); + ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter " + "event", + connection->getInputChannelName().c_str()); #endif - dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; - } + dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER; + } - dispatchEntry->resolvedFlags = motionEntry->flags; - if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { - dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - } - if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) { - dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; - } + dispatchEntry->resolvedFlags = motionEntry->flags; + if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { + dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + } + if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED) { + dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + } - if (!connection->inputState.trackMotion(motionEntry, - dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) { + if (!connection->inputState.trackMotion(motionEntry, dispatchEntry->resolvedAction, + dispatchEntry->resolvedFlags)) { #if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event", - connection->getInputChannelName().c_str()); + ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion " + "event", + connection->getInputChannelName().c_str()); #endif - delete dispatchEntry; - return; // skip the inconsistent event - } + delete dispatchEntry; + return; // skip the inconsistent event + } - dispatchPointerDownOutsideFocus(motionEntry->source, - dispatchEntry->resolvedAction, inputTarget->inputChannel->getToken()); + dispatchPointerDownOutsideFocus(motionEntry->source, dispatchEntry->resolvedAction, + inputTarget->inputChannel->getToken()); - break; - } + break; + } } // Remember that we are waiting for this dispatch to complete. @@ -2126,11 +2122,10 @@ void InputDispatcher::enqueueDispatchEntryLocked( // Enqueue the dispatch entry. connection->outboundQueue.enqueueAtTail(dispatchEntry); traceOutboundQueueLength(connection); - } void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t action, - const sp<IBinder>& newToken) { + const sp<IBinder>& newToken) { int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; uint32_t maskedSource = source & AINPUT_SOURCE_CLASS_MASK; if (maskedSource != AINPUT_SOURCE_CLASS_POINTER || maskedAction != AMOTION_EVENT_ACTION_DOWN) { @@ -2151,25 +2146,24 @@ void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t a return; } - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); commandEntry->newToken = newToken; } void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection) { + const sp<Connection>& connection) { if (ATRACE_ENABLED()) { std::string message = StringPrintf("startDispatchCycleLocked(inputChannel=%s)", - connection->getInputChannelName().c_str()); + connection->getInputChannelName().c_str()); ATRACE_NAME(message.c_str()); } #if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ startDispatchCycle", - connection->getInputChannelName().c_str()); + ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str()); #endif - while (connection->status == Connection::STATUS_NORMAL - && !connection->outboundQueue.isEmpty()) { + while (connection->status == Connection::STATUS_NORMAL && + !connection->outboundQueue.isEmpty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.head; dispatchEntry->deliveryTime = currentTime; @@ -2177,70 +2171,77 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, status_t status; EventEntry* eventEntry = dispatchEntry->eventEntry; switch (eventEntry->type) { - case EventEntry::TYPE_KEY: { - KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); - - // Publish the key event. - status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq, - keyEntry->deviceId, keyEntry->source, keyEntry->displayId, - dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, - keyEntry->keyCode, keyEntry->scanCode, - keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, - keyEntry->eventTime); - break; - } - - case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); + case EventEntry::TYPE_KEY: { + KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); + + // Publish the key event. + status = connection->inputPublisher + .publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId, + keyEntry->source, keyEntry->displayId, + dispatchEntry->resolvedAction, + dispatchEntry->resolvedFlags, keyEntry->keyCode, + keyEntry->scanCode, keyEntry->metaState, + keyEntry->repeatCount, keyEntry->downTime, + keyEntry->eventTime); + break; + } - PointerCoords scaledCoords[MAX_POINTERS]; - const PointerCoords* usingCoords = motionEntry->pointerCoords; - - // Set the X and Y offset depending on the input source. - float xOffset, yOffset; - if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) - && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { - float globalScaleFactor = dispatchEntry->globalScaleFactor; - float wxs = dispatchEntry->windowXScale; - float wys = dispatchEntry->windowYScale; - xOffset = dispatchEntry->xOffset * wxs; - yOffset = dispatchEntry->yOffset * wys; - if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) { - for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { - scaledCoords[i] = motionEntry->pointerCoords[i]; - scaledCoords[i].scale(globalScaleFactor, wxs, wys); + case EventEntry::TYPE_MOTION: { + MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); + + PointerCoords scaledCoords[MAX_POINTERS]; + const PointerCoords* usingCoords = motionEntry->pointerCoords; + + // Set the X and Y offset depending on the input source. + float xOffset, yOffset; + if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && + !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { + float globalScaleFactor = dispatchEntry->globalScaleFactor; + float wxs = dispatchEntry->windowXScale; + float wys = dispatchEntry->windowYScale; + xOffset = dispatchEntry->xOffset * wxs; + yOffset = dispatchEntry->yOffset * wys; + if (wxs != 1.0f || wys != 1.0f || globalScaleFactor != 1.0f) { + for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { + scaledCoords[i] = motionEntry->pointerCoords[i]; + scaledCoords[i].scale(globalScaleFactor, wxs, wys); + } + usingCoords = scaledCoords; } - usingCoords = scaledCoords; - } - } else { - xOffset = 0.0f; - yOffset = 0.0f; + } else { + xOffset = 0.0f; + yOffset = 0.0f; - // We don't want the dispatch target to know. - if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { - for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { - scaledCoords[i].clear(); + // We don't want the dispatch target to know. + if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { + for (uint32_t i = 0; i < motionEntry->pointerCount; i++) { + scaledCoords[i].clear(); + } + usingCoords = scaledCoords; } - usingCoords = scaledCoords; } - } - // Publish the motion event. - status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq, - motionEntry->deviceId, motionEntry->source, motionEntry->displayId, - dispatchEntry->resolvedAction, motionEntry->actionButton, - dispatchEntry->resolvedFlags, motionEntry->edgeFlags, - motionEntry->metaState, motionEntry->buttonState, motionEntry->classification, - xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, - motionEntry->downTime, motionEntry->eventTime, - motionEntry->pointerCount, motionEntry->pointerProperties, - usingCoords); - break; - } + // Publish the motion event. + status = connection->inputPublisher + .publishMotionEvent(dispatchEntry->seq, motionEntry->deviceId, + motionEntry->source, motionEntry->displayId, + dispatchEntry->resolvedAction, + motionEntry->actionButton, + dispatchEntry->resolvedFlags, + motionEntry->edgeFlags, motionEntry->metaState, + motionEntry->buttonState, + motionEntry->classification, xOffset, yOffset, + motionEntry->xPrecision, + motionEntry->yPrecision, motionEntry->downTime, + motionEntry->eventTime, + motionEntry->pointerCount, + motionEntry->pointerProperties, usingCoords); + break; + } - default: - ALOG_ASSERT(false); - return; + default: + ALOG_ASSERT(false); + return; } // Check the result. @@ -2248,24 +2249,25 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, if (status == WOULD_BLOCK) { if (connection->waitQueue.isEmpty()) { ALOGE("channel '%s' ~ Could not publish event because the pipe is full. " - "This is unexpected because the wait queue is empty, so the pipe " - "should be empty and we shouldn't have any problems writing an " - "event to it, status=%d", connection->getInputChannelName().c_str(), - status); + "This is unexpected because the wait queue is empty, so the pipe " + "should be empty and we shouldn't have any problems writing an " + "event to it, status=%d", + connection->getInputChannelName().c_str(), status); abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); } else { // Pipe is full and we are waiting for the app to finish process some events // before sending more events to it. #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ Could not publish event because the pipe is full, " - "waiting for the application to catch up", - connection->getInputChannelName().c_str()); + "waiting for the application to catch up", + connection->getInputChannelName().c_str()); #endif connection->inputPublisherBlocked = true; } } else { ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, " - "status=%d", connection->getInputChannelName().c_str(), status); + "status=%d", + connection->getInputChannelName().c_str(), status); abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); } return; @@ -2280,16 +2282,17 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection, uint32_t seq, bool handled) { + const sp<Connection>& connection, uint32_t seq, + bool handled) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s", - connection->getInputChannelName().c_str(), seq, toString(handled)); + connection->getInputChannelName().c_str(), seq, toString(handled)); #endif connection->inputPublisherBlocked = false; - if (connection->status == Connection::STATUS_BROKEN - || connection->status == Connection::STATUS_ZOMBIE) { + if (connection->status == Connection::STATUS_BROKEN || + connection->status == Connection::STATUS_ZOMBIE) { return; } @@ -2298,10 +2301,11 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, } void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection, bool notify) { + const sp<Connection>& connection, + bool notify) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s", - connection->getInputChannelName().c_str(), toString(notify)); + connection->getInputChannelName().c_str(), toString(notify)); #endif // Clear the dispatch queues. @@ -2345,7 +2349,8 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd); if (connectionIndex < 0) { ALOGE("Received spurious receive callback for unknown input channel. " - "fd=%d, events=0x%x", fd, events); + "fd=%d, events=0x%x", + fd, events); return 0; // remove the callback } @@ -2354,7 +2359,8 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) { if (!(events & ALOOPER_EVENT_INPUT)) { ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " - "events=0x%x", connection->getInputChannelName().c_str(), events); + "events=0x%x", + connection->getInputChannelName().c_str(), events); return 1; } @@ -2381,7 +2387,7 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { notify = status != DEAD_OBJECT || !connection->monitor; if (notify) { ALOGE("channel '%s' ~ Failed to receive finished signal. status=%d", - connection->getInputChannelName().c_str(), status); + connection->getInputChannelName().c_str(), status); } } else { // Monitor channels are never explicitly unregistered. @@ -2390,25 +2396,25 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { notify = !connection->monitor; if (notify) { ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. " - "events=0x%x", connection->getInputChannelName().c_str(), events); + "events=0x%x", + connection->getInputChannelName().c_str(), events); } } // Unregister the channel. d->unregisterInputChannelLocked(connection->inputChannel, notify); return 0; // remove the callback - } // release lock + } // release lock } -void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked ( +void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( const CancelationOptions& options) { for (size_t i = 0; i < mConnectionsByFd.size(); i++) { - synthesizeCancelationEventsForConnectionLocked( - mConnectionsByFd.valueAt(i), options); + synthesizeCancelationEventsForConnectionLocked(mConnectionsByFd.valueAt(i), options); } } -void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked ( +void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( const CancelationOptions& options) { synthesizeCancelationEventsForMonitorsLocked(options, mGlobalMonitorsByDisplay); synthesizeCancelationEventsForMonitorsLocked(options, mGestureMonitorsByDisplay); @@ -2429,8 +2435,7 @@ void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( const sp<InputChannel>& channel, const CancelationOptions& options) { ssize_t index = getConnectionIndexLocked(channel); if (index >= 0) { - synthesizeCancelationEventsForConnectionLocked( - mConnectionsByFd.valueAt(index), options); + synthesizeCancelationEventsForConnectionLocked(mConnectionsByFd.valueAt(index), options); } } @@ -2443,32 +2448,31 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( nsecs_t currentTime = now(); std::vector<EventEntry*> cancelationEvents; - connection->inputState.synthesizeCancelationEvents(currentTime, - cancelationEvents, options); + connection->inputState.synthesizeCancelationEvents(currentTime, cancelationEvents, options); if (!cancelationEvents.empty()) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("channel '%s' ~ Synthesized %zu cancelation events to bring channel back in sync " - "with reality: %s, mode=%d.", - connection->getInputChannelName().c_str(), cancelationEvents.size(), - options.reason, options.mode); + "with reality: %s, mode=%d.", + connection->getInputChannelName().c_str(), cancelationEvents.size(), options.reason, + options.mode); #endif for (size_t i = 0; i < cancelationEvents.size(); i++) { EventEntry* cancelationEventEntry = cancelationEvents[i]; switch (cancelationEventEntry->type) { - case EventEntry::TYPE_KEY: - logOutboundKeyDetails("cancel - ", - static_cast<KeyEntry*>(cancelationEventEntry)); - break; - case EventEntry::TYPE_MOTION: - logOutboundMotionDetails("cancel - ", - static_cast<MotionEntry*>(cancelationEventEntry)); - break; + case EventEntry::TYPE_KEY: + logOutboundKeyDetails("cancel - ", + static_cast<KeyEntry*>(cancelationEventEntry)); + break; + case EventEntry::TYPE_MOTION: + logOutboundMotionDetails("cancel - ", + static_cast<MotionEntry*>(cancelationEventEntry)); + break; } InputTarget target; - sp<InputWindowHandle> windowHandle = getWindowHandleLocked( - connection->inputChannel->getToken()); + sp<InputWindowHandle> windowHandle = + getWindowHandleLocked(connection->inputChannel->getToken()); if (windowHandle != nullptr) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); target.xOffset = -windowInfo->frameLeft; @@ -2485,7 +2489,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( target.flags = InputTarget::FLAG_DISPATCH_AS_IS; enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref - &target, InputTarget::FLAG_DISPATCH_AS_IS); + &target, InputTarget::FLAG_DISPATCH_AS_IS); cancelationEventEntry->release(); } @@ -2494,8 +2498,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } } -InputDispatcher::MotionEntry* -InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) { +MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, + BitSet32 pointerIds) { ALOG_ASSERT(pointerIds.value != 0); uint32_t splitPointerIndexMap[MAX_POINTERS]; @@ -2506,7 +2510,7 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet uint32_t splitPointerCount = 0; for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount; - originalPointerIndex++) { + originalPointerIndex++) { const PointerProperties& pointerProperties = originalMotionEntry->pointerProperties[originalPointerIndex]; uint32_t pointerId = uint32_t(pointerProperties.id); @@ -2526,16 +2530,16 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet // or ACTION_POINTER_DOWN events that caused us to decide to split the pointers // in this way. ALOGW("Dropping split motion event because the pointer count is %d but " - "we expected there to be %d pointers. This probably means we received " - "a broken sequence of pointer ids from the input device.", - splitPointerCount, pointerIds.count()); + "we expected there to be %d pointers. This probably means we received " + "a broken sequence of pointer ids from the input device.", + splitPointerCount, pointerIds.count()); return nullptr; } int32_t action = originalMotionEntry->action; int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; - if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN - || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { + if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN || + maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { int32_t originalPointerIndex = getMotionEventActionPointerIndex(action); const PointerProperties& pointerProperties = originalMotionEntry->pointerProperties[originalPointerIndex]; @@ -2544,15 +2548,16 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet if (pointerIds.count() == 1) { // The first/last pointer went down/up. action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN - ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; + ? AMOTION_EVENT_ACTION_DOWN + : AMOTION_EVENT_ACTION_UP; } else { // A secondary pointer went down/up. uint32_t splitPointerIndex = 0; while (pointerId != uint32_t(splitPointerProperties[splitPointerIndex].id)) { splitPointerIndex += 1; } - action = maskedAction | (splitPointerIndex - << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + action = maskedAction | + (splitPointerIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); } } else { // An unrelated pointer changed. @@ -2560,24 +2565,16 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet } } - MotionEntry* splitMotionEntry = new MotionEntry( - originalMotionEntry->sequenceNum, - originalMotionEntry->eventTime, - originalMotionEntry->deviceId, - originalMotionEntry->source, - originalMotionEntry->displayId, - originalMotionEntry->policyFlags, - action, - originalMotionEntry->actionButton, - originalMotionEntry->flags, - originalMotionEntry->metaState, - originalMotionEntry->buttonState, - originalMotionEntry->classification, - originalMotionEntry->edgeFlags, - originalMotionEntry->xPrecision, - originalMotionEntry->yPrecision, - originalMotionEntry->downTime, - splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); + MotionEntry* splitMotionEntry = + new MotionEntry(originalMotionEntry->sequenceNum, originalMotionEntry->eventTime, + originalMotionEntry->deviceId, originalMotionEntry->source, + originalMotionEntry->displayId, originalMotionEntry->policyFlags, + action, originalMotionEntry->actionButton, originalMotionEntry->flags, + originalMotionEntry->metaState, originalMotionEntry->buttonState, + originalMotionEntry->classification, originalMotionEntry->edgeFlags, + originalMotionEntry->xPrecision, originalMotionEntry->yPrecision, + originalMotionEntry->downTime, splitPointerCount, + splitPointerProperties, splitPointerCoords, 0, 0); if (originalMotionEntry->injectionState) { splitMotionEntry->injectionState = originalMotionEntry->injectionState; @@ -2613,7 +2610,7 @@ void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChange * This will potentially overwrite keyCode and metaState. */ void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, - int32_t& keyCode, int32_t& metaState) { + int32_t& keyCode, int32_t& metaState) { if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) { int32_t newKeyCode = AKEYCODE_UNKNOWN; if (keyCode == AKEYCODE_DEL) { @@ -2645,12 +2642,12 @@ void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int3 void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyKey - eventTime=%" PRId64 - ", deviceId=%d, source=0x%x, displayId=%" PRId32 "policyFlags=0x%x, action=0x%x, " - "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64, - args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, - args->action, args->flags, args->keyCode, args->scanCode, - args->metaState, args->downTime); + ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 + "policyFlags=0x%x, action=0x%x, " + "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64, + args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, + args->action, args->flags, args->keyCode, args->scanCode, args->metaState, + args->downTime); #endif if (!validateKeyEvent(args->action)) { return; @@ -2676,15 +2673,14 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { accelerateMetaShortcuts(args->deviceId, args->action, keyCode, metaState); KeyEvent event; - event.initialize(args->deviceId, args->source, args->displayId, args->action, - flags, keyCode, args->scanCode, metaState, repeatCount, - args->downTime, args->eventTime); + event.initialize(args->deviceId, args->source, args->displayId, args->action, flags, keyCode, + args->scanCode, metaState, repeatCount, args->downTime, args->eventTime); android::base::Timer t; mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms", - std::to_string(t.duration().count()).c_str()); + std::to_string(t.duration().count()).c_str()); } bool needWake; @@ -2702,10 +2698,10 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { mLock.lock(); } - KeyEntry* newEntry = new KeyEntry(args->sequenceNum, args->eventTime, - args->deviceId, args->source, args->displayId, policyFlags, - args->action, flags, keyCode, args->scanCode, - metaState, repeatCount, args->downTime); + KeyEntry* newEntry = + new KeyEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source, + args->displayId, policyFlags, args->action, flags, keyCode, + args->scanCode, metaState, repeatCount, args->downTime); needWake = enqueueInboundEventLocked(newEntry); mLock.unlock(); @@ -2723,32 +2719,31 @@ bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 - ", policyFlags=0x%x, " - "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x," - "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, - args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, - args->action, args->actionButton, args->flags, args->metaState, args->buttonState, - args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime); + ", policyFlags=0x%x, " + "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x," + "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, + args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, + args->action, args->actionButton, args->flags, args->metaState, args->buttonState, + args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime); for (uint32_t i = 0; i < args->pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " - "x=%f, y=%f, pressure=%f, size=%f, " - "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " - "orientation=%f", - i, args->pointerProperties[i].id, - args->pointerProperties[i].toolType, - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + "x=%f, y=%f, pressure=%f, size=%f, " + "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " + "orientation=%f", + i, args->pointerProperties[i].id, args->pointerProperties[i].toolType, + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } #endif - if (!validateMotionEvent(args->action, args->actionButton, - args->pointerCount, args->pointerProperties)) { + if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount, + args->pointerProperties)) { return; } @@ -2759,7 +2754,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mPolicy->interceptMotionBeforeQueueing(args->displayId, args->eventTime, /*byref*/ policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms", - std::to_string(t.duration().count()).c_str()); + std::to_string(t.duration().count()).c_str()); } bool needWake; @@ -2770,12 +2765,11 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - event.initialize(args->deviceId, args->source, args->displayId, - args->action, args->actionButton, - args->flags, args->edgeFlags, args->metaState, args->buttonState, - args->classification, 0, 0, args->xPrecision, args->yPrecision, - args->downTime, args->eventTime, - args->pointerCount, args->pointerProperties, args->pointerCoords); + event.initialize(args->deviceId, args->source, args->displayId, args->action, + args->actionButton, args->flags, args->edgeFlags, args->metaState, + args->buttonState, args->classification, 0, 0, args->xPrecision, + args->yPrecision, args->downTime, args->eventTime, args->pointerCount, + args->pointerProperties, args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { @@ -2786,12 +2780,13 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { } // Just enqueue a new motion event. - MotionEntry* newEntry = new MotionEntry(args->sequenceNum, args->eventTime, - args->deviceId, args->source, args->displayId, policyFlags, - args->action, args->actionButton, args->flags, - args->metaState, args->buttonState, args->classification, - args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime, - args->pointerCount, args->pointerProperties, args->pointerCoords, 0, 0); + MotionEntry* newEntry = + new MotionEntry(args->sequenceNum, args->eventTime, args->deviceId, args->source, + args->displayId, policyFlags, args->action, args->actionButton, + args->flags, args->metaState, args->buttonState, + args->classification, args->edgeFlags, args->xPrecision, + args->yPrecision, args->downTime, args->pointerCount, + args->pointerProperties, args->pointerCoords, 0, 0); needWake = enqueueInboundEventLocked(newEntry); mLock.unlock(); @@ -2809,20 +2804,19 @@ bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, " - "switchMask=0x%08x", - args->eventTime, args->policyFlags, args->switchValues, args->switchMask); + "switchMask=0x%08x", + args->eventTime, args->policyFlags, args->switchValues, args->switchMask); #endif uint32_t policyFlags = args->policyFlags; policyFlags |= POLICY_FLAG_TRUSTED; - mPolicy->notifySwitch(args->eventTime, - args->switchValues, args->switchMask, policyFlags); + mPolicy->notifySwitch(args->eventTime, args->switchValues, args->switchMask, policyFlags); } void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", - args->eventTime, args->deviceId); + ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime, + args->deviceId); #endif bool needWake; @@ -2839,13 +2833,13 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { } } -int32_t InputDispatcher::injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, - uint32_t policyFlags) { +int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injectorPid, + int32_t injectorUid, int32_t syncMode, + int32_t timeoutMillis, uint32_t policyFlags) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " - "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x", - event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags); + "syncMode=%d, timeoutMillis=%d, policyFlags=0x%08x", + event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis, policyFlags); #endif nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); @@ -2858,104 +2852,107 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, EventEntry* firstInjectedEntry; EventEntry* lastInjectedEntry; switch (event->getType()) { - case AINPUT_EVENT_TYPE_KEY: { - KeyEvent keyEvent; - keyEvent.initialize(*static_cast<const KeyEvent*>(event)); - int32_t action = keyEvent.getAction(); - if (! validateKeyEvent(action)) { - return INPUT_EVENT_INJECTION_FAILED; - } + case AINPUT_EVENT_TYPE_KEY: { + KeyEvent keyEvent; + keyEvent.initialize(*static_cast<const KeyEvent*>(event)); + int32_t action = keyEvent.getAction(); + if (!validateKeyEvent(action)) { + return INPUT_EVENT_INJECTION_FAILED; + } + + int32_t flags = keyEvent.getFlags(); + int32_t keyCode = keyEvent.getKeyCode(); + int32_t metaState = keyEvent.getMetaState(); + accelerateMetaShortcuts(keyEvent.getDeviceId(), action, + /*byref*/ keyCode, /*byref*/ metaState); + keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), + keyEvent.getDisplayId(), action, flags, keyCode, + keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(), + keyEvent.getDownTime(), keyEvent.getEventTime()); + + if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) { + policyFlags |= POLICY_FLAG_VIRTUAL; + } - int32_t flags = keyEvent.getFlags(); - int32_t keyCode = keyEvent.getKeyCode(); - int32_t metaState = keyEvent.getMetaState(); - accelerateMetaShortcuts(keyEvent.getDeviceId(), action, - /*byref*/ keyCode, /*byref*/ metaState); - keyEvent.initialize(keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(), - action, flags, keyCode, keyEvent.getScanCode(), metaState, keyEvent.getRepeatCount(), - keyEvent.getDownTime(), keyEvent.getEventTime()); + if (!(policyFlags & POLICY_FLAG_FILTERED)) { + android::base::Timer t; + mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags); + if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { + ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms", + std::to_string(t.duration().count()).c_str()); + } + } - if (flags & AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY) { - policyFlags |= POLICY_FLAG_VIRTUAL; + mLock.lock(); + firstInjectedEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, + keyEvent.getEventTime(), keyEvent.getDeviceId(), + keyEvent.getSource(), keyEvent.getDisplayId(), + policyFlags, action, flags, keyEvent.getKeyCode(), + keyEvent.getScanCode(), keyEvent.getMetaState(), + keyEvent.getRepeatCount(), keyEvent.getDownTime()); + lastInjectedEntry = firstInjectedEntry; + break; } - if (!(policyFlags & POLICY_FLAG_FILTERED)) { - android::base::Timer t; - mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags); - if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { - ALOGW("Excessive delay in interceptKeyBeforeQueueing; took %s ms", - std::to_string(t.duration().count()).c_str()); + case AINPUT_EVENT_TYPE_MOTION: { + const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event); + int32_t action = motionEvent->getAction(); + size_t pointerCount = motionEvent->getPointerCount(); + const PointerProperties* pointerProperties = motionEvent->getPointerProperties(); + int32_t actionButton = motionEvent->getActionButton(); + int32_t displayId = motionEvent->getDisplayId(); + if (!validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) { + return INPUT_EVENT_INJECTION_FAILED; } - } - mLock.lock(); - firstInjectedEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(), - keyEvent.getDeviceId(), keyEvent.getSource(), keyEvent.getDisplayId(), - policyFlags, action, flags, - keyEvent.getKeyCode(), keyEvent.getScanCode(), keyEvent.getMetaState(), - keyEvent.getRepeatCount(), keyEvent.getDownTime()); - lastInjectedEntry = firstInjectedEntry; - break; - } - - case AINPUT_EVENT_TYPE_MOTION: { - const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event); - int32_t action = motionEvent->getAction(); - size_t pointerCount = motionEvent->getPointerCount(); - const PointerProperties* pointerProperties = motionEvent->getPointerProperties(); - int32_t actionButton = motionEvent->getActionButton(); - int32_t displayId = motionEvent->getDisplayId(); - if (! validateMotionEvent(action, actionButton, pointerCount, pointerProperties)) { - return INPUT_EVENT_INJECTION_FAILED; - } + if (!(policyFlags & POLICY_FLAG_FILTERED)) { + nsecs_t eventTime = motionEvent->getEventTime(); + android::base::Timer t; + mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags); + if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { + ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms", + std::to_string(t.duration().count()).c_str()); + } + } - if (!(policyFlags & POLICY_FLAG_FILTERED)) { - nsecs_t eventTime = motionEvent->getEventTime(); - android::base::Timer t; - mPolicy->interceptMotionBeforeQueueing(displayId, eventTime, /*byref*/ policyFlags); - if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { - ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms", - std::to_string(t.duration().count()).c_str()); + mLock.lock(); + const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); + const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); + firstInjectedEntry = + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, + motionEvent->getDeviceId(), motionEvent->getSource(), + motionEvent->getDisplayId(), policyFlags, action, actionButton, + motionEvent->getFlags(), motionEvent->getMetaState(), + motionEvent->getButtonState(), motionEvent->getClassification(), + motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), + motionEvent->getYPrecision(), motionEvent->getDownTime(), + uint32_t(pointerCount), pointerProperties, samplePointerCoords, + motionEvent->getXOffset(), motionEvent->getYOffset()); + lastInjectedEntry = firstInjectedEntry; + for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { + sampleEventTimes += 1; + samplePointerCoords += pointerCount; + MotionEntry* nextInjectedEntry = + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, + motionEvent->getDeviceId(), motionEvent->getSource(), + motionEvent->getDisplayId(), policyFlags, action, + actionButton, motionEvent->getFlags(), + motionEvent->getMetaState(), motionEvent->getButtonState(), + motionEvent->getClassification(), + motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), + motionEvent->getYPrecision(), motionEvent->getDownTime(), + uint32_t(pointerCount), pointerProperties, + samplePointerCoords, motionEvent->getXOffset(), + motionEvent->getYOffset()); + lastInjectedEntry->next = nextInjectedEntry; + lastInjectedEntry = nextInjectedEntry; } + break; } - mLock.lock(); - const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); - const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); - firstInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), motionEvent->getDisplayId(), - policyFlags, - action, actionButton, motionEvent->getFlags(), - motionEvent->getMetaState(), motionEvent->getButtonState(), - motionEvent->getClassification(), motionEvent->getEdgeFlags(), - motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), - uint32_t(pointerCount), pointerProperties, samplePointerCoords, - motionEvent->getXOffset(), motionEvent->getYOffset()); - lastInjectedEntry = firstInjectedEntry; - for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { - sampleEventTimes += 1; - samplePointerCoords += pointerCount; - MotionEntry* nextInjectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, - *sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), - motionEvent->getDisplayId(), policyFlags, - action, actionButton, motionEvent->getFlags(), - motionEvent->getMetaState(), motionEvent->getButtonState(), - motionEvent->getClassification(), motionEvent->getEdgeFlags(), - motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), - uint32_t(pointerCount), pointerProperties, samplePointerCoords, - motionEvent->getXOffset(), motionEvent->getYOffset()); - lastInjectedEntry->next = nextInjectedEntry; - lastInjectedEntry = nextInjectedEntry; - } - break; - } - - default: - ALOGW("Cannot inject event of type %d", event->getType()); - return INPUT_EVENT_INJECTION_FAILED; + default: + ALOGW("Cannot inject event of type %d", event->getType()); + return INPUT_EVENT_INJECTION_FAILED; } InjectionState* injectionState = new InjectionState(injectorPid, injectorUid); @@ -2967,7 +2964,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, lastInjectedEntry->injectionState = injectionState; bool needWake = false; - for (EventEntry* entry = firstInjectedEntry; entry != nullptr; ) { + for (EventEntry* entry = firstInjectedEntry; entry != nullptr;) { EventEntry* nextEntry = entry->next; needWake |= enqueueInboundEventLocked(entry); entry = nextEntry; @@ -2996,7 +2993,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, if (remainingTimeout <= 0) { #if DEBUG_INJECTION ALOGD("injectInputEvent - Timed out waiting for injection result " - "to become available."); + "to become available."); #endif injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; break; @@ -3005,18 +3002,18 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout)); } - if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED - && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) { + if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED && + syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) { while (injectionState->pendingForegroundDispatches != 0) { #if DEBUG_INJECTION ALOGD("injectInputEvent - Waiting for %d pending foreground dispatches.", - injectionState->pendingForegroundDispatches); + injectionState->pendingForegroundDispatches); #endif nsecs_t remainingTimeout = endTime - now(); if (remainingTimeout <= 0) { #if DEBUG_INJECTION - ALOGD("injectInputEvent - Timed out waiting for pending foreground " - "dispatches to finish."); + ALOGD("injectInputEvent - Timed out waiting for pending foreground " + "dispatches to finish."); #endif injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; break; @@ -3032,16 +3029,16 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, #if DEBUG_INJECTION ALOGD("injectInputEvent - Finished with result %d. " - "injectorPid=%d, injectorUid=%d", - injectionResult, injectorPid, injectorUid); + "injectorPid=%d, injectorUid=%d", + injectionResult, injectorPid, injectorUid); #endif return injectionResult; } bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) { - return injectorUid == 0 - || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); + return injectorUid == 0 || + mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); } void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) { @@ -3049,26 +3046,25 @@ void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionRes if (injectionState) { #if DEBUG_INJECTION ALOGD("Setting input event injection result to %d. " - "injectorPid=%d, injectorUid=%d", - injectionResult, injectionState->injectorPid, injectionState->injectorUid); + "injectorPid=%d, injectorUid=%d", + injectionResult, injectionState->injectorPid, injectionState->injectorUid); #endif - if (injectionState->injectionIsAsync - && !(entry->policyFlags & POLICY_FLAG_FILTERED)) { + if (injectionState->injectionIsAsync && !(entry->policyFlags & POLICY_FLAG_FILTERED)) { // Log the outcome since the injector did not wait for the injection result. switch (injectionResult) { - case INPUT_EVENT_INJECTION_SUCCEEDED: - ALOGV("Asynchronous input event injection succeeded."); - break; - case INPUT_EVENT_INJECTION_FAILED: - ALOGW("Asynchronous input event injection failed."); - break; - case INPUT_EVENT_INJECTION_PERMISSION_DENIED: - ALOGW("Asynchronous input event injection permission denied."); - break; - case INPUT_EVENT_INJECTION_TIMED_OUT: - ALOGW("Asynchronous input event injection timed out."); - break; + case INPUT_EVENT_INJECTION_SUCCEEDED: + ALOGV("Asynchronous input event injection succeeded."); + break; + case INPUT_EVENT_INJECTION_FAILED: + ALOGW("Asynchronous input event injection failed."); + break; + case INPUT_EVENT_INJECTION_PERMISSION_DENIED: + ALOGW("Asynchronous input event injection permission denied."); + break; + case INPUT_EVENT_INJECTION_TIMED_OUT: + ALOGW("Asynchronous input event injection timed out."); + break; } } @@ -3099,7 +3095,7 @@ std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked( int32_t displayId) const { std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>::const_iterator it = mWindowHandlesByDisplay.find(displayId); - if(it != mWindowHandlesByDisplay.end()) { + if (it != mWindowHandlesByDisplay.end()) { return it->second; } @@ -3127,9 +3123,9 @@ bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowH if (handle->getToken() == windowHandle->getToken()) { if (windowHandle->getInfo()->displayId != it.first) { ALOGE("Found window %s in display %" PRId32 - ", but it should belong to display %" PRId32, - windowHandle->getName().c_str(), it.first, - windowHandle->getInfo()->displayId); + ", but it should belong to display %" PRId32, + windowHandle->getName().c_str(), it.first, + windowHandle->getInfo()->displayId); } return true; } @@ -3154,7 +3150,8 @@ sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token * For removed handle, check if need to send a cancel event if already in touch. */ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& inputWindowHandles, - int32_t displayId, const sp<ISetInputWindowsListener>& setInputWindowsListener) { + int32_t displayId, + const sp<ISetInputWindowsListener>& setInputWindowsListener) { #if DEBUG_FOCUS ALOGD("setInputWindows displayId=%" PRId32, displayId); #endif @@ -3221,8 +3218,8 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& for (const sp<InputWindowHandle>& windowHandle : newHandles) { // Set newFocusedWindowHandle to the top most focused window instead of the last one - if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus - && windowHandle->getInfo()->visible) { + if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && + windowHandle->getInfo()->visible) { newFocusedWindowHandle = windowHandle; } if (windowHandle == mLastHoverWindowHandle) { @@ -3245,22 +3242,21 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& if (oldFocusedWindowHandle != nullptr) { #if DEBUG_FOCUS ALOGD("Focus left window: %s in display %" PRId32, - oldFocusedWindowHandle->getName().c_str(), displayId); + oldFocusedWindowHandle->getName().c_str(), displayId); #endif - sp<InputChannel> focusedInputChannel = getInputChannelLocked( - oldFocusedWindowHandle->getToken()); + sp<InputChannel> focusedInputChannel = + getInputChannelLocked(oldFocusedWindowHandle->getToken()); if (focusedInputChannel != nullptr) { CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, - "focus left window"); - synthesizeCancelationEventsForInputChannelLocked( - focusedInputChannel, options); + "focus left window"); + synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); } mFocusedWindowHandlesByDisplay.erase(displayId); } if (newFocusedWindowHandle != nullptr) { #if DEBUG_FOCUS ALOGD("Focus entered window: %s in display %" PRId32, - newFocusedWindowHandle->getName().c_str(), displayId); + newFocusedWindowHandle->getName().c_str(), displayId); #endif mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; } @@ -3268,30 +3264,29 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& if (mFocusedDisplayId == displayId) { onFocusChangedLocked(oldFocusedWindowHandle, newFocusedWindowHandle); } - } ssize_t stateIndex = mTouchStatesByDisplay.indexOfKey(displayId); if (stateIndex >= 0) { TouchState& state = mTouchStatesByDisplay.editValueAt(stateIndex); - for (size_t i = 0; i < state.windows.size(); ) { + for (size_t i = 0; i < state.windows.size();) { TouchedWindow& touchedWindow = state.windows[i]; if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { #if DEBUG_FOCUS ALOGD("Touched window was removed: %s in display %" PRId32, - touchedWindow.windowHandle->getName().c_str(), displayId); + touchedWindow.windowHandle->getName().c_str(), displayId); #endif sp<InputChannel> touchedInputChannel = getInputChannelLocked(touchedWindow.windowHandle->getToken()); if (touchedInputChannel != nullptr) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "touched window was removed"); - synthesizeCancelationEventsForInputChannelLocked( - touchedInputChannel, options); + "touched window was removed"); + synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, + options); } state.windows.erase(state.windows.begin() + i); } else { - ++i; + ++i; } } } @@ -3342,7 +3337,7 @@ void InputDispatcher::setFocusedApplication( } #if DEBUG_FOCUS - //logDispatchStateLocked(); + // logDispatchStateLocked(); #endif } // release lock @@ -3371,11 +3366,11 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { getValueByKey(mFocusedWindowHandlesByDisplay, mFocusedDisplayId); if (oldFocusedWindowHandle != nullptr) { sp<InputChannel> inputChannel = - getInputChannelLocked(oldFocusedWindowHandle->getToken()); + getInputChannelLocked(oldFocusedWindowHandle->getToken()); if (inputChannel != nullptr) { - CancelationOptions options( - CancelationOptions::CANCEL_NON_POINTER_EVENTS, - "The display which contains this window no longer has focus."); + CancelationOptions + options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, + "The display which contains this window no longer has focus."); options.displayId = ADISPLAY_ID_NONE; synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); } @@ -3394,8 +3389,8 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { for (auto& it : mFocusedWindowHandlesByDisplay) { const int32_t displayId = it.first; const sp<InputWindowHandle>& windowHandle = it.second; - ALOGE("Display #%" PRId32 " has focused window: '%s'\n", - displayId, windowHandle->getName().c_str()); + ALOGE("Display #%" PRId32 " has focused window: '%s'\n", displayId, + windowHandle->getName().c_str()); } } } @@ -3485,7 +3480,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< } #if DEBUG_FOCUS ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", - fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str()); + fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str()); #endif if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) { #if DEBUG_FOCUS @@ -3505,9 +3500,9 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< state.windows.erase(state.windows.begin() + i); - int32_t newTargetFlags = oldTargetFlags - & (InputTarget::FLAG_FOREGROUND - | InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); + int32_t newTargetFlags = oldTargetFlags & + (InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_SPLIT | + InputTarget::FLAG_DISPATCH_AS_IS); state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds); found = true; @@ -3515,16 +3510,15 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< } } } -Found: + Found: - if (! found) { + if (!found) { #if DEBUG_FOCUS ALOGD("Focus transfer failed because from window did not have focus."); #endif return false; } - sp<InputChannel> fromChannel = getInputChannelLocked(fromToken); sp<InputChannel> toChannel = getInputChannelLocked(toToken); ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel); @@ -3534,8 +3528,9 @@ Found: sp<Connection> toConnection = mConnectionsByFd.valueAt(toConnectionIndex); fromConnection->inputState.copyPointerStateTo(toConnection->inputState); - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "transferring touch focus from this window to another window"); + CancelationOptions + options(CancelationOptions::CANCEL_POINTER_EVENTS, + "transferring touch focus from this window to another window"); synthesizeCancelationEventsForConnectionLocked(fromConnection, options); } @@ -3590,12 +3585,12 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (auto& it : mFocusedApplicationHandlesByDisplay) { const int32_t displayId = it.first; const sp<InputApplicationHandle>& applicationHandle = it.second; - dump += StringPrintf( - INDENT2 "displayId=%" PRId32 ", name='%s', dispatchingTimeout=%0.3fms\n", - displayId, - applicationHandle->getName().c_str(), - applicationHandle->getDispatchingTimeout( - DEFAULT_INPUT_DISPATCHING_TIMEOUT) / 1000000.0); + dump += StringPrintf(INDENT2 "displayId=%" PRId32 + ", name='%s', dispatchingTimeout=%0.3fms\n", + displayId, applicationHandle->getName().c_str(), + applicationHandle->getDispatchingTimeout( + DEFAULT_INPUT_DISPATCHING_TIMEOUT) / + 1000000.0); } } else { dump += StringPrintf(INDENT "FocusedApplications: <none>\n"); @@ -3606,8 +3601,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (auto& it : mFocusedWindowHandlesByDisplay) { const int32_t displayId = it.first; const sp<InputWindowHandle>& windowHandle = it.second; - dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", - displayId, windowHandle->getName().c_str()); + dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId, + windowHandle->getName().c_str()); } } else { dump += StringPrintf(INDENT "FocusedWindows: <none>\n"); @@ -3618,16 +3613,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (size_t i = 0; i < mTouchStatesByDisplay.size(); i++) { const TouchState& state = mTouchStatesByDisplay.valueAt(i); dump += StringPrintf(INDENT2 "%d: down=%s, split=%s, deviceId=%d, source=0x%08x\n", - state.displayId, toString(state.down), toString(state.split), - state.deviceId, state.source); + state.displayId, toString(state.down), toString(state.split), + state.deviceId, state.source); if (!state.windows.empty()) { dump += INDENT3 "Windows:\n"; for (size_t i = 0; i < state.windows.size(); i++) { const TouchedWindow& touchedWindow = state.windows[i]; - dump += StringPrintf(INDENT4 "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n", - i, touchedWindow.windowHandle->getName().c_str(), - touchedWindow.pointerIds.value, - touchedWindow.targetFlags); + dump += StringPrintf(INDENT4 + "%zu: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n", + i, touchedWindow.windowHandle->getName().c_str(), + touchedWindow.pointerIds.value, touchedWindow.targetFlags); } } else { dump += INDENT3 "Windows: <none>\n"; @@ -3636,8 +3631,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT3 "Portal windows:\n"; for (size_t i = 0; i < state.portalWindows.size(); i++) { const sp<InputWindowHandle> portalWindowHandle = state.portalWindows[i]; - dump += StringPrintf(INDENT4 "%zu: name='%s'\n", - i, portalWindowHandle->getName().c_str()); + dump += StringPrintf(INDENT4 "%zu: name='%s'\n", i, + portalWindowHandle->getName().c_str()); } } } @@ -3646,7 +3641,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } if (!mWindowHandlesByDisplay.empty()) { - for (auto& it : mWindowHandlesByDisplay) { + for (auto& it : mWindowHandlesByDisplay) { const std::vector<sp<InputWindowHandle>> windowHandles = it.second; dump += StringPrintf(INDENT "Display: %" PRId32 "\n", it.first); if (!windowHandles.empty()) { @@ -3656,28 +3651,31 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { const InputWindowInfo* windowInfo = windowHandle->getInfo(); dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, " - "portalToDisplayId=%d, paused=%s, hasFocus=%s, hasWallpaper=%s, " - "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, " - "frame=[%d,%d][%d,%d], globalScale=%f, windowScale=(%f,%f), " - "touchableRegion=", - i, windowInfo->name.c_str(), windowInfo->displayId, - windowInfo->portalToDisplayId, - toString(windowInfo->paused), - toString(windowInfo->hasFocus), - toString(windowInfo->hasWallpaper), - toString(windowInfo->visible), - toString(windowInfo->canReceiveKeys), - windowInfo->layoutParamsFlags, windowInfo->layoutParamsType, - windowInfo->layer, - windowInfo->frameLeft, windowInfo->frameTop, - windowInfo->frameRight, windowInfo->frameBottom, - windowInfo->globalScaleFactor, - windowInfo->windowXScale, windowInfo->windowYScale); + "portalToDisplayId=%d, paused=%s, hasFocus=%s, " + "hasWallpaper=%s, " + "visible=%s, canReceiveKeys=%s, flags=0x%08x, " + "type=0x%08x, layer=%d, " + "frame=[%d,%d][%d,%d], globalScale=%f, " + "windowScale=(%f,%f), " + "touchableRegion=", + i, windowInfo->name.c_str(), windowInfo->displayId, + windowInfo->portalToDisplayId, + toString(windowInfo->paused), + toString(windowInfo->hasFocus), + toString(windowInfo->hasWallpaper), + toString(windowInfo->visible), + toString(windowInfo->canReceiveKeys), + windowInfo->layoutParamsFlags, + windowInfo->layoutParamsType, windowInfo->layer, + windowInfo->frameLeft, windowInfo->frameTop, + windowInfo->frameRight, windowInfo->frameBottom, + windowInfo->globalScaleFactor, windowInfo->windowXScale, + windowInfo->windowYScale); dumpRegion(dump, windowInfo->touchableRegion); dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures); dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", - windowInfo->ownerPid, windowInfo->ownerUid, - windowInfo->dispatchingTimeout / 1000000.0); + windowInfo->ownerPid, windowInfo->ownerUid, + windowInfo->dispatchingTimeout / 1000000.0); } } else { dump += INDENT2 "Windows: <none>\n"; @@ -3688,16 +3686,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } if (!mGlobalMonitorsByDisplay.empty() || !mGestureMonitorsByDisplay.empty()) { - for (auto& it : mGlobalMonitorsByDisplay) { + for (auto& it : mGlobalMonitorsByDisplay) { const std::vector<Monitor>& monitors = it.second; dump += StringPrintf(INDENT "Global monitors in display %" PRId32 ":\n", it.first); dumpMonitors(dump, monitors); - } - for (auto& it : mGestureMonitorsByDisplay) { + } + for (auto& it : mGestureMonitorsByDisplay) { const std::vector<Monitor>& monitors = it.second; dump += StringPrintf(INDENT "Gesture monitors in display %" PRId32 ":\n", it.first); dumpMonitors(dump, monitors); - } + } } else { dump += INDENT "Monitors: <none>\n"; } @@ -3710,8 +3708,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (EventEntry* entry = mRecentQueue.head; entry; entry = entry->next) { dump += INDENT2; entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", - (currentTime - entry->eventTime) * 0.000001f); + dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); } } else { dump += INDENT "RecentQueue: <empty>\n"; @@ -3723,7 +3720,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT2; mPendingEvent->appendDescription(dump); dump += StringPrintf(", age=%0.1fms\n", - (currentTime - mPendingEvent->eventTime) * 0.000001f); + (currentTime - mPendingEvent->eventTime) * 0.000001f); } else { dump += INDENT "PendingEvent: <none>\n"; } @@ -3734,8 +3731,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (EventEntry* entry = mInboundQueue.head; entry; entry = entry->next) { dump += INDENT2; entry->appendDescription(dump); - dump += StringPrintf(", age=%0.1fms\n", - (currentTime - entry->eventTime) * 0.000001f); + dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); } } else { dump += INDENT "InboundQueue: <empty>\n"; @@ -3746,8 +3742,8 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (size_t i = 0; i < mReplacedKeys.size(); i++) { const KeyReplacement& replacement = mReplacedKeys.keyAt(i); int32_t newKeyCode = mReplacedKeys.valueAt(i); - dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", - i, replacement.keyCode, replacement.deviceId, newKeyCode); + dump += StringPrintf(INDENT2 "%zu: originalKeyCode=%d, deviceId=%d, newKeyCode=%d\n", i, + replacement.keyCode, replacement.deviceId, newKeyCode); } } else { dump += INDENT "ReplacedKeys: <empty>\n"; @@ -3758,22 +3754,22 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { for (size_t i = 0; i < mConnectionsByFd.size(); i++) { const sp<Connection>& connection = mConnectionsByFd.valueAt(i); dump += StringPrintf(INDENT2 "%zu: channelName='%s', windowName='%s', " - "status=%s, monitor=%s, inputPublisherBlocked=%s\n", - i, connection->getInputChannelName().c_str(), - connection->getWindowName().c_str(), - connection->getStatusLabel(), toString(connection->monitor), - toString(connection->inputPublisherBlocked)); + "status=%s, monitor=%s, inputPublisherBlocked=%s\n", + i, connection->getInputChannelName().c_str(), + connection->getWindowName().c_str(), connection->getStatusLabel(), + toString(connection->monitor), + toString(connection->inputPublisherBlocked)); if (!connection->outboundQueue.isEmpty()) { dump += StringPrintf(INDENT3 "OutboundQueue: length=%u\n", - connection->outboundQueue.count()); + connection->outboundQueue.count()); for (DispatchEntry* entry = connection->outboundQueue.head; entry; - entry = entry->next) { + entry = entry->next) { dump.append(INDENT4); entry->eventEntry->appendDescription(dump); dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n", - entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f); + entry->targetFlags, entry->resolvedAction, + (currentTime - entry->eventEntry->eventTime) * 0.000001f); } } else { dump += INDENT3 "OutboundQueue: <empty>\n"; @@ -3781,16 +3777,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (!connection->waitQueue.isEmpty()) { dump += StringPrintf(INDENT3 "WaitQueue: length=%u\n", - connection->waitQueue.count()); + connection->waitQueue.count()); for (DispatchEntry* entry = connection->waitQueue.head; entry; - entry = entry->next) { + entry = entry->next) { dump += INDENT4; entry->eventEntry->appendDescription(dump); dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, " - "age=%0.1fms, wait=%0.1fms\n", - entry->targetFlags, entry->resolvedAction, - (currentTime - entry->eventEntry->eventTime) * 0.000001f, - (currentTime - entry->deliveryTime) * 0.000001f); + "age=%0.1fms, wait=%0.1fms\n", + entry->targetFlags, entry->resolvedAction, + (currentTime - entry->eventEntry->eventTime) * 0.000001f, + (currentTime - entry->deliveryTime) * 0.000001f); } } else { dump += INDENT3 "WaitQueue: <empty>\n"; @@ -3802,16 +3798,15 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { if (isAppSwitchPendingLocked()) { dump += StringPrintf(INDENT "AppSwitch: pending, due in %0.1fms\n", - (mAppSwitchDueTime - now()) / 1000000.0); + (mAppSwitchDueTime - now()) / 1000000.0); } else { dump += INDENT "AppSwitch: not pending\n"; } dump += INDENT "Configuration:\n"; - dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", - mConfig.keyRepeatDelay * 0.000001f); + dump += StringPrintf(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f); dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %0.1fms\n", - mConfig.keyRepeatTimeout * 0.000001f); + mConfig.keyRepeatTimeout * 0.000001f); } void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) { @@ -3825,10 +3820,10 @@ void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor> } status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, - int32_t displayId) { + int32_t displayId) { #if DEBUG_REGISTRATION ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32, - inputChannel->getName().c_str(), displayId); + inputChannel->getName().c_str(), displayId); #endif { // acquire lock @@ -3836,7 +3831,7 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan if (getConnectionIndexLocked(inputChannel) >= 0) { ALOGW("Attempted to register already registered input channel '%s'", - inputChannel->getName().c_str()); + inputChannel->getName().c_str()); return BAD_VALUE; } @@ -3855,7 +3850,7 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan } status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChannel, - int32_t displayId, bool isGestureMonitor) { + int32_t displayId, bool isGestureMonitor) { { // acquire lock std::scoped_lock _l(mLock); @@ -3875,13 +3870,11 @@ status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChan mConnectionsByFd.add(fd, connection); mInputChannelsByToken[inputChannel->getToken()] = inputChannel; - auto& monitorsByDisplay = isGestureMonitor - ? mGestureMonitorsByDisplay - : mGlobalMonitorsByDisplay; + auto& monitorsByDisplay = + isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay; monitorsByDisplay[displayId].emplace_back(inputChannel); mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); - } // Wake the looper because some connections have changed. mLooper->wake(); @@ -3909,11 +3902,11 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh } status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, - bool notify) { + bool notify) { ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); if (connectionIndex < 0) { ALOGW("Attempted to unregister already unregistered input channel '%s'", - inputChannel->getName().c_str()); + inputChannel->getName().c_str()); return BAD_VALUE; } @@ -3940,16 +3933,17 @@ void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputCh removeMonitorChannelLocked(inputChannel, mGestureMonitorsByDisplay); } -void InputDispatcher::removeMonitorChannelLocked(const sp<InputChannel>& inputChannel, +void InputDispatcher::removeMonitorChannelLocked( + const sp<InputChannel>& inputChannel, std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) { - for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end(); ) { + for (auto it = monitorsByDisplay.begin(); it != monitorsByDisplay.end();) { std::vector<Monitor>& monitors = it->second; const size_t numMonitors = monitors.size(); for (size_t i = 0; i < numMonitors; i++) { - if (monitors[i].inputChannel == inputChannel) { - monitors.erase(monitors.begin() + i); - break; - } + if (monitors[i].inputChannel == inputChannel) { + monitors.erase(monitors.begin() + i); + break; + } } if (monitors.empty()) { it = monitorsByDisplay.erase(it); @@ -3985,14 +3979,14 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { } if (!foundDeviceId || !state.down) { ALOGW("Attempted to pilfer points from a monitor without any on-going pointer streams." - " Ignoring."); + " Ignoring."); return BAD_VALUE; } int32_t deviceId = foundDeviceId.value(); // Send cancel events to all the input channels we're stealing from. CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "gesture monitor stole pointer stream"); + "gesture monitor stole pointer stream"); options.deviceId = deviceId; options.displayId = displayId; for (const TouchedWindow& window : state.windows) { @@ -4005,7 +3999,6 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { return OK; } - std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked( const sp<IBinder>& token) { for (const auto& it : mGestureMonitorsByDisplay) { @@ -4034,46 +4027,47 @@ ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputC return -1; } -void InputDispatcher::onDispatchCycleFinishedLocked( - nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) { - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doDispatchCycleFinishedLockedInterruptible); +void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, + const sp<Connection>& connection, uint32_t seq, + bool handled) { + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doDispatchCycleFinishedLockedInterruptible); commandEntry->connection = connection; commandEntry->eventTime = currentTime; commandEntry->seq = seq; commandEntry->handled = handled; } -void InputDispatcher::onDispatchCycleBrokenLocked( - nsecs_t currentTime, const sp<Connection>& connection) { +void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime, + const sp<Connection>& connection) { ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", - connection->getInputChannelName().c_str()); + connection->getInputChannelName().c_str()); - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); commandEntry->connection = connection; } void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus, - const sp<InputWindowHandle>& newFocus) { + const sp<InputWindowHandle>& newFocus) { sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr; sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr; - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyFocusChangedLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doNotifyFocusChangedLockedInterruptible); commandEntry->oldToken = oldToken; commandEntry->newToken = newToken; } -void InputDispatcher::onANRLocked( - nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle, - const sp<InputWindowHandle>& windowHandle, - nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) { +void InputDispatcher::onANRLocked(nsecs_t currentTime, + const sp<InputApplicationHandle>& applicationHandle, + const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime, + nsecs_t waitStartTime, const char* reason) { float dispatchLatency = (currentTime - eventTime) * 0.000001f; float waitDuration = (currentTime - waitStartTime) * 0.000001f; ALOGI("Application is not responding: %s. " - "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), - dispatchLatency, waitDuration, reason); + "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s", + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency, + waitDuration, reason); // Capture a record of the InputDispatcher state at the time of the ANR. time_t t = time(nullptr); @@ -4084,23 +4078,23 @@ void InputDispatcher::onANRLocked( mLastANRState.clear(); mLastANRState += INDENT "ANR:\n"; mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr); - mLastANRState += StringPrintf(INDENT2 "Window: %s\n", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str()); + mLastANRState += + StringPrintf(INDENT2 "Window: %s\n", + getApplicationWindowLabel(applicationHandle, windowHandle).c_str()); mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency); mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration); mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason); dumpDispatchStateLocked(mLastANRState); - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyANRLockedInterruptible); + CommandEntry* commandEntry = + postCommandLocked(&InputDispatcher::doNotifyANRLockedInterruptible); commandEntry->inputApplicationHandle = applicationHandle; - commandEntry->inputChannel = windowHandle != nullptr ? - getInputChannelLocked(windowHandle->getToken()) : nullptr; + commandEntry->inputChannel = + windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr; commandEntry->reason = reason; } -void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible ( - CommandEntry* commandEntry) { +void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) { mLock.unlock(); mPolicy->notifyConfigurationChanged(commandEntry->eventTime); @@ -4108,8 +4102,7 @@ void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible ( mLock.lock(); } -void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( - CommandEntry* commandEntry) { +void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) { sp<Connection> connection = commandEntry->connection; if (connection->status != Connection::STATUS_ZOMBIE) { @@ -4121,8 +4114,7 @@ void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( } } -void InputDispatcher::doNotifyFocusChangedLockedInterruptible( - CommandEntry* commandEntry) { +void InputDispatcher::doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) { sp<IBinder> oldToken = commandEntry->oldToken; sp<IBinder> newToken = commandEntry->newToken; mLock.unlock(); @@ -4130,19 +4122,18 @@ void InputDispatcher::doNotifyFocusChangedLockedInterruptible( mLock.lock(); } -void InputDispatcher::doNotifyANRLockedInterruptible( - CommandEntry* commandEntry) { +void InputDispatcher::doNotifyANRLockedInterruptible(CommandEntry* commandEntry) { mLock.unlock(); - nsecs_t newTimeout = mPolicy->notifyANR( - commandEntry->inputApplicationHandle, - commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr, - commandEntry->reason); + nsecs_t newTimeout = + mPolicy->notifyANR(commandEntry->inputApplicationHandle, + commandEntry->inputChannel ? commandEntry->inputChannel->getToken() + : nullptr, + commandEntry->reason); mLock.lock(); - resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, - commandEntry->inputChannel); + resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel); } void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( @@ -4155,13 +4146,13 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( mLock.unlock(); android::base::Timer t; - sp<IBinder> token = commandEntry->inputChannel != nullptr ? - commandEntry->inputChannel->getToken() : nullptr; - nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, - &event, entry->policyFlags); + sp<IBinder> token = commandEntry->inputChannel != nullptr + ? commandEntry->inputChannel->getToken() + : nullptr; + nsecs_t delay = mPolicy->interceptKeyBeforeDispatching(token, &event, entry->policyFlags); if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) { ALOGW("Excessive delay in interceptKeyBeforeDispatching; took %s ms", - std::to_string(t.duration().count()).c_str()); + std::to_string(t.duration().count()).c_str()); } mLock.lock(); @@ -4183,8 +4174,7 @@ void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntr mLock.lock(); } -void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( - CommandEntry* commandEntry) { +void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) { sp<Connection> connection = commandEntry->connection; nsecs_t finishTime = commandEntry->eventTime; uint32_t seq = commandEntry->seq; @@ -4197,7 +4187,7 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { std::string msg = StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", - connection->getWindowName().c_str(), eventDuration * 0.000001f); + connection->getWindowName().c_str(), eventDuration * 0.000001f); dispatchEntry->eventEntry->appendDescription(msg); ALOGI("%s", msg.c_str()); } @@ -4205,12 +4195,12 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( bool restartEvent; if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry); - restartEvent = afterKeyEventLockedInterruptible(connection, - dispatchEntry, keyEntry, handled); + restartEvent = + afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled); } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) { MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry); - restartEvent = afterMotionEventLockedInterruptible(connection, - dispatchEntry, motionEntry, handled); + restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, + motionEntry, handled); } else { restartEvent = false; } @@ -4236,7 +4226,8 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( } bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection, - DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) { + DispatchEntry* dispatchEntry, + KeyEntry* keyEntry, bool handled) { if (keyEntry->flags & AKEY_EVENT_FLAG_FALLBACK) { if (!handled) { // Report the key as unhandled, since the fallback was not handled. @@ -4261,9 +4252,9 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con // Dispatch the unhandled key to the policy with the cancel flag. #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Unhandled key event: Asking policy to cancel fallback action. " - "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, - keyEntry->policyFlags); + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, + keyEntry->policyFlags); #endif KeyEvent event; initializeKeyEvent(&event, keyEntry); @@ -4271,8 +4262,8 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con mLock.unlock(); - mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), - &event, keyEntry->policyFlags, &event); + mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event, + keyEntry->policyFlags, &event); mLock.lock(); @@ -4291,15 +4282,13 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con // If the application did not handle a non-fallback key, first check // that we are in a good state to perform unhandled key event processing // Then ask the policy what to do with it. - bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN - && keyEntry->repeatCount == 0; + bool initialDown = keyEntry->action == AKEY_EVENT_ACTION_DOWN && keyEntry->repeatCount == 0; if (fallbackKeyCode == -1 && !initialDown) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Unhandled key event: Skipping unhandled key event processing " - "since this is not an initial down. " - "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - originalKeyCode, keyEntry->action, keyEntry->repeatCount, - keyEntry->policyFlags); + "since this is not an initial down. " + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + originalKeyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags); #endif return false; } @@ -4307,17 +4296,16 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con // Dispatch the unhandled key to the policy. #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Unhandled key event: Asking policy to perform fallback action. " - "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", - keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, - keyEntry->policyFlags); + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, keyEntry->policyFlags); #endif KeyEvent event; initializeKeyEvent(&event, keyEntry); mLock.unlock(); - bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), - &event, keyEntry->policyFlags, &event); + bool fallback = mPolicy->dispatchUnhandledKey(connection->inputChannel->getToken(), &event, + keyEntry->policyFlags, &event); mLock.lock(); @@ -4342,19 +4330,19 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con // Cancel the fallback key if the policy decides not to send it anymore. // We will continue to dispatch the key to the policy but we will no // longer dispatch a fallback key to the application. - if (fallbackKeyCode != AKEYCODE_UNKNOWN - && (!fallback || fallbackKeyCode != event.getKeyCode())) { + if (fallbackKeyCode != AKEYCODE_UNKNOWN && + (!fallback || fallbackKeyCode != event.getKeyCode())) { #if DEBUG_OUTBOUND_EVENT_DETAILS if (fallback) { ALOGD("Unhandled key event: Policy requested to send key %d" - "as a fallback for %d, but on the DOWN it had requested " - "to send %d instead. Fallback canceled.", - event.getKeyCode(), originalKeyCode, fallbackKeyCode); + "as a fallback for %d, but on the DOWN it had requested " + "to send %d instead. Fallback canceled.", + event.getKeyCode(), originalKeyCode, fallbackKeyCode); } else { ALOGD("Unhandled key event: Policy did not request fallback for %d, " - "but on the DOWN it had requested to send %d. " - "Fallback canceled.", - originalKeyCode, fallbackKeyCode); + "but on the DOWN it had requested to send %d. " + "Fallback canceled.", + originalKeyCode, fallbackKeyCode); } #endif @@ -4366,8 +4354,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con fallback = false; fallbackKeyCode = AKEYCODE_UNKNOWN; if (keyEntry->action != AKEY_EVENT_ACTION_UP) { - connection->inputState.setFallbackKey(originalKeyCode, - fallbackKeyCode); + connection->inputState.setFallbackKey(originalKeyCode, fallbackKeyCode); } } @@ -4377,11 +4364,10 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con const KeyedVector<int32_t, int32_t>& fallbackKeys = connection->inputState.getFallbackKeys(); for (size_t i = 0; i < fallbackKeys.size(); i++) { - msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), - fallbackKeys.valueAt(i)); + msg += StringPrintf(", %d->%d", fallbackKeys.keyAt(i), fallbackKeys.valueAt(i)); } ALOGD("Unhandled key event: %zu currently tracked fallback keys%s.", - fallbackKeys.size(), msg.c_str()); + fallbackKeys.size(), msg.c_str()); } #endif @@ -4401,8 +4387,8 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Unhandled key event: Dispatching fallback key. " - "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x", - originalKeyCode, fallbackKeyCode, keyEntry->metaState); + "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x", + originalKeyCode, fallbackKeyCode, keyEntry->metaState); #endif return true; // restart the event } else { @@ -4418,7 +4404,8 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& con } bool InputDispatcher::afterMotionEventLockedInterruptible(const sp<Connection>& connection, - DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) { + DispatchEntry* dispatchEntry, + MotionEntry* motionEntry, bool handled) { return false; } @@ -4432,12 +4419,13 @@ void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* comman void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) { event->initialize(entry->deviceId, entry->source, entry->displayId, entry->action, entry->flags, - entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, - entry->downTime, entry->eventTime); + entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, + entry->downTime, entry->eventTime); } void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, - int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) { + int32_t injectionResult, + nsecs_t timeSpentWaitingForApplication) { // TODO Write some statistics about how long we spend waiting. } @@ -4482,762 +4470,4 @@ void InputDispatcher::monitor() { mDispatcherIsAlive.wait(_l); } - -// --- InputDispatcher::InjectionState --- - -InputDispatcher::InjectionState::InjectionState(int32_t injectorPid, int32_t injectorUid) : - refCount(1), - injectorPid(injectorPid), injectorUid(injectorUid), - injectionResult(INPUT_EVENT_INJECTION_PENDING), injectionIsAsync(false), - pendingForegroundDispatches(0) { -} - -InputDispatcher::InjectionState::~InjectionState() { -} - -void InputDispatcher::InjectionState::release() { - refCount -= 1; - if (refCount == 0) { - delete this; - } else { - ALOG_ASSERT(refCount > 0); - } -} - - -// --- InputDispatcher::EventEntry --- - -InputDispatcher::EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, - nsecs_t eventTime, uint32_t policyFlags) : - sequenceNum(sequenceNum), refCount(1), type(type), eventTime(eventTime), - policyFlags(policyFlags), injectionState(nullptr), dispatchInProgress(false) { -} - -InputDispatcher::EventEntry::~EventEntry() { - releaseInjectionState(); -} - -void InputDispatcher::EventEntry::release() { - refCount -= 1; - if (refCount == 0) { - delete this; - } else { - ALOG_ASSERT(refCount > 0); - } -} - -void InputDispatcher::EventEntry::releaseInjectionState() { - if (injectionState) { - injectionState->release(); - injectionState = nullptr; - } -} - - -// --- InputDispatcher::ConfigurationChangedEntry --- - -InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry( - uint32_t sequenceNum, nsecs_t eventTime) : - EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) { -} - -InputDispatcher::ConfigurationChangedEntry::~ConfigurationChangedEntry() { -} - -void InputDispatcher::ConfigurationChangedEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags); -} - - -// --- InputDispatcher::DeviceResetEntry --- - -InputDispatcher::DeviceResetEntry::DeviceResetEntry( - uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId) : - EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), - deviceId(deviceId) { -} - -InputDispatcher::DeviceResetEntry::~DeviceResetEntry() { -} - -void InputDispatcher::DeviceResetEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", - deviceId, policyFlags); -} - - -// --- InputDispatcher::KeyEntry --- - -InputDispatcher::KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, - int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, - int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, - int32_t repeatCount, nsecs_t downTime) : - EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags), - deviceId(deviceId), source(source), displayId(displayId), action(action), flags(flags), - keyCode(keyCode), scanCode(scanCode), metaState(metaState), - repeatCount(repeatCount), downTime(downTime), - syntheticRepeat(false), interceptKeyResult(KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN), - interceptKeyWakeupTime(0) { -} - -InputDispatcher::KeyEntry::~KeyEntry() { -} - -void InputDispatcher::KeyEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("KeyEvent"); -} - -void InputDispatcher::KeyEntry::recycle() { - releaseInjectionState(); - - dispatchInProgress = false; - syntheticRepeat = false; - interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; - interceptKeyWakeupTime = 0; -} - - -// --- InputDispatcher::MotionEntry --- - -InputDispatcher::MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, - uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action, - int32_t actionButton, - int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, float xPrecision, float yPrecision, nsecs_t downTime, - uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset) : - EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), - eventTime(eventTime), - deviceId(deviceId), source(source), displayId(displayId), action(action), - actionButton(actionButton), flags(flags), metaState(metaState), buttonState(buttonState), - classification(classification), edgeFlags(edgeFlags), - xPrecision(xPrecision), yPrecision(yPrecision), - downTime(downTime), pointerCount(pointerCount) { - for (uint32_t i = 0; i < pointerCount; i++) { - this->pointerProperties[i].copyFrom(pointerProperties[i]); - this->pointerCoords[i].copyFrom(pointerCoords[i]); - if (xOffset || yOffset) { - this->pointerCoords[i].applyOffset(xOffset, yOffset); - } - } -} - -InputDispatcher::MotionEntry::~MotionEntry() { -} - -void InputDispatcher::MotionEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("MotionEvent"); -} - - -// --- InputDispatcher::DispatchEntry --- - -volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic; - -InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry, - int32_t targetFlags, float xOffset, float yOffset, float globalScaleFactor, - float windowXScale, float windowYScale) : - seq(nextSeq()), - eventEntry(eventEntry), targetFlags(targetFlags), - xOffset(xOffset), yOffset(yOffset), globalScaleFactor(globalScaleFactor), - windowXScale(windowXScale), windowYScale(windowYScale), - deliveryTime(0), resolvedAction(0), resolvedFlags(0) { - eventEntry->refCount += 1; -} - -InputDispatcher::DispatchEntry::~DispatchEntry() { - eventEntry->release(); -} - -uint32_t InputDispatcher::DispatchEntry::nextSeq() { - // Sequence number 0 is reserved and will never be returned. - uint32_t seq; - do { - seq = android_atomic_inc(&sNextSeqAtomic); - } while (!seq); - return seq; -} - - -// --- InputDispatcher::InputState --- - -InputDispatcher::InputState::InputState() { -} - -InputDispatcher::InputState::~InputState() { -} - -bool InputDispatcher::InputState::isNeutral() const { - return mKeyMementos.empty() && mMotionMementos.empty(); -} - -bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source, - int32_t displayId) const { - for (const MotionMemento& memento : mMotionMementos) { - if (memento.deviceId == deviceId - && memento.source == source - && memento.displayId == displayId - && memento.hovering) { - return true; - } - } - return false; -} - -bool InputDispatcher::InputState::trackKey(const KeyEntry* entry, - int32_t action, int32_t flags) { - switch (action) { - case AKEY_EVENT_ACTION_UP: { - if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) { - for (size_t i = 0; i < mFallbackKeys.size(); ) { - if (mFallbackKeys.valueAt(i) == entry->keyCode) { - mFallbackKeys.removeItemsAt(i); - } else { - i += 1; - } - } - } - ssize_t index = findKeyMemento(entry); - if (index >= 0) { - mKeyMementos.erase(mKeyMementos.begin() + index); - return true; - } - /* FIXME: We can't just drop the key up event because that prevents creating - * popup windows that are automatically shown when a key is held and then - * dismissed when the key is released. The problem is that the popup will - * not have received the original key down, so the key up will be considered - * to be inconsistent with its observed state. We could perhaps handle this - * by synthesizing a key down but that will cause other problems. - * - * So for now, allow inconsistent key up events to be dispatched. - * -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, " - "keyCode=%d, scanCode=%d", - entry->deviceId, entry->source, entry->keyCode, entry->scanCode); -#endif - return false; - */ - return true; - } - - case AKEY_EVENT_ACTION_DOWN: { - ssize_t index = findKeyMemento(entry); - if (index >= 0) { - mKeyMementos.erase(mKeyMementos.begin() + index); - } - addKeyMemento(entry, flags); - return true; - } - - default: - return true; - } -} - -bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry, - int32_t action, int32_t flags) { - int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK; - switch (actionMasked) { - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: { - ssize_t index = findMotionMemento(entry, false /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - return true; - } -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, " - "displayId=%" PRId32 ", actionMasked=%d", - entry->deviceId, entry->source, entry->displayId, actionMasked); -#endif - return false; - } - - case AMOTION_EVENT_ACTION_DOWN: { - ssize_t index = findMotionMemento(entry, false /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - } - addMotionMemento(entry, flags, false /*hovering*/); - return true; - } - - case AMOTION_EVENT_ACTION_POINTER_UP: - case AMOTION_EVENT_ACTION_POINTER_DOWN: - case AMOTION_EVENT_ACTION_MOVE: { - if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) { - // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need to - // generate cancellation events for these since they're based in relative rather than - // absolute units. - return true; - } - - ssize_t index = findMotionMemento(entry, false /*hovering*/); - - if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) { - // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all - // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral. Any - // other value and we need to track the motion so we can send cancellation events for - // anything generating fallback events (e.g. DPad keys for joystick movements). - if (index >= 0) { - if (entry->pointerCoords[0].isEmpty()) { - mMotionMementos.erase(mMotionMementos.begin() + index); - } else { - MotionMemento& memento = mMotionMementos[index]; - memento.setPointers(entry); - } - } else if (!entry->pointerCoords[0].isEmpty()) { - addMotionMemento(entry, flags, false /*hovering*/); - } - - // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP. - return true; - } - if (index >= 0) { - MotionMemento& memento = mMotionMementos[index]; - memento.setPointers(entry); - return true; - } -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent motion pointer up/down or move event: " - "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d", - entry->deviceId, entry->source, entry->displayId, actionMasked); -#endif - return false; - } - - case AMOTION_EVENT_ACTION_HOVER_EXIT: { - ssize_t index = findMotionMemento(entry, true /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - return true; - } -#if DEBUG_OUTBOUND_EVENT_DETAILS - ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, " - "displayId=%" PRId32, - entry->deviceId, entry->source, entry->displayId); -#endif - return false; - } - - case AMOTION_EVENT_ACTION_HOVER_ENTER: - case AMOTION_EVENT_ACTION_HOVER_MOVE: { - ssize_t index = findMotionMemento(entry, true /*hovering*/); - if (index >= 0) { - mMotionMementos.erase(mMotionMementos.begin() + index); - } - addMotionMemento(entry, flags, true /*hovering*/); - return true; - } - - default: - return true; - } -} - -ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const { - for (size_t i = 0; i < mKeyMementos.size(); i++) { - const KeyMemento& memento = mKeyMementos[i]; - if (memento.deviceId == entry->deviceId - && memento.source == entry->source - && memento.displayId == entry->displayId - && memento.keyCode == entry->keyCode - && memento.scanCode == entry->scanCode) { - return i; - } - } - return -1; -} - -ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry, - bool hovering) const { - for (size_t i = 0; i < mMotionMementos.size(); i++) { - const MotionMemento& memento = mMotionMementos[i]; - if (memento.deviceId == entry->deviceId - && memento.source == entry->source - && memento.displayId == entry->displayId - && memento.hovering == hovering) { - return i; - } - } - return -1; -} - -void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) { - KeyMemento memento; - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.displayId = entry->displayId; - memento.keyCode = entry->keyCode; - memento.scanCode = entry->scanCode; - memento.metaState = entry->metaState; - memento.flags = flags; - memento.downTime = entry->downTime; - memento.policyFlags = entry->policyFlags; - mKeyMementos.push_back(memento); -} - -void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, - int32_t flags, bool hovering) { - MotionMemento memento; - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.displayId = entry->displayId; - memento.flags = flags; - memento.xPrecision = entry->xPrecision; - memento.yPrecision = entry->yPrecision; - memento.downTime = entry->downTime; - memento.setPointers(entry); - memento.hovering = hovering; - memento.policyFlags = entry->policyFlags; - mMotionMementos.push_back(memento); -} - -void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) { - pointerCount = entry->pointerCount; - for (uint32_t i = 0; i < entry->pointerCount; i++) { - pointerProperties[i].copyFrom(entry->pointerProperties[i]); - pointerCoords[i].copyFrom(entry->pointerCoords[i]); - } -} - -void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime, - std::vector<EventEntry*>& outEvents, const CancelationOptions& options) { - for (KeyMemento& memento : mKeyMementos) { - if (shouldCancelKey(memento, options)) { - outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - memento.deviceId, memento.source, memento.displayId, memento.policyFlags, - AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED, - memento.keyCode, memento.scanCode, memento.metaState, 0, memento.downTime)); - } - } - - for (const MotionMemento& memento : mMotionMementos) { - if (shouldCancelMotion(memento, options)) { - const int32_t action = memento.hovering ? - AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL; - outEvents.push_back(new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, - memento.deviceId, memento.source, memento.displayId, memento.policyFlags, - action, 0 /*actionButton*/, memento.flags, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - memento.xPrecision, memento.yPrecision, memento.downTime, - memento.pointerCount, memento.pointerProperties, memento.pointerCoords, - 0 /*xOffset*/, 0 /*yOffset*/)); - } - } -} - -void InputDispatcher::InputState::clear() { - mKeyMementos.clear(); - mMotionMementos.clear(); - mFallbackKeys.clear(); -} - -void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const { - for (size_t i = 0; i < mMotionMementos.size(); i++) { - const MotionMemento& memento = mMotionMementos[i]; - if (memento.source & AINPUT_SOURCE_CLASS_POINTER) { - for (size_t j = 0; j < other.mMotionMementos.size(); ) { - const MotionMemento& otherMemento = other.mMotionMementos[j]; - if (memento.deviceId == otherMemento.deviceId - && memento.source == otherMemento.source - && memento.displayId == otherMemento.displayId) { - other.mMotionMementos.erase(other.mMotionMementos.begin() + j); - } else { - j += 1; - } - } - other.mMotionMementos.push_back(memento); - } - } -} - -int32_t InputDispatcher::InputState::getFallbackKey(int32_t originalKeyCode) { - ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); - return index >= 0 ? mFallbackKeys.valueAt(index) : -1; -} - -void InputDispatcher::InputState::setFallbackKey(int32_t originalKeyCode, - int32_t fallbackKeyCode) { - ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); - if (index >= 0) { - mFallbackKeys.replaceValueAt(index, fallbackKeyCode); - } else { - mFallbackKeys.add(originalKeyCode, fallbackKeyCode); - } -} - -void InputDispatcher::InputState::removeFallbackKey(int32_t originalKeyCode) { - mFallbackKeys.removeItem(originalKeyCode); -} - -bool InputDispatcher::InputState::shouldCancelKey(const KeyMemento& memento, - const CancelationOptions& options) { - if (options.keyCode && memento.keyCode != options.keyCode.value()) { - return false; - } - - if (options.deviceId && memento.deviceId != options.deviceId.value()) { - return false; - } - - if (options.displayId && memento.displayId != options.displayId.value()) { - return false; - } - - switch (options.mode) { - case CancelationOptions::CANCEL_ALL_EVENTS: - case CancelationOptions::CANCEL_NON_POINTER_EVENTS: - return true; - case CancelationOptions::CANCEL_FALLBACK_EVENTS: - return memento.flags & AKEY_EVENT_FLAG_FALLBACK; - default: - return false; - } -} - -bool InputDispatcher::InputState::shouldCancelMotion(const MotionMemento& memento, - const CancelationOptions& options) { - if (options.deviceId && memento.deviceId != options.deviceId.value()) { - return false; - } - - if (options.displayId && memento.displayId != options.displayId.value()) { - return false; - } - - switch (options.mode) { - case CancelationOptions::CANCEL_ALL_EVENTS: - return true; - case CancelationOptions::CANCEL_POINTER_EVENTS: - return memento.source & AINPUT_SOURCE_CLASS_POINTER; - case CancelationOptions::CANCEL_NON_POINTER_EVENTS: - return !(memento.source & AINPUT_SOURCE_CLASS_POINTER); - default: - return false; - } -} - - -// --- InputDispatcher::Connection --- - -InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor) : - status(STATUS_NORMAL), inputChannel(inputChannel), - monitor(monitor), - inputPublisher(inputChannel), inputPublisherBlocked(false) { -} - -InputDispatcher::Connection::~Connection() { -} - -const std::string InputDispatcher::Connection::getWindowName() const { - if (inputChannel != nullptr) { - return inputChannel->getName(); - } - if (monitor) { - return "monitor"; - } - return "?"; -} - -const char* InputDispatcher::Connection::getStatusLabel() const { - switch (status) { - case STATUS_NORMAL: - return "NORMAL"; - - case STATUS_BROKEN: - return "BROKEN"; - - case STATUS_ZOMBIE: - return "ZOMBIE"; - - default: - return "UNKNOWN"; - } -} - -InputDispatcher::DispatchEntry* InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) { - for (DispatchEntry* entry = waitQueue.head; entry != nullptr; entry = entry->next) { - if (entry->seq == seq) { - return entry; - } - } - return nullptr; -} - -// --- InputDispatcher::Monitor -InputDispatcher::Monitor::Monitor(const sp<InputChannel>& inputChannel) : - inputChannel(inputChannel) { -} - - -// --- InputDispatcher::CommandEntry --- -// -InputDispatcher::CommandEntry::CommandEntry(Command command) : - command(command), eventTime(0), keyEntry(nullptr), userActivityEventType(0), - seq(0), handled(false) { -} - -InputDispatcher::CommandEntry::~CommandEntry() { -} - -// --- InputDispatcher::TouchedMonitor --- -InputDispatcher::TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, - float yOffset) : monitor(monitor), xOffset(xOffset), yOffset(yOffset) { -} - -// --- InputDispatcher::TouchState --- - -InputDispatcher::TouchState::TouchState() : - down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) { -} - -InputDispatcher::TouchState::~TouchState() { -} - -void InputDispatcher::TouchState::reset() { - down = false; - split = false; - deviceId = -1; - source = 0; - displayId = ADISPLAY_ID_NONE; - windows.clear(); - portalWindows.clear(); - gestureMonitors.clear(); -} - -void InputDispatcher::TouchState::copyFrom(const TouchState& other) { - down = other.down; - split = other.split; - deviceId = other.deviceId; - source = other.source; - displayId = other.displayId; - windows = other.windows; - portalWindows = other.portalWindows; - gestureMonitors = other.gestureMonitors; -} - -void InputDispatcher::TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, - int32_t targetFlags, BitSet32 pointerIds) { - if (targetFlags & InputTarget::FLAG_SPLIT) { - split = true; - } - - for (size_t i = 0; i < windows.size(); i++) { - TouchedWindow& touchedWindow = windows[i]; - if (touchedWindow.windowHandle == windowHandle) { - touchedWindow.targetFlags |= targetFlags; - if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { - touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS; - } - touchedWindow.pointerIds.value |= pointerIds.value; - return; - } - } - - TouchedWindow touchedWindow; - touchedWindow.windowHandle = windowHandle; - touchedWindow.targetFlags = targetFlags; - touchedWindow.pointerIds = pointerIds; - windows.push_back(touchedWindow); -} - -void InputDispatcher::TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) { - size_t numWindows = portalWindows.size(); - for (size_t i = 0; i < numWindows; i++) { - if (portalWindows[i] == windowHandle) { - return; - } - } - portalWindows.push_back(windowHandle); -} - -void InputDispatcher::TouchState::addGestureMonitors( - const std::vector<TouchedMonitor>& newMonitors) { - const size_t newSize = gestureMonitors.size() + newMonitors.size(); - gestureMonitors.reserve(newSize); - gestureMonitors.insert(std::end(gestureMonitors), - std::begin(newMonitors), std::end(newMonitors)); -} - -void InputDispatcher::TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) { - for (size_t i = 0; i < windows.size(); i++) { - if (windows[i].windowHandle == windowHandle) { - windows.erase(windows.begin() + i); - return; - } - } -} - -void InputDispatcher::TouchState::removeWindowByToken(const sp<IBinder>& token) { - for (size_t i = 0; i < windows.size(); i++) { - if (windows[i].windowHandle->getToken() == token) { - windows.erase(windows.begin() + i); - return; - } - } -} - -void InputDispatcher::TouchState::filterNonAsIsTouchWindows() { - for (size_t i = 0 ; i < windows.size(); ) { - TouchedWindow& window = windows[i]; - if (window.targetFlags & (InputTarget::FLAG_DISPATCH_AS_IS - | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) { - window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK; - window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS; - i += 1; - } else { - windows.erase(windows.begin() + i); - } - } -} - -void InputDispatcher::TouchState::filterNonMonitors() { - windows.clear(); - portalWindows.clear(); -} - -sp<InputWindowHandle> InputDispatcher::TouchState::getFirstForegroundWindowHandle() const { - for (size_t i = 0; i < windows.size(); i++) { - const TouchedWindow& window = windows[i]; - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { - return window.windowHandle; - } - } - return nullptr; -} - -bool InputDispatcher::TouchState::isSlippery() const { - // Must have exactly one foreground window. - bool haveSlipperyForegroundWindow = false; - for (const TouchedWindow& window : windows) { - if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { - if (haveSlipperyForegroundWindow - || !(window.windowHandle->getInfo()->layoutParamsFlags - & InputWindowInfo::FLAG_SLIPPERY)) { - return false; - } - haveSlipperyForegroundWindow = true; - } - } - return haveSlipperyForegroundWindow; -} - - -// --- InputDispatcherThread --- - -InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) : - Thread(/*canCallJava*/ true), mDispatcher(dispatcher) { -} - -InputDispatcherThread::~InputDispatcherThread() { -} - -bool InputDispatcherThread::threadLoop() { - mDispatcher->dispatchOnce(); - return true; -} - -} // namespace android +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h new file mode 100644 index 0000000000..67bf199cd0 --- /dev/null +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -0,0 +1,464 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_DISPATCHER_H +#define _UI_INPUT_DISPATCHER_H + +#include "CancelationOptions.h" +#include "Entry.h" +#include "InjectionState.h" +#include "InputDispatcherConfiguration.h" +#include "InputDispatcherInterface.h" +#include "InputDispatcherPolicyInterface.h" +#include "InputState.h" +#include "InputTarget.h" +#include "Monitor.h" +#include "Queue.h" +#include "TouchState.h" +#include "TouchedWindow.h" + +#include <cutils/atomic.h> +#include <input/Input.h> +#include <input/InputApplication.h> +#include <input/InputTransport.h> +#include <input/InputWindow.h> +#include <limits.h> +#include <stddef.h> +#include <ui/Region.h> +#include <unistd.h> +#include <utils/BitSet.h> +#include <utils/Looper.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> +#include <utils/threads.h> +#include <condition_variable> +#include <deque> +#include <optional> +#include <unordered_map> + +#include <InputListener.h> +#include <InputReporterInterface.h> + +namespace android::inputdispatcher { + +class Connection; + +/* Dispatches events to input targets. Some functions of the input dispatcher, such as + * identifying input targets, are controlled by a separate policy object. + * + * IMPORTANT INVARIANT: + * Because the policy can potentially block or cause re-entrance into the input dispatcher, + * the input dispatcher never calls into the policy while holding its internal locks. + * The implementation is also carefully designed to recover from scenarios such as an + * input channel becoming unregistered while identifying input targets or processing timeouts. + * + * Methods marked 'Locked' must be called with the lock acquired. + * + * Methods marked 'LockedInterruptible' must be called with the lock acquired but + * may during the course of their execution release the lock, call into the policy, and + * then reacquire the lock. The caller is responsible for recovering gracefully. + * + * A 'LockedInterruptible' method may called a 'Locked' method, but NOT vice-versa. + */ +class InputDispatcher : public android::InputDispatcherInterface { +protected: + virtual ~InputDispatcher(); + +public: + explicit InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy); + + virtual void dump(std::string& dump) override; + virtual void monitor() override; + + virtual void dispatchOnce() override; + + virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; + virtual void notifyKey(const NotifyKeyArgs* args) override; + virtual void notifyMotion(const NotifyMotionArgs* args) override; + virtual void notifySwitch(const NotifySwitchArgs* args) override; + virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + + virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, + int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, + uint32_t policyFlags) override; + + virtual void setInputWindows( + const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId, + const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) override; + virtual void setFocusedApplication( + int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) override; + virtual void setFocusedDisplay(int32_t displayId) override; + virtual void setInputDispatchMode(bool enabled, bool frozen) override; + virtual void setInputFilterEnabled(bool enabled) override; + + virtual bool transferTouchFocus(const sp<IBinder>& fromToken, + const sp<IBinder>& toToken) override; + + virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, + int32_t displayId) override; + virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId, + bool isGestureMonitor) override; + virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override; + virtual status_t pilferPointers(const sp<IBinder>& token) override; + +private: + + enum DropReason { + DROP_REASON_NOT_DROPPED = 0, + DROP_REASON_POLICY = 1, + DROP_REASON_APP_SWITCH = 2, + DROP_REASON_DISABLED = 3, + DROP_REASON_BLOCKED = 4, + DROP_REASON_STALE = 5, + }; + + sp<InputDispatcherPolicyInterface> mPolicy; + android::InputDispatcherConfiguration mConfig; + + std::mutex mLock; + + std::condition_variable mDispatcherIsAlive; + + sp<Looper> mLooper; + + EventEntry* mPendingEvent GUARDED_BY(mLock); + Queue<EventEntry> mInboundQueue GUARDED_BY(mLock); + Queue<EventEntry> mRecentQueue GUARDED_BY(mLock); + Queue<CommandEntry> mCommandQueue GUARDED_BY(mLock); + + DropReason mLastDropReason GUARDED_BY(mLock); + + void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock); + + // Enqueues an inbound event. Returns true if mLooper->wake() should be called. + bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock); + + // Cleans up input state when dropping an inbound event. + void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock); + + // Adds an event to a queue of recent events for debugging purposes. + void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock); + + // App switch latency optimization. + bool mAppSwitchSawKeyDown GUARDED_BY(mLock); + nsecs_t mAppSwitchDueTime GUARDED_BY(mLock); + + bool isAppSwitchKeyEvent(KeyEntry* keyEntry); + bool isAppSwitchPendingLocked() REQUIRES(mLock); + void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock); + + // Stale event latency optimization. + static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry); + + // Blocked event latency optimization. Drops old events when the user intends + // to transfer focus to a new application. + EventEntry* mNextUnblockedEvent GUARDED_BY(mLock); + + sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, + bool addOutsideTargets = false, + bool addPortalWindows = false) REQUIRES(mLock); + + // All registered connections mapped by channel file descriptor. + KeyedVector<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock); + + struct IBinderHash { + std::size_t operator()(const sp<IBinder>& b) const { + return std::hash<IBinder*>{}(b.get()); + } + }; + std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken + GUARDED_BY(mLock); + + // Finds the display ID of the gesture monitor identified by the provided token. + std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token) + REQUIRES(mLock); + + ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock); + + // Input channels that will receive a copy of all input events sent to the provided display. + std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock); + + // Input channels that will receive pointer events that start within the corresponding display. + // These are a bit special when compared to global monitors since they'll cause gesture streams + // to continue even when there isn't a touched window,and have the ability to steal the rest of + // the pointer stream in order to claim it for a system gesture. + std::unordered_map<int32_t, std::vector<Monitor>> mGestureMonitorsByDisplay GUARDED_BY(mLock); + + // Event injection and synchronization. + std::condition_variable mInjectionResultAvailable; + bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid); + void setInjectionResult(EventEntry* entry, int32_t injectionResult); + + std::condition_variable mInjectionSyncFinished; + void incrementPendingForegroundDispatches(EventEntry* entry); + void decrementPendingForegroundDispatches(EventEntry* entry); + + // Key repeat tracking. + struct KeyRepeatState { + KeyEntry* lastKeyEntry; // or null if no repeat + nsecs_t nextRepeatTime; + } mKeyRepeatState GUARDED_BY(mLock); + + void resetKeyRepeatLocked() REQUIRES(mLock); + KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock); + + // Key replacement tracking + struct KeyReplacement { + int32_t keyCode; + int32_t deviceId; + bool operator==(const KeyReplacement& rhs) const { + return keyCode == rhs.keyCode && deviceId == rhs.deviceId; + } + bool operator<(const KeyReplacement& rhs) const { + return keyCode != rhs.keyCode ? keyCode < rhs.keyCode : deviceId < rhs.deviceId; + } + }; + // Maps the key code replaced, device id tuple to the key code it was replaced with + KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock); + // Process certain Meta + Key combinations + void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode, + int32_t& metaState); + + // Deferred command processing. + bool haveCommandsLocked() const REQUIRES(mLock); + bool runCommandsLockedInterruptible() REQUIRES(mLock); + CommandEntry* postCommandLocked(Command command) REQUIRES(mLock); + + // Input filter processing. + bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); + bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock); + + // Inbound event processing. + void drainInboundQueueLocked() REQUIRES(mLock); + void releasePendingEventLocked() REQUIRES(mLock); + void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock); + + // Dispatch state. + bool mDispatchEnabled GUARDED_BY(mLock); + bool mDispatchFrozen GUARDED_BY(mLock); + bool mInputFilterEnabled GUARDED_BY(mLock); + + std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay + GUARDED_BY(mLock); + // Get window handles by display, return an empty vector if not found. + std::vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const + REQUIRES(mLock); + sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const + REQUIRES(mLock); + sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock); + bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock); + + // Focus tracking for keys, trackball, etc. + std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay + GUARDED_BY(mLock); + + KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock); + TouchState mTempTouchState GUARDED_BY(mLock); + + // Focused applications. + std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay + GUARDED_BY(mLock); + + // Top focused display. + int32_t mFocusedDisplayId GUARDED_BY(mLock); + + // Dispatcher state at time of last ANR. + std::string mLastANRState GUARDED_BY(mLock); + + // Dispatch inbound events. + bool dispatchConfigurationChangedLocked(nsecs_t currentTime, ConfigurationChangedEntry* entry) + REQUIRES(mLock); + bool dispatchDeviceResetLocked(nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock); + bool dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, + nsecs_t* nextWakeupTime) REQUIRES(mLock); + bool dispatchMotionLocked(nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, + nsecs_t* nextWakeupTime) REQUIRES(mLock); + void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry, + const std::vector<InputTarget>& inputTargets) REQUIRES(mLock); + + void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry); + void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry); + + // Keeping track of ANR timeouts. + enum InputTargetWaitCause { + INPUT_TARGET_WAIT_CAUSE_NONE, + INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY, + INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY, + }; + + InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock); + nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock); + nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock); + bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock); + sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock); + + // Contains the last window which received a hover event. + sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock); + + // Finding targets for input events. + int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, + const sp<InputApplicationHandle>& applicationHandle, + const sp<InputWindowHandle>& windowHandle, + nsecs_t* nextWakeupTime, const char* reason) + REQUIRES(mLock); + + void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock); + + void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, + const sp<InputChannel>& inputChannel) + REQUIRES(mLock); + nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); + void resetANRTimeoutsLocked() REQUIRES(mLock); + + int32_t getTargetDisplayId(const EventEntry* entry); + int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, + std::vector<InputTarget>& inputTargets, + nsecs_t* nextWakeupTime) REQUIRES(mLock); + int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, + std::vector<InputTarget>& inputTargets, + nsecs_t* nextWakeupTime, + bool* outConflictingPointerActions) REQUIRES(mLock); + std::vector<TouchedMonitor> findTouchedGestureMonitorsLocked( + int32_t displayId, const std::vector<sp<InputWindowHandle>>& portalWindows) + REQUIRES(mLock); + void addGestureMonitors(const std::vector<Monitor>& monitors, + std::vector<TouchedMonitor>& outTouchedMonitors, float xOffset = 0, + float yOffset = 0); + + void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, + BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) + REQUIRES(mLock); + void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset, + std::vector<InputTarget>& inputTargets) REQUIRES(mLock); + void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId, + float xOffset = 0, float yOffset = 0) REQUIRES(mLock); + + void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock); + bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, + const InjectionState* injectionState); + bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, int32_t x, + int32_t y) const REQUIRES(mLock); + bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock); + std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle, + const sp<InputWindowHandle>& windowHandle); + + std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime, + const sp<InputWindowHandle>& windowHandle, + const EventEntry* eventEntry, + const char* targetType) REQUIRES(mLock); + + // Manage the dispatch cycle for a single connection. + // These methods are deliberately not Interruptible because doing all of the work + // with the mutex held makes it easier to ensure that connection invariants are maintained. + // If needed, the methods post commands to run later once the critical bits are done. + void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, + EventEntry* eventEntry, const InputTarget* inputTarget) + REQUIRES(mLock); + void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, + EventEntry* eventEntry, const InputTarget* inputTarget) + REQUIRES(mLock); + void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry, + const InputTarget* inputTarget, int32_t dispatchMode) + REQUIRES(mLock); + void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) + REQUIRES(mLock); + void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, + uint32_t seq, bool handled) REQUIRES(mLock); + void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, + bool notify) REQUIRES(mLock); + void drainDispatchQueue(Queue<DispatchEntry>* queue); + void releaseDispatchEntry(DispatchEntry* dispatchEntry); + static int handleReceiveCallback(int fd, int events, void* data); + // The action sent should only be of type AMOTION_EVENT_* + void dispatchPointerDownOutsideFocus(uint32_t source, int32_t action, + const sp<IBinder>& newToken) REQUIRES(mLock); + + void synthesizeCancelationEventsForAllConnectionsLocked(const CancelationOptions& options) + REQUIRES(mLock); + void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options) + REQUIRES(mLock); + void synthesizeCancelationEventsForMonitorsLocked( + const CancelationOptions& options, + std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock); + void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel, + const CancelationOptions& options) + REQUIRES(mLock); + void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection, + const CancelationOptions& options) + REQUIRES(mLock); + + // Splitting motion events across windows. + MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds); + + // Reset and drop everything the dispatcher is doing. + void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock); + + // Dump state. + void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock); + void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors); + void logDispatchStateLocked() REQUIRES(mLock); + + // Registration. + void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock); + void removeMonitorChannelLocked( + const sp<InputChannel>& inputChannel, + std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock); + status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify) + REQUIRES(mLock); + + // Interesting events that we might like to log or tell the framework about. + void onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection, + uint32_t seq, bool handled) REQUIRES(mLock); + void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection) + REQUIRES(mLock); + void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus, + const sp<InputWindowHandle>& newFocus) REQUIRES(mLock); + void onANRLocked(nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle, + const sp<InputWindowHandle>& windowHandle, nsecs_t eventTime, + nsecs_t waitStartTime, const char* reason) REQUIRES(mLock); + + // Outbound policy interactions. + void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) + REQUIRES(mLock); + void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) + REQUIRES(mLock); + void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + bool afterKeyEventLockedInterruptible(const sp<Connection>& connection, + DispatchEntry* dispatchEntry, KeyEntry* keyEntry, + bool handled) REQUIRES(mLock); + bool afterMotionEventLockedInterruptible(const sp<Connection>& connection, + DispatchEntry* dispatchEntry, MotionEntry* motionEntry, + bool handled) REQUIRES(mLock); + void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry); + void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + + // Statistics gathering. + void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, + int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); + void traceInboundQueueLengthLocked() REQUIRES(mLock); + void traceOutboundQueueLength(const sp<Connection>& connection); + void traceWaitQueueLength(const sp<Connection>& connection); + + sp<InputReporterInterface> mReporter; +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_DISPATCHER_H diff --git a/cmds/installd/art_helper/art_image_values.cpp b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp index a139049d9f..8d7fa7573b 100644 --- a/cmds/installd/art_helper/art_image_values.cpp +++ b/services/inputflinger/dispatcher/InputDispatcherFactory.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,24 +14,14 @@ * limitations under the License. */ -#include "art_image_values.h" +#include "InputDispatcherFactory.h" +#include "InputDispatcher.h" namespace android { -namespace installd { -namespace art { -uint32_t GetImageBaseAddress() { - return ART_BASE_ADDRESS; +sp<InputDispatcherInterface> createInputDispatcher( + const sp<InputDispatcherPolicyInterface>& policy) { + return new android::inputdispatcher::InputDispatcher(policy); } -int32_t GetImageMinBaseAddressDelta() { - return ART_BASE_ADDRESS_MIN_DELTA; -} -int32_t GetImageMaxBaseAddressDelta() { - return ART_BASE_ADDRESS_MAX_DELTA; -} - -static_assert(ART_BASE_ADDRESS_MIN_DELTA < ART_BASE_ADDRESS_MAX_DELTA, "Inconsistent setup"); -} // namespace art -} // namespace installd -} // namespace android +} // namespace android diff --git a/libs/binder/include/binder/Map.h b/services/inputflinger/dispatcher/InputDispatcherThread.cpp index 96a4f8a2a5..18b1b8c10a 100644 --- a/libs/binder/include/binder/Map.h +++ b/services/inputflinger/dispatcher/InputDispatcherThread.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,26 +14,20 @@ * limitations under the License. */ -#ifndef ANDROID_MAP_H -#define ANDROID_MAP_H +#include "InputDispatcherThread.h" -#include <map> -#include <string> +#include "InputDispatcherInterface.h" -// --------------------------------------------------------------------------- namespace android { -namespace binder { -class Value; +InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) + : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {} -/** - * Convenience typedef for ::std::map<::std::string,::android::binder::Value> - */ -typedef ::std::map<::std::string, Value> Map; - -} // namespace binder -} // namespace android +InputDispatcherThread::~InputDispatcherThread() {} -// --------------------------------------------------------------------------- +bool InputDispatcherThread::threadLoop() { + mDispatcher->dispatchOnce(); + return true; +} -#endif // ANDROID_MAP_H +} // namespace android diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp new file mode 100644 index 0000000000..7d9b03a70e --- /dev/null +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InputState.h" + +namespace android::inputdispatcher { + +InputState::InputState() {} + +InputState::~InputState() {} + +bool InputState::isNeutral() const { + return mKeyMementos.empty() && mMotionMementos.empty(); +} + +bool InputState::isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const { + for (const MotionMemento& memento : mMotionMementos) { + if (memento.deviceId == deviceId && memento.source == source && + memento.displayId == displayId && memento.hovering) { + return true; + } + } + return false; +} + +bool InputState::trackKey(const KeyEntry* entry, int32_t action, int32_t flags) { + switch (action) { + case AKEY_EVENT_ACTION_UP: { + if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) { + for (size_t i = 0; i < mFallbackKeys.size();) { + if (mFallbackKeys.valueAt(i) == entry->keyCode) { + mFallbackKeys.removeItemsAt(i); + } else { + i += 1; + } + } + } + ssize_t index = findKeyMemento(entry); + if (index >= 0) { + mKeyMementos.erase(mKeyMementos.begin() + index); + return true; + } + /* FIXME: We can't just drop the key up event because that prevents creating + * popup windows that are automatically shown when a key is held and then + * dismissed when the key is released. The problem is that the popup will + * not have received the original key down, so the key up will be considered + * to be inconsistent with its observed state. We could perhaps handle this + * by synthesizing a key down but that will cause other problems. + * + * So for now, allow inconsistent key up events to be dispatched. + * + #if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, " + "keyCode=%d, scanCode=%d", + entry->deviceId, entry->source, entry->keyCode, entry->scanCode); + #endif + return false; + */ + return true; + } + + case AKEY_EVENT_ACTION_DOWN: { + ssize_t index = findKeyMemento(entry); + if (index >= 0) { + mKeyMementos.erase(mKeyMementos.begin() + index); + } + addKeyMemento(entry, flags); + return true; + } + + default: + return true; + } +} + +bool InputState::trackMotion(const MotionEntry* entry, int32_t action, int32_t flags) { + int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK; + switch (actionMasked) { + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, " + "displayId=%" PRId32 ", actionMasked=%d", + entry->deviceId, entry->source, entry->displayId, actionMasked); +#endif + return false; + } + + case AMOTION_EVENT_ACTION_DOWN: { + ssize_t index = findMotionMemento(entry, false /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + } + addMotionMemento(entry, flags, false /*hovering*/); + return true; + } + + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_MOVE: { + if (entry->source & AINPUT_SOURCE_CLASS_NAVIGATION) { + // Trackballs can send MOVE events with a corresponding DOWN or UP. There's no need + // to generate cancellation events for these since they're based in relative rather + // than absolute units. + return true; + } + + ssize_t index = findMotionMemento(entry, false /*hovering*/); + + if (entry->source & AINPUT_SOURCE_CLASS_JOYSTICK) { + // Joysticks can send MOVE events without a corresponding DOWN or UP. Since all + // joystick axes are normalized to [-1, 1] we can trust that 0 means it's neutral. + // Any other value and we need to track the motion so we can send cancellation + // events for anything generating fallback events (e.g. DPad keys for joystick + // movements). + if (index >= 0) { + if (entry->pointerCoords[0].isEmpty()) { + mMotionMementos.erase(mMotionMementos.begin() + index); + } else { + MotionMemento& memento = mMotionMementos[index]; + memento.setPointers(entry); + } + } else if (!entry->pointerCoords[0].isEmpty()) { + addMotionMemento(entry, flags, false /*hovering*/); + } + + // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP. + return true; + } + if (index >= 0) { + MotionMemento& memento = mMotionMementos[index]; + memento.setPointers(entry); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent motion pointer up/down or move event: " + "deviceId=%d, source=%08x, displayId=%" PRId32 ", actionMasked=%d", + entry->deviceId, entry->source, entry->displayId, actionMasked); +#endif + return false; + } + + case AMOTION_EVENT_ACTION_HOVER_EXIT: { + ssize_t index = findMotionMemento(entry, true /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + return true; + } +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x, " + "displayId=%" PRId32, + entry->deviceId, entry->source, entry->displayId); +#endif + return false; + } + + case AMOTION_EVENT_ACTION_HOVER_ENTER: + case AMOTION_EVENT_ACTION_HOVER_MOVE: { + ssize_t index = findMotionMemento(entry, true /*hovering*/); + if (index >= 0) { + mMotionMementos.erase(mMotionMementos.begin() + index); + } + addMotionMemento(entry, flags, true /*hovering*/); + return true; + } + + default: + return true; + } +} + +ssize_t InputState::findKeyMemento(const KeyEntry* entry) const { + for (size_t i = 0; i < mKeyMementos.size(); i++) { + const KeyMemento& memento = mKeyMementos[i]; + if (memento.deviceId == entry->deviceId && memento.source == entry->source && + memento.displayId == entry->displayId && memento.keyCode == entry->keyCode && + memento.scanCode == entry->scanCode) { + return i; + } + } + return -1; +} + +ssize_t InputState::findMotionMemento(const MotionEntry* entry, bool hovering) const { + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos[i]; + if (memento.deviceId == entry->deviceId && memento.source == entry->source && + memento.displayId == entry->displayId && memento.hovering == hovering) { + return i; + } + } + return -1; +} + +void InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) { + KeyMemento memento; + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.displayId = entry->displayId; + memento.keyCode = entry->keyCode; + memento.scanCode = entry->scanCode; + memento.metaState = entry->metaState; + memento.flags = flags; + memento.downTime = entry->downTime; + memento.policyFlags = entry->policyFlags; + mKeyMementos.push_back(memento); +} + +void InputState::addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering) { + MotionMemento memento; + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.displayId = entry->displayId; + memento.flags = flags; + memento.xPrecision = entry->xPrecision; + memento.yPrecision = entry->yPrecision; + memento.downTime = entry->downTime; + memento.setPointers(entry); + memento.hovering = hovering; + memento.policyFlags = entry->policyFlags; + mMotionMementos.push_back(memento); +} + +void InputState::MotionMemento::setPointers(const MotionEntry* entry) { + pointerCount = entry->pointerCount; + for (uint32_t i = 0; i < entry->pointerCount; i++) { + pointerProperties[i].copyFrom(entry->pointerProperties[i]); + pointerCoords[i].copyFrom(entry->pointerCoords[i]); + } +} + +void InputState::synthesizeCancelationEvents(nsecs_t currentTime, + std::vector<EventEntry*>& outEvents, + const CancelationOptions& options) { + for (KeyMemento& memento : mKeyMementos) { + if (shouldCancelKey(memento, options)) { + outEvents.push_back(new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, + memento.deviceId, memento.source, memento.displayId, + memento.policyFlags, AKEY_EVENT_ACTION_UP, + memento.flags | AKEY_EVENT_FLAG_CANCELED, + memento.keyCode, memento.scanCode, memento.metaState, + 0, memento.downTime)); + } + } + + for (const MotionMemento& memento : mMotionMementos) { + if (shouldCancelMotion(memento, options)) { + const int32_t action = memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT + : AMOTION_EVENT_ACTION_CANCEL; + outEvents.push_back( + new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, currentTime, memento.deviceId, + memento.source, memento.displayId, memento.policyFlags, action, + 0 /*actionButton*/, memento.flags, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, + memento.yPrecision, memento.downTime, memento.pointerCount, + memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/, + 0 /*yOffset*/)); + } + } +} + +void InputState::clear() { + mKeyMementos.clear(); + mMotionMementos.clear(); + mFallbackKeys.clear(); +} + +void InputState::copyPointerStateTo(InputState& other) const { + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos[i]; + if (memento.source & AINPUT_SOURCE_CLASS_POINTER) { + for (size_t j = 0; j < other.mMotionMementos.size();) { + const MotionMemento& otherMemento = other.mMotionMementos[j]; + if (memento.deviceId == otherMemento.deviceId && + memento.source == otherMemento.source && + memento.displayId == otherMemento.displayId) { + other.mMotionMementos.erase(other.mMotionMementos.begin() + j); + } else { + j += 1; + } + } + other.mMotionMementos.push_back(memento); + } + } +} + +int32_t InputState::getFallbackKey(int32_t originalKeyCode) { + ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); + return index >= 0 ? mFallbackKeys.valueAt(index) : -1; +} + +void InputState::setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode) { + ssize_t index = mFallbackKeys.indexOfKey(originalKeyCode); + if (index >= 0) { + mFallbackKeys.replaceValueAt(index, fallbackKeyCode); + } else { + mFallbackKeys.add(originalKeyCode, fallbackKeyCode); + } +} + +void InputState::removeFallbackKey(int32_t originalKeyCode) { + mFallbackKeys.removeItem(originalKeyCode); +} + +bool InputState::shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options) { + if (options.keyCode && memento.keyCode != options.keyCode.value()) { + return false; + } + + if (options.deviceId && memento.deviceId != options.deviceId.value()) { + return false; + } + + if (options.displayId && memento.displayId != options.displayId.value()) { + return false; + } + + switch (options.mode) { + case CancelationOptions::CANCEL_ALL_EVENTS: + case CancelationOptions::CANCEL_NON_POINTER_EVENTS: + return true; + case CancelationOptions::CANCEL_FALLBACK_EVENTS: + return memento.flags & AKEY_EVENT_FLAG_FALLBACK; + default: + return false; + } +} + +bool InputState::shouldCancelMotion(const MotionMemento& memento, + const CancelationOptions& options) { + if (options.deviceId && memento.deviceId != options.deviceId.value()) { + return false; + } + + if (options.displayId && memento.displayId != options.displayId.value()) { + return false; + } + + switch (options.mode) { + case CancelationOptions::CANCEL_ALL_EVENTS: + return true; + case CancelationOptions::CANCEL_POINTER_EVENTS: + return memento.source & AINPUT_SOURCE_CLASS_POINTER; + case CancelationOptions::CANCEL_NON_POINTER_EVENTS: + return !(memento.source & AINPUT_SOURCE_CLASS_POINTER); + default: + return false; + } +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h new file mode 100644 index 0000000000..205b64728a --- /dev/null +++ b/services/inputflinger/dispatcher/InputState.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H + +#include "CancelationOptions.h" +#include "Entry.h" + +#include <utils/Timers.h> + +namespace android::inputdispatcher { + +// Sequence number for synthesized or injected events. +constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; + +/* Tracks dispatched key and motion event state so that cancellation events can be + * synthesized when events are dropped. */ +class InputState { +public: + InputState(); + ~InputState(); + + // Returns true if there is no state to be canceled. + bool isNeutral() const; + + // Returns true if the specified source is known to have received a hover enter + // motion event. + bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const; + + // Records tracking information for a key event that has just been published. + // Returns true if the event should be delivered, false if it is inconsistent + // and should be skipped. + bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags); + + // Records tracking information for a motion event that has just been published. + // Returns true if the event should be delivered, false if it is inconsistent + // and should be skipped. + bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags); + + // Synthesizes cancelation events for the current state and resets the tracked state. + void synthesizeCancelationEvents(nsecs_t currentTime, std::vector<EventEntry*>& outEvents, + const CancelationOptions& options); + + // Clears the current state. + void clear(); + + // Copies pointer-related parts of the input state to another instance. + void copyPointerStateTo(InputState& other) const; + + // Gets the fallback key associated with a keycode. + // Returns -1 if none. + // Returns AKEYCODE_UNKNOWN if we are only dispatching the unhandled key to the policy. + int32_t getFallbackKey(int32_t originalKeyCode); + + // Sets the fallback key for a particular keycode. + void setFallbackKey(int32_t originalKeyCode, int32_t fallbackKeyCode); + + // Removes the fallback key for a particular keycode. + void removeFallbackKey(int32_t originalKeyCode); + + inline const KeyedVector<int32_t, int32_t>& getFallbackKeys() const { return mFallbackKeys; } + +private: + struct KeyMemento { + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + int32_t flags; + nsecs_t downTime; + uint32_t policyFlags; + }; + + struct MotionMemento { + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t flags; + float xPrecision; + float yPrecision; + nsecs_t downTime; + uint32_t pointerCount; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + bool hovering; + uint32_t policyFlags; + + void setPointers(const MotionEntry* entry); + }; + + std::vector<KeyMemento> mKeyMementos; + std::vector<MotionMemento> mMotionMementos; + KeyedVector<int32_t, int32_t> mFallbackKeys; + + ssize_t findKeyMemento(const KeyEntry* entry) const; + ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const; + + void addKeyMemento(const KeyEntry* entry, int32_t flags); + void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering); + + static bool shouldCancelKey(const KeyMemento& memento, const CancelationOptions& options); + static bool shouldCancelMotion(const MotionMemento& memento, const CancelationOptions& options); +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTSTATE_H diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp new file mode 100644 index 0000000000..80fa2cb995 --- /dev/null +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InputTarget.h" + +#include <android-base/stringprintf.h> +#include <inttypes.h> +#include <string> + +using android::base::StringPrintf; + +namespace android::inputdispatcher { + +std::string dispatchModeToString(int32_t dispatchMode) { + switch (dispatchMode) { + case InputTarget::FLAG_DISPATCH_AS_IS: + return "DISPATCH_AS_IS"; + case InputTarget::FLAG_DISPATCH_AS_OUTSIDE: + return "DISPATCH_AS_OUTSIDE"; + case InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER: + return "DISPATCH_AS_HOVER_ENTER"; + case InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT: + return "DISPATCH_AS_HOVER_EXIT"; + case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT: + return "DISPATCH_AS_SLIPPERY_EXIT"; + case InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER: + return "DISPATCH_AS_SLIPPERY_ENTER"; + } + return StringPrintf("%" PRId32, dispatchMode); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h new file mode 100644 index 0000000000..5acf92b1b1 --- /dev/null +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H + +#include <input/InputTransport.h> +#include <utils/BitSet.h> +#include <utils/RefBase.h> + +namespace android::inputdispatcher { + +/* + * An input target specifies how an input event is to be dispatched to a particular window + * including the window's input channel, control flags, a timeout, and an X / Y offset to + * be added to input event coordinates to compensate for the absolute position of the + * window area. + */ +struct InputTarget { + enum { + /* This flag indicates that the event is being delivered to a foreground application. */ + FLAG_FOREGROUND = 1 << 0, + + /* This flag indicates that the MotionEvent falls within the area of the target + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ + FLAG_WINDOW_IS_OBSCURED = 1 << 1, + + /* This flag indicates that a motion event is being split across multiple windows. */ + FLAG_SPLIT = 1 << 2, + + /* This flag indicates that the pointer coordinates dispatched to the application + * will be zeroed out to avoid revealing information to an application. This is + * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing + * the same UID from watching all touches. */ + FLAG_ZERO_COORDS = 1 << 3, + + /* This flag indicates that the event should be sent as is. + * Should always be set unless the event is to be transmuted. */ + FLAG_DISPATCH_AS_IS = 1 << 8, + + /* This flag indicates that a MotionEvent with AMOTION_EVENT_ACTION_DOWN falls outside + * of the area of this target and so should instead be delivered as an + * AMOTION_EVENT_ACTION_OUTSIDE to this target. */ + FLAG_DISPATCH_AS_OUTSIDE = 1 << 9, + + /* This flag indicates that a hover sequence is starting in the given window. + * The event is transmuted into ACTION_HOVER_ENTER. */ + FLAG_DISPATCH_AS_HOVER_ENTER = 1 << 10, + + /* This flag indicates that a hover event happened outside of a window which handled + * previous hover events, signifying the end of the current hover sequence for that + * window. + * The event is transmuted into ACTION_HOVER_ENTER. */ + FLAG_DISPATCH_AS_HOVER_EXIT = 1 << 11, + + /* This flag indicates that the event should be canceled. + * It is used to transmute ACTION_MOVE into ACTION_CANCEL when a touch slips + * outside of a window. */ + FLAG_DISPATCH_AS_SLIPPERY_EXIT = 1 << 12, + + /* This flag indicates that the event should be dispatched as an initial down. + * It is used to transmute ACTION_MOVE into ACTION_DOWN when a touch slips + * into a new window. */ + FLAG_DISPATCH_AS_SLIPPERY_ENTER = 1 << 13, + + /* Mask for all dispatch modes. */ + FLAG_DISPATCH_MASK = FLAG_DISPATCH_AS_IS | FLAG_DISPATCH_AS_OUTSIDE | + FLAG_DISPATCH_AS_HOVER_ENTER | FLAG_DISPATCH_AS_HOVER_EXIT | + FLAG_DISPATCH_AS_SLIPPERY_EXIT | FLAG_DISPATCH_AS_SLIPPERY_ENTER, + + /* This flag indicates that the target of a MotionEvent is partly or wholly + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ + FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, + + }; + + // The input channel to be targeted. + sp<InputChannel> inputChannel; + + // Flags for the input target. + int32_t flags; + + // The x and y offset to add to a MotionEvent as it is delivered. + // (ignored for KeyEvents) + float xOffset, yOffset; + + // Scaling factor to apply to MotionEvent as it is delivered. + // (ignored for KeyEvents) + float globalScaleFactor; + float windowXScale = 1.0f; + float windowYScale = 1.0f; + + // The subset of pointer ids to include in motion events dispatched to this input target + // if FLAG_SPLIT is set. + BitSet32 pointerIds; +}; + +std::string dispatchModeToString(int32_t dispatchMode); + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTTARGET_H diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp new file mode 100644 index 0000000000..289b0848bf --- /dev/null +++ b/services/inputflinger/dispatcher/Monitor.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Monitor.h" + +namespace android::inputdispatcher { + +// --- Monitor --- +Monitor::Monitor(const sp<InputChannel>& inputChannel) : inputChannel(inputChannel) {} + +// --- TouchedMonitor --- +TouchedMonitor::TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset) + : monitor(monitor), xOffset(xOffset), yOffset(yOffset) {} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h new file mode 100644 index 0000000000..b67c9eb507 --- /dev/null +++ b/services/inputflinger/dispatcher/Monitor.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_MONITOR_H +#define _UI_INPUT_INPUTDISPATCHER_MONITOR_H + +#include <input/InputTransport.h> + +namespace android::inputdispatcher { + +struct Monitor { + sp<InputChannel> inputChannel; // never null + + explicit Monitor(const sp<InputChannel>& inputChannel); +}; + +// For tracking the offsets we need to apply when adding gesture monitor targets. +struct TouchedMonitor { + Monitor monitor; + float xOffset = 0.f; + float yOffset = 0.f; + + explicit TouchedMonitor(const Monitor& monitor, float xOffset, float yOffset); +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_MONITOR_H diff --git a/services/inputflinger/dispatcher/Queue.h b/services/inputflinger/dispatcher/Queue.h new file mode 100644 index 0000000000..0e75821661 --- /dev/null +++ b/services/inputflinger/dispatcher/Queue.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_QUEUE_H +#define _UI_INPUT_INPUTDISPATCHER_QUEUE_H + +namespace android::inputdispatcher { + +// Generic queue implementation. +template <typename T> +struct Queue { + T* head; + T* tail; + uint32_t entryCount; + + inline Queue() : head(nullptr), tail(nullptr), entryCount(0) {} + + inline bool isEmpty() const { return !head; } + + inline void enqueueAtTail(T* entry) { + entryCount++; + entry->prev = tail; + if (tail) { + tail->next = entry; + } else { + head = entry; + } + entry->next = nullptr; + tail = entry; + } + + inline void enqueueAtHead(T* entry) { + entryCount++; + entry->next = head; + if (head) { + head->prev = entry; + } else { + tail = entry; + } + entry->prev = nullptr; + head = entry; + } + + inline void dequeue(T* entry) { + entryCount--; + if (entry->prev) { + entry->prev->next = entry->next; + } else { + head = entry->next; + } + if (entry->next) { + entry->next->prev = entry->prev; + } else { + tail = entry->prev; + } + } + + inline T* dequeueAtHead() { + entryCount--; + T* entry = head; + head = entry->next; + if (head) { + head->prev = nullptr; + } else { + tail = nullptr; + } + return entry; + } + + uint32_t count() const { return entryCount; } +}; + +} // namespace android::inputdispatcher + +#endif // _UI_INPUT_INPUTDISPATCHER_QUEUE_H diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp new file mode 100644 index 0000000000..18848a0c2f --- /dev/null +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <input/InputWindow.h> + +#include "InputTarget.h" + +#include "TouchState.h" + +using android::InputWindowHandle; + +namespace android::inputdispatcher { + +TouchState::TouchState() + : down(false), split(false), deviceId(-1), source(0), displayId(ADISPLAY_ID_NONE) {} + +TouchState::~TouchState() {} + +void TouchState::reset() { + down = false; + split = false; + deviceId = -1; + source = 0; + displayId = ADISPLAY_ID_NONE; + windows.clear(); + portalWindows.clear(); + gestureMonitors.clear(); +} + +void TouchState::copyFrom(const TouchState& other) { + down = other.down; + split = other.split; + deviceId = other.deviceId; + source = other.source; + displayId = other.displayId; + windows = other.windows; + portalWindows = other.portalWindows; + gestureMonitors = other.gestureMonitors; +} + +void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags, + BitSet32 pointerIds) { + if (targetFlags & InputTarget::FLAG_SPLIT) { + split = true; + } + + for (size_t i = 0; i < windows.size(); i++) { + TouchedWindow& touchedWindow = windows[i]; + if (touchedWindow.windowHandle == windowHandle) { + touchedWindow.targetFlags |= targetFlags; + if (targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) { + touchedWindow.targetFlags &= ~InputTarget::FLAG_DISPATCH_AS_IS; + } + touchedWindow.pointerIds.value |= pointerIds.value; + return; + } + } + + TouchedWindow touchedWindow; + touchedWindow.windowHandle = windowHandle; + touchedWindow.targetFlags = targetFlags; + touchedWindow.pointerIds = pointerIds; + windows.push_back(touchedWindow); +} + +void TouchState::addPortalWindow(const sp<InputWindowHandle>& windowHandle) { + size_t numWindows = portalWindows.size(); + for (size_t i = 0; i < numWindows; i++) { + if (portalWindows[i] == windowHandle) { + return; + } + } + portalWindows.push_back(windowHandle); +} + +void TouchState::addGestureMonitors(const std::vector<TouchedMonitor>& newMonitors) { + const size_t newSize = gestureMonitors.size() + newMonitors.size(); + gestureMonitors.reserve(newSize); + gestureMonitors.insert(std::end(gestureMonitors), std::begin(newMonitors), + std::end(newMonitors)); +} + +void TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) { + for (size_t i = 0; i < windows.size(); i++) { + if (windows[i].windowHandle == windowHandle) { + windows.erase(windows.begin() + i); + return; + } + } +} + +void TouchState::removeWindowByToken(const sp<IBinder>& token) { + for (size_t i = 0; i < windows.size(); i++) { + if (windows[i].windowHandle->getToken() == token) { + windows.erase(windows.begin() + i); + return; + } + } +} + +void TouchState::filterNonAsIsTouchWindows() { + for (size_t i = 0; i < windows.size();) { + TouchedWindow& window = windows[i]; + if (window.targetFlags & + (InputTarget::FLAG_DISPATCH_AS_IS | InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER)) { + window.targetFlags &= ~InputTarget::FLAG_DISPATCH_MASK; + window.targetFlags |= InputTarget::FLAG_DISPATCH_AS_IS; + i += 1; + } else { + windows.erase(windows.begin() + i); + } + } +} + +void TouchState::filterNonMonitors() { + windows.clear(); + portalWindows.clear(); +} + +sp<InputWindowHandle> TouchState::getFirstForegroundWindowHandle() const { + for (size_t i = 0; i < windows.size(); i++) { + const TouchedWindow& window = windows[i]; + if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + return window.windowHandle; + } + } + return nullptr; +} + +bool TouchState::isSlippery() const { + // Must have exactly one foreground window. + bool haveSlipperyForegroundWindow = false; + for (const TouchedWindow& window : windows) { + if (window.targetFlags & InputTarget::FLAG_FOREGROUND) { + if (haveSlipperyForegroundWindow || + !(window.windowHandle->getInfo()->layoutParamsFlags & + InputWindowInfo::FLAG_SLIPPERY)) { + return false; + } + haveSlipperyForegroundWindow = true; + } + } + return haveSlipperyForegroundWindow; +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h new file mode 100644 index 0000000000..3e0e0eb897 --- /dev/null +++ b/services/inputflinger/dispatcher/TouchState.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H +#define _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H + +#include "Monitor.h" +#include "TouchedWindow.h" + +namespace android { + +class InputWindowHandle; + +namespace inputdispatcher { + +struct TouchState { + bool down; + bool split; + int32_t deviceId; // id of the device that is currently down, others are rejected + uint32_t source; // source of the device that is current down, others are rejected + int32_t displayId; // id to the display that currently has a touch, others are rejected + std::vector<TouchedWindow> windows; + + // This collects the portal windows that the touch has gone through. Each portal window + // targets a display (embedded display for most cases). With this info, we can add the + // monitoring channels of the displays touched. + std::vector<sp<android::InputWindowHandle>> portalWindows; + + std::vector<TouchedMonitor> gestureMonitors; + + TouchState(); + ~TouchState(); + void reset(); + void copyFrom(const TouchState& other); + void addOrUpdateWindow(const sp<android::InputWindowHandle>& windowHandle, int32_t targetFlags, + BitSet32 pointerIds); + void addPortalWindow(const sp<android::InputWindowHandle>& windowHandle); + void addGestureMonitors(const std::vector<TouchedMonitor>& monitors); + void removeWindow(const sp<android::InputWindowHandle>& windowHandle); + void removeWindowByToken(const sp<IBinder>& token); + void filterNonAsIsTouchWindows(); + void filterNonMonitors(); + sp<InputWindowHandle> getFirstForegroundWindowHandle() const; + bool isSlippery() const; +}; + +} // namespace inputdispatcher +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHSTATE_H diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h new file mode 100644 index 0000000000..8713aa3f56 --- /dev/null +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H +#define _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H + +namespace android { + +class InputWindowHandle; + +namespace inputdispatcher { + +// Focus tracking for touch. +struct TouchedWindow { + sp<android::InputWindowHandle> windowHandle; + int32_t targetFlags; + BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set +}; + +} // namespace inputdispatcher +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_TOUCHEDWINDOW_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h new file mode 100644 index 0000000000..00abf47cd2 --- /dev/null +++ b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H + +#include <utils/Timers.h> + +namespace android { + +/* + * Input dispatcher configuration. + * + * Specifies various options that modify the behavior of the input dispatcher. + * The values provided here are merely defaults. The actual values will come from ViewConfiguration + * and are passed into the dispatcher during initialization. + */ +struct InputDispatcherConfiguration { + // The key repeat initial timeout. + nsecs_t keyRepeatTimeout; + + // The key repeat inter-key delay. + nsecs_t keyRepeatDelay; + + InputDispatcherConfiguration() + : keyRepeatTimeout(500 * 1000000LL), keyRepeatDelay(50 * 1000000LL) {} +}; + +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERCONFIGURATION_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherFactory.h b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h new file mode 100644 index 0000000000..a359557f80 --- /dev/null +++ b/services/inputflinger/dispatcher/include/InputDispatcherFactory.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H + +#include <utils/RefBase.h> + +#include "InputDispatcherInterface.h" +#include "InputDispatcherPolicyInterface.h" + +namespace android { + +// This factory method is used to encapsulate implementation details in internal header files. +sp<InputDispatcherInterface> createInputDispatcher( + const sp<InputDispatcherPolicyInterface>& policy); + +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERFACTORY_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h new file mode 100644 index 0000000000..9329ca664e --- /dev/null +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H + +#include <InputListener.h> +#include <input/ISetInputWindowsListener.h> + +namespace android { + +class InputApplicationHandle; +class InputChannel; +class InputWindowHandle; + +/* + * Constants used to report the outcome of input event injection. + */ +enum { + /* (INTERNAL USE ONLY) Specifies that injection is pending and its outcome is unknown. */ + INPUT_EVENT_INJECTION_PENDING = -1, + + /* Injection succeeded. */ + INPUT_EVENT_INJECTION_SUCCEEDED = 0, + + /* Injection failed because the injector did not have permission to inject + * into the application with input focus. */ + INPUT_EVENT_INJECTION_PERMISSION_DENIED = 1, + + /* Injection failed because there were no available input targets. */ + INPUT_EVENT_INJECTION_FAILED = 2, + + /* Injection failed due to a timeout. */ + INPUT_EVENT_INJECTION_TIMED_OUT = 3 +}; + +/* Notifies the system about input events generated by the input reader. + * The dispatcher is expected to be mostly asynchronous. */ +class InputDispatcherInterface : public virtual RefBase, public InputListenerInterface { +protected: + InputDispatcherInterface() {} + virtual ~InputDispatcherInterface() {} + +public: + /* Dumps the state of the input dispatcher. + * + * This method may be called on any thread (usually by the input manager). */ + virtual void dump(std::string& dump) = 0; + + /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ + virtual void monitor() = 0; + + /* Runs a single iteration of the dispatch loop. + * Nominally processes one queued event, a timeout, or a response from an input consumer. + * + * This method should only be called on the input dispatcher thread. + */ + virtual void dispatchOnce() = 0; + + /* Injects an input event and optionally waits for sync. + * The synchronization mode determines whether the method blocks while waiting for + * input injection to proceed. + * Returns one of the INPUT_EVENT_INJECTION_XXX constants. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual int32_t injectInputEvent(const InputEvent* event, int32_t injectorPid, + int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, + uint32_t policyFlags) = 0; + + /* Sets the list of input windows. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setInputWindows( + const std::vector<sp<InputWindowHandle> >& inputWindowHandles, int32_t displayId, + const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) = 0; + + /* Sets the focused application on the given display. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setFocusedApplication( + int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) = 0; + + /* Sets the focused display. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setFocusedDisplay(int32_t displayId) = 0; + + /* Sets the input dispatching mode. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual void setInputDispatchMode(bool enabled, bool frozen) = 0; + + /* Sets whether input event filtering is enabled. + * When enabled, incoming input events are sent to the policy's filterInputEvent + * method instead of being dispatched. The filter is expected to use + * injectInputEvent to inject the events it would like to have dispatched. + * It should include POLICY_FLAG_FILTERED in the policy flags during injection. + */ + virtual void setInputFilterEnabled(bool enabled) = 0; + + /* Transfers touch focus from one window to another window. + * + * Returns true on success. False if the window did not actually have touch focus. + */ + virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0; + + /* Registers input channels that may be used as targets for input events. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, + int32_t displayId) = 0; + + /* Registers input channels to be used to monitor input events. + * + * Each monitor must target a specific display and will only receive input events sent to that + * display. If the monitor is a gesture monitor, it will only receive pointer events on the + * targeted display. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId, + bool gestureMonitor) = 0; + + /* Unregister input channels that will no longer receive input events. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) = 0; + + /* Allows an input monitor steal the current pointer stream away from normal input windows. + * + * This method may be called on any thread (usually by the input manager). + */ + virtual status_t pilferPointers(const sp<IBinder>& token) = 0; +}; + +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERINTERFACE_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h new file mode 100644 index 0000000000..4214488f04 --- /dev/null +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H + +#include "InputDispatcherConfiguration.h" + +#include <binder/IBinder.h> +#include <input/Input.h> +#include <utils/RefBase.h> + +namespace android { + +class InputApplicationHandle; + +/* + * Input dispatcher policy interface. + * + * The input reader policy is used by the input reader to interact with the Window Manager + * and other system components. + * + * The actual implementation is partially supported by callbacks into the DVM + * via JNI. This interface is also mocked in the unit tests. + */ +class InputDispatcherPolicyInterface : public virtual RefBase { +protected: + InputDispatcherPolicyInterface() {} + virtual ~InputDispatcherPolicyInterface() {} + +public: + /* Notifies the system that a configuration change has occurred. */ + virtual void notifyConfigurationChanged(nsecs_t when) = 0; + + /* Notifies the system that an application is not responding. + * Returns a new timeout to continue waiting, or 0 to abort dispatch. */ + virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, + const sp<IBinder>& token, const std::string& reason) = 0; + + /* Notifies the system that an input channel is unrecoverably broken. */ + virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0; + virtual void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) = 0; + + /* Gets the input dispatcher configuration. */ + virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) = 0; + + /* Filters an input event. + * Return true to dispatch the event unmodified, false to consume the event. + * A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED + * to injectInputEvent. + */ + virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) = 0; + + /* Intercepts a key event immediately before queueing it. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing such as updating policy flags. + * + * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event + * should be dispatched to applications. + */ + virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags) = 0; + + /* Intercepts a touch, trackball or other motion event before queueing it. + * The policy can use this method as an opportunity to perform power management functions + * and early event preprocessing such as updating policy flags. + * + * This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event + * should be dispatched to applications. + */ + virtual void interceptMotionBeforeQueueing(const int32_t displayId, nsecs_t when, + uint32_t& policyFlags) = 0; + + /* Allows the policy a chance to intercept a key before dispatching. */ + virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, + const KeyEvent* keyEvent, + uint32_t policyFlags) = 0; + + /* Allows the policy a chance to perform default processing for an unhandled key. + * Returns an alternate keycode to redispatch as a fallback, or 0 to give up. */ + virtual bool dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent* keyEvent, + uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) = 0; + + /* Notifies the policy about switch events. + */ + virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, + uint32_t policyFlags) = 0; + + /* Poke user activity for an event dispatched to a window. */ + virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0; + + /* Checks whether a given application pid/uid has permission to inject input events + * into other applications. + * + * This method is special in that its implementation promises to be non-reentrant and + * is safe to call while holding other locks. (Most other methods make no such guarantees!) + */ + virtual bool checkInjectEventsPermissionNonReentrant(int32_t injectorPid, + int32_t injectorUid) = 0; + + /* Notifies the policy that a pointer down event has occurred outside the current focused + * window. + * + * The touchedToken passed as an argument is the window that received the input event. + */ + virtual void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) = 0; +}; + +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERPOLICYINTERFACE_H diff --git a/services/inputflinger/dispatcher/include/InputDispatcherThread.h b/services/inputflinger/dispatcher/include/InputDispatcherThread.h new file mode 100644 index 0000000000..2604959656 --- /dev/null +++ b/services/inputflinger/dispatcher/include/InputDispatcherThread.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H +#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H + +#include <utils/RefBase.h> +#include <utils/threads.h> + +namespace android { + +class InputDispatcherInterface; + +/* Enqueues and dispatches input events, endlessly. */ +class InputDispatcherThread : public Thread { +public: + explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher); + ~InputDispatcherThread(); + +private: + virtual bool threadLoop(); + + sp<InputDispatcherInterface> mDispatcher; +}; + +} // namespace android + +#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp index 2f046c3527..683c05d8a0 100644 --- a/services/inputflinger/host/InputDriver.cpp +++ b/services/inputflinger/host/InputDriver.cpp @@ -127,10 +127,10 @@ input_device_identifier_t* InputDriver::createDeviceIdentifier( input_bus_t bus, const char* uniqueId) { auto identifier = new ::input_device_identifier { .name = name, - .productId = productId, - .vendorId = vendorId, - .bus = bus, .uniqueId = uniqueId, + .bus = bus, + .vendorId = vendorId, + .productId = productId, }; // TODO: store this identifier somewhere return identifier; diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp new file mode 100644 index 0000000000..4e97397e2b --- /dev/null +++ b/services/inputflinger/reader/Android.bp @@ -0,0 +1,72 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library_headers { + name: "libinputreader_headers", + export_include_dirs: [ + "include", + "mapper", + "mapper/accumulator", + ], +} + +cc_library_shared { + name: "libinputreader", + defaults: ["inputflinger_defaults"], + + srcs: [ + "EventHub.cpp", + "InputDevice.cpp", + "mapper/accumulator/CursorButtonAccumulator.cpp", + "mapper/accumulator/CursorScrollAccumulator.cpp", + "mapper/accumulator/SingleTouchMotionAccumulator.cpp", + "mapper/accumulator/TouchButtonAccumulator.cpp", + "mapper/CursorInputMapper.cpp", + "mapper/ExternalStylusInputMapper.cpp", + "mapper/InputMapper.cpp", + "mapper/JoystickInputMapper.cpp", + "mapper/KeyboardInputMapper.cpp", + "mapper/MultiTouchInputMapper.cpp", + "mapper/RotaryEncoderInputMapper.cpp", + "mapper/SingleTouchInputMapper.cpp", + "mapper/SwitchInputMapper.cpp", + "mapper/TouchInputMapper.cpp", + "mapper/VibratorInputMapper.cpp", + "InputReader.cpp", + "InputReaderFactory.cpp", + "TouchVideoDevice.cpp", + ], + + shared_libs: [ + "libbase", + "libinputflinger_base", + "libcrypto", + "libcutils", + "libinput", + "liblog", + "libui", + "libutils", + "libhardware_legacy", + "libstatslog", + ], + + header_libs: [ + "libinputflinger_headers", + "libinputreader_headers", + ], + + export_header_lib_headers: [ + "libinputflinger_headers", + ], +} diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index ce5627271a..a5e5415dd7 100644 --- a/services/inputflinger/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -25,9 +25,9 @@ #include <stdlib.h> #include <string.h> #include <sys/epoll.h> -#include <sys/limits.h> #include <sys/inotify.h> #include <sys/ioctl.h> +#include <sys/limits.h> #include <sys/utsname.h> #include <unistd.h> @@ -42,13 +42,13 @@ #include <android-base/stringprintf.h> #include <cutils/properties.h> #include <openssl/sha.h> +#include <utils/Errors.h> #include <utils/Log.h> #include <utils/Timers.h> #include <utils/threads.h> -#include <utils/Errors.h> -#include <input/KeyLayoutMap.h> #include <input/KeyCharacterMap.h> +#include <input/KeyLayoutMap.h> #include <input/VirtualKeyMap.h> /* this macro is used to tell if "bit" is set in "array" @@ -56,10 +56,10 @@ * operation with a byte that only has the relevant bit set. * eg. to check for the 12th bit, we do (array[1] & 1<<4) */ -#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8))) +#define test_bit(bit, array) ((array)[(bit) / 8] & (1 << ((bit) % 8))) /* this macro computes the number of bytes needed to represent a bit array of the specified size */ -#define sizeof_bit_array(bits) (((bits) + 7) / 8) +#define sizeof_bit_array(bits) (((bits) + 7) / 8) #define INDENT " " #define INDENT2 " " @@ -71,10 +71,10 @@ namespace android { static constexpr bool DEBUG = false; -static const char *WAKE_LOCK_ID = "KeyEvents"; -static const char *DEVICE_PATH = "/dev/input"; +static const char* WAKE_LOCK_ID = "KeyEvents"; +static const char* DEVICE_PATH = "/dev/input"; // v4l2 devices go directly into /dev -static const char *VIDEO_DEVICE_PATH = "/dev"; +static const char* VIDEO_DEVICE_PATH = "/dev"; static inline const char* toString(bool value) { return value ? "true" : "false"; @@ -126,7 +126,7 @@ static bool isV4lTouchNode(const char* name) { * directly from /dev. */ static bool isV4lScanningEnabled() { - return property_get_bool("ro.input.video_enabled", true /* default_value */); + return property_get_bool("ro.input.video_enabled", true /* default_value */); } static nsecs_t processEventTimestamp(const struct input_event& event) { @@ -153,27 +153,27 @@ uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) { // Touch devices get dibs on touch-related axes. if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) { switch (axis) { - case ABS_X: - case ABS_Y: - case ABS_PRESSURE: - case ABS_TOOL_WIDTH: - case ABS_DISTANCE: - case ABS_TILT_X: - case ABS_TILT_Y: - case ABS_MT_SLOT: - case ABS_MT_TOUCH_MAJOR: - case ABS_MT_TOUCH_MINOR: - case ABS_MT_WIDTH_MAJOR: - case ABS_MT_WIDTH_MINOR: - case ABS_MT_ORIENTATION: - case ABS_MT_POSITION_X: - case ABS_MT_POSITION_Y: - case ABS_MT_TOOL_TYPE: - case ABS_MT_BLOB_ID: - case ABS_MT_TRACKING_ID: - case ABS_MT_PRESSURE: - case ABS_MT_DISTANCE: - return INPUT_DEVICE_CLASS_TOUCH; + case ABS_X: + case ABS_Y: + case ABS_PRESSURE: + case ABS_TOOL_WIDTH: + case ABS_DISTANCE: + case ABS_TILT_X: + case ABS_TILT_Y: + case ABS_MT_SLOT: + case ABS_MT_TOUCH_MAJOR: + case ABS_MT_TOUCH_MINOR: + case ABS_MT_WIDTH_MAJOR: + case ABS_MT_WIDTH_MINOR: + case ABS_MT_ORIENTATION: + case ABS_MT_POSITION_X: + case ABS_MT_POSITION_Y: + case ABS_MT_TOOL_TYPE: + case ABS_MT_BLOB_ID: + case ABS_MT_TRACKING_ID: + case ABS_MT_PRESSURE: + case ABS_MT_DISTANCE: + return INPUT_DEVICE_CLASS_TOUCH; } } @@ -191,12 +191,20 @@ uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) { // --- EventHub::Device --- EventHub::Device::Device(int fd, int32_t id, const std::string& path, - const InputDeviceIdentifier& identifier) : - next(nullptr), - fd(fd), id(id), path(path), identifier(identifier), - classes(0), configuration(nullptr), virtualKeyMap(nullptr), - ffEffectPlaying(false), ffEffectId(-1), controllerNumber(0), - enabled(true), isVirtual(fd < 0) { + const InputDeviceIdentifier& identifier) + : next(nullptr), + fd(fd), + id(id), + path(path), + identifier(identifier), + classes(0), + configuration(nullptr), + virtualKeyMap(nullptr), + ffEffectPlaying(false), + ffEffectId(-1), + controllerNumber(0), + enabled(true), + isVirtual(fd < 0) { memset(keyBitmask, 0, sizeof(keyBitmask)); memset(absBitmask, 0, sizeof(absBitmask)); memset(relBitmask, 0, sizeof(relBitmask)); @@ -220,7 +228,7 @@ void EventHub::Device::close() { status_t EventHub::Device::enable() { fd = open(path.c_str(), O_RDWR | O_CLOEXEC | O_NONBLOCK); - if(fd < 0) { + if (fd < 0) { ALOGE("could not open %s, %s\n", path.c_str(), strerror(errno)); return -errno; } @@ -242,12 +250,18 @@ bool EventHub::Device::hasValidFd() { const int EventHub::EPOLL_MAX_EVENTS; -EventHub::EventHub(void) : - mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mControllerNumbers(), - mOpeningDevices(nullptr), mClosingDevices(nullptr), +EventHub::EventHub(void) + : mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), + mNextDeviceId(1), + mControllerNumbers(), + mOpeningDevices(nullptr), + mClosingDevices(nullptr), mNeedToSendFinishedDeviceScan(false), - mNeedToReopenDevices(false), mNeedToScanDevices(true), - mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { + mNeedToReopenDevices(false), + mNeedToScanDevices(true), + mPendingEventCount(0), + mPendingEventIndex(0), + mPendingINotify(false) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); mEpollFd = epoll_create1(EPOLL_CLOEXEC); @@ -255,12 +269,12 @@ EventHub::EventHub(void) : mINotifyFd = inotify_init(); mInputWd = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); - LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", - DEVICE_PATH, strerror(errno)); + LOG_ALWAYS_FATAL_IF(mInputWd < 0, "Could not register INotify for %s: %s", DEVICE_PATH, + strerror(errno)); if (isV4lScanningEnabled()) { mVideoWd = inotify_add_watch(mINotifyFd, VIDEO_DEVICE_PATH, IN_DELETE | IN_CREATE); LOG_ALWAYS_FATAL_IF(mVideoWd < 0, "Could not register INotify for %s: %s", - VIDEO_DEVICE_PATH, strerror(errno)); + VIDEO_DEVICE_PATH, strerror(errno)); } else { mVideoWd = -1; ALOGI("Video device scanning disabled"); @@ -282,16 +296,16 @@ EventHub::EventHub(void) : result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", - errno); + errno); result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", - errno); + errno); eventItem.data.fd = mWakeReadPipeFd; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", - errno); + errno); int major, minor; getLinuxRelease(&major, &minor); @@ -348,7 +362,7 @@ void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) } status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const { + RawAbsoluteAxisInfo* outAxisInfo) const { outAxisInfo->clear(); if (axis >= 0 && axis <= ABS_MAX) { @@ -357,9 +371,9 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, Device* device = getDeviceLocked(deviceId); if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) { struct input_absinfo info; - if(ioctl(device->fd, EVIOCGABS(axis), &info)) { - ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", - axis, device->identifier.name.c_str(), device->fd, errno); + if (ioctl(device->fd, EVIOCGABS(axis), &info)) { + ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis, + device->identifier.name.c_str(), device->fd, errno); return -errno; } @@ -466,9 +480,9 @@ status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* Device* device = getDeviceLocked(deviceId); if (device && device->hasValidFd() && test_bit(axis, device->absBitmask)) { struct input_absinfo info; - if(ioctl(device->fd, EVIOCGABS(axis), &info)) { - ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", - axis, device->identifier.name.c_str(), device->fd, errno); + if (ioctl(device->fd, EVIOCGABS(axis), &info)) { + ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", axis, + device->identifier.name.c_str(), device->fd, errno); return -errno; } @@ -479,8 +493,8 @@ status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* return -1; } -bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) const { +bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); @@ -489,9 +503,9 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { scanCodes.clear(); - status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey( - keyCodes[codeIndex], &scanCodes); - if (! err) { + status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex], + &scanCodes); + if (!err) { // check the possible scan codes identified by the layout map against the // map of codes actually emitted by the driver for (size_t sc = 0; sc < scanCodes.size(); sc++) { @@ -507,9 +521,8 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, return false; } -status_t EventHub::mapKey(int32_t deviceId, - int32_t scanCode, int32_t usageCode, int32_t metaState, - int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const { +status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, + int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); status_t status = NAME_NOT_FOUND; @@ -616,7 +629,7 @@ void EventHub::setLedStateLocked(Device* device, int32_t led, bool on) { } void EventHub::getVirtualKeyDefinitions(int32_t deviceId, - std::vector<VirtualKeyDefinition>& outVirtualKeys) const { + std::vector<VirtualKeyDefinition>& outVirtualKeys) const { outVirtualKeys.clear(); AutoMutex _l(mLock); @@ -637,15 +650,13 @@ sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const { return nullptr; } -bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, - const sp<KeyCharacterMap>& map) { +bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); if (device) { if (map != device->overlayKeyMap) { device->overlayKeyMap = map; - device->combinedKeyMap = KeyCharacterMap::combine( - device->keyMap.keyCharacterMap, map); + device->combinedKeyMap = KeyCharacterMap::combine(device->keyMap.keyCharacterMap, map); return true; } } @@ -654,8 +665,7 @@ bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, static std::string generateDescriptor(InputDeviceIdentifier& identifier) { std::string rawDescriptor; - rawDescriptor += StringPrintf(":%04x:%04x:", identifier.vendor, - identifier.product); + rawDescriptor += StringPrintf(":%04x:%04x:", identifier.vendor, identifier.product); // TODO add handling for USB devices to not uniqueify kbs that show up twice if (!identifier.uniqueId.empty()) { rawDescriptor += "uniqueId:"; @@ -694,13 +704,13 @@ void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { if (identifier.uniqueId.empty()) { // If it didn't have a unique id check for conflicts and enforce // uniqueness if necessary. - while(getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) { + while (getDeviceByDescriptorLocked(identifier.descriptor) != nullptr) { identifier.nonce++; rawDescriptor = generateDescriptor(identifier); } } ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.c_str(), - identifier.descriptor.c_str()); + identifier.descriptor.c_str()); } void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { @@ -717,7 +727,7 @@ void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { effect.replay.delay = 0; if (ioctl(device->fd, EVIOCSFF, &effect)) { ALOGW("Could not upload force feedback effect to device %s due to error %d.", - device->identifier.name.c_str(), errno); + device->identifier.name.c_str(), errno); return; } device->ffEffectId = effect.id; @@ -730,7 +740,7 @@ void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { ev.value = 1; if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) { ALOGW("Could not start force feedback effect on device %s due to error %d.", - device->identifier.name.c_str(), errno); + device->identifier.name.c_str(), errno); return; } device->ffEffectPlaying = true; @@ -752,7 +762,7 @@ void EventHub::cancelVibrate(int32_t deviceId) { ev.value = 0; if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) { ALOGW("Could not stop force feedback effect on device %s due to error %d.", - device->identifier.name.c_str(), errno); + device->identifier.name.c_str(), errno); return; } } @@ -839,12 +849,12 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz // Report any devices that had last been added/removed. while (mClosingDevices) { Device* device = mClosingDevices; - ALOGV("Reporting device closed: id=%d, name=%s\n", - device->id, device->path.c_str()); + ALOGV("Reporting device closed: id=%d, name=%s\n", device->id, device->path.c_str()); mClosingDevices = device->next; event->when = now; - event->deviceId = (device->id == mBuiltInKeyboardId) ? - ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID : device->id; + event->deviceId = (device->id == mBuiltInKeyboardId) + ? ReservedInputDeviceId::BUILT_IN_KEYBOARD_ID + : device->id; event->type = DEVICE_REMOVED; event += 1; delete device; @@ -862,8 +872,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz while (mOpeningDevices != nullptr) { Device* device = mOpeningDevices; - ALOGV("Reporting device opened: id=%d, name=%s\n", - device->id, device->path.c_str()); + ALOGV("Reporting device opened: id=%d, name=%s\n", device->id, device->path.c_str()); mOpeningDevices = device->next; event->when = now; event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; @@ -909,15 +918,15 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); } else { ALOGW("Received unexpected epoll event 0x%08x for wake read pipe.", - eventItem.events); + eventItem.events); } continue; } Device* device = getDeviceByFdLocked(eventItem.data.fd); if (!device) { - ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", - eventItem.events, eventItem.data.fd); + ALOGE("Received unexpected epoll event 0x%08x for unknown fd %d.", eventItem.events, + eventItem.data.fd); ALOG_ASSERT(!DEBUG); continue; } @@ -926,30 +935,30 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz size_t numFrames = device->videoDevice->readAndQueueFrames(); if (numFrames == 0) { ALOGE("Received epoll event for video device %s, but could not read frame", - device->videoDevice->getName().c_str()); + device->videoDevice->getName().c_str()); } } else if (eventItem.events & EPOLLHUP) { // TODO(b/121395353) - consider adding EPOLLRDHUP ALOGI("Removing video device %s due to epoll hang-up event.", - device->videoDevice->getName().c_str()); + device->videoDevice->getName().c_str()); unregisterVideoDeviceFromEpollLocked(*device->videoDevice); device->videoDevice = nullptr; } else { - ALOGW("Received unexpected epoll event 0x%08x for device %s.", - eventItem.events, device->videoDevice->getName().c_str()); + ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, + device->videoDevice->getName().c_str()); ALOG_ASSERT(!DEBUG); } continue; } // This must be an input event if (eventItem.events & EPOLLIN) { - int32_t readSize = read(device->fd, readBuffer, - sizeof(struct input_event) * capacity); + int32_t readSize = + read(device->fd, readBuffer, sizeof(struct input_event) * capacity); if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { // Device was removed before INotify noticed. ALOGW("could not get event, removed? (fd: %d size: %" PRId32 - " bufferSize: %zu capacity: %zu errno: %d)\n", - device->fd, readSize, bufferSize, capacity, errno); + " bufferSize: %zu capacity: %zu errno: %d)\n", + device->fd, readSize, bufferSize, capacity, errno); deviceChanged = true; closeDeviceLocked(device); } else if (readSize < 0) { @@ -981,12 +990,12 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } } else if (eventItem.events & EPOLLHUP) { ALOGI("Removing device %s due to epoll hang-up event.", - device->identifier.name.c_str()); + device->identifier.name.c_str()); deviceChanged = true; closeDeviceLocked(device); } else { - ALOGW("Received unexpected epoll event 0x%08x for device %s.", - eventItem.events, device->identifier.name.c_str()); + ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, + device->identifier.name.c_str()); } } @@ -1081,7 +1090,7 @@ void EventHub::wake() { void EventHub::scanDevicesLocked() { status_t result = scanDirLocked(DEVICE_PATH); - if(result < 0) { + if (result < 0) { ALOGE("scan dir failed for %s", DEVICE_PATH); } if (isV4lScanningEnabled()) { @@ -1109,12 +1118,12 @@ static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint3 } static const int32_t GAMEPAD_KEYCODES[] = { - AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, - AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, - AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, - AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, - AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, - AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE, + AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, // + AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, // + AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, // + AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, // + AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, // + AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE, // }; status_t EventHub::registerFdForEpoll(int fd) { @@ -1181,7 +1190,7 @@ void EventHub::unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& vide status_t result = unregisterFdFromEpoll(videoDevice.getFd()); if (result != OK) { ALOGW("Could not remove video device fd from epoll for device: %s", - videoDevice.getName().c_str()); + videoDevice.getName().c_str()); } } } @@ -1192,7 +1201,7 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { ALOGV("Opening device: %s", devicePath); int fd = open(devicePath, O_RDWR | O_CLOEXEC | O_NONBLOCK); - if(fd < 0) { + if (fd < 0) { ALOGE("could not open %s, %s\n", devicePath, strerror(errno)); return -1; } @@ -1200,7 +1209,7 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { InputDeviceIdentifier identifier; // Get device name. - if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) { + if (ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1), &buffer) < 1) { ALOGE("Could not get device name for %s: %s", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; @@ -1219,7 +1228,7 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // Get device driver version. int driverVersion; - if(ioctl(fd, EVIOCGVERSION, &driverVersion)) { + if (ioctl(fd, EVIOCGVERSION, &driverVersion)) { ALOGE("could not get driver version for %s, %s\n", devicePath, strerror(errno)); close(fd); return -1; @@ -1227,7 +1236,7 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // Get device identifier. struct input_id inputId; - if(ioctl(fd, EVIOCGID, &inputId)) { + if (ioctl(fd, EVIOCGID, &inputId)) { ALOGE("could not get device input id for %s, %s\n", devicePath, strerror(errno)); close(fd); return -1; @@ -1238,16 +1247,16 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { identifier.version = inputId.version; // Get device physical location. - if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) { - //fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno)); + if (ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) { + // fprintf(stderr, "could not get location for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; identifier.location = buffer; } // Get device unique id. - if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) { - //fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno)); + if (ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) { + // fprintf(stderr, "could not get idstring for %s, %s\n", devicePath, strerror(errno)); } else { buffer[sizeof(buffer) - 1] = '\0'; identifier.uniqueId = buffer; @@ -1262,16 +1271,16 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { ALOGV("add device %d: %s\n", deviceId, devicePath); ALOGV(" bus: %04x\n" - " vendor %04x\n" - " product %04x\n" - " version %04x\n", - identifier.bus, identifier.vendor, identifier.product, identifier.version); + " vendor %04x\n" + " product %04x\n" + " version %04x\n", + identifier.bus, identifier.vendor, identifier.product, identifier.version); ALOGV(" name: \"%s\"\n", identifier.name.c_str()); ALOGV(" location: \"%s\"\n", identifier.location.c_str()); ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.c_str()); ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.c_str()); - ALOGV(" driver: v%d.%d.%d\n", - driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff); + ALOGV(" driver: v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff, + driverVersion & 0xff); // Load the configuration file for the device. loadConfigurationLocked(device); @@ -1287,21 +1296,21 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // See if this is a keyboard. Ignore everything in the button range except for // joystick and gamepad buttons which are handled like keyboards for the most part. - bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) - || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK), - sizeof_bit_array(KEY_MAX + 1)); + bool haveKeyboardKeys = + containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) || + containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK), + sizeof_bit_array(KEY_MAX + 1)); bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC), - sizeof_bit_array(BTN_MOUSE)) - || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK), - sizeof_bit_array(BTN_DIGI)); + sizeof_bit_array(BTN_MOUSE)) || + containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK), + sizeof_bit_array(BTN_DIGI)); if (haveKeyboardKeys || haveGamepadButtons) { device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; } // See if this is a cursor device such as a trackball or mouse. - if (test_bit(BTN_MOUSE, device->keyBitmask) - && test_bit(REL_X, device->relBitmask) - && test_bit(REL_Y, device->relBitmask)) { + if (test_bit(BTN_MOUSE, device->keyBitmask) && test_bit(REL_X, device->relBitmask) && + test_bit(REL_Y, device->relBitmask)) { device->classes |= INPUT_DEVICE_CLASS_CURSOR; } @@ -1309,31 +1318,29 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { String8 deviceType = String8(); if (device->configuration && device->configuration->tryGetProperty(String8("device.type"), deviceType)) { - if (!deviceType.compare(String8("rotaryEncoder"))) { - device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER; - } + if (!deviceType.compare(String8("rotaryEncoder"))) { + device->classes |= INPUT_DEVICE_CLASS_ROTARY_ENCODER; + } } // See if this is a touch pad. // Is this a new modern multi-touch driver? - if (test_bit(ABS_MT_POSITION_X, device->absBitmask) - && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) { + if (test_bit(ABS_MT_POSITION_X, device->absBitmask) && + test_bit(ABS_MT_POSITION_Y, device->absBitmask)) { // Some joysticks such as the PS3 controller report axes that conflict // with the ABS_MT range. Try to confirm that the device really is // a touch screen. if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) { device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT; } - // Is this an old style single-touch driver? - } else if (test_bit(BTN_TOUCH, device->keyBitmask) - && test_bit(ABS_X, device->absBitmask) - && test_bit(ABS_Y, device->absBitmask)) { + // Is this an old style single-touch driver? + } else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) && + test_bit(ABS_Y, device->absBitmask)) { device->classes |= INPUT_DEVICE_CLASS_TOUCH; - // Is this a BT stylus? + // Is this a BT stylus? } else if ((test_bit(ABS_PRESSURE, device->absBitmask) || - test_bit(BTN_TOUCH, device->keyBitmask)) - && !test_bit(ABS_X, device->absBitmask) - && !test_bit(ABS_Y, device->absBitmask)) { + test_bit(BTN_TOUCH, device->keyBitmask)) && + !test_bit(ABS_X, device->absBitmask) && !test_bit(ABS_Y, device->absBitmask)) { device->classes |= INPUT_DEVICE_CLASS_EXTERNAL_STYLUS; // Keyboard will try to claim some of the buttons but we really want to reserve those so we // can fuse it with the touch screen data, so just take them back. Note this means an @@ -1347,8 +1354,8 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { if (haveGamepadButtons) { uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK; for (int i = 0; i <= ABS_MAX; i++) { - if (test_bit(i, device->absBitmask) - && (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) { + if (test_bit(i, device->absBitmask) && + (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) { device->classes = assumedClasses; break; } @@ -1389,10 +1396,8 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // Configure the keyboard, gamepad or virtual keyboard. if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { // Register the keyboard as a built-in keyboard if it is eligible. - if (!keyMapStatus - && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD - && isEligibleBuiltInKeyboard(device->identifier, - device->configuration, &device->keyMap)) { + if (!keyMapStatus && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD && + isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) { mBuiltInKeyboardId = device->id; } @@ -1403,15 +1408,15 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // See if this device has a DPAD. if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) && - hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) && - hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) && - hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) && - hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) { + hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) && + hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) && + hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) && + hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) { device->classes |= INPUT_DEVICE_CLASS_DPAD; } // See if this device has a gamepad. - for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) { + for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES) / sizeof(GAMEPAD_KEYCODES[0]); i++) { if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) { device->classes |= INPUT_DEVICE_CLASS_GAMEPAD; break; @@ -1421,8 +1426,8 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { // If the device isn't recognized as something we handle, don't monitor it. if (device->classes == 0) { - ALOGV("Dropping device: id=%d, path='%s', name='%s'", - deviceId, devicePath, device->identifier.name.c_str()); + ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath, + device->identifier.name.c_str()); delete device; return -1; } @@ -1437,8 +1442,8 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { device->classes |= INPUT_DEVICE_CLASS_EXTERNAL; } - if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD) - && device->classes & INPUT_DEVICE_CLASS_GAMEPAD) { + if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_DPAD) && + device->classes & INPUT_DEVICE_CLASS_GAMEPAD) { device->controllerNumber = getNextControllerNumberLocked(device); setLedForControllerLocked(device); } @@ -1451,10 +1456,12 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { break; } } - mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(), - mUnattachedVideoDevices.end(), - [](const std::unique_ptr<TouchVideoDevice>& videoDevice){ - return videoDevice == nullptr; }), mUnattachedVideoDevices.end()); + mUnattachedVideoDevices + .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(), + [](const std::unique_ptr<TouchVideoDevice>& videoDevice) { + return videoDevice == nullptr; + }), + mUnattachedVideoDevices.end()); if (registerDeviceForEpollLocked(device) != OK) { delete device; @@ -1464,13 +1471,10 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { configureFd(device); ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, " - "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ", - deviceId, fd, devicePath, device->identifier.name.c_str(), - device->classes, - device->configurationFile.c_str(), - device->keyMap.keyLayoutFile.c_str(), - device->keyMap.keyCharacterMapFile.c_str(), - toString(mBuiltInKeyboardId == deviceId)); + "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, ", + deviceId, fd, devicePath, device->identifier.name.c_str(), device->classes, + device->configurationFile.c_str(), device->keyMap.keyLayoutFile.c_str(), + device->keyMap.keyCharacterMapFile.c_str(), toString(mBuiltInKeyboardId == deviceId)); addDeviceLocked(device); return OK; @@ -1482,8 +1486,8 @@ void EventHub::configureFd(Device* device) { // Disable kernel key repeat since we handle it ourselves unsigned int repeatRate[] = {0, 0}; if (ioctl(device->fd, EVIOCSREP, repeatRate)) { - ALOGW("Unable to disable kernel key repeat for %s: %s", - device->path.c_str(), strerror(errno)); + ALOGW("Unable to disable kernel key repeat for %s: %s", device->path.c_str(), + strerror(errno)); } } @@ -1507,8 +1511,7 @@ void EventHub::configureFd(Device* device) { // clock. int clockId = CLOCK_MONOTONIC; bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId); - ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.c_str(), - toString(usingClockIoctl)); + ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.c_str(), toString(usingClockIoctl)); } void EventHub::openVideoDeviceLocked(const std::string& devicePath) { @@ -1532,7 +1535,7 @@ void EventHub::openVideoDeviceLocked(const std::string& devicePath) { // Couldn't find a matching input device, so just add it to a temporary holding queue. // A matching input device may appear later. ALOGI("Adding video device %s to list of unattached video devices", - videoDevice->getName().c_str()); + videoDevice->getName().c_str()); mUnattachedVideoDevices.push_back(std::move(videoDevice)); } @@ -1589,12 +1592,10 @@ void EventHub::createVirtualKeyboardLocked() { identifier.uniqueId = "<virtual>"; assignDescriptorLocked(identifier); - Device* device = new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>", - identifier); - device->classes = INPUT_DEVICE_CLASS_KEYBOARD - | INPUT_DEVICE_CLASS_ALPHAKEY - | INPUT_DEVICE_CLASS_DPAD - | INPUT_DEVICE_CLASS_VIRTUAL; + Device* device = + new Device(-1, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, "<virtual>", identifier); + device->classes = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY | + INPUT_DEVICE_CLASS_DPAD | INPUT_DEVICE_CLASS_VIRTUAL; loadKeyMapLocked(device); addDeviceLocked(device); } @@ -1610,14 +1611,14 @@ void EventHub::loadConfigurationLocked(Device* device) { device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION); if (device->configurationFile.empty()) { ALOGD("No input device configuration file found for device '%s'.", - device->identifier.name.c_str()); + device->identifier.name.c_str()); } else { status_t status = PropertyMap::load(String8(device->configurationFile.c_str()), - &device->configuration); + &device->configuration); if (status) { ALOGE("Error loading input device configuration file for device '%s'. " - "Using default configuration.", - device->identifier.name.c_str()); + "Using default configuration.", + device->identifier.name.c_str()); } } } @@ -1661,7 +1662,7 @@ bool EventHub::deviceHasMicLocked(Device* device) { int32_t EventHub::getNextControllerNumberLocked(Device* device) { if (mControllerNumbers.isFull()) { ALOGI("Maximum number of controllers reached, assigning controller number 0 to device %s", - device->identifier.name.c_str()); + device->identifier.name.c_str()); return 0; } // Since the controller number 0 is reserved for non-controllers, translate all numbers up by @@ -1671,7 +1672,7 @@ int32_t EventHub::getNextControllerNumberLocked(Device* device) { void EventHub::releaseControllerNumberLocked(Device* device) { int32_t num = device->controllerNumber; - device->controllerNumber= 0; + device->controllerNumber = 0; if (num == 0) { return; } @@ -1692,7 +1693,7 @@ bool EventHub::hasKeycodeLocked(Device* device, int keycode) const { std::vector<int32_t> scanCodes; device->keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes); const size_t N = scanCodes.size(); - for (size_t i=0; i<N && i<=KEY_MAX; i++) { + for (size_t i = 0; i < N && i <= KEY_MAX; i++) { int32_t sc = scanCodes[i]; if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) { return true; @@ -1708,8 +1709,8 @@ status_t EventHub::mapLed(Device* device, int32_t led, int32_t* outScanCode) con } int32_t scanCode; - if(device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { - if(scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) { + if (device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { + if (scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) { *outScanCode = scanCode; return NO_ERROR; } @@ -1717,7 +1718,7 @@ status_t EventHub::mapLed(Device* device, int32_t led, int32_t* outScanCode) con return NAME_NOT_FOUND; } -void EventHub::closeDeviceByPathLocked(const char *devicePath) { +void EventHub::closeDeviceByPathLocked(const char* devicePath) { Device* device = getDeviceByPathLocked(devicePath); if (device) { closeDeviceLocked(device); @@ -1742,10 +1743,13 @@ void EventHub::closeVideoDeviceByPathLocked(const std::string& devicePath) { return; } } - mUnattachedVideoDevices.erase(std::remove_if(mUnattachedVideoDevices.begin(), - mUnattachedVideoDevices.end(), [&devicePath]( - const std::unique_ptr<TouchVideoDevice>& videoDevice) { - return videoDevice->getPath() == devicePath; }), mUnattachedVideoDevices.end()); + mUnattachedVideoDevices + .erase(std::remove_if(mUnattachedVideoDevices.begin(), mUnattachedVideoDevices.end(), + [&devicePath]( + const std::unique_ptr<TouchVideoDevice>& videoDevice) { + return videoDevice->getPath() == devicePath; + }), + mUnattachedVideoDevices.end()); } void EventHub::closeAllDevicesLocked() { @@ -1756,13 +1760,12 @@ void EventHub::closeAllDevicesLocked() { } void EventHub::closeDeviceLocked(Device* device) { - ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x", - device->path.c_str(), device->identifier.name.c_str(), device->id, - device->fd, device->classes); + ALOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x", device->path.c_str(), + device->identifier.name.c_str(), device->id, device->fd, device->classes); if (device->id == mBuiltInKeyboardId) { ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", - device->path.c_str(), mBuiltInKeyboardId); + device->path.c_str(), mBuiltInKeyboardId); mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD; } @@ -1780,7 +1783,7 @@ void EventHub::closeDeviceLocked(Device* device) { // Unlink for opening devices list if it is present. Device* pred = nullptr; bool found = false; - for (Device* entry = mOpeningDevices; entry != nullptr; ) { + for (Device* entry = mOpeningDevices; entry != nullptr;) { if (entry == device) { found = true; break; @@ -1812,30 +1815,28 @@ status_t EventHub::readNotifyLocked() { char event_buf[512]; int event_size; int event_pos = 0; - struct inotify_event *event; + struct inotify_event* event; ALOGV("EventHub::readNotify nfd: %d\n", mINotifyFd); res = read(mINotifyFd, event_buf, sizeof(event_buf)); - if(res < (int)sizeof(*event)) { - if(errno == EINTR) - return 0; + if (res < (int)sizeof(*event)) { + if (errno == EINTR) return 0; ALOGW("could not get event, %s\n", strerror(errno)); return -1; } - while(res >= (int)sizeof(*event)) { - event = (struct inotify_event *)(event_buf + event_pos); - if(event->len) { + while (res >= (int)sizeof(*event)) { + event = (struct inotify_event*)(event_buf + event_pos); + if (event->len) { if (event->wd == mInputWd) { std::string filename = StringPrintf("%s/%s", DEVICE_PATH, event->name); - if(event->mask & IN_CREATE) { + if (event->mask & IN_CREATE) { openDeviceLocked(filename.c_str()); } else { ALOGI("Removing device '%s' due to inotify event\n", filename.c_str()); closeDeviceByPathLocked(filename.c_str()); } - } - else if (event->wd == mVideoWd) { + } else if (event->wd == mVideoWd) { if (isV4lTouchNode(event->name)) { std::string filename = StringPrintf("%s/%s", VIDEO_DEVICE_PATH, event->name); if (event->mask & IN_CREATE) { @@ -1845,8 +1846,7 @@ status_t EventHub::readNotifyLocked() { closeVideoDeviceByPathLocked(filename); } } - } - else { + } else { LOG_ALWAYS_FATAL("Unexpected inotify event, wd = %i", event->wd); } } @@ -1857,22 +1857,19 @@ status_t EventHub::readNotifyLocked() { return 0; } -status_t EventHub::scanDirLocked(const char *dirname) -{ +status_t EventHub::scanDirLocked(const char* dirname) { char devname[PATH_MAX]; - char *filename; - DIR *dir; - struct dirent *de; + char* filename; + DIR* dir; + struct dirent* de; dir = opendir(dirname); - if(dir == nullptr) - return -1; + if (dir == nullptr) return -1; strcpy(devname, dirname); filename = devname + strlen(devname); *filename++ = '/'; - while((de = readdir(dir))) { - if(de->d_name[0] == '.' && - (de->d_name[1] == '\0' || - (de->d_name[1] == '.' && de->d_name[2] == '\0'))) + while ((de = readdir(dir))) { + if (de->d_name[0] == '.' && + (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0'))) continue; strcpy(filename, de->d_name); openDeviceLocked(devname); @@ -1884,17 +1881,16 @@ status_t EventHub::scanDirLocked(const char *dirname) /** * Look for all dirname/v4l-touch* devices, and open them. */ -status_t EventHub::scanVideoDirLocked(const std::string& dirname) -{ +status_t EventHub::scanVideoDirLocked(const std::string& dirname) { DIR* dir; struct dirent* de; dir = opendir(dirname.c_str()); - if(!dir) { + if (!dir) { ALOGE("Could not open video directory %s", dirname.c_str()); return BAD_VALUE; } - while((de = readdir(dir))) { + while ((de = readdir(dir))) { const char* name = de->d_name; if (isV4lTouchNode(name)) { ALOGI("Found touch video device %s", name); @@ -1926,10 +1922,10 @@ void EventHub::dump(std::string& dump) { const Device* device = mDevices.valueAt(i); if (mBuiltInKeyboardId == device->id) { dump += StringPrintf(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n", - device->id, device->identifier.name.c_str()); + device->id, device->identifier.name.c_str()); } else { dump += StringPrintf(INDENT2 "%d: %s\n", device->id, - device->identifier.name.c_str()); + device->identifier.name.c_str()); } dump += StringPrintf(INDENT3 "Classes: 0x%08x\n", device->classes); dump += StringPrintf(INDENT3 "Path: %s\n", device->path.c_str()); @@ -1939,17 +1935,17 @@ void EventHub::dump(std::string& dump) { dump += StringPrintf(INDENT3 "ControllerNumber: %d\n", device->controllerNumber); dump += StringPrintf(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.c_str()); dump += StringPrintf(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, " - "product=0x%04x, version=0x%04x\n", - device->identifier.bus, device->identifier.vendor, - device->identifier.product, device->identifier.version); + "product=0x%04x, version=0x%04x\n", + device->identifier.bus, device->identifier.vendor, + device->identifier.product, device->identifier.version); dump += StringPrintf(INDENT3 "KeyLayoutFile: %s\n", - device->keyMap.keyLayoutFile.c_str()); + device->keyMap.keyLayoutFile.c_str()); dump += StringPrintf(INDENT3 "KeyCharacterMapFile: %s\n", - device->keyMap.keyCharacterMapFile.c_str()); + device->keyMap.keyCharacterMapFile.c_str()); dump += StringPrintf(INDENT3 "ConfigurationFile: %s\n", - device->configurationFile.c_str()); + device->configurationFile.c_str()); dump += StringPrintf(INDENT3 "HaveKeyboardLayoutOverlay: %s\n", - toString(device->overlayKeyMap != nullptr)); + toString(device->overlayKeyMap != nullptr)); dump += INDENT3 "VideoDevice: "; if (device->videoDevice) { dump += device->videoDevice->dump() + "\n"; @@ -1974,5 +1970,4 @@ void EventHub::monitor() { mLock.unlock(); } - }; // namespace android diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp new file mode 100644 index 0000000000..d0613b0c69 --- /dev/null +++ b/services/inputflinger/reader/InputDevice.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "InputDevice.h" + +#include "InputMapper.h" + +namespace android { + +InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, + int32_t controllerNumber, const InputDeviceIdentifier& identifier, + uint32_t classes) + : mContext(context), + mId(id), + mGeneration(generation), + mControllerNumber(controllerNumber), + mIdentifier(identifier), + mClasses(classes), + mSources(0), + mIsExternal(false), + mHasMic(false), + mDropUntilNextSync(false) {} + +InputDevice::~InputDevice() { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + delete mMappers[i]; + } + mMappers.clear(); +} + +bool InputDevice::isEnabled() { + return getEventHub()->isDeviceEnabled(mId); +} + +void InputDevice::setEnabled(bool enabled, nsecs_t when) { + if (isEnabled() == enabled) { + return; + } + + if (enabled) { + getEventHub()->enableDevice(mId); + reset(when); + } else { + reset(when); + getEventHub()->disableDevice(mId); + } + // Must change generation to flag this device as changed + bumpGeneration(); +} + +void InputDevice::dump(std::string& dump) { + InputDeviceInfo deviceInfo; + getDeviceInfo(&deviceInfo); + + dump += StringPrintf(INDENT "Device %d: %s\n", deviceInfo.getId(), + deviceInfo.getDisplayName().c_str()); + dump += StringPrintf(INDENT2 "Generation: %d\n", mGeneration); + dump += StringPrintf(INDENT2 "IsExternal: %s\n", toString(mIsExternal)); + dump += StringPrintf(INDENT2 "AssociatedDisplayPort: "); + if (mAssociatedDisplayPort) { + dump += StringPrintf("%" PRIu8 "\n", *mAssociatedDisplayPort); + } else { + dump += "<none>\n"; + } + dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic)); + dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); + dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); + + const std::vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges(); + if (!ranges.empty()) { + dump += INDENT2 "Motion Ranges:\n"; + for (size_t i = 0; i < ranges.size(); i++) { + const InputDeviceInfo::MotionRange& range = ranges[i]; + const char* label = getAxisLabel(range.axis); + char name[32]; + if (label) { + strncpy(name, label, sizeof(name)); + name[sizeof(name) - 1] = '\0'; + } else { + snprintf(name, sizeof(name), "%d", range.axis); + } + dump += StringPrintf(INDENT3 + "%s: source=0x%08x, " + "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n", + name, range.source, range.min, range.max, range.flat, range.fuzz, + range.resolution); + } + } + + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->dump(dump); + } +} + +void InputDevice::addMapper(InputMapper* mapper) { + mMappers.push_back(mapper); +} + +void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + mSources = 0; + + if (!isIgnored()) { + if (!changes) { // first time only + mContext->getEventHub()->getConfiguration(mId, &mConfiguration); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { + if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { + sp<KeyCharacterMap> keyboardLayout = + mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier); + if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) { + bumpGeneration(); + } + } + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) { + if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { + std::string alias = mContext->getPolicy()->getDeviceAlias(mIdentifier); + if (mAlias != alias) { + mAlias = alias; + bumpGeneration(); + } + } + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { + ssize_t index = config->disabledDevices.indexOf(mId); + bool enabled = index < 0; + setEnabled(enabled, when); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + // In most situations, no port will be specified. + mAssociatedDisplayPort = std::nullopt; + // Find the display port that corresponds to the current input port. + const std::string& inputPort = mIdentifier.location; + if (!inputPort.empty()) { + const std::unordered_map<std::string, uint8_t>& ports = config->portAssociations; + const auto& displayPort = ports.find(inputPort); + if (displayPort != ports.end()) { + mAssociatedDisplayPort = std::make_optional(displayPort->second); + } + } + } + + for (InputMapper* mapper : mMappers) { + mapper->configure(when, config, changes); + mSources |= mapper->getSources(); + } + } +} + +void InputDevice::reset(nsecs_t when) { + for (InputMapper* mapper : mMappers) { + mapper->reset(when); + } + + mContext->updateGlobalMetaState(); + + notifyReset(when); +} + +void InputDevice::process(const RawEvent* rawEvents, size_t count) { + // Process all of the events in order for each mapper. + // We cannot simply ask each mapper to process them in bulk because mappers may + // have side-effects that must be interleaved. For example, joystick movement events and + // gamepad button presses are handled by different mappers but they should be dispatched + // in the order received. + for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) { +#if DEBUG_RAW_EVENTS + ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64, + rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, rawEvent->when); +#endif + + if (mDropUntilNextSync) { + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + mDropUntilNextSync = false; +#if DEBUG_RAW_EVENTS + ALOGD("Recovered from input event buffer overrun."); +#endif + } else { +#if DEBUG_RAW_EVENTS + ALOGD("Dropped input event while waiting for next input sync."); +#endif + } + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { + ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); + mDropUntilNextSync = true; + reset(rawEvent->when); + } else { + for (InputMapper* mapper : mMappers) { + mapper->process(rawEvent); + } + } + --count; + } +} + +void InputDevice::timeoutExpired(nsecs_t when) { + for (InputMapper* mapper : mMappers) { + mapper->timeoutExpired(when); + } +} + +void InputDevice::updateExternalStylusState(const StylusState& state) { + for (InputMapper* mapper : mMappers) { + mapper->updateExternalStylusState(state); + } +} + +void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { + outDeviceInfo->initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal, + mHasMic); + for (InputMapper* mapper : mMappers) { + mapper->populateDeviceInfo(outDeviceInfo); + } +} + +int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getState(sourceMask, keyCode, &InputMapper::getKeyCodeState); +} + +int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getState(sourceMask, scanCode, &InputMapper::getScanCodeState); +} + +int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getState(sourceMask, switchCode, &InputMapper::getSwitchState); +} + +int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { + int32_t result = AKEY_STATE_UNKNOWN; + for (InputMapper* mapper : mMappers) { + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that + // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. + int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code); + if (currentResult >= AKEY_STATE_DOWN) { + return currentResult; + } else if (currentResult == AKEY_STATE_UP) { + result = currentResult; + } + } + } + return result; +} + +bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + bool result = false; + for (InputMapper* mapper : mMappers) { + if (sourcesMatchMask(mapper->getSources(), sourceMask)) { + result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + } + } + return result; +} + +void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) { + for (InputMapper* mapper : mMappers) { + mapper->vibrate(pattern, patternSize, repeat, token); + } +} + +void InputDevice::cancelVibrate(int32_t token) { + for (InputMapper* mapper : mMappers) { + mapper->cancelVibrate(token); + } +} + +void InputDevice::cancelTouch(nsecs_t when) { + for (InputMapper* mapper : mMappers) { + mapper->cancelTouch(when); + } +} + +int32_t InputDevice::getMetaState() { + int32_t result = 0; + for (InputMapper* mapper : mMappers) { + result |= mapper->getMetaState(); + } + return result; +} + +void InputDevice::updateMetaState(int32_t keyCode) { + for (InputMapper* mapper : mMappers) { + mapper->updateMetaState(keyCode); + } +} + +void InputDevice::fadePointer() { + for (InputMapper* mapper : mMappers) { + mapper->fadePointer(); + } +} + +void InputDevice::bumpGeneration() { + mGeneration = mContext->bumpGeneration(); +} + +void InputDevice::notifyReset(nsecs_t when) { + NotifyDeviceResetArgs args(mContext->getNextSequenceNum(), when, mId); + mContext->getListener()->notifyDeviceReset(&args); +} + +std::optional<int32_t> InputDevice::getAssociatedDisplay() { + for (InputMapper* mapper : mMappers) { + std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplay(); + if (associatedDisplayId) { + return associatedDisplayId; + } + } + + return std::nullopt; +} + +} // namespace android diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp new file mode 100644 index 0000000000..27cbf192d3 --- /dev/null +++ b/services/inputflinger/reader/InputReader.cpp @@ -0,0 +1,767 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "InputReader.h" + +#include "CursorInputMapper.h" +#include "ExternalStylusInputMapper.h" +#include "InputReaderContext.h" +#include "JoystickInputMapper.h" +#include "KeyboardInputMapper.h" +#include "MultiTouchInputMapper.h" +#include "RotaryEncoderInputMapper.h" +#include "SingleTouchInputMapper.h" +#include "SwitchInputMapper.h" +#include "VibratorInputMapper.h" + +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <math.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> + +#include <log/log.h> + +#include <android-base/stringprintf.h> +#include <input/Keyboard.h> +#include <input/VirtualKeyMap.h> + + +using android::base::StringPrintf; + +namespace android { + +InputReader::InputReader(const sp<EventHubInterface>& eventHub, + const sp<InputReaderPolicyInterface>& policy, + const sp<InputListenerInterface>& listener) + : mContext(this), + mEventHub(eventHub), + mPolicy(policy), + mNextSequenceNum(1), + mGlobalMetaState(0), + mGeneration(1), + mDisableVirtualKeysTimeout(LLONG_MIN), + mNextTimeout(LLONG_MAX), + mConfigurationChangesToRefresh(0) { + mQueuedListener = new QueuedInputListener(listener); + + { // acquire lock + AutoMutex _l(mLock); + + refreshConfigurationLocked(0); + updateGlobalMetaStateLocked(); + } // release lock +} + +InputReader::~InputReader() { + for (size_t i = 0; i < mDevices.size(); i++) { + delete mDevices.valueAt(i); + } +} + +void InputReader::loopOnce() { + int32_t oldGeneration; + int32_t timeoutMillis; + bool inputDevicesChanged = false; + std::vector<InputDeviceInfo> inputDevices; + { // acquire lock + AutoMutex _l(mLock); + + oldGeneration = mGeneration; + timeoutMillis = -1; + + uint32_t changes = mConfigurationChangesToRefresh; + if (changes) { + mConfigurationChangesToRefresh = 0; + timeoutMillis = 0; + refreshConfigurationLocked(changes); + } else if (mNextTimeout != LLONG_MAX) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); + } + } // release lock + + size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); + + { // acquire lock + AutoMutex _l(mLock); + mReaderIsAliveCondition.broadcast(); + + if (count) { + processEventsLocked(mEventBuffer, count); + } + + if (mNextTimeout != LLONG_MAX) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (now >= mNextTimeout) { +#if DEBUG_RAW_EVENTS + ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); +#endif + mNextTimeout = LLONG_MAX; + timeoutExpiredLocked(now); + } + } + + if (oldGeneration != mGeneration) { + inputDevicesChanged = true; + getInputDevicesLocked(inputDevices); + } + } // release lock + + // Send out a message that the describes the changed input devices. + if (inputDevicesChanged) { + mPolicy->notifyInputDevicesChanged(inputDevices); + } + + // Flush queued events out to the listener. + // This must happen outside of the lock because the listener could potentially call + // back into the InputReader's methods, such as getScanCodeState, or become blocked + // on another thread similarly waiting to acquire the InputReader lock thereby + // resulting in a deadlock. This situation is actually quite plausible because the + // listener is actually the input dispatcher, which calls into the window manager, + // which occasionally calls into the input reader. + mQueuedListener->flush(); +} + +void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { + for (const RawEvent* rawEvent = rawEvents; count;) { + int32_t type = rawEvent->type; + size_t batchSize = 1; + if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) { + int32_t deviceId = rawEvent->deviceId; + while (batchSize < count) { + if (rawEvent[batchSize].type >= EventHubInterface::FIRST_SYNTHETIC_EVENT || + rawEvent[batchSize].deviceId != deviceId) { + break; + } + batchSize += 1; + } +#if DEBUG_RAW_EVENTS + ALOGD("BatchSize: %zu Count: %zu", batchSize, count); +#endif + processEventsForDeviceLocked(deviceId, rawEvent, batchSize); + } else { + switch (rawEvent->type) { + case EventHubInterface::DEVICE_ADDED: + addDeviceLocked(rawEvent->when, rawEvent->deviceId); + break; + case EventHubInterface::DEVICE_REMOVED: + removeDeviceLocked(rawEvent->when, rawEvent->deviceId); + break; + case EventHubInterface::FINISHED_DEVICE_SCAN: + handleConfigurationChangedLocked(rawEvent->when); + break; + default: + ALOG_ASSERT(false); // can't happen + break; + } + } + count -= batchSize; + rawEvent += batchSize; + } +} + +void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); + return; + } + + InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId); + uint32_t classes = mEventHub->getDeviceClasses(deviceId); + int32_t controllerNumber = mEventHub->getDeviceControllerNumber(deviceId); + + InputDevice* device = createDeviceLocked(deviceId, controllerNumber, identifier, classes); + device->configure(when, &mConfig, 0); + device->reset(when); + + if (device->isIgnored()) { + ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, + identifier.name.c_str()); + } else { + ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, identifier.name.c_str(), + device->getSources()); + } + + mDevices.add(deviceId, device); + bumpGenerationLocked(); + + if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + notifyExternalStylusPresenceChanged(); + } +} + +void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { + InputDevice* device = nullptr; + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); + return; + } + + device = mDevices.valueAt(deviceIndex); + mDevices.removeItemsAt(deviceIndex, 1); + bumpGenerationLocked(); + + if (device->isIgnored()) { + ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", device->getId(), + device->getName().c_str()); + } else { + ALOGI("Device removed: id=%d, name='%s', sources=0x%08x", device->getId(), + device->getName().c_str(), device->getSources()); + } + + if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + notifyExternalStylusPresenceChanged(); + } + + device->reset(when); + delete device; +} + +InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber, + const InputDeviceIdentifier& identifier, + uint32_t classes) { + InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(), + controllerNumber, identifier, classes); + + // External devices. + if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { + device->setExternal(true); + } + + // Devices with mics. + if (classes & INPUT_DEVICE_CLASS_MIC) { + device->setMic(true); + } + + // Switch-like devices. + if (classes & INPUT_DEVICE_CLASS_SWITCH) { + device->addMapper(new SwitchInputMapper(device)); + } + + // Scroll wheel-like devices. + if (classes & INPUT_DEVICE_CLASS_ROTARY_ENCODER) { + device->addMapper(new RotaryEncoderInputMapper(device)); + } + + // Vibrator-like devices. + if (classes & INPUT_DEVICE_CLASS_VIBRATOR) { + device->addMapper(new VibratorInputMapper(device)); + } + + // Keyboard-like devices. + uint32_t keyboardSource = 0; + int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; + if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { + keyboardSource |= AINPUT_SOURCE_KEYBOARD; + } + if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { + keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; + } + if (classes & INPUT_DEVICE_CLASS_DPAD) { + keyboardSource |= AINPUT_SOURCE_DPAD; + } + if (classes & INPUT_DEVICE_CLASS_GAMEPAD) { + keyboardSource |= AINPUT_SOURCE_GAMEPAD; + } + + if (keyboardSource != 0) { + device->addMapper(new KeyboardInputMapper(device, keyboardSource, keyboardType)); + } + + // Cursor-like devices. + if (classes & INPUT_DEVICE_CLASS_CURSOR) { + device->addMapper(new CursorInputMapper(device)); + } + + // Touchscreens and touchpad devices. + if (classes & INPUT_DEVICE_CLASS_TOUCH_MT) { + device->addMapper(new MultiTouchInputMapper(device)); + } else if (classes & INPUT_DEVICE_CLASS_TOUCH) { + device->addMapper(new SingleTouchInputMapper(device)); + } + + // Joystick-like devices. + if (classes & INPUT_DEVICE_CLASS_JOYSTICK) { + device->addMapper(new JoystickInputMapper(device)); + } + + // External stylus-like devices. + if (classes & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS) { + device->addMapper(new ExternalStylusInputMapper(device)); + } + + return device; +} + +void InputReader::processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, + size_t count) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + ALOGW("Discarding event for unknown deviceId %d.", deviceId); + return; + } + + InputDevice* device = mDevices.valueAt(deviceIndex); + if (device->isIgnored()) { + // ALOGD("Discarding event for ignored deviceId %d.", deviceId); + return; + } + + device->process(rawEvents, count); +} + +void InputReader::timeoutExpiredLocked(nsecs_t when) { + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + if (!device->isIgnored()) { + device->timeoutExpired(when); + } + } +} + +void InputReader::handleConfigurationChangedLocked(nsecs_t when) { + // Reset global meta state because it depends on the list of all configured devices. + updateGlobalMetaStateLocked(); + + // Enqueue configuration changed. + NotifyConfigurationChangedArgs args(mContext.getNextSequenceNum(), when); + mQueuedListener->notifyConfigurationChanged(&args); +} + +void InputReader::refreshConfigurationLocked(uint32_t changes) { + mPolicy->getReaderConfiguration(&mConfig); + mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); + + if (changes) { + ALOGI("Reconfiguring input devices. changes=0x%08x", changes); + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + + if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { + mEventHub->requestReopenDevices(); + } else { + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + device->configure(now, &mConfig, changes); + } + } + } +} + +void InputReader::updateGlobalMetaStateLocked() { + mGlobalMetaState = 0; + + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + mGlobalMetaState |= device->getMetaState(); + } +} + +int32_t InputReader::getGlobalMetaStateLocked() { + return mGlobalMetaState; +} + +void InputReader::notifyExternalStylusPresenceChanged() { + refreshConfigurationLocked(InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE); +} + +void InputReader::getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices) { + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + if (device->getClasses() & INPUT_DEVICE_CLASS_EXTERNAL_STYLUS && !device->isIgnored()) { + InputDeviceInfo info; + device->getDeviceInfo(&info); + outDevices.push_back(info); + } + } +} + +void InputReader::dispatchExternalStylusState(const StylusState& state) { + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + device->updateExternalStylusState(state); + } +} + +void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) { + mDisableVirtualKeysTimeout = time; +} + +bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode, + int32_t scanCode) { + if (now < mDisableVirtualKeysTimeout) { + ALOGI("Dropping virtual key from device %s because virtual keys are " + "temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d", + device->getName().c_str(), (mDisableVirtualKeysTimeout - now) * 0.000001, keyCode, + scanCode); + return true; + } else { + return false; + } +} + +void InputReader::fadePointerLocked() { + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + device->fadePointer(); + } +} + +void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) { + if (when < mNextTimeout) { + mNextTimeout = when; + mEventHub->wake(); + } +} + +int32_t InputReader::bumpGenerationLocked() { + return ++mGeneration; +} + +void InputReader::getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) { + AutoMutex _l(mLock); + getInputDevicesLocked(outInputDevices); +} + +void InputReader::getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices) { + outInputDevices.clear(); + + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (!device->isIgnored()) { + InputDeviceInfo info; + device->getDeviceInfo(&info); + outInputDevices.push_back(info); + } + } +} + +int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) { + AutoMutex _l(mLock); + + return getStateLocked(deviceId, sourceMask, keyCode, &InputDevice::getKeyCodeState); +} + +int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) { + AutoMutex _l(mLock); + + return getStateLocked(deviceId, sourceMask, scanCode, &InputDevice::getScanCodeState); +} + +int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) { + AutoMutex _l(mLock); + + return getStateLocked(deviceId, sourceMask, switchCode, &InputDevice::getSwitchState); +} + +int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32_t code, + GetStateFunc getStateFunc) { + int32_t result = AKEY_STATE_UNKNOWN; + if (deviceId >= 0) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = (device->*getStateFunc)(sourceMask, code); + } + } + } else { + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that + // value. Otherwise, return AKEY_STATE_UP as long as one device reports it. + int32_t currentResult = (device->*getStateFunc)(sourceMask, code); + if (currentResult >= AKEY_STATE_DOWN) { + return currentResult; + } else if (currentResult == AKEY_STATE_UP) { + result = currentResult; + } + } + } + } + return result; +} + +void InputReader::toggleCapsLockState(int32_t deviceId) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId); + return; + } + + InputDevice* device = mDevices.valueAt(deviceIndex); + if (device->isIgnored()) { + return; + } + + device->updateMetaState(AKEYCODE_CAPS_LOCK); +} + +bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + AutoMutex _l(mLock); + + memset(outFlags, 0, numCodes); + return markSupportedKeyCodesLocked(deviceId, sourceMask, numCodes, keyCodes, outFlags); +} + +bool InputReader::markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, + size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) { + bool result = false; + if (deviceId >= 0) { + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result = device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + } + } + } else { + size_t numDevices = mDevices.size(); + for (size_t i = 0; i < numDevices; i++) { + InputDevice* device = mDevices.valueAt(i); + if (!device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { + result |= device->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); + } + } + } + return result; +} + +void InputReader::requestRefreshConfiguration(uint32_t changes) { + AutoMutex _l(mLock); + + if (changes) { + bool needWake = !mConfigurationChangesToRefresh; + mConfigurationChangesToRefresh |= changes; + + if (needWake) { + mEventHub->wake(); + } + } +} + +void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, + ssize_t repeat, int32_t token) { + AutoMutex _l(mLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + device->vibrate(pattern, patternSize, repeat, token); + } +} + +void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { + AutoMutex _l(mLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + device->cancelVibrate(token); + } +} + +bool InputReader::isInputDeviceEnabled(int32_t deviceId) { + AutoMutex _l(mLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + return device->isEnabled(); + } + ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); + return false; +} + +bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) { + AutoMutex _l(mLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex < 0) { + ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId); + return false; + } + + InputDevice* device = mDevices.valueAt(deviceIndex); + std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplay(); + // No associated display. By default, can dispatch to all displays. + if (!associatedDisplayId) { + return true; + } + + if (*associatedDisplayId == ADISPLAY_ID_NONE) { + ALOGW("Device has associated, but no associated display id."); + return true; + } + + return *associatedDisplayId == displayId; +} + +void InputReader::dump(std::string& dump) { + AutoMutex _l(mLock); + + mEventHub->dump(dump); + dump += "\n"; + + dump += "Input Reader State:\n"; + + for (size_t i = 0; i < mDevices.size(); i++) { + mDevices.valueAt(i)->dump(dump); + } + + dump += INDENT "Configuration:\n"; + dump += INDENT2 "ExcludedDeviceNames: ["; + for (size_t i = 0; i < mConfig.excludedDeviceNames.size(); i++) { + if (i != 0) { + dump += ", "; + } + dump += mConfig.excludedDeviceNames[i]; + } + dump += "]\n"; + dump += StringPrintf(INDENT2 "VirtualKeyQuietTime: %0.1fms\n", + mConfig.virtualKeyQuietTime * 0.000001f); + + dump += StringPrintf(INDENT2 "PointerVelocityControlParameters: " + "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, " + "acceleration=%0.3f\n", + mConfig.pointerVelocityControlParameters.scale, + mConfig.pointerVelocityControlParameters.lowThreshold, + mConfig.pointerVelocityControlParameters.highThreshold, + mConfig.pointerVelocityControlParameters.acceleration); + + dump += StringPrintf(INDENT2 "WheelVelocityControlParameters: " + "scale=%0.3f, lowThreshold=%0.3f, highThreshold=%0.3f, " + "acceleration=%0.3f\n", + mConfig.wheelVelocityControlParameters.scale, + mConfig.wheelVelocityControlParameters.lowThreshold, + mConfig.wheelVelocityControlParameters.highThreshold, + mConfig.wheelVelocityControlParameters.acceleration); + + dump += StringPrintf(INDENT2 "PointerGesture:\n"); + dump += StringPrintf(INDENT3 "Enabled: %s\n", toString(mConfig.pointerGesturesEnabled)); + dump += StringPrintf(INDENT3 "QuietInterval: %0.1fms\n", + mConfig.pointerGestureQuietInterval * 0.000001f); + dump += StringPrintf(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n", + mConfig.pointerGestureDragMinSwitchSpeed); + dump += StringPrintf(INDENT3 "TapInterval: %0.1fms\n", + mConfig.pointerGestureTapInterval * 0.000001f); + dump += StringPrintf(INDENT3 "TapDragInterval: %0.1fms\n", + mConfig.pointerGestureTapDragInterval * 0.000001f); + dump += StringPrintf(INDENT3 "TapSlop: %0.1fpx\n", mConfig.pointerGestureTapSlop); + dump += StringPrintf(INDENT3 "MultitouchSettleInterval: %0.1fms\n", + mConfig.pointerGestureMultitouchSettleInterval * 0.000001f); + dump += StringPrintf(INDENT3 "MultitouchMinDistance: %0.1fpx\n", + mConfig.pointerGestureMultitouchMinDistance); + dump += StringPrintf(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n", + mConfig.pointerGestureSwipeTransitionAngleCosine); + dump += StringPrintf(INDENT3 "SwipeMaxWidthRatio: %0.1f\n", + mConfig.pointerGestureSwipeMaxWidthRatio); + dump += StringPrintf(INDENT3 "MovementSpeedRatio: %0.1f\n", + mConfig.pointerGestureMovementSpeedRatio); + dump += StringPrintf(INDENT3 "ZoomSpeedRatio: %0.1f\n", mConfig.pointerGestureZoomSpeedRatio); + + dump += INDENT3 "Viewports:\n"; + mConfig.dump(dump); +} + +void InputReader::monitor() { + // Acquire and release the lock to ensure that the reader has not deadlocked. + mLock.lock(); + mEventHub->wake(); + mReaderIsAliveCondition.wait(mLock); + mLock.unlock(); + + // Check the EventHub + mEventHub->monitor(); +} + +// --- InputReader::ContextImpl --- + +InputReader::ContextImpl::ContextImpl(InputReader* reader) : mReader(reader) {} + +void InputReader::ContextImpl::updateGlobalMetaState() { + // lock is already held by the input loop + mReader->updateGlobalMetaStateLocked(); +} + +int32_t InputReader::ContextImpl::getGlobalMetaState() { + // lock is already held by the input loop + return mReader->getGlobalMetaStateLocked(); +} + +void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) { + // lock is already held by the input loop + mReader->disableVirtualKeysUntilLocked(time); +} + +bool InputReader::ContextImpl::shouldDropVirtualKey(nsecs_t now, InputDevice* device, + int32_t keyCode, int32_t scanCode) { + // lock is already held by the input loop + return mReader->shouldDropVirtualKeyLocked(now, device, keyCode, scanCode); +} + +void InputReader::ContextImpl::fadePointer() { + // lock is already held by the input loop + mReader->fadePointerLocked(); +} + +void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) { + // lock is already held by the input loop + mReader->requestTimeoutAtTimeLocked(when); +} + +int32_t InputReader::ContextImpl::bumpGeneration() { + // lock is already held by the input loop + return mReader->bumpGenerationLocked(); +} + +void InputReader::ContextImpl::getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) { + // lock is already held by whatever called refreshConfigurationLocked + mReader->getExternalStylusDevicesLocked(outDevices); +} + +void InputReader::ContextImpl::dispatchExternalStylusState(const StylusState& state) { + mReader->dispatchExternalStylusState(state); +} + +InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { + return mReader->mPolicy.get(); +} + +InputListenerInterface* InputReader::ContextImpl::getListener() { + return mReader->mQueuedListener.get(); +} + +EventHubInterface* InputReader::ContextImpl::getEventHub() { + return mReader->mEventHub.get(); +} + +uint32_t InputReader::ContextImpl::getNextSequenceNum() { + return (mReader->mNextSequenceNum)++; +} + +} // namespace android diff --git a/services/inputflinger/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp index 3534f6b760..9f73680913 100644 --- a/services/inputflinger/InputReaderFactory.cpp +++ b/services/inputflinger/reader/InputReaderFactory.cpp @@ -15,13 +15,13 @@ */ #include "InputReaderFactory.h" + #include "InputReader.h" namespace android { -sp<InputReaderInterface> createInputReader( - const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener) { +sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy, + const sp<InputListenerInterface>& listener) { return new InputReader(new EventHub(), policy, listener); } diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h new file mode 100644 index 0000000000..827e31a1bd --- /dev/null +++ b/services/inputflinger/reader/Macros.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_MACROS_H +#define _UI_INPUTREADER_MACROS_H + +#define LOG_TAG "InputReader" + +//#define LOG_NDEBUG 0 + +// Log debug messages for each raw event received from the EventHub. +#define DEBUG_RAW_EVENTS 0 + +// Log debug messages about touch screen filtering hacks. +#define DEBUG_HACKS 0 + +// Log debug messages about virtual key processing. +#define DEBUG_VIRTUAL_KEYS 0 + +// Log debug messages about pointers. +#define DEBUG_POINTERS 0 + +// Log debug messages about pointer assignment calculations. +#define DEBUG_POINTER_ASSIGNMENT 0 + +// Log debug messages about gesture detection. +#define DEBUG_GESTURES 0 + +// Log debug messages about the vibrator. +#define DEBUG_VIBRATOR 0 + +// Log debug messages about fusing stylus data. +#define DEBUG_STYLUS_FUSION 0 + +#define INDENT " " +#define INDENT2 " " +#define INDENT3 " " +#define INDENT4 " " +#define INDENT5 " " + +#include <input/Input.h> + +namespace android { + +// --- Static Functions --- + +template <typename T> +inline static T abs(const T& value) { + return value < 0 ? -value : value; +} + +template <typename T> +inline static T min(const T& a, const T& b) { + return a < b ? a : b; +} + +inline static float avg(float x, float y) { + return (x + y) / 2; +} + +static inline const char* toString(bool value) { + return value ? "true" : "false"; +} + +static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { + return (sources & sourceMask & ~AINPUT_SOURCE_CLASS_MASK) != 0; +} + +} // namespace android + +#endif // _UI_INPUTREADER_MACROS_H
\ No newline at end of file diff --git a/services/inputflinger/TouchVideoDevice.cpp b/services/inputflinger/reader/TouchVideoDevice.cpp index 19c1313dcd..c075078528 100644 --- a/services/inputflinger/TouchVideoDevice.cpp +++ b/services/inputflinger/reader/TouchVideoDevice.cpp @@ -37,10 +37,13 @@ using android::base::unique_fd; namespace android { TouchVideoDevice::TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath, - uint32_t height, uint32_t width, - const std::array<const int16_t*, NUM_BUFFERS>& readLocations) : - mFd(fd), mName(std::move(name)), mPath(std::move(devicePath)), - mHeight(height), mWidth(width), + uint32_t height, uint32_t width, + const std::array<const int16_t*, NUM_BUFFERS>& readLocations) + : mFd(fd), + mName(std::move(name)), + mPath(std::move(devicePath)), + mHeight(height), + mWidth(width), mReadLocations(readLocations) { mFrames.reserve(MAX_QUEUE_SIZE); }; @@ -60,11 +63,11 @@ std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePat } if (!(cap.capabilities & V4L2_CAP_TOUCH)) { ALOGE("Capability V4L2_CAP_TOUCH is not present, can't use device for heatmap data. " - "Make sure device specifies V4L2_CAP_TOUCH"); + "Make sure device specifies V4L2_CAP_TOUCH"); return nullptr; } - ALOGI("Opening video device: driver = %s, card = %s, bus_info = %s, version = %i", - cap.driver, cap.card, cap.bus_info, cap.version); + ALOGI("Opening video device: driver = %s, card = %s, bus_info = %s, version = %i", cap.driver, + cap.card, cap.bus_info, cap.version); std::string name = reinterpret_cast<const char*>(cap.card); struct v4l2_input v4l2_input_struct; @@ -77,7 +80,7 @@ std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePat if (v4l2_input_struct.type != V4L2_INPUT_TYPE_TOUCH) { ALOGE("Video device does not provide touch data. " - "Make sure device specifies V4L2_INPUT_TYPE_TOUCH."); + "Make sure device specifies V4L2_INPUT_TYPE_TOUCH."); return nullptr; } @@ -120,14 +123,14 @@ std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePat return nullptr; } if (buf.length != height * width * sizeof(int16_t)) { - ALOGE("Unexpected value of buf.length = %i (offset = %" PRIu32 ")", - buf.length, buf.m.offset); + ALOGE("Unexpected value of buf.length = %i (offset = %" PRIu32 ")", buf.length, + buf.m.offset); return nullptr; } - readLocations[i] = static_cast<const int16_t*>(mmap(nullptr /* start anywhere */, - buf.length, PROT_READ /* required */, MAP_SHARED /* recommended */, - fd.get(), buf.m.offset)); + readLocations[i] = static_cast<const int16_t*>( + mmap(nullptr /* start anywhere */, buf.length, PROT_READ /* required */, + MAP_SHARED /* recommended */, fd.get(), buf.m.offset)); if (readLocations[i] == MAP_FAILED) { ALOGE("%s: map failed: %s", __func__, strerror(errno)); return nullptr; @@ -150,8 +153,9 @@ std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePat } } // Using 'new' to access a non-public constructor. - return std::unique_ptr<TouchVideoDevice>(new TouchVideoDevice( - fd.release(), std::move(name), std::move(devicePath), height, width, readLocations)); + return std::unique_ptr<TouchVideoDevice>(new TouchVideoDevice(fd.release(), std::move(name), + std::move(devicePath), height, + width, readLocations)); } size_t TouchVideoDevice::readAndQueueFrames() { @@ -163,10 +167,10 @@ size_t TouchVideoDevice::readAndQueueFrames() { } // Concatenate the vectors, then clip up to maximum size allowed mFrames.insert(mFrames.end(), std::make_move_iterator(frames.begin()), - std::make_move_iterator(frames.end())); + std::make_move_iterator(frames.end())); if (mFrames.size() > MAX_QUEUE_SIZE) { ALOGE("More than %zu frames have been accumulated. Dropping %zu frames", MAX_QUEUE_SIZE, - mFrames.size() - MAX_QUEUE_SIZE); + mFrames.size() - MAX_QUEUE_SIZE); mFrames.erase(mFrames.begin(), mFrames.end() - MAX_QUEUE_SIZE); } return numFrames; @@ -193,8 +197,8 @@ std::optional<TouchVideoFrame> TouchVideoDevice::readFrame() { if ((buf.flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) != V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC) { // We use CLOCK_MONOTONIC for input events, so if the clocks don't match, // we can't compare timestamps. Just log a warning, since this is a driver issue - ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC", - buf.timestamp.tv_sec, buf.timestamp.tv_usec); + ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC", buf.timestamp.tv_sec, + buf.timestamp.tv_usec); } std::vector<int16_t> data(mHeight * mWidth); const int16_t* readFrom = mReadLocations[buf.index]; @@ -233,7 +237,7 @@ TouchVideoDevice::~TouchVideoDevice() { } for (const int16_t* buffer : mReadLocations) { void* bufferAddress = static_cast<void*>(const_cast<int16_t*>(buffer)); - result = munmap(bufferAddress, mHeight * mWidth * sizeof(int16_t)); + result = munmap(bufferAddress, mHeight * mWidth * sizeof(int16_t)); if (result == -1) { ALOGE("%s: Couldn't unmap: [%s]", __func__, strerror(errno)); } @@ -242,9 +246,9 @@ TouchVideoDevice::~TouchVideoDevice() { std::string TouchVideoDevice::dump() const { return StringPrintf("Video device %s (%s) : height=%" PRIu32 ", width=%" PRIu32 - ", fd=%i, hasValidFd=%s", - mName.c_str(), mPath.c_str(), mHeight, mWidth, mFd.get(), - hasValidFd() ? "true" : "false"); + ", fd=%i, hasValidFd=%s", + mName.c_str(), mPath.c_str(), mHeight, mWidth, mFd.get(), + hasValidFd() ? "true" : "false"); } } // namespace android diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 63a20ef3e2..794396acca 100644 --- a/services/inputflinger/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -14,7 +14,6 @@ * limitations under the License. */ -// #ifndef _RUNTIME_EVENT_HUB_H #define _RUNTIME_EVENT_HUB_H @@ -22,17 +21,17 @@ #include <input/Input.h> #include <input/InputDevice.h> -#include <input/Keyboard.h> -#include <input/KeyLayoutMap.h> #include <input/KeyCharacterMap.h> +#include <input/KeyLayoutMap.h> +#include <input/Keyboard.h> #include <input/VirtualKeyMap.h> -#include <utils/Mutex.h> -#include <utils/Log.h> -#include <utils/List.h> +#include <utils/BitSet.h> #include <utils/Errors.h> -#include <utils/PropertyMap.h> #include <utils/KeyedVector.h> -#include <utils/BitSet.h> +#include <utils/List.h> +#include <utils/Log.h> +#include <utils/Mutex.h> +#include <utils/PropertyMap.h> #include <linux/input.h> #include <sys/epoll.h> @@ -41,8 +40,8 @@ /* Convenience constants. */ -#define BTN_FIRST 0x100 // first button code -#define BTN_LAST 0x15f // last button code +#define BTN_FIRST 0x100 // first button code +#define BTN_LAST 0x15f // last button code namespace android { @@ -61,10 +60,10 @@ struct RawEvent { struct RawAbsoluteAxisInfo { bool valid; // true if the information is valid, false otherwise - int32_t minValue; // minimum value - int32_t maxValue; // maximum value - int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8 - int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise + int32_t minValue; // minimum value + int32_t maxValue; // maximum value + int32_t flat; // center flat position, eg. flat == 8 means center is between -8 and 8 + int32_t fuzz; // error tolerance, eg. fuzz == 4 means value is +/- 4 due to noise int32_t resolution; // resolution in units per mm or radians per mm inline void clear() { @@ -82,37 +81,37 @@ struct RawAbsoluteAxisInfo { */ enum { /* The input device is a keyboard or has buttons. */ - INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001, + INPUT_DEVICE_CLASS_KEYBOARD = 0x00000001, /* The input device is an alpha-numeric keyboard (not just a dial pad). */ - INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002, + INPUT_DEVICE_CLASS_ALPHAKEY = 0x00000002, /* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */ - INPUT_DEVICE_CLASS_TOUCH = 0x00000004, + INPUT_DEVICE_CLASS_TOUCH = 0x00000004, /* The input device is a cursor device such as a trackball or mouse. */ - INPUT_DEVICE_CLASS_CURSOR = 0x00000008, + INPUT_DEVICE_CLASS_CURSOR = 0x00000008, /* The input device is a multi-touch touchscreen. */ - INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010, + INPUT_DEVICE_CLASS_TOUCH_MT = 0x00000010, /* The input device is a directional pad (implies keyboard, has DPAD keys). */ - INPUT_DEVICE_CLASS_DPAD = 0x00000020, + INPUT_DEVICE_CLASS_DPAD = 0x00000020, /* The input device is a gamepad (implies keyboard, has BUTTON keys). */ - INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040, + INPUT_DEVICE_CLASS_GAMEPAD = 0x00000040, /* The input device has switches. */ - INPUT_DEVICE_CLASS_SWITCH = 0x00000080, + INPUT_DEVICE_CLASS_SWITCH = 0x00000080, /* The input device is a joystick (implies gamepad, has joystick absolute axes). */ - INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100, + INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100, /* The input device has a vibrator (supports FF_RUMBLE). */ - INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200, + INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200, /* The input device has a microphone. */ - INPUT_DEVICE_CLASS_MIC = 0x00000400, + INPUT_DEVICE_CLASS_MIC = 0x00000400, /* The input device is an external stylus (has data we want to fuse with touch data). */ INPUT_DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800, @@ -121,10 +120,10 @@ enum { INPUT_DEVICE_CLASS_ROTARY_ENCODER = 0x00001000, /* The input device is virtual (not a real device, not part of UI configuration). */ - INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000, + INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000, /* The input device is external (not built-in). */ - INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000, + INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000, }; /* @@ -148,8 +147,8 @@ extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses); */ class EventHubInterface : public virtual RefBase { protected: - EventHubInterface() { } - virtual ~EventHubInterface() { } + EventHubInterface() {} + virtual ~EventHubInterface() {} public: // Synthetic raw event type codes produced when devices are added or removed. @@ -174,18 +173,17 @@ public: virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0; virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const = 0; + RawAbsoluteAxisInfo* outAxisInfo) const = 0; virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0; virtual bool hasInputProperty(int32_t deviceId, int property) const = 0; - virtual status_t mapKey(int32_t deviceId, - int32_t scanCode, int32_t usageCode, int32_t metaState, - int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const = 0; + virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, + int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, + uint32_t* outFlags) const = 0; - virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, - AxisInfo* outAxisInfo) const = 0; + virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const = 0; // Sets devices that are excluded from opening. // This can be used to ignore input devices for sensors. @@ -213,13 +211,13 @@ public: virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const = 0; virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0; virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, - int32_t* outValue) const = 0; + int32_t* outValue) const = 0; /* * Examine key input devices for specific framework keycode support */ virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags) const = 0; + uint8_t* outFlags) const = 0; virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0; @@ -227,8 +225,8 @@ public: virtual bool hasLed(int32_t deviceId, int32_t led) const = 0; virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0; - virtual void getVirtualKeyDefinitions(int32_t deviceId, - std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0; + virtual void getVirtualKeyDefinitions( + int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const = 0; virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0; virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0; @@ -259,8 +257,7 @@ public: virtual status_t disableDevice(int32_t deviceId) = 0; }; -class EventHub : public EventHubInterface -{ +class EventHub : public EventHubInterface { public: EventHub(); @@ -273,18 +270,17 @@ public: virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const; virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const; + RawAbsoluteAxisInfo* outAxisInfo) const; virtual bool hasRelativeAxis(int32_t deviceId, int axis) const; virtual bool hasInputProperty(int32_t deviceId, int property) const; - virtual status_t mapKey(int32_t deviceId, - int32_t scanCode, int32_t usageCode, int32_t metaState, - int32_t* outKeycode, int32_t *outMetaState, uint32_t* outFlags) const; + virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, + int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, + uint32_t* outFlags) const; - virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, - AxisInfo* outAxisInfo) const; + virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const; virtual void setExcludedDevices(const std::vector<std::string>& devices); @@ -293,8 +289,8 @@ public: virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const; virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const; - virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) const; + virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags) const; virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize); virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId); @@ -304,7 +300,7 @@ public: virtual void setLedState(int32_t deviceId, int32_t led, bool on); virtual void getVirtualKeyDefinitions(int32_t deviceId, - std::vector<VirtualKeyDefinition>& outVirtualKeys) const; + std::vector<VirtualKeyDefinition>& outVirtualKeys) const; virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const; virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map); @@ -357,7 +353,7 @@ private: int32_t controllerNumber; Device(int fd, int32_t id, const std::string& path, - const InputDeviceIdentifier& identifier); + const InputDeviceIdentifier& identifier); ~Device(); void close(); @@ -382,7 +378,7 @@ private: void addDeviceLocked(Device* device); void assignDescriptorLocked(InputDeviceIdentifier& identifier); - void closeDeviceByPathLocked(const char *devicePath); + void closeDeviceByPathLocked(const char* devicePath); void closeVideoDeviceByPathLocked(const std::string& devicePath); void closeDeviceLocked(Device* device); void closeAllDevicesLocked(); @@ -399,7 +395,7 @@ private: status_t unregisterDeviceFromEpollLocked(Device* device); void unregisterVideoDeviceFromEpollLocked(const TouchVideoDevice& videoDevice); - status_t scanDirLocked(const char *dirname); + status_t scanDirLocked(const char* dirname); status_t scanVideoDirLocked(const std::string& dirname); void scanDevicesLocked(); status_t readNotifyLocked(); @@ -455,8 +451,8 @@ private: */ std::vector<std::unique_ptr<TouchVideoDevice>> mUnattachedVideoDevices; - Device *mOpeningDevices; - Device *mClosingDevices; + Device* mOpeningDevices; + Device* mClosingDevices; bool mNeedToSendFinishedDeviceScan; bool mNeedToReopenDevices; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h new file mode 100644 index 0000000000..57f0b319c8 --- /dev/null +++ b/services/inputflinger/reader/include/InputDevice.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_INPUT_DEVICE_H +#define _UI_INPUTREADER_INPUT_DEVICE_H + +#include "EventHub.h" +#include "InputReaderBase.h" +#include "InputReaderContext.h" + +#include <input/DisplayViewport.h> +#include <input/InputDevice.h> +#include <utils/PropertyMap.h> + +#include <stdint.h> +#include <optional> +#include <vector> + +namespace android { + +class InputMapper; + +/* Represents the state of a single input device. */ +class InputDevice { +public: + InputDevice(InputReaderContext* context, int32_t id, int32_t generation, + int32_t controllerNumber, const InputDeviceIdentifier& identifier, + uint32_t classes); + ~InputDevice(); + + inline InputReaderContext* getContext() { return mContext; } + inline int32_t getId() const { return mId; } + inline int32_t getControllerNumber() const { return mControllerNumber; } + inline int32_t getGeneration() const { return mGeneration; } + inline const std::string getName() const { return mIdentifier.name; } + inline const std::string getDescriptor() { return mIdentifier.descriptor; } + inline uint32_t getClasses() const { return mClasses; } + inline uint32_t getSources() const { return mSources; } + + inline bool isExternal() { return mIsExternal; } + inline void setExternal(bool external) { mIsExternal = external; } + inline std::optional<uint8_t> getAssociatedDisplayPort() const { + return mAssociatedDisplayPort; + } + + inline void setMic(bool hasMic) { mHasMic = hasMic; } + inline bool hasMic() const { return mHasMic; } + + inline bool isIgnored() { return mMappers.empty(); } + + bool isEnabled(); + void setEnabled(bool enabled, nsecs_t when); + + void dump(std::string& dump); + void addMapper(InputMapper* mapper); + void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + void reset(nsecs_t when); + void process(const RawEvent* rawEvents, size_t count); + void timeoutExpired(nsecs_t when); + void updateExternalStylusState(const StylusState& state); + + void getDeviceInfo(InputDeviceInfo* outDeviceInfo); + int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, + uint8_t* outFlags); + void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + void cancelVibrate(int32_t token); + void cancelTouch(nsecs_t when); + + int32_t getMetaState(); + void updateMetaState(int32_t keyCode); + + void fadePointer(); + + void bumpGeneration(); + + void notifyReset(nsecs_t when); + + inline const PropertyMap& getConfiguration() { return mConfiguration; } + inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } + + bool hasKey(int32_t code) { return getEventHub()->hasScanCode(mId, code); } + + bool hasAbsoluteAxis(int32_t code) { + RawAbsoluteAxisInfo info; + getEventHub()->getAbsoluteAxisInfo(mId, code, &info); + return info.valid; + } + + bool isKeyPressed(int32_t code) { + return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN; + } + + int32_t getAbsoluteAxisValue(int32_t code) { + int32_t value; + getEventHub()->getAbsoluteAxisValue(mId, code, &value); + return value; + } + + std::optional<int32_t> getAssociatedDisplay(); + +private: + InputReaderContext* mContext; + int32_t mId; + int32_t mGeneration; + int32_t mControllerNumber; + InputDeviceIdentifier mIdentifier; + std::string mAlias; + uint32_t mClasses; + + std::vector<InputMapper*> mMappers; + + uint32_t mSources; + bool mIsExternal; + std::optional<uint8_t> mAssociatedDisplayPort; + bool mHasMic; + bool mDropUntilNextSync; + + typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); + int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); + + PropertyMap mConfiguration; +}; + +} // namespace android + +#endif //_UI_INPUTREADER_INPUT_DEVICE_H diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h new file mode 100644 index 0000000000..e29c8f219c --- /dev/null +++ b/services/inputflinger/reader/include/InputReader.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_INPUT_READER_H +#define _UI_INPUTREADER_INPUT_READER_H + +#include "EventHub.h" +#include "InputListener.h" +#include "InputReaderBase.h" +#include "InputReaderContext.h" + +#include <utils/BitSet.h> +#include <utils/Condition.h> +#include <utils/KeyedVector.h> +#include <utils/Mutex.h> +#include <utils/Timers.h> + +#include <vector> + +namespace android { + +class InputDevice; +class InputMapper; +struct StylusState; + +/* The input reader reads raw event data from the event hub and processes it into input events + * that it sends to the input listener. Some functions of the input reader, such as early + * event filtering in low power states, are controlled by a separate policy object. + * + * The InputReader owns a collection of InputMappers. Most of the work it does happens + * on the input reader thread but the InputReader can receive queries from other system + * components running on arbitrary threads. To keep things manageable, the InputReader + * uses a single Mutex to guard its state. The Mutex may be held while calling into the + * EventHub or the InputReaderPolicy but it is never held while calling into the + * InputListener. + */ +class InputReader : public InputReaderInterface { +public: + InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, + const sp<InputListenerInterface>& listener); + virtual ~InputReader(); + + virtual void dump(std::string& dump); + virtual void monitor(); + + virtual void loopOnce(); + + virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices); + + virtual bool isInputDeviceEnabled(int32_t deviceId); + + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode); + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode); + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw); + + virtual void toggleCapsLockState(int32_t deviceId); + + virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual void requestRefreshConfiguration(uint32_t changes); + + virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, + ssize_t repeat, int32_t token); + virtual void cancelVibrate(int32_t deviceId, int32_t token); + + virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId); +protected: + // These members are protected so they can be instrumented by test cases. + virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber, + const InputDeviceIdentifier& identifier, + uint32_t classes); + + class ContextImpl : public InputReaderContext { + InputReader* mReader; + + public: + explicit ContextImpl(InputReader* reader); + + virtual void updateGlobalMetaState(); + virtual int32_t getGlobalMetaState(); + virtual void disableVirtualKeysUntil(nsecs_t time); + virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, + int32_t scanCode); + virtual void fadePointer(); + virtual void requestTimeoutAtTime(nsecs_t when); + virtual int32_t bumpGeneration(); + virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices); + virtual void dispatchExternalStylusState(const StylusState& outState); + virtual InputReaderPolicyInterface* getPolicy(); + virtual InputListenerInterface* getListener(); + virtual EventHubInterface* getEventHub(); + virtual uint32_t getNextSequenceNum(); + } mContext; + + friend class ContextImpl; + +private: + Mutex mLock; + + Condition mReaderIsAliveCondition; + + sp<EventHubInterface> mEventHub; + sp<InputReaderPolicyInterface> mPolicy; + sp<QueuedInputListener> mQueuedListener; + + InputReaderConfiguration mConfig; + + // used by InputReaderContext::getNextSequenceNum() as a counter for event sequence numbers + uint32_t mNextSequenceNum; + + // The event queue. + static const int EVENT_BUFFER_SIZE = 256; + RawEvent mEventBuffer[EVENT_BUFFER_SIZE]; + + KeyedVector<int32_t, InputDevice*> mDevices; + + // low-level input event decoding and device management + void processEventsLocked(const RawEvent* rawEvents, size_t count); + + void addDeviceLocked(nsecs_t when, int32_t deviceId); + void removeDeviceLocked(nsecs_t when, int32_t deviceId); + void processEventsForDeviceLocked(int32_t deviceId, const RawEvent* rawEvents, size_t count); + void timeoutExpiredLocked(nsecs_t when); + + void handleConfigurationChangedLocked(nsecs_t when); + + int32_t mGlobalMetaState; + void updateGlobalMetaStateLocked(); + int32_t getGlobalMetaStateLocked(); + + void notifyExternalStylusPresenceChanged(); + void getExternalStylusDevicesLocked(std::vector<InputDeviceInfo>& outDevices); + void dispatchExternalStylusState(const StylusState& state); + + void fadePointerLocked(); + + int32_t mGeneration; + int32_t bumpGenerationLocked(); + + void getInputDevicesLocked(std::vector<InputDeviceInfo>& outInputDevices); + + nsecs_t mDisableVirtualKeysTimeout; + void disableVirtualKeysUntilLocked(nsecs_t time); + bool shouldDropVirtualKeyLocked(nsecs_t now, InputDevice* device, int32_t keyCode, + int32_t scanCode); + + nsecs_t mNextTimeout; + void requestTimeoutAtTimeLocked(nsecs_t when); + + uint32_t mConfigurationChangesToRefresh; + void refreshConfigurationLocked(uint32_t changes); + + // 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, + GetStateFunc getStateFunc); + bool markSupportedKeyCodesLocked(int32_t deviceId, uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_INPUT_READER_H diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h new file mode 100644 index 0000000000..3472346d44 --- /dev/null +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_INPUT_READER_CONTEXT_H +#define _UI_INPUTREADER_INPUT_READER_CONTEXT_H + +#include <input/InputDevice.h> + +#include <vector> + +namespace android { + +class EventHubInterface; +class InputDevice; +class InputListenerInterface; +class InputMapper; +class InputReaderPolicyInterface; +struct StylusState; + +/* Internal interface used by individual input devices to access global input device state + * and parameters maintained by the input reader. + */ +class InputReaderContext { +public: + InputReaderContext() {} + virtual ~InputReaderContext() {} + + virtual void updateGlobalMetaState() = 0; + virtual int32_t getGlobalMetaState() = 0; + + virtual void disableVirtualKeysUntil(nsecs_t time) = 0; + virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, + int32_t scanCode) = 0; + + virtual void fadePointer() = 0; + + virtual void requestTimeoutAtTime(nsecs_t when) = 0; + virtual int32_t bumpGeneration() = 0; + + virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) = 0; + virtual void dispatchExternalStylusState(const StylusState& outState) = 0; + + virtual InputReaderPolicyInterface* getPolicy() = 0; + virtual InputListenerInterface* getListener() = 0; + virtual EventHubInterface* getEventHub() = 0; + + virtual uint32_t getNextSequenceNum() = 0; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_INPUT_READER_CONTEXT_H diff --git a/services/inputflinger/reader/include/StylusState.h b/services/inputflinger/reader/include/StylusState.h new file mode 100644 index 0000000000..17f158c9e1 --- /dev/null +++ b/services/inputflinger/reader/include/StylusState.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_STYLUS_STATE_H +#define _UI_INPUTREADER_STYLUS_STATE_H + +#include <input/Input.h> + +#include <limits.h> + +namespace android { + +struct StylusState { + /* Time the stylus event was received. */ + nsecs_t when; + /* Pressure as reported by the stylus, normalized to the range [0, 1.0]. */ + float pressure; + /* The state of the stylus buttons as a bitfield (e.g. AMOTION_EVENT_BUTTON_SECONDARY). */ + uint32_t buttons; + /* Which tool type the stylus is currently using (e.g. AMOTION_EVENT_TOOL_TYPE_ERASER). */ + int32_t toolType; + + void copyFrom(const StylusState& other) { + when = other.when; + pressure = other.pressure; + buttons = other.buttons; + toolType = other.toolType; + } + + void clear() { + when = LLONG_MAX; + pressure = 0.f; + buttons = 0; + toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + } +}; + +} // namespace android + +#endif // _UI_INPUTREADER_STYLUS_STATE_H diff --git a/services/inputflinger/TouchVideoDevice.h b/services/inputflinger/reader/include/TouchVideoDevice.h index 0e7e2ef496..5a32443f29 100644 --- a/services/inputflinger/TouchVideoDevice.h +++ b/services/inputflinger/reader/include/TouchVideoDevice.h @@ -14,14 +14,14 @@ * limitations under the License. */ -#ifndef _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H -#define _INPUTFLINGER_TOUCH_VIDEO_DEVICE_H +#ifndef _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H +#define _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H -#include <array> #include <android-base/unique_fd.h> #include <input/TouchVideoFrame.h> -#include <optional> #include <stdint.h> +#include <array> +#include <optional> #include <string> #include <vector> @@ -109,9 +109,9 @@ private: * The constructor is private because opening a v4l2 device requires many checks. * To get a new TouchVideoDevice, use 'create' instead. */ - explicit TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath, - uint32_t height, uint32_t width, - const std::array<const int16_t*, NUM_BUFFERS>& readLocations); + explicit TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath, uint32_t height, + uint32_t width, + const std::array<const int16_t*, NUM_BUFFERS>& readLocations); /** * Read all currently available frames. */ @@ -121,5 +121,7 @@ private: */ std::optional<TouchVideoFrame> readFrame(); }; + } // namespace android -#endif //_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H + +#endif // _UI_INPUTFLINGER_TOUCH_VIDEO_DEVICE_H diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp new file mode 100644 index 0000000000..da85fda0e9 --- /dev/null +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "CursorInputMapper.h" + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "TouchCursorInputMapperCommon.h" + +namespace android { + +// --- CursorMotionAccumulator --- + +CursorMotionAccumulator::CursorMotionAccumulator() { + clearRelativeAxes(); +} + +void CursorMotionAccumulator::reset(InputDevice* device) { + clearRelativeAxes(); +} + +void CursorMotionAccumulator::clearRelativeAxes() { + mRelX = 0; + mRelY = 0; +} + +void CursorMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_REL) { + switch (rawEvent->code) { + case REL_X: + mRelX = rawEvent->value; + break; + case REL_Y: + mRelY = rawEvent->value; + break; + } + } +} + +void CursorMotionAccumulator::finishSync() { + clearRelativeAxes(); +} + +// --- CursorInputMapper --- + +CursorInputMapper::CursorInputMapper(InputDevice* device) : InputMapper(device) {} + +CursorInputMapper::~CursorInputMapper() {} + +uint32_t CursorInputMapper::getSources() { + return mSource; +} + +void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mParameters.mode == Parameters::MODE_POINTER) { + float minX, minY, maxX, maxY; + if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { + info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f); + } + } else { + info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f); + } + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); + + if (mCursorScrollAccumulator.haveRelativeVWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); + } + if (mCursorScrollAccumulator.haveRelativeHWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); + } +} + +void CursorInputMapper::dump(std::string& dump) { + dump += INDENT2 "Cursor Input Mapper:\n"; + dumpParameters(dump); + dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale); + dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale); + dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision); + dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision); + dump += StringPrintf(INDENT3 "HaveVWheel: %s\n", + toString(mCursorScrollAccumulator.haveRelativeVWheel())); + dump += StringPrintf(INDENT3 "HaveHWheel: %s\n", + toString(mCursorScrollAccumulator.haveRelativeHWheel())); + dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); + dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); + dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); + dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState); + dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState))); + dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); +} + +void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + mCursorScrollAccumulator.configure(getDevice()); + + // Configure basic parameters. + configureParameters(); + + // Configure device mode. + switch (mParameters.mode) { + case Parameters::MODE_POINTER_RELATIVE: + // Should not happen during first time configuration. + ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER"); + mParameters.mode = Parameters::MODE_POINTER; + [[fallthrough]]; + case Parameters::MODE_POINTER: + mSource = AINPUT_SOURCE_MOUSE; + mXPrecision = 1.0f; + mYPrecision = 1.0f; + mXScale = 1.0f; + mYScale = 1.0f; + mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + break; + case Parameters::MODE_NAVIGATION: + mSource = AINPUT_SOURCE_TRACKBALL; + mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + break; + } + + mVWheelScale = 1.0f; + mHWheelScale = 1.0f; + } + + if ((!changes && config->pointerCapture) || + (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)) { + if (config->pointerCapture) { + if (mParameters.mode == Parameters::MODE_POINTER) { + mParameters.mode = Parameters::MODE_POINTER_RELATIVE; + mSource = AINPUT_SOURCE_MOUSE_RELATIVE; + // Keep PointerController around in order to preserve the pointer position. + mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } else { + ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); + } + } else { + if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) { + mParameters.mode = Parameters::MODE_POINTER; + mSource = AINPUT_SOURCE_MOUSE; + } else { + ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE"); + } + } + bumpGeneration(); + if (changes) { + getDevice()->notifyReset(when); + } + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { + mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters); + mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters); + mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + mOrientation = DISPLAY_ORIENTATION_0; + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { + std::optional<DisplayViewport> internalViewport = + config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + if (internalViewport) { + mOrientation = internalViewport->orientation; + } + } + + // Update the PointerController if viewports changed. + if (mParameters.mode == Parameters::MODE_POINTER) { + getPolicy()->obtainPointerController(getDeviceId()); + } + bumpGeneration(); + } +} + +void CursorInputMapper::configureParameters() { + mParameters.mode = Parameters::MODE_POINTER; + String8 cursorModeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) { + if (cursorModeString == "navigation") { + mParameters.mode = Parameters::MODE_NAVIGATION; + } else if (cursorModeString != "pointer" && cursorModeString != "default") { + ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string()); + } + } + + mParameters.orientationAware = false; + getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"), + mParameters.orientationAware); + + mParameters.hasAssociatedDisplay = false; + if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { + mParameters.hasAssociatedDisplay = true; + } +} + +void CursorInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n", + toString(mParameters.hasAssociatedDisplay)); + + switch (mParameters.mode) { + case Parameters::MODE_POINTER: + dump += INDENT4 "Mode: pointer\n"; + break; + case Parameters::MODE_POINTER_RELATIVE: + dump += INDENT4 "Mode: relative pointer\n"; + break; + case Parameters::MODE_NAVIGATION: + dump += INDENT4 "Mode: navigation\n"; + break; + default: + ALOG_ASSERT(false); + } + + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); +} + +void CursorInputMapper::reset(nsecs_t when) { + mButtonState = 0; + mDownTime = 0; + + mPointerVelocityControl.reset(); + mWheelXVelocityControl.reset(); + mWheelYVelocityControl.reset(); + + mCursorButtonAccumulator.reset(getDevice()); + mCursorMotionAccumulator.reset(getDevice()); + mCursorScrollAccumulator.reset(getDevice()); + + InputMapper::reset(when); +} + +void CursorInputMapper::process(const RawEvent* rawEvent) { + mCursorButtonAccumulator.process(rawEvent); + mCursorMotionAccumulator.process(rawEvent); + mCursorScrollAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void CursorInputMapper::sync(nsecs_t when) { + int32_t lastButtonState = mButtonState; + int32_t currentButtonState = mCursorButtonAccumulator.getButtonState(); + mButtonState = currentButtonState; + + bool wasDown = isPointerDown(lastButtonState); + bool down = isPointerDown(currentButtonState); + bool downChanged; + if (!wasDown && down) { + mDownTime = when; + downChanged = true; + } else if (wasDown && !down) { + downChanged = true; + } else { + downChanged = false; + } + nsecs_t downTime = mDownTime; + bool buttonsChanged = currentButtonState != lastButtonState; + int32_t buttonsPressed = currentButtonState & ~lastButtonState; + int32_t buttonsReleased = lastButtonState & ~currentButtonState; + + float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale; + float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale; + bool moved = deltaX != 0 || deltaY != 0; + + // Rotate delta according to orientation if needed. + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay && + (deltaX != 0.0f || deltaY != 0.0f)) { + rotateDelta(mOrientation, &deltaX, &deltaY); + } + + // Move the pointer. + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE; + + PointerCoords pointerCoords; + pointerCoords.clear(); + + float vscroll = mCursorScrollAccumulator.getRelativeVWheel(); + float hscroll = mCursorScrollAccumulator.getRelativeHWheel(); + bool scrolled = vscroll != 0 || hscroll != 0; + + mWheelYVelocityControl.move(when, nullptr, &vscroll); + mWheelXVelocityControl.move(when, &hscroll, nullptr); + + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + int32_t displayId; + if (mSource == AINPUT_SOURCE_MOUSE) { + if (moved || scrolled || buttonsChanged) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + + if (moved) { + mPointerController->move(deltaX, deltaY); + } + + if (buttonsChanged) { + mPointerController->setButtonState(currentButtonState); + } + + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } + + float x, y; + mPointerController->getPosition(&x, &y); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); + displayId = mPointerController->getDisplayId(); + } else { + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); + displayId = ADISPLAY_ID_NONE; + } + + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); + + // Moving an external trackball or mouse should wake the device. + // We don't do this for internal cursor devices to prevent them from waking up + // the device in your pocket. + // TODO: Use the input device configuration to control this behavior more finely. + uint32_t policyFlags = 0; + if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) { + policyFlags |= POLICY_FLAG_WAKE; + } + + // Synthesize key down from buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, + displayId, policyFlags, lastButtonState, currentButtonState); + + // Send motion event. + if (downChanged || moved || scrolled || buttonsChanged) { + int32_t metaState = mContext->getGlobalMetaState(); + int32_t buttonState = lastButtonState; + int32_t motionEventAction; + if (downChanged) { + motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; + } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) { + motionEventAction = AMOTION_EVENT_ACTION_MOVE; + } else { + motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE; + } + + if (buttonsReleased) { + BitSet32 released(buttonsReleased); + while (!released.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); + buttonState &= ~actionButton; + NotifyMotionArgs releaseArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&releaseArgs); + } + } + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, motionEventAction, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + + if (buttonsPressed) { + BitSet32 pressed(buttonsPressed); + while (!pressed.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); + buttonState |= actionButton; + NotifyMotionArgs pressArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&pressArgs); + } + } + + ALOG_ASSERT(buttonState == currentButtonState); + + // Send hover move after UP to tell the application that the mouse is hovering now. + if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { + NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, + 0, metaState, currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&hoverArgs); + } + + // Send scroll events. + if (scrolled) { + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); + + NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), + mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&scrollArgs); + } + } + + // Synthesize key up from buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, + displayId, policyFlags, lastButtonState, currentButtonState); + + mCursorMotionAccumulator.finishSync(); + mCursorScrollAccumulator.finishSync(); +} + +int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); + } else { + return AKEY_STATE_UNKNOWN; + } +} + +void CursorInputMapper::fadePointer() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } +} + +std::optional<int32_t> CursorInputMapper::getAssociatedDisplay() { + if (mParameters.hasAssociatedDisplay) { + if (mParameters.mode == Parameters::MODE_POINTER) { + return std::make_optional(mPointerController->getDisplayId()); + } else { + // If the device is orientationAware and not a mouse, + // it expects to dispatch events to any display + return std::make_optional(ADISPLAY_ID_NONE); + } + } + return std::nullopt; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h new file mode 100644 index 0000000000..eb2ad54c80 --- /dev/null +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H +#define _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "InputMapper.h" + +#include <PointerControllerInterface.h> +#include <input/VelocityControl.h> + +namespace android { + +class VelocityControl; +class PointerControllerInterface; + +class CursorButtonAccumulator; +class CursorScrollAccumulator; + +/* Keeps track of cursor movements. */ +class CursorMotionAccumulator { +public: + CursorMotionAccumulator(); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + void finishSync(); + + inline int32_t getRelativeX() const { return mRelX; } + inline int32_t getRelativeY() const { return mRelY; } + +private: + int32_t mRelX; + int32_t mRelY; + + void clearRelativeAxes(); +}; + +class CursorInputMapper : public InputMapper { +public: + explicit CursorInputMapper(InputDevice* device); + virtual ~CursorInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + + virtual void fadePointer(); + + virtual std::optional<int32_t> getAssociatedDisplay(); + +private: + // Amount that trackball needs to move in order to generate a key event. + static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; + + // Immutable configuration parameters. + struct Parameters { + enum Mode { + MODE_POINTER, + MODE_POINTER_RELATIVE, + MODE_NAVIGATION, + }; + + Mode mode; + bool hasAssociatedDisplay; + bool orientationAware; + } mParameters; + + CursorButtonAccumulator mCursorButtonAccumulator; + CursorMotionAccumulator mCursorMotionAccumulator; + CursorScrollAccumulator mCursorScrollAccumulator; + + int32_t mSource; + float mXScale; + float mYScale; + float mXPrecision; + float mYPrecision; + + float mVWheelScale; + float mHWheelScale; + + // Velocity controls for mouse pointer and wheel movements. + // The controls for X and Y wheel movements are separate to keep them decoupled. + VelocityControl mPointerVelocityControl; + VelocityControl mWheelXVelocityControl; + VelocityControl mWheelYVelocityControl; + + int32_t mOrientation; + + sp<PointerControllerInterface> mPointerController; + + int32_t mButtonState; + nsecs_t mDownTime; + + void configureParameters(); + void dumpParameters(std::string& dump); + + void sync(nsecs_t when); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_CURSOR_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp new file mode 100644 index 0000000000..9aa0770245 --- /dev/null +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "ExternalStylusInputMapper.h" + +#include "SingleTouchMotionAccumulator.h" +#include "TouchButtonAccumulator.h" + +namespace android { + +ExternalStylusInputMapper::ExternalStylusInputMapper(InputDevice* device) : InputMapper(device) {} + +uint32_t ExternalStylusInputMapper::getSources() { + return AINPUT_SOURCE_STYLUS; +} + +void ExternalStylusInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, AINPUT_SOURCE_STYLUS, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f); +} + +void ExternalStylusInputMapper::dump(std::string& dump) { + dump += INDENT2 "External Stylus Input Mapper:\n"; + dump += INDENT3 "Raw Stylus Axes:\n"; + dumpRawAbsoluteAxisInfo(dump, mRawPressureAxis, "Pressure"); + dump += INDENT3 "Stylus State:\n"; + dumpStylusState(dump, mStylusState); +} + +void ExternalStylusInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPressureAxis); + mTouchButtonAccumulator.configure(getDevice()); +} + +void ExternalStylusInputMapper::reset(nsecs_t when) { + InputDevice* device = getDevice(); + mSingleTouchMotionAccumulator.reset(device); + mTouchButtonAccumulator.reset(device); + InputMapper::reset(when); +} + +void ExternalStylusInputMapper::process(const RawEvent* rawEvent) { + mSingleTouchMotionAccumulator.process(rawEvent); + mTouchButtonAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void ExternalStylusInputMapper::sync(nsecs_t when) { + mStylusState.clear(); + + mStylusState.when = when; + + mStylusState.toolType = mTouchButtonAccumulator.getToolType(); + if (mStylusState.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + mStylusState.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + + int32_t pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); + if (mRawPressureAxis.valid) { + mStylusState.pressure = float(pressure) / mRawPressureAxis.maxValue; + } else if (mTouchButtonAccumulator.isToolActive()) { + mStylusState.pressure = 1.0f; + } else { + mStylusState.pressure = 0.0f; + } + + mStylusState.buttons = mTouchButtonAccumulator.getButtonState(); + + mContext->dispatchExternalStylusState(mStylusState); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h new file mode 100644 index 0000000000..9764fbb3c1 --- /dev/null +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H +#define _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H + +#include "InputMapper.h" + +#include "SingleTouchMotionAccumulator.h" +#include "StylusState.h" +#include "TouchButtonAccumulator.h" + +namespace android { + +class ExternalStylusInputMapper : public InputMapper { +public: + explicit ExternalStylusInputMapper(InputDevice* device); + virtual ~ExternalStylusInputMapper() = default; + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + virtual void sync(nsecs_t when); + +private: + SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; + RawAbsoluteAxisInfo mRawPressureAxis; + TouchButtonAccumulator mTouchButtonAccumulator; + + StylusState mStylusState; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_EXTERNAL_STYLUS_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp new file mode 100644 index 0000000000..d941528d14 --- /dev/null +++ b/services/inputflinger/reader/mapper/InputMapper.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "InputMapper.h" + +#include "InputDevice.h" + +namespace android { + +InputMapper::InputMapper(InputDevice* device) : mDevice(device), mContext(device->getContext()) {} + +InputMapper::~InputMapper() {} + +void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { + info->addSource(getSources()); +} + +void InputMapper::dump(std::string& dump) {} + +void InputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) {} + +void InputMapper::reset(nsecs_t when) {} + +void InputMapper::timeoutExpired(nsecs_t when) {} + +int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return AKEY_STATE_UNKNOWN; +} + +int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return AKEY_STATE_UNKNOWN; +} + +bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return false; +} + +void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) {} + +void InputMapper::cancelVibrate(int32_t token) {} + +void InputMapper::cancelTouch(nsecs_t when) {} + +int32_t InputMapper::getMetaState() { + return 0; +} + +void InputMapper::updateMetaState(int32_t keyCode) {} + +void InputMapper::updateExternalStylusState(const StylusState& state) {} + +void InputMapper::fadePointer() {} + +status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo) { + return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo); +} + +void InputMapper::bumpGeneration() { + mDevice->bumpGeneration(); +} + +void InputMapper::dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, + const char* name) { + if (axis.valid) { + dump += StringPrintf(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d, resolution=%d\n", name, + axis.minValue, axis.maxValue, axis.flat, axis.fuzz, axis.resolution); + } else { + dump += StringPrintf(INDENT4 "%s: unknown range\n", name); + } +} + +void InputMapper::dumpStylusState(std::string& dump, const StylusState& state) { + dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when); + dump += StringPrintf(INDENT4 "Pressure: %f\n", state.pressure); + dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons); + dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h new file mode 100644 index 0000000000..cfd207cc4d --- /dev/null +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_INPUT_MAPPER_H +#define _UI_INPUTREADER_INPUT_MAPPER_H + +#include "EventHub.h" +#include "InputDevice.h" +#include "InputListener.h" +#include "InputReaderContext.h" +#include "StylusState.h" + +namespace android { + +/* An input mapper transforms raw input events into cooked event data. + * A single input device can have multiple associated input mappers in order to interpret + * different classes of events. + * + * InputMapper lifecycle: + * - create + * - configure with 0 changes + * - reset + * - process, process, process (may occasionally reconfigure with non-zero changes or reset) + * - reset + * - destroy + */ +class InputMapper { +public: + explicit InputMapper(InputDevice* device); + virtual ~InputMapper(); + + inline InputDevice* getDevice() { return mDevice; } + inline int32_t getDeviceId() { return mDevice->getId(); } + inline const std::string getDeviceName() { return mDevice->getName(); } + inline InputReaderContext* getContext() { return mContext; } + inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } + inline InputListenerInterface* getListener() { return mContext->getListener(); } + inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } + + virtual uint32_t getSources() = 0; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent) = 0; + virtual void timeoutExpired(nsecs_t when); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + virtual void cancelVibrate(int32_t token); + virtual void cancelTouch(nsecs_t when); + + virtual int32_t getMetaState(); + virtual void updateMetaState(int32_t keyCode); + + virtual void updateExternalStylusState(const StylusState& state); + + virtual void fadePointer(); + virtual std::optional<int32_t> getAssociatedDisplay() { return std::nullopt; } + +protected: + InputDevice* mDevice; + InputReaderContext* mContext; + + status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); + void bumpGeneration(); + + static void dumpRawAbsoluteAxisInfo(std::string& dump, const RawAbsoluteAxisInfo& axis, + const char* name); + static void dumpStylusState(std::string& dump, const StylusState& state); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_INPUT_MAPPER_H diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp new file mode 100644 index 0000000000..b493e8368f --- /dev/null +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "JoystickInputMapper.h" + +namespace android { + +JoystickInputMapper::JoystickInputMapper(InputDevice* device) : InputMapper(device) {} + +JoystickInputMapper::~JoystickInputMapper() {} + +uint32_t JoystickInputMapper::getSources() { + return AINPUT_SOURCE_JOYSTICK; +} + +void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + for (size_t i = 0; i < mAxes.size(); i++) { + const Axis& axis = mAxes.valueAt(i); + addMotionRange(axis.axisInfo.axis, axis, info); + + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + addMotionRange(axis.axisInfo.highAxis, axis, info); + } + } +} + +void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info) { + info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, axis.fuzz, + axis.resolution); + /* In order to ease the transition for developers from using the old axes + * to the newer, more semantically correct axes, we'll continue to register + * the old axes as duplicates of their corresponding new ones. */ + int32_t compatAxis = getCompatAxis(axisId); + if (compatAxis >= 0) { + info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, axis.min, axis.max, axis.flat, + axis.fuzz, axis.resolution); + } +} + +/* A mapping from axes the joystick actually has to the axes that should be + * artificially created for compatibility purposes. + * Returns -1 if no compatibility axis is needed. */ +int32_t JoystickInputMapper::getCompatAxis(int32_t axis) { + switch (axis) { + case AMOTION_EVENT_AXIS_LTRIGGER: + return AMOTION_EVENT_AXIS_BRAKE; + case AMOTION_EVENT_AXIS_RTRIGGER: + return AMOTION_EVENT_AXIS_GAS; + } + return -1; +} + +void JoystickInputMapper::dump(std::string& dump) { + dump += INDENT2 "Joystick Input Mapper:\n"; + + dump += INDENT3 "Axes:\n"; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + const char* label = getAxisLabel(axis.axisInfo.axis); + if (label) { + dump += StringPrintf(INDENT4 "%s", label); + } else { + dump += StringPrintf(INDENT4 "%d", axis.axisInfo.axis); + } + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + label = getAxisLabel(axis.axisInfo.highAxis); + if (label) { + dump += StringPrintf(" / %s (split at %d)", label, axis.axisInfo.splitValue); + } else { + dump += StringPrintf(" / %d (split at %d)", axis.axisInfo.highAxis, + axis.axisInfo.splitValue); + } + } else if (axis.axisInfo.mode == AxisInfo::MODE_INVERT) { + dump += " (invert)"; + } + + dump += StringPrintf(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n", + axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); + dump += StringPrintf(INDENT4 " scale=%0.5f, offset=%0.5f, " + "highScale=%0.5f, highOffset=%0.5f\n", + axis.scale, axis.offset, axis.highScale, axis.highOffset); + dump += StringPrintf(INDENT4 " rawAxis=%d, rawMin=%d, rawMax=%d, " + "rawFlat=%d, rawFuzz=%d, rawResolution=%d\n", + mAxes.keyAt(i), axis.rawAxisInfo.minValue, axis.rawAxisInfo.maxValue, + axis.rawAxisInfo.flat, axis.rawAxisInfo.fuzz, + axis.rawAxisInfo.resolution); + } +} + +void JoystickInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + // Collect all axes. + for (int32_t abs = 0; abs <= ABS_MAX; abs++) { + if (!(getAbsAxisUsage(abs, getDevice()->getClasses()) & INPUT_DEVICE_CLASS_JOYSTICK)) { + continue; // axis must be claimed by a different device + } + + RawAbsoluteAxisInfo rawAxisInfo; + getAbsoluteAxisInfo(abs, &rawAxisInfo); + if (rawAxisInfo.valid) { + // Map axis. + AxisInfo axisInfo; + bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo); + if (!explicitlyMapped) { + // Axis is not explicitly mapped, will choose a generic axis later. + axisInfo.mode = AxisInfo::MODE_NORMAL; + axisInfo.axis = -1; + } + + // Apply flat override. + int32_t rawFlat = + axisInfo.flatOverride < 0 ? rawAxisInfo.flat : axisInfo.flatOverride; + + // Calculate scaling factors and limits. + Axis axis; + if (axisInfo.mode == AxisInfo::MODE_SPLIT) { + float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); + float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale, + 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } else if (isCenteredAxis(axisInfo.axis)) { + float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale, + offset, -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } else { + float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale, + 0.0f, 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); + } + + // To eliminate noise while the joystick is at rest, filter out small variations + // in axis values up front. + axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f; + + mAxes.add(abs, axis); + } + } + + // If there are too many axes, start dropping them. + // Prefer to keep explicitly mapped axes. + if (mAxes.size() > PointerCoords::MAX_AXES) { + ALOGI("Joystick '%s' has %zu axes but the framework only supports a maximum of %d.", + getDeviceName().c_str(), mAxes.size(), PointerCoords::MAX_AXES); + pruneAxes(true); + pruneAxes(false); + } + + // Assign generic axis ids to remaining axes. + int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + if (axis.axisInfo.axis < 0) { + while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 && + haveAxis(nextGenericAxisId)) { + nextGenericAxisId += 1; + } + + if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { + axis.axisInfo.axis = nextGenericAxisId; + nextGenericAxisId += 1; + } else { + ALOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " + "have already been assigned to other axes.", + getDeviceName().c_str(), mAxes.keyAt(i)); + mAxes.removeItemsAt(i--); + numAxes -= 1; + } + } + } + } +} + +bool JoystickInputMapper::haveAxis(int32_t axisId) { + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + if (axis.axisInfo.axis == axisId || + (axis.axisInfo.mode == AxisInfo::MODE_SPLIT && axis.axisInfo.highAxis == axisId)) { + return true; + } + } + return false; +} + +void JoystickInputMapper::pruneAxes(bool ignoreExplicitlyMappedAxes) { + size_t i = mAxes.size(); + while (mAxes.size() > PointerCoords::MAX_AXES && i-- > 0) { + if (ignoreExplicitlyMappedAxes && mAxes.valueAt(i).explicitlyMapped) { + continue; + } + ALOGI("Discarding joystick '%s' axis %d because there are too many axes.", + getDeviceName().c_str(), mAxes.keyAt(i)); + mAxes.removeItemsAt(i); + } +} + +bool JoystickInputMapper::isCenteredAxis(int32_t axis) { + switch (axis) { + case AMOTION_EVENT_AXIS_X: + case AMOTION_EVENT_AXIS_Y: + case AMOTION_EVENT_AXIS_Z: + case AMOTION_EVENT_AXIS_RX: + case AMOTION_EVENT_AXIS_RY: + case AMOTION_EVENT_AXIS_RZ: + case AMOTION_EVENT_AXIS_HAT_X: + case AMOTION_EVENT_AXIS_HAT_Y: + case AMOTION_EVENT_AXIS_ORIENTATION: + case AMOTION_EVENT_AXIS_RUDDER: + case AMOTION_EVENT_AXIS_WHEEL: + return true; + default: + return false; + } +} + +void JoystickInputMapper::reset(nsecs_t when) { + // Recenter all axes. + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + axis.resetValue(); + } + + InputMapper::reset(when); +} + +void JoystickInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_ABS: { + ssize_t index = mAxes.indexOfKey(rawEvent->code); + if (index >= 0) { + Axis& axis = mAxes.editValueAt(index); + float newValue, highNewValue; + switch (axis.axisInfo.mode) { + case AxisInfo::MODE_INVERT: + newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale + + axis.offset; + highNewValue = 0.0f; + break; + case AxisInfo::MODE_SPLIT: + if (rawEvent->value < axis.axisInfo.splitValue) { + newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale + + axis.offset; + highNewValue = 0.0f; + } else if (rawEvent->value > axis.axisInfo.splitValue) { + newValue = 0.0f; + highNewValue = + (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale + + axis.highOffset; + } else { + newValue = 0.0f; + highNewValue = 0.0f; + } + break; + default: + newValue = rawEvent->value * axis.scale + axis.offset; + highNewValue = 0.0f; + break; + } + axis.newValue = newValue; + axis.highNewValue = highNewValue; + } + break; + } + + case EV_SYN: + switch (rawEvent->code) { + case SYN_REPORT: + sync(rawEvent->when, false /*force*/); + break; + } + break; + } +} + +void JoystickInputMapper::sync(nsecs_t when, bool force) { + if (!filterAxes(force)) { + return; + } + + int32_t metaState = mContext->getGlobalMetaState(); + int32_t buttonState = 0; + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + + PointerCoords pointerCoords; + pointerCoords.clear(); + + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + const Axis& axis = mAxes.valueAt(i); + setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue); + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis, + axis.highCurrentValue); + } + } + + // Moving a joystick axis should not wake the device because joysticks can + // be fairly noisy even when not in use. On the other hand, pushing a gamepad + // button will likely wake the device. + // TODO: Use the input device configuration to control this behavior more finely. + uint32_t policyFlags = 0; + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), + AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, + AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, 0, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); +} + +void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, + float value) { + pointerCoords->setAxisValue(axis, value); + /* In order to ease the transition for developers from using the old axes + * to the newer, more semantically correct axes, we'll continue to produce + * values for the old axes as mirrors of the value of their corresponding + * new axes. */ + int32_t compatAxis = getCompatAxis(axis); + if (compatAxis >= 0) { + pointerCoords->setAxisValue(compatAxis, value); + } +} + +bool JoystickInputMapper::filterAxes(bool force) { + bool atLeastOneSignificantChange = force; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + if (force || + hasValueChangedSignificantly(axis.filter, axis.newValue, axis.currentValue, axis.min, + axis.max)) { + axis.currentValue = axis.newValue; + atLeastOneSignificantChange = true; + } + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { + if (force || + hasValueChangedSignificantly(axis.filter, axis.highNewValue, axis.highCurrentValue, + axis.min, axis.max)) { + axis.highCurrentValue = axis.highNewValue; + atLeastOneSignificantChange = true; + } + } + } + return atLeastOneSignificantChange; +} + +bool JoystickInputMapper::hasValueChangedSignificantly(float filter, float newValue, + float currentValue, float min, float max) { + if (newValue != currentValue) { + // Filter out small changes in value unless the value is converging on the axis + // bounds or center point. This is intended to reduce the amount of information + // sent to applications by particularly noisy joysticks (such as PS3). + if (fabs(newValue - currentValue) > filter || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, min) || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, max) || + hasMovedNearerToValueWithinFilteredRange(filter, newValue, currentValue, 0)) { + return true; + } + } + return false; +} + +bool JoystickInputMapper::hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, + float currentValue, + float thresholdValue) { + float newDistance = fabs(newValue - thresholdValue); + if (newDistance < filter) { + float oldDistance = fabs(currentValue - thresholdValue); + if (newDistance < oldDistance) { + return true; + } + } + return false; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h new file mode 100644 index 0000000000..1b071d0480 --- /dev/null +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H +#define _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class JoystickInputMapper : public InputMapper { +public: + explicit JoystickInputMapper(InputDevice* device); + virtual ~JoystickInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +private: + struct Axis { + RawAbsoluteAxisInfo rawAxisInfo; + AxisInfo axisInfo; + + bool explicitlyMapped; // true if the axis was explicitly assigned an axis id + + float scale; // scale factor from raw to normalized values + float offset; // offset to add after scaling for normalization + float highScale; // scale factor from raw to normalized values of high split + float highOffset; // offset to add after scaling for normalization of high split + + float min; // normalized inclusive minimum + float max; // normalized inclusive maximum + float flat; // normalized flat region size + float fuzz; // normalized error tolerance + float resolution; // normalized resolution in units/mm + + float filter; // filter out small variations of this size + float currentValue; // current value + float newValue; // most recent value + float highCurrentValue; // current value of high split + float highNewValue; // most recent value of high split + + void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, + bool explicitlyMapped, float scale, float offset, float highScale, + float highOffset, float min, float max, float flat, float fuzz, + float resolution) { + this->rawAxisInfo = rawAxisInfo; + this->axisInfo = axisInfo; + this->explicitlyMapped = explicitlyMapped; + this->scale = scale; + this->offset = offset; + this->highScale = highScale; + this->highOffset = highOffset; + this->min = min; + this->max = max; + this->flat = flat; + this->fuzz = fuzz; + this->resolution = resolution; + this->filter = 0; + resetValue(); + } + + void resetValue() { + this->currentValue = 0; + this->newValue = 0; + this->highCurrentValue = 0; + this->highNewValue = 0; + } + }; + + // Axes indexed by raw ABS_* axis index. + KeyedVector<int32_t, Axis> mAxes; + + void sync(nsecs_t when, bool force); + + bool haveAxis(int32_t axisId); + void pruneAxes(bool ignoreExplicitlyMappedAxes); + bool filterAxes(bool force); + + static bool hasValueChangedSignificantly(float filter, float newValue, float currentValue, + float min, float max); + static bool hasMovedNearerToValueWithinFilteredRange(float filter, float newValue, + float currentValue, float thresholdValue); + + static bool isCenteredAxis(int32_t axis); + static int32_t getCompatAxis(int32_t axis); + + static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info); + static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, float value); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_JOYSTICK_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp new file mode 100644 index 0000000000..0e91c0e2cc --- /dev/null +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "KeyboardInputMapper.h" + +namespace android { + +// --- Static Definitions --- + +static int32_t rotateValueUsingRotationMap(int32_t value, int32_t orientation, + const int32_t map[][4], size_t mapSize) { + if (orientation != DISPLAY_ORIENTATION_0) { + for (size_t i = 0; i < mapSize; i++) { + if (value == map[i][0]) { + return map[i][orientation]; + } + } + } + return value; +} + +static const int32_t keyCodeRotationMap[][4] = { + // key codes enumerated counter-clockwise with the original (unrotated) key first + // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation + {AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT}, + {AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN}, + {AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT}, + {AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP}, + {AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT, + AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT}, + {AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP, + AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN}, + {AKEYCODE_SYSTEM_NAVIGATION_UP, AKEYCODE_SYSTEM_NAVIGATION_LEFT, + AKEYCODE_SYSTEM_NAVIGATION_DOWN, AKEYCODE_SYSTEM_NAVIGATION_RIGHT}, + {AKEYCODE_SYSTEM_NAVIGATION_LEFT, AKEYCODE_SYSTEM_NAVIGATION_DOWN, + AKEYCODE_SYSTEM_NAVIGATION_RIGHT, AKEYCODE_SYSTEM_NAVIGATION_UP}, +}; + +static const size_t keyCodeRotationMapSize = + sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); + +static int32_t rotateStemKey(int32_t value, int32_t orientation, const int32_t map[][2], + size_t mapSize) { + if (orientation == DISPLAY_ORIENTATION_180) { + for (size_t i = 0; i < mapSize; i++) { + if (value == map[i][0]) { + return map[i][1]; + } + } + } + return value; +} + +// The mapping can be defined using input device configuration properties keyboard.rotated.stem_X +static int32_t stemKeyRotationMap[][2] = { + // key codes enumerated with the original (unrotated) key first + // no rotation, 180 degree rotation + {AKEYCODE_STEM_PRIMARY, AKEYCODE_STEM_PRIMARY}, + {AKEYCODE_STEM_1, AKEYCODE_STEM_1}, + {AKEYCODE_STEM_2, AKEYCODE_STEM_2}, + {AKEYCODE_STEM_3, AKEYCODE_STEM_3}, +}; + +static const size_t stemKeyRotationMapSize = + sizeof(stemKeyRotationMap) / sizeof(stemKeyRotationMap[0]); + +static int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { + keyCode = rotateStemKey(keyCode, orientation, stemKeyRotationMap, stemKeyRotationMapSize); + return rotateValueUsingRotationMap(keyCode, orientation, keyCodeRotationMap, + keyCodeRotationMapSize); +} + +// --- KeyboardInputMapper --- + +KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType) + : InputMapper(device), mSource(source), mKeyboardType(keyboardType) {} + +KeyboardInputMapper::~KeyboardInputMapper() {} + +uint32_t KeyboardInputMapper::getSources() { + return mSource; +} + +int32_t KeyboardInputMapper::getOrientation() { + if (mViewport) { + return mViewport->orientation; + } + return DISPLAY_ORIENTATION_0; +} + +int32_t KeyboardInputMapper::getDisplayId() { + if (mViewport) { + return mViewport->displayId; + } + return ADISPLAY_ID_NONE; +} + +void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setKeyboardType(mKeyboardType); + info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId())); +} + +void KeyboardInputMapper::dump(std::string& dump) { + dump += INDENT2 "Keyboard Input Mapper:\n"; + dumpParameters(dump); + dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); + dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); + dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); + dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); + dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); +} + +void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + if (!changes) { // first time only + // Configure basic parameters. + configureParameters(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + if (mParameters.orientationAware) { + mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + } + } +} + +static void mapStemKey(int32_t keyCode, const PropertyMap& config, char const* property) { + int32_t mapped = 0; + if (config.tryGetProperty(String8(property), mapped) && mapped > 0) { + for (size_t i = 0; i < stemKeyRotationMapSize; i++) { + if (stemKeyRotationMap[i][0] == keyCode) { + stemKeyRotationMap[i][1] = mapped; + return; + } + } + } +} + +void KeyboardInputMapper::configureParameters() { + mParameters.orientationAware = false; + const PropertyMap& config = getDevice()->getConfiguration(); + config.tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); + + if (mParameters.orientationAware) { + mapStemKey(AKEYCODE_STEM_PRIMARY, config, "keyboard.rotated.stem_primary"); + mapStemKey(AKEYCODE_STEM_1, config, "keyboard.rotated.stem_1"); + mapStemKey(AKEYCODE_STEM_2, config, "keyboard.rotated.stem_2"); + mapStemKey(AKEYCODE_STEM_3, config, "keyboard.rotated.stem_3"); + } + + mParameters.handlesKeyRepeat = false; + config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat); +} + +void KeyboardInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); + dump += StringPrintf(INDENT4 "HandlesKeyRepeat: %s\n", toString(mParameters.handlesKeyRepeat)); +} + +void KeyboardInputMapper::reset(nsecs_t when) { + mMetaState = AMETA_NONE; + mDownTime = 0; + mKeyDowns.clear(); + mCurrentHidUsage = 0; + + resetLedState(); + + InputMapper::reset(when); +} + +void KeyboardInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_KEY: { + int32_t scanCode = rawEvent->code; + int32_t usageCode = mCurrentHidUsage; + mCurrentHidUsage = 0; + + if (isKeyboardOrGamepadKey(scanCode)) { + processKey(rawEvent->when, rawEvent->value != 0, scanCode, usageCode); + } + break; + } + case EV_MSC: { + if (rawEvent->code == MSC_SCAN) { + mCurrentHidUsage = rawEvent->value; + } + break; + } + case EV_SYN: { + if (rawEvent->code == SYN_REPORT) { + mCurrentHidUsage = 0; + } + } + } +} + +bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { + return scanCode < BTN_MOUSE || scanCode >= KEY_OK || + (scanCode >= BTN_MISC && scanCode < BTN_MOUSE) || + (scanCode >= BTN_JOYSTICK && scanCode < BTN_DIGI); +} + +bool KeyboardInputMapper::isMediaKey(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: + case AKEYCODE_MEDIA_PLAY_PAUSE: + case AKEYCODE_MUTE: + case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MEDIA_STOP: + case AKEYCODE_MEDIA_NEXT: + case AKEYCODE_MEDIA_PREVIOUS: + case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: + case AKEYCODE_MEDIA_FAST_FORWARD: + case AKEYCODE_MEDIA_SKIP_FORWARD: + case AKEYCODE_MEDIA_SKIP_BACKWARD: + case AKEYCODE_MEDIA_STEP_FORWARD: + case AKEYCODE_MEDIA_STEP_BACKWARD: + case AKEYCODE_MEDIA_AUDIO_TRACK: + case AKEYCODE_VOLUME_UP: + case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: + case AKEYCODE_TV_AUDIO_DESCRIPTION: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: + case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: + return true; + } + return false; +} + +void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode) { + int32_t keyCode; + int32_t keyMetaState; + uint32_t policyFlags; + + if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, mMetaState, &keyCode, + &keyMetaState, &policyFlags)) { + keyCode = AKEYCODE_UNKNOWN; + keyMetaState = mMetaState; + policyFlags = 0; + } + + if (down) { + // Rotate key codes according to orientation if needed. + if (mParameters.orientationAware) { + keyCode = rotateKeyCode(keyCode, getOrientation()); + } + + // Add key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key repeat, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns[keyDownIndex].keyCode; + } else { + // key down + if ((policyFlags & POLICY_FLAG_VIRTUAL) && + mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) { + return; + } + if (policyFlags & POLICY_FLAG_GESTURE) { + mDevice->cancelTouch(when); + } + + KeyDown keyDown; + keyDown.keyCode = keyCode; + keyDown.scanCode = scanCode; + mKeyDowns.push_back(keyDown); + } + + mDownTime = when; + } else { + // Remove key down. + ssize_t keyDownIndex = findKeyDown(scanCode); + if (keyDownIndex >= 0) { + // key up, be sure to use same keycode as before in case of rotation + keyCode = mKeyDowns[keyDownIndex].keyCode; + mKeyDowns.erase(mKeyDowns.begin() + (size_t)keyDownIndex); + } else { + // key was not actually down + ALOGI("Dropping key up from device %s because the key was not down. " + "keyCode=%d, scanCode=%d", + getDeviceName().c_str(), keyCode, scanCode); + return; + } + } + + if (updateMetaStateIfNeeded(keyCode, down)) { + // If global meta state changed send it along with the key. + // If it has not changed then we'll use what keymap gave us, + // since key replacement logic might temporarily reset a few + // meta bits for given key. + keyMetaState = mMetaState; + } + + nsecs_t downTime = mDownTime; + + // Key down on external an keyboard should wake the device. + // We don't do this for internal keyboards to prevent them from waking up in your pocket. + // For internal keyboards, the key layout file should specify the policy flags for + // each wake key individually. + // TODO: Use the input device configuration to control this behavior more finely. + if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) { + policyFlags |= POLICY_FLAG_WAKE; + } + + if (mParameters.handlesKeyRepeat) { + policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; + } + + NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, getDisplayId(), + policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); + getListener()->notifyKey(&args); +} + +ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { + size_t n = mKeyDowns.size(); + for (size_t i = 0; i < n; i++) { + if (mKeyDowns[i].scanCode == scanCode) { + return i; + } + } + return -1; +} + +int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); +} + +int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); +} + +bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); +} + +int32_t KeyboardInputMapper::getMetaState() { + return mMetaState; +} + +void KeyboardInputMapper::updateMetaState(int32_t keyCode) { + updateMetaStateIfNeeded(keyCode, false); +} + +bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) { + int32_t oldMetaState = mMetaState; + int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState); + bool metaStateChanged = oldMetaState != newMetaState; + if (metaStateChanged) { + mMetaState = newMetaState; + updateLedState(false); + + getContext()->updateGlobalMetaState(); + } + + return metaStateChanged; +} + +void KeyboardInputMapper::resetLedState() { + initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK); + initializeLedState(mNumLockLedState, ALED_NUM_LOCK); + initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK); + + updateLedState(true); +} + +void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) { + ledState.avail = getEventHub()->hasLed(getDeviceId(), led); + ledState.on = false; +} + +void KeyboardInputMapper::updateLedState(bool reset) { + updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset); + updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset); + updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset); +} + +void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, int32_t led, + int32_t modifier, bool reset) { + if (ledState.avail) { + bool desiredState = (mMetaState & modifier) != 0; + if (reset || ledState.on != desiredState) { + getEventHub()->setLedState(getDeviceId(), led, desiredState); + ledState.on = desiredState; + } + } +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h new file mode 100644 index 0000000000..7a68fc33f8 --- /dev/null +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H +#define _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class KeyboardInputMapper : public InputMapper { +public: + KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType); + virtual ~KeyboardInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual int32_t getMetaState(); + virtual void updateMetaState(int32_t keyCode); + +private: + // The current viewport. + std::optional<DisplayViewport> mViewport; + + struct KeyDown { + int32_t keyCode; + int32_t scanCode; + }; + + uint32_t mSource; + int32_t mKeyboardType; + + std::vector<KeyDown> mKeyDowns; // keys that are down + int32_t mMetaState; + nsecs_t mDownTime; // time of most recent key down + + int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none + + struct LedState { + bool avail; // led is available + bool on; // we think the led is currently on + }; + LedState mCapsLockLedState; + LedState mNumLockLedState; + LedState mScrollLockLedState; + + // Immutable configuration parameters. + struct Parameters { + bool orientationAware; + bool handlesKeyRepeat; + } mParameters; + + void configureParameters(); + void dumpParameters(std::string& dump); + + int32_t getOrientation(); + int32_t getDisplayId(); + + bool isKeyboardOrGamepadKey(int32_t scanCode); + bool isMediaKey(int32_t keyCode); + + void processKey(nsecs_t when, bool down, int32_t scanCode, int32_t usageCode); + + bool updateMetaStateIfNeeded(int32_t keyCode, bool down); + + ssize_t findKeyDown(int32_t scanCode); + + void resetLedState(); + void initializeLedState(LedState& ledState, int32_t led); + void updateLedState(bool reset); + void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_KEYBOARD_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp new file mode 100644 index 0000000000..7460a3130e --- /dev/null +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "MultiTouchInputMapper.h" + +namespace android { + +// --- Constants --- + +// Maximum number of slots supported when using the slot-based Multitouch Protocol B. +static constexpr size_t MAX_SLOTS = 32; + +// --- MultiTouchMotionAccumulator --- + +MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() + : mCurrentSlot(-1), + mSlots(nullptr), + mSlotCount(0), + mUsingSlotsProtocol(false), + mHaveStylus(false) {} + +MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { + delete[] mSlots; +} + +void MultiTouchMotionAccumulator::configure(InputDevice* device, size_t slotCount, + bool usingSlotsProtocol) { + mSlotCount = slotCount; + mUsingSlotsProtocol = usingSlotsProtocol; + mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE); + + delete[] mSlots; + mSlots = new Slot[slotCount]; +} + +void MultiTouchMotionAccumulator::reset(InputDevice* device) { + // Unfortunately there is no way to read the initial contents of the slots. + // So when we reset the accumulator, we must assume they are all zeroes. + if (mUsingSlotsProtocol) { + // Query the driver for the current slot index and use it as the initial slot + // before we start reading events from the device. It is possible that the + // current slot index will not be the same as it was when the first event was + // written into the evdev buffer, which means the input mapper could start + // out of sync with the initial state of the events in the evdev buffer. + // In the extremely unlikely case that this happens, the data from + // two slots will be confused until the next ABS_MT_SLOT event is received. + // This can cause the touch point to "jump", but at least there will be + // no stuck touches. + int32_t initialSlot; + status_t status = device->getEventHub()->getAbsoluteAxisValue(device->getId(), ABS_MT_SLOT, + &initialSlot); + if (status) { + ALOGD("Could not retrieve current multitouch slot index. status=%d", status); + initialSlot = -1; + } + clearSlots(initialSlot); + } else { + clearSlots(-1); + } + mDeviceTimestamp = 0; +} + +void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { + if (mSlots) { + for (size_t i = 0; i < mSlotCount; i++) { + mSlots[i].clear(); + } + } + mCurrentSlot = initialSlot; +} + +void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + bool newSlot = false; + if (mUsingSlotsProtocol) { + if (rawEvent->code == ABS_MT_SLOT) { + mCurrentSlot = rawEvent->value; + newSlot = true; + } + } else if (mCurrentSlot < 0) { + mCurrentSlot = 0; + } + + if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlotCount) { +#if DEBUG_POINTERS + if (newSlot) { + ALOGW("MultiTouch device emitted invalid slot index %d but it " + "should be between 0 and %zd; ignoring this slot.", + mCurrentSlot, mSlotCount - 1); + } +#endif + } else { + Slot* slot = &mSlots[mCurrentSlot]; + + switch (rawEvent->code) { + case ABS_MT_POSITION_X: + slot->mInUse = true; + slot->mAbsMTPositionX = rawEvent->value; + break; + case ABS_MT_POSITION_Y: + slot->mInUse = true; + slot->mAbsMTPositionY = rawEvent->value; + break; + case ABS_MT_TOUCH_MAJOR: + slot->mInUse = true; + slot->mAbsMTTouchMajor = rawEvent->value; + break; + case ABS_MT_TOUCH_MINOR: + slot->mInUse = true; + slot->mAbsMTTouchMinor = rawEvent->value; + slot->mHaveAbsMTTouchMinor = true; + break; + case ABS_MT_WIDTH_MAJOR: + slot->mInUse = true; + slot->mAbsMTWidthMajor = rawEvent->value; + break; + case ABS_MT_WIDTH_MINOR: + slot->mInUse = true; + slot->mAbsMTWidthMinor = rawEvent->value; + slot->mHaveAbsMTWidthMinor = true; + break; + case ABS_MT_ORIENTATION: + slot->mInUse = true; + slot->mAbsMTOrientation = rawEvent->value; + break; + case ABS_MT_TRACKING_ID: + if (mUsingSlotsProtocol && rawEvent->value < 0) { + // The slot is no longer in use but it retains its previous contents, + // which may be reused for subsequent touches. + slot->mInUse = false; + } else { + slot->mInUse = true; + slot->mAbsMTTrackingId = rawEvent->value; + } + break; + case ABS_MT_PRESSURE: + slot->mInUse = true; + slot->mAbsMTPressure = rawEvent->value; + break; + case ABS_MT_DISTANCE: + slot->mInUse = true; + slot->mAbsMTDistance = rawEvent->value; + break; + case ABS_MT_TOOL_TYPE: + slot->mInUse = true; + slot->mAbsMTToolType = rawEvent->value; + slot->mHaveAbsMTToolType = true; + break; + } + } + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { + // MultiTouch Sync: The driver has returned all data for *one* of the pointers. + mCurrentSlot += 1; + } else if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) { + mDeviceTimestamp = rawEvent->value; + } +} + +void MultiTouchMotionAccumulator::finishSync() { + if (!mUsingSlotsProtocol) { + clearSlots(-1); + } +} + +bool MultiTouchMotionAccumulator::hasStylus() const { + return mHaveStylus; +} + +// --- MultiTouchMotionAccumulator::Slot --- + +MultiTouchMotionAccumulator::Slot::Slot() { + clear(); +} + +void MultiTouchMotionAccumulator::Slot::clear() { + mInUse = false; + mHaveAbsMTTouchMinor = false; + mHaveAbsMTWidthMinor = false; + mHaveAbsMTToolType = false; + mAbsMTPositionX = 0; + mAbsMTPositionY = 0; + mAbsMTTouchMajor = 0; + mAbsMTTouchMinor = 0; + mAbsMTWidthMajor = 0; + mAbsMTWidthMinor = 0; + mAbsMTOrientation = 0; + mAbsMTTrackingId = -1; + mAbsMTPressure = 0; + mAbsMTDistance = 0; + mAbsMTToolType = 0; +} + +int32_t MultiTouchMotionAccumulator::Slot::getToolType() const { + if (mHaveAbsMTToolType) { + switch (mAbsMTToolType) { + case MT_TOOL_FINGER: + return AMOTION_EVENT_TOOL_TYPE_FINGER; + case MT_TOOL_PEN: + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +// --- MultiTouchInputMapper --- + +MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} + +MultiTouchInputMapper::~MultiTouchInputMapper() {} + +void MultiTouchInputMapper::reset(nsecs_t when) { + mMultiTouchMotionAccumulator.reset(getDevice()); + + mPointerIdBits.clear(); + + TouchInputMapper::reset(when); +} + +void MultiTouchInputMapper::process(const RawEvent* rawEvent) { + TouchInputMapper::process(rawEvent); + + mMultiTouchMotionAccumulator.process(rawEvent); +} + +void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { + size_t inCount = mMultiTouchMotionAccumulator.getSlotCount(); + size_t outCount = 0; + BitSet32 newPointerIdBits; + mHavePointerIds = true; + + for (size_t inIndex = 0; inIndex < inCount; inIndex++) { + const MultiTouchMotionAccumulator::Slot* inSlot = + mMultiTouchMotionAccumulator.getSlot(inIndex); + if (!inSlot->isInUse()) { + continue; + } + + if (outCount >= MAX_POINTERS) { +#if DEBUG_POINTERS + ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " + "ignoring the rest.", + getDeviceName().c_str(), MAX_POINTERS); +#endif + break; // too many fingers! + } + + RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount]; + outPointer.x = inSlot->getX(); + outPointer.y = inSlot->getY(); + outPointer.pressure = inSlot->getPressure(); + outPointer.touchMajor = inSlot->getTouchMajor(); + outPointer.touchMinor = inSlot->getTouchMinor(); + outPointer.toolMajor = inSlot->getToolMajor(); + outPointer.toolMinor = inSlot->getToolMinor(); + outPointer.orientation = inSlot->getOrientation(); + outPointer.distance = inSlot->getDistance(); + outPointer.tiltX = 0; + outPointer.tiltY = 0; + + outPointer.toolType = inSlot->getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = mTouchButtonAccumulator.getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + } + } + + bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && + (mTouchButtonAccumulator.isHovering() || + (mRawPointerAxes.pressure.valid && inSlot->getPressure() <= 0)); + outPointer.isHovering = isHovering; + + // Assign pointer id using tracking id if available. + if (mHavePointerIds) { + int32_t trackingId = inSlot->getTrackingId(); + int32_t id = -1; + if (trackingId >= 0) { + for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) { + uint32_t n = idBits.clearFirstMarkedBit(); + if (mPointerTrackingIdMap[n] == trackingId) { + id = n; + } + } + + if (id < 0 && !mPointerIdBits.isFull()) { + id = mPointerIdBits.markFirstUnmarkedBit(); + mPointerTrackingIdMap[id] = trackingId; + } + } + if (id < 0) { + mHavePointerIds = false; + outState->rawPointerData.clearIdBits(); + newPointerIdBits.clear(); + } else { + outPointer.id = id; + outState->rawPointerData.idToIndex[id] = outCount; + outState->rawPointerData.markIdBit(id, isHovering); + newPointerIdBits.markBit(id); + } + } + outCount += 1; + } + + outState->deviceTimestamp = mMultiTouchMotionAccumulator.getDeviceTimestamp(); + outState->rawPointerData.pointerCount = outCount; + mPointerIdBits = newPointerIdBits; + + mMultiTouchMotionAccumulator.finishSync(); +} + +void MultiTouchInputMapper::configureRawPointerAxes() { + TouchInputMapper::configureRawPointerAxes(); + + getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x); + getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y); + getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor); + getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor); + getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor); + getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor); + getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation); + getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure); + getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance); + getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId); + getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot); + + if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid && + mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) { + size_t slotCount = mRawPointerAxes.slot.maxValue + 1; + if (slotCount > MAX_SLOTS) { + ALOGW("MultiTouch Device %s reported %zu slots but the framework " + "only supports a maximum of %zu slots at this time.", + getDeviceName().c_str(), slotCount, MAX_SLOTS); + slotCount = MAX_SLOTS; + } + mMultiTouchMotionAccumulator.configure(getDevice(), slotCount, true /*usingSlotsProtocol*/); + } else { + mMultiTouchMotionAccumulator.configure(getDevice(), MAX_POINTERS, + false /*usingSlotsProtocol*/); + } +} + +bool MultiTouchInputMapper::hasStylus() const { + return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus(); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h new file mode 100644 index 0000000000..873dda1aec --- /dev/null +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H + +#include "TouchInputMapper.h" + +namespace android { + +/* Keeps track of the state of multi-touch protocol. */ +class MultiTouchMotionAccumulator { +public: + class Slot { + public: + inline bool isInUse() const { return mInUse; } + inline int32_t getX() const { return mAbsMTPositionX; } + inline int32_t getY() const { return mAbsMTPositionY; } + inline int32_t getTouchMajor() const { return mAbsMTTouchMajor; } + inline int32_t getTouchMinor() const { + return mHaveAbsMTTouchMinor ? mAbsMTTouchMinor : mAbsMTTouchMajor; + } + inline int32_t getToolMajor() const { return mAbsMTWidthMajor; } + inline int32_t getToolMinor() const { + return mHaveAbsMTWidthMinor ? mAbsMTWidthMinor : mAbsMTWidthMajor; + } + inline int32_t getOrientation() const { return mAbsMTOrientation; } + inline int32_t getTrackingId() const { return mAbsMTTrackingId; } + inline int32_t getPressure() const { return mAbsMTPressure; } + inline int32_t getDistance() const { return mAbsMTDistance; } + inline int32_t getToolType() const; + + private: + friend class MultiTouchMotionAccumulator; + + bool mInUse; + bool mHaveAbsMTTouchMinor; + bool mHaveAbsMTWidthMinor; + bool mHaveAbsMTToolType; + + int32_t mAbsMTPositionX; + int32_t mAbsMTPositionY; + int32_t mAbsMTTouchMajor; + int32_t mAbsMTTouchMinor; + int32_t mAbsMTWidthMajor; + int32_t mAbsMTWidthMinor; + int32_t mAbsMTOrientation; + int32_t mAbsMTTrackingId; + int32_t mAbsMTPressure; + int32_t mAbsMTDistance; + int32_t mAbsMTToolType; + + Slot(); + void clear(); + }; + + MultiTouchMotionAccumulator(); + ~MultiTouchMotionAccumulator(); + + void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol); + void reset(InputDevice* device); + void process(const RawEvent* rawEvent); + void finishSync(); + bool hasStylus() const; + + inline size_t getSlotCount() const { return mSlotCount; } + inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } + inline uint32_t getDeviceTimestamp() const { return mDeviceTimestamp; } + +private: + int32_t mCurrentSlot; + Slot* mSlots; + size_t mSlotCount; + bool mUsingSlotsProtocol; + bool mHaveStylus; + uint32_t mDeviceTimestamp; + + void clearSlots(int32_t initialSlot); +}; + +class MultiTouchInputMapper : public TouchInputMapper { +public: + explicit MultiTouchInputMapper(InputDevice* device); + virtual ~MultiTouchInputMapper(); + + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void syncTouch(nsecs_t when, RawState* outState); + virtual void configureRawPointerAxes(); + virtual bool hasStylus() const; + +private: + MultiTouchMotionAccumulator mMultiTouchMotionAccumulator; + + // Specifies the pointer id bits that are in use, and their associated tracking id. + BitSet32 mPointerIdBits; + int32_t mPointerTrackingIdMap[MAX_POINTER_ID + 1]; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_MULTI_TOUCH_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp new file mode 100644 index 0000000000..803fdf3656 --- /dev/null +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "RotaryEncoderInputMapper.h" + +#include "CursorScrollAccumulator.h" + +namespace android { + +RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDevice* device) + : InputMapper(device), mOrientation(DISPLAY_ORIENTATION_0) { + mSource = AINPUT_SOURCE_ROTARY_ENCODER; +} + +RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {} + +uint32_t RotaryEncoderInputMapper::getSources() { + return mSource; +} + +void RotaryEncoderInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mRotaryEncoderScrollAccumulator.haveRelativeVWheel()) { + float res = 0.0f; + if (!mDevice->getConfiguration().tryGetProperty(String8("device.res"), res)) { + ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n"); + } + if (!mDevice->getConfiguration().tryGetProperty(String8("device.scalingFactor"), + mScalingFactor)) { + ALOGW("Rotary Encoder device configuration file didn't specify scaling factor," + "default to 1.0!\n"); + mScalingFactor = 1.0f; + } + info->addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + res * mScalingFactor); + } +} + +void RotaryEncoderInputMapper::dump(std::string& dump) { + dump += INDENT2 "Rotary Encoder Input Mapper:\n"; + dump += StringPrintf(INDENT3 "HaveWheel: %s\n", + toString(mRotaryEncoderScrollAccumulator.haveRelativeVWheel())); +} + +void RotaryEncoderInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + if (!changes) { + mRotaryEncoderScrollAccumulator.configure(getDevice()); + } + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + std::optional<DisplayViewport> internalViewport = + config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + if (internalViewport) { + mOrientation = internalViewport->orientation; + } else { + mOrientation = DISPLAY_ORIENTATION_0; + } + } +} + +void RotaryEncoderInputMapper::reset(nsecs_t when) { + mRotaryEncoderScrollAccumulator.reset(getDevice()); + + InputMapper::reset(when); +} + +void RotaryEncoderInputMapper::process(const RawEvent* rawEvent) { + mRotaryEncoderScrollAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } +} + +void RotaryEncoderInputMapper::sync(nsecs_t when) { + PointerCoords pointerCoords; + pointerCoords.clear(); + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_UNKNOWN; + + float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel(); + bool scrolled = scroll != 0; + + // This is not a pointer, so it's not associated with a display. + int32_t displayId = ADISPLAY_ID_NONE; + + // Moving the rotary encoder should wake the device (if specified). + uint32_t policyFlags = 0; + if (scrolled && getDevice()->isExternal()) { + policyFlags |= POLICY_FLAG_WAKE; + } + + if (mOrientation == DISPLAY_ORIENTATION_180) { + scroll = -scroll; + } + + // Send motion event. + if (scrolled) { + int32_t metaState = mContext->getGlobalMetaState(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); + + NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, + metaState, /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, + 0, 0, 0, /* videoFrames */ {}); + getListener()->notifyMotion(&scrollArgs); + } + + mRotaryEncoderScrollAccumulator.finishSync(); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h new file mode 100644 index 0000000000..26488373bd --- /dev/null +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H +#define _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H + +#include "CursorScrollAccumulator.h" +#include "InputMapper.h" + +namespace android { + +class RotaryEncoderInputMapper : public InputMapper { +public: + explicit RotaryEncoderInputMapper(InputDevice* device); + virtual ~RotaryEncoderInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +private: + CursorScrollAccumulator mRotaryEncoderScrollAccumulator; + + int32_t mSource; + float mScalingFactor; + int32_t mOrientation; + + void sync(nsecs_t when); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_ROTARY_ENCODER_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp new file mode 100644 index 0000000000..440d282686 --- /dev/null +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SingleTouchInputMapper.h" + +namespace android { + +SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) : TouchInputMapper(device) {} + +SingleTouchInputMapper::~SingleTouchInputMapper() {} + +void SingleTouchInputMapper::reset(nsecs_t when) { + mSingleTouchMotionAccumulator.reset(getDevice()); + + TouchInputMapper::reset(when); +} + +void SingleTouchInputMapper::process(const RawEvent* rawEvent) { + TouchInputMapper::process(rawEvent); + + mSingleTouchMotionAccumulator.process(rawEvent); +} + +void SingleTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { + if (mTouchButtonAccumulator.isToolActive()) { + outState->rawPointerData.pointerCount = 1; + outState->rawPointerData.idToIndex[0] = 0; + + bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE && + (mTouchButtonAccumulator.isHovering() || + (mRawPointerAxes.pressure.valid && + mSingleTouchMotionAccumulator.getAbsolutePressure() <= 0)); + outState->rawPointerData.markIdBit(0, isHovering); + + RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[0]; + outPointer.id = 0; + outPointer.x = mSingleTouchMotionAccumulator.getAbsoluteX(); + outPointer.y = mSingleTouchMotionAccumulator.getAbsoluteY(); + outPointer.pressure = mSingleTouchMotionAccumulator.getAbsolutePressure(); + outPointer.touchMajor = 0; + outPointer.touchMinor = 0; + outPointer.toolMajor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); + outPointer.toolMinor = mSingleTouchMotionAccumulator.getAbsoluteToolWidth(); + outPointer.orientation = 0; + outPointer.distance = mSingleTouchMotionAccumulator.getAbsoluteDistance(); + outPointer.tiltX = mSingleTouchMotionAccumulator.getAbsoluteTiltX(); + outPointer.tiltY = mSingleTouchMotionAccumulator.getAbsoluteTiltY(); + outPointer.toolType = mTouchButtonAccumulator.getToolType(); + if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + } + outPointer.isHovering = isHovering; + } +} + +void SingleTouchInputMapper::configureRawPointerAxes() { + TouchInputMapper::configureRawPointerAxes(); + + getAbsoluteAxisInfo(ABS_X, &mRawPointerAxes.x); + getAbsoluteAxisInfo(ABS_Y, &mRawPointerAxes.y); + getAbsoluteAxisInfo(ABS_PRESSURE, &mRawPointerAxes.pressure); + getAbsoluteAxisInfo(ABS_TOOL_WIDTH, &mRawPointerAxes.toolMajor); + getAbsoluteAxisInfo(ABS_DISTANCE, &mRawPointerAxes.distance); + getAbsoluteAxisInfo(ABS_TILT_X, &mRawPointerAxes.tiltX); + getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY); +} + +bool SingleTouchInputMapper::hasStylus() const { + return mTouchButtonAccumulator.hasStylus(); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h new file mode 100644 index 0000000000..d6b1455b68 --- /dev/null +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H + +#include "SingleTouchMotionAccumulator.h" +#include "TouchInputMapper.h" + +namespace android { + +class SingleTouchInputMapper : public TouchInputMapper { +public: + explicit SingleTouchInputMapper(InputDevice* device); + virtual ~SingleTouchInputMapper(); + + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + +protected: + virtual void syncTouch(nsecs_t when, RawState* outState); + virtual void configureRawPointerAxes(); + virtual bool hasStylus() const; + +private: + SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; +}; + +} // namespace android + +#endif // _UI_INPUTREADER_SINGLE_TOUCH_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp new file mode 100644 index 0000000000..4ff941f5cf --- /dev/null +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "SwitchInputMapper.h" + +namespace android { + +SwitchInputMapper::SwitchInputMapper(InputDevice* device) + : InputMapper(device), mSwitchValues(0), mUpdatedSwitchMask(0) {} + +SwitchInputMapper::~SwitchInputMapper() {} + +uint32_t SwitchInputMapper::getSources() { + return AINPUT_SOURCE_SWITCH; +} + +void SwitchInputMapper::process(const RawEvent* rawEvent) { + switch (rawEvent->type) { + case EV_SW: + processSwitch(rawEvent->code, rawEvent->value); + break; + + case EV_SYN: + if (rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } + } +} + +void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { + if (switchCode >= 0 && switchCode < 32) { + if (switchValue) { + mSwitchValues |= 1 << switchCode; + } else { + mSwitchValues &= ~(1 << switchCode); + } + mUpdatedSwitchMask |= 1 << switchCode; + } +} + +void SwitchInputMapper::sync(nsecs_t when) { + if (mUpdatedSwitchMask) { + uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; + NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues, + mUpdatedSwitchMask); + getListener()->notifySwitch(&args); + + mUpdatedSwitchMask = 0; + } +} + +int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { + return getEventHub()->getSwitchState(getDeviceId(), switchCode); +} + +void SwitchInputMapper::dump(std::string& dump) { + dump += INDENT2 "Switch Input Mapper:\n"; + dump += StringPrintf(INDENT3 "SwitchValues: %x\n", mSwitchValues); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h new file mode 100644 index 0000000000..dd4bb9ed65 --- /dev/null +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class SwitchInputMapper : public InputMapper { +public: + explicit SwitchInputMapper(InputDevice* device); + virtual ~SwitchInputMapper(); + + virtual uint32_t getSources(); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); + virtual void dump(std::string& dump); + +private: + uint32_t mSwitchValues; + uint32_t mUpdatedSwitchMask; + + void processSwitch(int32_t switchCode, int32_t switchValue); + void sync(nsecs_t when); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_SWITCH_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h new file mode 100644 index 0000000000..efa3d6d2b2 --- /dev/null +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H +#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H + +#include "EventHub.h" +#include "InputListener.h" +#include "InputReaderContext.h" + +#include <stdint.h> + +namespace android { + +// --- Static Definitions --- + +static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { + float temp; + switch (orientation) { + case DISPLAY_ORIENTATION_90: + temp = *deltaX; + *deltaX = *deltaY; + *deltaY = -temp; + break; + + case DISPLAY_ORIENTATION_180: + *deltaX = -*deltaX; + *deltaY = -*deltaY; + break; + + case DISPLAY_ORIENTATION_270: + temp = *deltaX; + *deltaX = -*deltaY; + *deltaY = temp; + break; + } +} + +// Returns true if the pointer should be reported as being down given the specified +// button states. This determines whether the event is reported as a touch event. +static bool isPointerDown(int32_t buttonState) { + return buttonState & + (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY | + AMOTION_EVENT_BUTTON_TERTIARY); +} + +static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nsecs_t when, + int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t lastButtonState, + int32_t currentButtonState, int32_t buttonState, int32_t keyCode) { + if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) && + (currentButtonState & buttonState)) || + (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && + !(currentButtonState & buttonState))) { + NotifyKeyArgs args(context->getNextSequenceNum(), when, deviceId, source, displayId, + policyFlags, action, 0, keyCode, 0, context->getGlobalMetaState(), when); + context->getListener()->notifyKey(&args); + } +} + +static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, nsecs_t when, + int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t lastButtonState, + int32_t currentButtonState) { + synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, + lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_BACK, + AKEYCODE_BACK); + synthesizeButtonKey(context, action, when, deviceId, source, displayId, policyFlags, + lastButtonState, currentButtonState, AMOTION_EVENT_BUTTON_FORWARD, + AKEYCODE_FORWARD); +} + +} // namespace android + +#endif // _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp new file mode 100644 index 0000000000..32ed97bffe --- /dev/null +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -0,0 +1,3909 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "TouchInputMapper.h" + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "TouchButtonAccumulator.h" +#include "TouchCursorInputMapperCommon.h" + +#include <statslog.h> + +// How often to report input event statistics +static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60); + +namespace android { + +// --- Constants --- + +// Maximum amount of latency to add to touch events while waiting for data from an +// external stylus. +static constexpr nsecs_t EXTERNAL_STYLUS_DATA_TIMEOUT = ms2ns(72); + +// Maximum amount of time to wait on touch data before pushing out new pressure data. +static constexpr nsecs_t TOUCH_DATA_TIMEOUT = ms2ns(20); + +// Artificial latency on synthetic events created from stylus data without corresponding touch +// data. +static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); + +// --- Static Definitions --- + +template <typename T> +inline static void swap(T& a, T& b) { + T temp = a; + a = b; + b = temp; +} + +static float calculateCommonVector(float a, float b) { + if (a > 0 && b > 0) { + return a < b ? a : b; + } else if (a < 0 && b < 0) { + return a > b ? a : b; + } else { + return 0; + } +} + +inline static float distance(float x1, float y1, float x2, float y2) { + return hypotf(x1 - x2, y1 - y2); +} + +inline static int32_t signExtendNybble(int32_t value) { + return value >= 8 ? value - 16 : value; +} + +// --- RawPointerAxes --- + +RawPointerAxes::RawPointerAxes() { + clear(); +} + +void RawPointerAxes::clear() { + x.clear(); + y.clear(); + pressure.clear(); + touchMajor.clear(); + touchMinor.clear(); + toolMajor.clear(); + toolMinor.clear(); + orientation.clear(); + distance.clear(); + tiltX.clear(); + tiltY.clear(); + trackingId.clear(); + slot.clear(); +} + +// --- RawPointerData --- + +RawPointerData::RawPointerData() { + clear(); +} + +void RawPointerData::clear() { + pointerCount = 0; + clearIdBits(); +} + +void RawPointerData::copyFrom(const RawPointerData& other) { + pointerCount = other.pointerCount; + hoveringIdBits = other.hoveringIdBits; + touchingIdBits = other.touchingIdBits; + + for (uint32_t i = 0; i < pointerCount; i++) { + pointers[i] = other.pointers[i]; + + int id = pointers[i].id; + idToIndex[id] = other.idToIndex[id]; + } +} + +void RawPointerData::getCentroidOfTouchingPointers(float* outX, float* outY) const { + float x = 0, y = 0; + uint32_t count = touchingIdBits.count(); + if (count) { + for (BitSet32 idBits(touchingIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const Pointer& pointer = pointerForId(id); + x += pointer.x; + y += pointer.y; + } + x /= count; + y /= count; + } + *outX = x; + *outY = y; +} + +// --- CookedPointerData --- + +CookedPointerData::CookedPointerData() { + clear(); +} + +void CookedPointerData::clear() { + pointerCount = 0; + hoveringIdBits.clear(); + touchingIdBits.clear(); +} + +void CookedPointerData::copyFrom(const CookedPointerData& other) { + pointerCount = other.pointerCount; + hoveringIdBits = other.hoveringIdBits; + touchingIdBits = other.touchingIdBits; + + for (uint32_t i = 0; i < pointerCount; i++) { + pointerProperties[i].copyFrom(other.pointerProperties[i]); + pointerCoords[i].copyFrom(other.pointerCoords[i]); + + int id = pointerProperties[i].id; + idToIndex[id] = other.idToIndex[id]; + } +} + +// --- TouchInputMapper --- + +TouchInputMapper::TouchInputMapper(InputDevice* device) + : InputMapper(device), + mSource(0), + mDeviceMode(DEVICE_MODE_DISABLED), + mSurfaceWidth(-1), + mSurfaceHeight(-1), + mSurfaceLeft(0), + mSurfaceTop(0), + mPhysicalWidth(-1), + mPhysicalHeight(-1), + mPhysicalLeft(0), + mPhysicalTop(0), + mSurfaceOrientation(DISPLAY_ORIENTATION_0) {} + +TouchInputMapper::~TouchInputMapper() {} + +uint32_t TouchInputMapper::getSources() { + return mSource; +} + +void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + if (mDeviceMode != DEVICE_MODE_DISABLED) { + info->addMotionRange(mOrientedRanges.x); + info->addMotionRange(mOrientedRanges.y); + info->addMotionRange(mOrientedRanges.pressure); + + if (mOrientedRanges.haveSize) { + info->addMotionRange(mOrientedRanges.size); + } + + if (mOrientedRanges.haveTouchSize) { + info->addMotionRange(mOrientedRanges.touchMajor); + info->addMotionRange(mOrientedRanges.touchMinor); + } + + if (mOrientedRanges.haveToolSize) { + info->addMotionRange(mOrientedRanges.toolMajor); + info->addMotionRange(mOrientedRanges.toolMinor); + } + + if (mOrientedRanges.haveOrientation) { + info->addMotionRange(mOrientedRanges.orientation); + } + + if (mOrientedRanges.haveDistance) { + info->addMotionRange(mOrientedRanges.distance); + } + + if (mOrientedRanges.haveTilt) { + info->addMotionRange(mOrientedRanges.tilt); + } + + if (mCursorScrollAccumulator.haveRelativeVWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f); + } + if (mCursorScrollAccumulator.haveRelativeHWheel()) { + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f); + } + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { + const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; + const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, + x.fuzz, x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, + y.fuzz, y.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, + x.fuzz, x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, + y.fuzz, y.resolution); + } + info->setButtonUnderPad(mParameters.hasButtonUnderPad); + } +} + +void TouchInputMapper::dump(std::string& dump) { + dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", modeToString(mDeviceMode)); + dumpParameters(dump); + dumpVirtualKeys(dump); + dumpRawPointerAxes(dump); + dumpCalibration(dump); + dumpAffineTransformation(dump); + dumpSurface(dump); + + dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n"); + dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate); + dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate); + dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale); + dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale); + dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision); + dump += StringPrintf(INDENT4 "YPrecision: %0.3f\n", mYPrecision); + dump += StringPrintf(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale); + dump += StringPrintf(INDENT4 "PressureScale: %0.3f\n", mPressureScale); + dump += StringPrintf(INDENT4 "SizeScale: %0.3f\n", mSizeScale); + dump += StringPrintf(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale); + dump += StringPrintf(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale); + dump += StringPrintf(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt)); + dump += StringPrintf(INDENT4 "TiltXCenter: %0.3f\n", mTiltXCenter); + dump += StringPrintf(INDENT4 "TiltXScale: %0.3f\n", mTiltXScale); + dump += StringPrintf(INDENT4 "TiltYCenter: %0.3f\n", mTiltYCenter); + dump += StringPrintf(INDENT4 "TiltYScale: %0.3f\n", mTiltYScale); + + dump += StringPrintf(INDENT3 "Last Raw Button State: 0x%08x\n", mLastRawState.buttonState); + dump += StringPrintf(INDENT3 "Last Raw Touch: pointerCount=%d\n", + mLastRawState.rawPointerData.pointerCount); + for (uint32_t i = 0; i < mLastRawState.rawPointerData.pointerCount; i++) { + const RawPointerData::Pointer& pointer = mLastRawState.rawPointerData.pointers[i]; + dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%d, y=%d, pressure=%d, " + "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, " + "orientation=%d, tiltX=%d, tiltY=%d, distance=%d, " + "toolType=%d, isHovering=%s\n", + i, pointer.id, pointer.x, pointer.y, pointer.pressure, + pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, + pointer.toolMinor, pointer.orientation, pointer.tiltX, pointer.tiltY, + pointer.distance, pointer.toolType, toString(pointer.isHovering)); + } + + dump += StringPrintf(INDENT3 "Last Cooked Button State: 0x%08x\n", + mLastCookedState.buttonState); + dump += StringPrintf(INDENT3 "Last Cooked Touch: pointerCount=%d\n", + mLastCookedState.cookedPointerData.pointerCount); + for (uint32_t i = 0; i < mLastCookedState.cookedPointerData.pointerCount; i++) { + const PointerProperties& pointerProperties = + mLastCookedState.cookedPointerData.pointerProperties[i]; + const PointerCoords& pointerCoords = mLastCookedState.cookedPointerData.pointerCoords[i]; + dump += StringPrintf(INDENT4 "[%d]: id=%d, x=%0.3f, y=%0.3f, pressure=%0.3f, " + "touchMajor=%0.3f, touchMinor=%0.3f, toolMajor=%0.3f, " + "toolMinor=%0.3f, " + "orientation=%0.3f, tilt=%0.3f, distance=%0.3f, " + "toolType=%d, isHovering=%s\n", + i, pointerProperties.id, pointerCoords.getX(), pointerCoords.getY(), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_TILT), + pointerCoords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), + pointerProperties.toolType, + toString(mLastCookedState.cookedPointerData.isHovering(i))); + } + + dump += INDENT3 "Stylus Fusion:\n"; + dump += StringPrintf(INDENT4 "ExternalStylusConnected: %s\n", + toString(mExternalStylusConnected)); + dump += StringPrintf(INDENT4 "External Stylus ID: %" PRId64 "\n", mExternalStylusId); + dump += StringPrintf(INDENT4 "External Stylus Data Timeout: %" PRId64 "\n", + mExternalStylusFusionTimeout); + dump += INDENT3 "External Stylus State:\n"; + dumpStylusState(dump, mExternalStylusState); + + if (mDeviceMode == DEVICE_MODE_POINTER) { + dump += StringPrintf(INDENT3 "Pointer Gesture Detector:\n"); + dump += StringPrintf(INDENT4 "XMovementScale: %0.3f\n", mPointerXMovementScale); + dump += StringPrintf(INDENT4 "YMovementScale: %0.3f\n", mPointerYMovementScale); + dump += StringPrintf(INDENT4 "XZoomScale: %0.3f\n", mPointerXZoomScale); + dump += StringPrintf(INDENT4 "YZoomScale: %0.3f\n", mPointerYZoomScale); + dump += StringPrintf(INDENT4 "MaxSwipeWidth: %f\n", mPointerGestureMaxSwipeWidth); + } +} + +const char* TouchInputMapper::modeToString(DeviceMode deviceMode) { + switch (deviceMode) { + case DEVICE_MODE_DISABLED: + return "disabled"; + case DEVICE_MODE_DIRECT: + return "direct"; + case DEVICE_MODE_UNSCALED: + return "unscaled"; + case DEVICE_MODE_NAVIGATION: + return "navigation"; + case DEVICE_MODE_POINTER: + return "pointer"; + } + return "unknown"; +} + +void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) { + InputMapper::configure(when, config, changes); + + mConfig = *config; + + if (!changes) { // first time only + // Configure basic parameters. + configureParameters(); + + // Configure common accumulators. + mCursorScrollAccumulator.configure(getDevice()); + mTouchButtonAccumulator.configure(getDevice()); + + // Configure absolute axis information. + configureRawPointerAxes(); + + // Prepare input device calibration. + parseCalibration(); + resolveCalibration(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION)) { + // Update location calibration to reflect current settings + updateAffineTransformation(); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { + // Update pointer speed. + mPointerVelocityControl.setParameters(mConfig.pointerVelocityControlParameters); + mWheelXVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); + mWheelYVelocityControl.setParameters(mConfig.wheelVelocityControlParameters); + } + + bool resetNeeded = false; + if (!changes || + (changes & + (InputReaderConfiguration::CHANGE_DISPLAY_INFO | + InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT | + InputReaderConfiguration::CHANGE_SHOW_TOUCHES | + InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) { + // Configure device sources, surface dimensions, orientation and + // scaling factors. + configureSurface(when, &resetNeeded); + } + + if (changes && resetNeeded) { + // Send reset, unless this is the first time the device has been configured, + // in which case the reader will call reset itself after all mappers are ready. + getDevice()->notifyReset(when); + } +} + +void TouchInputMapper::resolveExternalStylusPresence() { + std::vector<InputDeviceInfo> devices; + mContext->getExternalStylusDevices(devices); + mExternalStylusConnected = !devices.empty(); + + if (!mExternalStylusConnected) { + resetExternalStylus(); + } +} + +void TouchInputMapper::configureParameters() { + // Use the pointer presentation mode for devices that do not support distinct + // multitouch. The spot-based presentation relies on being able to accurately + // locate two or more fingers on the touch pad. + mParameters.gestureMode = getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_SEMI_MT) + ? Parameters::GESTURE_MODE_SINGLE_TOUCH + : Parameters::GESTURE_MODE_MULTI_TOUCH; + + String8 gestureModeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("touch.gestureMode"), + gestureModeString)) { + if (gestureModeString == "single-touch") { + mParameters.gestureMode = Parameters::GESTURE_MODE_SINGLE_TOUCH; + } else if (gestureModeString == "multi-touch") { + mParameters.gestureMode = Parameters::GESTURE_MODE_MULTI_TOUCH; + } else if (gestureModeString != "default") { + ALOGW("Invalid value for touch.gestureMode: '%s'", gestureModeString.string()); + } + } + + if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) { + // The device is a touch screen. + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; + } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) { + // The device is a pointing device like a track pad. + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } else if (getEventHub()->hasRelativeAxis(getDeviceId(), REL_X) || + getEventHub()->hasRelativeAxis(getDeviceId(), REL_Y)) { + // The device is a cursor device with a touch pad attached. + // By default don't use the touch pad to move the pointer. + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else { + // The device is a touch pad of unknown purpose. + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } + + mParameters.hasButtonUnderPad = + getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD); + + String8 deviceTypeString; + if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"), + deviceTypeString)) { + if (deviceTypeString == "touchScreen") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; + } else if (deviceTypeString == "touchPad") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else if (deviceTypeString == "touchNavigation") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION; + } else if (deviceTypeString == "pointer") { + mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; + } else if (deviceTypeString != "default") { + ALOGW("Invalid value for touch.deviceType: '%s'", deviceTypeString.string()); + } + } + + mParameters.orientationAware = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN; + getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), + mParameters.orientationAware); + + mParameters.hasAssociatedDisplay = false; + mParameters.associatedDisplayIsExternal = false; + if (mParameters.orientationAware || + mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN || + mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + mParameters.hasAssociatedDisplay = true; + if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN) { + mParameters.associatedDisplayIsExternal = getDevice()->isExternal(); + String8 uniqueDisplayId; + getDevice()->getConfiguration().tryGetProperty(String8("touch.displayId"), + uniqueDisplayId); + mParameters.uniqueDisplayId = uniqueDisplayId.c_str(); + } + } + if (getDevice()->getAssociatedDisplayPort()) { + mParameters.hasAssociatedDisplay = true; + } + + // Initial downs on external touch devices should wake the device. + // Normally we don't do this for internal touch screens to prevent them from waking + // up in your pocket but you can enable it using the input device configuration. + mParameters.wake = getDevice()->isExternal(); + getDevice()->getConfiguration().tryGetProperty(String8("touch.wake"), mParameters.wake); +} + +void TouchInputMapper::dumpParameters(std::string& dump) { + dump += INDENT3 "Parameters:\n"; + + switch (mParameters.gestureMode) { + case Parameters::GESTURE_MODE_SINGLE_TOUCH: + dump += INDENT4 "GestureMode: single-touch\n"; + break; + case Parameters::GESTURE_MODE_MULTI_TOUCH: + dump += INDENT4 "GestureMode: multi-touch\n"; + break; + default: + assert(false); + } + + switch (mParameters.deviceType) { + case Parameters::DEVICE_TYPE_TOUCH_SCREEN: + dump += INDENT4 "DeviceType: touchScreen\n"; + break; + case Parameters::DEVICE_TYPE_TOUCH_PAD: + dump += INDENT4 "DeviceType: touchPad\n"; + break; + case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION: + dump += INDENT4 "DeviceType: touchNavigation\n"; + break; + case Parameters::DEVICE_TYPE_POINTER: + dump += INDENT4 "DeviceType: pointer\n"; + break; + default: + ALOG_ASSERT(false); + } + + dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, " + "displayId='%s'\n", + toString(mParameters.hasAssociatedDisplay), + toString(mParameters.associatedDisplayIsExternal), + mParameters.uniqueDisplayId.c_str()); + dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); +} + +void TouchInputMapper::configureRawPointerAxes() { + mRawPointerAxes.clear(); +} + +void TouchInputMapper::dumpRawPointerAxes(std::string& dump) { + dump += INDENT3 "Raw Touch Axes:\n"; + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.x, "X"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.y, "Y"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.pressure, "Pressure"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMajor, "TouchMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.touchMinor, "TouchMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMajor, "ToolMajor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.toolMinor, "ToolMinor"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.orientation, "Orientation"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.distance, "Distance"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltX, "TiltX"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.tiltY, "TiltY"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.trackingId, "TrackingId"); + dumpRawAbsoluteAxisInfo(dump, mRawPointerAxes.slot, "Slot"); +} + +bool TouchInputMapper::hasExternalStylus() const { + return mExternalStylusConnected; +} + +/** + * Determine which DisplayViewport to use. + * 1. If display port is specified, return the matching viewport. If matching viewport not + * found, then return. + * 2. If a device has associated display, get the matching viewport by either unique id or by + * the display type (internal or external). + * 3. Otherwise, use a non-display viewport. + */ +std::optional<DisplayViewport> TouchInputMapper::findViewport() { + if (mParameters.hasAssociatedDisplay) { + const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort(); + if (displayPort) { + // Find the viewport that contains the same port + std::optional<DisplayViewport> v = mConfig.getDisplayViewportByPort(*displayPort); + if (!v) { + ALOGW("Input device %s should be associated with display on port %" PRIu8 ", " + "but the corresponding viewport is not found.", + getDeviceName().c_str(), *displayPort); + } + return v; + } + + if (!mParameters.uniqueDisplayId.empty()) { + return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId); + } + + ViewportType viewportTypeToUse; + if (mParameters.associatedDisplayIsExternal) { + viewportTypeToUse = ViewportType::VIEWPORT_EXTERNAL; + } else { + viewportTypeToUse = ViewportType::VIEWPORT_INTERNAL; + } + + std::optional<DisplayViewport> viewport = + mConfig.getDisplayViewportByType(viewportTypeToUse); + if (!viewport && viewportTypeToUse == ViewportType::VIEWPORT_EXTERNAL) { + ALOGW("Input device %s should be associated with external display, " + "fallback to internal one for the external viewport is not found.", + getDeviceName().c_str()); + viewport = mConfig.getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + } + + return viewport; + } + + DisplayViewport newViewport; + // Raw width and height in the natural orientation. + int32_t rawWidth = mRawPointerAxes.getRawWidth(); + int32_t rawHeight = mRawPointerAxes.getRawHeight(); + newViewport.setNonDisplayViewport(rawWidth, rawHeight); + return std::make_optional(newViewport); +} + +void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { + int32_t oldDeviceMode = mDeviceMode; + + resolveExternalStylusPresence(); + + // Determine device mode. + if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER && + mConfig.pointerGesturesEnabled) { + mSource = AINPUT_SOURCE_MOUSE; + mDeviceMode = DEVICE_MODE_POINTER; + if (hasStylus()) { + mSource |= AINPUT_SOURCE_STYLUS; + } + } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN && + mParameters.hasAssociatedDisplay) { + mSource = AINPUT_SOURCE_TOUCHSCREEN; + mDeviceMode = DEVICE_MODE_DIRECT; + if (hasStylus()) { + mSource |= AINPUT_SOURCE_STYLUS; + } + if (hasExternalStylus()) { + mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS; + } + } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) { + mSource = AINPUT_SOURCE_TOUCH_NAVIGATION; + mDeviceMode = DEVICE_MODE_NAVIGATION; + } else { + mSource = AINPUT_SOURCE_TOUCHPAD; + mDeviceMode = DEVICE_MODE_UNSCALED; + } + + // Ensure we have valid X and Y axes. + if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { + ALOGW("Touch device '%s' did not report support for X or Y axis! " + "The device will be inoperable.", + getDeviceName().c_str()); + mDeviceMode = DEVICE_MODE_DISABLED; + return; + } + + // Get associated display dimensions. + std::optional<DisplayViewport> newViewport = findViewport(); + if (!newViewport) { + ALOGI("Touch device '%s' could not query the properties of its associated " + "display. The device will be inoperable until the display size " + "becomes available.", + getDeviceName().c_str()); + mDeviceMode = DEVICE_MODE_DISABLED; + return; + } + + // Raw width and height in the natural orientation. + int32_t rawWidth = mRawPointerAxes.getRawWidth(); + int32_t rawHeight = mRawPointerAxes.getRawHeight(); + + bool viewportChanged = mViewport != *newViewport; + if (viewportChanged) { + mViewport = *newViewport; + + if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) { + // Convert rotated viewport to natural surface coordinates. + int32_t naturalLogicalWidth, naturalLogicalHeight; + int32_t naturalPhysicalWidth, naturalPhysicalHeight; + int32_t naturalPhysicalLeft, naturalPhysicalTop; + int32_t naturalDeviceWidth, naturalDeviceHeight; + switch (mViewport.orientation) { + case DISPLAY_ORIENTATION_90: + naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; + naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; + naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; + naturalPhysicalTop = mViewport.physicalLeft; + naturalDeviceWidth = mViewport.deviceHeight; + naturalDeviceHeight = mViewport.deviceWidth; + break; + case DISPLAY_ORIENTATION_180: + naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; + naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; + naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; + naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom; + naturalDeviceWidth = mViewport.deviceWidth; + naturalDeviceHeight = mViewport.deviceHeight; + break; + case DISPLAY_ORIENTATION_270: + naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; + naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; + naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalLeft = mViewport.physicalTop; + naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; + naturalDeviceWidth = mViewport.deviceHeight; + naturalDeviceHeight = mViewport.deviceWidth; + break; + case DISPLAY_ORIENTATION_0: + default: + naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; + naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; + naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalLeft = mViewport.physicalLeft; + naturalPhysicalTop = mViewport.physicalTop; + naturalDeviceWidth = mViewport.deviceWidth; + naturalDeviceHeight = mViewport.deviceHeight; + break; + } + + if (naturalPhysicalHeight == 0 || naturalPhysicalWidth == 0) { + ALOGE("Viewport is not set properly: %s", mViewport.toString().c_str()); + naturalPhysicalHeight = naturalPhysicalHeight == 0 ? 1 : naturalPhysicalHeight; + naturalPhysicalWidth = naturalPhysicalWidth == 0 ? 1 : naturalPhysicalWidth; + } + + mPhysicalWidth = naturalPhysicalWidth; + mPhysicalHeight = naturalPhysicalHeight; + mPhysicalLeft = naturalPhysicalLeft; + mPhysicalTop = naturalPhysicalTop; + + mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; + mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; + mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; + mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; + + mSurfaceOrientation = + mParameters.orientationAware ? mViewport.orientation : DISPLAY_ORIENTATION_0; + } else { + mPhysicalWidth = rawWidth; + mPhysicalHeight = rawHeight; + mPhysicalLeft = 0; + mPhysicalTop = 0; + + mSurfaceWidth = rawWidth; + mSurfaceHeight = rawHeight; + mSurfaceLeft = 0; + mSurfaceTop = 0; + mSurfaceOrientation = DISPLAY_ORIENTATION_0; + } + } + + // If moving between pointer modes, need to reset some state. + bool deviceModeChanged = mDeviceMode != oldDeviceMode; + if (deviceModeChanged) { + mOrientedRanges.clear(); + } + + // Create or update pointer controller if needed. + if (mDeviceMode == DEVICE_MODE_POINTER || + (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) { + if (mPointerController == nullptr || viewportChanged) { + mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + } + } else { + mPointerController.clear(); + } + + if (viewportChanged || deviceModeChanged) { + ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " + "display id %d", + getDeviceId(), getDeviceName().c_str(), mSurfaceWidth, mSurfaceHeight, + mSurfaceOrientation, mDeviceMode, mViewport.displayId); + + // Configure X and Y factors. + mXScale = float(mSurfaceWidth) / rawWidth; + mYScale = float(mSurfaceHeight) / rawHeight; + mXTranslate = -mSurfaceLeft; + mYTranslate = -mSurfaceTop; + mXPrecision = 1.0f / mXScale; + mYPrecision = 1.0f / mYScale; + + mOrientedRanges.x.axis = AMOTION_EVENT_AXIS_X; + mOrientedRanges.x.source = mSource; + mOrientedRanges.y.axis = AMOTION_EVENT_AXIS_Y; + mOrientedRanges.y.source = mSource; + + configureVirtualKeys(); + + // Scale factor for terms that are not oriented in a particular axis. + // If the pixels are square then xScale == yScale otherwise we fake it + // by choosing an average. + mGeometricScale = avg(mXScale, mYScale); + + // Size of diagonal axis. + float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight); + + // Size factors. + if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { + if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.touchMajor.maxValue != 0) { + mSizeScale = 1.0f / mRawPointerAxes.touchMajor.maxValue; + } else if (mRawPointerAxes.toolMajor.valid && mRawPointerAxes.toolMajor.maxValue != 0) { + mSizeScale = 1.0f / mRawPointerAxes.toolMajor.maxValue; + } else { + mSizeScale = 0.0f; + } + + mOrientedRanges.haveTouchSize = true; + mOrientedRanges.haveToolSize = true; + mOrientedRanges.haveSize = true; + + mOrientedRanges.touchMajor.axis = AMOTION_EVENT_AXIS_TOUCH_MAJOR; + mOrientedRanges.touchMajor.source = mSource; + mOrientedRanges.touchMajor.min = 0; + mOrientedRanges.touchMajor.max = diagonalSize; + mOrientedRanges.touchMajor.flat = 0; + mOrientedRanges.touchMajor.fuzz = 0; + mOrientedRanges.touchMajor.resolution = 0; + + mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; + mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; + + mOrientedRanges.toolMajor.axis = AMOTION_EVENT_AXIS_TOOL_MAJOR; + mOrientedRanges.toolMajor.source = mSource; + mOrientedRanges.toolMajor.min = 0; + mOrientedRanges.toolMajor.max = diagonalSize; + mOrientedRanges.toolMajor.flat = 0; + mOrientedRanges.toolMajor.fuzz = 0; + mOrientedRanges.toolMajor.resolution = 0; + + mOrientedRanges.toolMinor = mOrientedRanges.toolMajor; + mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR; + + mOrientedRanges.size.axis = AMOTION_EVENT_AXIS_SIZE; + mOrientedRanges.size.source = mSource; + mOrientedRanges.size.min = 0; + mOrientedRanges.size.max = 1.0; + mOrientedRanges.size.flat = 0; + mOrientedRanges.size.fuzz = 0; + mOrientedRanges.size.resolution = 0; + } else { + mSizeScale = 0.0f; + } + + // Pressure factors. + mPressureScale = 0; + float pressureMax = 1.0; + if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL || + mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) { + if (mCalibration.havePressureScale) { + mPressureScale = mCalibration.pressureScale; + pressureMax = mPressureScale * mRawPointerAxes.pressure.maxValue; + } else if (mRawPointerAxes.pressure.valid && mRawPointerAxes.pressure.maxValue != 0) { + mPressureScale = 1.0f / mRawPointerAxes.pressure.maxValue; + } + } + + mOrientedRanges.pressure.axis = AMOTION_EVENT_AXIS_PRESSURE; + mOrientedRanges.pressure.source = mSource; + mOrientedRanges.pressure.min = 0; + mOrientedRanges.pressure.max = pressureMax; + mOrientedRanges.pressure.flat = 0; + mOrientedRanges.pressure.fuzz = 0; + mOrientedRanges.pressure.resolution = 0; + + // Tilt + mTiltXCenter = 0; + mTiltXScale = 0; + mTiltYCenter = 0; + mTiltYScale = 0; + mHaveTilt = mRawPointerAxes.tiltX.valid && mRawPointerAxes.tiltY.valid; + if (mHaveTilt) { + mTiltXCenter = avg(mRawPointerAxes.tiltX.minValue, mRawPointerAxes.tiltX.maxValue); + mTiltYCenter = avg(mRawPointerAxes.tiltY.minValue, mRawPointerAxes.tiltY.maxValue); + mTiltXScale = M_PI / 180; + mTiltYScale = M_PI / 180; + + mOrientedRanges.haveTilt = true; + + mOrientedRanges.tilt.axis = AMOTION_EVENT_AXIS_TILT; + mOrientedRanges.tilt.source = mSource; + mOrientedRanges.tilt.min = 0; + mOrientedRanges.tilt.max = M_PI_2; + mOrientedRanges.tilt.flat = 0; + mOrientedRanges.tilt.fuzz = 0; + mOrientedRanges.tilt.resolution = 0; + } + + // Orientation + mOrientationScale = 0; + if (mHaveTilt) { + mOrientedRanges.haveOrientation = true; + + mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; + mOrientedRanges.orientation.source = mSource; + mOrientedRanges.orientation.min = -M_PI; + mOrientedRanges.orientation.max = M_PI; + mOrientedRanges.orientation.flat = 0; + mOrientedRanges.orientation.fuzz = 0; + mOrientedRanges.orientation.resolution = 0; + } else if (mCalibration.orientationCalibration != + Calibration::ORIENTATION_CALIBRATION_NONE) { + if (mCalibration.orientationCalibration == + Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) { + if (mRawPointerAxes.orientation.valid) { + if (mRawPointerAxes.orientation.maxValue > 0) { + mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue; + } else if (mRawPointerAxes.orientation.minValue < 0) { + mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue; + } else { + mOrientationScale = 0; + } + } + } + + mOrientedRanges.haveOrientation = true; + + mOrientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION; + mOrientedRanges.orientation.source = mSource; + mOrientedRanges.orientation.min = -M_PI_2; + mOrientedRanges.orientation.max = M_PI_2; + mOrientedRanges.orientation.flat = 0; + mOrientedRanges.orientation.fuzz = 0; + mOrientedRanges.orientation.resolution = 0; + } + + // Distance + mDistanceScale = 0; + if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) { + if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_SCALED) { + if (mCalibration.haveDistanceScale) { + mDistanceScale = mCalibration.distanceScale; + } else { + mDistanceScale = 1.0f; + } + } + + mOrientedRanges.haveDistance = true; + + mOrientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE; + mOrientedRanges.distance.source = mSource; + mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale; + mOrientedRanges.distance.max = mRawPointerAxes.distance.maxValue * mDistanceScale; + mOrientedRanges.distance.flat = 0; + mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale; + mOrientedRanges.distance.resolution = 0; + } + + // Compute oriented precision, scales and ranges. + // Note that the maximum value reported is an inclusive maximum value so it is one + // unit less than the total width or height of surface. + switch (mSurfaceOrientation) { + case DISPLAY_ORIENTATION_90: + case DISPLAY_ORIENTATION_270: + mOrientedXPrecision = mYPrecision; + mOrientedYPrecision = mXPrecision; + + mOrientedRanges.x.min = mYTranslate; + mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1; + mOrientedRanges.x.flat = 0; + mOrientedRanges.x.fuzz = 0; + mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; + + mOrientedRanges.y.min = mXTranslate; + mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1; + mOrientedRanges.y.flat = 0; + mOrientedRanges.y.fuzz = 0; + mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; + break; + + default: + mOrientedXPrecision = mXPrecision; + mOrientedYPrecision = mYPrecision; + + mOrientedRanges.x.min = mXTranslate; + mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1; + mOrientedRanges.x.flat = 0; + mOrientedRanges.x.fuzz = 0; + mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; + + mOrientedRanges.y.min = mYTranslate; + mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1; + mOrientedRanges.y.flat = 0; + mOrientedRanges.y.fuzz = 0; + mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; + break; + } + + // Location + updateAffineTransformation(); + + if (mDeviceMode == DEVICE_MODE_POINTER) { + // Compute pointer gesture detection parameters. + float rawDiagonal = hypotf(rawWidth, rawHeight); + float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight); + + // Scale movements such that one whole swipe of the touch pad covers a + // given area relative to the diagonal size of the display when no acceleration + // is applied. + // Assume that the touch pad has a square aspect ratio such that movements in + // X and Y of the same number of raw units cover the same physical distance. + mPointerXMovementScale = + mConfig.pointerGestureMovementSpeedRatio * displayDiagonal / rawDiagonal; + mPointerYMovementScale = mPointerXMovementScale; + + // Scale zooms to cover a smaller range of the display than movements do. + // This value determines the area around the pointer that is affected by freeform + // pointer gestures. + mPointerXZoomScale = + mConfig.pointerGestureZoomSpeedRatio * displayDiagonal / rawDiagonal; + mPointerYZoomScale = mPointerXZoomScale; + + // Max width between pointers to detect a swipe gesture is more than some fraction + // of the diagonal axis of the touch pad. Touches that are wider than this are + // translated into freeform gestures. + mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; + + // Abort current pointer usages because the state has changed. + abortPointerUsage(when, 0 /*policyFlags*/); + } + + // Inform the dispatcher about the changes. + *outResetNeeded = true; + bumpGeneration(); + } +} + +void TouchInputMapper::dumpSurface(std::string& dump) { + dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str()); + dump += StringPrintf(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth); + dump += StringPrintf(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); + dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); + dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); + dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth); + dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight); + dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft); + dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop); + dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation); +} + +void TouchInputMapper::configureVirtualKeys() { + std::vector<VirtualKeyDefinition> virtualKeyDefinitions; + getEventHub()->getVirtualKeyDefinitions(getDeviceId(), virtualKeyDefinitions); + + mVirtualKeys.clear(); + + if (virtualKeyDefinitions.size() == 0) { + return; + } + + int32_t touchScreenLeft = mRawPointerAxes.x.minValue; + int32_t touchScreenTop = mRawPointerAxes.y.minValue; + int32_t touchScreenWidth = mRawPointerAxes.getRawWidth(); + int32_t touchScreenHeight = mRawPointerAxes.getRawHeight(); + + for (const VirtualKeyDefinition& virtualKeyDefinition : virtualKeyDefinitions) { + VirtualKey virtualKey; + + virtualKey.scanCode = virtualKeyDefinition.scanCode; + int32_t keyCode; + int32_t dummyKeyMetaState; + uint32_t flags; + if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, 0, &keyCode, + &dummyKeyMetaState, &flags)) { + ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); + continue; // drop the key + } + + virtualKey.keyCode = keyCode; + virtualKey.flags = flags; + + // convert the key definition's display coordinates into touch coordinates for a hit box + int32_t halfWidth = virtualKeyDefinition.width / 2; + int32_t halfHeight = virtualKeyDefinition.height / 2; + + virtualKey.hitLeft = + (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mSurfaceWidth + + touchScreenLeft; + virtualKey.hitRight = + (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mSurfaceWidth + + touchScreenLeft; + virtualKey.hitTop = + (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mSurfaceHeight + + touchScreenTop; + virtualKey.hitBottom = + (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mSurfaceHeight + + touchScreenTop; + mVirtualKeys.push_back(virtualKey); + } +} + +void TouchInputMapper::dumpVirtualKeys(std::string& dump) { + if (!mVirtualKeys.empty()) { + dump += INDENT3 "Virtual Keys:\n"; + + for (size_t i = 0; i < mVirtualKeys.size(); i++) { + const VirtualKey& virtualKey = mVirtualKeys[i]; + dump += StringPrintf(INDENT4 "%zu: scanCode=%d, keyCode=%d, " + "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n", + i, virtualKey.scanCode, virtualKey.keyCode, virtualKey.hitLeft, + virtualKey.hitRight, virtualKey.hitTop, virtualKey.hitBottom); + } + } +} + +void TouchInputMapper::parseCalibration() { + const PropertyMap& in = getDevice()->getConfiguration(); + Calibration& out = mCalibration; + + // Size + out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT; + String8 sizeCalibrationString; + if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) { + if (sizeCalibrationString == "none") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; + } else if (sizeCalibrationString == "geometric") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; + } else if (sizeCalibrationString == "diameter") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER; + } else if (sizeCalibrationString == "box") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX; + } else if (sizeCalibrationString == "area") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA; + } else if (sizeCalibrationString != "default") { + ALOGW("Invalid value for touch.size.calibration: '%s'", sizeCalibrationString.string()); + } + } + + out.haveSizeScale = in.tryGetProperty(String8("touch.size.scale"), out.sizeScale); + out.haveSizeBias = in.tryGetProperty(String8("touch.size.bias"), out.sizeBias); + out.haveSizeIsSummed = in.tryGetProperty(String8("touch.size.isSummed"), out.sizeIsSummed); + + // Pressure + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT; + String8 pressureCalibrationString; + if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) { + if (pressureCalibrationString == "none") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; + } else if (pressureCalibrationString == "physical") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; + } else if (pressureCalibrationString == "amplitude") { + out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; + } else if (pressureCalibrationString != "default") { + ALOGW("Invalid value for touch.pressure.calibration: '%s'", + pressureCalibrationString.string()); + } + } + + out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), out.pressureScale); + + // Orientation + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT; + String8 orientationCalibrationString; + if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) { + if (orientationCalibrationString == "none") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; + } else if (orientationCalibrationString == "interpolated") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; + } else if (orientationCalibrationString == "vector") { + out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_VECTOR; + } else if (orientationCalibrationString != "default") { + ALOGW("Invalid value for touch.orientation.calibration: '%s'", + orientationCalibrationString.string()); + } + } + + // Distance + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT; + String8 distanceCalibrationString; + if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) { + if (distanceCalibrationString == "none") { + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; + } else if (distanceCalibrationString == "scaled") { + out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; + } else if (distanceCalibrationString != "default") { + ALOGW("Invalid value for touch.distance.calibration: '%s'", + distanceCalibrationString.string()); + } + } + + out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale); + + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT; + String8 coverageCalibrationString; + if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) { + if (coverageCalibrationString == "none") { + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; + } else if (coverageCalibrationString == "box") { + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX; + } else if (coverageCalibrationString != "default") { + ALOGW("Invalid value for touch.coverage.calibration: '%s'", + coverageCalibrationString.string()); + } + } +} + +void TouchInputMapper::resolveCalibration() { + // Size + if (mRawPointerAxes.touchMajor.valid || mRawPointerAxes.toolMajor.valid) { + if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DEFAULT) { + mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; + } + } else { + mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; + } + + // Pressure + if (mRawPointerAxes.pressure.valid) { + if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_DEFAULT) { + mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; + } + } else { + mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; + } + + // Orientation + if (mRawPointerAxes.orientation.valid) { + if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_DEFAULT) { + mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; + } + } else { + mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; + } + + // Distance + if (mRawPointerAxes.distance.valid) { + if (mCalibration.distanceCalibration == Calibration::DISTANCE_CALIBRATION_DEFAULT) { + mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED; + } + } else { + mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; + } + + // Coverage + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) { + mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; + } +} + +void TouchInputMapper::dumpCalibration(std::string& dump) { + dump += INDENT3 "Calibration:\n"; + + // Size + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_NONE: + dump += INDENT4 "touch.size.calibration: none\n"; + break; + case Calibration::SIZE_CALIBRATION_GEOMETRIC: + dump += INDENT4 "touch.size.calibration: geometric\n"; + break; + case Calibration::SIZE_CALIBRATION_DIAMETER: + dump += INDENT4 "touch.size.calibration: diameter\n"; + break; + case Calibration::SIZE_CALIBRATION_BOX: + dump += INDENT4 "touch.size.calibration: box\n"; + break; + case Calibration::SIZE_CALIBRATION_AREA: + dump += INDENT4 "touch.size.calibration: area\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.haveSizeScale) { + dump += StringPrintf(INDENT4 "touch.size.scale: %0.3f\n", mCalibration.sizeScale); + } + + if (mCalibration.haveSizeBias) { + dump += StringPrintf(INDENT4 "touch.size.bias: %0.3f\n", mCalibration.sizeBias); + } + + if (mCalibration.haveSizeIsSummed) { + dump += StringPrintf(INDENT4 "touch.size.isSummed: %s\n", + toString(mCalibration.sizeIsSummed)); + } + + // Pressure + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_NONE: + dump += INDENT4 "touch.pressure.calibration: none\n"; + break; + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + dump += INDENT4 "touch.pressure.calibration: physical\n"; + break; + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + dump += INDENT4 "touch.pressure.calibration: amplitude\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.havePressureScale) { + dump += StringPrintf(INDENT4 "touch.pressure.scale: %0.3f\n", mCalibration.pressureScale); + } + + // Orientation + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_NONE: + dump += INDENT4 "touch.orientation.calibration: none\n"; + break; + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + dump += INDENT4 "touch.orientation.calibration: interpolated\n"; + break; + case Calibration::ORIENTATION_CALIBRATION_VECTOR: + dump += INDENT4 "touch.orientation.calibration: vector\n"; + break; + default: + ALOG_ASSERT(false); + } + + // Distance + switch (mCalibration.distanceCalibration) { + case Calibration::DISTANCE_CALIBRATION_NONE: + dump += INDENT4 "touch.distance.calibration: none\n"; + break; + case Calibration::DISTANCE_CALIBRATION_SCALED: + dump += INDENT4 "touch.distance.calibration: scaled\n"; + break; + default: + ALOG_ASSERT(false); + } + + if (mCalibration.haveDistanceScale) { + dump += StringPrintf(INDENT4 "touch.distance.scale: %0.3f\n", mCalibration.distanceScale); + } + + switch (mCalibration.coverageCalibration) { + case Calibration::COVERAGE_CALIBRATION_NONE: + dump += INDENT4 "touch.coverage.calibration: none\n"; + break; + case Calibration::COVERAGE_CALIBRATION_BOX: + dump += INDENT4 "touch.coverage.calibration: box\n"; + break; + default: + ALOG_ASSERT(false); + } +} + +void TouchInputMapper::dumpAffineTransformation(std::string& dump) { + dump += INDENT3 "Affine Transformation:\n"; + + dump += StringPrintf(INDENT4 "X scale: %0.3f\n", mAffineTransform.x_scale); + dump += StringPrintf(INDENT4 "X ymix: %0.3f\n", mAffineTransform.x_ymix); + dump += StringPrintf(INDENT4 "X offset: %0.3f\n", mAffineTransform.x_offset); + dump += StringPrintf(INDENT4 "Y xmix: %0.3f\n", mAffineTransform.y_xmix); + dump += StringPrintf(INDENT4 "Y scale: %0.3f\n", mAffineTransform.y_scale); + dump += StringPrintf(INDENT4 "Y offset: %0.3f\n", mAffineTransform.y_offset); +} + +void TouchInputMapper::updateAffineTransformation() { + mAffineTransform = getPolicy()->getTouchAffineTransformation(mDevice->getDescriptor(), + mSurfaceOrientation); +} + +void TouchInputMapper::reset(nsecs_t when) { + mCursorButtonAccumulator.reset(getDevice()); + mCursorScrollAccumulator.reset(getDevice()); + mTouchButtonAccumulator.reset(getDevice()); + + mPointerVelocityControl.reset(); + mWheelXVelocityControl.reset(); + mWheelYVelocityControl.reset(); + + mRawStatesPending.clear(); + mCurrentRawState.clear(); + mCurrentCookedState.clear(); + mLastRawState.clear(); + mLastCookedState.clear(); + mPointerUsage = POINTER_USAGE_NONE; + mSentHoverEnter = false; + mHavePointerIds = false; + mCurrentMotionAborted = false; + mDownTime = 0; + + mCurrentVirtualKey.down = false; + + mPointerGesture.reset(); + mPointerSimple.reset(); + resetExternalStylus(); + + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->clearSpots(); + } + + InputMapper::reset(when); +} + +void TouchInputMapper::resetExternalStylus() { + mExternalStylusState.clear(); + mExternalStylusId = -1; + mExternalStylusFusionTimeout = LLONG_MAX; + mExternalStylusDataPending = false; +} + +void TouchInputMapper::clearStylusDataPendingFlags() { + mExternalStylusDataPending = false; + mExternalStylusFusionTimeout = LLONG_MAX; +} + +void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) { + nsecs_t now = systemTime(CLOCK_MONOTONIC); + nsecs_t latency = now - evdevTime; + mStatistics.addValue(nanoseconds_to_microseconds(latency)); + nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime; + if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) { + android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mStatistics.min, + mStatistics.max, mStatistics.mean(), mStatistics.stdev(), + mStatistics.count); + mStatistics.reset(now); + } +} + +void TouchInputMapper::process(const RawEvent* rawEvent) { + mCursorButtonAccumulator.process(rawEvent); + mCursorScrollAccumulator.process(rawEvent); + mTouchButtonAccumulator.process(rawEvent); + + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { + reportEventForStatistics(rawEvent->when); + sync(rawEvent->when); + } +} + +void TouchInputMapper::sync(nsecs_t when) { + const RawState* last = + mRawStatesPending.empty() ? &mCurrentRawState : &mRawStatesPending.back(); + + // Push a new state. + mRawStatesPending.emplace_back(); + + RawState* next = &mRawStatesPending.back(); + next->clear(); + next->when = when; + + // Sync button state. + next->buttonState = + mTouchButtonAccumulator.getButtonState() | mCursorButtonAccumulator.getButtonState(); + + // Sync scroll + next->rawVScroll = mCursorScrollAccumulator.getRelativeVWheel(); + next->rawHScroll = mCursorScrollAccumulator.getRelativeHWheel(); + mCursorScrollAccumulator.finishSync(); + + // Sync touch + syncTouch(when, next); + + // Assign pointer ids. + if (!mHavePointerIds) { + assignPointerIds(last, next); + } + +#if DEBUG_RAW_EVENTS + ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, " + "hovering ids 0x%08x -> 0x%08x", + last->rawPointerData.pointerCount, next->rawPointerData.pointerCount, + last->rawPointerData.touchingIdBits.value, next->rawPointerData.touchingIdBits.value, + last->rawPointerData.hoveringIdBits.value, next->rawPointerData.hoveringIdBits.value); +#endif + + processRawTouches(false /*timeout*/); +} + +void TouchInputMapper::processRawTouches(bool timeout) { + if (mDeviceMode == DEVICE_MODE_DISABLED) { + // Drop all input if the device is disabled. + mCurrentRawState.clear(); + mRawStatesPending.clear(); + return; + } + + // Drain any pending touch states. The invariant here is that the mCurrentRawState is always + // valid and must go through the full cook and dispatch cycle. This ensures that anything + // touching the current state will only observe the events that have been dispatched to the + // rest of the pipeline. + const size_t N = mRawStatesPending.size(); + size_t count; + for (count = 0; count < N; count++) { + const RawState& next = mRawStatesPending[count]; + + // A failure to assign the stylus id means that we're waiting on stylus data + // and so should defer the rest of the pipeline. + if (assignExternalStylusId(next, timeout)) { + break; + } + + // All ready to go. + clearStylusDataPendingFlags(); + mCurrentRawState.copyFrom(next); + if (mCurrentRawState.when < mLastRawState.when) { + mCurrentRawState.when = mLastRawState.when; + } + cookAndDispatch(mCurrentRawState.when); + } + if (count != 0) { + mRawStatesPending.erase(mRawStatesPending.begin(), mRawStatesPending.begin() + count); + } + + if (mExternalStylusDataPending) { + if (timeout) { + nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY; + clearStylusDataPendingFlags(); + mCurrentRawState.copyFrom(mLastRawState); +#if DEBUG_STYLUS_FUSION + ALOGD("Timeout expired, synthesizing event with new stylus data"); +#endif + cookAndDispatch(when); + } else if (mExternalStylusFusionTimeout == LLONG_MAX) { + mExternalStylusFusionTimeout = mExternalStylusState.when + TOUCH_DATA_TIMEOUT; + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + } + } +} + +void TouchInputMapper::cookAndDispatch(nsecs_t when) { + // Always start with a clean state. + mCurrentCookedState.clear(); + + // Apply stylus buttons to current raw state. + applyExternalStylusButtonState(when); + + // Handle policy on initial down or hover events. + bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && + mCurrentRawState.rawPointerData.pointerCount != 0; + + uint32_t policyFlags = 0; + bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState; + if (initialDown || buttonsPressed) { + // If this is a touch screen, hide the pointer on an initial down. + if (mDeviceMode == DEVICE_MODE_DIRECT) { + getContext()->fadePointer(); + } + + if (mParameters.wake) { + policyFlags |= POLICY_FLAG_WAKE; + } + } + + // Consume raw off-screen touches before cooking pointer data. + // If touches are consumed, subsequent code will not receive any pointer data. + if (consumeRawTouches(when, policyFlags)) { + mCurrentRawState.rawPointerData.clear(); + } + + // Cook pointer data. This call populates the mCurrentCookedState.cookedPointerData structure + // with cooked pointer data that has the same ids and indices as the raw data. + // The following code can use either the raw or cooked data, as needed. + cookPointerData(); + + // Apply stylus pressure to current cooked state. + applyExternalStylusTouchState(when); + + // Synthesize key down from raw buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, getDeviceId(), mSource, + mViewport.displayId, policyFlags, mLastCookedState.buttonState, + mCurrentCookedState.buttonState); + + // Dispatch the touches either directly or by translation through a pointer on screen. + if (mDeviceMode == DEVICE_MODE_POINTER) { + for (BitSet32 idBits(mCurrentRawState.rawPointerData.touchingIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + mCurrentCookedState.stylusIdBits.markBit(id); + } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + mCurrentCookedState.fingerIdBits.markBit(id); + } else if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_MOUSE) { + mCurrentCookedState.mouseIdBits.markBit(id); + } + } + for (BitSet32 idBits(mCurrentRawState.rawPointerData.hoveringIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + if (pointer.toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || + pointer.toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + mCurrentCookedState.stylusIdBits.markBit(id); + } + } + + // Stylus takes precedence over all tools, then mouse, then finger. + PointerUsage pointerUsage = mPointerUsage; + if (!mCurrentCookedState.stylusIdBits.isEmpty()) { + mCurrentCookedState.mouseIdBits.clear(); + mCurrentCookedState.fingerIdBits.clear(); + pointerUsage = POINTER_USAGE_STYLUS; + } else if (!mCurrentCookedState.mouseIdBits.isEmpty()) { + mCurrentCookedState.fingerIdBits.clear(); + pointerUsage = POINTER_USAGE_MOUSE; + } else if (!mCurrentCookedState.fingerIdBits.isEmpty() || + isPointerDown(mCurrentRawState.buttonState)) { + pointerUsage = POINTER_USAGE_GESTURES; + } + + dispatchPointerUsage(when, policyFlags, pointerUsage); + } else { + if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches && + mPointerController != nullptr) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + + mPointerController->setButtonState(mCurrentRawState.buttonState); + mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.touchingIdBits, + mViewport.displayId); + } + + if (!mCurrentMotionAborted) { + dispatchButtonRelease(when, policyFlags); + dispatchHoverExit(when, policyFlags); + dispatchTouches(when, policyFlags); + dispatchHoverEnterAndMove(when, policyFlags); + dispatchButtonPress(when, policyFlags); + } + + if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { + mCurrentMotionAborted = false; + } + } + + // Synthesize key up from raw buttons if needed. + synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, getDeviceId(), mSource, + mViewport.displayId, policyFlags, mLastCookedState.buttonState, + mCurrentCookedState.buttonState); + + // Clear some transient state. + mCurrentRawState.rawVScroll = 0; + mCurrentRawState.rawHScroll = 0; + + // Copy current touch to last touch in preparation for the next cycle. + mLastRawState.copyFrom(mCurrentRawState); + mLastCookedState.copyFrom(mCurrentCookedState); +} + +void TouchInputMapper::applyExternalStylusButtonState(nsecs_t when) { + if (mDeviceMode == DEVICE_MODE_DIRECT && hasExternalStylus() && mExternalStylusId != -1) { + mCurrentRawState.buttonState |= mExternalStylusState.buttons; + } +} + +void TouchInputMapper::applyExternalStylusTouchState(nsecs_t when) { + CookedPointerData& currentPointerData = mCurrentCookedState.cookedPointerData; + const CookedPointerData& lastPointerData = mLastCookedState.cookedPointerData; + + if (mExternalStylusId != -1 && currentPointerData.isTouching(mExternalStylusId)) { + float pressure = mExternalStylusState.pressure; + if (pressure == 0.0f && lastPointerData.isTouching(mExternalStylusId)) { + const PointerCoords& coords = lastPointerData.pointerCoordsForId(mExternalStylusId); + pressure = coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); + } + PointerCoords& coords = currentPointerData.editPointerCoordsWithId(mExternalStylusId); + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + + PointerProperties& properties = + currentPointerData.editPointerPropertiesWithId(mExternalStylusId); + if (mExternalStylusState.toolType != AMOTION_EVENT_TOOL_TYPE_UNKNOWN) { + properties.toolType = mExternalStylusState.toolType; + } + } +} + +bool TouchInputMapper::assignExternalStylusId(const RawState& state, bool timeout) { + if (mDeviceMode != DEVICE_MODE_DIRECT || !hasExternalStylus()) { + return false; + } + + const bool initialDown = mLastRawState.rawPointerData.pointerCount == 0 && + state.rawPointerData.pointerCount != 0; + if (initialDown) { + if (mExternalStylusState.pressure != 0.0f) { +#if DEBUG_STYLUS_FUSION + ALOGD("Have both stylus and touch data, beginning fusion"); +#endif + mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit(); + } else if (timeout) { +#if DEBUG_STYLUS_FUSION + ALOGD("Timeout expired, assuming touch is not a stylus."); +#endif + resetExternalStylus(); + } else { + if (mExternalStylusFusionTimeout == LLONG_MAX) { + mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT; + } +#if DEBUG_STYLUS_FUSION + ALOGD("No stylus data but stylus is connected, requesting timeout " + "(%" PRId64 "ms)", + mExternalStylusFusionTimeout); +#endif + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + return true; + } + } + + // Check if the stylus pointer has gone up. + if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) { +#if DEBUG_STYLUS_FUSION + ALOGD("Stylus pointer is going up"); +#endif + mExternalStylusId = -1; + } + + return false; +} + +void TouchInputMapper::timeoutExpired(nsecs_t when) { + if (mDeviceMode == DEVICE_MODE_POINTER) { + if (mPointerUsage == POINTER_USAGE_GESTURES) { + dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/); + } + } else if (mDeviceMode == DEVICE_MODE_DIRECT) { + if (mExternalStylusFusionTimeout < when) { + processRawTouches(true /*timeout*/); + } else if (mExternalStylusFusionTimeout != LLONG_MAX) { + getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout); + } + } +} + +void TouchInputMapper::updateExternalStylusState(const StylusState& state) { + mExternalStylusState.copyFrom(state); + if (mExternalStylusId != -1 || mExternalStylusFusionTimeout != LLONG_MAX) { + // We're either in the middle of a fused stream of data or we're waiting on data before + // dispatching the initial down, so go ahead and dispatch now that we have fresh stylus + // data. + mExternalStylusDataPending = true; + processRawTouches(false /*timeout*/); + } +} + +bool TouchInputMapper::consumeRawTouches(nsecs_t when, uint32_t policyFlags) { + // Check for release of a virtual key. + if (mCurrentVirtualKey.down) { + if (mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + // Pointer went up while virtual key was down. + mCurrentVirtualKey.down = false; + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); + } + return true; + } + + if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { + uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); + if (virtualKey && virtualKey->keyCode == mCurrentVirtualKey.keyCode) { + // Pointer is still within the space of the virtual key. + return true; + } + } + + // Pointer left virtual key area or another pointer also went down. + // Send key cancellation but do not consume the touch yet. + // This is useful when the user swipes through from the virtual key area + // into the main display surface. + mCurrentVirtualKey.down = false; + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode, + mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY | + AKEY_EVENT_FLAG_CANCELED); + } + } + + if (mLastRawState.rawPointerData.touchingIdBits.isEmpty() && + !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + // Pointer just went down. Check for virtual key press or off-screen touches. + uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit(); + const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id); + if (!isPointInsideSurface(pointer.x, pointer.y)) { + // If exactly one pointer went down, check for virtual key hit. + // Otherwise we will drop the entire stroke. + if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) { + const VirtualKey* virtualKey = findVirtualKeyHit(pointer.x, pointer.y); + if (virtualKey) { + mCurrentVirtualKey.down = true; + mCurrentVirtualKey.downTime = when; + mCurrentVirtualKey.keyCode = virtualKey->keyCode; + mCurrentVirtualKey.scanCode = virtualKey->scanCode; + mCurrentVirtualKey.ignored = + mContext->shouldDropVirtualKey(when, getDevice(), virtualKey->keyCode, + virtualKey->scanCode); + + if (!mCurrentVirtualKey.ignored) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", + mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode); +#endif + dispatchVirtualKey(when, policyFlags, AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM | + AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY); + } + } + } + return true; + } + } + + // Disable all virtual key touches that happen within a short time interval of the + // most recent touch within the screen area. The idea is to filter out stray + // virtual key presses when interacting with the touch screen. + // + // Problems we're trying to solve: + // + // 1. While scrolling a list or dragging the window shade, the user swipes down into a + // virtual key area that is implemented by a separate touch panel and accidentally + // triggers a virtual key. + // + // 2. While typing in the on screen keyboard, the user taps slightly outside the screen + // area and accidentally triggers a virtual key. This often happens when virtual keys + // are layed out below the screen near to where the on screen keyboard's space bar + // is displayed. + if (mConfig.virtualKeyQuietTime > 0 && + !mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) { + mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); + } + return false; +} + +void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, + int32_t keyEventAction, int32_t keyEventFlags) { + int32_t keyCode = mCurrentVirtualKey.keyCode; + int32_t scanCode = mCurrentVirtualKey.scanCode; + nsecs_t downTime = mCurrentVirtualKey.downTime; + int32_t metaState = mContext->getGlobalMetaState(); + policyFlags |= POLICY_FLAG_VIRTUAL; + + NotifyKeyArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, + mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, + scanCode, metaState, downTime); + getListener()->notifyKey(&args); +} + +void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { + BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; + if (!currentIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentCookedState.deviceTimestamp, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + mCurrentMotionAborted = true; + } +} + +void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { + BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; + BitSet32 lastIdBits = mLastCookedState.cookedPointerData.touchingIdBits; + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + + if (currentIdBits == lastIdBits) { + if (!currentIdBits.isEmpty()) { + // No pointer id changes so this is a move event. + // The listener takes care of batching moves so we don't have to deal with that here. + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + mCurrentCookedState.deviceTimestamp, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + } else { + // There may be pointers going up and pointers going down and pointers moving + // all at the same time. + BitSet32 upIdBits(lastIdBits.value & ~currentIdBits.value); + BitSet32 downIdBits(currentIdBits.value & ~lastIdBits.value); + BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value); + BitSet32 dispatchedIdBits(lastIdBits.value); + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool moveNeeded = + updateMovedPointers(mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, moveIdBits); + if (buttonState != mLastCookedState.buttonState) { + moveNeeded = true; + } + + // Dispatch pointer up events. + while (!upIdBits.isEmpty()) { + uint32_t upId = upIdBits.clearFirstMarkedBit(); + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, + metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + dispatchedIdBits.clearBit(upId); + } + + // Dispatch move events if any of the remaining pointers moved from their old locations. + // Although applications receive new locations as part of individual pointer up + // events, they do not generally handle them except when presented in a move event. + if (moveNeeded && !moveIdBits.isEmpty()) { + ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, 0, mCurrentCookedState.deviceTimestamp, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + + // Dispatch pointer down events using the new pointer locations. + while (!downIdBits.isEmpty()) { + uint32_t downId = downIdBits.clearFirstMarkedBit(); + dispatchedIdBits.markBit(downId); + + if (dispatchedIdBits.count() == 1) { + // First pointer is going down. Set down time. + mDownTime = when; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, + metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, + downId, mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } + } +} + +void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) { + if (mSentHoverEnter && + (mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty() || + !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) { + int32_t metaState = getContext()->getGlobalMetaState(); + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, + mLastCookedState.buttonState, 0, mLastCookedState.deviceTimestamp, + mLastCookedState.cookedPointerData.pointerProperties, + mLastCookedState.cookedPointerData.pointerCoords, + mLastCookedState.cookedPointerData.idToIndex, + mLastCookedState.cookedPointerData.hoveringIdBits, -1, mOrientedXPrecision, + mOrientedYPrecision, mDownTime); + mSentHoverEnter = false; + } +} + +void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags) { + if (mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty() && + !mCurrentCookedState.cookedPointerData.hoveringIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + if (!mSentHoverEnter) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, + metaState, mCurrentRawState.buttonState, 0, + mCurrentCookedState.deviceTimestamp, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + mSentHoverEnter = true; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, 0, mCurrentCookedState.deviceTimestamp, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.hoveringIdBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) { + BitSet32 releasedButtons(mLastCookedState.buttonState & ~mCurrentCookedState.buttonState); + const BitSet32& idBits = findActiveIdBits(mLastCookedState.cookedPointerData); + const int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mLastCookedState.buttonState; + while (!releasedButtons.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(releasedButtons.clearFirstMarkedBit()); + buttonState &= ~actionButton; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE, + actionButton, 0, metaState, buttonState, 0, + mCurrentCookedState.deviceTimestamp, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) { + BitSet32 pressedButtons(mCurrentCookedState.buttonState & ~mLastCookedState.buttonState); + const BitSet32& idBits = findActiveIdBits(mCurrentCookedState.cookedPointerData); + const int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mLastCookedState.buttonState; + while (!pressedButtons.isEmpty()) { + int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit()); + buttonState |= actionButton; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, + 0, metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp, + mCurrentCookedState.cookedPointerData.pointerProperties, + mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, + mOrientedXPrecision, mOrientedYPrecision, mDownTime); + } +} + +const BitSet32& TouchInputMapper::findActiveIdBits(const CookedPointerData& cookedPointerData) { + if (!cookedPointerData.touchingIdBits.isEmpty()) { + return cookedPointerData.touchingIdBits; + } + return cookedPointerData.hoveringIdBits; +} + +void TouchInputMapper::cookPointerData() { + uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount; + + mCurrentCookedState.cookedPointerData.clear(); + mCurrentCookedState.deviceTimestamp = mCurrentRawState.deviceTimestamp; + mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount; + mCurrentCookedState.cookedPointerData.hoveringIdBits = + mCurrentRawState.rawPointerData.hoveringIdBits; + mCurrentCookedState.cookedPointerData.touchingIdBits = + mCurrentRawState.rawPointerData.touchingIdBits; + + if (mCurrentCookedState.cookedPointerData.pointerCount == 0) { + mCurrentCookedState.buttonState = 0; + } else { + mCurrentCookedState.buttonState = mCurrentRawState.buttonState; + } + + // Walk through the the active pointers and map device coordinates onto + // surface coordinates and adjust for display orientation. + for (uint32_t i = 0; i < currentPointerCount; i++) { + const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i]; + + // Size + float touchMajor, touchMinor, toolMajor, toolMinor, size; + switch (mCalibration.sizeCalibration) { + case Calibration::SIZE_CALIBRATION_GEOMETRIC: + case Calibration::SIZE_CALIBRATION_DIAMETER: + case Calibration::SIZE_CALIBRATION_BOX: + case Calibration::SIZE_CALIBRATION_AREA: + if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) { + touchMajor = in.touchMajor; + touchMinor = mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; + toolMajor = in.toolMajor; + toolMinor = mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; + size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) + : in.touchMajor; + } else if (mRawPointerAxes.touchMajor.valid) { + toolMajor = touchMajor = in.touchMajor; + toolMinor = touchMinor = + mRawPointerAxes.touchMinor.valid ? in.touchMinor : in.touchMajor; + size = mRawPointerAxes.touchMinor.valid ? avg(in.touchMajor, in.touchMinor) + : in.touchMajor; + } else if (mRawPointerAxes.toolMajor.valid) { + touchMajor = toolMajor = in.toolMajor; + touchMinor = toolMinor = + mRawPointerAxes.toolMinor.valid ? in.toolMinor : in.toolMajor; + size = mRawPointerAxes.toolMinor.valid ? avg(in.toolMajor, in.toolMinor) + : in.toolMajor; + } else { + ALOG_ASSERT(false, + "No touch or tool axes. " + "Size calibration should have been resolved to NONE."); + touchMajor = 0; + touchMinor = 0; + toolMajor = 0; + toolMinor = 0; + size = 0; + } + + if (mCalibration.haveSizeIsSummed && mCalibration.sizeIsSummed) { + uint32_t touchingCount = mCurrentRawState.rawPointerData.touchingIdBits.count(); + if (touchingCount > 1) { + touchMajor /= touchingCount; + touchMinor /= touchingCount; + toolMajor /= touchingCount; + toolMinor /= touchingCount; + size /= touchingCount; + } + } + + if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_GEOMETRIC) { + touchMajor *= mGeometricScale; + touchMinor *= mGeometricScale; + toolMajor *= mGeometricScale; + toolMinor *= mGeometricScale; + } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_AREA) { + touchMajor = touchMajor > 0 ? sqrtf(touchMajor) : 0; + touchMinor = touchMajor; + toolMajor = toolMajor > 0 ? sqrtf(toolMajor) : 0; + toolMinor = toolMajor; + } else if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_DIAMETER) { + touchMinor = touchMajor; + toolMinor = toolMajor; + } + + mCalibration.applySizeScaleAndBias(&touchMajor); + mCalibration.applySizeScaleAndBias(&touchMinor); + mCalibration.applySizeScaleAndBias(&toolMajor); + mCalibration.applySizeScaleAndBias(&toolMinor); + size *= mSizeScale; + break; + default: + touchMajor = 0; + touchMinor = 0; + toolMajor = 0; + toolMinor = 0; + size = 0; + break; + } + + // Pressure + float pressure; + switch (mCalibration.pressureCalibration) { + case Calibration::PRESSURE_CALIBRATION_PHYSICAL: + case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: + pressure = in.pressure * mPressureScale; + break; + default: + pressure = in.isHovering ? 0 : 1; + break; + } + + // Tilt and Orientation + float tilt; + float orientation; + if (mHaveTilt) { + float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale; + float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale; + orientation = atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)); + tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle)); + } else { + tilt = 0; + + switch (mCalibration.orientationCalibration) { + case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: + orientation = in.orientation * mOrientationScale; + break; + case Calibration::ORIENTATION_CALIBRATION_VECTOR: { + int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); + int32_t c2 = signExtendNybble(in.orientation & 0x0f); + if (c1 != 0 || c2 != 0) { + orientation = atan2f(c1, c2) * 0.5f; + float confidence = hypotf(c1, c2); + float scale = 1.0f + confidence / 16.0f; + touchMajor *= scale; + touchMinor /= scale; + toolMajor *= scale; + toolMinor /= scale; + } else { + orientation = 0; + } + break; + } + default: + orientation = 0; + } + } + + // Distance + float distance; + switch (mCalibration.distanceCalibration) { + case Calibration::DISTANCE_CALIBRATION_SCALED: + distance = in.distance * mDistanceScale; + break; + default: + distance = 0; + } + + // Coverage + int32_t rawLeft, rawTop, rawRight, rawBottom; + switch (mCalibration.coverageCalibration) { + case Calibration::COVERAGE_CALIBRATION_BOX: + rawLeft = (in.toolMinor & 0xffff0000) >> 16; + rawRight = in.toolMinor & 0x0000ffff; + rawBottom = in.toolMajor & 0x0000ffff; + rawTop = (in.toolMajor & 0xffff0000) >> 16; + break; + default: + rawLeft = rawTop = rawRight = rawBottom = 0; + break; + } + + // Adjust X,Y coords for device calibration + // TODO: Adjust coverage coords? + float xTransformed = in.x, yTransformed = in.y; + mAffineTransform.applyTo(xTransformed, yTransformed); + + // Adjust X, Y, and coverage coords for surface orientation. + float x, y; + float left, top, right, bottom; + + switch (mSurfaceOrientation) { + case DISPLAY_ORIENTATION_90: + x = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + y = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale + mXTranslate; + left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; + top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; + orientation -= M_PI_2; + if (mOrientedRanges.haveOrientation && + orientation < mOrientedRanges.orientation.min) { + orientation += + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + case DISPLAY_ORIENTATION_180: + x = float(mRawPointerAxes.x.maxValue - xTransformed) * mXScale; + y = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale + mYTranslate; + left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale; + right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale; + bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; + top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; + orientation -= M_PI; + if (mOrientedRanges.haveOrientation && + orientation < mOrientedRanges.orientation.min) { + orientation += + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + case DISPLAY_ORIENTATION_270: + x = float(mRawPointerAxes.y.maxValue - yTransformed) * mYScale; + y = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale; + right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale; + bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + orientation += M_PI_2; + if (mOrientedRanges.haveOrientation && + orientation > mOrientedRanges.orientation.max) { + orientation -= + (mOrientedRanges.orientation.max - mOrientedRanges.orientation.min); + } + break; + default: + x = float(xTransformed - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + y = float(yTransformed - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + break; + } + + // Write output coords. + PointerCoords& out = mCurrentCookedState.cookedPointerData.pointerCoords[i]; + out.clear(); + out.setAxisValue(AMOTION_EVENT_AXIS_X, x); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + out.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); + out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); + out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt); + out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance); + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom); + } else { + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); + } + + // Write output properties. + PointerProperties& properties = mCurrentCookedState.cookedPointerData.pointerProperties[i]; + uint32_t id = in.id; + properties.clear(); + properties.id = id; + properties.toolType = in.toolType; + + // Write id index. + mCurrentCookedState.cookedPointerData.idToIndex[id] = i; + } +} + +void TouchInputMapper::dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, + PointerUsage pointerUsage) { + if (pointerUsage != mPointerUsage) { + abortPointerUsage(when, policyFlags); + mPointerUsage = pointerUsage; + } + + switch (mPointerUsage) { + case POINTER_USAGE_GESTURES: + dispatchPointerGestures(when, policyFlags, false /*isTimeout*/); + break; + case POINTER_USAGE_STYLUS: + dispatchPointerStylus(when, policyFlags); + break; + case POINTER_USAGE_MOUSE: + dispatchPointerMouse(when, policyFlags); + break; + default: + break; + } +} + +void TouchInputMapper::abortPointerUsage(nsecs_t when, uint32_t policyFlags) { + switch (mPointerUsage) { + case POINTER_USAGE_GESTURES: + abortPointerGestures(when, policyFlags); + break; + case POINTER_USAGE_STYLUS: + abortPointerStylus(when, policyFlags); + break; + case POINTER_USAGE_MOUSE: + abortPointerMouse(when, policyFlags); + break; + default: + break; + } + + mPointerUsage = POINTER_USAGE_NONE; +} + +void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout) { + // Update current gesture coordinates. + bool cancelPreviousGesture, finishPreviousGesture; + bool sendEvents = + preparePointerGestures(when, &cancelPreviousGesture, &finishPreviousGesture, isTimeout); + if (!sendEvents) { + return; + } + if (finishPreviousGesture) { + cancelPreviousGesture = false; + } + + // Update the pointer presentation and spots. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + if (finishPreviousGesture || cancelPreviousGesture) { + mPointerController->clearSpots(); + } + + if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { + mPointerController->setSpots(mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, + mPointerController->getDisplayId()); + } + } else { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + } + + // Show or hide the pointer if needed. + switch (mPointerGesture.currentGestureMode) { + case PointerGesture::NEUTRAL: + case PointerGesture::QUIET: + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH && + mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) { + // Remind the user of where the pointer is after finishing a gesture with spots. + mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL); + } + break; + case PointerGesture::TAP: + case PointerGesture::TAP_DRAG: + case PointerGesture::BUTTON_CLICK_OR_DRAG: + case PointerGesture::HOVER: + case PointerGesture::PRESS: + case PointerGesture::SWIPE: + // Unfade the pointer when the current gesture manipulates the + // area directly under the pointer. + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + break; + case PointerGesture::FREEFORM: + // Fade the pointer when the current gesture manipulates a different + // area and there are spots to guide the user experience. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } else { + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } + break; + } + + // Send events! + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentCookedState.buttonState; + + // Update last coordinates of pointers that have moved so that we observe the new + // pointer positions at the same time as other pointers that have just gone up. + bool down = mPointerGesture.currentGestureMode == PointerGesture::TAP || + mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG || + mPointerGesture.currentGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG || + mPointerGesture.currentGestureMode == PointerGesture::PRESS || + mPointerGesture.currentGestureMode == PointerGesture::SWIPE || + mPointerGesture.currentGestureMode == PointerGesture::FREEFORM; + bool moveNeeded = false; + if (down && !cancelPreviousGesture && !finishPreviousGesture && + !mPointerGesture.lastGestureIdBits.isEmpty() && + !mPointerGesture.currentGestureIdBits.isEmpty()) { + BitSet32 movedGestureIdBits(mPointerGesture.currentGestureIdBits.value & + mPointerGesture.lastGestureIdBits.value); + moveNeeded = updateMovedPointers(mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, movedGestureIdBits); + if (buttonState != mLastCookedState.buttonState) { + moveNeeded = true; + } + } + + // Send motion events for all pointers that went up or were canceled. + BitSet32 dispatchedGestureIdBits(mPointerGesture.lastGestureIdBits); + if (!dispatchedGestureIdBits.isEmpty()) { + if (cancelPreviousGesture) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, + mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, + mPointerGesture.downTime); + + dispatchedGestureIdBits.clear(); + } else { + BitSet32 upGestureIdBits; + if (finishPreviousGesture) { + upGestureIdBits = dispatchedGestureIdBits; + } else { + upGestureIdBits.value = + dispatchedGestureIdBits.value & ~mPointerGesture.currentGestureIdBits.value; + } + while (!upGestureIdBits.isEmpty()) { + uint32_t id = upGestureIdBits.clearFirstMarkedBit(); + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, + metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0, + 0, mPointerGesture.downTime); + + dispatchedGestureIdBits.clearBit(id); + } + } + } + + // Send motion events for all pointers that moved. + if (moveNeeded) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, + mPointerGesture.downTime); + } + + // Send motion events for all pointers that went down. + if (down) { + BitSet32 downGestureIdBits(mPointerGesture.currentGestureIdBits.value & + ~dispatchedGestureIdBits.value); + while (!downGestureIdBits.isEmpty()) { + uint32_t id = downGestureIdBits.clearFirstMarkedBit(); + dispatchedGestureIdBits.markBit(id); + + if (dispatchedGestureIdBits.count() == 1) { + mPointerGesture.downTime = when; + } + + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, + metaState, buttonState, 0, + /* deviceTimestamp */ 0, mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0, + 0, mPointerGesture.downTime); + } + } + + // Send motion events for hover. + if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, + mPointerGesture.currentGestureProperties, + mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, -1, 0, 0, mPointerGesture.downTime); + } else if (dispatchedGestureIdBits.isEmpty() && !mPointerGesture.lastGestureIdBits.isEmpty()) { + // Synthesize a hover move event after all pointers go up to indicate that + // the pointer is hovering again even if the user is not currently touching + // the touch pad. This ensures that a view will receive a fresh hover enter + // event after a tap. + float x, y; + mPointerController->getPosition(&x, &y); + + PointerProperties pointerProperties; + pointerProperties.clear(); + pointerProperties.id = 0; + pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + PointerCoords pointerCoords; + pointerCoords.clear(); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + + const int32_t displayId = mPointerController->getDisplayId(); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, + mPointerGesture.downTime, /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Update state. + mPointerGesture.lastGestureMode = mPointerGesture.currentGestureMode; + if (!down) { + mPointerGesture.lastGestureIdBits.clear(); + } else { + mPointerGesture.lastGestureIdBits = mPointerGesture.currentGestureIdBits; + for (BitSet32 idBits(mPointerGesture.currentGestureIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + mPointerGesture.lastGestureProperties[index].copyFrom( + mPointerGesture.currentGestureProperties[index]); + mPointerGesture.lastGestureCoords[index].copyFrom( + mPointerGesture.currentGestureCoords[index]); + mPointerGesture.lastGestureIdToIndex[id] = index; + } + } +} + +void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) { + // Cancel previously dispatches pointers. + if (!mPointerGesture.lastGestureIdBits.isEmpty()) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t buttonState = mCurrentRawState.buttonState; + dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, + mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, + mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1, + 0, 0, mPointerGesture.downTime); + } + + // Reset the current pointer gesture. + mPointerGesture.reset(); + mPointerVelocityControl.reset(); + + // Remove any current spots. + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->clearSpots(); + } +} + +bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, + bool* outFinishPreviousGesture, bool isTimeout) { + *outCancelPreviousGesture = false; + *outFinishPreviousGesture = false; + + // Handle TAP timeout. + if (isTimeout) { +#if DEBUG_GESTURES + ALOGD("Gestures: Processing timeout"); +#endif + + if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { + if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { + // The tap/drag timeout has not yet expired. + getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime + + mConfig.pointerGestureTapDragInterval); + } else { + // The tap is finished. +#if DEBUG_GESTURES + ALOGD("Gestures: TAP finished"); +#endif + *outFinishPreviousGesture = true; + + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; + mPointerGesture.currentGestureIdBits.clear(); + + mPointerVelocityControl.reset(); + return true; + } + } + + // We did not handle this timeout. + return false; + } + + const uint32_t currentFingerCount = mCurrentCookedState.fingerIdBits.count(); + const uint32_t lastFingerCount = mLastCookedState.fingerIdBits.count(); + + // Update the velocity tracker. + { + VelocityTracker::Position positions[MAX_POINTERS]; + uint32_t count = 0; + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty(); count++) { + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(id); + positions[count].x = pointer.x * mPointerXMovementScale; + positions[count].y = pointer.y * mPointerYMovementScale; + } + mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits, + positions); + } + + // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning + // to NEUTRAL, then we should not generate tap event. + if (mPointerGesture.lastGestureMode != PointerGesture::HOVER && + mPointerGesture.lastGestureMode != PointerGesture::TAP && + mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) { + mPointerGesture.resetTap(); + } + + // Pick a new active touch id if needed. + // Choose an arbitrary pointer that just went down, if there is one. + // Otherwise choose an arbitrary remaining pointer. + // This guarantees we always have an active touch id when there is at least one pointer. + // We keep the same active touch id for as long as possible. + int32_t lastActiveTouchId = mPointerGesture.activeTouchId; + int32_t activeTouchId = lastActiveTouchId; + if (activeTouchId < 0) { + if (!mCurrentCookedState.fingerIdBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = + mCurrentCookedState.fingerIdBits.firstMarkedBit(); + mPointerGesture.firstTouchTime = when; + } + } else if (!mCurrentCookedState.fingerIdBits.hasBit(activeTouchId)) { + if (!mCurrentCookedState.fingerIdBits.isEmpty()) { + activeTouchId = mPointerGesture.activeTouchId = + mCurrentCookedState.fingerIdBits.firstMarkedBit(); + } else { + activeTouchId = mPointerGesture.activeTouchId = -1; + } + } + + // Determine whether we are in quiet time. + bool isQuietTime = false; + if (activeTouchId < 0) { + mPointerGesture.resetQuietTime(); + } else { + isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval; + if (!isQuietTime) { + if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS || + mPointerGesture.lastGestureMode == PointerGesture::SWIPE || + mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) && + currentFingerCount < 2) { + // Enter quiet time when exiting swipe or freeform state. + // This is to prevent accidentally entering the hover state and flinging the + // pointer when finishing a swipe and there is still one pointer left onscreen. + isQuietTime = true; + } else if (mPointerGesture.lastGestureMode == PointerGesture::BUTTON_CLICK_OR_DRAG && + currentFingerCount >= 2 && !isPointerDown(mCurrentRawState.buttonState)) { + // Enter quiet time when releasing the button and there are still two or more + // fingers down. This may indicate that one finger was used to press the button + // but it has not gone up yet. + isQuietTime = true; + } + if (isQuietTime) { + mPointerGesture.quietTime = when; + } + } + } + + // Switch states based on button and pointer state. + if (isQuietTime) { + // Case 1: Quiet time. (QUIET) +#if DEBUG_GESTURES + ALOGD("Gestures: QUIET for next %0.3fms", + (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f); +#endif + if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) { + *outFinishPreviousGesture = true; + } + + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::QUIET; + mPointerGesture.currentGestureIdBits.clear(); + + mPointerVelocityControl.reset(); + } else if (isPointerDown(mCurrentRawState.buttonState)) { + // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG) + // The pointer follows the active touch point. + // Emit DOWN, MOVE, UP events at the pointer location. + // + // Only the active touch matters; other fingers are ignored. This policy helps + // to handle the case where the user places a second finger on the touch pad + // to apply the necessary force to depress an integrated button below the surface. + // We don't want the second finger to be delivered to applications. + // + // For this to work well, we need to make sure to track the pointer that is really + // active. If the user first puts one finger down to click then adds another + // finger to drag then the active pointer should switch to the finger that is + // being dragged. +#if DEBUG_GESTURES + ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, " + "currentFingerCount=%d", + activeTouchId, currentFingerCount); +#endif + // Reset state when just starting. + if (mPointerGesture.lastGestureMode != PointerGesture::BUTTON_CLICK_OR_DRAG) { + *outFinishPreviousGesture = true; + mPointerGesture.activeGestureId = 0; + } + + // Switch pointers if needed. + // Find the fastest pointer and follow it. + if (activeTouchId >= 0 && currentFingerCount > 1) { + int32_t bestId = -1; + float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + float vx, vy; + if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) { + float speed = hypotf(vx, vy); + if (speed > bestSpeed) { + bestId = id; + bestSpeed = speed; + } + } + } + if (bestId >= 0 && bestId != activeTouchId) { + mPointerGesture.activeTouchId = activeTouchId = bestId; +#if DEBUG_GESTURES + ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, " + "bestId=%d, bestSpeed=%0.3f", + bestId, bestSpeed); +#endif + } + } + + float deltaX = 0, deltaY = 0; + if (activeTouchId >= 0 && mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { + const RawPointerData::Pointer& currentPointer = + mCurrentRawState.rawPointerData.pointerForId(activeTouchId); + const RawPointerData::Pointer& lastPointer = + mLastRawState.rawPointerData.pointerForId(activeTouchId); + deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; + deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + // Move the pointer using a relative motion. + // When using spots, the click will occur at the position of the anchor + // spot and all other spots will move there. + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureMode = PointerGesture::BUTTON_CLICK_OR_DRAG; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (currentFingerCount == 0) { + // Case 3. No fingers down and button is not pressed. (NEUTRAL) + if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) { + *outFinishPreviousGesture = true; + } + + // Watch for taps coming out of HOVER or TAP_DRAG mode. + // Checking for taps after TAP_DRAG allows us to detect double-taps. + bool tapped = false; + if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER || + mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) && + lastFingerCount == 1) { + if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { + float x, y; + mPointerController->getPosition(&x, &y); + if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && + fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { +#if DEBUG_GESTURES + ALOGD("Gestures: TAP"); +#endif + + mPointerGesture.tapUpTime = when; + getContext()->requestTimeoutAtTime(when + + mConfig.pointerGestureTapDragInterval); + + mPointerGesture.activeGestureId = 0; + mPointerGesture.currentGestureMode = PointerGesture::TAP; + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = + mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = + AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.tapX); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.tapY); + mPointerGesture.currentGestureCoords[0] + .setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + + tapped = true; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX, + y - mPointerGesture.tapY); +#endif + } + } else { +#if DEBUG_GESTURES + if (mPointerGesture.tapDownTime != LLONG_MIN) { + ALOGD("Gestures: Not a TAP, %0.3fms since down", + (when - mPointerGesture.tapDownTime) * 0.000001f); + } else { + ALOGD("Gestures: Not a TAP, incompatible mode transitions"); + } +#endif + } + } + + mPointerVelocityControl.reset(); + + if (!tapped) { +#if DEBUG_GESTURES + ALOGD("Gestures: NEUTRAL"); +#endif + mPointerGesture.activeGestureId = -1; + mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; + mPointerGesture.currentGestureIdBits.clear(); + } + } else if (currentFingerCount == 1) { + // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG) + // The pointer follows the active touch point. + // When in HOVER, emit HOVER_MOVE events at the pointer location. + // When in TAP_DRAG, emit MOVE events at the pointer location. + ALOG_ASSERT(activeTouchId >= 0); + + mPointerGesture.currentGestureMode = PointerGesture::HOVER; + if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { + if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { + float x, y; + mPointerController->getPosition(&x, &y); + if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && + fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { + mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f", + x - mPointerGesture.tapX, y - mPointerGesture.tapY); +#endif + } + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up", + (when - mPointerGesture.tapUpTime) * 0.000001f); +#endif + } + } else if (mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) { + mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; + } + + float deltaX = 0, deltaY = 0; + if (mLastCookedState.fingerIdBits.hasBit(activeTouchId)) { + const RawPointerData::Pointer& currentPointer = + mCurrentRawState.rawPointerData.pointerForId(activeTouchId); + const RawPointerData::Pointer& lastPointer = + mLastRawState.rawPointerData.pointerForId(activeTouchId); + deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale; + deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + // Move the pointer using a relative motion. + // When using spots, the hover or drag will occur at the position of the anchor spot. + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + bool down; + if (mPointerGesture.currentGestureMode == PointerGesture::TAP_DRAG) { +#if DEBUG_GESTURES + ALOGD("Gestures: TAP_DRAG"); +#endif + down = true; + } else { +#if DEBUG_GESTURES + ALOGD("Gestures: HOVER"); +#endif + if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) { + *outFinishPreviousGesture = true; + } + mPointerGesture.activeGestureId = 0; + down = false; + } + + float x, y; + mPointerController->getPosition(&x, &y); + + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + down ? 1.0f : 0.0f); + + if (lastFingerCount == 0 && currentFingerCount != 0) { + mPointerGesture.resetTap(); + mPointerGesture.tapDownTime = when; + mPointerGesture.tapX = x; + mPointerGesture.tapY = y; + } + } else { + // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) + // We need to provide feedback for each finger that goes down so we cannot wait + // for the fingers to move before deciding what to do. + // + // The ambiguous case is deciding what to do when there are two fingers down but they + // have not moved enough to determine whether they are part of a drag or part of a + // freeform gesture, or just a press or long-press at the pointer location. + // + // When there are two fingers we start with the PRESS hypothesis and we generate a + // down at the pointer location. + // + // When the two fingers move enough or when additional fingers are added, we make + // a decision to transition into SWIPE or FREEFORM mode accordingly. + ALOG_ASSERT(activeTouchId >= 0); + + bool settled = when >= + mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval; + if (mPointerGesture.lastGestureMode != PointerGesture::PRESS && + mPointerGesture.lastGestureMode != PointerGesture::SWIPE && + mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + *outFinishPreviousGesture = true; + } else if (!settled && currentFingerCount > lastFingerCount) { + // Additional pointers have gone down but not yet settled. + // Reset the gesture. +#if DEBUG_GESTURES + ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " + "settle time remaining %0.3fms", + (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - + when) * 0.000001f); +#endif + *outCancelPreviousGesture = true; + } else { + // Continue previous gesture. + mPointerGesture.currentGestureMode = mPointerGesture.lastGestureMode; + } + + if (*outFinishPreviousGesture || *outCancelPreviousGesture) { + mPointerGesture.currentGestureMode = PointerGesture::PRESS; + mPointerGesture.activeGestureId = 0; + mPointerGesture.referenceIdBits.clear(); + mPointerVelocityControl.reset(); + + // Use the centroid and pointer location as the reference points for the gesture. +#if DEBUG_GESTURES + ALOGD("Gestures: Using centroid as reference for MULTITOUCH, " + "settle time remaining %0.3fms", + (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval - + when) * 0.000001f); +#endif + mCurrentRawState.rawPointerData + .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, + &mPointerGesture.referenceTouchY); + mPointerController->getPosition(&mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); + } + + // Clear the reference deltas for fingers not yet included in the reference calculation. + for (BitSet32 idBits(mCurrentCookedState.fingerIdBits.value & + ~mPointerGesture.referenceIdBits.value); + !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + mPointerGesture.referenceDeltas[id].dx = 0; + mPointerGesture.referenceDeltas[id].dy = 0; + } + mPointerGesture.referenceIdBits = mCurrentCookedState.fingerIdBits; + + // Add delta for all fingers and calculate a common movement delta. + float commonDeltaX = 0, commonDeltaY = 0; + BitSet32 commonIdBits(mLastCookedState.fingerIdBits.value & + mCurrentCookedState.fingerIdBits.value); + for (BitSet32 idBits(commonIdBits); !idBits.isEmpty();) { + bool first = (idBits == commonIdBits); + uint32_t id = idBits.clearFirstMarkedBit(); + const RawPointerData::Pointer& cpd = mCurrentRawState.rawPointerData.pointerForId(id); + const RawPointerData::Pointer& lpd = mLastRawState.rawPointerData.pointerForId(id); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx += cpd.x - lpd.x; + delta.dy += cpd.y - lpd.y; + + if (first) { + commonDeltaX = delta.dx; + commonDeltaY = delta.dy; + } else { + commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); + commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); + } + } + + // Consider transitions from PRESS to SWIPE or MULTITOUCH. + if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { + float dist[MAX_POINTER_ID + 1]; + int32_t distOverThreshold = 0; + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + dist[id] = hypotf(delta.dx * mPointerXZoomScale, delta.dy * mPointerYZoomScale); + if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) { + distOverThreshold += 1; + } + } + + // Only transition when at least two pointers have moved further than + // the minimum distance threshold. + if (distOverThreshold >= 2) { + if (currentFingerCount > 2) { + // There are more than two pointers, switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", + currentFingerCount); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else { + // There are exactly two pointers. + BitSet32 idBits(mCurrentCookedState.fingerIdBits); + uint32_t id1 = idBits.clearFirstMarkedBit(); + uint32_t id2 = idBits.firstMarkedBit(); + const RawPointerData::Pointer& p1 = + mCurrentRawState.rawPointerData.pointerForId(id1); + const RawPointerData::Pointer& p2 = + mCurrentRawState.rawPointerData.pointerForId(id2); + float mutualDistance = distance(p1.x, p1.y, p2.x, p2.y); + if (mutualDistance > mPointerGestureMaxSwipeWidth) { + // There are two pointers but they are too far apart for a SWIPE, + // switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", + mutualDistance, mPointerGestureMaxSwipeWidth); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else { + // There are two pointers. Wait for both pointers to start moving + // before deciding whether this is a SWIPE or FREEFORM gesture. + float dist1 = dist[id1]; + float dist2 = dist[id2]; + if (dist1 >= mConfig.pointerGestureMultitouchMinDistance && + dist2 >= mConfig.pointerGestureMultitouchMinDistance) { + // Calculate the dot product of the displacement vectors. + // When the vectors are oriented in approximately the same direction, + // the angle betweeen them is near zero and the cosine of the angle + // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * + // mag(v2). + PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; + PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; + float dx1 = delta1.dx * mPointerXZoomScale; + float dy1 = delta1.dy * mPointerYZoomScale; + float dx2 = delta2.dx * mPointerXZoomScale; + float dy2 = delta2.dy * mPointerYZoomScale; + float dot = dx1 * dx2 + dy1 * dy2; + float cosine = dot / (dist1 * dist2); // denominator always > 0 + if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) { + // Pointers are moving in the same direction. Switch to SWIPE. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to SWIPE, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f >= %0.3f", + dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, + mConfig.pointerGestureMultitouchMinDistance, cosine, + mConfig.pointerGestureSwipeTransitionAngleCosine); +#endif + mPointerGesture.currentGestureMode = PointerGesture::SWIPE; + } else { + // Pointers are moving in different directions. Switch to FREEFORM. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS transitioned to FREEFORM, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f < %0.3f", + dist1, mConfig.pointerGestureMultitouchMinDistance, dist2, + mConfig.pointerGestureMultitouchMinDistance, cosine, + mConfig.pointerGestureSwipeTransitionAngleCosine); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } + } + } + } + } + } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // Switch from SWIPE to FREEFORM if additional pointers go down. + // Cancel previous gesture. + if (currentFingerCount > 2) { +#if DEBUG_GESTURES + ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2", + currentFingerCount); +#endif + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } + } + + // Move the reference points based on the overall group motion of the fingers + // except in PRESS mode while waiting for a transition to occur. + if (mPointerGesture.currentGestureMode != PointerGesture::PRESS && + (commonDeltaX || commonDeltaY)) { + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx = 0; + delta.dy = 0; + } + + mPointerGesture.referenceTouchX += commonDeltaX; + mPointerGesture.referenceTouchY += commonDeltaY; + + commonDeltaX *= mPointerXMovementScale; + commonDeltaY *= mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY); + mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); + + mPointerGesture.referenceGestureX += commonDeltaX; + mPointerGesture.referenceGestureY += commonDeltaY; + } + + // Report gestures. + if (mPointerGesture.currentGestureMode == PointerGesture::PRESS || + mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { + // PRESS or SWIPE mode. +#if DEBUG_GESTURES + ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); +#endif + ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); + + mPointerGesture.currentGestureIdBits.clear(); + mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0; + mPointerGesture.currentGestureProperties[0].clear(); + mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId; + mPointerGesture.currentGestureProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[0].clear(); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY); + mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); + } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { + // FREEFORM mode. +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM activeTouchId=%d," + "activeGestureId=%d, currentTouchPointerCount=%d", + activeTouchId, mPointerGesture.activeGestureId, currentFingerCount); +#endif + ALOG_ASSERT(mPointerGesture.activeGestureId >= 0); + + mPointerGesture.currentGestureIdBits.clear(); + + BitSet32 mappedTouchIdBits; + BitSet32 usedGestureIdBits; + if (mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { + // Initially, assign the active gesture id to the active touch point + // if there is one. No other touch id bits are mapped yet. + if (!*outCancelPreviousGesture) { + mappedTouchIdBits.markBit(activeTouchId); + usedGestureIdBits.markBit(mPointerGesture.activeGestureId); + mPointerGesture.freeformTouchToGestureIdMap[activeTouchId] = + mPointerGesture.activeGestureId; + } else { + mPointerGesture.activeGestureId = -1; + } + } else { + // Otherwise, assume we mapped all touches from the previous frame. + // Reuse all mappings that are still applicable. + mappedTouchIdBits.value = mLastCookedState.fingerIdBits.value & + mCurrentCookedState.fingerIdBits.value; + usedGestureIdBits = mPointerGesture.lastGestureIdBits; + + // Check whether we need to choose a new active gesture id because the + // current went went up. + for (BitSet32 upTouchIdBits(mLastCookedState.fingerIdBits.value & + ~mCurrentCookedState.fingerIdBits.value); + !upTouchIdBits.isEmpty();) { + uint32_t upTouchId = upTouchIdBits.clearFirstMarkedBit(); + uint32_t upGestureId = mPointerGesture.freeformTouchToGestureIdMap[upTouchId]; + if (upGestureId == uint32_t(mPointerGesture.activeGestureId)) { + mPointerGesture.activeGestureId = -1; + break; + } + } + } + +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM follow up " + "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, " + "activeGestureId=%d", + mappedTouchIdBits.value, usedGestureIdBits.value, + mPointerGesture.activeGestureId); +#endif + + BitSet32 idBits(mCurrentCookedState.fingerIdBits); + for (uint32_t i = 0; i < currentFingerCount; i++) { + uint32_t touchId = idBits.clearFirstMarkedBit(); + uint32_t gestureId; + if (!mappedTouchIdBits.hasBit(touchId)) { + gestureId = usedGestureIdBits.markFirstUnmarkedBit(); + mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId; +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM " + "new mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } else { + gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId]; +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM " + "existing mapping for touch id %d -> gesture id %d", + touchId, gestureId); +#endif + } + mPointerGesture.currentGestureIdBits.markBit(gestureId); + mPointerGesture.currentGestureIdToIndex[gestureId] = i; + + const RawPointerData::Pointer& pointer = + mCurrentRawState.rawPointerData.pointerForId(touchId); + float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale; + float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale; + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + + mPointerGesture.currentGestureProperties[i].clear(); + mPointerGesture.currentGestureProperties[i].id = gestureId; + mPointerGesture.currentGestureProperties[i].toolType = + AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerGesture.currentGestureCoords[i].clear(); + mPointerGesture.currentGestureCoords[i] + .setAxisValue(AMOTION_EVENT_AXIS_X, + mPointerGesture.referenceGestureX + deltaX); + mPointerGesture.currentGestureCoords[i] + .setAxisValue(AMOTION_EVENT_AXIS_Y, + mPointerGesture.referenceGestureY + deltaY); + mPointerGesture.currentGestureCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + 1.0f); + } + + if (mPointerGesture.activeGestureId < 0) { + mPointerGesture.activeGestureId = + mPointerGesture.currentGestureIdBits.firstMarkedBit(); +#if DEBUG_GESTURES + ALOGD("Gestures: FREEFORM new " + "activeGestureId=%d", + mPointerGesture.activeGestureId); +#endif + } + } + } + + mPointerController->setButtonState(mCurrentRawState.buttonState); + +#if DEBUG_GESTURES + ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, " + "currentGestureMode=%d, currentGestureIdBits=0x%08x, " + "lastGestureMode=%d, lastGestureIdBits=0x%08x", + toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture), + mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value, + mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value); + for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.currentGestureIdToIndex[id]; + const PointerProperties& properties = mPointerGesture.currentGestureProperties[index]; + const PointerCoords& coords = mPointerGesture.currentGestureCoords[index]; + ALOGD(" currentGesture[%d]: index=%d, toolType=%d, " + "x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } + for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = mPointerGesture.lastGestureIdToIndex[id]; + const PointerProperties& properties = mPointerGesture.lastGestureProperties[index]; + const PointerCoords& coords = mPointerGesture.lastGestureCoords[index]; + ALOGD(" lastGesture[%d]: index=%d, toolType=%d, " + "x=%0.3f, y=%0.3f, pressure=%0.3f", + id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_Y), + coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + } +#endif + return true; +} + +void TouchInputMapper::dispatchPointerStylus(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + bool down, hovering; + if (!mCurrentCookedState.stylusIdBits.isEmpty()) { + uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); + uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; + float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(); + float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY(); + mPointerController->setPosition(x, y); + + hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); + down = !hovering; + + mPointerController->getPosition(&x, &y); + mPointerSimple.currentCoords.copyFrom( + mCurrentCookedState.cookedPointerData.pointerCoords[index]); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerSimple.currentProperties.id = 0; + mPointerSimple.currentProperties.toolType = + mCurrentCookedState.cookedPointerData.pointerProperties[index].toolType; + } else { + down = false; + hovering = false; + } + + dispatchPointerSimple(when, policyFlags, down, hovering); +} + +void TouchInputMapper::abortPointerStylus(nsecs_t when, uint32_t policyFlags) { + abortPointerSimple(when, policyFlags); +} + +void TouchInputMapper::dispatchPointerMouse(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + bool down, hovering; + if (!mCurrentCookedState.mouseIdBits.isEmpty()) { + uint32_t id = mCurrentCookedState.mouseIdBits.firstMarkedBit(); + uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id]; + float deltaX = 0, deltaY = 0; + if (mLastCookedState.mouseIdBits.hasBit(id)) { + uint32_t lastIndex = mCurrentRawState.rawPointerData.idToIndex[id]; + deltaX = (mCurrentRawState.rawPointerData.pointers[currentIndex].x - + mLastRawState.rawPointerData.pointers[lastIndex].x) * + mPointerXMovementScale; + deltaY = (mCurrentRawState.rawPointerData.pointers[currentIndex].y - + mLastRawState.rawPointerData.pointers[lastIndex].y) * + mPointerYMovementScale; + + rotateDelta(mSurfaceOrientation, &deltaX, &deltaY); + mPointerVelocityControl.move(when, &deltaX, &deltaY); + + mPointerController->move(deltaX, deltaY); + } else { + mPointerVelocityControl.reset(); + } + + down = isPointerDown(mCurrentRawState.buttonState); + hovering = !down; + + float x, y; + mPointerController->getPosition(&x, &y); + mPointerSimple.currentCoords.copyFrom( + mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, + hovering ? 0.0f : 1.0f); + mPointerSimple.currentProperties.id = 0; + mPointerSimple.currentProperties.toolType = + mCurrentCookedState.cookedPointerData.pointerProperties[currentIndex].toolType; + } else { + mPointerVelocityControl.reset(); + + down = false; + hovering = false; + } + + dispatchPointerSimple(when, policyFlags, down, hovering); +} + +void TouchInputMapper::abortPointerMouse(nsecs_t when, uint32_t policyFlags) { + abortPointerSimple(when, policyFlags); + + mPointerVelocityControl.reset(); +} + +void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, + bool hovering) { + int32_t metaState = getContext()->getGlobalMetaState(); + int32_t displayId = mViewport.displayId; + + if (mPointerController != nullptr) { + if (down || hovering) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->clearSpots(); + mPointerController->setButtonState(mCurrentRawState.buttonState); + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } + displayId = mPointerController->getDisplayId(); + } + + if (mPointerSimple.down && !down) { + mPointerSimple.down = false; + + // Send up. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, + mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (mPointerSimple.hovering && !hovering) { + mPointerSimple.hovering = false; + + // Send hover exit. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, + metaState, mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, + mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (down) { + if (!mPointerSimple.down) { + mPointerSimple.down = true; + mPointerSimple.downTime = when; + + // Send down. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Send move. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (hovering) { + if (!mPointerSimple.hovering) { + mPointerSimple.hovering = true; + + // Send hover enter. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, + metaState, mCurrentRawState.buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, + /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Send hover move. + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, + metaState, mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { + float vscroll = mCurrentRawState.rawVScroll; + float hscroll = mCurrentRawState.rawHScroll; + mWheelYVelocityControl.move(when, nullptr, &vscroll); + mWheelXVelocityControl.move(when, &hscroll, nullptr); + + // Send scroll. + PointerCoords pointerCoords; + pointerCoords.copyFrom(mPointerSimple.currentCoords); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); + + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, + displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, + &mPointerSimple.currentProperties, &pointerCoords, + mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime, + /* videoFrames */ {}); + getListener()->notifyMotion(&args); + } + + // Save state. + if (down || hovering) { + mPointerSimple.lastCoords.copyFrom(mPointerSimple.currentCoords); + mPointerSimple.lastProperties.copyFrom(mPointerSimple.currentProperties); + } else { + mPointerSimple.reset(); + } +} + +void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) { + mPointerSimple.currentCoords.clear(); + mPointerSimple.currentProperties.clear(); + + dispatchPointerSimple(when, policyFlags, false, false); +} + +void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, + int32_t action, int32_t actionButton, int32_t flags, + int32_t metaState, int32_t buttonState, int32_t edgeFlags, + uint32_t deviceTimestamp, const PointerProperties* properties, + const PointerCoords* coords, const uint32_t* idToIndex, + BitSet32 idBits, int32_t changedId, float xPrecision, + float yPrecision, nsecs_t downTime) { + PointerCoords pointerCoords[MAX_POINTERS]; + PointerProperties pointerProperties[MAX_POINTERS]; + uint32_t pointerCount = 0; + while (!idBits.isEmpty()) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t index = idToIndex[id]; + pointerProperties[pointerCount].copyFrom(properties[index]); + pointerCoords[pointerCount].copyFrom(coords[index]); + + if (changedId >= 0 && id == uint32_t(changedId)) { + action |= pointerCount << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + } + + pointerCount += 1; + } + + ALOG_ASSERT(pointerCount != 0); + + if (changedId >= 0 && pointerCount == 1) { + // Replace initial down and final up action. + // We can compare the action without masking off the changed pointer index + // because we know the index is 0. + if (action == AMOTION_EVENT_ACTION_POINTER_DOWN) { + action = AMOTION_EVENT_ACTION_DOWN; + } else if (action == AMOTION_EVENT_ACTION_POINTER_UP) { + action = AMOTION_EVENT_ACTION_UP; + } else { + // Can't happen. + ALOG_ASSERT(false); + } + } + const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE); + const int32_t deviceId = getDeviceId(); + std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId); + std::for_each(frames.begin(), frames.end(), + [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); + NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, + policyFlags, action, actionButton, flags, metaState, buttonState, + MotionClassification::NONE, edgeFlags, deviceTimestamp, pointerCount, + pointerProperties, pointerCoords, xPrecision, yPrecision, downTime, + std::move(frames)); + getListener()->notifyMotion(&args); +} + +bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, + const PointerCoords* inCoords, + const uint32_t* inIdToIndex, + PointerProperties* outProperties, + PointerCoords* outCoords, const uint32_t* outIdToIndex, + BitSet32 idBits) const { + bool changed = false; + while (!idBits.isEmpty()) { + uint32_t id = idBits.clearFirstMarkedBit(); + uint32_t inIndex = inIdToIndex[id]; + uint32_t outIndex = outIdToIndex[id]; + + const PointerProperties& curInProperties = inProperties[inIndex]; + const PointerCoords& curInCoords = inCoords[inIndex]; + PointerProperties& curOutProperties = outProperties[outIndex]; + PointerCoords& curOutCoords = outCoords[outIndex]; + + if (curInProperties != curOutProperties) { + curOutProperties.copyFrom(curInProperties); + changed = true; + } + + if (curInCoords != curOutCoords) { + curOutCoords.copyFrom(curInCoords); + changed = true; + } + } + return changed; +} + +void TouchInputMapper::fadePointer() { + if (mPointerController != nullptr) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + } +} + +void TouchInputMapper::cancelTouch(nsecs_t when) { + abortPointerUsage(when, 0 /*policyFlags*/); + abortTouches(when, 0 /* policyFlags*/); +} + +bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { + const float scaledX = x * mXScale; + const float scaledY = y * mYScale; + return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && + scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth && + y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && + scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight; +} + +const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { + for (const VirtualKey& virtualKey : mVirtualKeys) { +#if DEBUG_VIRTUAL_KEYS + ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " + "left=%d, top=%d, right=%d, bottom=%d", + x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, virtualKey.hitTop, + virtualKey.hitRight, virtualKey.hitBottom); +#endif + + if (virtualKey.isHit(x, y)) { + return &virtualKey; + } + } + + return nullptr; +} + +void TouchInputMapper::assignPointerIds(const RawState* last, RawState* current) { + uint32_t currentPointerCount = current->rawPointerData.pointerCount; + uint32_t lastPointerCount = last->rawPointerData.pointerCount; + + current->rawPointerData.clearIdBits(); + + if (currentPointerCount == 0) { + // No pointers to assign. + return; + } + + if (lastPointerCount == 0) { + // All pointers are new. + for (uint32_t i = 0; i < currentPointerCount; i++) { + uint32_t id = i; + current->rawPointerData.pointers[i].id = id; + current->rawPointerData.idToIndex[id] = i; + current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(i)); + } + return; + } + + if (currentPointerCount == 1 && lastPointerCount == 1 && + current->rawPointerData.pointers[0].toolType == last->rawPointerData.pointers[0].toolType) { + // Only one pointer and no change in count so it must have the same id as before. + uint32_t id = last->rawPointerData.pointers[0].id; + current->rawPointerData.pointers[0].id = id; + current->rawPointerData.idToIndex[id] = 0; + current->rawPointerData.markIdBit(id, current->rawPointerData.isHovering(0)); + return; + } + + // General case. + // We build a heap of squared euclidean distances between current and last pointers + // associated with the current and last pointer indices. Then, we find the best + // match (by distance) for each current pointer. + // The pointers must have the same tool type but it is possible for them to + // transition from hovering to touching or vice-versa while retaining the same id. + PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; + + uint32_t heapSize = 0; + for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; + currentPointerIndex++) { + for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; + lastPointerIndex++) { + const RawPointerData::Pointer& currentPointer = + current->rawPointerData.pointers[currentPointerIndex]; + const RawPointerData::Pointer& lastPointer = + last->rawPointerData.pointers[lastPointerIndex]; + if (currentPointer.toolType == lastPointer.toolType) { + int64_t deltaX = currentPointer.x - lastPointer.x; + int64_t deltaY = currentPointer.y - lastPointer.y; + + uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); + + // Insert new element into the heap (sift up). + heap[heapSize].currentPointerIndex = currentPointerIndex; + heap[heapSize].lastPointerIndex = lastPointerIndex; + heap[heapSize].distance = distance; + heapSize += 1; + } + } + } + + // Heapify + for (uint32_t startIndex = heapSize / 2; startIndex != 0;) { + startIndex -= 1; + for (uint32_t parentIndex = startIndex;;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } + + if (childIndex + 1 < heapSize && + heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + } + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i, + heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance); + } +#endif + + // Pull matches out by increasing order of distance. + // To avoid reassigning pointers that have already been matched, the loop keeps track + // of which last and current pointers have been matched using the matchedXXXBits variables. + // It also tracks the used pointer id bits. + BitSet32 matchedLastBits(0); + BitSet32 matchedCurrentBits(0); + BitSet32 usedIdBits(0); + bool first = true; + for (uint32_t i = min(currentPointerCount, lastPointerCount); heapSize > 0 && i > 0; i--) { + while (heapSize > 0) { + if (first) { + // The first time through the loop, we just consume the root element of + // the heap (the one with smallest distance). + first = false; + } else { + // Previous iterations consumed the root element of the heap. + // Pop root element off of the heap (sift down). + heap[0] = heap[heapSize]; + for (uint32_t parentIndex = 0;;) { + uint32_t childIndex = parentIndex * 2 + 1; + if (childIndex >= heapSize) { + break; + } + + if (childIndex + 1 < heapSize && + heap[childIndex + 1].distance < heap[childIndex].distance) { + childIndex += 1; + } + + if (heap[parentIndex].distance <= heap[childIndex].distance) { + break; + } + + swap(heap[parentIndex], heap[childIndex]); + parentIndex = childIndex; + } + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize); + for (size_t i = 0; i < heapSize; i++) { + ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i, + heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance); + } +#endif + } + + heapSize -= 1; + + uint32_t currentPointerIndex = heap[0].currentPointerIndex; + if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched + + uint32_t lastPointerIndex = heap[0].lastPointerIndex; + if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched + + matchedCurrentBits.markBit(currentPointerIndex); + matchedLastBits.markBit(lastPointerIndex); + + uint32_t id = last->rawPointerData.pointers[lastPointerIndex].id; + current->rawPointerData.pointers[currentPointerIndex].id = id; + current->rawPointerData.idToIndex[id] = currentPointerIndex; + current->rawPointerData.markIdBit(id, + current->rawPointerData.isHovering( + currentPointerIndex)); + usedIdBits.markBit(id); + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32 + ", distance=%" PRIu64, + lastPointerIndex, currentPointerIndex, id, heap[0].distance); +#endif + break; + } + } + + // Assign fresh ids to pointers that were not matched in the process. + for (uint32_t i = currentPointerCount - matchedCurrentBits.count(); i != 0; i--) { + uint32_t currentPointerIndex = matchedCurrentBits.markFirstUnmarkedBit(); + uint32_t id = usedIdBits.markFirstUnmarkedBit(); + + current->rawPointerData.pointers[currentPointerIndex].id = id; + current->rawPointerData.idToIndex[id] = currentPointerIndex; + current->rawPointerData.markIdBit(id, + current->rawPointerData.isHovering(currentPointerIndex)); + +#if DEBUG_POINTER_ASSIGNMENT + ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id); +#endif + } +} + +int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { + if (mCurrentVirtualKey.down && mCurrentVirtualKey.keyCode == keyCode) { + return AKEY_STATE_VIRTUAL; + } + + for (const VirtualKey& virtualKey : mVirtualKeys) { + if (virtualKey.keyCode == keyCode) { + return AKEY_STATE_UP; + } + } + + return AKEY_STATE_UNKNOWN; +} + +int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + if (mCurrentVirtualKey.down && mCurrentVirtualKey.scanCode == scanCode) { + return AKEY_STATE_VIRTUAL; + } + + for (const VirtualKey& virtualKey : mVirtualKeys) { + if (virtualKey.scanCode == scanCode) { + return AKEY_STATE_UP; + } + } + + return AKEY_STATE_UNKNOWN; +} + +bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags) { + for (const VirtualKey& virtualKey : mVirtualKeys) { + for (size_t i = 0; i < numCodes; i++) { + if (virtualKey.keyCode == keyCodes[i]) { + outFlags[i] = 1; + } + } + } + + return true; +} + +std::optional<int32_t> TouchInputMapper::getAssociatedDisplay() { + if (mParameters.hasAssociatedDisplay) { + if (mDeviceMode == DEVICE_MODE_POINTER) { + return std::make_optional(mPointerController->getDisplayId()); + } else { + return std::make_optional(mViewport.displayId); + } + } + return std::nullopt; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h new file mode 100644 index 0000000000..d14812aecd --- /dev/null +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -0,0 +1,838 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H +#define _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H + +#include "CursorButtonAccumulator.h" +#include "CursorScrollAccumulator.h" +#include "EventHub.h" +#include "InputMapper.h" +#include "InputReaderBase.h" +#include "TouchButtonAccumulator.h" + +#include <stdint.h> + +namespace android { + +/** + * Basic statistics information. + * Keep track of min, max, average, and standard deviation of the received samples. + * Used to report latency information about input events. + */ +struct LatencyStatistics { + float min; + float max; + // Sum of all samples + float sum; + // Sum of squares of all samples + float sum2; + // The number of samples + size_t count; + // The last time statistics were reported. + nsecs_t lastReportTime; + + LatencyStatistics() { reset(systemTime(SYSTEM_TIME_MONOTONIC)); } + + inline void addValue(float x) { + if (x < min) { + min = x; + } + if (x > max) { + max = x; + } + sum += x; + sum2 += x * x; + count++; + } + + // Get the average value. Should not be called if no samples have been added. + inline float mean() { + if (count == 0) { + return 0; + } + return sum / count; + } + + // Get the standard deviation. Should not be called if no samples have been added. + inline float stdev() { + if (count == 0) { + return 0; + } + float average = mean(); + return sqrt(sum2 / count - average * average); + } + + /** + * Reset internal state. The variable 'when' is the time when the data collection started. + * Call this to start a new data collection window. + */ + inline void reset(nsecs_t when) { + max = 0; + min = std::numeric_limits<float>::max(); + sum = 0; + sum2 = 0; + count = 0; + lastReportTime = when; + } +}; + +/* Raw axis information from the driver. */ +struct RawPointerAxes { + RawAbsoluteAxisInfo x; + RawAbsoluteAxisInfo y; + RawAbsoluteAxisInfo pressure; + RawAbsoluteAxisInfo touchMajor; + RawAbsoluteAxisInfo touchMinor; + RawAbsoluteAxisInfo toolMajor; + RawAbsoluteAxisInfo toolMinor; + RawAbsoluteAxisInfo orientation; + RawAbsoluteAxisInfo distance; + RawAbsoluteAxisInfo tiltX; + RawAbsoluteAxisInfo tiltY; + RawAbsoluteAxisInfo trackingId; + RawAbsoluteAxisInfo slot; + + RawPointerAxes(); + inline int32_t getRawWidth() const { return x.maxValue - x.minValue + 1; } + inline int32_t getRawHeight() const { return y.maxValue - y.minValue + 1; } + void clear(); +}; + +/* Raw data for a collection of pointers including a pointer id mapping table. */ +struct RawPointerData { + struct Pointer { + uint32_t id; + int32_t x; + int32_t y; + int32_t pressure; + int32_t touchMajor; + int32_t touchMinor; + int32_t toolMajor; + int32_t toolMinor; + int32_t orientation; + int32_t distance; + int32_t tiltX; + int32_t tiltY; + int32_t toolType; // a fully decoded AMOTION_EVENT_TOOL_TYPE constant + bool isHovering; + }; + + uint32_t pointerCount; + Pointer pointers[MAX_POINTERS]; + BitSet32 hoveringIdBits, touchingIdBits; + uint32_t idToIndex[MAX_POINTER_ID + 1]; + + RawPointerData(); + void clear(); + void copyFrom(const RawPointerData& other); + void getCentroidOfTouchingPointers(float* outX, float* outY) const; + + inline void markIdBit(uint32_t id, bool isHovering) { + if (isHovering) { + hoveringIdBits.markBit(id); + } else { + touchingIdBits.markBit(id); + } + } + + inline void clearIdBits() { + hoveringIdBits.clear(); + touchingIdBits.clear(); + } + + inline const Pointer& pointerForId(uint32_t id) const { return pointers[idToIndex[id]]; } + + inline bool isHovering(uint32_t pointerIndex) { return pointers[pointerIndex].isHovering; } +}; + +/* Cooked data for a collection of pointers including a pointer id mapping table. */ +struct CookedPointerData { + uint32_t pointerCount; + PointerProperties pointerProperties[MAX_POINTERS]; + PointerCoords pointerCoords[MAX_POINTERS]; + BitSet32 hoveringIdBits, touchingIdBits; + uint32_t idToIndex[MAX_POINTER_ID + 1]; + + CookedPointerData(); + void clear(); + void copyFrom(const CookedPointerData& other); + + inline const PointerCoords& pointerCoordsForId(uint32_t id) const { + return pointerCoords[idToIndex[id]]; + } + + inline PointerCoords& editPointerCoordsWithId(uint32_t id) { + return pointerCoords[idToIndex[id]]; + } + + inline PointerProperties& editPointerPropertiesWithId(uint32_t id) { + return pointerProperties[idToIndex[id]]; + } + + inline bool isHovering(uint32_t pointerIndex) const { + return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id); + } + + inline bool isTouching(uint32_t pointerIndex) const { + return touchingIdBits.hasBit(pointerProperties[pointerIndex].id); + } +}; + +class TouchInputMapper : public InputMapper { +public: + explicit TouchInputMapper(InputDevice* device); + virtual ~TouchInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void dump(std::string& dump); + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); + virtual void reset(nsecs_t when); + virtual void process(const RawEvent* rawEvent); + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, + const int32_t* keyCodes, uint8_t* outFlags); + + virtual void fadePointer(); + virtual void cancelTouch(nsecs_t when); + virtual void timeoutExpired(nsecs_t when); + virtual void updateExternalStylusState(const StylusState& state); + virtual std::optional<int32_t> getAssociatedDisplay(); + +protected: + CursorButtonAccumulator mCursorButtonAccumulator; + CursorScrollAccumulator mCursorScrollAccumulator; + TouchButtonAccumulator mTouchButtonAccumulator; + + struct VirtualKey { + int32_t keyCode; + int32_t scanCode; + uint32_t flags; + + // computed hit box, specified in touch screen coords based on known display size + int32_t hitLeft; + int32_t hitTop; + int32_t hitRight; + int32_t hitBottom; + + inline bool isHit(int32_t x, int32_t y) const { + return x >= hitLeft && x <= hitRight && y >= hitTop && y <= hitBottom; + } + }; + + // Input sources and device mode. + uint32_t mSource; + + enum DeviceMode { + DEVICE_MODE_DISABLED, // input is disabled + DEVICE_MODE_DIRECT, // direct mapping (touchscreen) + DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad) + DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation) + DEVICE_MODE_POINTER, // pointer mapping (pointer) + }; + DeviceMode mDeviceMode; + + // The reader's configuration. + InputReaderConfiguration mConfig; + + // Immutable configuration parameters. + struct Parameters { + enum DeviceType { + DEVICE_TYPE_TOUCH_SCREEN, + DEVICE_TYPE_TOUCH_PAD, + DEVICE_TYPE_TOUCH_NAVIGATION, + DEVICE_TYPE_POINTER, + }; + + DeviceType deviceType; + bool hasAssociatedDisplay; + bool associatedDisplayIsExternal; + bool orientationAware; + bool hasButtonUnderPad; + std::string uniqueDisplayId; + + enum GestureMode { + GESTURE_MODE_SINGLE_TOUCH, + GESTURE_MODE_MULTI_TOUCH, + }; + GestureMode gestureMode; + + bool wake; + } mParameters; + + // Immutable calibration parameters in parsed form. + struct Calibration { + // Size + enum SizeCalibration { + SIZE_CALIBRATION_DEFAULT, + SIZE_CALIBRATION_NONE, + SIZE_CALIBRATION_GEOMETRIC, + SIZE_CALIBRATION_DIAMETER, + SIZE_CALIBRATION_BOX, + SIZE_CALIBRATION_AREA, + }; + + SizeCalibration sizeCalibration; + + bool haveSizeScale; + float sizeScale; + bool haveSizeBias; + float sizeBias; + bool haveSizeIsSummed; + bool sizeIsSummed; + + // Pressure + enum PressureCalibration { + PRESSURE_CALIBRATION_DEFAULT, + PRESSURE_CALIBRATION_NONE, + PRESSURE_CALIBRATION_PHYSICAL, + PRESSURE_CALIBRATION_AMPLITUDE, + }; + + PressureCalibration pressureCalibration; + bool havePressureScale; + float pressureScale; + + // Orientation + enum OrientationCalibration { + ORIENTATION_CALIBRATION_DEFAULT, + ORIENTATION_CALIBRATION_NONE, + ORIENTATION_CALIBRATION_INTERPOLATED, + ORIENTATION_CALIBRATION_VECTOR, + }; + + OrientationCalibration orientationCalibration; + + // Distance + enum DistanceCalibration { + DISTANCE_CALIBRATION_DEFAULT, + DISTANCE_CALIBRATION_NONE, + DISTANCE_CALIBRATION_SCALED, + }; + + DistanceCalibration distanceCalibration; + bool haveDistanceScale; + float distanceScale; + + enum CoverageCalibration { + COVERAGE_CALIBRATION_DEFAULT, + COVERAGE_CALIBRATION_NONE, + COVERAGE_CALIBRATION_BOX, + }; + + CoverageCalibration coverageCalibration; + + inline void applySizeScaleAndBias(float* outSize) const { + if (haveSizeScale) { + *outSize *= sizeScale; + } + if (haveSizeBias) { + *outSize += sizeBias; + } + if (*outSize < 0) { + *outSize = 0; + } + } + } mCalibration; + + // Affine location transformation/calibration + struct TouchAffineTransformation mAffineTransform; + + RawPointerAxes mRawPointerAxes; + + struct RawState { + nsecs_t when; + uint32_t deviceTimestamp; + + // Raw pointer sample data. + RawPointerData rawPointerData; + + int32_t buttonState; + + // Scroll state. + int32_t rawVScroll; + int32_t rawHScroll; + + void copyFrom(const RawState& other) { + when = other.when; + deviceTimestamp = other.deviceTimestamp; + rawPointerData.copyFrom(other.rawPointerData); + buttonState = other.buttonState; + rawVScroll = other.rawVScroll; + rawHScroll = other.rawHScroll; + } + + void clear() { + when = 0; + deviceTimestamp = 0; + rawPointerData.clear(); + buttonState = 0; + rawVScroll = 0; + rawHScroll = 0; + } + }; + + struct CookedState { + uint32_t deviceTimestamp; + // Cooked pointer sample data. + CookedPointerData cookedPointerData; + + // Id bits used to differentiate fingers, stylus and mouse tools. + BitSet32 fingerIdBits; + BitSet32 stylusIdBits; + BitSet32 mouseIdBits; + + int32_t buttonState; + + void copyFrom(const CookedState& other) { + deviceTimestamp = other.deviceTimestamp; + cookedPointerData.copyFrom(other.cookedPointerData); + fingerIdBits = other.fingerIdBits; + stylusIdBits = other.stylusIdBits; + mouseIdBits = other.mouseIdBits; + buttonState = other.buttonState; + } + + void clear() { + deviceTimestamp = 0; + cookedPointerData.clear(); + fingerIdBits.clear(); + stylusIdBits.clear(); + mouseIdBits.clear(); + buttonState = 0; + } + }; + + std::vector<RawState> mRawStatesPending; + RawState mCurrentRawState; + CookedState mCurrentCookedState; + RawState mLastRawState; + CookedState mLastCookedState; + + // State provided by an external stylus + StylusState mExternalStylusState; + int64_t mExternalStylusId; + nsecs_t mExternalStylusFusionTimeout; + bool mExternalStylusDataPending; + + // True if we sent a HOVER_ENTER event. + bool mSentHoverEnter; + + // Have we assigned pointer IDs for this stream + bool mHavePointerIds; + + // Is the current stream of direct touch events aborted + bool mCurrentMotionAborted; + + // The time the primary pointer last went down. + nsecs_t mDownTime; + + // The pointer controller, or null if the device is not a pointer. + sp<PointerControllerInterface> mPointerController; + + std::vector<VirtualKey> mVirtualKeys; + + virtual void configureParameters(); + virtual void dumpParameters(std::string& dump); + virtual void configureRawPointerAxes(); + virtual void dumpRawPointerAxes(std::string& dump); + virtual void configureSurface(nsecs_t when, bool* outResetNeeded); + virtual void dumpSurface(std::string& dump); + virtual void configureVirtualKeys(); + virtual void dumpVirtualKeys(std::string& dump); + virtual void parseCalibration(); + virtual void resolveCalibration(); + virtual void dumpCalibration(std::string& dump); + virtual void updateAffineTransformation(); + virtual void dumpAffineTransformation(std::string& dump); + virtual void resolveExternalStylusPresence(); + virtual bool hasStylus() const = 0; + virtual bool hasExternalStylus() const; + + virtual void syncTouch(nsecs_t when, RawState* outState) = 0; + +private: + // The current viewport. + // The components of the viewport are specified in the display's rotated orientation. + DisplayViewport mViewport; + + // The surface orientation, width and height set by configureSurface(). + // The width and height are derived from the viewport but are specified + // in the natural orientation. + // The surface origin specifies how the surface coordinates should be translated + // to align with the logical display coordinate space. + int32_t mSurfaceWidth; + int32_t mSurfaceHeight; + int32_t mSurfaceLeft; + int32_t mSurfaceTop; + + // Similar to the surface coordinates, but in the raw display coordinate space rather than in + // the logical coordinate space. + int32_t mPhysicalWidth; + int32_t mPhysicalHeight; + int32_t mPhysicalLeft; + int32_t mPhysicalTop; + + // The orientation may be different from the viewport orientation as it specifies + // the rotation of the surface coordinates required to produce the viewport's + // requested orientation, so it will depend on whether the device is orientation aware. + int32_t mSurfaceOrientation; + + // Translation and scaling factors, orientation-independent. + float mXTranslate; + float mXScale; + float mXPrecision; + + float mYTranslate; + float mYScale; + float mYPrecision; + + float mGeometricScale; + + float mPressureScale; + + float mSizeScale; + + float mOrientationScale; + + float mDistanceScale; + + bool mHaveTilt; + float mTiltXCenter; + float mTiltXScale; + float mTiltYCenter; + float mTiltYScale; + + bool mExternalStylusConnected; + + // Oriented motion ranges for input device info. + struct OrientedRanges { + InputDeviceInfo::MotionRange x; + InputDeviceInfo::MotionRange y; + InputDeviceInfo::MotionRange pressure; + + bool haveSize; + InputDeviceInfo::MotionRange size; + + bool haveTouchSize; + InputDeviceInfo::MotionRange touchMajor; + InputDeviceInfo::MotionRange touchMinor; + + bool haveToolSize; + InputDeviceInfo::MotionRange toolMajor; + InputDeviceInfo::MotionRange toolMinor; + + bool haveOrientation; + InputDeviceInfo::MotionRange orientation; + + bool haveDistance; + InputDeviceInfo::MotionRange distance; + + bool haveTilt; + InputDeviceInfo::MotionRange tilt; + + OrientedRanges() { clear(); } + + void clear() { + haveSize = false; + haveTouchSize = false; + haveToolSize = false; + haveOrientation = false; + haveDistance = false; + haveTilt = false; + } + } mOrientedRanges; + + // Oriented dimensions and precision. + float mOrientedXPrecision; + float mOrientedYPrecision; + + struct CurrentVirtualKeyState { + bool down; + bool ignored; + nsecs_t downTime; + int32_t keyCode; + int32_t scanCode; + } mCurrentVirtualKey; + + // Scale factor for gesture or mouse based pointer movements. + float mPointerXMovementScale; + float mPointerYMovementScale; + + // Scale factor for gesture based zooming and other freeform motions. + float mPointerXZoomScale; + float mPointerYZoomScale; + + // The maximum swipe width. + float mPointerGestureMaxSwipeWidth; + + struct PointerDistanceHeapElement { + uint32_t currentPointerIndex : 8; + uint32_t lastPointerIndex : 8; + uint64_t distance : 48; // squared distance + }; + + enum PointerUsage { + POINTER_USAGE_NONE, + POINTER_USAGE_GESTURES, + POINTER_USAGE_STYLUS, + POINTER_USAGE_MOUSE, + }; + PointerUsage mPointerUsage; + + struct PointerGesture { + enum Mode { + // No fingers, button is not pressed. + // Nothing happening. + NEUTRAL, + + // No fingers, button is not pressed. + // Tap detected. + // Emits DOWN and UP events at the pointer location. + TAP, + + // Exactly one finger dragging following a tap. + // Pointer follows the active finger. + // Emits DOWN, MOVE and UP events at the pointer location. + // + // Detect double-taps when the finger goes up while in TAP_DRAG mode. + TAP_DRAG, + + // Button is pressed. + // Pointer follows the active finger if there is one. Other fingers are ignored. + // Emits DOWN, MOVE and UP events at the pointer location. + BUTTON_CLICK_OR_DRAG, + + // Exactly one finger, button is not pressed. + // Pointer follows the active finger. + // Emits HOVER_MOVE events at the pointer location. + // + // Detect taps when the finger goes up while in HOVER mode. + HOVER, + + // Exactly two fingers but neither have moved enough to clearly indicate + // whether a swipe or freeform gesture was intended. We consider the + // pointer to be pressed so this enables clicking or long-pressing on buttons. + // Pointer does not move. + // Emits DOWN, MOVE and UP events with a single stationary pointer coordinate. + PRESS, + + // Exactly two fingers moving in the same direction, button is not pressed. + // Pointer does not move. + // Emits DOWN, MOVE and UP events with a single pointer coordinate that + // follows the midpoint between both fingers. + SWIPE, + + // Two or more fingers moving in arbitrary directions, button is not pressed. + // Pointer does not move. + // Emits DOWN, POINTER_DOWN, MOVE, POINTER_UP and UP events that follow + // each finger individually relative to the initial centroid of the finger. + FREEFORM, + + // Waiting for quiet time to end before starting the next gesture. + QUIET, + }; + + // Time the first finger went down. + nsecs_t firstTouchTime; + + // The active pointer id from the raw touch data. + int32_t activeTouchId; // -1 if none + + // The active pointer id from the gesture last delivered to the application. + int32_t activeGestureId; // -1 if none + + // Pointer coords and ids for the current and previous pointer gesture. + Mode currentGestureMode; + BitSet32 currentGestureIdBits; + uint32_t currentGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerProperties currentGestureProperties[MAX_POINTERS]; + PointerCoords currentGestureCoords[MAX_POINTERS]; + + Mode lastGestureMode; + BitSet32 lastGestureIdBits; + uint32_t lastGestureIdToIndex[MAX_POINTER_ID + 1]; + PointerProperties lastGestureProperties[MAX_POINTERS]; + PointerCoords lastGestureCoords[MAX_POINTERS]; + + // Time the pointer gesture last went down. + nsecs_t downTime; + + // Time when the pointer went down for a TAP. + nsecs_t tapDownTime; + + // Time when the pointer went up for a TAP. + nsecs_t tapUpTime; + + // Location of initial tap. + float tapX, tapY; + + // Time we started waiting for quiescence. + nsecs_t quietTime; + + // Reference points for multitouch gestures. + float referenceTouchX; // reference touch X/Y coordinates in surface units + float referenceTouchY; + float referenceGestureX; // reference gesture X/Y coordinates in pixels + float referenceGestureY; + + // Distance that each pointer has traveled which has not yet been + // subsumed into the reference gesture position. + BitSet32 referenceIdBits; + struct Delta { + float dx, dy; + }; + Delta referenceDeltas[MAX_POINTER_ID + 1]; + + // Describes how touch ids are mapped to gesture ids for freeform gestures. + uint32_t freeformTouchToGestureIdMap[MAX_POINTER_ID + 1]; + + // A velocity tracker for determining whether to switch active pointers during drags. + VelocityTracker velocityTracker; + + void reset() { + firstTouchTime = LLONG_MIN; + activeTouchId = -1; + activeGestureId = -1; + currentGestureMode = NEUTRAL; + currentGestureIdBits.clear(); + lastGestureMode = NEUTRAL; + lastGestureIdBits.clear(); + downTime = 0; + velocityTracker.clear(); + resetTap(); + resetQuietTime(); + } + + void resetTap() { + tapDownTime = LLONG_MIN; + tapUpTime = LLONG_MIN; + } + + void resetQuietTime() { quietTime = LLONG_MIN; } + } mPointerGesture; + + struct PointerSimple { + PointerCoords currentCoords; + PointerProperties currentProperties; + PointerCoords lastCoords; + PointerProperties lastProperties; + + // True if the pointer is down. + bool down; + + // True if the pointer is hovering. + bool hovering; + + // Time the pointer last went down. + nsecs_t downTime; + + void reset() { + currentCoords.clear(); + currentProperties.clear(); + lastCoords.clear(); + lastProperties.clear(); + down = false; + hovering = false; + downTime = 0; + } + } mPointerSimple; + + // The pointer and scroll velocity controls. + VelocityControl mPointerVelocityControl; + VelocityControl mWheelXVelocityControl; + VelocityControl mWheelYVelocityControl; + + // Latency statistics for touch events + struct LatencyStatistics mStatistics; + + std::optional<DisplayViewport> findViewport(); + + void resetExternalStylus(); + void clearStylusDataPendingFlags(); + + void sync(nsecs_t when); + + bool consumeRawTouches(nsecs_t when, uint32_t policyFlags); + void processRawTouches(bool timeout); + void cookAndDispatch(nsecs_t when); + void dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t keyEventAction, + int32_t keyEventFlags); + + void dispatchTouches(nsecs_t when, uint32_t policyFlags); + void dispatchHoverExit(nsecs_t when, uint32_t policyFlags); + void dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFlags); + void dispatchButtonRelease(nsecs_t when, uint32_t policyFlags); + void dispatchButtonPress(nsecs_t when, uint32_t policyFlags); + const BitSet32& findActiveIdBits(const CookedPointerData& cookedPointerData); + void cookPointerData(); + void abortTouches(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerUsage(nsecs_t when, uint32_t policyFlags, PointerUsage pointerUsage); + void abortPointerUsage(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout); + void abortPointerGestures(nsecs_t when, uint32_t policyFlags); + bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, + bool* outFinishPreviousGesture, bool isTimeout); + + void dispatchPointerStylus(nsecs_t when, uint32_t policyFlags); + void abortPointerStylus(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerMouse(nsecs_t when, uint32_t policyFlags); + void abortPointerMouse(nsecs_t when, uint32_t policyFlags); + + void dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, bool down, bool hovering); + void abortPointerSimple(nsecs_t when, uint32_t policyFlags); + + bool assignExternalStylusId(const RawState& state, bool timeout); + void applyExternalStylusButtonState(nsecs_t when); + void applyExternalStylusTouchState(nsecs_t when); + + // Dispatches a motion event. + // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the + // method will take care of setting the index and transmuting the action to DOWN or UP + // it is the first / last pointer to go down / up. + void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, + int32_t edgeFlags, uint32_t deviceTimestamp, + const PointerProperties* properties, const PointerCoords* coords, + const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, + float xPrecision, float yPrecision, nsecs_t downTime); + + // Updates pointer coords and properties for pointers with specified ids that have moved. + // Returns true if any of them changed. + bool updateMovedPointers(const PointerProperties* inProperties, const PointerCoords* inCoords, + const uint32_t* inIdToIndex, PointerProperties* outProperties, + PointerCoords* outCoords, const uint32_t* outIdToIndex, + BitSet32 idBits) const; + + bool isPointInsideSurface(int32_t x, int32_t y); + const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y); + + static void assignPointerIds(const RawState* last, RawState* current); + + void reportEventForStatistics(nsecs_t evdevTime); + + const char* modeToString(DeviceMode deviceMode); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp new file mode 100644 index 0000000000..a27fab4581 --- /dev/null +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Macros.h" + +#include "VibratorInputMapper.h" + +namespace android { + +VibratorInputMapper::VibratorInputMapper(InputDevice* device) + : InputMapper(device), mVibrating(false) {} + +VibratorInputMapper::~VibratorInputMapper() {} + +uint32_t VibratorInputMapper::getSources() { + return 0; +} + +void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setVibrator(true); +} + +void VibratorInputMapper::process(const RawEvent* rawEvent) { + // TODO: Handle FF_STATUS, although it does not seem to be widely supported. +} + +void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) { +#if DEBUG_VIBRATOR + std::string patternStr; + for (size_t i = 0; i < patternSize; i++) { + if (i != 0) { + patternStr += ", "; + } + patternStr += StringPrintf("%" PRId64, pattern[i]); + } + ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(), + patternStr.c_str(), repeat, token); +#endif + + mVibrating = true; + memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t)); + mPatternSize = patternSize; + mRepeat = repeat; + mToken = token; + mIndex = -1; + + nextStep(); +} + +void VibratorInputMapper::cancelVibrate(int32_t token) { +#if DEBUG_VIBRATOR + ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); +#endif + + if (mVibrating && mToken == token) { + stopVibrating(); + } +} + +void VibratorInputMapper::timeoutExpired(nsecs_t when) { + if (mVibrating) { + if (when >= mNextStepTime) { + nextStep(); + } else { + getContext()->requestTimeoutAtTime(mNextStepTime); + } + } +} + +void VibratorInputMapper::nextStep() { + mIndex += 1; + if (size_t(mIndex) >= mPatternSize) { + if (mRepeat < 0) { + // We are done. + stopVibrating(); + return; + } + mIndex = mRepeat; + } + + bool vibratorOn = mIndex & 1; + nsecs_t duration = mPattern[mIndex]; + if (vibratorOn) { +#if DEBUG_VIBRATOR + ALOGD("nextStep: sending vibrate deviceId=%d, duration=%" PRId64, getDeviceId(), duration); +#endif + getEventHub()->vibrate(getDeviceId(), duration); + } else { +#if DEBUG_VIBRATOR + ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); +#endif + getEventHub()->cancelVibrate(getDeviceId()); + } + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + mNextStepTime = now + duration; + getContext()->requestTimeoutAtTime(mNextStepTime); +#if DEBUG_VIBRATOR + ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f); +#endif +} + +void VibratorInputMapper::stopVibrating() { + mVibrating = false; +#if DEBUG_VIBRATOR + ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); +#endif + getEventHub()->cancelVibrate(getDeviceId()); +} + +void VibratorInputMapper::dump(std::string& dump) { + dump += INDENT2 "Vibrator Input Mapper:\n"; + dump += StringPrintf(INDENT3 "Vibrating: %s\n", toString(mVibrating)); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h new file mode 100644 index 0000000000..6b33f4811e --- /dev/null +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H +#define _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H + +#include "InputMapper.h" + +namespace android { + +class VibratorInputMapper : public InputMapper { +public: + explicit VibratorInputMapper(InputDevice* device); + virtual ~VibratorInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void process(const RawEvent* rawEvent); + + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + virtual void cancelVibrate(int32_t token); + virtual void timeoutExpired(nsecs_t when); + virtual void dump(std::string& dump); + +private: + bool mVibrating; + nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE]; + size_t mPatternSize; + ssize_t mRepeat; + int32_t mToken; + ssize_t mIndex; + nsecs_t mNextStepTime; + + void nextStep(); + void stopVibrating(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_VIBRATOR_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp new file mode 100644 index 0000000000..0337d51126 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CursorButtonAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +CursorButtonAccumulator::CursorButtonAccumulator() { + clearButtons(); +} + +void CursorButtonAccumulator::reset(InputDevice* device) { + mBtnLeft = device->isKeyPressed(BTN_LEFT); + mBtnRight = device->isKeyPressed(BTN_RIGHT); + mBtnMiddle = device->isKeyPressed(BTN_MIDDLE); + mBtnBack = device->isKeyPressed(BTN_BACK); + mBtnSide = device->isKeyPressed(BTN_SIDE); + mBtnForward = device->isKeyPressed(BTN_FORWARD); + mBtnExtra = device->isKeyPressed(BTN_EXTRA); + mBtnTask = device->isKeyPressed(BTN_TASK); +} + +void CursorButtonAccumulator::clearButtons() { + mBtnLeft = 0; + mBtnRight = 0; + mBtnMiddle = 0; + mBtnBack = 0; + mBtnSide = 0; + mBtnForward = 0; + mBtnExtra = 0; + mBtnTask = 0; +} + +void CursorButtonAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_KEY) { + switch (rawEvent->code) { + case BTN_LEFT: + mBtnLeft = rawEvent->value; + break; + case BTN_RIGHT: + mBtnRight = rawEvent->value; + break; + case BTN_MIDDLE: + mBtnMiddle = rawEvent->value; + break; + case BTN_BACK: + mBtnBack = rawEvent->value; + break; + case BTN_SIDE: + mBtnSide = rawEvent->value; + break; + case BTN_FORWARD: + mBtnForward = rawEvent->value; + break; + case BTN_EXTRA: + mBtnExtra = rawEvent->value; + break; + case BTN_TASK: + mBtnTask = rawEvent->value; + break; + } + } +} + +uint32_t CursorButtonAccumulator::getButtonState() const { + uint32_t result = 0; + if (mBtnLeft) { + result |= AMOTION_EVENT_BUTTON_PRIMARY; + } + if (mBtnRight) { + result |= AMOTION_EVENT_BUTTON_SECONDARY; + } + if (mBtnMiddle) { + result |= AMOTION_EVENT_BUTTON_TERTIARY; + } + if (mBtnBack || mBtnSide) { + result |= AMOTION_EVENT_BUTTON_BACK; + } + if (mBtnForward || mBtnExtra) { + result |= AMOTION_EVENT_BUTTON_FORWARD; + } + return result; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h new file mode 100644 index 0000000000..d9123109a3 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H +#define _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H + +#include <stdint.h> + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of the state of mouse or touch pad buttons. */ +class CursorButtonAccumulator { +public: + CursorButtonAccumulator(); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + + uint32_t getButtonState() const; + +private: + bool mBtnLeft; + bool mBtnRight; + bool mBtnMiddle; + bool mBtnBack; + bool mBtnSide; + bool mBtnForward; + bool mBtnExtra; + bool mBtnTask; + + void clearButtons(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_CURSOR_BUTTON_ACCUMULATOR_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp new file mode 100644 index 0000000000..d744096d94 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CursorScrollAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +CursorScrollAccumulator::CursorScrollAccumulator() : mHaveRelWheel(false), mHaveRelHWheel(false) { + clearRelativeAxes(); +} + +void CursorScrollAccumulator::configure(InputDevice* device) { + mHaveRelWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_WHEEL); + mHaveRelHWheel = device->getEventHub()->hasRelativeAxis(device->getId(), REL_HWHEEL); +} + +void CursorScrollAccumulator::reset(InputDevice* device) { + clearRelativeAxes(); +} + +void CursorScrollAccumulator::clearRelativeAxes() { + mRelWheel = 0; + mRelHWheel = 0; +} + +void CursorScrollAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_REL) { + switch (rawEvent->code) { + case REL_WHEEL: + mRelWheel = rawEvent->value; + break; + case REL_HWHEEL: + mRelHWheel = rawEvent->value; + break; + } + } +} + +void CursorScrollAccumulator::finishSync() { + clearRelativeAxes(); +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h new file mode 100644 index 0000000000..85f331fd8a --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H +#define _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H + +#include <stdint.h> + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of cursor scrolling motions. */ + +class CursorScrollAccumulator { +public: + CursorScrollAccumulator(); + void configure(InputDevice* device); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + void finishSync(); + + inline bool haveRelativeVWheel() const { return mHaveRelWheel; } + inline bool haveRelativeHWheel() const { return mHaveRelHWheel; } + + inline int32_t getRelativeX() const { return mRelX; } + inline int32_t getRelativeY() const { return mRelY; } + inline int32_t getRelativeVWheel() const { return mRelWheel; } + inline int32_t getRelativeHWheel() const { return mRelHWheel; } + +private: + bool mHaveRelWheel; + bool mHaveRelHWheel; + + int32_t mRelX; + int32_t mRelY; + int32_t mRelWheel; + int32_t mRelHWheel; + + void clearRelativeAxes(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_CURSOR_SCROLL_ACCUMULATOR_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp new file mode 100644 index 0000000000..e9ba727a0d --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SingleTouchMotionAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +SingleTouchMotionAccumulator::SingleTouchMotionAccumulator() { + clearAbsoluteAxes(); +} + +void SingleTouchMotionAccumulator::reset(InputDevice* device) { + mAbsX = device->getAbsoluteAxisValue(ABS_X); + mAbsY = device->getAbsoluteAxisValue(ABS_Y); + mAbsPressure = device->getAbsoluteAxisValue(ABS_PRESSURE); + mAbsToolWidth = device->getAbsoluteAxisValue(ABS_TOOL_WIDTH); + mAbsDistance = device->getAbsoluteAxisValue(ABS_DISTANCE); + mAbsTiltX = device->getAbsoluteAxisValue(ABS_TILT_X); + mAbsTiltY = device->getAbsoluteAxisValue(ABS_TILT_Y); +} + +void SingleTouchMotionAccumulator::clearAbsoluteAxes() { + mAbsX = 0; + mAbsY = 0; + mAbsPressure = 0; + mAbsToolWidth = 0; + mAbsDistance = 0; + mAbsTiltX = 0; + mAbsTiltY = 0; +} + +void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_ABS) { + switch (rawEvent->code) { + case ABS_X: + mAbsX = rawEvent->value; + break; + case ABS_Y: + mAbsY = rawEvent->value; + break; + case ABS_PRESSURE: + mAbsPressure = rawEvent->value; + break; + case ABS_TOOL_WIDTH: + mAbsToolWidth = rawEvent->value; + break; + case ABS_DISTANCE: + mAbsDistance = rawEvent->value; + break; + case ABS_TILT_X: + mAbsTiltX = rawEvent->value; + break; + case ABS_TILT_Y: + mAbsTiltY = rawEvent->value; + break; + } + } +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h new file mode 100644 index 0000000000..75f8a961b3 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H +#define _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H + +#include <stdint.h> + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of the state of single-touch protocol. */ +class SingleTouchMotionAccumulator { +public: + SingleTouchMotionAccumulator(); + + void process(const RawEvent* rawEvent); + void reset(InputDevice* device); + + inline int32_t getAbsoluteX() const { return mAbsX; } + inline int32_t getAbsoluteY() const { return mAbsY; } + inline int32_t getAbsolutePressure() const { return mAbsPressure; } + inline int32_t getAbsoluteToolWidth() const { return mAbsToolWidth; } + inline int32_t getAbsoluteDistance() const { return mAbsDistance; } + inline int32_t getAbsoluteTiltX() const { return mAbsTiltX; } + inline int32_t getAbsoluteTiltY() const { return mAbsTiltY; } + +private: + int32_t mAbsX; + int32_t mAbsY; + int32_t mAbsPressure; + int32_t mAbsToolWidth; + int32_t mAbsDistance; + int32_t mAbsTiltX; + int32_t mAbsTiltY; + + void clearAbsoluteAxes(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_SINGLE_TOUCH_MOTION_ACCUMULATOR_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp new file mode 100644 index 0000000000..d2f06c86fd --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TouchButtonAccumulator.h" + +#include "EventHub.h" +#include "InputDevice.h" + +namespace android { + +TouchButtonAccumulator::TouchButtonAccumulator() : mHaveBtnTouch(false), mHaveStylus(false) { + clearButtons(); +} + +void TouchButtonAccumulator::configure(InputDevice* device) { + mHaveBtnTouch = device->hasKey(BTN_TOUCH); + mHaveStylus = device->hasKey(BTN_TOOL_PEN) || device->hasKey(BTN_TOOL_RUBBER) || + device->hasKey(BTN_TOOL_BRUSH) || device->hasKey(BTN_TOOL_PENCIL) || + device->hasKey(BTN_TOOL_AIRBRUSH); +} + +void TouchButtonAccumulator::reset(InputDevice* device) { + mBtnTouch = device->isKeyPressed(BTN_TOUCH); + mBtnStylus = device->isKeyPressed(BTN_STYLUS); + // BTN_0 is what gets mapped for the HID usage Digitizers.SecondaryBarrelSwitch + mBtnStylus2 = device->isKeyPressed(BTN_STYLUS2) || device->isKeyPressed(BTN_0); + mBtnToolFinger = device->isKeyPressed(BTN_TOOL_FINGER); + mBtnToolPen = device->isKeyPressed(BTN_TOOL_PEN); + mBtnToolRubber = device->isKeyPressed(BTN_TOOL_RUBBER); + mBtnToolBrush = device->isKeyPressed(BTN_TOOL_BRUSH); + mBtnToolPencil = device->isKeyPressed(BTN_TOOL_PENCIL); + mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH); + mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE); + mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS); + mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP); + mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP); + mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP); +} + +void TouchButtonAccumulator::clearButtons() { + mBtnTouch = 0; + mBtnStylus = 0; + mBtnStylus2 = 0; + mBtnToolFinger = 0; + mBtnToolPen = 0; + mBtnToolRubber = 0; + mBtnToolBrush = 0; + mBtnToolPencil = 0; + mBtnToolAirbrush = 0; + mBtnToolMouse = 0; + mBtnToolLens = 0; + mBtnToolDoubleTap = 0; + mBtnToolTripleTap = 0; + mBtnToolQuadTap = 0; +} + +void TouchButtonAccumulator::process(const RawEvent* rawEvent) { + if (rawEvent->type == EV_KEY) { + switch (rawEvent->code) { + case BTN_TOUCH: + mBtnTouch = rawEvent->value; + break; + case BTN_STYLUS: + mBtnStylus = rawEvent->value; + break; + case BTN_STYLUS2: + case BTN_0: // BTN_0 is what gets mapped for the HID usage + // Digitizers.SecondaryBarrelSwitch + mBtnStylus2 = rawEvent->value; + break; + case BTN_TOOL_FINGER: + mBtnToolFinger = rawEvent->value; + break; + case BTN_TOOL_PEN: + mBtnToolPen = rawEvent->value; + break; + case BTN_TOOL_RUBBER: + mBtnToolRubber = rawEvent->value; + break; + case BTN_TOOL_BRUSH: + mBtnToolBrush = rawEvent->value; + break; + case BTN_TOOL_PENCIL: + mBtnToolPencil = rawEvent->value; + break; + case BTN_TOOL_AIRBRUSH: + mBtnToolAirbrush = rawEvent->value; + break; + case BTN_TOOL_MOUSE: + mBtnToolMouse = rawEvent->value; + break; + case BTN_TOOL_LENS: + mBtnToolLens = rawEvent->value; + break; + case BTN_TOOL_DOUBLETAP: + mBtnToolDoubleTap = rawEvent->value; + break; + case BTN_TOOL_TRIPLETAP: + mBtnToolTripleTap = rawEvent->value; + break; + case BTN_TOOL_QUADTAP: + mBtnToolQuadTap = rawEvent->value; + break; + } + } +} + +uint32_t TouchButtonAccumulator::getButtonState() const { + uint32_t result = 0; + if (mBtnStylus) { + result |= AMOTION_EVENT_BUTTON_STYLUS_PRIMARY; + } + if (mBtnStylus2) { + result |= AMOTION_EVENT_BUTTON_STYLUS_SECONDARY; + } + return result; +} + +int32_t TouchButtonAccumulator::getToolType() const { + if (mBtnToolMouse || mBtnToolLens) { + return AMOTION_EVENT_TOOL_TYPE_MOUSE; + } + if (mBtnToolRubber) { + return AMOTION_EVENT_TOOL_TYPE_ERASER; + } + if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) { + return AMOTION_EVENT_TOOL_TYPE_STYLUS; + } + if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) { + return AMOTION_EVENT_TOOL_TYPE_FINGER; + } + return AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +bool TouchButtonAccumulator::isToolActive() const { + return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber || mBtnToolBrush || + mBtnToolPencil || mBtnToolAirbrush || mBtnToolMouse || mBtnToolLens || + mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap; +} + +bool TouchButtonAccumulator::isHovering() const { + return mHaveBtnTouch && !mBtnTouch; +} + +bool TouchButtonAccumulator::hasStylus() const { + return mHaveStylus; +} + +} // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h new file mode 100644 index 0000000000..65b6bdcc12 --- /dev/null +++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H +#define _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H + +#include <stdint.h> + +namespace android { + +class InputDevice; +struct RawEvent; + +/* Keeps track of the state of touch, stylus and tool buttons. */ +class TouchButtonAccumulator { +public: + TouchButtonAccumulator(); + void configure(InputDevice* device); + void reset(InputDevice* device); + + void process(const RawEvent* rawEvent); + + uint32_t getButtonState() const; + int32_t getToolType() const; + bool isToolActive() const; + bool isHovering() const; + bool hasStylus() const; + +private: + bool mHaveBtnTouch; + bool mHaveStylus; + + bool mBtnTouch; + bool mBtnStylus; + bool mBtnStylus2; + bool mBtnToolFinger; + bool mBtnToolPen; + bool mBtnToolRubber; + bool mBtnToolBrush; + bool mBtnToolPencil; + bool mBtnToolAirbrush; + bool mBtnToolMouse; + bool mBtnToolLens; + bool mBtnToolDoubleTap; + bool mBtnToolTripleTap; + bool mBtnToolQuadTap; + + void clearButtons(); +}; + +} // namespace android + +#endif // _UI_INPUTREADER_TOUCH_BUTTON_ACCUMULATOR_H
\ No newline at end of file diff --git a/services/nativeperms/Android.bp b/services/inputflinger/reporter/Android.bp index cbc7d665e3..5956fb0794 100644 --- a/services/nativeperms/Android.bp +++ b/services/inputflinger/reporter/Android.bp @@ -1,4 +1,4 @@ -// Copyright 2016 The Android Open Source Project +// Copyright (C) 2019 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,24 +11,31 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// -cc_binary { - name: "nativeperms", +cc_library_headers { + name: "libinputreporter_headers", + export_include_dirs: ["."], +} + +cc_library_shared { + name: "libinputreporter", + defaults: ["inputflinger_defaults"], + srcs: [ - "nativeperms.cpp", - "android/os/IPermissionController.aidl", - ], - cflags: [ - "-Wall", - "-Werror", + "InputReporter.cpp", ], + shared_libs: [ - "libbinder", - "libbrillo", - "libbrillo-binder", - "libchrome", + "liblog", "libutils", ], - init_rc: ["nativeperms.rc"], + + header_libs: [ + "libinputflinger_headers", + ], + + export_header_lib_headers: [ + "libinputflinger_headers", + ], } + diff --git a/services/inputflinger/InputReporter.cpp b/services/inputflinger/reporter/InputReporter.cpp index 8d3153c367..b591d3f909 100644 --- a/services/inputflinger/InputReporter.cpp +++ b/services/inputflinger/reporter/InputReporter.cpp @@ -27,15 +27,15 @@ public: }; void InputReporter::reportUnhandledKey(uint32_t sequenceNum) { - // do nothing + // do nothing } void InputReporter::reportDroppedKey(uint32_t sequenceNum) { - // do nothing + // do nothing } sp<InputReporterInterface> createInputReporter() { - return new InputReporter(); + return new InputReporter(); } } // namespace android diff --git a/services/inputflinger/include/InputReporterInterface.h b/services/inputflinger/reporter/InputReporterInterface.h index 906d7f25f2..e5d360609f 100644 --- a/services/inputflinger/include/InputReporterInterface.h +++ b/services/inputflinger/reporter/InputReporterInterface.h @@ -27,7 +27,7 @@ namespace android { */ class InputReporterInterface : public virtual RefBase { protected: - virtual ~InputReporterInterface() { } + virtual ~InputReporterInterface() {} public: // Report a key that was not handled by the system or apps. diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 9054316204..c4f8626a98 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -32,4 +32,7 @@ cc_test { "libinputflinger_base", "libinputservice", ], + header_libs: [ + "libinputreader_headers", + ], } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 9fe6481ca0..551bee18e8 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -14,14 +14,16 @@ * limitations under the License. */ -#include "../InputDispatcher.h" +#include "../dispatcher/InputDispatcher.h" + +#include <InputDispatcherThread.h> #include <binder/Binder.h> #include <gtest/gtest.h> #include <linux/input.h> -namespace android { +namespace android::inputdispatcher { // An arbitrary time value. static const nsecs_t ARBITRARY_TIME = 1234; @@ -1025,4 +1027,4 @@ TEST_F(InputDispatcherOnPointerDownOutsideFocus, mFakePolicy->assertOnPointerDownEquals(nullptr); } -} // namespace android +} // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index d35302885d..aeb4ad62a8 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -14,8 +14,16 @@ * limitations under the License. */ -#include "../InputReader.h" -#include "TestInputListener.h" +#include <CursorInputMapper.h> +#include <InputDevice.h> +#include <InputMapper.h> +#include <InputReader.h> +#include <KeyboardInputMapper.h> +#include <MultiTouchInputMapper.h> +#include <SingleTouchInputMapper.h> +#include <SwitchInputMapper.h> +#include <TestInputListener.h> +#include <TouchInputMapper.h> #include <gtest/gtest.h> #include <inttypes.h> diff --git a/services/nativeperms/.clang-format b/services/nativeperms/.clang-format deleted file mode 100644 index 6006e6f4fd..0000000000 --- a/services/nativeperms/.clang-format +++ /dev/null @@ -1,13 +0,0 @@ -BasedOnStyle: Google -AllowShortFunctionsOnASingleLine: Inline -AllowShortIfStatementsOnASingleLine: true -AllowShortLoopsOnASingleLine: true -BinPackArguments: true -BinPackParameters: true -ColumnLimit: 80 -CommentPragmas: NOLINT:.* -ContinuationIndentWidth: 8 -DerivePointerAlignment: false -IndentWidth: 4 -PointerAlignment: Left -TabWidth: 4 diff --git a/services/nativeperms/android/os/IPermissionController.aidl b/services/nativeperms/android/os/IPermissionController.aidl deleted file mode 100644 index 89db85ca0a..0000000000 --- a/services/nativeperms/android/os/IPermissionController.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* //device/java/android/android/os/IPowerManager.aidl -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -package android.os; - -/** @hide */ -interface IPermissionController { - boolean checkPermission(String permission, int pid, int uid); - String[] getPackagesForUid(int uid); - boolean isRuntimePermission(String permission); -} diff --git a/services/nativeperms/android/os/README b/services/nativeperms/android/os/README deleted file mode 100644 index e4144995b0..0000000000 --- a/services/nativeperms/android/os/README +++ /dev/null @@ -1,4 +0,0 @@ -IPermissionController.aidl in this directory is a verbatim copy of -https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/IPermissionController.aidl, -because some Brillo manifests do not currently include the frameworks/base repo. -TODO(jorgelo): Figure out a way to use the .aidl file in frameworks/base. diff --git a/services/nativeperms/nativeperms.cpp b/services/nativeperms/nativeperms.cpp deleted file mode 100644 index 7f03bedb29..0000000000 --- a/services/nativeperms/nativeperms.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <base/at_exit.h> -#include <base/logging.h> -#include <base/message_loop/message_loop.h> -#include <binder/IServiceManager.h> -#include <binder/Status.h> -#include <brillo/binder_watcher.h> -#include <brillo/message_loops/base_message_loop.h> -#include <brillo/syslog_logging.h> -#include <utils/String16.h> - -#include "android/os/BnPermissionController.h" - -namespace { -static android::String16 serviceName("permission"); -} - -namespace android { - -class PermissionService : public android::os::BnPermissionController { - public: - ::android::binder::Status checkPermission( - const ::android::String16& permission, int32_t pid, int32_t uid, - bool* _aidl_return) { - (void)permission; - (void)pid; - (void)uid; - *_aidl_return = true; - return binder::Status::ok(); - } - - ::android::binder::Status getPackagesForUid( - int32_t uid, ::std::vector<::android::String16>* _aidl_return) { - (void)uid; - // Brillo doesn't currently have installable packages. - if (_aidl_return) { - _aidl_return->clear(); - } - return binder::Status::ok(); - } - - ::android::binder::Status isRuntimePermission( - const ::android::String16& permission, bool* _aidl_return) { - (void)permission; - // Brillo doesn't currently have runtime permissions. - *_aidl_return = false; - return binder::Status::ok(); - } -}; - -} // namespace android - -int main() { - base::AtExitManager atExitManager; - brillo::InitLog(brillo::kLogToSyslog); - // Register the service with servicemanager. - android::status_t status = android::defaultServiceManager()->addService( - serviceName, new android::PermissionService()); - CHECK(status == android::OK) << "Failed to get IPermissionController " - "binder from servicemanager."; - - // Create a message loop. - base::MessageLoopForIO messageLoopForIo; - brillo::BaseMessageLoop messageLoop{&messageLoopForIo}; - - // Initialize a binder watcher. - brillo::BinderWatcher watcher(&messageLoop); - watcher.Init(); - - // Run the message loop. - messageLoop.Run(); -} diff --git a/services/nativeperms/nativeperms.rc b/services/nativeperms/nativeperms.rc deleted file mode 100644 index 704c0a2acc..0000000000 --- a/services/nativeperms/nativeperms.rc +++ /dev/null @@ -1,4 +0,0 @@ -service nativeperms /system/bin/nativeperms - class main - user system - group system diff --git a/services/schedulerservice/Android.bp b/services/schedulerservice/Android.bp index 0227164ef5..73802dbc9f 100644 --- a/services/schedulerservice/Android.bp +++ b/services/schedulerservice/Android.bp @@ -6,8 +6,6 @@ cc_library_shared { cflags: ["-Wall", "-Werror"], shared_libs: [ "libhidlbase", - "libhidltransport", - "libhwbinder", "libmediautils", "liblog", "libutils", diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 33a2747312..1c9a4af72b 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -45,8 +45,6 @@ cc_library_shared { "libcrypto", "libbase", "libhidlbase", - "libhidltransport", - "libhwbinder", "libfmq", "android.hardware.sensors@1.0", "android.hardware.sensors@2.0", diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS index 81099e895c..90c233030e 100644 --- a/services/sensorservice/OWNERS +++ b/services/sensorservice/OWNERS @@ -1,3 +1,3 @@ arthuri@google.com bduddie@google.com -bstack@google.com +stange@google.com diff --git a/services/sensorservice/hidl/Android.bp b/services/sensorservice/hidl/Android.bp index 02c13fa579..d0c83d6002 100644 --- a/services/sensorservice/hidl/Android.bp +++ b/services/sensorservice/hidl/Android.bp @@ -13,8 +13,6 @@ cc_library_shared { shared_libs: [ "libbase", "libhidlbase", - "libhidltransport", - "libhwbinder", "libutils", "libsensor", "android.frameworks.sensorservice@1.0", diff --git a/services/stats/Android.bp b/services/stats/Android.bp new file mode 100644 index 0000000000..1ce0524299 --- /dev/null +++ b/services/stats/Android.bp @@ -0,0 +1,22 @@ +cc_library_shared { + name: "libstatshidl", + srcs: [ + "StatsHal.cpp", + ], + cflags: ["-Wall", "-Werror"], + shared_libs: [ + "android.frameworks.stats@1.0", + "libhidlbase", + "liblog", + "libstatslog", + "libstatssocket", + "libutils", + ], + export_include_dirs: [ + "include/", + ], + local_include_dirs: [ + "include/stats", + ], + vintf_fragments: ["android.frameworks.stats@1.0-service.xml"] +} diff --git a/services/stats/OWNERS b/services/stats/OWNERS new file mode 100644 index 0000000000..a61babf32e --- /dev/null +++ b/services/stats/OWNERS @@ -0,0 +1,9 @@ +jeffreyhuang@google.com +joeo@google.com +jtnguyen@google.com +muhammadq@google.com +ruchirr@google.com +singhtejinder@google.com +tsaichristine@google.com +yaochen@google.com +yro@google.com diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp new file mode 100644 index 0000000000..b775431cff --- /dev/null +++ b/services/stats/StatsHal.cpp @@ -0,0 +1,124 @@ +/* + * 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. + */ + +#define DEBUG false // STOPSHIP if true +#define LOG_TAG "StatsHal" + +#include <log/log.h> +#include <statslog.h> + +#include "StatsHal.h" + +namespace android { +namespace frameworks { +namespace stats { +namespace V1_0 { +namespace implementation { + +StatsHal::StatsHal() {} + +hardware::Return<void> StatsHal::reportSpeakerImpedance( + const SpeakerImpedance& speakerImpedance) { + android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED, + speakerImpedance.speakerLocation, speakerImpedance.milliOhms); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportHardwareFailed(const HardwareFailed& hardwareFailed) { + android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType), + hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode)); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportPhysicalDropDetected( + const PhysicalDropDetected& physicalDropDetected) { + android::util::stats_write(android::util::PHYSICAL_DROP_DETECTED, + int32_t(physicalDropDetected.confidencePctg), physicalDropDetected.accelPeak, + physicalDropDetected.freefallDuration); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportChargeCycles(const ChargeCycles& chargeCycles) { + std::vector<int32_t> buckets = chargeCycles.cycleBucket; + int initialSize = buckets.size(); + for (int i = 0; i < 10 - initialSize; i++) { + buckets.push_back(-1); // Push -1 for buckets that do not exist. + } + android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1], + buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8], + buckets[9]); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportBatteryHealthSnapshot( + const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) { + android::util::stats_write(android::util::BATTERY_HEALTH_SNAPSHOT, + int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC, + batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA, + batteryHealthSnapshotArgs.openCircuitVoltageMicroV, + batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportSlowIo(const SlowIo& slowIo) { + android::util::stats_write(android::util::SLOW_IO, int32_t(slowIo.operation), slowIo.count); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportBatteryCausedShutdown( + const BatteryCausedShutdown& batteryCausedShutdown) { + android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN, + batteryCausedShutdown.voltageMicroV); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportUsbPortOverheatEvent( + const UsbPortOverheatEvent& usbPortOverheatEvent) { + android::util::stats_write(android::util::USB_PORT_OVERHEAT_EVENT_REPORTED, + usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC, + usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis, + usbPortOverheatEvent.timeToInactive); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportSpeechDspStat( + const SpeechDspStat& speechDspStat) { + android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED, + speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis, + speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount); + + return hardware::Void(); +} + +hardware::Return<void> StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) { + ALOGW("reportVendorAtom unsupported"); + std::string reverDomainName = vendorAtom.reverseDomainName; + return hardware::Void(); +} + +} // namespace implementation +} // namespace V1_0 +} // namespace stats +} // namespace frameworks +} // namespace android diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats@1.0-service.xml new file mode 100644 index 0000000000..bb02f66a28 --- /dev/null +++ b/services/stats/android.frameworks.stats@1.0-service.xml @@ -0,0 +1,11 @@ +<manifest version="1.0" type="framework"> + <hal> + <name>android.frameworks.stats</name> + <transport>hwbinder</transport> + <version>1.0</version> + <interface> + <name>IStats</name> + <instance>default</instance> + </interface> + </hal> +</manifest> diff --git a/services/stats/include/stats/StatsHal.h b/services/stats/include/stats/StatsHal.h new file mode 100644 index 0000000000..ad14263426 --- /dev/null +++ b/services/stats/include/stats/StatsHal.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android/frameworks/stats/1.0/IStats.h> +#include <android/frameworks/stats/1.0/types.h> + +using namespace android::frameworks::stats::V1_0; + +namespace android { +namespace frameworks { +namespace stats { +namespace V1_0 { +namespace implementation { + +using android::hardware::Return; + +/** +* Implements the Stats HAL +*/ +class StatsHal : public IStats { +public: + StatsHal(); + + /** + * Binder call to get SpeakerImpedance atom. + */ + virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override; + + /** + * Binder call to get HardwareFailed atom. + */ + virtual Return<void> reportHardwareFailed(const HardwareFailed& hardwareFailed) override; + + /** + * Binder call to get PhysicalDropDetected atom. + */ + virtual Return<void> reportPhysicalDropDetected( + const PhysicalDropDetected& physicalDropDetected) override; + + /** + * Binder call to get ChargeCyclesReported atom. + */ + virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override; + + /** + * Binder call to get BatteryHealthSnapshot atom. + */ + virtual Return<void> reportBatteryHealthSnapshot( + const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) override; + + /** + * Binder call to get SlowIo atom. + */ + virtual Return<void> reportSlowIo(const SlowIo& slowIo) override; + + /** + * Binder call to get BatteryCausedShutdown atom. + */ + virtual Return<void> reportBatteryCausedShutdown( + const BatteryCausedShutdown& batteryCausedShutdown) override; + + /** + * Binder call to get UsbPortOverheatEvent atom. + */ + virtual Return<void> reportUsbPortOverheatEvent( + const UsbPortOverheatEvent& usbPortOverheatEvent) override; + + /** + * Binder call to get Speech DSP state atom. + */ + virtual Return<void> reportSpeechDspStat( + const SpeechDspStat& speechDspStat) override; + + /** + * Binder call to get vendor atom. + */ + virtual Return<void> reportVendorAtom(const VendorAtom& vendorAtom) override; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace stats +} // namespace frameworks +} // namespace android diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 501d176401..cda982ac23 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -43,8 +43,6 @@ cc_defaults { "libgui", "libhardware", "libhidlbase", - "libhidltransport", - "libhwbinder", "liblayers_proto", "liblog", "libnativewindow", @@ -85,8 +83,6 @@ cc_defaults { "android.hardware.graphics.composer@2.3", "android.hardware.power@1.3", "libhidlbase", - "libhidltransport", - "libhwbinder", ], } @@ -100,6 +96,10 @@ cc_defaults { lto: { thin: true, }, + // TODO(b/131771163): Fix broken fuzzer support with LTO. + sanitize: { + fuzzer: false, + }, } cc_library_headers { @@ -183,9 +183,6 @@ cc_defaults { cflags: [ "-DLOG_TAG=\"SurfaceFlinger\"", ], - whole_static_libs: [ - "libsigchain", - ], shared_libs: [ "android.frameworks.displayservice@1.0", "android.hardware.configstore-utils", @@ -196,7 +193,6 @@ cc_defaults { "libcutils", "libdisplayservicehidl", "libhidlbase", - "libhidltransport", "libinput", "liblayers_proto", "liblog", @@ -245,8 +241,6 @@ cc_library_shared { "android.hardware.configstore@1.1", "android.hardware.graphics.common@1.2", "libhidlbase", - "libhidltransport", - "libhwbinder", "libui", "libutils", "liblog", @@ -257,8 +251,6 @@ cc_library_shared { export_shared_lib_headers: [ "android.hardware.graphics.common@1.2", "libhidlbase", - "libhidltransport", - "libhwbinder", ], export_static_lib_headers: [ "SurfaceFlingerProperties", diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index eb1bf66cd6..af5fe62469 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -243,8 +243,9 @@ bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) { bool sidebandStreamChanged = true; if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) { // mSidebandStreamChanged was changed to false + mSidebandStream = mConsumer->getSidebandStream(); auto& layerCompositionState = getCompositionLayer()->editState().frontEnd; - layerCompositionState.sidebandStream = mConsumer->getSidebandStream(); + layerCompositionState.sidebandStream = mSidebandStream; if (layerCompositionState.sidebandStream != nullptr) { setTransactionFlags(eTransactionNeeded); mFlinger->setTransactionFlags(eTraversalNeeded); diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 6f076ad11f..1c31ab9d7d 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -118,6 +118,13 @@ cc_test { // // You can either "make dist tests" before flashing, or set this // option to false temporarily. - address: true, + + + // FIXME: ASAN build is broken for a while, but was not discovered + // since new PM silently suppressed ASAN. Temporarily turn off ASAN + // to unblock the compiler upgrade process. + // address: true, + // http://b/139747256 + address: false, }, } diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 4a13bfb7ae..5700d72cae 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -272,13 +272,17 @@ void DisplayDevice::setProjection(int orientation, scissor = displayBounds; } + uint32_t transformOrientation; + if (isPrimary()) { sPrimaryDisplayOrientation = displayStateOrientationToTransformOrientation(orientation); + transformOrientation = displayStateOrientationToTransformOrientation( + (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1)); + } else { + transformOrientation = displayStateOrientationToTransformOrientation(orientation); } - getCompositionDisplay()->setProjection(globalTransform, - displayStateOrientationToTransformOrientation( - orientation), + getCompositionDisplay()->setProjection(globalTransform, transformOrientation, frame, viewport, scissor, needsFiltering); } diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 44f3eae1ea..3e6ddedf48 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -153,7 +153,6 @@ void Layer::removeRemoteSyncPoints() { mRemoteSyncPoints.clear(); { - Mutex::Autolock pendingStateLock(mPendingStateMutex); for (State pendingState : mPendingStates) { pendingState.barrierLayer_legacy = nullptr; } @@ -907,6 +906,7 @@ uint32_t Layer::doTransaction(uint32_t flags) { // Commit the transaction commitTransaction(c); + mPendingStatesSnapshot = mPendingStates; mCurrentState.callbackHandles = {}; return flags; } @@ -1874,14 +1874,61 @@ void Layer::setInputInfo(const InputWindowInfo& info) { setTransactionFlags(eTransactionNeeded); } -void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet, - uint32_t traceFlags) { +void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags) const { + ui::Transform transform = getTransform(); + + if (traceFlags & SurfaceTracing::TRACE_CRITICAL) { + for (const auto& pendingState : mPendingStatesSnapshot) { + auto barrierLayer = pendingState.barrierLayer_legacy.promote(); + if (barrierLayer != nullptr) { + BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer(); + barrierLayerProto->set_id(barrierLayer->sequence); + barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy); + } + } + + auto buffer = mActiveBuffer; + if (buffer != nullptr) { + LayerProtoHelper::writeToProto(buffer, + [&]() { return layerInfo->mutable_active_buffer(); }); + LayerProtoHelper::writeToProto(ui::Transform(mCurrentTransform), + layerInfo->mutable_buffer_transform()); + } + layerInfo->set_invalidate(contentDirty); + layerInfo->set_is_protected(isProtected()); + layerInfo->set_dataspace( + dataspaceDetails(static_cast<android_dataspace>(mCurrentDataSpace))); + layerInfo->set_queued_frames(getQueuedFrameCount()); + layerInfo->set_refresh_pending(isBufferLatched()); + layerInfo->set_curr_frame(mCurrentFrameNumber); + layerInfo->set_effective_scaling_mode(getEffectiveScalingMode()); + + layerInfo->set_corner_radius(getRoundedCornerState().radius); + LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform()); + LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), + [&]() { return layerInfo->mutable_position(); }); + LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); }); + LayerProtoHelper::writeToProto(visibleRegion, + [&]() { return layerInfo->mutable_visible_region(); }); + LayerProtoHelper::writeToProto(surfaceDamageRegion, + [&]() { return layerInfo->mutable_damage_region(); }); + } + + if (traceFlags & SurfaceTracing::TRACE_EXTRA) { + LayerProtoHelper::writeToProto(mSourceBounds, + [&]() { return layerInfo->mutable_source_bounds(); }); + LayerProtoHelper::writeToProto(mScreenBounds, + [&]() { return layerInfo->mutable_screen_bounds(); }); + } +} + +void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet, + uint32_t traceFlags) const { const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren; const State& state = useDrawing ? mDrawingState : mCurrentState; ui::Transform requestedTransform = state.active_legacy.transform; - ui::Transform transform = getTransform(); if (traceFlags & SurfaceTracing::TRACE_CRITICAL) { layerInfo->set_id(sequence); @@ -1901,17 +1948,10 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet, LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy, [&]() { return layerInfo->mutable_transparent_region(); }); - LayerProtoHelper::writeToProto(visibleRegion, - [&]() { return layerInfo->mutable_visible_region(); }); - LayerProtoHelper::writeToProto(surfaceDamageRegion, - [&]() { return layerInfo->mutable_damage_region(); }); layerInfo->set_layer_stack(getLayerStack()); layerInfo->set_z(state.z); - LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), - [&]() { return layerInfo->mutable_position(); }); - LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() { return layerInfo->mutable_requested_position(); @@ -1922,15 +1962,9 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet, LayerProtoHelper::writeToProto(state.crop_legacy, [&]() { return layerInfo->mutable_crop(); }); - layerInfo->set_corner_radius(getRoundedCornerState().radius); layerInfo->set_is_opaque(isOpaque(state)); - layerInfo->set_invalidate(contentDirty); - layerInfo->set_is_protected(isProtected()); - // XXX (b/79210409) mCurrentDataSpace is not protected - layerInfo->set_dataspace( - dataspaceDetails(static_cast<android_dataspace>(mCurrentDataSpace))); layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat())); LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); }); @@ -1938,7 +1972,6 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet, [&]() { return layerInfo->mutable_requested_color(); }); layerInfo->set_flags(state.flags); - LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform()); LayerProtoHelper::writeToProto(requestedTransform, layerInfo->mutable_requested_transform()); @@ -1955,29 +1988,6 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet, } else { layerInfo->set_z_order_relative_of(-1); } - - auto buffer = mActiveBuffer; - if (buffer != nullptr) { - LayerProtoHelper::writeToProto(buffer, - [&]() { return layerInfo->mutable_active_buffer(); }); - LayerProtoHelper::writeToProto(ui::Transform(mCurrentTransform), - layerInfo->mutable_buffer_transform()); - } - - layerInfo->set_queued_frames(getQueuedFrameCount()); - layerInfo->set_refresh_pending(isBufferLatched()); - layerInfo->set_curr_frame(mCurrentFrameNumber); - layerInfo->set_effective_scaling_mode(getEffectiveScalingMode()); - - for (const auto& pendingState : mPendingStates) { - auto barrierLayer = pendingState.barrierLayer_legacy.promote(); - if (barrierLayer != nullptr) { - BarrierLayerProto* barrierLayerProto = layerInfo->add_barrier_layer(); - barrierLayerProto->set_id(barrierLayer->sequence); - barrierLayerProto->set_frame_number(pendingState.frameNumber_legacy); - } - } - LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); }); } if (traceFlags & SurfaceTracing::TRACE_INPUT) { @@ -1990,23 +2000,19 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet, for (const auto& entry : state.metadata.mMap) { (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend()); } - LayerProtoHelper::writeToProto(mEffectiveTransform, - layerInfo->mutable_effective_transform()); - LayerProtoHelper::writeToProto(mSourceBounds, - [&]() { return layerInfo->mutable_source_bounds(); }); - LayerProtoHelper::writeToProto(mScreenBounds, - [&]() { return layerInfo->mutable_screen_bounds(); }); } } -void Layer::writeToProto(LayerProto* layerInfo, const sp<DisplayDevice>& displayDevice, - uint32_t traceFlags) { +void Layer::writeToProtoCompositionState(LayerProto* layerInfo, + const sp<DisplayDevice>& displayDevice, + uint32_t traceFlags) const { auto outputLayer = findOutputLayerForDisplay(displayDevice); if (!outputLayer) { return; } - writeToProto(layerInfo, LayerVector::StateSet::Drawing, traceFlags); + writeToProtoDrawingState(layerInfo, traceFlags); + writeToProtoCommonState(layerInfo, LayerVector::StateSet::Drawing, traceFlags); const auto& compositionState = outputLayer->getState(); @@ -2024,26 +2030,12 @@ void Layer::writeToProto(LayerProto* layerInfo, const sp<DisplayDevice>& display static_cast<int32_t>(compositionState.hwc ? (*compositionState.hwc).hwcCompositionType : Hwc2::IComposerClient::Composition::CLIENT); layerInfo->set_hwc_composition_type(compositionType); - - if (std::strcmp(getTypeId(), "BufferLayer") == 0 && - static_cast<BufferLayer*>(this)->isProtected()) { - layerInfo->set_is_protected(true); - } else { - layerInfo->set_is_protected(false); - } } bool Layer::isRemovedFromCurrentState() const { return mRemovedFromCurrentState; } -// Debug helper for b/137560795 -#define INT32_MIGHT_OVERFLOW(n) (((n) >= INT32_MAX / 2) || ((n) <= INT32_MIN / 2)) - -#define RECT_BOUNDS_INVALID(rect) \ - (INT32_MIGHT_OVERFLOW((rect).left) || INT32_MIGHT_OVERFLOW((rect).right) || \ - INT32_MIGHT_OVERFLOW((rect).bottom) || INT32_MIGHT_OVERFLOW((rect).top)) - InputWindowInfo Layer::fillInputInfo() { InputWindowInfo info = mDrawingState.inputInfo; @@ -2054,14 +2046,14 @@ InputWindowInfo Layer::fillInputInfo() { ui::Transform t = getTransform(); const float xScale = t.sx(); const float yScale = t.sy(); - float xSurfaceInset = info.surfaceInset; - float ySurfaceInset = info.surfaceInset; + int32_t xSurfaceInset = info.surfaceInset; + int32_t ySurfaceInset = info.surfaceInset; if (xScale != 1.0f || yScale != 1.0f) { - info.windowXScale *= 1.0f / xScale; - info.windowYScale *= 1.0f / yScale; + info.windowXScale *= (xScale != 0.0f) ? 1.0f / xScale : 0.0f; + info.windowYScale *= (yScale != 0.0f) ? 1.0f / yScale : 0.0f; info.touchableRegion.scaleSelf(xScale, yScale); - xSurfaceInset *= xScale; - ySurfaceInset *= yScale; + xSurfaceInset = std::round(xSurfaceInset * xScale); + ySurfaceInset = std::round(ySurfaceInset * yScale); } // Transform layer size to screen space and inset it by surface insets. @@ -2074,25 +2066,10 @@ InputWindowInfo Layer::fillInputInfo() { } layerBounds = t.transform(layerBounds); - // debug check for b/137560795 - { - if (RECT_BOUNDS_INVALID(layerBounds)) { - ALOGE("layer %s bounds are invalid (%" PRIi32 ", %" PRIi32 ", %" PRIi32 ", %" PRIi32 - ")", - mName.c_str(), layerBounds.left, layerBounds.top, layerBounds.right, - layerBounds.bottom); - std::string out; - getTransform().dump(out, "Transform"); - ALOGE("%s", out.c_str()); - layerBounds.left = layerBounds.top = layerBounds.right = layerBounds.bottom = 0; - } + // clamp inset to layer bounds + xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0; + ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0; - if (INT32_MIGHT_OVERFLOW(xSurfaceInset) || INT32_MIGHT_OVERFLOW(ySurfaceInset)) { - ALOGE("layer %s surface inset are invalid (%" PRIi32 ", %" PRIi32 ")", mName.c_str(), - int32_t(xSurfaceInset), int32_t(ySurfaceInset)); - xSurfaceInset = ySurfaceInset = 0; - } - } layerBounds.inset(xSurfaceInset, ySurfaceInset, xSurfaceInset, ySurfaceInset); // Input coordinate should match the layer bounds. diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index b46eb112e7..3b4d8733c7 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -437,11 +437,21 @@ public: bool isRemovedFromCurrentState() const; - void writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet, - uint32_t traceFlags = SurfaceTracing::TRACE_ALL); - - void writeToProto(LayerProto* layerInfo, const sp<DisplayDevice>& displayDevice, - uint32_t traceFlags = SurfaceTracing::TRACE_ALL); + // Write states that are modified by the main thread. This includes drawing + // state as well as buffer data. This should be called in the main or tracing + // thread. + void writeToProtoDrawingState(LayerProto* layerInfo, + uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; + // Write states that are modified by the main thread. This includes drawing + // state as well as buffer data and composition data for layers on the specified + // display. This should be called in the main or tracing thread. + void writeToProtoCompositionState(LayerProto* layerInfo, const sp<DisplayDevice>& displayDevice, + uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; + // Write drawing or current state. If writing current state, the caller should hold the + // external mStateLock. If writing drawing state, this function should be called on the + // main or tracing thread. + void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet stateSet, + uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; virtual Geometry getActiveGeometry(const Layer::State& s) const { return s.active_legacy; } virtual uint32_t getActiveWidth(const Layer::State& s) const { return s.active_legacy.w; } @@ -831,13 +841,15 @@ protected: bool mPrimaryDisplayOnly = false; - // these are protected by an external lock - State mCurrentState; + // These are only accessed by the main thread or the tracing thread. State mDrawingState; - std::atomic<uint32_t> mTransactionFlags{0}; + // Store a copy of the pending state so that the drawing thread can access the + // states without a lock. + Vector<State> mPendingStatesSnapshot; - // Accessed from main thread and binder threads - Mutex mPendingStateMutex; + // these are protected by an external lock (mStateLock) + State mCurrentState; + std::atomic<uint32_t> mTransactionFlags{0}; Vector<State> mPendingStates; // Timestamp history for UIAutomation. Thread safe. diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS index 69d8c89b45..f2bc65db35 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -5,4 +5,6 @@ chaviw@google.com lpy@google.com marissaw@google.com racarr@google.com -stoza@google.com
\ No newline at end of file +steventhomas@google.com +stoza@google.com +vhau@google.com diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index f8bd95872c..8da5612b5b 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -601,11 +601,6 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() { return currRefreshRateType; } -Scheduler::RefreshRateType Scheduler::getPreferredRefreshRateType() { - std::lock_guard<std::mutex> lock(mFeatureStateLock); - return mRefreshRateType; -} - void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) { std::lock_guard<std::mutex> lock(mCallbackLock); if (mChangeRefreshRateCallback) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 38184570c1..5d8bb4cd2f 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -189,9 +189,6 @@ public: // calls DispSync::dump() on primary disp sync void dumpPrimaryDispSync(std::string& result) const; - // Get the appropriate refresh type for current conditions. - RefreshRateType getPreferredRefreshRateType(); - protected: virtual std::unique_ptr<EventThread> makeEventThread( const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs, diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 32748cffc2..7047710e85 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -540,9 +540,9 @@ void SurfaceFlinger::bootFinished() // wait patiently for the window manager death const String16 name("window"); - sp<IBinder> window(defaultServiceManager()->getService(name)); - if (window != 0) { - window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)); + mWindowManager = defaultServiceManager()->getService(name); + if (mWindowManager != 0) { + mWindowManager->linkToDeath(static_cast<IBinder::DeathRecipient*>(this)); } sp<IBinder> input(defaultServiceManager()->getService( String16("inputflinger"))); @@ -1825,6 +1825,7 @@ void SurfaceFlinger::handleMessageRefresh() { preComposition(); rebuildLayerStacks(); calculateWorkingSet(); + long compositionTime = elapsedRealtimeNano(); for (const auto& [token, display] : mDisplays) { beginFrame(display); prepareFrame(display); @@ -1851,6 +1852,12 @@ void SurfaceFlinger::handleMessageRefresh() { mVsyncModulator.onRefreshed(mHadClientComposition); mLayersWithQueuedFrames.clear(); + if (mVisibleRegionsDirty) { + mVisibleRegionsDirty = false; + if (mTracingEnabled) { + mTracing.notify(compositionTime, "visibleRegionsDirty"); + } + } } @@ -1860,9 +1867,6 @@ bool SurfaceFlinger::handleMessageInvalidate() { if (mVisibleRegionsDirty) { computeLayerBounds(); - if (mTracingEnabled) { - mTracing.notify("visibleRegionsDirty"); - } } for (auto& layer : mLayersPendingRefresh) { @@ -2269,7 +2273,6 @@ void SurfaceFlinger::rebuildLayerStacks() { // rebuild the visible layer list per screen if (CC_UNLIKELY(mVisibleRegionsDirty)) { ATRACE_NAME("rebuildLayerStacks VR Dirty"); - mVisibleRegionsDirty = false; invalidateHwcGeometry(); for (const auto& pair : mDisplays) { @@ -4656,18 +4659,22 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, if (const auto it = dumpers.find(flag); it != dumpers.end()) { (it->second)(args, asProto, result); - } else { - if (asProto) { - LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current); - result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize()); - } else { - dumpAllLocked(args, result); - } + } else if (!asProto) { + dumpAllLocked(args, result); } if (locked) { mStateLock.unlock(); } + + LayersProto layersProto = dumpProtoFromMainThread(); + if (asProto) { + result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize()); + } else { + auto layerTree = LayerProtoParser::generateLayerTree(layersProto); + result.append(LayerProtoParser::layerTreeToString(layerTree)); + result.append("\n"); + } } write(fd, result.c_str(), result.size()); return NO_ERROR; @@ -4910,19 +4917,23 @@ void SurfaceFlinger::dumpWideColorInfo(std::string& result) const { result.append("\n"); } -LayersProto SurfaceFlinger::dumpProtoInfo(LayerVector::StateSet stateSet, - uint32_t traceFlags) const { +LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const { LayersProto layersProto; - const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; - const State& state = useDrawing ? mDrawingState : mCurrentState; - state.traverseInZOrder([&](Layer* layer) { + mDrawingState.traverseInZOrder([&](Layer* layer) { LayerProto* layerProto = layersProto.add_layers(); - layer->writeToProto(layerProto, stateSet, traceFlags); + layer->writeToProtoDrawingState(layerProto, traceFlags); + layer->writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags); }); return layersProto; } +LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) { + LayersProto layersProto; + postMessageSync(new LambdaMessage([&]() { layersProto = dumpDrawingStateProto(traceFlags); })); + return layersProto; +} + LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo( const sp<DisplayDevice>& displayDevice) const { LayersProto layersProto; @@ -4943,7 +4954,7 @@ LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo( mDrawingState.traverseInZOrder([&](Layer* layer) { if (!layer->visibleRegion.isEmpty() && !display->getOutputLayersOrderedByZ().empty()) { LayerProto* layerProto = layersProto.add_layers(); - layer->writeToProto(layerProto, displayDevice); + layer->writeToProtoCompositionState(layerProto, displayDevice); } }); @@ -5008,13 +5019,6 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co colorizer.reset(result); { - LayersProto layersProto = dumpProtoInfo(LayerVector::StateSet::Current); - auto layerTree = LayerProtoParser::generateLayerTree(layersProto); - result.append(LayerProtoParser::layerTreeToString(layerTree)); - result.append("\n"); - } - - { StringAppendF(&result, "Composition layers\n"); mDrawingState.traverseInZOrder([&](Layer* layer) { auto compositionLayer = layer->getCompositionLayer(); @@ -6173,25 +6177,6 @@ void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& disp } } -void SurfaceFlinger::setPreferredDisplayConfig() { - const auto& type = mScheduler->getPreferredRefreshRateType(); - const auto& config = mRefreshRateConfigs.getRefreshRate(type); - if (config && isDisplayConfigAllowed(config->configId)) { - ALOGV("switching to Scheduler preferred config %d", config->configId); - setDesiredActiveConfig({type, config->configId, Scheduler::ConfigEvent::Changed}); - } else { - // Set the highest allowed config by iterating backwards on available refresh rates - const auto& refreshRates = mRefreshRateConfigs.getRefreshRates(); - for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) { - if (iter->second && isDisplayConfigAllowed(iter->second->configId)) { - ALOGV("switching to allowed config %d", iter->second->configId); - setDesiredActiveConfig({iter->first, iter->second->configId, - Scheduler::ConfigEvent::Changed}); - } - } - } -} - void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display, const std::vector<int32_t>& allowedConfigs) { if (!display->isPrimary()) { @@ -6213,7 +6198,16 @@ void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& d mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, display->getActiveConfig()); - setPreferredDisplayConfig(); + // Set the highest allowed config by iterating backwards on available refresh rates + const auto& refreshRates = mRefreshRateConfigs.getRefreshRates(); + for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) { + if (iter->second && isDisplayConfigAllowed(iter->second->configId)) { + ALOGV("switching to config %d", iter->second->configId); + setDesiredActiveConfig( + {iter->first, iter->second->configId, Scheduler::ConfigEvent::Changed}); + break; + } + } } status_t SurfaceFlinger::setAllowedDisplayConfigs(const sp<IBinder>& displayToken, diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 9744862172..8e4203aed5 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -263,7 +263,8 @@ public: status_t postMessageAsync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0); // post a synchronous message to the main thread - status_t postMessageSync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0); + status_t postMessageSync(const sp<MessageBase>& msg, nsecs_t reltime = 0, uint32_t flags = 0) + EXCLUDES(mStateLock); // force full composition on all displays void repaintEverything(); @@ -508,9 +509,9 @@ private: using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType; struct ActiveConfigInfo { - RefreshRateType type; - int configId; - Scheduler::ConfigEvent event; + RefreshRateType type = RefreshRateType::DEFAULT; + int configId = 0; + Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None; bool operator!=(const ActiveConfigInfo& other) const { return type != other.type || configId != other.configId || event != other.event; @@ -534,9 +535,6 @@ private: // called on the main thread in response to setPowerMode() void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock); - // Query the Scheduler or allowed display configs list for a matching config, and set it - void setPreferredDisplayConfig() REQUIRES(mStateLock); - // called on the main thread in response to setAllowedDisplayConfigs() void setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display, const std::vector<int32_t>& allowedConfigs) @@ -902,8 +900,9 @@ private: void dumpBufferingStats(std::string& result) const; void dumpDisplayIdentificationData(std::string& result) const; void dumpWideColorInfo(std::string& result) const; - LayersProto dumpProtoInfo(LayerVector::StateSet stateSet, - uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; + LayersProto dumpDrawingStateProto(uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; + LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL) + EXCLUDES(mStateLock); void withTracingLock(std::function<void()> operation) REQUIRES(mStateLock); LayersProto dumpVisibleLayersProtoInfo(const sp<DisplayDevice>& display) const; @@ -1104,6 +1103,9 @@ private: // either AID_GRAPHICS or AID_SYSTEM. status_t CheckTransactCodeCredentials(uint32_t code); + // to linkToDeath + sp<IBinder> mWindowManager; + std::unique_ptr<dvr::VrFlinger> mVrFlinger; std::atomic<bool> mVrFlingerRequestsDisplay = false; static bool useVrFlinger; diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp index c4ab0668e7..5d9be0b8a9 100644 --- a/services/surfaceflinger/SurfaceTracing.cpp +++ b/services/surfaceflinger/SurfaceTracing.cpp @@ -68,8 +68,9 @@ bool SurfaceTracing::addTraceToBuffer(LayersTraceProto& entry) { return mEnabled; } -void SurfaceTracing::notify(const char* where) { +void SurfaceTracing::notify(long compositionTime, const char* where) { std::scoped_lock lock(mSfLock); + mCompositionTime = compositionTime; mWhere = where; mCanStartTrace.notify_one(); } @@ -160,9 +161,9 @@ LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) { ATRACE_CALL(); LayersTraceProto entry; - entry.set_elapsed_realtime_nanos(elapsedRealtimeNano()); + entry.set_elapsed_realtime_nanos(mCompositionTime); entry.set_where(where); - LayersProto layers(mFlinger.dumpProtoInfo(LayerVector::StateSet::Drawing, mTraceFlags)); + LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags)); entry.mutable_layers()->Swap(&layers); return entry; diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h index 4773307a65..395d5622c7 100644 --- a/services/surfaceflinger/SurfaceTracing.h +++ b/services/surfaceflinger/SurfaceTracing.h @@ -46,7 +46,7 @@ public: bool disable(); status_t writeToFile(); bool isEnabled() const; - void notify(const char* where); + void notify(long compositionTime, const char* where); void setBufferSize(size_t bufferSizeInByte); void writeToFileAsync(); @@ -81,6 +81,8 @@ private: std::queue<LayersTraceProto> mStorage; }; + long mCompositionTime; + void mainLoop(); void addFirstEntry(); LayersTraceProto traceWhenNotified(); diff --git a/services/surfaceflinger/TimeStats/OWNERS b/services/surfaceflinger/TimeStats/OWNERS index ac02d12fcd..1441f91489 100644 --- a/services/surfaceflinger/TimeStats/OWNERS +++ b/services/surfaceflinger/TimeStats/OWNERS @@ -1 +1,2 @@ -zzyiwei@google.com
\ No newline at end of file +alecmouri@google.com +zzyiwei@google.com diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp index cb368b0886..d03cb7b22a 100644 --- a/services/surfaceflinger/layerproto/Android.bp +++ b/services/surfaceflinger/layerproto/Android.bp @@ -43,7 +43,7 @@ java_library_static { type: "nano", }, srcs: ["*.proto"], - no_framework_libs: true, + sdk_version: "core_platform", target: { android: { jarjar_rules: "jarjar-rules.txt", diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc index aea602bba4..d3942e8bbe 100644 --- a/services/surfaceflinger/surfaceflinger.rc +++ b/services/surfaceflinger/surfaceflinger.rc @@ -2,6 +2,7 @@ service surfaceflinger /system/bin/surfaceflinger class core animation user system group graphics drmrpc readproc + capabilities SYS_NICE onrestart restart zygote writepid /dev/stune/foreground/tasks socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0 diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index 74baf37731..a4f4285552 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -36,7 +36,7 @@ owner: Platform prop { api_name: "vsync_event_phase_offset_ns" type: Long - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.vsync_event_phase_offset_ns" } @@ -44,7 +44,7 @@ prop { prop { api_name: "vsync_sf_event_phase_offset_ns" type: Long - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.vsync_sf_event_phase_offset_ns" } @@ -53,7 +53,7 @@ prop { prop { api_name: "use_context_priority" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.use_context_priority" } @@ -62,7 +62,7 @@ prop { prop { api_name: "max_frame_buffer_acquired_buffers" type: Long - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers" } @@ -80,7 +80,7 @@ prop { prop { api_name: "has_wide_color_display" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.has_wide_color_display" } @@ -90,7 +90,7 @@ prop { prop { api_name: "running_without_sync_framework" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.running_without_sync_framework" } @@ -108,7 +108,7 @@ prop { prop { api_name: "has_HDR_display" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.has_HDR_display" } @@ -117,7 +117,7 @@ prop { prop { api_name: "present_time_offset_from_vsync_ns" type: Long - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.present_time_offset_from_vsync_ns" } @@ -129,7 +129,7 @@ prop { prop { api_name: "force_hwc_copy_for_virtual_displays" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays" } @@ -139,7 +139,7 @@ prop { prop { api_name: "max_virtual_display_dimension" type: Long - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.max_virtual_display_dimension" } @@ -151,7 +151,7 @@ prop { prop { api_name: "use_vr_flinger" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.use_vr_flinger" } @@ -161,7 +161,7 @@ prop { prop { api_name: "start_graphics_allocator_service" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.start_graphics_allocator_service" } @@ -171,7 +171,7 @@ prop { api_name: "primary_display_orientation" type: Enum enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270" - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.primary_display_orientation" } @@ -182,7 +182,7 @@ prop { prop { api_name: "use_color_management" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.use_color_management" } @@ -209,7 +209,7 @@ prop { prop { api_name: "default_composition_dataspace" type: Long - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.default_composition_dataspace" } @@ -220,7 +220,7 @@ prop { prop { api_name: "default_composition_pixel_format" type: Integer - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.default_composition_pixel_format" } @@ -235,7 +235,7 @@ prop { prop { api_name: "wcg_composition_dataspace" type: Long - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.wcg_composition_dataspace" } @@ -246,7 +246,7 @@ prop { prop { api_name: "wcg_composition_pixel_format" type: Integer - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.wcg_composition_pixel_format" } @@ -260,7 +260,7 @@ prop { prop { api_name: "color_space_agnostic_dataspace" type: Long - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.color_space_agnostic_dataspace" } @@ -272,7 +272,7 @@ prop { prop { api_name: "display_primary_red" type: DoubleList - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.display_primary_red" } @@ -280,7 +280,7 @@ prop { prop { api_name: "display_primary_green" type: DoubleList - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.display_primary_green" } @@ -288,7 +288,7 @@ prop { prop { api_name: "display_primary_blue" type: DoubleList - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.display_primary_blue" } @@ -296,7 +296,7 @@ prop { prop { api_name: "display_primary_white" type: DoubleList - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.display_primary_white" } @@ -307,7 +307,7 @@ prop { prop { api_name: "set_idle_timer_ms" type: Integer - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.set_idle_timer_ms" } @@ -318,7 +318,7 @@ prop { prop { api_name: "set_touch_timer_ms" type: Integer - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.set_touch_timer_ms" } @@ -330,7 +330,7 @@ prop { prop { api_name: "set_display_power_timer_ms" type: Integer - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.set_display_power_timer_ms" } @@ -340,7 +340,7 @@ prop { prop { api_name: "use_smart_90_for_video" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.use_smart_90_for_video" } @@ -348,7 +348,7 @@ prop { prop { api_name: "enable_protected_contents" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.protected_contents" } @@ -358,7 +358,7 @@ prop { prop { api_name: "support_kernel_idle_timer" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.support_kernel_idle_timer" } diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt new file mode 100644 index 0000000000..b66e56ecc7 --- /dev/null +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -0,0 +1,138 @@ +props { + module: "android.sysprop.SurfaceFlingerProperties" + prop { + api_name: "color_space_agnostic_dataspace" + type: Long + prop_name: "ro.surface_flinger.color_space_agnostic_dataspace" + } + prop { + api_name: "default_composition_dataspace" + type: Long + prop_name: "ro.surface_flinger.default_composition_dataspace" + } + prop { + api_name: "default_composition_pixel_format" + type: Integer + prop_name: "ro.surface_flinger.default_composition_pixel_format" + } + prop { + api_name: "display_primary_blue" + type: DoubleList + prop_name: "ro.surface_flinger.display_primary_blue" + } + prop { + api_name: "display_primary_green" + type: DoubleList + prop_name: "ro.surface_flinger.display_primary_green" + } + prop { + api_name: "display_primary_red" + type: DoubleList + prop_name: "ro.surface_flinger.display_primary_red" + } + prop { + api_name: "display_primary_white" + type: DoubleList + prop_name: "ro.surface_flinger.display_primary_white" + } + prop { + api_name: "enable_protected_contents" + prop_name: "ro.surface_flinger.protected_contents" + } + prop { + api_name: "force_hwc_copy_for_virtual_displays" + prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays" + } + prop { + api_name: "has_HDR_display" + prop_name: "ro.surface_flinger.has_HDR_display" + } + prop { + api_name: "has_wide_color_display" + prop_name: "ro.surface_flinger.has_wide_color_display" + } + prop { + api_name: "max_frame_buffer_acquired_buffers" + type: Long + prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers" + } + prop { + api_name: "max_virtual_display_dimension" + type: Long + prop_name: "ro.surface_flinger.max_virtual_display_dimension" + } + prop { + api_name: "present_time_offset_from_vsync_ns" + type: Long + prop_name: "ro.surface_flinger.present_time_offset_from_vsync_ns" + } + prop { + api_name: "primary_display_orientation" + type: Enum + prop_name: "ro.surface_flinger.primary_display_orientation" + enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270" + } + prop { + api_name: "running_without_sync_framework" + prop_name: "ro.surface_flinger.running_without_sync_framework" + } + prop { + api_name: "set_display_power_timer_ms" + type: Integer + prop_name: "ro.surface_flinger.set_display_power_timer_ms" + } + prop { + api_name: "set_idle_timer_ms" + type: Integer + prop_name: "ro.surface_flinger.set_idle_timer_ms" + } + prop { + api_name: "set_touch_timer_ms" + type: Integer + prop_name: "ro.surface_flinger.set_touch_timer_ms" + } + prop { + api_name: "start_graphics_allocator_service" + prop_name: "ro.surface_flinger.start_graphics_allocator_service" + } + prop { + api_name: "support_kernel_idle_timer" + prop_name: "ro.surface_flinger.support_kernel_idle_timer" + } + prop { + api_name: "use_color_management" + prop_name: "ro.surface_flinger.use_color_management" + } + prop { + api_name: "use_context_priority" + prop_name: "ro.surface_flinger.use_context_priority" + } + prop { + api_name: "use_smart_90_for_video" + prop_name: "ro.surface_flinger.use_smart_90_for_video" + } + prop { + api_name: "use_vr_flinger" + prop_name: "ro.surface_flinger.use_vr_flinger" + } + prop { + api_name: "vsync_event_phase_offset_ns" + type: Long + prop_name: "ro.surface_flinger.vsync_event_phase_offset_ns" + } + prop { + api_name: "vsync_sf_event_phase_offset_ns" + type: Long + prop_name: "ro.surface_flinger.vsync_sf_event_phase_offset_ns" + } + prop { + api_name: "wcg_composition_dataspace" + type: Long + prop_name: "ro.surface_flinger.wcg_composition_dataspace" + } + prop { + api_name: "wcg_composition_pixel_format" + type: Integer + prop_name: "ro.surface_flinger.wcg_composition_pixel_format" + } +} diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt new file mode 100644 index 0000000000..b66e56ecc7 --- /dev/null +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt @@ -0,0 +1,138 @@ +props { + module: "android.sysprop.SurfaceFlingerProperties" + prop { + api_name: "color_space_agnostic_dataspace" + type: Long + prop_name: "ro.surface_flinger.color_space_agnostic_dataspace" + } + prop { + api_name: "default_composition_dataspace" + type: Long + prop_name: "ro.surface_flinger.default_composition_dataspace" + } + prop { + api_name: "default_composition_pixel_format" + type: Integer + prop_name: "ro.surface_flinger.default_composition_pixel_format" + } + prop { + api_name: "display_primary_blue" + type: DoubleList + prop_name: "ro.surface_flinger.display_primary_blue" + } + prop { + api_name: "display_primary_green" + type: DoubleList + prop_name: "ro.surface_flinger.display_primary_green" + } + prop { + api_name: "display_primary_red" + type: DoubleList + prop_name: "ro.surface_flinger.display_primary_red" + } + prop { + api_name: "display_primary_white" + type: DoubleList + prop_name: "ro.surface_flinger.display_primary_white" + } + prop { + api_name: "enable_protected_contents" + prop_name: "ro.surface_flinger.protected_contents" + } + prop { + api_name: "force_hwc_copy_for_virtual_displays" + prop_name: "ro.surface_flinger.force_hwc_copy_for_virtual_displays" + } + prop { + api_name: "has_HDR_display" + prop_name: "ro.surface_flinger.has_HDR_display" + } + prop { + api_name: "has_wide_color_display" + prop_name: "ro.surface_flinger.has_wide_color_display" + } + prop { + api_name: "max_frame_buffer_acquired_buffers" + type: Long + prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers" + } + prop { + api_name: "max_virtual_display_dimension" + type: Long + prop_name: "ro.surface_flinger.max_virtual_display_dimension" + } + prop { + api_name: "present_time_offset_from_vsync_ns" + type: Long + prop_name: "ro.surface_flinger.present_time_offset_from_vsync_ns" + } + prop { + api_name: "primary_display_orientation" + type: Enum + prop_name: "ro.surface_flinger.primary_display_orientation" + enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270" + } + prop { + api_name: "running_without_sync_framework" + prop_name: "ro.surface_flinger.running_without_sync_framework" + } + prop { + api_name: "set_display_power_timer_ms" + type: Integer + prop_name: "ro.surface_flinger.set_display_power_timer_ms" + } + prop { + api_name: "set_idle_timer_ms" + type: Integer + prop_name: "ro.surface_flinger.set_idle_timer_ms" + } + prop { + api_name: "set_touch_timer_ms" + type: Integer + prop_name: "ro.surface_flinger.set_touch_timer_ms" + } + prop { + api_name: "start_graphics_allocator_service" + prop_name: "ro.surface_flinger.start_graphics_allocator_service" + } + prop { + api_name: "support_kernel_idle_timer" + prop_name: "ro.surface_flinger.support_kernel_idle_timer" + } + prop { + api_name: "use_color_management" + prop_name: "ro.surface_flinger.use_color_management" + } + prop { + api_name: "use_context_priority" + prop_name: "ro.surface_flinger.use_context_priority" + } + prop { + api_name: "use_smart_90_for_video" + prop_name: "ro.surface_flinger.use_smart_90_for_video" + } + prop { + api_name: "use_vr_flinger" + prop_name: "ro.surface_flinger.use_vr_flinger" + } + prop { + api_name: "vsync_event_phase_offset_ns" + type: Long + prop_name: "ro.surface_flinger.vsync_event_phase_offset_ns" + } + prop { + api_name: "vsync_sf_event_phase_offset_ns" + type: Long + prop_name: "ro.surface_flinger.vsync_sf_event_phase_offset_ns" + } + prop { + api_name: "wcg_composition_dataspace" + type: Long + prop_name: "ro.surface_flinger.wcg_composition_dataspace" + } + prop { + api_name: "wcg_composition_pixel_format" + type: Integer + prop_name: "ro.surface_flinger.wcg_composition_pixel_format" + } +} diff --git a/services/surfaceflinger/sysprop/api/current.txt b/services/surfaceflinger/sysprop/api/current.txt deleted file mode 100644 index d802177e24..0000000000 --- a/services/surfaceflinger/sysprop/api/current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/services/surfaceflinger/sysprop/api/removed.txt b/services/surfaceflinger/sysprop/api/removed.txt deleted file mode 100644 index d802177e24..0000000000 --- a/services/surfaceflinger/sysprop/api/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/services/surfaceflinger/sysprop/api/system-current.txt b/services/surfaceflinger/sysprop/api/system-current.txt deleted file mode 100644 index 79854b36a2..0000000000 --- a/services/surfaceflinger/sysprop/api/system-current.txt +++ /dev/null @@ -1,45 +0,0 @@ -// Signature format: 2.0 -package android.sysprop { - - public final class SurfaceFlingerProperties { - method public static java.util.Optional<java.lang.Long> color_space_agnostic_dataspace(); - method public static java.util.Optional<java.lang.Long> default_composition_dataspace(); - method public static java.util.Optional<java.lang.Integer> default_composition_pixel_format(); - method public static java.util.List<java.lang.Double> display_primary_blue(); - method public static java.util.List<java.lang.Double> display_primary_green(); - method public static java.util.List<java.lang.Double> display_primary_red(); - method public static java.util.List<java.lang.Double> display_primary_white(); - method public static java.util.Optional<java.lang.Boolean> enable_protected_contents(); - method public static java.util.Optional<java.lang.Boolean> force_hwc_copy_for_virtual_displays(); - method public static java.util.Optional<java.lang.Boolean> has_HDR_display(); - method public static java.util.Optional<java.lang.Boolean> has_wide_color_display(); - method public static java.util.Optional<java.lang.Long> max_frame_buffer_acquired_buffers(); - method public static java.util.Optional<java.lang.Long> max_virtual_display_dimension(); - method public static java.util.Optional<java.lang.Long> present_time_offset_from_vsync_ns(); - method public static java.util.Optional<android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values> primary_display_orientation(); - method public static java.util.Optional<java.lang.Boolean> running_without_sync_framework(); - method public static java.util.Optional<java.lang.Integer> set_display_power_timer_ms(); - method public static java.util.Optional<java.lang.Integer> set_idle_timer_ms(); - method public static java.util.Optional<java.lang.Integer> set_touch_timer_ms(); - method public static java.util.Optional<java.lang.Boolean> start_graphics_allocator_service(); - method public static java.util.Optional<java.lang.Boolean> support_kernel_idle_timer(); - method public static java.util.Optional<java.lang.Boolean> use_color_management(); - method public static java.util.Optional<java.lang.Boolean> use_context_priority(); - method public static java.util.Optional<java.lang.Boolean> use_smart_90_for_video(); - method public static java.util.Optional<java.lang.Boolean> use_vr_flinger(); - method public static java.util.Optional<java.lang.Long> vsync_event_phase_offset_ns(); - method public static java.util.Optional<java.lang.Long> vsync_sf_event_phase_offset_ns(); - method public static java.util.Optional<java.lang.Long> wcg_composition_dataspace(); - method public static java.util.Optional<java.lang.Integer> wcg_composition_pixel_format(); - } - - public enum SurfaceFlingerProperties.primary_display_orientation_values { - method public String getPropValue(); - enum_constant public static final android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values ORIENTATION_0; - enum_constant public static final android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values ORIENTATION_180; - enum_constant public static final android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values ORIENTATION_270; - enum_constant public static final android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values ORIENTATION_90; - } - -} - diff --git a/services/surfaceflinger/sysprop/api/system-removed.txt b/services/surfaceflinger/sysprop/api/system-removed.txt deleted file mode 100644 index d802177e24..0000000000 --- a/services/surfaceflinger/sysprop/api/system-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/services/surfaceflinger/sysprop/api/test-current.txt b/services/surfaceflinger/sysprop/api/test-current.txt deleted file mode 100644 index d802177e24..0000000000 --- a/services/surfaceflinger/sysprop/api/test-current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/services/surfaceflinger/sysprop/api/test-removed.txt b/services/surfaceflinger/sysprop/api/test-removed.txt deleted file mode 100644 index d802177e24..0000000000 --- a/services/surfaceflinger/sysprop/api/test-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp index a2c0611b1e..2feff4570e 100644 --- a/services/surfaceflinger/tests/fakehwc/Android.bp +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -20,8 +20,6 @@ cc_test { "libgui", "libhardware", "libhidlbase", - "libhidltransport", - "libhwbinder", "liblayers_proto", "liblog", "libnativewindow", diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp index f9e0b6413b..a892a2abd0 100644 --- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp +++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp @@ -54,10 +54,11 @@ using namespace sftest; namespace { // Mock test helpers +using ::testing::_; +using ::testing::DoAll; using ::testing::Invoke; using ::testing::Return; using ::testing::SetArgPointee; -using ::testing::_; using Transaction = SurfaceComposerClient::Transaction; diff --git a/services/surfaceflinger/version-script32.txt b/services/surfaceflinger/version-script32.txt deleted file mode 100644 index 2340785c42..0000000000 --- a/services/surfaceflinger/version-script32.txt +++ /dev/null @@ -1,12 +0,0 @@ -{ -global: - EnsureFrontOfChain; - AddSpecialSignalHandlerFn; - RemoveSpecialSignalHandlerFn; - bsd_signal; - sigaction; - signal; - sigprocmask; -local: - *; -}; diff --git a/services/surfaceflinger/version-script64.txt b/services/surfaceflinger/version-script64.txt deleted file mode 100644 index acf36309ea..0000000000 --- a/services/surfaceflinger/version-script64.txt +++ /dev/null @@ -1,11 +0,0 @@ -{ -global: - EnsureFrontOfChain; - AddSpecialSignalHandlerFn; - RemoveSpecialSignalHandlerFn; - sigaction; - signal; - sigprocmask; -local: - *; -}; diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp index 4c34b938c8..c202b5c07c 100644 --- a/services/vr/hardware_composer/Android.bp +++ b/services/vr/hardware_composer/Android.bp @@ -1,180 +1,151 @@ cc_library_shared { - name: "libvr_hwc-hal", + name: "libvr_hwc-hal", + + srcs: [ + "impl/vr_hwc.cpp", + "impl/vr_composer_client.cpp", + ], + + static_libs: [ + "libbroadcastring", + "libdisplay", + ], + + shared_libs: [ + "android.frameworks.vr.composer@1.0", + "android.hardware.graphics.composer@2.1", + "android.hardware.graphics.mapper@2.0", + "android.hardware.graphics.mapper@3.0", + "libbase", + "libbufferhubqueue", + "libbinder", + "libcutils", + "libfmq", + "libhardware", + "libhidlbase", + "liblog", + "libsync", + "libui", + "libutils", + "libpdx_default_transport", + ], + + header_libs: [ + "android.hardware.graphics.composer@2.1-command-buffer", + "android.hardware.graphics.composer@2.1-hal", + ], + + export_header_lib_headers: [ + "android.hardware.graphics.composer@2.1-hal", + ], + + export_static_lib_headers: [ + "libdisplay", + ], + + export_shared_lib_headers: [ + "android.frameworks.vr.composer@1.0", + "android.hardware.graphics.composer@2.1", + ], + + export_include_dirs: ["."], + + cflags: [ + "-DLOG_TAG=\"vr_hwc\"", + "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", + "-Wall", + "-Werror", + "-Wno-error=unused-private-field", + // Warnings in vr_hwc.cpp to be fixed after sync of goog/master. + "-Wno-sign-compare", + "-Wno-unused-parameter", + ], - srcs: [ - "impl/vr_hwc.cpp", - "impl/vr_composer_client.cpp", - ], - - static_libs: [ - "libbroadcastring", - "libdisplay", - ], - - shared_libs: [ - "android.frameworks.vr.composer@1.0", - "android.hardware.graphics.composer@2.1", - "android.hardware.graphics.mapper@2.0", - "android.hardware.graphics.mapper@3.0", - "libbase", - "libbufferhubqueue", - "libbinder", - "libcutils", - "libfmq", - "libhardware", - "libhidlbase", - "libhidltransport", - "liblog", - "libsync", - "libui", - "libutils", - "libpdx_default_transport", - ], - - header_libs: [ - "android.hardware.graphics.composer@2.1-command-buffer", - "android.hardware.graphics.composer@2.1-hal", - ], - - export_header_lib_headers: [ - "android.hardware.graphics.composer@2.1-hal", - ], - - export_static_lib_headers: [ - "libdisplay", - ], - - export_shared_lib_headers: [ - "android.frameworks.vr.composer@1.0", - "android.hardware.graphics.composer@2.1", - ], - - export_include_dirs: ["."], - - cflags: [ - "-DLOG_TAG=\"vr_hwc\"", - "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", - "-Wall", - "-Werror", - "-Wno-error=unused-private-field", - // Warnings in vr_hwc.cpp to be fixed after sync of goog/master. - "-Wno-sign-compare", - "-Wno-unused-parameter", - ], - -} - -cc_library_static { - name: "libvr_hwc-binder", - srcs: [ - "aidl/android/dvr/IVrComposer.aidl", - "aidl/android/dvr/IVrComposerCallback.aidl", - "aidl/android/dvr/parcelable_composer_frame.cpp", - "aidl/android/dvr/parcelable_composer_layer.cpp", - "aidl/android/dvr/parcelable_unique_fd.cpp", - ], - aidl: { - include_dirs: ["frameworks/native/services/vr/hardware_composer/aidl"], - export_aidl_headers: true, - }, - export_include_dirs: ["aidl"], - - cflags: [ - "-Wall", - "-Werror", - ], - - shared_libs: [ - "libbinder", - "libui", - "libutils", - "libvr_hwc-hal", - ], } cc_library_static { - name: "libvr_hwc-impl", - srcs: [ - "vr_composer.cpp", - ], - static_libs: [ - "libvr_hwc-binder", - ], - shared_libs: [ - "libbase", - "libbinder", - "liblog", - "libui", - "libutils", - "libvr_hwc-hal", - ], - export_shared_lib_headers: [ - "libvr_hwc-hal", - ], - cflags: [ - "-DLOG_TAG=\"vr_hwc\"", - "-Wall", - "-Werror", - ], + name: "libvr_hwc-impl", + srcs: [ + "vr_composer.cpp", + ], + static_libs: [ + "libvr_hwc-binder", + ], + shared_libs: [ + "libbase", + "libbinder", + "liblog", + "libui", + "libutils", + "libvr_hwc-hal", + ], + export_shared_lib_headers: [ + "libvr_hwc-hal", + ], + cflags: [ + "-DLOG_TAG=\"vr_hwc\"", + "-Wall", + "-Werror", + ], } cc_binary { - name: "vr_hwc", - vintf_fragments: ["manifest_vr_hwc.xml"], - srcs: [ - "vr_hardware_composer_service.cpp" - ], - static_libs: [ - "libvr_hwc-impl", - // NOTE: This needs to be included after the *-impl lib otherwise the - // symbols in the *-binder library get optimized out. - "libvr_hwc-binder", - ], - shared_libs: [ - "android.frameworks.vr.composer@1.0", - "android.hardware.graphics.composer@2.1", - "libbase", - "libbinder", - "liblog", - "libhardware", - "libhidlbase", - "libui", - "libutils", - "libvr_hwc-hal", - ], - cflags: [ - "-DLOG_TAG=\"vr_hwc\"", - "-Wall", - "-Werror", - ], - init_rc: [ - "vr_hwc.rc", - ], + name: "vr_hwc", + vintf_fragments: ["manifest_vr_hwc.xml"], + srcs: [ + "vr_hardware_composer_service.cpp", + ], + static_libs: [ + "libvr_hwc-impl", + // NOTE: This needs to be included after the *-impl lib otherwise the + // symbols in the *-binder library get optimized out. + "libvr_hwc-binder", + ], + shared_libs: [ + "android.frameworks.vr.composer@1.0", + "android.hardware.graphics.composer@2.1", + "libbase", + "libbinder", + "liblog", + "libhardware", + "libhidlbase", + "libui", + "libutils", + "libvr_hwc-hal", + ], + cflags: [ + "-DLOG_TAG=\"vr_hwc\"", + "-Wall", + "-Werror", + ], + init_rc: [ + "vr_hwc.rc", + ], } cc_test { - name: "vr_hwc_test", - gtest: true, - srcs: ["tests/vr_composer_test.cpp"], - static_libs: [ - "libgtest", - "libvr_hwc-impl", - // NOTE: This needs to be included after the *-impl lib otherwise the - // symbols in the *-binder library get optimized out. - "libvr_hwc-binder", - ], - cflags: [ - "-Wall", - "-Werror", - // warnings in vr_composer_test.cpp to be fixed after merge of goog/master - "-Wno-sign-compare", - "-Wno-unused-parameter", - ], - shared_libs: [ - "libbase", - "libbinder", - "liblog", - "libui", - "libutils", - ], + name: "vr_hwc_test", + gtest: true, + srcs: ["tests/vr_composer_test.cpp"], + static_libs: [ + "libgtest", + "libvr_hwc-impl", + // NOTE: This needs to be included after the *-impl lib otherwise the + // symbols in the *-binder library get optimized out. + "libvr_hwc-binder", + ], + cflags: [ + "-Wall", + "-Werror", + // warnings in vr_composer_test.cpp to be fixed after merge of goog/master + "-Wno-sign-compare", + "-Wno-unused-parameter", + ], + shared_libs: [ + "libbase", + "libbinder", + "liblog", + "libui", + "libutils", + ], } diff --git a/services/vr/hardware_composer/aidl/Android.bp b/services/vr/hardware_composer/aidl/Android.bp new file mode 100644 index 0000000000..a1d5392071 --- /dev/null +++ b/services/vr/hardware_composer/aidl/Android.bp @@ -0,0 +1,27 @@ +cc_library_static { + name: "libvr_hwc-binder", + srcs: [ + "android/dvr/IVrComposer.aidl", + "android/dvr/IVrComposerCallback.aidl", + "android/dvr/parcelable_composer_frame.cpp", + "android/dvr/parcelable_composer_layer.cpp", + "android/dvr/parcelable_unique_fd.cpp", + ], + aidl: { + local_include_dirs: ["."], + export_aidl_headers: true, + }, + export_include_dirs: ["."], + + cflags: [ + "-Wall", + "-Werror", + ], + + shared_libs: [ + "libbinder", + "libui", + "libutils", + "libvr_hwc-hal", + ], +} diff --git a/services/vr/virtual_touchpad/Android.bp b/services/vr/virtual_touchpad/Android.bp index 131a306c08..dcaa663160 100644 --- a/services/vr/virtual_touchpad/Android.bp +++ b/services/vr/virtual_touchpad/Android.bp @@ -62,7 +62,7 @@ cc_test { service_src = [ "main.cpp", "VirtualTouchpadService.cpp", - "aidl/android/dvr/IVirtualTouchpadService.aidl", + ":virtualtouchpad_aidl", ] service_static_libs = [ @@ -99,7 +99,7 @@ cc_binary { client_src = [ "VirtualTouchpadClient.cpp", "DvrVirtualTouchpadClient.cpp", - "aidl/android/dvr/IVirtualTouchpadService.aidl", + ":virtualtouchpad_aidl", ] client_shared_libs = [ @@ -122,3 +122,9 @@ cc_library { name: "libvirtualtouchpadclient", export_include_dirs: ["include"], } + +filegroup { + name: "virtualtouchpad_aidl", + srcs: ["aidl/android/dvr/IVirtualTouchpadService.aidl"], + path: "aidl", +} diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index 993b751747..4d6b2be301 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -41,11 +41,13 @@ cc_library_shared { "-DVK_NO_PROTOTYPES", "-fvisibility=hidden", "-fstrict-aliasing", - "-Weverything", + "-Wextra", "-Werror", "-Wno-padded", + "-Wno-sign-compare", "-Wno-switch-enum", - "-Wno-undef", + "-Wno-unused-variable", + "-Wno-unused-function", // Have clang emit complete debug_info. "-fstandalone-debug", @@ -87,7 +89,6 @@ cc_library_shared { "libbase", "libdl_android", "libhidlbase", - "libhidltransport", "liblog", "libui", "libgraphicsenv", diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp index dd9efd3e17..f8263dbb93 100644 --- a/vulkan/libvulkan/api.cpp +++ b/vulkan/libvulkan/api.cpp @@ -521,7 +521,11 @@ LayerChain::LayerChain(bool is_instance, get_device_proc_addr_(nullptr), driver_extensions_(nullptr), driver_extension_count_(0) { - enabled_extensions_.set(driver::ProcHook::EXTENSION_CORE); + // advertise the loader supported core Vulkan API version at vulkan::api + for (uint32_t i = driver::ProcHook::EXTENSION_CORE_1_0; + i != driver::ProcHook::EXTENSION_COUNT; ++i) { + enabled_extensions_.set(i); + } } LayerChain::~LayerChain() { diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl index bdd3573b11..a5a0405f2d 100644 --- a/vulkan/libvulkan/code-generator.tmpl +++ b/vulkan/libvulkan/code-generator.tmpl @@ -765,6 +765,19 @@ VK_KHR_bind_memory2 {{end}} + +{{/* +------------------------------------------------------------------------------ + Emits the ProcHook enum for core Vulkan API verions. +------------------------------------------------------------------------------ +*/}} +{{define "driver.GetProcHookEnum"}} + {{if GetAnnotation $ "vulkan1_1"}}ProcHook::EXTENSION_CORE_1_1 + {{else}}ProcHook::EXTENSION_CORE_1_0 + {{end}} +{{end}} + + {{/* ------------------------------------------------------------------------------ Emits true if a function needs a ProcHook stub. @@ -778,6 +791,8 @@ VK_KHR_bind_memory2 {{if $ext}} {{if not (Macro "IsExtensionInternal" $ext)}}true{{end}} {{end}} + + {{if GetAnnotation $ "vulkan1_1"}}true{{end}} {{end}} {{end}} @@ -801,7 +816,8 @@ VK_KHR_bind_memory2 {{TrimPrefix "VK_" $e}}, {{end}} ¶ - EXTENSION_CORE, // valid bit + EXTENSION_CORE_1_0, + EXTENSION_CORE_1_1, EXTENSION_COUNT, EXTENSION_UNKNOWN, }; @@ -838,14 +854,21 @@ VK_KHR_bind_memory2 {{AssertType $ "Function"}} {{if (Macro "driver.NeedProcHookStub" $)}} + {{$ext_name := Strings ("")}} + {{$ext_hook := Strings ("")}} {{$ext := GetAnnotation $ "extension"}} - {{$ext_name := index $ext.Arguments 0}} + {{if $ext}} + {{$ext_name = index $ext.Arguments 0}} + {{$ext_hook = Strings ("ProcHook::") (Macro "BaseName" $ext)}} + {{else}} + {{$ext_name = Strings ("VK_VERSION_1_0")}} + {{$ext_hook = (Macro "driver.GetProcHookEnum" $)}} + {{end}} {{$base := (Macro "BaseName" $)}} VKAPI_ATTR {{Node "Type" $.Return}} checked{{$base}}({{Macro "Parameters" $}}) { {{$p0 := index $.CallParameters 0}} - {{$ext_hook := Strings ("ProcHook::") (Macro "BaseName" $ext)}} if (GetData({{$p0.Name}}).hook_extensions[{{$ext_hook}}]) { {{if not (IsVoid $.Return.Type)}}return §{{end}} @@ -878,7 +901,7 @@ VK_KHR_bind_memory2 { "{{$.Name}}", ProcHook::GLOBAL, - ProcHook::EXTENSION_CORE, + {{Macro "driver.GetProcHookEnum" $}}, reinterpret_cast<PFN_vkVoidFunction>({{$base}}), nullptr, }, @@ -911,7 +934,7 @@ VK_KHR_bind_memory2 nullptr, {{end}} {{else}} - ProcHook::EXTENSION_CORE, + {{Macro "driver.GetProcHookEnum" $}}, reinterpret_cast<PFN_vkVoidFunction>({{$base}}), nullptr, {{end}} @@ -934,18 +957,23 @@ VK_KHR_bind_memory2 ProcHook::DEVICE, {{$ext := GetAnnotation $ "extension"}} - {{if $ext}} - ProcHook::{{Macro "BaseName" $ext}}, - - {{if (Macro "IsExtensionInternal" $ext)}} - nullptr, - nullptr, + {{if or $ext (GetAnnotation $ "vulkan1_1")}} + {{if $ext}} + ProcHook::{{Macro "BaseName" $ext}}, + {{if Macro "IsExtensionInternal" $ext}} + nullptr, + nullptr, + {{else}} + reinterpret_cast<PFN_vkVoidFunction>({{$base}}), + reinterpret_cast<PFN_vkVoidFunction>(checked{{$base}}), + {{end}} {{else}} + {{Macro "driver.GetProcHookEnum" $}}, reinterpret_cast<PFN_vkVoidFunction>({{$base}}), reinterpret_cast<PFN_vkVoidFunction>(checked{{$base}}), {{end}} {{else}} - ProcHook::EXTENSION_CORE, + {{Macro "driver.GetProcHookEnum" $}}, reinterpret_cast<PFN_vkVoidFunction>({{$base}}), nullptr, {{end}} diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 8ea09805ae..73ba92c13f 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -24,7 +24,10 @@ #include <dlfcn.h> #include <algorithm> #include <array> +#include <climits> #include <new> +#include <sstream> +#include <string> #include <log/log.h> @@ -104,6 +107,7 @@ class CreateInfoWrapper { VkResult Validate(); void DowngradeApiVersion(); + void UpgradeDeviceCoreApiVersion(uint32_t api_version); const std::bitset<ProcHook::EXTENSION_COUNT>& GetHookExtensions() const; const std::bitset<ProcHook::EXTENSION_COUNT>& GetHalExtensions() const; @@ -152,15 +156,12 @@ class CreateInfoWrapper { Hal Hal::hal_; void* LoadLibrary(const android_dlextinfo& dlextinfo, - const char* subname, - int subname_len) { + const std::string_view subname) { ATRACE_CALL(); - const char kLibFormat[] = "vulkan.%*s.so"; - char* name = static_cast<char*>( - alloca(sizeof(kLibFormat) + static_cast<size_t>(subname_len))); - sprintf(name, kLibFormat, subname_len, subname); - return android_dlopen_ext(name, RTLD_LOCAL | RTLD_NOW, &dlextinfo); + std::stringstream ss; + ss << "vulkan." << subname << ".so"; + return android_dlopen_ext(ss.str().c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); } const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{ @@ -180,8 +181,9 @@ int LoadDriver(android_namespace_t* library_namespace, char prop[PROPERTY_VALUE_MAX]; for (auto key : HAL_SUBNAME_KEY_PROPERTIES) { int prop_len = property_get(key, prop, nullptr); - if (prop_len > 0) { - so = LoadLibrary(dlextinfo, prop, prop_len); + if (prop_len > 0 && prop_len <= UINT_MAX) { + std::string_view lib_name(prop, static_cast<unsigned int>(prop_len)); + so = LoadLibrary(dlextinfo, lib_name); if (so) break; } @@ -333,8 +335,12 @@ CreateInfoWrapper::CreateInfoWrapper(const VkInstanceCreateInfo& create_info, physical_dev_(VK_NULL_HANDLE), instance_info_(create_info), extension_filter_() { - hook_extensions_.set(ProcHook::EXTENSION_CORE); - hal_extensions_.set(ProcHook::EXTENSION_CORE); + // instance core versions need to match the loader api version + for (uint32_t i = ProcHook::EXTENSION_CORE_1_0; + i != ProcHook::EXTENSION_COUNT; ++i) { + hook_extensions_.set(i); + hal_extensions_.set(i); + } } CreateInfoWrapper::CreateInfoWrapper(VkPhysicalDevice physical_dev, @@ -345,8 +351,9 @@ CreateInfoWrapper::CreateInfoWrapper(VkPhysicalDevice physical_dev, physical_dev_(physical_dev), dev_info_(create_info), extension_filter_() { - hook_extensions_.set(ProcHook::EXTENSION_CORE); - hal_extensions_.set(ProcHook::EXTENSION_CORE); + // initialize with baseline core API version + hook_extensions_.set(ProcHook::EXTENSION_CORE_1_0); + hal_extensions_.set(ProcHook::EXTENSION_CORE_1_0); } CreateInfoWrapper::~CreateInfoWrapper() { @@ -545,7 +552,8 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::ANDROID_external_memory_android_hardware_buffer: case ProcHook::ANDROID_native_buffer: case ProcHook::GOOGLE_display_timing: - case ProcHook::EXTENSION_CORE: + case ProcHook::EXTENSION_CORE_1_0: + case ProcHook::EXTENSION_CORE_1_1: case ProcHook::EXTENSION_COUNT: // Device and meta extensions. If we ever get here it's a bug in // our code. But enumerating them lets us avoid having a default @@ -593,7 +601,8 @@ void CreateInfoWrapper::FilterExtension(const char* name) { case ProcHook::EXT_debug_report: case ProcHook::EXT_swapchain_colorspace: case ProcHook::ANDROID_native_buffer: - case ProcHook::EXTENSION_CORE: + case ProcHook::EXTENSION_CORE_1_0: + case ProcHook::EXTENSION_CORE_1_1: case ProcHook::EXTENSION_COUNT: // Instance and meta extensions. If we ever get here it's a bug // in our code. But enumerating them lets us avoid having a @@ -641,6 +650,28 @@ void CreateInfoWrapper::DowngradeApiVersion() { } } +void CreateInfoWrapper::UpgradeDeviceCoreApiVersion(uint32_t api_version) { + ALOG_ASSERT(!is_instance_, "Device only API called by instance wrapper."); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" + api_version ^= VK_VERSION_PATCH(api_version); +#pragma clang diagnostic pop + // cap the API version to the loader supported highest version + if (api_version > VK_API_VERSION_1_1) + api_version = VK_API_VERSION_1_1; + switch (api_version) { + case VK_API_VERSION_1_1: + hook_extensions_.set(ProcHook::EXTENSION_CORE_1_1); + hal_extensions_.set(ProcHook::EXTENSION_CORE_1_1); + [[clang::fallthrough]]; + case VK_API_VERSION_1_0: + break; + default: + ALOGD("Unknown upgrade API version[%u]", api_version); + break; + } +} + VKAPI_ATTR void* DefaultAllocate(void*, size_t size, size_t alignment, @@ -772,7 +803,7 @@ PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const char* pName) { : nullptr; break; case ProcHook::DEVICE: - proc = (hook->extension == ProcHook::EXTENSION_CORE) + proc = (hook->extension == ProcHook::EXTENSION_CORE_1_0) ? hook->proc : hook->checked_proc; break; @@ -1120,6 +1151,13 @@ VkResult CreateDevice(VkPhysicalDevice physicalDevice, if (!data) return VK_ERROR_OUT_OF_HOST_MEMORY; + VkPhysicalDeviceProperties properties; + ATRACE_BEGIN("driver.GetPhysicalDeviceProperties"); + instance_data.driver.GetPhysicalDeviceProperties(physicalDevice, + &properties); + ATRACE_END(); + + wrapper.UpgradeDeviceCoreApiVersion(properties.apiVersion); data->hook_extensions |= wrapper.GetHookExtensions(); // call into the driver @@ -1164,12 +1202,6 @@ VkResult CreateDevice(VkPhysicalDevice physicalDevice, return VK_ERROR_INCOMPATIBLE_DRIVER; } - VkPhysicalDeviceProperties properties; - ATRACE_BEGIN("driver.GetPhysicalDeviceProperties"); - instance_data.driver.GetPhysicalDeviceProperties(physicalDevice, - &properties); - ATRACE_END(); - if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) { // Log that the app is hitting software Vulkan implementation android::GraphicsEnv::getInstance().setTargetStats( diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h index 075a15b31a..ffe2a8bfe9 100644 --- a/vulkan/libvulkan/driver.h +++ b/vulkan/libvulkan/driver.h @@ -67,9 +67,7 @@ struct InstanceData { : opaque_api_data(), allocator(alloc), driver(), - get_device_proc_addr(nullptr) { - hook_extensions.set(ProcHook::EXTENSION_CORE); - } + get_device_proc_addr(nullptr) {} api::InstanceData opaque_api_data; @@ -89,9 +87,7 @@ struct DeviceData { : opaque_api_data(), allocator(alloc), debug_report_callbacks(debug_report_callbacks_), - driver() { - hook_extensions.set(ProcHook::EXTENSION_CORE); - } + driver() {} api::DeviceData opaque_api_data; diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp index 574c3273d0..aa31735eef 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -31,6 +31,23 @@ namespace { // clang-format off +VKAPI_ATTR VkResult checkedBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) { + if (GetData(device).hook_extensions[ProcHook::EXTENSION_CORE_1_1]) { + return BindImageMemory2(device, bindInfoCount, pBindInfos); + } else { + Logger(device).Err(device, "VK_VERSION_1_0 not enabled. vkBindImageMemory2 not executed."); + return VK_SUCCESS; + } +} + +VKAPI_ATTR void checkedGetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) { + if (GetData(device).hook_extensions[ProcHook::EXTENSION_CORE_1_1]) { + GetDeviceQueue2(device, pQueueInfo, pQueue); + } else { + Logger(device).Err(device, "VK_VERSION_1_0 not enabled. vkGetDeviceQueue2 not executed."); + } +} + VKAPI_ATTR VkResult checkedCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) { if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) { return CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); @@ -174,16 +191,16 @@ const ProcHook g_proc_hooks[] = { { "vkAllocateCommandBuffers", ProcHook::DEVICE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_0, reinterpret_cast<PFN_vkVoidFunction>(AllocateCommandBuffers), nullptr, }, { "vkBindImageMemory2", ProcHook::DEVICE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_1, reinterpret_cast<PFN_vkVoidFunction>(BindImageMemory2), - nullptr, + reinterpret_cast<PFN_vkVoidFunction>(checkedBindImageMemory2), }, { "vkBindImageMemory2KHR", @@ -209,14 +226,14 @@ const ProcHook g_proc_hooks[] = { { "vkCreateDevice", ProcHook::INSTANCE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_0, reinterpret_cast<PFN_vkVoidFunction>(CreateDevice), nullptr, }, { "vkCreateInstance", ProcHook::GLOBAL, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_0, reinterpret_cast<PFN_vkVoidFunction>(CreateInstance), nullptr, }, @@ -244,14 +261,14 @@ const ProcHook g_proc_hooks[] = { { "vkDestroyDevice", ProcHook::DEVICE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_0, reinterpret_cast<PFN_vkVoidFunction>(DestroyDevice), nullptr, }, { "vkDestroyInstance", ProcHook::INSTANCE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_0, reinterpret_cast<PFN_vkVoidFunction>(DestroyInstance), nullptr, }, @@ -272,28 +289,28 @@ const ProcHook g_proc_hooks[] = { { "vkEnumerateDeviceExtensionProperties", ProcHook::INSTANCE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_0, reinterpret_cast<PFN_vkVoidFunction>(EnumerateDeviceExtensionProperties), nullptr, }, { "vkEnumerateInstanceExtensionProperties", ProcHook::GLOBAL, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_0, reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceExtensionProperties), nullptr, }, { "vkEnumeratePhysicalDeviceGroups", ProcHook::INSTANCE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_1, reinterpret_cast<PFN_vkVoidFunction>(EnumeratePhysicalDeviceGroups), nullptr, }, { "vkEnumeratePhysicalDevices", ProcHook::INSTANCE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_0, reinterpret_cast<PFN_vkVoidFunction>(EnumeratePhysicalDevices), nullptr, }, @@ -314,28 +331,28 @@ const ProcHook g_proc_hooks[] = { { "vkGetDeviceProcAddr", ProcHook::DEVICE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_0, reinterpret_cast<PFN_vkVoidFunction>(GetDeviceProcAddr), nullptr, }, { "vkGetDeviceQueue", ProcHook::DEVICE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_0, reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue), nullptr, }, { "vkGetDeviceQueue2", ProcHook::DEVICE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_1, reinterpret_cast<PFN_vkVoidFunction>(GetDeviceQueue2), - nullptr, + reinterpret_cast<PFN_vkVoidFunction>(checkedGetDeviceQueue2), }, { "vkGetInstanceProcAddr", ProcHook::INSTANCE, - ProcHook::EXTENSION_CORE, + ProcHook::EXTENSION_CORE_1_0, reinterpret_cast<PFN_vkVoidFunction>(GetInstanceProcAddr), nullptr, }, diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 3faf6c0e32..831efb7026 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -48,7 +48,8 @@ struct ProcHook { ANDROID_external_memory_android_hardware_buffer, KHR_bind_memory2, - EXTENSION_CORE, // valid bit + EXTENSION_CORE_1_0, + EXTENSION_CORE_1_1, EXTENSION_COUNT, EXTENSION_UNKNOWN, }; diff --git a/vulkan/libvulkan/layers_extensions.cpp b/vulkan/libvulkan/layers_extensions.cpp index 691f3b0f52..7326692490 100644 --- a/vulkan/libvulkan/layers_extensions.cpp +++ b/vulkan/libvulkan/layers_extensions.cpp @@ -24,6 +24,7 @@ #include <string.h> #include <sys/prctl.h> +#include <memory> #include <mutex> #include <string> #include <vector> @@ -101,9 +102,7 @@ class LayerLibrary { bool EnumerateLayers(size_t library_idx, std::vector<Layer>& instance_layers) const; - void* GetGPA(const Layer& layer, - const char* gpa_name, - size_t gpa_name_len) const; + void* GetGPA(const Layer& layer, const std::string_view gpa_name) const; const std::string GetFilename() { return filename_; } @@ -226,17 +225,10 @@ bool LayerLibrary::EnumerateLayers(size_t library_idx, } // get layer properties - VkLayerProperties* properties = static_cast<VkLayerProperties*>(alloca( - (num_instance_layers + num_device_layers) * sizeof(VkLayerProperties))); - result = enumerate_instance_layers(&num_instance_layers, properties); - if (result != VK_SUCCESS) { - ALOGE("vkEnumerateInstanceLayerProperties failed for library '%s': %d", - path_.c_str(), result); - return false; - } + auto properties = std::make_unique<VkLayerProperties[]>(num_instance_layers + num_device_layers); if (num_device_layers > 0) { result = enumerate_device_layers(VK_NULL_HANDLE, &num_device_layers, - properties + num_instance_layers); + properties.get() + num_instance_layers); if (result != VK_SUCCESS) { ALOGE( "vkEnumerateDeviceLayerProperties failed for library '%s': %d", @@ -321,21 +313,11 @@ bool LayerLibrary::EnumerateLayers(size_t library_idx, return true; } -void* LayerLibrary::GetGPA(const Layer& layer, - const char* gpa_name, - size_t gpa_name_len) const { - void* gpa; - size_t layer_name_len = - std::max(size_t{2}, strlen(layer.properties.layerName)); - char* name = static_cast<char*>(alloca(layer_name_len + gpa_name_len + 1)); - strcpy(name, layer.properties.layerName); - strcpy(name + layer_name_len, gpa_name); - if (!(gpa = GetTrampoline(name))) { - strcpy(name, "vk"); - strcpy(name + 2, gpa_name); - gpa = GetTrampoline(name); - } - return gpa; +void* LayerLibrary::GetGPA(const Layer& layer, const std::string_view gpa_name) const { + std::string layer_name { layer.properties.layerName }; + if (void* gpa = GetTrampoline((layer_name.append(gpa_name).c_str()))) + return gpa; + return GetTrampoline((std::string {"vk"}.append(gpa_name)).c_str()); } // ---------------------------------------------------------------------------- @@ -388,9 +370,8 @@ void ForEachFileInZip(const std::string& zipname, return; } std::string prefix(dir_in_zip + "/"); - const ZipString prefix_str(prefix.c_str()); void* iter_cookie = nullptr; - if ((err = StartIteration(zip, &iter_cookie, &prefix_str, nullptr)) != 0) { + if ((err = StartIteration(zip, &iter_cookie, prefix, "")) != 0) { ALOGE("failed to iterate entries in apk '%s': %d", zipname.c_str(), err); CloseArchive(zip); @@ -399,11 +380,9 @@ void ForEachFileInZip(const std::string& zipname, ALOGD("searching for layers in '%s!/%s'", zipname.c_str(), dir_in_zip.c_str()); ZipEntry entry; - ZipString name; + std::string name; while (Next(iter_cookie, &entry, &name) == 0) { - std::string filename( - reinterpret_cast<const char*>(name.name) + prefix.length(), - name.name_length - prefix.length()); + std::string filename(name.substr(prefix.length())); // only enumerate direct entries of the directory, not subdirectories if (filename.find('/') != filename.npos) continue; @@ -473,10 +452,9 @@ const VkExtensionProperties* FindExtension( } void* GetLayerGetProcAddr(const Layer& layer, - const char* gpa_name, - size_t gpa_name_len) { + const std::string_view gpa_name) { const LayerLibrary& library = g_layer_libraries[layer.library_idx]; - return library.GetGPA(layer, gpa_name, gpa_name_len); + return library.GetGPA(layer, gpa_name); } } // anonymous namespace @@ -558,13 +536,13 @@ LayerRef::LayerRef(LayerRef&& other) noexcept : layer_(other.layer_) { PFN_vkGetInstanceProcAddr LayerRef::GetGetInstanceProcAddr() const { return layer_ ? reinterpret_cast<PFN_vkGetInstanceProcAddr>( - GetLayerGetProcAddr(*layer_, "GetInstanceProcAddr", 19)) + GetLayerGetProcAddr(*layer_, "GetInstanceProcAddr")) : nullptr; } PFN_vkGetDeviceProcAddr LayerRef::GetGetDeviceProcAddr() const { return layer_ ? reinterpret_cast<PFN_vkGetDeviceProcAddr>( - GetLayerGetProcAddr(*layer_, "GetDeviceProcAddr", 17)) + GetLayerGetProcAddr(*layer_, "GetDeviceProcAddr")) : nullptr; } diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index a8949d36f4..14191197c2 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -708,22 +708,6 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, const InstanceData& instance_data = GetData(pdev); - // TODO(jessehall): Fill out the set of supported formats. Longer term, add - // a new gralloc method to query whether a (format, usage) pair is - // supported, and check that for each gralloc format that corresponds to a - // Vulkan format. Shorter term, just add a few more formats to the ones - // hardcoded below. - - const VkSurfaceFormatKHR kFormats[] = { - {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - {VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, - }; - const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]); - uint32_t total_num_formats = kNumFormats; - bool wide_color_support = false; Surface& surface = *SurfaceFromHandle(surface_handle); int err = native_window_get_wide_color_support(surface.window.get(), @@ -737,43 +721,72 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, wide_color_support && instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace); - const VkSurfaceFormatKHR kWideColorFormats[] = { - {VK_FORMAT_R8G8B8A8_UNORM, - VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}, - {VK_FORMAT_R8G8B8A8_SRGB, - VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}, - {VK_FORMAT_R16G16B16A16_SFLOAT, - VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT}, - {VK_FORMAT_R16G16B16A16_SFLOAT, - VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT}, - {VK_FORMAT_A2B10G10R10_UNORM_PACK32, - VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}, - }; - const uint32_t kNumWideColorFormats = - sizeof(kWideColorFormats) / sizeof(kWideColorFormats[0]); + AHardwareBuffer_Desc desc = {}; + desc.width = 1; + desc.height = 1; + desc.layers = 1; + desc.usage = surface.consumer_usage | + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | + AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER; + + // We must support R8G8B8A8 + std::vector<VkSurfaceFormatKHR> all_formats = { + {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, + {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}}; + if (wide_color_support) { - total_num_formats += kNumWideColorFormats; + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); + } + + desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM; + if (AHardwareBuffer_isSupported(&desc)) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + } + + desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT; + if (AHardwareBuffer_isSupported(&desc)) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + if (wide_color_support) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, + VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT}); + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, + VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT}); + } + } + + desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM; + if (AHardwareBuffer_isSupported(&desc)) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); + if (wide_color_support) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, + VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); + } } VkResult result = VK_SUCCESS; if (formats) { - uint32_t out_count = 0; - uint32_t transfer_count = 0; - if (*count < total_num_formats) + uint32_t transfer_count = all_formats.size(); + if (transfer_count > *count) { + transfer_count = *count; result = VK_INCOMPLETE; - transfer_count = std::min(*count, kNumFormats); - std::copy(kFormats, kFormats + transfer_count, formats); - out_count += transfer_count; - if (wide_color_support) { - transfer_count = std::min(*count - out_count, kNumWideColorFormats); - std::copy(kWideColorFormats, kWideColorFormats + transfer_count, - formats + out_count); - out_count += transfer_count; } - *count = out_count; + std::copy(all_formats.begin(), all_formats.begin() + transfer_count, + formats); + *count = transfer_count; } else { - *count = total_num_formats; + *count = all_formats.size(); } + return result; } @@ -1280,6 +1293,7 @@ VkResult CreateSwapchainKHR(VkDevice device, VkImageCreateInfo image_create = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = &image_native_buffer, + .flags = createProtectedSwapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u, .imageType = VK_IMAGE_TYPE_2D, .format = create_info->imageFormat, .extent = {0, 0, 1}, @@ -1288,7 +1302,6 @@ VkResult CreateSwapchainKHR(VkDevice device, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = create_info->imageUsage, - .flags = createProtectedSwapchain ? VK_IMAGE_CREATE_PROTECTED_BIT : 0u, .sharingMode = create_info->imageSharingMode, .queueFamilyIndexCount = create_info->queueFamilyIndexCount, .pQueueFamilyIndices = create_info->pQueueFamilyIndices, @@ -1304,7 +1317,14 @@ VkResult CreateSwapchainKHR(VkDevice device, // TODO(jessehall): Improve error reporting. Can we enumerate // possible errors and translate them to valid Vulkan result codes? ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err); - result = VK_ERROR_SURFACE_LOST_KHR; + switch (-err) { + case ENOMEM: + result = VK_ERROR_OUT_OF_DEVICE_MEMORY; + break; + default: + result = VK_ERROR_SURFACE_LOST_KHR; + break; + } break; } img.buffer = buffer; @@ -1381,7 +1401,7 @@ void DestroySwapchainKHR(VkDevice device, bool active = swapchain->surface.swapchain_handle == swapchain_handle; ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr; - if (swapchain->frame_timestamps_enabled) { + if (window && swapchain->frame_timestamps_enabled) { native_window_enable_frame_timestamps(window, false); } for (uint32_t i = 0; i < swapchain->num_images; i++) diff --git a/vulkan/nulldrv/Android.bp b/vulkan/nulldrv/Android.bp index dedf419dc3..ba025046fa 100644 --- a/vulkan/nulldrv/Android.bp +++ b/vulkan/nulldrv/Android.bp @@ -23,18 +23,11 @@ cc_library_shared { "-fvisibility=hidden", "-fstrict-aliasing", "-DLOG_TAG=\"vknulldrv\"", - "-Weverything", + "-Wextra", "-Werror", - "-Wno-padded", - "-Wno-undef", - "-Wno-zero-length-array", "-DLOG_NDEBUG=0", ], - cppflags: [ - "-Wno-c++98-compat-pedantic", - "-Wno-c99-extensions", - ], srcs: [ "null_driver.cpp", diff --git a/vulkan/tools/Android.bp b/vulkan/tools/Android.bp index 2514094a15..91d64fe1c9 100644 --- a/vulkan/tools/Android.bp +++ b/vulkan/tools/Android.bp @@ -22,16 +22,8 @@ cc_binary { "-DLOG_TAG=\"vkinfo\"", - "-Weverything", + "-Wextra", "-Werror", - "-Wno-padded", - "-Wno-undef", - "-Wno-switch-enum", - ], - cppflags: [ - "-Wno-c++98-compat-pedantic", - "-Wno-c99-extensions", - "-Wno-old-style-cast", ], srcs: ["vkinfo.cpp"], diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp index 89bc926aa6..f9e4916722 100644 --- a/vulkan/tools/vkinfo.cpp +++ b/vulkan/tools/vkinfo.cpp @@ -195,10 +195,10 @@ void GatherGpuInfo(VkPhysicalDevice gpu, .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .queueCreateInfoCount = 1, .pQueueCreateInfos = &queue_create_info, - .enabledExtensionCount = num_extensions, - .ppEnabledExtensionNames = extensions, .enabledLayerCount = (options.validate) ? num_layers : 0, .ppEnabledLayerNames = kValidationLayers, + .enabledExtensionCount = num_extensions, + .ppEnabledExtensionNames = extensions, .pEnabledFeatures = &info.features, }; result = vkCreateDevice(gpu, &create_info, nullptr, &device); @@ -272,10 +272,10 @@ void GatherInfo(VulkanInfo* info, const Options& options) { const VkInstanceCreateInfo create_info = { .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, .pApplicationInfo = &application_info, - .enabledExtensionCount = num_extensions, - .ppEnabledExtensionNames = extensions, .enabledLayerCount = (options.validate) ? num_layers : 0, .ppEnabledLayerNames = kValidationLayers, + .enabledExtensionCount = num_extensions, + .ppEnabledExtensionNames = extensions, }; VkInstance instance; result = vkCreateInstance(&create_info, nullptr, &instance); |