diff options
100 files changed, 5260 insertions, 1116 deletions
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index 77915d5376..e42ee0540b 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -202,7 +202,6 @@ status_t DumpstateService::dump(int fd, const Vector<String16>&) { dprintf(fd, "base_name: %s\n", ds_->base_name_.c_str()); dprintf(fd, "name: %s\n", ds_->name_.c_str()); dprintf(fd, "now: %ld\n", ds_->now_); - dprintf(fd, "is_zipping: %s\n", ds_->IsZipping() ? "true" : "false"); dprintf(fd, "notification title: %s\n", ds_->options_->notification_title.c_str()); dprintf(fd, "notification description: %s\n", ds_->options_->notification_description.c_str()); diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index e97949eb1d..a951c4f0af 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -371,14 +371,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 with descending - * order. If |limit_by_mtime| is set, the vector only contains files that - * were written in the last 30 minutes. + * order. */ static std::vector<DumpData> GetDumpFds(const std::string& dir_path, - const std::string& file_prefix, - bool limit_by_mtime) { - const time_t thirty_minutes_ago = ds.now_ - 60 * 30; - + const std::string& file_prefix) { std::unique_ptr<DIR, decltype(&closedir)> dump_dir(opendir(dir_path.c_str()), closedir); if (dump_dir == nullptr) { @@ -412,11 +408,6 @@ static std::vector<DumpData> GetDumpFds(const std::string& dir_path, continue; } - if (limit_by_mtime && st.st_mtime < thirty_minutes_ago) { - MYLOGI("Excluding stale dump file: %s\n", abs_path.c_str()); - continue; - } - dump_data.emplace_back(DumpData{abs_path, std::move(fd), st.st_mtime}); } if (!dump_data.empty()) { @@ -447,7 +438,7 @@ static bool AddDumps(const std::vector<DumpData>::const_iterator start, strerror(errno)); } - if (ds.IsZipping() && add_to_zip) { + if (add_to_zip) { if (ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd, /* timeout = */ 0ms) != OK) { MYLOGE("Unable to add %s to zip file, addZipEntryFromFd failed\n", name.c_str()); } @@ -486,7 +477,6 @@ void do_mountinfo(int pid, const char* name __attribute__((unused))) { } void add_mountinfo() { - if (!ds.IsZipping()) return; std::string title = "MOUNT INFO"; mount_points.clear(); DurationReporter duration_reporter(title, true); @@ -823,11 +813,6 @@ static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = { status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd, std::chrono::milliseconds timeout = 0ms) { - if (!IsZipping()) { - MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n", - entry_name.c_str()); - return INVALID_OPERATION; - } std::string valid_name = entry_name; // Rename extension if necessary. @@ -928,21 +913,12 @@ static int _add_file_from_fd(const char* title __attribute__((unused)), const ch } void Dumpstate::AddDir(const std::string& dir, bool recursive) { - if (!IsZipping()) { - MYLOGD("Not adding dir %s because it's not a zipped bugreport\n", dir.c_str()); - return; - } MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive); DurationReporter duration_reporter(dir, true); dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd); } bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) { - if (!IsZipping()) { - MYLOGD("Not adding text zip entry %s because it's not a zipped bugreport\n", - entry_name.c_str()); - return false; - } MYLOGD("Adding zip text entry %s\n", entry_name.c_str()); int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_); if (err != 0) { @@ -1035,10 +1011,6 @@ static void DoLogcat() { } static void DumpIncidentReport() { - if (!ds.IsZipping()) { - MYLOGD("Not dumping incident report because it's not a zipped bugreport\n"); - return; - } 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, @@ -1064,10 +1036,6 @@ static void MaybeAddSystemTraceToZip() { // This function copies into the .zip the system trace that was snapshotted // by the early call to MaybeSnapshotSystemTrace(), if any background // tracing was happening. - if (!ds.IsZipping()) { - MYLOGD("Not dumping system trace because it's not a zipped bugreport\n"); - return; - } if (!ds.has_system_trace_) { // No background trace was happening at the time dumpstate was invoked. return; @@ -1079,10 +1047,6 @@ static void MaybeAddSystemTraceToZip() { } static void DumpVisibleWindowViews() { - if (!ds.IsZipping()) { - MYLOGD("Not dumping visible views because it's not a zipped bugreport\n"); - return; - } DurationReporter duration_reporter("VISIBLE WINDOW VIEWS"); const std::string path = ds.bugreport_internal_dir_ + "/tmp_visible_window_views"; auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), @@ -1163,7 +1127,7 @@ static void AddAnrTraceDir(const bool add_to_zip, const std::string& anr_traces_ } static void AddAnrTraceFiles() { - const bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR; + const bool add_to_zip = ds.version_ == VERSION_SPLIT_ANR; std::string anr_traces_dir = "/data/anr"; @@ -1303,10 +1267,6 @@ static Dumpstate::RunStatus RunDumpsysTextNormalPriority(const std::string& titl static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priority, std::chrono::milliseconds timeout, std::chrono::milliseconds service_timeout) { - if (!ds.IsZipping()) { - MYLOGD("Not dumping %s because it's not a zipped bugreport\n", title.c_str()); - return Dumpstate::RunStatus::OK; - } sp<android::IServiceManager> sm = defaultServiceManager(); Dumpsys dumpsys(sm.get()); Vector<String16> args; @@ -1386,12 +1346,6 @@ static Dumpstate::RunStatus RunDumpsysNormal() { * if it's not running in the parallel task. */ static void DumpHals(int out_fd = STDOUT_FILENO) { - if (!ds.IsZipping()) { - RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"}, - CommandOptions::WithTimeout(60).AsRootIfAvailable().Build(), - false, out_fd); - return; - } RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build(), false, out_fd); @@ -1846,8 +1800,8 @@ Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() { /* Run some operations that require root. */ if (!PropertiesHelper::IsDryRun()) { - ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping()); - ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX, !ds.IsZipping()); + ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX); + ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX); } ds.AddDir(RECOVERY_DIR, true); @@ -2394,11 +2348,6 @@ void Dumpstate::DumpstateBoard(int out_fd) { dprintf(out_fd, "== Board\n"); dprintf(out_fd, "========================================================\n"); - if (!IsZipping()) { - MYLOGD("Not dumping board info because it's not a zipped bugreport\n"); - return; - } - /* * mount debugfs for non-user builds with ro.product.debugfs_restrictions.enabled * set to true and unmount it after invoking dumpstateBoard_* methods. @@ -3567,10 +3516,6 @@ void Progress::Dump(int fd, const std::string& prefix) const { 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); } @@ -4216,10 +4161,6 @@ void dump_frozen_cgroupfs(const char *dir, int level, } void dump_frozen_cgroupfs() { - if (!ds.IsZipping()) { - MYLOGD("Not adding cgroupfs because it's not a zipped bugreport\n"); - return; - } MYLOGD("Adding frozen processes from %s\n", CGROUPFS_DIR); DurationReporter duration_reporter("FROZEN CGROUPFS"); if (PropertiesHelper::IsDryRun()) return; diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 852b9a8e24..d0acb31cc0 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -214,9 +214,6 @@ class Dumpstate { static Dumpstate& GetInstance(); - /* Checkes whether dumpstate is generating a zipped bugreport. */ - bool IsZipping() const; - /* Initialize dumpstate fields before starting bugreport generation */ void Initialize(); diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 677d6c78ea..49c1318945 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -61,6 +61,10 @@ class ServiceManagerMock : public IServiceManager { MOCK_METHOD1(getDeclaredInstances, Vector<String16>(const String16&)); MOCK_METHOD1(updatableViaApex, std::optional<String16>(const String16&)); MOCK_METHOD1(getConnectionInfo, std::optional<ConnectionInfo>(const String16&)); + MOCK_METHOD2(registerForNotifications, status_t(const String16&, + const sp<LocalRegistrationCallback>&)); + MOCK_METHOD2(unregisterForNotifications, status_t(const String16&, + const sp<LocalRegistrationCallback>&)); protected: MOCK_METHOD0(onAsBinder, IBinder*()); }; diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 31dee235e8..5003151b51 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -119,6 +119,12 @@ prebuilt_etc { } prebuilt_etc { + name: "android.hardware.sensor.dynamic.head_tracker.prebuilt.xml", + src: "android.hardware.sensor.dynamic.head_tracker.xml", + defaults: ["frameworks_native_data_etc_defaults"], +} + +prebuilt_etc { name: "android.hardware.sensor.gyroscope.prebuilt.xml", src: "android.hardware.sensor.gyroscope.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/data/etc/android.hardware.sensor.accelerometer_limited_axes.xml b/data/etc/android.hardware.sensor.accelerometer_limited_axes.xml new file mode 100644 index 0000000000..6a43d5dc5f --- /dev/null +++ b/data/etc/android.hardware.sensor.accelerometer_limited_axes.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Feature for devices with a limited axes accelerometer sensor. --> +<permissions> + <feature name="android.hardware.sensor.accelerometer_limited_axes" /> +</permissions> diff --git a/data/etc/android.hardware.sensor.accelerometer_limited_axes_uncalibrated.xml b/data/etc/android.hardware.sensor.accelerometer_limited_axes_uncalibrated.xml new file mode 100644 index 0000000000..4c6755c45a --- /dev/null +++ b/data/etc/android.hardware.sensor.accelerometer_limited_axes_uncalibrated.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Feature for devices with an uncalibrated limited axes accelerometer sensor. --> +<permissions> + <feature name="android.hardware.sensor.accelerometer_limited_axes_uncalibrated" /> +</permissions> diff --git a/data/etc/android.hardware.sensor.dynamic.head_tracker.xml b/data/etc/android.hardware.sensor.dynamic.head_tracker.xml new file mode 100644 index 0000000000..ece58c9a72 --- /dev/null +++ b/data/etc/android.hardware.sensor.dynamic.head_tracker.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Feature for devices with a head tracker sensor. --> +<permissions> + <feature name="android.hardware.sensor.dynamic.head_tracker" /> +</permissions>
\ No newline at end of file diff --git a/data/etc/android.hardware.sensor.gyroscope_limited_axes.xml b/data/etc/android.hardware.sensor.gyroscope_limited_axes.xml new file mode 100644 index 0000000000..45c6547fe1 --- /dev/null +++ b/data/etc/android.hardware.sensor.gyroscope_limited_axes.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Feature for devices with a limited axes gyroscope. --> +<permissions> + <feature name="android.hardware.sensor.gyroscope_limited_axes" /> +</permissions> diff --git a/data/etc/android.hardware.sensor.gyroscope_limited_axes_uncalibrated.xml b/data/etc/android.hardware.sensor.gyroscope_limited_axes_uncalibrated.xml new file mode 100644 index 0000000000..2ca8364677 --- /dev/null +++ b/data/etc/android.hardware.sensor.gyroscope_limited_axes_uncalibrated.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Feature for devices with an uncalibrated limited axes gyroscope. --> +<permissions> + <feature name="android.hardware.sensor.gyroscope_limited_axes_uncalibrated" /> +</permissions> diff --git a/data/etc/android.hardware.sensor.heading.xml b/data/etc/android.hardware.sensor.heading.xml new file mode 100644 index 0000000000..8b81823b45 --- /dev/null +++ b/data/etc/android.hardware.sensor.heading.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- Feature for devices with a heading sensor. --> +<permissions> + <feature name="android.hardware.sensor.heading" /> +</permissions> diff --git a/data/etc/android.hardware.telephony.calling.xml b/data/etc/android.hardware.telephony.calling.xml index 3e92a6601f..ab245fc238 100644 --- a/data/etc/android.hardware.telephony.calling.xml +++ b/data/etc/android.hardware.telephony.calling.xml @@ -18,7 +18,7 @@ <permissions> <feature name="android.hardware.telephony" /> <feature name="android.software.telecom" /> - <feature name="android.hardware.telephony.radio" /> + <feature name="android.hardware.telephony.radio.access" /> <feature name="android.hardware.telephony.subscription" /> <feature name="android.hardware.telephony.calling" /> </permissions> diff --git a/data/etc/android.hardware.telephony.cdma.xml b/data/etc/android.hardware.telephony.cdma.xml index c81cf06079..0a8cfc4901 100644 --- a/data/etc/android.hardware.telephony.cdma.xml +++ b/data/etc/android.hardware.telephony.cdma.xml @@ -18,7 +18,7 @@ <permissions> <feature name="android.hardware.telephony" /> <feature name="android.software.telecom" /> - <feature name="android.hardware.telephony.radio" /> + <feature name="android.hardware.telephony.radio.access" /> <feature name="android.hardware.telephony.subscription" /> <feature name="android.hardware.telephony.cdma" /> <feature name="android.hardware.telephony.calling" /> diff --git a/data/etc/android.hardware.telephony.data.xml b/data/etc/android.hardware.telephony.data.xml index 2716152650..a9bb36e25e 100644 --- a/data/etc/android.hardware.telephony.data.xml +++ b/data/etc/android.hardware.telephony.data.xml @@ -17,7 +17,7 @@ <!-- This is the standard set of features for devices to support Telephony Data API. --> <permissions> <feature name="android.hardware.telephony" /> - <feature name="android.hardware.telephony.radio" /> + <feature name="android.hardware.telephony.radio.access" /> <feature name="android.hardware.telephony.subscription" /> <feature name="android.hardware.telephony.data" /> </permissions> diff --git a/data/etc/android.hardware.telephony.gsm.xml b/data/etc/android.hardware.telephony.gsm.xml index e368a06bfd..e3952bf8c7 100644 --- a/data/etc/android.hardware.telephony.gsm.xml +++ b/data/etc/android.hardware.telephony.gsm.xml @@ -18,7 +18,7 @@ <permissions> <feature name="android.hardware.telephony" /> <feature name="android.software.telecom" /> - <feature name="android.hardware.telephony.radio" /> + <feature name="android.hardware.telephony.radio.access" /> <feature name="android.hardware.telephony.subscription" /> <feature name="android.hardware.telephony.gsm" /> <feature name="android.hardware.telephony.calling" /> diff --git a/data/etc/android.hardware.telephony.radio.xml b/data/etc/android.hardware.telephony.radio.access.xml index 4377705f2f..c98267ce8c 100644 --- a/data/etc/android.hardware.telephony.radio.xml +++ b/data/etc/android.hardware.telephony.radio.access.xml @@ -17,5 +17,5 @@ <!-- This is the standard set of features for devices to support Telephony Radio Access API. --> <permissions> <feature name="android.hardware.telephony" /> - <feature name="android.hardware.telephony.radio" /> + <feature name="android.hardware.telephony.radio.access" /> </permissions> diff --git a/include/android/sensor.h b/include/android/sensor.h index 45e8afc7de..cf1ca02cb3 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -263,6 +263,52 @@ enum { * Measures the orientation and rotational velocity of a user's head. */ ASENSOR_TYPE_HEAD_TRACKER = 37, + /** + * {@link ASENSOR_TYPE_ACCELEROMETER_LIMITED_AXES} + * reporting-mode: continuous + * + * The first three values are in SI units (m/s^2) and measure the acceleration of the device + * minus the force of gravity. The last three values indicate which acceleration axes are + * supported. A value of 1.0 means supported and a value of 0 means not supported. + */ + ASENSOR_TYPE_ACCELEROMETER_LIMITED_AXES = 38, + /** + * {@link ASENSOR_TYPE_GYROSCOPE_LIMITED_AXES} + * reporting-mode: continuous + * + * The first three values are in radians/second and measure the rate of rotation around the X, + * Y and Z axis. The last three values indicate which rotation axes are supported. A value of + * 1.0 means supported and a value of 0 means not supported. + */ + ASENSOR_TYPE_GYROSCOPE_LIMITED_AXES = 39, + /** + * {@link ASENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED} + * reporting-mode: continuous + * + * The first three values are in SI units (m/s^2) and measure the acceleration of the device + * minus the force of gravity. The middle three values represent the estimated bias for each + * axis. The last three values indicate which acceleration axes are supported. A value of 1.0 + * means supported and a value of 0 means not supported. + */ + ASENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = 40, + /** + * {@link ASENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED} + * reporting-mode: continuous + * + * The first three values are in radians/second and measure the rate of rotation around the X, + * Y and Z axis. The middle three values represent the estimated drift around each axis in + * rad/s. The last three values indicate which rotation axes are supported. A value of 1.0 means + * supported and a value of 0 means not supported. + */ + ASENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED = 41, + /** + * {@link ASENSOR_TYPE_HEADING} + * reporting-mode: continuous + * + * A heading sensor measures the direction in which the device is pointing + * relative to true north in degrees. + */ + ASENSOR_TYPE_HEADING = 42, }; /** @@ -479,6 +525,69 @@ typedef struct AHeadTrackerEvent { int32_t discontinuity_count; } AHeadTrackerEvent; +typedef struct ALimitedAxesImuEvent { + union { + float calib[3]; + struct { + float x; + float y; + float z; + }; + }; + union { + float supported[3]; + struct { + float x_supported; + float y_supported; + float z_supported; + }; + }; +} ALimitedAxesImuEvent; + +typedef struct ALimitedAxesImuUncalibratedEvent { + union { + float uncalib[3]; + struct { + float x_uncalib; + float y_uncalib; + float z_uncalib; + }; + }; + union { + float bias[3]; + struct { + float x_bias; + float y_bias; + float z_bias; + }; + }; + union { + float supported[3]; + struct { + float x_supported; + float y_supported; + float z_supported; + }; + }; +} ALimitedAxesImuUncalibratedEvent; + +typedef struct AHeadingEvent { + /** + * The direction in which the device is pointing relative to true north in + * degrees. The value must be between 0.0 (inclusive) and 360.0 (exclusive), + * with 0 indicating north, 90 east, 180 south, and 270 west. + */ + float heading; + /** + * Accuracy is defined at 68% confidence. In the case where the underlying + * distribution is assumed Gaussian normal, this would be considered one + * standard deviation. For example, if the heading returns 60 degrees, and + * accuracy returns 10 degrees, then there is a 68 percent probability of + * the true heading being between 50 degrees and 70 degrees. + */ + float accuracy; +} AHeadingEvent; + /** * Information that describes a sensor event, refer to * <a href="/reference/android/hardware/SensorEvent">SensorEvent</a> for additional @@ -516,6 +625,9 @@ typedef struct ASensorEvent { ADynamicSensorEvent dynamic_sensor_meta; AAdditionalInfoEvent additional_info; AHeadTrackerEvent head_tracker; + ALimitedAxesImuEvent limited_axes_imu; + ALimitedAxesImuUncalibratedEvent limited_axes_imu_uncalibrated; + AHeadingEvent heading; }; union { uint64_t data[8]; diff --git a/include/input/Input.h b/include/input/Input.h index 55ebb90ff6..e421dee275 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -534,7 +534,7 @@ public: inline int32_t getActionMasked() const { return getActionMasked(mAction); } - static int32_t getActionIndex(int32_t action) { + static uint8_t getActionIndex(int32_t action) { return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; } diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index ec9d5544f9..0970ca5aa5 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -54,6 +54,8 @@ constexpr const bool kEnableRpcDevServers = true; constexpr const bool kEnableRpcDevServers = false; #endif +// Log any reply transactions for which the data exceeds this size +#define LOG_REPLIES_OVER_SIZE (300 * 1024) // --------------------------------------------------------------------------- IBinder::IBinder() @@ -296,6 +298,10 @@ status_t BBinder::transact( // In case this is being transacted on in the same process. if (reply != nullptr) { reply->setDataPosition(0); + if (reply->dataSize() > LOG_REPLIES_OVER_SIZE) { + ALOGW("Large reply transaction of %zu bytes, interface descriptor %s, code %d", + reply->dataSize(), String8(getInterfaceDescriptor()).c_str(), code); + } } return err; diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 06542f064d..056ef0ab74 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -48,6 +48,9 @@ uint32_t BpBinder::sBinderProxyCountHighWatermark = 2500; // Another arbitrary value a binder count needs to drop below before another callback will be called uint32_t BpBinder::sBinderProxyCountLowWatermark = 2000; +// Log any transactions for which the data exceeds this size +#define LOG_TRANSACTIONS_OVER_SIZE (300 * 1024) + enum { LIMIT_REACHED_MASK = 0x80000000, // A flag denoting that the limit has been reached COUNTING_VALUE_MASK = 0x7FFFFFFF, // A mask of the remaining bits for the count value @@ -302,6 +305,14 @@ status_t BpBinder::transact( } else { status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags); } + if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) { + Mutex::Autolock _l(mLock); + ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d", + data.dataSize(), + mDescriptorCache.size() ? String8(mDescriptorCache).c_str() + : "<uncached descriptor>", + code); + } if (status == DEAD_OBJECT) mAlive = 0; diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 81e61daae1..ea2f8d2274 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -43,6 +43,8 @@ namespace android { +using AidlRegistrationCallback = IServiceManager::LocalRegistrationCallback; + using AidlServiceManager = android::os::IServiceManager; using android::binder::Status; @@ -79,7 +81,24 @@ public: Vector<String16> getDeclaredInstances(const String16& interface) override; std::optional<String16> updatableViaApex(const String16& name) override; std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override; + class RegistrationWaiter : public android::os::BnServiceCallback { + public: + explicit RegistrationWaiter(const sp<AidlRegistrationCallback>& callback) + : mImpl(callback) {} + Status onRegistration(const std::string& name, const sp<IBinder>& binder) override { + mImpl->onServiceRegistration(String16(name.c_str()), binder); + return Status::ok(); + } + + private: + sp<AidlRegistrationCallback> mImpl; + }; + + status_t registerForNotifications(const String16& service, + const sp<AidlRegistrationCallback>& cb) override; + status_t unregisterForNotifications(const String16& service, + const sp<AidlRegistrationCallback>& cb) override; // for legacy ABI const String16& getInterfaceDescriptor() const override { return mTheRealServiceManager->getInterfaceDescriptor(); @@ -90,6 +109,17 @@ public: protected: sp<AidlServiceManager> mTheRealServiceManager; + // AidlRegistrationCallback -> services that its been registered for + // notifications. + using LocalRegistrationAndWaiter = + std::pair<sp<LocalRegistrationCallback>, sp<RegistrationWaiter>>; + using ServiceCallbackMap = std::map<std::string, std::vector<LocalRegistrationAndWaiter>>; + ServiceCallbackMap mNameToRegistrationCallback; + std::mutex mNameToRegistrationLock; + + void removeRegistrationCallbackLocked(const sp<AidlRegistrationCallback>& cb, + ServiceCallbackMap::iterator* it, + sp<RegistrationWaiter>* waiter); // Directly get the service in a way that, for lazy services, requests the service to be started // if it is not currently started. This way, calls directly to ServiceManagerShim::getService @@ -442,6 +472,77 @@ std::optional<IServiceManager::ConnectionInfo> ServiceManagerShim::getConnection : std::nullopt; } +status_t ServiceManagerShim::registerForNotifications(const String16& name, + const sp<AidlRegistrationCallback>& cb) { + if (cb == nullptr) { + ALOGE("%s: null cb passed", __FUNCTION__); + return BAD_VALUE; + } + std::string nameStr = String8(name).c_str(); + sp<RegistrationWaiter> registrationWaiter = sp<RegistrationWaiter>::make(cb); + std::lock_guard<std::mutex> lock(mNameToRegistrationLock); + if (Status status = + mTheRealServiceManager->registerForNotifications(nameStr, registrationWaiter); + !status.isOk()) { + ALOGW("Failed to registerForNotifications for %s: %s", nameStr.c_str(), + status.toString8().c_str()); + return UNKNOWN_ERROR; + } + mNameToRegistrationCallback[nameStr].push_back(std::make_pair(cb, registrationWaiter)); + return OK; +} + +void ServiceManagerShim::removeRegistrationCallbackLocked(const sp<AidlRegistrationCallback>& cb, + ServiceCallbackMap::iterator* it, + sp<RegistrationWaiter>* waiter) { + std::vector<LocalRegistrationAndWaiter>& localRegistrationAndWaiters = (*it)->second; + for (auto lit = localRegistrationAndWaiters.begin(); + lit != localRegistrationAndWaiters.end();) { + if (lit->first == cb) { + if (waiter) { + *waiter = lit->second; + } + lit = localRegistrationAndWaiters.erase(lit); + } else { + ++lit; + } + } + + if (localRegistrationAndWaiters.empty()) { + mNameToRegistrationCallback.erase(*it); + } +} + +status_t ServiceManagerShim::unregisterForNotifications(const String16& name, + const sp<AidlRegistrationCallback>& cb) { + if (cb == nullptr) { + ALOGE("%s: null cb passed", __FUNCTION__); + return BAD_VALUE; + } + std::string nameStr = String8(name).c_str(); + std::lock_guard<std::mutex> lock(mNameToRegistrationLock); + auto it = mNameToRegistrationCallback.find(nameStr); + sp<RegistrationWaiter> registrationWaiter; + if (it != mNameToRegistrationCallback.end()) { + removeRegistrationCallbackLocked(cb, &it, ®istrationWaiter); + } else { + ALOGE("%s no callback registered for notifications on %s", __FUNCTION__, nameStr.c_str()); + return BAD_VALUE; + } + if (registrationWaiter == nullptr) { + ALOGE("%s Callback passed wasn't used to register for notifications", __FUNCTION__); + return BAD_VALUE; + } + if (Status status = mTheRealServiceManager->unregisterForNotifications(String8(name).c_str(), + registrationWaiter); + !status.isOk()) { + ALOGW("Failed to get service manager to unregisterForNotifications for %s: %s", + String8(name).c_str(), status.toString8().c_str()); + return UNKNOWN_ERROR; + } + return OK; +} + #ifndef __ANDROID__ // ServiceManagerShim for host. Implements the old libbinder android::IServiceManager API. // The internal implementation of the AIDL interface android::os::IServiceManager calls into diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp index a44c578230..83b97d04c6 100644 --- a/libs/binder/Status.cpp +++ b/libs/binder/Status.cpp @@ -211,6 +211,12 @@ status_t Status::writeToParcel(Parcel* parcel) const { return status; } +status_t Status::writeOverParcel(Parcel* parcel) const { + parcel->setDataSize(0); + parcel->setDataPosition(0); + return writeToParcel(parcel); +} + void Status::setException(int32_t ex, const String8& message) { mException = ex; mErrorCode = ex == EX_TRANSACTION_FAILED ? FAILED_TRANSACTION : NO_ERROR; diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 240e3c2b26..ea40db8ffa 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -115,6 +115,17 @@ public: unsigned int port; }; virtual std::optional<ConnectionInfo> getConnectionInfo(const String16& name) = 0; + + struct LocalRegistrationCallback : public virtual RefBase { + virtual void onServiceRegistration(const String16& instance, const sp<IBinder>& binder) = 0; + virtual ~LocalRegistrationCallback() {} + }; + + virtual status_t registerForNotifications(const String16& name, + const sp<LocalRegistrationCallback>& callback) = 0; + + virtual status_t unregisterForNotifications(const String16& name, + const sp<LocalRegistrationCallback>& callback) = 0; }; sp<IServiceManager> defaultServiceManager(); diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h index aaafa36d40..af34695875 100644 --- a/libs/binder/include/binder/Status.h +++ b/libs/binder/include/binder/Status.h @@ -117,6 +117,10 @@ public: status_t readFromParcel(const Parcel& parcel); status_t writeToParcel(Parcel* parcel) const; + // Convenience API to replace a Parcel with a status value, w/o requiring + // calling multiple APIs (makes generated code smaller). + status_t writeOverParcel(Parcel* parcel) const; + // Set one of the pre-defined exception types defined above. void setException(int32_t ex, const String8& message); // Set a service specific exception with error code. diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 77493b3882..79c8c8f88b 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -32,16 +32,6 @@ license { ], } -// TODO(b/211908498): remove this -cc_defaults { - name: "libbinder_ndk_host_user", - target: { - darwin: { - enabled: false, - }, - }, -} - cc_library { name: "libbinder_ndk", diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 86da5887a8..ff55d6ebdc 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -37,7 +37,11 @@ cc_test { srcs: ["binderDriverInterfaceTest.cpp"], header_libs: ["libbinder_headers"], compile_multilib: "32", - multilib: { lib32: { suffix: "" } }, + multilib: { + lib32: { + suffix: "", + }, + }, cflags: ["-DBINDER_IPC_32BIT=1"], test_suites: ["vts"], } @@ -52,7 +56,10 @@ cc_test { }, header_libs: ["libbinder_headers"], srcs: ["binderDriverInterfaceTest.cpp"], - test_suites: ["device-tests", "vts"], + test_suites: [ + "device-tests", + "vts", + ], } cc_test { @@ -69,7 +76,11 @@ cc_test { "libgmock", ], compile_multilib: "32", - multilib: { lib32: { suffix: "" } }, + multilib: { + lib32: { + suffix: "", + }, + }, cflags: ["-DBINDER_IPC_32BIT=1"], test_suites: ["vts"], require_root: true, @@ -84,7 +95,11 @@ cc_test { enabled: false, }, }, - srcs: ["binderParcelUnitTest.cpp", "binderBinderUnitTest.cpp"], + srcs: [ + "binderParcelUnitTest.cpp", + "binderBinderUnitTest.cpp", + "binderStatusUnitTest.cpp", + ], shared_libs: [ "libbinder", "libcutils", @@ -112,7 +127,10 @@ cc_test { static_libs: [ "libgmock", ], - test_suites: ["device-tests", "vts"], + test_suites: [ + "device-tests", + "vts", + ], require_root: true, } @@ -179,7 +197,6 @@ cc_test { }, defaults: [ "binder_test_defaults", - "libbinder_ndk_host_user", "libbinder_tls_shared_deps", ], @@ -232,7 +249,10 @@ cc_test { "libbinder_tls_test_utils", "libbinder_tls_static", ], - test_suites: ["general-tests", "device-tests"], + test_suites: [ + "general-tests", + "device-tests", + ], } cc_benchmark { @@ -348,7 +368,10 @@ cc_test { "liblog", "libutils", ], - test_suites: ["device-tests", "vts"], + test_suites: [ + "device-tests", + "vts", + ], require_root: true, } @@ -402,7 +425,10 @@ cc_test { "binderStabilityTestIface-ndk", ], - test_suites: ["device-tests", "vts"], + test_suites: [ + "device-tests", + "vts", + ], require_root: true, } diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 63a4b2cc68..700940a529 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -1220,6 +1220,19 @@ TEST_F(BinderLibTest, GotSid) { EXPECT_THAT(server->transact(BINDER_LIB_TEST_CAN_GET_SID, data, nullptr), StatusEq(OK)); } +TEST(ServiceNotifications, Unregister) { + auto sm = defaultServiceManager(); + using LocalRegistrationCallback = IServiceManager::LocalRegistrationCallback; + class LocalRegistrationCallbackImpl : public virtual LocalRegistrationCallback { + void onServiceRegistration(const String16 &, const sp<IBinder> &) override {} + virtual ~LocalRegistrationCallbackImpl() {} + }; + sp<LocalRegistrationCallback> cb = sp<LocalRegistrationCallbackImpl>::make(); + + EXPECT_EQ(sm->registerForNotifications(String16("RogerRafa"), cb), OK); + EXPECT_EQ(sm->unregisterForNotifications(String16("RogerRafa"), cb), OK); +} + class BinderLibRpcTestBase : public BinderLibTest { public: void SetUp() override { diff --git a/libs/binder/tests/binderStatusUnitTest.cpp b/libs/binder/tests/binderStatusUnitTest.cpp new file mode 100644 index 0000000000..a32ec5cc31 --- /dev/null +++ b/libs/binder/tests/binderStatusUnitTest.cpp @@ -0,0 +1,35 @@ +/* + * 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 <binder/Parcel.h> +#include <binder/Status.h> +#include <gtest/gtest.h> + +using android::Parcel; +using android::binder::Status; + +TEST(Status, WriteOverParcel) { + Status status = Status::fromExceptionCode(Status::EX_NULL_POINTER); + + Parcel indirect; + indirect.writeInt32(64); + status.writeOverParcel(&indirect); + + Parcel direct; + status.writeToParcel(&direct); + + EXPECT_EQ(0, indirect.compareData(direct)); +} diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp index 73f9d4d153..4f63194f03 100644 --- a/libs/cputimeinstate/Android.bp +++ b/libs/cputimeinstate/Android.bp @@ -45,4 +45,5 @@ cc_test { "-Wextra", ], require_root: true, + test_suites: ["general-tests"], } diff --git a/libs/cputimeinstate/TEST_MAPPING b/libs/cputimeinstate/TEST_MAPPING new file mode 100644 index 0000000000..4781520755 --- /dev/null +++ b/libs/cputimeinstate/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libtimeinstate_test" + } + ] +} diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 2112b10ebc..1513ecafc8 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -27,6 +27,7 @@ #include <gtest/gtest.h> +#include <android-base/properties.h> #include <android-base/unique_fd.h> #include <bpf/BpfMap.h> #include <cputimeinstate.h> @@ -40,24 +41,31 @@ static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365; using std::vector; -TEST(TimeInStateTest, IsTrackingSupported) { - isTrackingUidTimesSupported(); - SUCCEED(); -} +class TimeInStateTest : public testing::Test { + protected: + TimeInStateTest() {}; + + void SetUp() { + if (!isTrackingUidTimesSupported() || + !android::base::GetBoolProperty("sys.init.perf_lsm_hooks", false)) { + GTEST_SKIP(); + } + } +}; -TEST(TimeInStateTest, TotalTimeInState) { +TEST_F(TimeInStateTest, TotalTimeInState) { auto times = getTotalCpuFreqTimes(); ASSERT_TRUE(times.has_value()); EXPECT_FALSE(times->empty()); } -TEST(TimeInStateTest, SingleUidTimeInState) { +TEST_F(TimeInStateTest, SingleUidTimeInState) { auto times = getUidCpuFreqTimes(0); ASSERT_TRUE(times.has_value()); EXPECT_FALSE(times->empty()); } -TEST(TimeInStateTest, SingleUidConcurrentTimes) { +TEST_F(TimeInStateTest, SingleUidConcurrentTimes) { auto concurrentTimes = getUidConcurrentTimes(0); ASSERT_TRUE(concurrentTimes.has_value()); ASSERT_FALSE(concurrentTimes->active.empty()); @@ -117,7 +125,7 @@ static void TestUidTimesConsistent(const std::vector<std::vector<uint64_t>> &tim EXPECT_EQ(activeSum, policySum); } -TEST(TimeInStateTest, SingleUidTimesConsistent) { +TEST_F(TimeInStateTest, SingleUidTimesConsistent) { auto times = getUidCpuFreqTimes(0); ASSERT_TRUE(times.has_value()); @@ -127,7 +135,7 @@ TEST(TimeInStateTest, SingleUidTimesConsistent) { ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes)); } -TEST(TimeInStateTest, AllUidTimeInState) { +TEST_F(TimeInStateTest, AllUidTimeInState) { uint64_t zero = 0; auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; for (const auto &map : maps) { @@ -163,7 +171,7 @@ void TestCheckUpdate(const std::vector<std::vector<uint64_t>> &before, ASSERT_LE(sumAfter - sumBefore, NSEC_PER_SEC); } -TEST(TimeInStateTest, AllUidUpdatedTimeInState) { +TEST_F(TimeInStateTest, AllUidUpdatedTimeInState) { uint64_t lastUpdate = 0; auto map1 = getUidsUpdatedCpuFreqTimes(&lastUpdate); ASSERT_TRUE(map1.has_value()); @@ -197,7 +205,7 @@ TEST(TimeInStateTest, AllUidUpdatedTimeInState) { } } -TEST(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) { +TEST_F(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) { auto allUid = getUidsCpuFreqTimes(); auto total = getTotalCpuFreqTimes(); @@ -222,7 +230,7 @@ TEST(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) { } } -TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { +TEST_F(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { uint64_t zero = 0; auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; for (const auto &map : maps) { @@ -246,7 +254,7 @@ TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { } } -TEST(TimeInStateTest, AllUidConcurrentTimes) { +TEST_F(TimeInStateTest, AllUidConcurrentTimes) { uint64_t zero = 0; auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; for (const auto &map : maps) { @@ -264,7 +272,7 @@ TEST(TimeInStateTest, AllUidConcurrentTimes) { } } -TEST(TimeInStateTest, AllUidUpdatedConcurrentTimes) { +TEST_F(TimeInStateTest, AllUidUpdatedConcurrentTimes) { uint64_t lastUpdate = 0; auto map1 = getUidsUpdatedConcurrentTimes(&lastUpdate); ASSERT_TRUE(map1.has_value()); @@ -299,7 +307,7 @@ TEST(TimeInStateTest, AllUidUpdatedConcurrentTimes) { } } -TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) { +TEST_F(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) { uint64_t zero = 0; auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; for (const auto &map : maps) { @@ -328,7 +336,7 @@ void TestCheckDelta(uint64_t before, uint64_t after) { ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf()); } -TEST(TimeInStateTest, TotalTimeInStateMonotonic) { +TEST_F(TimeInStateTest, TotalTimeInStateMonotonic) { auto before = getTotalCpuFreqTimes(); ASSERT_TRUE(before.has_value()); sleep(1); @@ -344,7 +352,7 @@ TEST(TimeInStateTest, TotalTimeInStateMonotonic) { } } -TEST(TimeInStateTest, AllUidTimeInStateMonotonic) { +TEST_F(TimeInStateTest, AllUidTimeInStateMonotonic) { auto map1 = getUidsCpuFreqTimes(); ASSERT_TRUE(map1.has_value()); sleep(1); @@ -365,7 +373,7 @@ TEST(TimeInStateTest, AllUidTimeInStateMonotonic) { } } -TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) { +TEST_F(TimeInStateTest, AllUidConcurrentTimesMonotonic) { auto map1 = getUidsConcurrentTimes(); ASSERT_TRUE(map1.has_value()); ASSERT_FALSE(map1->empty()); @@ -393,7 +401,7 @@ TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) { } } -TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) { +TEST_F(TimeInStateTest, AllUidTimeInStateSanityCheck) { uint64_t zero = 0; auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; for (const auto &map : maps) { @@ -414,7 +422,7 @@ TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) { } } -TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) { +TEST_F(TimeInStateTest, AllUidConcurrentTimesSanityCheck) { uint64_t zero = 0; auto maps = {getUidsConcurrentTimes(), getUidsUpdatedConcurrentTimes(&zero)}; for (const auto &concurrentMap : maps) { @@ -441,7 +449,7 @@ TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) { } } -TEST(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) { +TEST_F(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) { uint32_t uid = 0; { // Find an unused UID @@ -463,7 +471,7 @@ TEST(TimeInStateTest, AllUidConcurrentTimesFailsOnInvalidBucket) { ASSERT_FALSE(deleteMapEntry(fd, &key)); } -TEST(TimeInStateTest, AllUidTimesConsistent) { +TEST_F(TimeInStateTest, AllUidTimesConsistent) { auto tisMap = getUidsCpuFreqTimes(); ASSERT_TRUE(tisMap.has_value()); @@ -481,7 +489,7 @@ TEST(TimeInStateTest, AllUidTimesConsistent) { } } -TEST(TimeInStateTest, RemoveUid) { +TEST_F(TimeInStateTest, RemoveUid) { uint32_t uid = 0; { // Find an unused UID @@ -547,7 +555,7 @@ TEST(TimeInStateTest, RemoveUid) { ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end()); } -TEST(TimeInStateTest, GetCpuFreqs) { +TEST_F(TimeInStateTest, GetCpuFreqs) { auto freqs = getCpuFreqs(); ASSERT_TRUE(freqs.has_value()); @@ -583,7 +591,7 @@ void *testThread(void *) { return nullptr; } -TEST(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) { +TEST_F(TimeInStateTest, GetAggregatedTaskCpuFreqTimes) { uint64_t startTimeNs = timeNanos(); sem_init(&pingsem, 0, 1); diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp index 9f0754b891..61e4a98846 100644 --- a/libs/fakeservicemanager/ServiceManager.cpp +++ b/libs/fakeservicemanager/ServiceManager.cpp @@ -84,4 +84,14 @@ std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo return std::nullopt; } +status_t ServiceManager::registerForNotifications(const String16&, + const sp<LocalRegistrationCallback>&) { + return INVALID_OPERATION; +} + +status_t ServiceManager::unregisterForNotifications(const String16&, + const sp<LocalRegistrationCallback>&) { + return INVALID_OPERATION; +} + } // namespace android diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h index b1496ba50b..6d6e008c4f 100644 --- a/libs/fakeservicemanager/ServiceManager.h +++ b/libs/fakeservicemanager/ServiceManager.h @@ -53,6 +53,11 @@ public: std::optional<IServiceManager::ConnectionInfo> getConnectionInfo(const String16& name) override; + status_t registerForNotifications(const String16& name, + const sp<LocalRegistrationCallback>& callback) override; + + status_t unregisterForNotifications(const String16& name, + const sp<LocalRegistrationCallback>& callback) override; private: std::map<String16, sp<IBinder>> mNameToService; }; diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp index 6afd1729de..bd21fbaf0e 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -23,7 +23,6 @@ package { cc_library { name: "libgralloctypes", - defaults: ["libbinder_ndk_host_user"], cflags: [ "-Wall", "-Werror", diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp index 6689771a24..3c3b6af670 100644 --- a/libs/gralloc/types/fuzzer/Android.bp +++ b/libs/gralloc/types/fuzzer/Android.bp @@ -9,12 +9,11 @@ package { cc_fuzz { name: "libgralloctypes_fuzzer", - defaults: ["libbinder_ndk_host_user"], host_supported: true, target: { darwin: { enabled: false, - } + }, }, fuzz_config: { diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp index 0827bbe3ee..ea5fb293a6 100644 --- a/libs/gui/LayerDebugInfo.cpp +++ b/libs/gui/LayerDebugInfo.cpp @@ -58,7 +58,6 @@ status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const { RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferStride)); RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferFormat)); RETURN_ON_ERROR(parcel->writeInt32(mNumQueuedFrames)); - RETURN_ON_ERROR(parcel->writeBool(mRefreshPending)); RETURN_ON_ERROR(parcel->writeBool(mIsOpaque)); RETURN_ON_ERROR(parcel->writeBool(mContentDirty)); RETURN_ON_ERROR(parcel->write(mStretchEffect)); @@ -103,7 +102,6 @@ status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) { RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferStride)); RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferFormat)); RETURN_ON_ERROR(parcel->readInt32(&mNumQueuedFrames)); - RETURN_ON_ERROR(parcel->readBool(&mRefreshPending)); RETURN_ON_ERROR(parcel->readBool(&mIsOpaque)); RETURN_ON_ERROR(parcel->readBool(&mContentDirty)); RETURN_ON_ERROR(parcel->read(mStretchEffect)); @@ -146,8 +144,7 @@ std::string to_string(const LayerDebugInfo& info) { StringAppendF(&result, " activeBuffer=[%4ux%4u:%4u,%s],", info.mActiveBufferWidth, info.mActiveBufferHeight, info.mActiveBufferStride, decodePixelFormat(info.mActiveBufferFormat).c_str()); - StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d", info.mNumQueuedFrames, - info.mRefreshPending); + StringAppendF(&result, " queued-frames=%d", info.mNumQueuedFrames); result.append("\n"); return result; } diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 1c7b270527..a8667867b2 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -69,7 +69,7 @@ bool WindowInfo::operator==(const WindowInfo& info) const { info.hasWallpaper == hasWallpaper && info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid && info.packageName == packageName && info.inputFeatures == inputFeatures && - info.displayId == displayId && info.portalToDisplayId == portalToDisplayId && + info.displayId == displayId && info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && info.applicationInfo == applicationInfo; } @@ -116,7 +116,6 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->writeUtf8AsUtf16(packageName) ?: parcel->writeInt32(inputFeatures.get()) ?: parcel->writeInt32(displayId) ?: - parcel->writeInt32(portalToDisplayId) ?: applicationInfo.writeToParcel(parcel) ?: parcel->write(touchableRegion) ?: parcel->writeBool(replaceTouchableRegionWithCrop) ?: @@ -180,7 +179,6 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { inputFeatures = Flags<Feature>(parcel->readInt32()); // clang-format off status = parcel->readInt32(&displayId) ?: - parcel->readInt32(&portalToDisplayId) ?: applicationInfo.readFromParcel(parcel) ?: parcel->read(touchableRegion) ?: parcel->readBool(&replaceTouchableRegionWithCrop); diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h index 8b7d32c0a5..af834d78df 100644 --- a/libs/gui/include/gui/LayerDebugInfo.h +++ b/libs/gui/include/gui/LayerDebugInfo.h @@ -64,7 +64,6 @@ public: int32_t mActiveBufferStride = 0; PixelFormat mActiveBufferFormat = PIXEL_FORMAT_NONE; int32_t mNumQueuedFrames = -1; - bool mRefreshPending = false; bool mIsOpaque = false; bool mContentDirty = false; StretchEffect mStretchEffect = {}; diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index 2bfaec8d03..eb64ac9b25 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -214,7 +214,6 @@ struct WindowInfo : public Parcelable { std::string packageName; Flags<Feature> inputFeatures; int32_t displayId = ADISPLAY_ID_NONE; - int32_t portalToDisplayId = ADISPLAY_ID_NONE; InputApplicationInfo applicationInfo; bool replaceTouchableRegionWithCrop = false; wp<IBinder> touchableRegionCropHandle; diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp index dcdf76fe35..ff3ba2aae0 100644 --- a/libs/gui/tests/WindowInfo_test.cpp +++ b/libs/gui/tests/WindowInfo_test.cpp @@ -70,7 +70,6 @@ TEST(WindowInfo, Parcelling) { i.packageName = "com.example.package"; i.inputFeatures = WindowInfo::Feature::DISABLE_USER_ACTIVITY; i.displayId = 34; - i.portalToDisplayId = 2; i.replaceTouchableRegionWithCrop = true; i.touchableRegionCropHandle = touchableRegionCropHandle; i.applicationInfo.name = "ApplicationFooBar"; @@ -107,7 +106,6 @@ TEST(WindowInfo, Parcelling) { ASSERT_EQ(i.packageName, i2.packageName); ASSERT_EQ(i.inputFeatures, i2.inputFeatures); ASSERT_EQ(i.displayId, i2.displayId); - ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId); ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop); ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle); ASSERT_EQ(i.applicationInfo, i2.applicationInfo); diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp index 6882ea3090..0128859ca6 100644 --- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp @@ -593,6 +593,10 @@ status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const { } void EGLConsumer::onFreeBufferLocked(int slotIndex) { + if (mEglSlots[slotIndex].mEglImage != nullptr && + mEglSlots[slotIndex].mEglImage == mCurrentTextureImage) { + mCurrentTextureImage.clear(); + } mEglSlots[slotIndex].mEglImage.clear(); } diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 612a0aabdc..e197150afe 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -49,6 +49,50 @@ constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false; namespace android { namespace renderengine { +namespace { + +double EOTF_PQ(double channel) { + float m1 = (2610.0 / 4096.0) / 4.0; + float m2 = (2523.0 / 4096.0) * 128.0; + float c1 = (3424.0 / 4096.0); + float c2 = (2413.0 / 4096.0) * 32.0; + float c3 = (2392.0 / 4096.0) * 32.0; + + float tmp = std::pow(std::clamp(channel, 0.0, 1.0), 1.0 / m2); + tmp = std::fmax(tmp - c1, 0.0) / (c2 - c3 * tmp); + return std::pow(tmp, 1.0 / m1); +} + +vec3 EOTF_PQ(vec3 color) { + return vec3(EOTF_PQ(color.r), EOTF_PQ(color.g), EOTF_PQ(color.b)); +} + +double EOTF_HLG(double channel) { + const float a = 0.17883277; + const float b = 0.28466892; + const float c = 0.55991073; + return channel <= 0.5 ? channel * channel / 3.0 : (exp((channel - c) / a) + b) / 12.0; +} + +vec3 EOTF_HLG(vec3 color) { + return vec3(EOTF_HLG(color.r), EOTF_HLG(color.g), EOTF_HLG(color.b)); +} + +double OETF_sRGB(double channel) { + return channel <= 0.0031308 ? channel * 12.92 : (pow(channel, 1.0 / 2.4) * 1.055) - 0.055; +} + +int sign(float in) { + return in >= 0.0 ? 1 : -1; +} + +vec3 OETF_sRGB(vec3 linear) { + return vec3(sign(linear.r) * OETF_sRGB(linear.r), sign(linear.g) * OETF_sRGB(linear.g), + sign(linear.b) * OETF_sRGB(linear.b)); +} + +} // namespace + class RenderEngineFactory { public: virtual ~RenderEngineFactory() = default; @@ -598,6 +642,12 @@ public: const renderengine::ShadowSettings& shadow, const ubyte4& backgroundColor); + // Tonemaps grey values from sourceDataspace -> Display P3 and checks that GPU and CPU + // implementations are identical Also implicitly checks that the injected tonemap shader + // compiles + void tonemap(ui::Dataspace sourceDataspace, std::function<vec3(vec3)> eotf, + std::function<vec3(vec3, float)> scaleOotf); + void initializeRenderEngine(); std::unique_ptr<renderengine::RenderEngine> mRE; @@ -1418,6 +1468,119 @@ void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds, invokeDraw(settings, layers); } +void RenderEngineTest::tonemap(ui::Dataspace sourceDataspace, std::function<vec3(vec3)> eotf, + std::function<vec3(vec3, float)> scaleOotf) { + constexpr int32_t kGreyLevels = 256; + + const auto rect = Rect(0, 0, kGreyLevels, 1); + + constexpr float kMaxLuminance = 750.f; + constexpr float kCurrentLuminanceNits = 500.f; + const renderengine::DisplaySettings display{ + .physicalDisplay = rect, + .clip = rect, + .maxLuminance = kMaxLuminance, + .currentLuminanceNits = kCurrentLuminanceNits, + .outputDataspace = ui::Dataspace::DISPLAY_P3, + }; + + auto buf = std::make_shared< + renderengine::impl:: + ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, + 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "input"), + *mRE, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + ASSERT_EQ(0, buf->getBuffer()->initCheck()); + { + uint8_t* pixels; + buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + + uint8_t color = 0; + for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) { + uint8_t* dest = pixels + (buf->getBuffer()->getStride() * j * 4); + for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) { + dest[0] = color; + dest[1] = color; + dest[2] = color; + dest[3] = 255; + color++; + dest += 4; + } + } + buf->getBuffer()->unlock(); + } + + mBuffer = std::make_shared< + renderengine::impl:: + ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, + 1, + GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_SW_WRITE_OFTEN | + GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE, + "output"), + *mRE, + renderengine::impl::ExternalTexture::Usage::READABLE | + renderengine::impl::ExternalTexture::Usage::WRITEABLE); + ASSERT_EQ(0, mBuffer->getBuffer()->initCheck()); + + const renderengine::LayerSettings layer{.geometry.boundaries = rect.toFloatRect(), + .source = + renderengine::PixelSource{ + .buffer = + renderengine::Buffer{ + .buffer = + std::move(buf), + .usePremultipliedAlpha = + true, + }, + }, + .alpha = 1.0f, + .sourceDataspace = sourceDataspace}; + + std::vector<renderengine::LayerSettings> layers{layer}; + invokeDraw(display, layers); + + ColorSpace displayP3 = ColorSpace::DisplayP3(); + ColorSpace bt2020 = ColorSpace::BT2020(); + + tonemap::Metadata metadata{.displayMaxLuminance = 750.0f}; + + auto generator = [=](Point location) { + const double normColor = static_cast<double>(location.x) / (kGreyLevels - 1); + const vec3 rgb = vec3(normColor, normColor, normColor); + + const vec3 linearRGB = eotf(rgb); + + const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB; + + const vec3 scaledXYZ = scaleOotf(xyz, kCurrentLuminanceNits); + const double gain = + tonemap::getToneMapper() + ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common:: + Dataspace>(sourceDataspace), + static_cast<aidl::android::hardware::graphics::common:: + Dataspace>( + ui::Dataspace::DISPLAY_P3), + scaleOotf(linearRGB, kCurrentLuminanceNits), scaledXYZ, + metadata); + const vec3 normalizedXYZ = scaledXYZ * gain / metadata.displayMaxLuminance; + + const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * normalizedXYZ) * 255; + return ubyte4(static_cast<uint8_t>(targetRGB.r), static_cast<uint8_t>(targetRGB.g), + static_cast<uint8_t>(targetRGB.b), 255); + }; + + expectBufferColor(Rect(kGreyLevels, 1), generator, 2); +} + INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, testing::Values(std::make_shared<GLESRenderEngineFactory>(), std::make_shared<GLESCMRenderEngineFactory>(), @@ -2412,155 +2575,47 @@ TEST_P(RenderEngineTest, test_isOpaque) { } } -double EOTF_PQ(double channel) { - float m1 = (2610.0 / 4096.0) / 4.0; - float m2 = (2523.0 / 4096.0) * 128.0; - float c1 = (3424.0 / 4096.0); - float c2 = (2413.0 / 4096.0) * 32.0; - float c3 = (2392.0 / 4096.0) * 32.0; - - float tmp = std::pow(std::clamp(channel, 0.0, 1.0), 1.0 / m2); - tmp = std::fmax(tmp - c1, 0.0) / (c2 - c3 * tmp); - return std::pow(tmp, 1.0 / m1); -} - -vec3 EOTF_PQ(vec3 color) { - return vec3(EOTF_PQ(color.r), EOTF_PQ(color.g), EOTF_PQ(color.b)); -} - -double OETF_sRGB(double channel) { - return channel <= 0.0031308 ? channel * 12.92 : (pow(channel, 1.0 / 2.4) * 1.055) - 0.055; -} - -int sign(float in) { - return in >= 0.0 ? 1 : -1; -} - -vec3 OETF_sRGB(vec3 linear) { - return vec3(sign(linear.r) * OETF_sRGB(linear.r), sign(linear.g) * OETF_sRGB(linear.g), - sign(linear.b) * OETF_sRGB(linear.b)); -} - TEST_P(RenderEngineTest, test_tonemapPQMatches) { if (!GetParam()->useColorManagement()) { - return; + GTEST_SKIP(); } if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { - return; + GTEST_SKIP(); } initializeRenderEngine(); - constexpr int32_t kGreyLevels = 256; - - const auto rect = Rect(0, 0, kGreyLevels, 1); - const renderengine::DisplaySettings display{ - .physicalDisplay = rect, - .clip = rect, - .maxLuminance = 750.0f, - .outputDataspace = ui::Dataspace::DISPLAY_P3, - }; - - auto buf = std::make_shared< - renderengine::impl:: - ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, - GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE, - "input"), - *mRE, - renderengine::impl::ExternalTexture::Usage::READABLE | - renderengine::impl::ExternalTexture::Usage::WRITEABLE); - ASSERT_EQ(0, buf->getBuffer()->initCheck()); - - { - uint8_t* pixels; - buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, - reinterpret_cast<void**>(&pixels)); + tonemap( + static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 | + HAL_DATASPACE_TRANSFER_ST2084 | HAL_DATASPACE_RANGE_FULL), + [](vec3 color) { return EOTF_PQ(color); }, + [](vec3 color, float) { + static constexpr float kMaxPQLuminance = 10000.f; + return color * kMaxPQLuminance; + }); +} - uint8_t color = 0; - for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) { - uint8_t* dest = pixels + (buf->getBuffer()->getStride() * j * 4); - for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) { - dest[0] = color; - dest[1] = color; - dest[2] = color; - dest[3] = 255; - color++; - dest += 4; - } - } - buf->getBuffer()->unlock(); +TEST_P(RenderEngineTest, test_tonemapHLGMatches) { + if (!GetParam()->useColorManagement()) { + GTEST_SKIP(); } - mBuffer = std::make_shared< - renderengine::impl:: - ExternalTexture>(new GraphicBuffer(kGreyLevels, 1, HAL_PIXEL_FORMAT_RGBA_8888, - 1, - GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN | - GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE, - "output"), - *mRE, - renderengine::impl::ExternalTexture::Usage::READABLE | - renderengine::impl::ExternalTexture::Usage::WRITEABLE); - ASSERT_EQ(0, mBuffer->getBuffer()->initCheck()); - - const renderengine::LayerSettings layer{ - .geometry.boundaries = rect.toFloatRect(), - .source = - renderengine::PixelSource{ - .buffer = - renderengine::Buffer{ - .buffer = std::move(buf), - .usePremultipliedAlpha = true, - }, - }, - .alpha = 1.0f, - .sourceDataspace = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 | - HAL_DATASPACE_TRANSFER_ST2084 | - HAL_DATASPACE_RANGE_FULL), - }; - - std::vector<renderengine::LayerSettings> layers{layer}; - invokeDraw(display, layers); - - ColorSpace displayP3 = ColorSpace::DisplayP3(); - ColorSpace bt2020 = ColorSpace::BT2020(); - - tonemap::Metadata metadata{.displayMaxLuminance = 750.0f}; - - auto generator = [=](Point location) { - const double normColor = static_cast<double>(location.x) / (kGreyLevels - 1); - const vec3 rgb = vec3(normColor, normColor, normColor); - - const vec3 linearRGB = EOTF_PQ(rgb); - - static constexpr float kMaxPQLuminance = 10000.f; - const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB * kMaxPQLuminance; - const double gain = - tonemap::getToneMapper() - ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common:: - Dataspace>( - HAL_DATASPACE_STANDARD_BT2020 | - HAL_DATASPACE_TRANSFER_ST2084 | - HAL_DATASPACE_RANGE_FULL), - static_cast<aidl::android::hardware::graphics::common:: - Dataspace>( - ui::Dataspace::DISPLAY_P3), - linearRGB * 10000.0, xyz, metadata); - const vec3 scaledXYZ = xyz * gain / metadata.displayMaxLuminance; + if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) { + GTEST_SKIP(); + } - const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * scaledXYZ) * 255; - return ubyte4(static_cast<uint8_t>(targetRGB.r), static_cast<uint8_t>(targetRGB.g), - static_cast<uint8_t>(targetRGB.b), 255); - }; + initializeRenderEngine(); - expectBufferColor(Rect(kGreyLevels, 1), generator, 2); + tonemap( + static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 | HAL_DATASPACE_TRANSFER_HLG | + HAL_DATASPACE_RANGE_FULL), + [](vec3 color) { return EOTF_HLG(color); }, + [](vec3 color, float currentLuminaceNits) { + static constexpr float kMaxHLGLuminance = 1000.f; + static const float kHLGGamma = 1.2 + 0.42 * std::log10(currentLuminaceNits / 1000); + return color * kMaxHLGLuminance * std::pow(color.y, kHLGGamma - 1); + }); } TEST_P(RenderEngineTest, r8_behaves_as_mask) { diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index da88e8541a..ec0ced8663 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -280,6 +280,26 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi mStringType = SENSOR_STRING_TYPE_HEAD_TRACKER; mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; break; + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES: + mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_LIMITED_AXES; + mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; + break; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES: + mStringType = SENSOR_STRING_TYPE_GYROSCOPE_LIMITED_AXES; + mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; + break; + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + mStringType = SENSOR_STRING_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED; + mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; + break; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED: + mStringType = SENSOR_STRING_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED; + mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; + break; + case SENSOR_TYPE_HEADING: + mStringType = SENSOR_STRING_TYPE_HEADING; + mFlags |= SENSOR_FLAG_CONTINUOUS_MODE; + break; default: // Only pipe the stringType, requiredPermission and flags for custom sensors. if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.stringType) { diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp index 4d88d5d417..03da3ecd62 100644 --- a/libs/shaders/shaders.cpp +++ b/libs/shaders/shaders.cpp @@ -18,6 +18,7 @@ #include <tonemap/tonemap.h> +#include <cmath> #include <optional> #include <math/mat4.h> @@ -26,12 +27,13 @@ namespace android::shaders { -static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace( - ui::Dataspace dataspace) { +namespace { + +aidl::android::hardware::graphics::common::Dataspace toAidlDataspace(ui::Dataspace dataspace) { return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace); } -static void generateEOTF(ui::Dataspace dataspace, std::string& shader) { +void generateEOTF(ui::Dataspace dataspace, std::string& shader) { switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { case HAL_DATASPACE_TRANSFER_ST2084: shader.append(R"( @@ -156,7 +158,7 @@ static void generateEOTF(ui::Dataspace dataspace, std::string& shader) { } } -static void generateXYZTransforms(std::string& shader) { +void generateXYZTransforms(std::string& shader) { shader.append(R"( uniform float4x4 in_rgbToXyz; uniform float4x4 in_xyzToRgb; @@ -171,8 +173,8 @@ static void generateXYZTransforms(std::string& shader) { } // Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits]) -static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, - ui::Dataspace outputDataspace, std::string& shader) { +void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace, + std::string& shader) { switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) { case HAL_DATASPACE_TRANSFER_ST2084: shader.append(R"( @@ -183,8 +185,9 @@ static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace, break; case HAL_DATASPACE_TRANSFER_HLG: shader.append(R"( + uniform float in_hlgGamma; float3 ScaleLuminance(float3 xyz) { - return xyz * 1000.0 * pow(xyz.y, 0.2); + return xyz * 1000.0 * pow(xyz.y, in_hlgGamma - 1); } )"); break; @@ -225,8 +228,10 @@ static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, break; case HAL_DATASPACE_TRANSFER_HLG: shader.append(R"( + uniform float in_hlgGamma; float3 NormalizeLuminance(float3 xyz) { - return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2); + return xyz / 1000.0 * + pow(xyz.y / 1000.0, (1 - in_hlgGamma) / (in_hlgGamma)); } )"); break; @@ -240,8 +245,8 @@ static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, } } -static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace, - std::string& shader) { +void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace, + std::string& shader) { shader.append(tonemap::getToneMapper() ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace), toAidlDataspace(outputDataspace)) @@ -262,7 +267,7 @@ static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDatas )"); } -static void generateOETF(ui::Dataspace dataspace, std::string& shader) { +void generateOETF(ui::Dataspace dataspace, std::string& shader) { switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) { case HAL_DATASPACE_TRANSFER_ST2084: shader.append(R"( @@ -384,7 +389,7 @@ static void generateOETF(ui::Dataspace dataspace, std::string& shader) { } } -static void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) { +void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) { shader.append(R"( uniform shader child; half4 main(float2 xy) { @@ -412,7 +417,7 @@ static void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shad } // please keep in sync with toSkColorSpace function in renderengine/skia/ColorSpaces.cpp -static ColorSpace toColorSpace(ui::Dataspace dataspace) { +ColorSpace toColorSpace(ui::Dataspace dataspace) { switch (dataspace & HAL_DATASPACE_STANDARD_MASK) { case HAL_DATASPACE_STANDARD_BT709: return ColorSpace::sRGB(); @@ -438,6 +443,21 @@ static ColorSpace toColorSpace(ui::Dataspace dataspace) { } } +template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true> +std::vector<uint8_t> buildUniformValue(T value) { + std::vector<uint8_t> result; + result.resize(sizeof(value)); + std::memcpy(result.data(), &value, sizeof(value)); + return result; +} + +// Refer to BT2100-2 +float computeHlgGamma(float currentDisplayBrightnessNits) { + return 1.2 + 0.42 * std::log10(currentDisplayBrightnessNits / 1000); +} + +} // namespace + std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) { std::string shaderString; generateEOTF(linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN @@ -451,14 +471,6 @@ std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) { return shaderString; } -template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true> -std::vector<uint8_t> buildUniformValue(T value) { - std::vector<uint8_t> result; - result.resize(sizeof(value)); - std::memcpy(result.data(), &value, sizeof(value)); - return result; -} - // Generates a list of uniforms to set on the LinearEffect shader above. std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect, const mat4& colorTransform, @@ -480,8 +492,13 @@ std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect colorTransform * mat4(outputColorSpace.getXYZtoRGB()))}); } + if ((linearEffect.inputDataspace & HAL_DATASPACE_TRANSFER_MASK) == HAL_DATASPACE_TRANSFER_HLG) { + uniforms.push_back( + {.name = "in_hlgGamma", + .value = buildUniformValue<float>(computeHlgGamma(currentDisplayLuminanceNits))}); + } + tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance, - .currentDisplayLuminanceNits = currentDisplayLuminanceNits, // If the input luminance is unknown, use display luminance (aka, // no-op any luminance changes) // This will be the case for eg screenshots in addition to diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h index 6233e6c418..b9abf8cd52 100644 --- a/libs/tonemap/include/tonemap/tonemap.h +++ b/libs/tonemap/include/tonemap/tonemap.h @@ -44,8 +44,6 @@ struct ShaderUniform { struct Metadata { // The maximum luminance of the display in nits float displayMaxLuminance = 0.0; - // The current luminance of the display in nits - float currentDisplayLuminanceNits = 0.0; // The maximum luminance of the content in nits float contentMaxLuminance = 0.0; }; diff --git a/libs/tonemap/tests/tonemap_test.cpp b/libs/tonemap/tests/tonemap_test.cpp index 7a7958f58f..1d46482627 100644 --- a/libs/tonemap/tests/tonemap_test.cpp +++ b/libs/tonemap/tests/tonemap_test.cpp @@ -61,7 +61,7 @@ TEST_F(TonemapTest, generateShaderSkSLUniforms_containsDefaultUniforms) { EXPECT_GT(contentLumFloat, 0); } -TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPoint) { +TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPointForPQ) { const auto shader = tonemap::getToneMapper() ->generateTonemapGainShaderSkSL(aidl::android::hardware::graphics::common:: @@ -73,4 +73,16 @@ TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPoint) { EXPECT_THAT(shader, HasSubstr("float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz)")); } +TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPointForHLG) { + const auto shader = + tonemap::getToneMapper() + ->generateTonemapGainShaderSkSL(aidl::android::hardware::graphics::common:: + Dataspace::BT2020_ITU_HLG, + aidl::android::hardware::graphics::common:: + Dataspace::DISPLAY_P3); + + // Other tests such as librenderengine_test will plug in the shader to check compilation. + EXPECT_THAT(shader, HasSubstr("float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz)")); +} + } // namespace android diff --git a/libs/tonemap/tonemap.cpp b/libs/tonemap/tonemap.cpp index c2372fe828..bc0a884ee4 100644 --- a/libs/tonemap/tonemap.cpp +++ b/libs/tonemap/tonemap.cpp @@ -407,7 +407,6 @@ public: )"); switch (sourceDataspaceInt & kTransferMask) { case kTransferST2084: - case kTransferHLG: switch (destinationDataspaceInt & kTransferMask) { case kTransferST2084: program.append(R"( @@ -428,39 +427,22 @@ public: break; default: - switch (sourceDataspaceInt & kTransferMask) { - case kTransferST2084: - program.append(R"( - float libtonemap_OETFTone(float channel) { - channel = channel / 10000.0; - float m1 = (2610.0 / 4096.0) / 4.0; - float m2 = (2523.0 / 4096.0) * 128.0; - float c1 = (3424.0 / 4096.0); - float c2 = (2413.0 / 4096.0) * 32.0; - float c3 = (2392.0 / 4096.0) * 32.0; - - float tmp = pow(channel, float(m1)); - tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); - return pow(tmp, float(m2)); - } - )"); - break; - case kTransferHLG: - program.append(R"( - float libtonemap_OETFTone(float channel) { - channel = channel / 1000.0; - const float a = 0.17883277; - const float b = 0.28466892; - const float c = 0.55991073; - return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) : - a * log(12.0 * channel - b) + c; - } - )"); - break; - } // Here we're mapping from HDR to SDR content, so interpolate using a // Hermitian polynomial onto the smaller luminance range. program.append(R"( + float libtonemap_OETFTone(float channel) { + channel = channel / 10000.0; + float m1 = (2610.0 / 4096.0) / 4.0; + float m2 = (2523.0 / 4096.0) * 128.0; + float c1 = (3424.0 / 4096.0); + float c2 = (2413.0 / 4096.0) * 32.0; + float c3 = (2392.0 / 4096.0) * 32.0; + + float tmp = pow(channel, float(m1)); + tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp); + return pow(tmp, float(m2)); + } + float libtonemap_ToneMapTargetNits(float maxRGB) { float maxInLumi = in_libtonemap_inputMaxLuminance; float maxOutLumi = in_libtonemap_displayMaxLuminance; @@ -508,6 +490,30 @@ public: break; } break; + case kTransferHLG: + switch (destinationDataspaceInt & kTransferMask) { + // HLG -> HDR does not tone-map at all + case kTransferST2084: + case kTransferHLG: + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + return maxRGB; + } + )"); + break; + default: + // libshaders follows BT2100 OOTF, but with a nominal peak display luminance + // of 1000 nits. Renormalize to max display luminance if we're tone-mapping + // down to SDR, as libshaders normalizes all SDR output from [0, + // maxDisplayLumins] -> [0, 1] + program.append(R"( + float libtonemap_ToneMapTargetNits(float maxRGB) { + return maxRGB * in_libtonemap_displayMaxLuminance / 1000.0; + } + )"); + break; + } + break; default: // Inverse tone-mapping and SDR-SDR mapping is not supported. program.append(R"( @@ -558,7 +564,6 @@ public: double targetNits = 0.0; switch (sourceDataspaceInt & kTransferMask) { case kTransferST2084: - case kTransferHLG: switch (destinationDataspaceInt & kTransferMask) { case kTransferST2084: targetNits = maxRGB; @@ -587,19 +592,9 @@ public: double x2 = x1 + (x3 - x1) * 4.0 / 17.0; double y2 = maxOutLumi * 0.9; - double greyNorm1 = 0.0; - double greyNorm2 = 0.0; - double greyNorm3 = 0.0; - - if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) { - greyNorm1 = OETF_ST2084(x1); - greyNorm2 = OETF_ST2084(x2); - greyNorm3 = OETF_ST2084(x3); - } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) { - greyNorm1 = OETF_HLG(x1); - greyNorm2 = OETF_HLG(x2); - greyNorm3 = OETF_HLG(x3); - } + const double greyNorm1 = OETF_ST2084(x1); + const double greyNorm2 = OETF_ST2084(x2); + const double greyNorm3 = OETF_ST2084(x3); double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1); double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2); @@ -613,12 +608,7 @@ public: break; } - double greyNits = 0.0; - if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) { - greyNits = OETF_ST2084(targetNits); - } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) { - greyNits = OETF_HLG(targetNits); - } + const double greyNits = OETF_ST2084(targetNits); if (greyNits <= greyNorm2) { targetNits = (greyNits - greyNorm2) * slope2 + y2; @@ -630,15 +620,20 @@ public: break; } break; - default: + case kTransferHLG: switch (destinationDataspaceInt & kTransferMask) { case kTransferST2084: case kTransferHLG: - default: targetNits = maxRGB; break; + default: + targetNits = maxRGB * metadata.displayMaxLuminance / 1000.0; + break; } break; + default: + targetNits = maxRGB; + break; } return targetNits / maxRGB; diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp index 33ab7c470e..cc96f83578 100644 --- a/libs/ui/Fence.cpp +++ b/libs/ui/Fence.cpp @@ -132,9 +132,13 @@ nsecs_t Fence::getSignalTime() const { ALOGE("sync_file_info returned NULL for fd %d", mFenceFd.get()); return SIGNAL_TIME_INVALID; } + if (finfo->status != 1) { + const auto status = finfo->status; + ALOGE_IF(status < 0, "%s: sync_file_info contains an error: <%d> for fd: <%d>", __func__, + status, mFenceFd.get()); sync_file_info_free(finfo); - return SIGNAL_TIME_PENDING; + return status < 0 ? SIGNAL_TIME_INVALID : SIGNAL_TIME_PENDING; } uint64_t timestamp = 0; diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index 9922d6afaf..1a42642838 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -62,6 +62,9 @@ namespace { static constexpr Error kTransactionError = Error::NO_RESOURCES; static const auto kAidlAllocatorServiceName = AidlIAllocator::descriptor + std::string("/default"); +// TODO(b/72323293, b/72703005): Remove these invalid bits from callers +static constexpr uint64_t kRemovedUsageBits = static_cast<uint64_t>((1 << 10) | (1 << 13)); + uint64_t getValidUsageBits() { static const uint64_t validUsageBits = []() -> uint64_t { uint64_t bits = 0; @@ -71,7 +74,7 @@ uint64_t getValidUsageBits() { } return bits; }(); - return validUsageBits; + return validUsageBits | kRemovedUsageBits; } uint64_t getValidUsageBits41() { @@ -98,9 +101,6 @@ static inline IMapper::Rect sGralloc4Rect(const Rect& rect) { static bool hasIAllocatorAidl() { // Avoid re-querying repeatedly for this information; static bool sHasIAllocatorAidl = []() -> bool { - // TODO: Enable after landing sepolicy changes - if constexpr ((true)) return false; - if (__builtin_available(android 31, *)) { return AServiceManager_isDeclared(kAidlAllocatorServiceName.c_str()); } diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index ec5832515c..22a69e5a30 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -49,6 +49,7 @@ filegroup { srcs: [ "InputClassifier.cpp", "InputClassifierConverter.cpp", + "UnwantedInteractionBlocker.cpp", "InputManager.cpp", ], } @@ -60,6 +61,7 @@ cc_defaults { "android.hardware.input.classifier@1.0", "libbase", "libbinder", + "libchrome", "libcrypto", "libcutils", "libhidlbase", @@ -76,6 +78,7 @@ cc_defaults { ], static_libs: [ "libattestation", + "libpalmrejection", ], } diff --git a/services/inputflinger/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp index fc8c7c39f9..b58a188a82 100644 --- a/services/inputflinger/InputClassifierConverter.cpp +++ b/services/inputflinger/InputClassifierConverter.cpp @@ -325,11 +325,6 @@ static std::vector<common::V1_0::VideoFrame> convertVideoFrames( return out; } -static uint8_t getActionIndex(int32_t action) { - return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> - AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; -} - static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args, std::vector<common::V1_0::PointerProperties>* outPointerProperties, std::vector<common::V1_0::PointerCoords>* outPointerCoords) { @@ -360,7 +355,7 @@ common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArg event.eventTime = args.eventTime; event.deviceTimestamp = 0; event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK); - event.actionIndex = getActionIndex(args.action); + event.actionIndex = MotionEvent::getActionIndex(args.action); event.actionButton = getActionButton(args.actionButton); event.flags = getFlags(args.flags); event.policyFlags = getPolicyFlags(args.policyFlags); diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 158d0ebd58..73b63e3141 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -188,6 +188,25 @@ bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { return true; } +std::string NotifyMotionArgs::dump() const { + std::string coords; + for (uint32_t i = 0; i < pointerCount; i++) { + if (!coords.empty()) { + coords += ", "; + } + coords += StringPrintf("{%" PRIu32 ": ", i); + coords += + StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f, pressure=%.1f", pointerProperties[i].id, + pointerCoords[i].getX(), pointerCoords[i].getY(), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + coords += "}"; + } + return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32 + ", source=%s, action=%s, pointerCount=%" PRIu32 " pointers=%s)", + id, eventTime, deviceId, inputEventSourceToString(source).c_str(), + MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str()); +} + void NotifyMotionArgs::notify(InputListenerInterface& listener) const { listener.notifyMotion(this); } diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 221e193136..7a9862d065 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -21,6 +21,7 @@ #include "InputManager.h" #include "InputDispatcherFactory.h" #include "InputReaderFactory.h" +#include "UnwantedInteractionBlocker.h" #include <binder/IPCThreadState.h> @@ -54,12 +55,17 @@ static int32_t exceptionCodeFromStatusT(status_t status) { } } +/** + * The event flow is via the "InputListener" interface, as follows: + * InputReader -> UnwantedInteractionBlocker -> InputClassifier -> InputDispatcher + */ InputManager::InputManager( const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { mDispatcher = createInputDispatcher(dispatcherPolicy); mClassifier = std::make_unique<InputClassifier>(*mDispatcher); - mReader = createInputReader(readerPolicy, *mClassifier); + mUnwantedInteractionBlocker = std::make_unique<UnwantedInteractionBlocker>(*mClassifier); + mReader = createInputReader(readerPolicy, *mUnwantedInteractionBlocker); } InputManager::~InputManager() { @@ -106,6 +112,10 @@ InputReaderInterface& InputManager::getReader() { return *mReader; } +UnwantedInteractionBlockerInterface& InputManager::getUnwantedInteractionBlocker() { + return *mUnwantedInteractionBlocker; +} + InputClassifierInterface& InputManager::getClassifier() { return *mClassifier; } diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index e00028364a..35d2b0fa19 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -23,6 +23,7 @@ #include "InputClassifier.h" #include "InputReaderBase.h" +#include "include/UnwantedInteractionBlockerInterface.h" #include <InputDispatcherInterface.h> #include <InputDispatcherPolicyInterface.h> @@ -46,11 +47,16 @@ class InputDispatcherThread; * The input manager has three components. * * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies - * policy, and posts messages to a queue managed by the InputClassifier. - * 2. The InputClassifier class starts a thread to communicate with the device-specific - * classifiers. It then waits on the queue of events from InputReader, applies a classification - * to them, and queues them for the InputDispatcher. - * 3. The InputDispatcher class starts a thread that waits for new events on the + * policy, and posts messages to a queue managed by the UnwantedInteractionBlocker. + * 2. The UnwantedInteractionBlocker is responsible for removing unwanted interactions. For example, + * this could be a palm on the screen. This stage would alter the event stream to remove either + * partially (some of the pointers) or fully (all touches) the unwanted interaction. The events + * are processed on the InputReader thread, without any additional queue. The events are then + * posted to the queue managed by the InputClassifier. + * 3. The InputClassifier class starts a thread to communicate with the device-specific + * classifiers. It then waits on the queue of events from UnwantedInteractionBlocker, applies + * a classification to them, and queues them for the InputDispatcher. + * 4. The InputDispatcher class starts a thread that waits for new events on the * previous queue and asynchronously dispatches them to applications. * * By design, none of these classes share any internal state. Moreover, all communication is @@ -76,6 +82,9 @@ public: /* Gets the input reader. */ virtual InputReaderInterface& getReader() = 0; + /* Gets the unwanted interaction blocker. */ + virtual UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() = 0; + /* Gets the input classifier */ virtual InputClassifierInterface& getClassifier() = 0; @@ -96,6 +105,7 @@ public: status_t stop() override; InputReaderInterface& getReader() override; + UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() override; InputClassifierInterface& getClassifier() override; InputDispatcherInterface& getDispatcher() override; @@ -107,6 +117,8 @@ public: private: std::unique_ptr<InputReaderInterface> mReader; + std::unique_ptr<UnwantedInteractionBlockerInterface> mUnwantedInteractionBlocker; + std::unique_ptr<InputClassifierInterface> mClassifier; std::unique_ptr<InputDispatcherInterface> mDispatcher; diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp new file mode 100644 index 0000000000..64dbb8ceb4 --- /dev/null +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -0,0 +1,687 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "UnwantedInteractionBlocker" +#include "UnwantedInteractionBlocker.h" + +#include <android-base/stringprintf.h> +#include <inttypes.h> +#include <linux/input-event-codes.h> +#include <linux/input.h> +#include <server_configurable_flags/get_flags.h> + +#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h" +#include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h" + +using android::base::StringPrintf; + +namespace android { + +// Category (=namespace) name for the input settings that are applied at boot time +static const char* INPUT_NATIVE_BOOT = "input_native_boot"; +/** + * Feature flag name. This flag determines whether palm rejection is enabled. To enable, specify + * 'true' (not case sensitive) or '1'. To disable, specify any other value. + */ +static const char* PALM_REJECTION_ENABLED = "palm_rejection_enabled"; + +static std::string toLower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + return s; +} + +static bool isFromTouchscreen(int32_t source) { + return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN); +} + +static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) { + return ::base::TimeTicks::UnixEpoch() + + ::base::Milliseconds(static_cast<float>(ns2ms(eventTime))); +} + +/** + * Return true if palm rejection is enabled via the server configurable flags. Return false + * otherwise. + */ +static bool isPalmRejectionEnabled() { + std::string value = toLower( + server_configurable_flags::GetServerConfigurableFlag(INPUT_NATIVE_BOOT, + PALM_REJECTION_ENABLED, "false")); + if (value == "true" || value == "1") { + return true; + } + return false; +} + +static int getLinuxToolType(int32_t toolType) { + switch (toolType) { + case AMOTION_EVENT_TOOL_TYPE_FINGER: + return MT_TOOL_FINGER; + case AMOTION_EVENT_TOOL_TYPE_STYLUS: + return MT_TOOL_PEN; + case AMOTION_EVENT_TOOL_TYPE_PALM: + return MT_TOOL_PALM; + } + ALOGW("Got tool type %" PRId32 ", converting to MT_TOOL_FINGER", toolType); + return MT_TOOL_FINGER; +} + +static std::string addPrefix(std::string str, const std::string& prefix) { + std::stringstream ss; + bool newLineStarted = true; + for (const auto& ch : str) { + if (newLineStarted) { + ss << prefix; + newLineStarted = false; + } + if (ch == '\n') { + newLineStarted = true; + } + ss << ch; + } + return ss.str(); +} + +template <typename T> +static std::string dumpSet(const std::set<T>& v) { + static_assert(std::is_integral<T>::value, "Only integral types can be printed."); + std::string out; + for (const T& entry : v) { + out += out.empty() ? "{" : ", "; + out += android::base::StringPrintf("%i", entry); + } + return out.empty() ? "{}" : (out + "}"); +} + +template <typename K, typename V> +static std::string dumpMap(const std::map<K, V>& map) { + static_assert(std::is_integral<K>::value, "Keys should have integral type to be printed."); + static_assert(std::is_integral<V>::value, "Values should have integral type to be printed."); + std::string out; + for (const auto& [k, v] : map) { + if (!out.empty()) { + out += "\n"; + } + out += android::base::StringPrintf("%i : %i", static_cast<int>(k), static_cast<int>(v)); + } + return out; +} + +static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) { + std::string out; + out += StringPrintf("max_x = %.2f\n", info.max_x); + out += StringPrintf("max_y = %.2f\n", info.max_y); + out += StringPrintf("x_res = %.2f\n", info.x_res); + out += StringPrintf("y_res = %.2f\n", info.y_res); + out += StringPrintf("major_radius_res = %.2f\n", info.major_radius_res); + out += StringPrintf("minor_radius_res = %.2f\n", info.minor_radius_res); + out += StringPrintf("minor_radius_supported = %s\n", + info.minor_radius_supported ? "true" : "false"); + out += StringPrintf("touch_major_res = %" PRId32 "\n", info.touch_major_res); + out += StringPrintf("touch_minor_res = %" PRId32 "\n", info.touch_minor_res); + return out; +} + +static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) { + for (size_t i = 0; i < args.pointerCount; i++) { + if (pointerId == args.pointerProperties[i].id) { + return AMOTION_EVENT_ACTION_POINTER_UP | + (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + } + } + LOG_ALWAYS_FATAL("Can't find pointerId %" PRId32 " in %s", pointerId, args.dump().c_str()); +} + +/** + * Find the action for individual pointer at the given pointer index. + * This is always equal to MotionEvent::getActionMasked, except for + * POINTER_UP or POINTER_DOWN events. For example, in a POINTER_UP event, the action for + * the active pointer is ACTION_POINTER_UP, while the action for the other pointers is ACTION_MOVE. + */ +static int32_t resolveActionForPointer(uint8_t pointerIndex, int32_t action) { + const int32_t actionMasked = MotionEvent::getActionMasked(action); + if (actionMasked != AMOTION_EVENT_ACTION_POINTER_DOWN && + actionMasked != AMOTION_EVENT_ACTION_POINTER_UP) { + return actionMasked; + } + // This is a POINTER_DOWN or POINTER_UP event + const uint8_t actionIndex = MotionEvent::getActionIndex(action); + if (pointerIndex == actionIndex) { + return actionMasked; + } + // When POINTER_DOWN or POINTER_UP happens, it's actually a MOVE for all of the other + // pointers + return AMOTION_EVENT_ACTION_MOVE; +} + +static const char* toString(bool value) { + return value ? "true" : "false"; +} + +std::string toString(const ::ui::InProgressTouchEvdev& touch) { + return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu," + " pressure=%.1f, major=%i, minor=%i, " + "tool_type=%i, altered=%s, was_touching=%s, touching=%s", + touch.x, touch.y, touch.tracking_id, touch.slot, touch.pressure, + touch.major, touch.minor, touch.tool_type, toString(touch.altered), + toString(touch.was_touching), toString(touch.touching)); +} + +/** + * Remove the data for the provided pointers from the args. The pointers are identified by their + * pointerId, not by the index inside the array. + * Return the new NotifyMotionArgs struct that has the remaining pointers. + * The only fields that may be different in the returned args from the provided args are: + * - action + * - pointerCount + * - pointerProperties + * - pointerCoords + * Action might change because it contains a pointer index. If another pointer is removed, the + * active pointer index would be shifted. + * Do not call this function for events with POINTER_UP or POINTER_DOWN events when removed pointer + * id is the acting pointer id. + * + * @param args the args from which the pointers should be removed + * @param pointerIds the pointer ids of the pointers that should be removed + */ +NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, + const std::set<int32_t>& pointerIds) { + const uint8_t actionIndex = MotionEvent::getActionIndex(args.action); + const int32_t actionMasked = MotionEvent::getActionMasked(args.action); + const bool isPointerUpOrDownAction = actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN || + actionMasked == AMOTION_EVENT_ACTION_POINTER_UP; + + NotifyMotionArgs newArgs{args}; + newArgs.pointerCount = 0; + int32_t newActionIndex = 0; + for (uint32_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + if (pointerIds.find(pointerId) != pointerIds.end()) { + // skip this pointer + if (isPointerUpOrDownAction && i == actionIndex) { + // The active pointer is being removed, so the action is no longer valid. + // Set the action to 'UNKNOWN' here. The caller is responsible for updating this + // action later to a proper value. + newArgs.action = ACTION_UNKNOWN; + } + continue; + } + newArgs.pointerProperties[newArgs.pointerCount].copyFrom(args.pointerProperties[i]); + newArgs.pointerCoords[newArgs.pointerCount].copyFrom(args.pointerCoords[i]); + if (i == actionIndex) { + newActionIndex = newArgs.pointerCount; + } + newArgs.pointerCount++; + } + // Update POINTER_DOWN or POINTER_UP actions + if (isPointerUpOrDownAction && newArgs.action != ACTION_UNKNOWN) { + newArgs.action = + actionMasked | (newActionIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + // Convert POINTER_DOWN and POINTER_UP to DOWN and UP if there's only 1 pointer remaining + if (newArgs.pointerCount == 1) { + if (actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) { + newArgs.action = AMOTION_EVENT_ACTION_DOWN; + } else if (actionMasked == AMOTION_EVENT_ACTION_POINTER_UP) { + newArgs.action = AMOTION_EVENT_ACTION_UP; + } + } + } + return newArgs; +} + +std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo( + const InputDeviceInfo& deviceInfo) { + if (!isFromTouchscreen(deviceInfo.getSources())) { + return std::nullopt; + } + AndroidPalmFilterDeviceInfo out; + const InputDeviceInfo::MotionRange* axisX = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN); + if (axisX != nullptr) { + out.max_x = axisX->max; + out.x_res = axisX->resolution; + } else { + ALOGW("Palm rejection is disabled for %s because AXIS_X is not supported", + deviceInfo.getDisplayName().c_str()); + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisY = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN); + if (axisY != nullptr) { + out.max_y = axisY->max; + out.y_res = axisY->resolution; + } else { + ALOGW("Palm rejection is disabled for %s because AXIS_Y is not supported", + deviceInfo.getDisplayName().c_str()); + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisMajor = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN); + if (axisMajor != nullptr) { + out.major_radius_res = axisMajor->resolution; + out.touch_major_res = axisMajor->resolution; + } else { + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisMinor = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MINOR, AINPUT_SOURCE_TOUCHSCREEN); + if (axisMinor != nullptr) { + out.minor_radius_res = axisMinor->resolution; + out.touch_minor_res = axisMinor->resolution; + out.minor_radius_supported = true; + } else { + out.minor_radius_supported = false; + } + + return out; +} + +/** + * Synthesize CANCEL events for any new pointers that should be canceled, while removing pointers + * that have already been canceled. + * The flow of the function is as follows: + * 1. Remove all already canceled pointers + * 2. Cancel all newly suppressed pointers + * 3. Decide what to do with the current event : keep it, or drop it + * The pointers can never be "unsuppressed": once a pointer is canceled, it will never become valid. + */ +std::vector<NotifyMotionArgs> cancelSuppressedPointers( + const NotifyMotionArgs& args, const std::set<int32_t>& oldSuppressedPointerIds, + const std::set<int32_t>& newSuppressedPointerIds) { + LOG_ALWAYS_FATAL_IF(args.pointerCount == 0, "0 pointers in %s", args.dump().c_str()); + + // First, let's remove the old suppressed pointers. They've already been canceled previously. + NotifyMotionArgs oldArgs = removePointerIds(args, oldSuppressedPointerIds); + + // Cancel any newly suppressed pointers. + std::vector<NotifyMotionArgs> out; + const int32_t activePointerId = + args.pointerProperties[MotionEvent::getActionIndex(args.action)].id; + const int32_t actionMasked = MotionEvent::getActionMasked(args.action); + // We will iteratively remove pointers from 'removedArgs'. + NotifyMotionArgs removedArgs{oldArgs}; + for (uint32_t i = 0; i < oldArgs.pointerCount; i++) { + const int32_t pointerId = oldArgs.pointerProperties[i].id; + if (newSuppressedPointerIds.find(pointerId) == newSuppressedPointerIds.end()) { + // This is a pointer that should not be canceled. Move on. + continue; + } + if (pointerId == activePointerId && actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) { + // Remove this pointer, but don't cancel it. We'll just not send the POINTER_DOWN event + removedArgs = removePointerIds(removedArgs, {pointerId}); + continue; + } + + if (removedArgs.pointerCount == 1) { + // We are about to remove the last pointer, which means there will be no more gesture + // remaining. This is identical to canceling all pointers, so just send a single CANCEL + // event, without any of the preceding POINTER_UP with FLAG_CANCELED events. + oldArgs.flags |= AMOTION_EVENT_FLAG_CANCELED; + oldArgs.action = AMOTION_EVENT_ACTION_CANCEL; + return {oldArgs}; + } + // Cancel the current pointer + out.push_back(removedArgs); + out.back().flags |= AMOTION_EVENT_FLAG_CANCELED; + out.back().action = getActionUpForPointerId(out.back(), pointerId); + + // Remove the newly canceled pointer from the args + removedArgs = removePointerIds(removedArgs, {pointerId}); + } + + // Now 'removedArgs' contains only pointers that are valid. + if (removedArgs.pointerCount <= 0 || removedArgs.action == ACTION_UNKNOWN) { + return out; + } + out.push_back(removedArgs); + return out; +} + +UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener) + : UnwantedInteractionBlocker(listener, isPalmRejectionEnabled()){}; + +UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener, + bool enablePalmRejection) + : mListener(listener), mEnablePalmRejection(enablePalmRejection) {} + +void UnwantedInteractionBlocker::notifyConfigurationChanged( + const NotifyConfigurationChangedArgs* args) { + mListener.notifyConfigurationChanged(args); +} + +void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs* args) { + mListener.notifyKey(args); +} + +void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) { + auto it = mPalmRejectors.find(args->deviceId); + const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source); + if (!sendToPalmRejector) { + mListener.notifyMotion(args); + return; + } + + const std::vector<NotifyMotionArgs> newMotions = it->second.processMotion(*args); + for (const NotifyMotionArgs& newArgs : newMotions) { + mListener.notifyMotion(&newArgs); + } +} + +void UnwantedInteractionBlocker::notifySwitch(const NotifySwitchArgs* args) { + mListener.notifySwitch(args); +} + +void UnwantedInteractionBlocker::notifySensor(const NotifySensorArgs* args) { + mListener.notifySensor(args); +} + +void UnwantedInteractionBlocker::notifyVibratorState(const NotifyVibratorStateArgs* args) { + mListener.notifyVibratorState(args); +} +void UnwantedInteractionBlocker::notifyDeviceReset(const NotifyDeviceResetArgs* args) { + auto it = mPalmRejectors.find(args->deviceId); + if (it != mPalmRejectors.end()) { + AndroidPalmFilterDeviceInfo info = it->second.getPalmFilterDeviceInfo(); + // Re-create the object instead of resetting it + mPalmRejectors.erase(it); + mPalmRejectors.emplace(args->deviceId, info); + } + mListener.notifyDeviceReset(args); +} + +void UnwantedInteractionBlocker::notifyPointerCaptureChanged( + const NotifyPointerCaptureChangedArgs* args) { + mListener.notifyPointerCaptureChanged(args); +} + +void UnwantedInteractionBlocker::notifyInputDevicesChanged( + const std::vector<InputDeviceInfo>& inputDevices) { + if (!mEnablePalmRejection) { + // Palm rejection is disabled. Don't create any palm rejector objects. + return; + } + + // Let's see which of the existing devices didn't change, so that we can keep them + // and prevent event stream disruption + std::set<int32_t /*deviceId*/> devicesToKeep; + for (const InputDeviceInfo& device : inputDevices) { + std::optional<AndroidPalmFilterDeviceInfo> info = createPalmFilterDeviceInfo(device); + if (!info) { + continue; + } + + auto [it, emplaced] = mPalmRejectors.try_emplace(device.getId(), *info); + if (!emplaced && *info != it->second.getPalmFilterDeviceInfo()) { + // Re-create the PalmRejector because the device info has changed. + mPalmRejectors.erase(it); + mPalmRejectors.emplace(device.getId(), *info); + } + devicesToKeep.insert(device.getId()); + } + // Delete all devices that we don't need to keep + std::erase_if(mPalmRejectors, [&devicesToKeep](const auto& item) { + auto const& [deviceId, _] = item; + return devicesToKeep.find(deviceId) == devicesToKeep.end(); + }); +} + +void UnwantedInteractionBlocker::dump(std::string& dump) { + dump += "UnwantedInteractionBlocker:\n"; + dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection)); + dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n", + toString(isPalmRejectionEnabled())); + dump += mPalmRejectors.empty() ? " mPalmRejectors: None\n" : " mPalmRejectors:\n"; + for (const auto& [deviceId, palmRejector] : mPalmRejectors) { + dump += StringPrintf(" deviceId = %" PRId32 ":\n", deviceId); + dump += addPrefix(palmRejector.dump(), " "); + } +} + +void UnwantedInteractionBlocker::monitor() {} + +UnwantedInteractionBlocker::~UnwantedInteractionBlocker() {} + +void SlotState::update(const NotifyMotionArgs& args) { + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + const int32_t resolvedAction = resolveActionForPointer(i, args.action); + processPointerId(pointerId, resolvedAction); + } +} + +size_t SlotState::findUnusedSlot() const { + size_t unusedSlot = 0; + // Since the collection is ordered, we can rely on the in-order traversal + for (const auto& [slot, trackingId] : mPointerIdsBySlot) { + if (unusedSlot != slot) { + break; + } + unusedSlot++; + } + return unusedSlot; +} + +void SlotState::processPointerId(int pointerId, int32_t actionMasked) { + switch (MotionEvent::getActionMasked(actionMasked)) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_HOVER_ENTER: { + // New pointer going down + size_t newSlot = findUnusedSlot(); + mPointerIdsBySlot[newSlot] = pointerId; + mSlotsByPointerId[pointerId] = newSlot; + return; + } + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_HOVER_MOVE: { + return; + } + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_HOVER_EXIT: { + auto it = mSlotsByPointerId.find(pointerId); + LOG_ALWAYS_FATAL_IF(it == mSlotsByPointerId.end()); + size_t slot = it->second; + // Erase this pointer from both collections + mPointerIdsBySlot.erase(slot); + mSlotsByPointerId.erase(pointerId); + return; + } + } + LOG_ALWAYS_FATAL("Unhandled action : %s", MotionEvent::actionToString(actionMasked).c_str()); + return; +} + +std::optional<size_t> SlotState::getSlotForPointerId(int32_t pointerId) const { + auto it = mSlotsByPointerId.find(pointerId); + if (it == mSlotsByPointerId.end()) { + return std::nullopt; + } + return it->second; +} + +std::string SlotState::dump() const { + std::string out = "mSlotsByPointerId:\n"; + out += addPrefix(dumpMap(mSlotsByPointerId), " ") + "\n"; + out += "mPointerIdsBySlot:\n"; + out += addPrefix(dumpMap(mPointerIdsBySlot), " ") + "\n"; + return out; +} + +PalmRejector::PalmRejector(const AndroidPalmFilterDeviceInfo& info, + std::unique_ptr<::ui::PalmDetectionFilter> filter) + : mSharedPalmState(std::make_unique<::ui::SharedPalmDetectionFilterState>()), + mDeviceInfo(info), + mPalmDetectionFilter(std::move(filter)) { + if (mPalmDetectionFilter != nullptr) { + // This path is used for testing. Non-testing invocations should let this constructor + // create a real PalmDetectionFilter + return; + } + std::unique_ptr<::ui::NeuralStylusPalmDetectionFilterModel> model = + std::make_unique<::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel>( + std::vector<float>()); + mPalmDetectionFilter = + std::make_unique<::ui::NeuralStylusPalmDetectionFilter>(mDeviceInfo, std::move(model), + mSharedPalmState.get()); +} + +std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args, + const AndroidPalmFilterDeviceInfo& deviceInfo, + const SlotState& oldSlotState, + const SlotState& newSlotState) { + std::vector<::ui::InProgressTouchEvdev> touches; + + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + touches.emplace_back(::ui::InProgressTouchEvdev()); + touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); + touches.back().minor = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); + touches.back().tool_type = getLinuxToolType(args.pointerProperties[i].toolType); + + // Whether there is new information for the touch. + touches.back().altered = true; + + // Whether the touch was cancelled. Touch events should be ignored till a + // new touch is initiated. + touches.back().was_cancelled = false; + + // Whether the touch is going to be canceled. + touches.back().cancelled = false; + + // Whether the touch is delayed at first appearance. Will not be reported yet. + touches.back().delayed = false; + + // Whether the touch was delayed before. + touches.back().was_delayed = false; + + // Whether the touch is held until end or no longer held. + touches.back().held = false; + + // Whether this touch was held before being sent. + touches.back().was_held = false; + + const int32_t resolvedAction = resolveActionForPointer(i, args.action); + const bool isDown = resolvedAction == AMOTION_EVENT_ACTION_POINTER_DOWN || + resolvedAction == AMOTION_EVENT_ACTION_DOWN; + touches.back().was_touching = !isDown; + + const bool isUpOrCancel = resolvedAction == AMOTION_EVENT_ACTION_CANCEL || + resolvedAction == AMOTION_EVENT_ACTION_UP || + resolvedAction == AMOTION_EVENT_ACTION_POINTER_UP; + + touches.back().x = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X); + touches.back().y = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y); + + std::optional<size_t> slot = newSlotState.getSlotForPointerId(pointerId); + if (!slot) { + slot = oldSlotState.getSlotForPointerId(pointerId); + } + LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer %d", pointerId); + touches.back().slot = *slot; + touches.back().tracking_id = (!isUpOrCancel) ? pointerId : -1; + touches.back().touching = !isUpOrCancel; + + // The fields 'radius_x' and 'radius_x' are not used for palm rejection + touches.back().pressure = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); + touches.back().tool_code = BTN_TOOL_FINGER; + // The field 'orientation' is not used for palm rejection + // The fields 'tilt_x' and 'tilt_y' are not used for palm rejection + touches.back().reported_tool_type = ::ui::EventPointerType::kTouch; + touches.back().stylus_button = false; + } + return touches; +} + +std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs& args) { + if (mPalmDetectionFilter == nullptr) { + return {args}; + } + const bool skipThisEvent = args.action == AMOTION_EVENT_ACTION_HOVER_ENTER || + args.action == AMOTION_EVENT_ACTION_HOVER_MOVE || + args.action == AMOTION_EVENT_ACTION_HOVER_EXIT || + args.action == AMOTION_EVENT_ACTION_BUTTON_PRESS || + args.action == AMOTION_EVENT_ACTION_BUTTON_RELEASE || + args.action == AMOTION_EVENT_ACTION_SCROLL; + if (skipThisEvent) { + // Lets not process hover events, button events, or scroll for now. + return {args}; + } + if (args.action == AMOTION_EVENT_ACTION_DOWN) { + mSuppressedPointerIds.clear(); + } + std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold; + std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress; + + // Store the slot state before we call getTouches and update it. This way, we can find + // the slots that have been removed due to the incoming event. + SlotState oldSlotState = mSlotState; + mSlotState.update(args); + std::vector<::ui::InProgressTouchEvdev> touches = + getTouches(args, mDeviceInfo, oldSlotState, mSlotState); + ::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime); + + mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress); + + // Now that we know which slots should be suppressed, let's convert those to pointer id's. + std::set<int32_t> oldSuppressedIds; + std::swap(oldSuppressedIds, mSuppressedPointerIds); + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + std::optional<size_t> slot = oldSlotState.getSlotForPointerId(pointerId); + if (!slot) { + slot = mSlotState.getSlotForPointerId(pointerId); + LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId); + } + if (slotsToSuppress.test(*slot)) { + mSuppressedPointerIds.insert(pointerId); + } + } + + std::vector<NotifyMotionArgs> argsWithoutUnwantedPointers = + cancelSuppressedPointers(args, oldSuppressedIds, mSuppressedPointerIds); + for (const NotifyMotionArgs& checkArgs : argsWithoutUnwantedPointers) { + LOG_ALWAYS_FATAL_IF(checkArgs.action == ACTION_UNKNOWN, "%s", checkArgs.dump().c_str()); + } + + if (mSuppressedPointerIds != oldSuppressedIds) { + if (argsWithoutUnwantedPointers.size() != 1 || + argsWithoutUnwantedPointers[0].pointerCount != args.pointerCount) { + ALOGI("Palm detected, removing pointer ids %s from %s", + dumpSet(mSuppressedPointerIds).c_str(), args.dump().c_str()); + } + } + + return argsWithoutUnwantedPointers; +} + +const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() { + return mDeviceInfo; +} + +std::string PalmRejector::dump() const { + std::string out; + out += "mDeviceInfo:\n"; + out += addPrefix(dumpDeviceInfo(mDeviceInfo), " "); + out += "mSlotState:\n"; + out += addPrefix(mSlotState.dump(), " "); + out += "mSuppressedPointerIds: "; + out += dumpSet(mSuppressedPointerIds) + "\n"; + return out; +} + +} // namespace android diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h new file mode 100644 index 0000000000..14068fd878 --- /dev/null +++ b/services/inputflinger/UnwantedInteractionBlocker.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <map> +#include <set> + +#include "include/UnwantedInteractionBlockerInterface.h" +#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h" +#include "ui/events/ozone/evdev/touch_filter/palm_detection_filter.h" + +namespace android { + +// --- Functions for manipulation of event streams + +struct AndroidPalmFilterDeviceInfo : ::ui::PalmFilterDeviceInfo { + // Additional fields from 'TouchEventConverterEvdev', added here for convenience + int32_t touch_major_res = 1; // info.GetAbsInfoByCode(ABS_MT_TOUCH_MAJOR).resolution; + int32_t touch_minor_res = 1; // info.GetAbsInfoByCode(ABS_MT_TOUCH_MINOR).resolution; + + auto operator<=>(const AndroidPalmFilterDeviceInfo&) const = default; +}; + +std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo( + const InputDeviceInfo& deviceInfo); + +static constexpr int32_t ACTION_UNKNOWN = -1; + +NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, + const std::set<int32_t>& pointerIds); + +std::vector<NotifyMotionArgs> cancelSuppressedPointers( + const NotifyMotionArgs& args, const std::set<int32_t>& oldSuppressedPointerIds, + const std::set<int32_t>& newSuppressedPointerIds); + +std::string toString(const ::ui::InProgressTouchEvdev& touch); + +// --- Main classes and interfaces --- + +class PalmRejector; + +// --- Implementations --- + +/** + * Implementation of the UnwantedInteractionBlockerInterface. + * Represents a separate stage of input processing. All of the input events go through this stage. + * Acts as a passthrough for all input events except for motion events. + * + * The events of motion type are sent to PalmRejectors. PalmRejectors detect unwanted touches, + * and emit input streams with the bad pointers removed. + */ +class UnwantedInteractionBlocker : public UnwantedInteractionBlockerInterface { +public: + explicit UnwantedInteractionBlocker(InputListenerInterface& listener); + explicit UnwantedInteractionBlocker(InputListenerInterface& listener, bool enablePalmRejection); + + void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; + void notifyKey(const NotifyKeyArgs* args) override; + void notifyMotion(const NotifyMotionArgs* args) override; + void notifySwitch(const NotifySwitchArgs* args) override; + void notifySensor(const NotifySensorArgs* args) override; + void notifyVibratorState(const NotifyVibratorStateArgs* args) override; + void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; + + void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override; + void dump(std::string& dump) override; + void monitor() override; + + ~UnwantedInteractionBlocker(); + +private: + // The next stage to pass input events to + InputListenerInterface& mListener; + const bool mEnablePalmRejection; + + // Detect and reject unwanted palms on screen + // Use a separate palm rejector for every touch device. + std::map<int32_t /*deviceId*/, PalmRejector> mPalmRejectors; +}; + +class SlotState { +public: + /** + * Update the state using the new information provided in the NotifyMotionArgs + */ + void update(const NotifyMotionArgs& args); + std::optional<size_t> getSlotForPointerId(int32_t pointerId) const; + std::string dump() const; + +private: + // Process a pointer with the provided action, and return the slot associated with it + void processPointerId(int32_t pointerId, int32_t action); + // The map from tracking id to slot state. Since the PalmRejectionFilter works close to the + // evdev level, the only way to tell it about UP or CANCEL events is by sending tracking id = -1 + // to the appropriate touch slot. So we need to reconstruct the original slot. + // The two collections below must always be in-sync. + // Use std::map instead of std::unordered_map because we rely on these collections being + // ordered. It also has better space efficiency than unordered_map because we only have a few + // pointers most of the time. + std::map<int32_t /*pointerId*/, size_t /*slot*/> mSlotsByPointerId; + std::map<size_t /*slot*/, int32_t /*pointerId */> mPointerIdsBySlot; + + size_t findUnusedSlot() const; +}; + +/** + * Convert an Android event to a linux-like 'InProgressTouchEvdev'. The provided SlotState's + * are used to figure out which slot does each pointer belong to. + */ +std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args, + const AndroidPalmFilterDeviceInfo& deviceInfo, + const SlotState& oldSlotState, + const SlotState& newSlotState); + +class PalmRejector { +public: + explicit PalmRejector(const AndroidPalmFilterDeviceInfo& info, + std::unique_ptr<::ui::PalmDetectionFilter> filter = nullptr); + std::vector<NotifyMotionArgs> processMotion(const NotifyMotionArgs& args); + + // Get the device info of this device, for comparison purposes + const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo(); + std::string dump() const; + +private: + PalmRejector(const PalmRejector&) = delete; + PalmRejector& operator=(const PalmRejector&) = delete; + + std::unique_ptr<::ui::SharedPalmDetectionFilterState> mSharedPalmState; + AndroidPalmFilterDeviceInfo mDeviceInfo; + std::unique_ptr<::ui::PalmDetectionFilter> mPalmDetectionFilter; + std::set<int32_t> mSuppressedPointerIds; + + // Used to help convert an Android touch stream to Linux input stream. + SlotState mSlotState; +}; + +} // namespace android diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index d1982fc53c..6c321bcedd 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -137,7 +137,7 @@ constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms; // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; -// Event log tags. See EventLogTags.logtags for reference +// Event log tags. See EventLogTags.logtags for reference. constexpr int LOGTAG_INPUT_INTERACTION = 62000; constexpr int LOGTAG_INPUT_FOCUS = 62001; constexpr int LOGTAG_INPUT_CANCEL = 62003; @@ -417,20 +417,6 @@ KeyEvent createKeyEvent(const KeyEntry& entry) { return event; } -std::optional<int32_t> findMonitorPidByToken( - const std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay, - const sp<IBinder>& token) { - for (const auto& it : monitorsByDisplay) { - const std::vector<Monitor>& monitors = it.second; - for (const Monitor& monitor : monitors) { - if (monitor.inputChannel->getConnectionToken() == token) { - return monitor.pid; - } - } - } - return std::nullopt; -} - bool shouldReportMetricsForConnection(const Connection& connection) { // Do not keep track of gesture monitors. They receive every event and would disproportionately // affect the statistics. @@ -548,6 +534,7 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), mNextUnblockedEvent(nullptr), + mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT), mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), @@ -707,8 +694,13 @@ nsecs_t InputDispatcher::processAnrsLocked() { return LONG_LONG_MIN; } -std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(const sp<IBinder>& token) { - sp<WindowInfoHandle> window = getWindowHandleLocked(token); +std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked( + const sp<Connection>& connection) { + if (connection->monitor) { + return mMonitorDispatchingTimeout; + } + const sp<WindowInfoHandle> window = + getWindowHandleLocked(connection->inputChannel->getConnectionToken()); if (window != nullptr) { return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } @@ -942,15 +934,16 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt return true; } - // Alternatively, maybe there's a gesture monitor that could handle this event - for (const auto& monitor : getValueByKey(mGestureMonitorsByDisplay, displayId)) { - sp<Connection> connection = - getConnectionLocked(monitor.inputChannel->getConnectionToken()); + // Alternatively, maybe there's a spy window that could handle this event. + const std::vector<sp<WindowInfoHandle>> touchedSpies = + findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus); + for (const auto& windowHandle : touchedSpies) { + const sp<Connection> connection = getConnectionLocked(windowHandle->getToken()); if (connection != nullptr && connection->responsive) { - // This monitor could take more input. Drop all events preceding this - // event, so that gesture monitor could get a chance to receive the stream + // This spy window could take more input. Drop all events preceding this + // event, so that the spy window can get a chance to receive the stream. ALOGW("Pruning the input queue because %s is unresponsive, but we have a " - "responsive gesture monitor that may handle the event", + "responsive spy window that may handle the event.", mAwaitedFocusedApplication->getName().c_str()); return true; } @@ -1994,7 +1987,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( TouchState tempTouchState; if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) { oldState = &(it->second); - tempTouchState.copyFrom(*oldState); + tempTouchState = *oldState; } bool isSplit = tempTouchState.split; @@ -2098,6 +2091,13 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle); } + if (newTouchedWindows.empty()) { + ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.", + x, y, displayId); + injectionResult = InputEventInjectionResult::FAILED; + goto Failed; + } + for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) { const WindowInfo& info = *windowHandle->getInfo(); @@ -2166,23 +2166,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds); } - - const std::vector<Monitor> newGestureMonitors = isDown - ? selectResponsiveMonitorsLocked( - getValueByKey(mGestureMonitorsByDisplay, displayId)) - : std::vector<Monitor>{}; - - if (newTouchedWindows.empty() && newGestureMonitors.empty() && - tempTouchState.gestureMonitors.empty()) { - ALOGI("Dropping event because there is no touchable window or gesture monitor at " - "(%d, %d) in display %" PRId32 ".", - x, y, displayId); - injectionResult = InputEventInjectionResult::FAILED; - goto Failed; - } - - tempTouchState.addGestureMonitors(newGestureMonitors); - } else { /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ @@ -2284,39 +2267,33 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( } } - // Check permission to inject into all touched foreground windows and ensure there - // is at least one touched foreground window. - { - bool haveForegroundOrSpyWindow = false; - for (const TouchedWindow& touchedWindow : tempTouchState.windows) { - const bool isForeground = - (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0; - if (touchedWindow.windowHandle->getInfo()->isSpy()) { - haveForegroundOrSpyWindow = true; - LOG_ALWAYS_FATAL_IF(isForeground, - "Spy window cannot be dispatched as a foreground window."); - } - if (isForeground) { - haveForegroundOrSpyWindow = true; - if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) { - injectionResult = InputEventInjectionResult::PERMISSION_DENIED; - injectionPermission = INJECTION_PERMISSION_DENIED; - goto Failed; - } - } - } - bool hasGestureMonitor = !tempTouchState.gestureMonitors.empty(); - if (!haveForegroundOrSpyWindow && !hasGestureMonitor) { - ALOGI("Dropping event because there is no touched window in display " - "%" PRId32 " or gesture monitor to receive it.", - displayId); - injectionResult = InputEventInjectionResult::FAILED; - goto Failed; - } + // Ensure that we have at least one foreground or spy window. It's possible that we dropped some + // of the touched windows we previously found if they became paused or unresponsive or were + // removed. + if (std::none_of(tempTouchState.windows.begin(), tempTouchState.windows.end(), + [](const TouchedWindow& touchedWindow) { + return (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0 || + touchedWindow.windowHandle->getInfo()->isSpy(); + })) { + ALOGI("Dropping event because there is no touched window on display %d to receive it.", + displayId); + injectionResult = InputEventInjectionResult::FAILED; + goto Failed; + } - // Permission granted to injection into all touched foreground windows. - injectionPermission = INJECTION_PERMISSION_GRANTED; + // Check permission to inject into all touched foreground windows. + if (std::any_of(tempTouchState.windows.begin(), tempTouchState.windows.end(), + [this, &entry](const TouchedWindow& touchedWindow) { + return (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0 && + !checkInjectionPermission(touchedWindow.windowHandle, + entry.injectionState); + })) { + injectionResult = InputEventInjectionResult::PERMISSION_DENIED; + injectionPermission = INJECTION_PERMISSION_DENIED; + goto Failed; } + // Permission granted to inject into all touched foreground windows. + injectionPermission = INJECTION_PERMISSION_GRANTED; // Check whether windows listening for outside touches are owned by the same UID. If it is // set the policy flag that we will not reveal coordinate information to this window. @@ -2374,10 +2351,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( touchedWindow.pointerIds, inputTargets); } - for (const auto& monitor : tempTouchState.gestureMonitors) { - addMonitoringTargetLocked(monitor, displayId, inputTargets); - } - // Drop the outside or hover touch windows since we will not care about them // in the next iteration. tempTouchState.filterNonAsIsTouchWindows(); @@ -2578,32 +2551,21 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId) { - std::unordered_map<int32_t, std::vector<Monitor>>::const_iterator it = - mGlobalMonitorsByDisplay.find(displayId); + auto monitorsIt = mGlobalMonitorsByDisplay.find(displayId); + if (monitorsIt == mGlobalMonitorsByDisplay.end()) return; - if (it != mGlobalMonitorsByDisplay.end()) { - const std::vector<Monitor>& monitors = it->second; - for (const Monitor& monitor : monitors) { - addMonitoringTargetLocked(monitor, displayId, inputTargets); + for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) { + InputTarget target; + target.inputChannel = monitor.inputChannel; + target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { + target.displayTransform = it->second.transform; } + target.setDefaultPointerTransform(target.displayTransform); + inputTargets.push_back(target); } } -void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, int32_t displayId, - std::vector<InputTarget>& inputTargets) { - InputTarget target; - target.inputChannel = monitor.inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - ui::Transform t; - if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { - const auto& displayTransform = it->second.transform; - target.displayTransform = displayTransform; - t = displayTransform; - } - target.setDefaultPointerTransform(t); - inputTargets.push_back(target); -} - bool InputDispatcher::checkInjectionPermission(const sp<WindowInfoHandle>& windowHandle, const InjectionState* injectionState) { if (injectionState && @@ -3205,8 +3167,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) { DispatchEntry* dispatchEntry = connection->outboundQueue.front(); dispatchEntry->deliveryTime = currentTime; - const std::chrono::nanoseconds timeout = - getDispatchingTimeoutLocked(connection->inputChannel->getConnectionToken()); + const std::chrono::nanoseconds timeout = getDispatchingTimeoutLocked(connection); dispatchEntry->timeoutTime = currentTime + timeout.count(); // Publish the event. @@ -3568,15 +3529,7 @@ void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( const CancelationOptions& options) { - synthesizeCancelationEventsForMonitorsLocked(options, mGlobalMonitorsByDisplay); - synthesizeCancelationEventsForMonitorsLocked(options, mGestureMonitorsByDisplay); -} - -void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( - const CancelationOptions& options, - std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) { - for (const auto& it : monitorsByDisplay) { - const std::vector<Monitor>& monitors = it.second; + for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { for (const Monitor& monitor : monitors) { synthesizeCancelationEventsForInputChannelLocked(monitor.inputChannel, options); } @@ -4950,23 +4903,42 @@ void InputDispatcher::setInputFilterEnabled(bool enabled) { mLooper->wake(); } -void InputDispatcher::setInTouchMode(bool inTouchMode) { +bool InputDispatcher::setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, + bool hasPermission) { bool needWake = false; { std::scoped_lock lock(mLock); if (mInTouchMode == inTouchMode) { - return; + return false; } if (DEBUG_TOUCH_MODE) { - ALOGD("Request to change touch mode from %s to %s", toString(mInTouchMode), - toString(inTouchMode)); - // TODO(b/198487159): Also print the current last interacted apps. + ALOGD("Request to change touch mode from %s to %s (calling pid=%d, uid=%d, " + "hasPermission=%s)", + toString(mInTouchMode), toString(inTouchMode), pid, uid, toString(hasPermission)); + } + if (!hasPermission) { + const sp<IBinder> focusedToken = + mFocusResolver.getFocusedWindowToken(mFocusedDisplayId); + + // TODO(b/198487159): if no window is currently focused, then we need to check the last + // interacted window (within 1 second timeout). We should allow touch mode change + // if the last interacted window owner's pid/uid match the calling ones. + if (focusedToken == nullptr) { + return false; + } + const sp<WindowInfoHandle> windowHandle = getWindowHandleLocked(focusedToken); + if (windowHandle == nullptr) { + return false; + } + const WindowInfo* windowInfo = windowHandle->getInfo(); + if (pid != windowInfo->ownerPid || uid != windowInfo->ownerUid) { + return false; + } } // TODO(b/198499018): Store touch mode per display. mInTouchMode = inTouchMode; - // TODO(b/198487159): Enforce that only last interacted apps can change touch mode. auto entry = std::make_unique<TouchModeEntry>(mIdGenerator.nextId(), now(), inTouchMode); needWake = enqueueInboundEventLocked(std::move(entry)); } // release lock @@ -4974,6 +4946,7 @@ void InputDispatcher::setInTouchMode(bool inTouchMode) { if (needWake) { mLooper->wake(); } + return true; } void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) { @@ -5261,22 +5234,16 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "Displays: <none>\n"; } - if (!mGlobalMonitorsByDisplay.empty() || !mGestureMonitorsByDisplay.empty()) { - 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) { - const std::vector<Monitor>& monitors = it.second; - dump += StringPrintf(INDENT "Gesture monitors in display %" PRId32 ":\n", it.first); + if (!mGlobalMonitorsByDisplay.empty()) { + for (const auto& [displayId, monitors] : mGlobalMonitorsByDisplay) { + dump += StringPrintf(INDENT "Global monitors on display %d:\n", displayId); dumpMonitors(dump, monitors); } } else { - dump += INDENT "Monitors: <none>\n"; + dump += INDENT "Global Monitors: <none>\n"; } - nsecs_t currentTime = now(); + const nsecs_t currentTime = now(); // Dump recently dispatched or dropped events from oldest to newest. if (!mRecentQueue.empty()) { @@ -5434,7 +5401,6 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const } Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_t displayId, - bool isGestureMonitor, const std::string& name, int32_t pid) { std::shared_ptr<InputChannel> serverChannel; @@ -5463,13 +5429,9 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_ std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, this, std::placeholders::_1, token); - auto& monitorsByDisplay = - isGestureMonitor ? mGestureMonitorsByDisplay : mGlobalMonitorsByDisplay; - monitorsByDisplay[displayId].emplace_back(serverChannel, pid); + mGlobalMonitorsByDisplay[displayId].emplace_back(serverChannel, pid); mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, new LooperEventCallback(callback), nullptr); - ALOGI("Created monitor %s for display %" PRId32 ", gesture=%s, pid=%" PRId32, name.c_str(), - displayId, toString(isGestureMonitor), pid); } // Wake the looper because some connections have changed. @@ -5517,26 +5479,14 @@ status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connection } void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionToken) { - removeMonitorChannelLocked(connectionToken, mGlobalMonitorsByDisplay); - removeMonitorChannelLocked(connectionToken, mGestureMonitorsByDisplay); -} - -void InputDispatcher::removeMonitorChannelLocked( - const sp<IBinder>& connectionToken, - std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) { - 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->getConnectionToken() == connectionToken) { - ALOGI("Erasing monitor %s on display %" PRId32 ", pid=%" PRId32, - monitors[i].inputChannel->getName().c_str(), it->first, monitors[i].pid); - monitors.erase(monitors.begin() + i); - break; - } - } + for (auto it = mGlobalMonitorsByDisplay.begin(); it != mGlobalMonitorsByDisplay.end();) { + auto& [displayId, monitors] = *it; + std::erase_if(monitors, [connectionToken](const Monitor& monitor) { + return monitor.inputChannel->getConnectionToken() == connectionToken; + }); + if (monitors.empty()) { - it = monitorsByDisplay.erase(it); + it = mGlobalMonitorsByDisplay.erase(it); } else { ++it; } @@ -5544,81 +5494,45 @@ void InputDispatcher::removeMonitorChannelLocked( } status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { - { // acquire lock - std::scoped_lock _l(mLock); + std::scoped_lock _l(mLock); - TouchState* statePtr = nullptr; - std::shared_ptr<InputChannel> requestingChannel; - int32_t displayId; - int32_t deviceId; - const std::optional<int32_t> foundGestureMonitorDisplayId = - findGestureMonitorDisplayByTokenLocked(token); - - // TODO: Optimize this function for pilfering from windows when removing gesture monitors. - if (foundGestureMonitorDisplayId) { - // A gesture monitor has requested to pilfer pointers. - displayId = *foundGestureMonitorDisplayId; - auto stateIt = mTouchStatesByDisplay.find(displayId); - if (stateIt == mTouchStatesByDisplay.end()) { - ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId); - return BAD_VALUE; - } - statePtr = &stateIt->second; + const std::shared_ptr<InputChannel> requestingChannel = getInputChannelLocked(token); + if (!requestingChannel) { + ALOGW("Attempted to pilfer pointers from an un-registered channel or invalid token"); + return BAD_VALUE; + } - for (const auto& monitor : statePtr->gestureMonitors) { - if (monitor.inputChannel->getConnectionToken() == token) { - requestingChannel = monitor.inputChannel; - deviceId = statePtr->deviceId; - } - } - } else { - // Check if a window has requested to pilfer pointers. - for (auto& [curDisplayId, state] : mTouchStatesByDisplay) { - const sp<WindowInfoHandle>& windowHandle = state.getWindow(token); - if (windowHandle != nullptr) { - displayId = curDisplayId; - requestingChannel = getInputChannelLocked(token); - deviceId = state.deviceId; - statePtr = &state; - break; - } - } - } + auto [statePtr, windowPtr] = findTouchStateAndWindowLocked(token); + if (statePtr == nullptr || windowPtr == nullptr || !statePtr->down) { + ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams." + " Ignoring."); + return BAD_VALUE; + } - if (requestingChannel == nullptr) { - ALOGW("Attempted to pilfer pointers from an un-registered channel or invalid token"); - return BAD_VALUE; - } - TouchState& state = *statePtr; - if (!state.down) { - ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams." - " Ignoring."); - return BAD_VALUE; - } + TouchState& state = *statePtr; - // Send cancel events to all the input channels we're stealing from. - CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, - "input channel stole pointer stream"); - options.deviceId = deviceId; - options.displayId = displayId; - std::string canceledWindows; - for (const TouchedWindow& window : state.windows) { - std::shared_ptr<InputChannel> channel = - getInputChannelLocked(window.windowHandle->getToken()); - if (channel != nullptr && channel->getConnectionToken() != token) { - synthesizeCancelationEventsForInputChannelLocked(channel, options); - canceledWindows += canceledWindows.empty() ? "[" : ", "; - canceledWindows += channel->getName(); - } + // Send cancel events to all the input channels we're stealing from. + CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, + "input channel stole pointer stream"); + options.deviceId = state.deviceId; + options.displayId = state.displayId; + std::string canceledWindows; + for (const TouchedWindow& window : state.windows) { + const std::shared_ptr<InputChannel> channel = + getInputChannelLocked(window.windowHandle->getToken()); + if (channel != nullptr && channel->getConnectionToken() != token) { + synthesizeCancelationEventsForInputChannelLocked(channel, options); + canceledWindows += canceledWindows.empty() ? "[" : ", "; + canceledWindows += channel->getName(); } - canceledWindows += canceledWindows.empty() ? "[]" : "]"; - ALOGI("Channel %s is stealing touch from %s", requestingChannel->getName().c_str(), - canceledWindows.c_str()); - - // Then clear the current touch state so we stop dispatching to them as well. - state.split = false; - state.filterWindowsExcept(token); } + canceledWindows += canceledWindows.empty() ? "[]" : "]"; + ALOGI("Channel %s is stealing touch from %s", requestingChannel->getName().c_str(), + canceledWindows.c_str()); + + // Prevent the gesture from being sent to any other windows. + state.filterWindowsExcept(token); + state.preventNewTargets = true; return OK; } @@ -5672,27 +5586,17 @@ void InputDispatcher::setDisplayEligibilityForPointerCapture(int32_t displayId, } // release lock } -std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked( - const sp<IBinder>& token) { - for (const auto& it : mGestureMonitorsByDisplay) { - const std::vector<Monitor>& monitors = it.second; +std::optional<int32_t> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) { + for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { for (const Monitor& monitor : monitors) { if (monitor.inputChannel->getConnectionToken() == token) { - return it.first; + return monitor.pid; } } } return std::nullopt; } -std::optional<int32_t> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) { - std::optional<int32_t> gesturePid = findMonitorPidByToken(mGestureMonitorsByDisplay, token); - if (gesturePid.has_value()) { - return gesturePid; - } - return findMonitorPidByToken(mGlobalMonitorsByDisplay, token); -} - sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const { if (inputConnectionToken == nullptr) { return nullptr; @@ -6400,4 +6304,9 @@ void InputDispatcher::cancelCurrentTouch() { mLooper->wake(); } +void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout) { + std::scoped_lock _l(mLock); + mMonitorDispatchingTimeout = timeout; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 756483904e..e162c78b81 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -117,7 +117,7 @@ public: void setFocusedDisplay(int32_t displayId) override; void setInputDispatchMode(bool enabled, bool frozen) override; void setInputFilterEnabled(bool enabled) override; - void setInTouchMode(bool inTouchMode) override; + bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) override; void setMaximumObscuringOpacityForTouch(float opacity) override; void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override; @@ -129,7 +129,6 @@ public: const std::string& name) override; void setFocusedWindow(const android::gui::FocusRequest&) override; base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId, - bool isGestureMonitor, const std::string& name, int32_t pid) override; status_t removeInputChannel(const sp<IBinder>& connectionToken) override; @@ -148,6 +147,9 @@ public: void cancelCurrentTouch() override; + // Public to allow tests to verify that a Monitor can get ANR. + void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout); + private: enum class DropReason { NOT_DROPPED, @@ -257,21 +259,12 @@ private: std::unordered_map<sp<IBinder>, sp<Connection>, StrongPointerHash<IBinder>> mConnectionsByToken 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); // Find a monitor pid by the provided token. std::optional<int32_t> findMonitorPidByTokenLocked(const sp<IBinder>& token) 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); - const HmacKeyManager mHmacKeyManager; const std::array<uint8_t, 32> getSignature(const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const; @@ -324,8 +317,12 @@ private: bool runCommandsLockedInterruptable() REQUIRES(mLock); void postCommandLocked(Command&& command) REQUIRES(mLock); + // The dispatching timeout to use for Monitors. + std::chrono::nanoseconds mMonitorDispatchingTimeout GUARDED_BY(mLock); + nsecs_t processAnrsLocked() REQUIRES(mLock); - std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<IBinder>& token) REQUIRES(mLock); + std::chrono::nanoseconds getDispatchingTimeoutLocked(const sp<Connection>& connection) + REQUIRES(mLock); // Input filter processing. bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); @@ -523,7 +520,6 @@ private: sp<android::gui::WindowInfoHandle> mLastHoverWindowHandle GUARDED_BY(mLock); void cancelEventsForAnrLocked(const sp<Connection>& connection) REQUIRES(mLock); - nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); // If a focused application changes, we should stop counting down the "no focused window" time, // because we will have no way of knowing when the previous application actually added a window. // This also means that we will miss cases like pulling down notification shade when the @@ -544,8 +540,6 @@ private: void addWindowTargetLocked(const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds, std::vector<InputTarget>& inputTargets) REQUIRES(mLock); - void addMonitoringTargetLocked(const Monitor& monitor, int32_t displayId, - std::vector<InputTarget>& inputTargets) REQUIRES(mLock); void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId) REQUIRES(mLock); void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock); @@ -611,9 +605,6 @@ private: 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 std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) REQUIRES(mLock); @@ -639,9 +630,6 @@ private: // Registration. void removeMonitorChannelLocked(const sp<IBinder>& connectionToken) REQUIRES(mLock); - void removeMonitorChannelLocked( - const sp<IBinder>& connectionToken, - std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay) REQUIRES(mLock); status_t removeInputChannelLocked(const sp<IBinder>& connectionToken, bool notify) REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 08c7826045..b63fe104fa 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -25,29 +25,8 @@ using android::gui::WindowInfoHandle; 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(); - 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; - gestureMonitors = other.gestureMonitors; + *this = TouchState(); } void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int32_t targetFlags, @@ -68,6 +47,8 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int } } + if (preventNewTargets) return; // Don't add new TouchedWindows. + TouchedWindow touchedWindow; touchedWindow.windowHandle = windowHandle; touchedWindow.targetFlags = targetFlags; @@ -75,13 +56,6 @@ void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle, int windows.push_back(touchedWindow); } -void TouchState::addGestureMonitors(const std::vector<Monitor>& 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::removeWindowByToken(const sp<IBinder>& token) { for (size_t i = 0; i < windows.size(); i++) { if (windows[i].windowHandle->getToken() == token) { diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 83ca90197b..9efb2808c5 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -29,22 +29,26 @@ class WindowInfoHandle; 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 + bool down = false; + bool split = false; + bool preventNewTargets = false; + + // id of the device that is currently down, others are rejected + int32_t deviceId = -1; + // source of the device that is current down, others are rejected + uint32_t source = 0; + // id to the display that currently has a touch, others are rejected + int32_t displayId = ADISPLAY_ID_NONE; + std::vector<TouchedWindow> windows; - std::vector<Monitor> gestureMonitors; + TouchState() = default; + ~TouchState() = default; + TouchState& operator=(const TouchState&) = default; - TouchState(); - ~TouchState(); void reset(); - void copyFrom(const TouchState& other); void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t targetFlags, BitSet32 pointerIds); - void addGestureMonitors(const std::vector<Monitor>& monitors); void removeWindowByToken(const sp<IBinder>& token); void filterNonAsIsTouchWindows(); void filterWindowsExcept(const sp<IBinder>& token); diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 16a6f16555..67e1b6f93b 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -123,8 +123,10 @@ public: * Touch mode is a global state that apps may enter / exit based on specific * user interactions with input devices. * If true, the device is in touch mode. + * + * Returns true when changing touch mode state. */ - virtual void setInTouchMode(bool inTouchMode) = 0; + virtual bool setInTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) = 0; /** * Sets the maximum allowed obscuring opacity by UID to propagate touches. @@ -169,16 +171,14 @@ public: const std::string& name) = 0; /** - * Creates an input channel to be used to monitor input events. + * Creates an input channel to be used to monitor all input events on a display. * * 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. + * display. * * This method may be called on any thread (usually by the input manager). */ virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId, - bool gestureMonitor, const std::string& name, int32_t pid) = 0; diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index db6310478c..dff58949a7 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -142,6 +142,8 @@ struct NotifyMotionArgs : public NotifyArgs { bool operator==(const NotifyMotionArgs& rhs) const; void notify(InputListenerInterface& listener) const override; + + std::string dump() const; }; /* Describes a sensor event. */ diff --git a/services/inputflinger/include/UnwantedInteractionBlockerInterface.h b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h new file mode 100644 index 0000000000..2327266563 --- /dev/null +++ b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "InputListener.h" + +namespace android { + +/** + * Base interface for an InputListener stage. + * Blocks unintentional input events. Not thread safe. Must be called from the same + * thread. All work is performed on the calling threads. + */ +class UnwantedInteractionBlockerInterface : public InputListenerInterface { +public: + /* Notifies the input reader policy that some input devices have changed + * and provides information about all current input devices. + * Important! This call should happen on the same thread as the calls to the + * InputListenerInterface methods. + * That is, same thread should call 'notifyMotion' and 'notifyInputDevicesChanged' and + * 'notifyDeviceReset'. If this architecture changes, we will need to make the implementation + * of this interface thread-safe. + */ + virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) = 0; + + /** + * Dump the state of the interaction blocker. + * 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; + + UnwantedInteractionBlockerInterface() {} + ~UnwantedInteractionBlockerInterface() override {} +}; + +} // namespace android diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index d19dbaf009..9d200bdeff 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -49,6 +49,7 @@ cc_test { "LatencyTracker_test.cpp", "TestInputListener.cpp", "UinputDevice.cpp", + "UnwantedInteractionBlocker_test.cpp", ], aidl: { include_dirs: [ @@ -58,7 +59,6 @@ cc_test { }, static_libs: [ "libc++fs", - "libinput", ], require_root: true, test_suites: ["device-tests"], diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index aa2f8326c8..872882e5d8 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -17,6 +17,7 @@ #include "../dispatcher/InputDispatcher.h" #include <android-base/properties.h> +#include <android-base/silent_death_test.h> #include <android-base/stringprintf.h> #include <android-base/thread_annotations.h> #include <binder/Binder.h> @@ -1126,6 +1127,16 @@ public: expectedFlags); } + void consumeMotionOutsideWithZeroedCoords(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT, + int32_t expectedFlags = 0) { + InputEvent* event = consume(); + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); + const MotionEvent& motionEvent = static_cast<MotionEvent&>(*event); + EXPECT_EQ(AMOTION_EVENT_ACTION_OUTSIDE, motionEvent.getActionMasked()); + EXPECT_EQ(0.f, motionEvent.getRawPointerCoords(0)->getX()); + EXPECT_EQ(0.f, motionEvent.getRawPointerCoords(0)->getY()); + } + void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) { ASSERT_NE(mInputReceiver, nullptr) << "Cannot consume events from a window with no receiver"; @@ -2766,9 +2777,9 @@ TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) { class FakeMonitorReceiver { public: FakeMonitorReceiver(const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name, - int32_t displayId, bool isGestureMonitor = false) { + int32_t displayId) { base::Result<std::unique_ptr<InputChannel>> channel = - dispatcher->createInputMonitor(displayId, isGestureMonitor, name, MONITOR_PID); + dispatcher->createInputMonitor(displayId, name, MONITOR_PID); mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name); } @@ -2829,6 +2840,8 @@ private: std::unique_ptr<FakeInputReceiver> mInputReceiver; }; +using InputDispatcherMonitorTest = InputDispatcherTest; + /** * Two entities that receive touch: A window, and a global monitor. * The touch goes to the window, and then the window disappears. @@ -2837,14 +2850,12 @@ private: * 1. foregroundWindow * 2. monitor <-- global monitor (doesn't observe z order, receives all events) */ -TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_GlobalMonitorTouchIsCanceled) { +TEST_F(InputDispatcherMonitorTest, MonitorTouchIsCanceledWhenForegroundWindowDisappears) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); - FakeMonitorReceiver monitor = - FakeMonitorReceiver(mDispatcher, "GlobalMonitor", ADISPLAY_ID_DEFAULT, - false /*isGestureMonitor*/); + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -2880,15 +2891,13 @@ TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_GlobalMonitorTouchIsC monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT); } -// Tests for gesture monitors -TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { +TEST_F(InputDispatcherMonitorTest, ReceivesMotionEvents) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) @@ -2897,71 +2906,34 @@ TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); } -TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) { - std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - - mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - window->setFocusable(true); - - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - setFocusedWindow(window); - - window->consumeFocusEvent(true); - - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); - - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)) - << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeKeyDown(ADISPLAY_ID_DEFAULT); - monitor.assertNoEvents(); -} +TEST_F(InputDispatcherMonitorTest, MonitorCannotPilferPointers) { + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); -TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionDown(ADISPLAY_ID_DEFAULT); monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); - window->releaseChannel(); - - mDispatcher->pilferPointers(monitor.getToken()); + // Pilfer pointers from the monitor. + // This should not do anything and the window should continue to receive events. + EXPECT_NE(OK, mDispatcher->pilferPointers(monitor.getToken())); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); -} - -TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) { - FakeMonitorReceiver monitor = - FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)); - std::optional<uint32_t> consumeSeq = monitor.receiveEvent(); - ASSERT_TRUE(consumeSeq); - - mFakePolicy->assertNotifyMonitorUnresponsiveWasCalled(DISPATCHING_TIMEOUT); - monitor.finishEvent(*consumeSeq); - ASSERT_TRUE(mDispatcher->waitForIdle()); - mFakePolicy->assertNotifyMonitorResponsiveWasCalled(); + monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT); + window->consumeMotionMove(ADISPLAY_ID_DEFAULT); } -// Tests for gesture monitors -TEST_F(InputDispatcherTest, GestureMonitor_NoWindowTransform) { +TEST_F(InputDispatcherMonitorTest, NoWindowTransform) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); @@ -2969,8 +2941,7 @@ TEST_F(InputDispatcherTest, GestureMonitor_NoWindowTransform) { window->setWindowOffset(20, 40); window->setWindowTransform(0, 1, -1, 0); - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) @@ -2981,15 +2952,14 @@ TEST_F(InputDispatcherTest, GestureMonitor_NoWindowTransform) { ASSERT_EQ(ui::Transform(), event->getTransform()); } -TEST_F(InputDispatcherTest, GestureMonitor_NoWindow) { +TEST_F(InputDispatcherMonitorTest, InjectionFailsWithNoWindow) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + ASSERT_EQ(InputEventInjectionResult::FAILED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + << "Injection should fail if there is a monitor, but no touchable window"; + monitor.assertNoEvents(); } TEST_F(InputDispatcherTest, TestMoveEvent) { @@ -3018,91 +2988,6 @@ TEST_F(InputDispatcherTest, TestMoveEvent) { 0 /*expectedFlags*/); } -TEST_F(InputDispatcherTest, GestureMonitor_SplitIfNoWindowTouched) { - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); - - std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - // Create a non touch modal window that supports split touch - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - window->setFrame(Rect(0, 0, 100, 100)); - window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - - // First finger down, no window touched. - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - {100, 200})) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); - window->assertNoEvents(); - - // Second finger down on window, the window should receive touch down. - const MotionEvent secondFingerDownEvent = - MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | - (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - AINPUT_SOURCE_TOUCHSCREEN) - .displayId(ADISPLAY_ID_DEFAULT) - .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) - .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) - .x(100) - .y(200)) - .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) - .build(); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, - InputEventInjectionSync::WAIT_FOR_RESULT)) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - - window->consumeMotionDown(ADISPLAY_ID_DEFAULT); - monitor.consumeMotionPointerDown(1 /* pointerIndex */); -} - -TEST_F(InputDispatcherTest, GestureMonitor_NoSplitAfterPilfer) { - FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); - - std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - // Create a non touch modal window that supports split touch - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); - window->setFrame(Rect(0, 0, 100, 100)); - window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - - // First finger down, no window touched. - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - {100, 200})) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); - window->assertNoEvents(); - - // Gesture monitor pilfer the pointers. - mDispatcher->pilferPointers(monitor.getToken()); - - // Second finger down on window, the window should not receive touch down. - const MotionEvent secondFingerDownEvent = - MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | - (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - AINPUT_SOURCE_TOUCHSCREEN) - .displayId(ADISPLAY_ID_DEFAULT) - .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) - .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) - .x(100) - .y(200)) - .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) - .build(); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, - InputEventInjectionSync::WAIT_FOR_RESULT)) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - - window->assertNoEvents(); - monitor.consumeMotionPointerDown(1 /* pointerIndex */); -} - /** * Dispatcher has touch mode enabled by default. Typically, the policy overrides that value to * the device default right away. In the test scenario, we check both the default value, @@ -3112,6 +2997,7 @@ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); + const WindowInfo& windowInfo = *window->getInfo(); // Set focused application. mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); @@ -3128,7 +3014,8 @@ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { window->consumeFocusEvent(false /*hasFocus*/, true /*inTouchMode*/); SCOPED_TRACE("Disable touch mode"); - mDispatcher->setInTouchMode(false); + mDispatcher->setInTouchMode(false, windowInfo.ownerPid, windowInfo.ownerUid, + /* hasPermission */ true); window->consumeTouchModeEvent(false); window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -3141,7 +3028,8 @@ TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) { window->consumeFocusEvent(false /*hasFocus*/, false /*inTouchMode*/); SCOPED_TRACE("Enable touch mode again"); - mDispatcher->setInTouchMode(true); + mDispatcher->setInTouchMode(true, windowInfo.ownerPid, windowInfo.ownerUid, + /* hasPermission */ true); window->consumeTouchModeEvent(true); window->setFocusable(true); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); @@ -4442,6 +4330,17 @@ protected: injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, WINDOW_LOCATION)); } + + sp<FakeWindowHandle> addSpyWindow() { + sp<FakeWindowHandle> spy = + new FakeWindowHandle(mApplication, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT); + spy->setTrustedOverlay(true); + spy->setFocusable(false); + spy->setInputFeatures(WindowInfo::Feature::SPY); + spy->setDispatchingTimeout(30ms); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, mWindow}}}); + return spy; + } }; // Send a tap and respond, which should not cause an ANR. @@ -4617,12 +4516,31 @@ TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); } -// If an app is not responding to a key event, gesture monitors should continue to receive +// A spy window can receive an ANR +TEST_F(InputDispatcherSingleWindowAnr, SpyWindowAnr) { + sp<FakeWindowHandle> spy = addSpyWindow(); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + mWindow->consumeMotionDown(); + + std::optional<uint32_t> sequenceNum = spy->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(sequenceNum); + const std::chrono::duration timeout = spy->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, spy->getToken()); + + spy->finishEvent(*sequenceNum); + spy->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT, + 0 /*flags*/); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(spy->getToken()); +} + +// If an app is not responding to a key event, spy windows should continue to receive // new motion events -TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnKey) { - FakeMonitorReceiver monitor = - FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); +TEST_F(InputDispatcherSingleWindowAnr, SpyWindowReceivesEventsDuringAppAnrOnKey) { + sp<FakeWindowHandle> spy = addSpyWindow(); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT)); @@ -4633,44 +4551,64 @@ TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnr const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); - // New tap will go to the gesture monitor, but not to the window + // New tap will go to the spy window, but not to the window tapOnWindow(); - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); - monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + spy->consumeMotionUp(ADISPLAY_ID_DEFAULT); mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion mDispatcher->waitForIdle(); mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken()); mWindow->assertNoEvents(); - monitor.assertNoEvents(); + spy->assertNoEvents(); } -// If an app is not responding to a motion event, gesture monitors should continue to receive +// If an app is not responding to a motion event, spy windows should continue to receive // new motion events -TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnrOnMotion) { - FakeMonitorReceiver monitor = - FakeMonitorReceiver(mDispatcher, "Gesture monitor", ADISPLAY_ID_DEFAULT, - true /*isGestureMonitor*/); +TEST_F(InputDispatcherSingleWindowAnr, SpyWindowReceivesEventsDuringAppAnrOnMotion) { + sp<FakeWindowHandle> spy = addSpyWindow(); tapOnWindow(); - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); - monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + spy->consumeMotionUp(ADISPLAY_ID_DEFAULT); mWindow->consumeMotionDown(); // Stuck on the ACTION_UP const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); - // New tap will go to the gesture monitor, but not to the window + // New tap will go to the spy window, but not to the window tapOnWindow(); - monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); - monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + spy->consumeMotionUp(ADISPLAY_ID_DEFAULT); mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion mDispatcher->waitForIdle(); mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken()); mWindow->assertNoEvents(); - monitor.assertNoEvents(); + spy->assertNoEvents(); +} + +TEST_F(InputDispatcherSingleWindowAnr, UnresponsiveMonitorAnr) { + mDispatcher->setMonitorDispatchingTimeoutForTest(30ms); + + FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + WINDOW_LOCATION)); + + mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + const std::optional<uint32_t> consumeSeq = monitor.receiveEvent(); + ASSERT_TRUE(consumeSeq); + + mFakePolicy->assertNotifyMonitorUnresponsiveWasCalled(30ms); + + monitor.finishEvent(*consumeSeq); + monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT); + + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyMonitorResponsiveWasCalled(); } // If a window is unresponsive, then you get anr. if the window later catches up and starts to @@ -5687,11 +5625,7 @@ TEST_F(InputDispatcherUntrustedTouchesTest, OutsideEvent_HasZeroCoordinates) { touch(); - InputEvent* event = w->consume(); - ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); - MotionEvent& motionEvent = static_cast<MotionEvent&>(*event); - EXPECT_EQ(0.0f, motionEvent.getRawPointerCoords(0)->getX()); - EXPECT_EQ(0.0f, motionEvent.getRawPointerCoords(0)->getY()); + w->consumeMotionOutsideWithZeroedCoords(); } TEST_F(InputDispatcherUntrustedTouchesTest, WindowWithOpacityBelowThreshold_AllowsTouch) { @@ -6300,19 +6234,23 @@ protected: mWindow->consumeFocusEvent(true); } - void changeAndVerifyTouchMode(bool inTouchMode) { - mDispatcher->setInTouchMode(inTouchMode); + void changeAndVerifyTouchMode(bool inTouchMode, int32_t pid, int32_t uid, bool hasPermission) { + mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission); mWindow->consumeTouchModeEvent(inTouchMode); mSecondWindow->consumeTouchModeEvent(inTouchMode); } }; TEST_F(InputDispatcherTouchModeChangedTests, ChangeTouchModeOnFocusedWindow) { - changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode); + const WindowInfo& windowInfo = *mWindow->getInfo(); + changeAndVerifyTouchMode(!InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid, + windowInfo.ownerUid, /* hasPermission */ false); } TEST_F(InputDispatcherTouchModeChangedTests, EventIsNotGeneratedIfNotChangingTouchMode) { - mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode); + const WindowInfo& windowInfo = *mWindow->getInfo(); + mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, windowInfo.ownerPid, + windowInfo.ownerUid, /* hasPermission */ true); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); } @@ -6338,6 +6276,7 @@ public: sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); window->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); + window->setFocusable(true); return window; } @@ -6345,10 +6284,13 @@ private: int mSpyCount{0}; }; +using InputDispatcherSpyWindowDeathTest = InputDispatcherSpyWindowTest; /** * Adding a spy window that is not a trusted overlay causes Dispatcher to abort. */ -TEST_F(InputDispatcherSpyWindowTest, UntrustedSpy_AbortsDispatcher) { +TEST_F(InputDispatcherSpyWindowDeathTest, UntrustedSpy_AbortsDispatcher) { + ScopedSilentDeath _silentDeath; + auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL); spy->setTrustedOverlay(false); ASSERT_DEATH(mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}}), @@ -6498,20 +6440,23 @@ TEST_F(InputDispatcherSpyWindowTest, ModalWindow) { /** * A spy window can listen for touches outside its touchable region using the WATCH_OUTSIDE_TOUCHES - * flag. + * flag, but it will get zero-ed out coordinates if the foreground has a different owner. */ TEST_F(InputDispatcherSpyWindowTest, WatchOutsideTouches) { auto window = createForeground(); + window->setOwnerInfo(12, 34); auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::WATCH_OUTSIDE_TOUCH); + spy->setOwnerInfo(56, 78); spy->setFrame(Rect{0, 0, 20, 20}); mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); // Inject an event outside the spy window's frame and touchable region. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; window->consumeMotionDown(); - spy->consumeMotionOutside(); + spy->consumeMotionOutsideWithZeroedCoords(); } /** @@ -6532,7 +6477,7 @@ TEST_F(InputDispatcherSpyWindowTest, PilferPointers) { spy2->consumeMotionDown(); // Pilfer pointers from the second spy window. - mDispatcher->pilferPointers(spy2->getToken()); + EXPECT_EQ(OK, mDispatcher->pilferPointers(spy2->getToken())); spy2->assertNoEvents(); spy1->consumeMotionCancel(); window->consumeMotionCancel(); @@ -6548,6 +6493,95 @@ TEST_F(InputDispatcherSpyWindowTest, PilferPointers) { } /** + * A spy window can pilfer pointers for a gesture even after the foreground window has been removed + * in the middle of the gesture. + */ +TEST_F(InputDispatcherSpyWindowTest, CanPilferAfterWindowIsRemovedMidStream) { + auto window = createForeground(); + auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + + window->releaseChannel(); + + EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken())); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + spy->consumeMotionUp(ADISPLAY_ID_DEFAULT); +} + +/** + * After a spy window pilfers pointers, new pointers that go down in its bounds should be sent to + * the spy, but not to any other windows. + */ +TEST_F(InputDispatcherSpyWindowTest, ContinuesToReceiveGestureAfterPilfer) { + auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); + auto window = createForeground(); + window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + + // First finger down on the window and the spy. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + spy->consumeMotionDown(); + window->consumeMotionDown(); + + // Spy window pilfers the pointers. + EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken())); + window->consumeMotionCancel(); + + // Second finger down on the window and spy, but the window should not receive the pointer down. + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(200)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + spy->consumeMotionPointerDown(1 /*pointerIndex*/); + + // Third finger goes down outside all windows, so injection should fail. + const MotionEvent thirdFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(200)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .pointer(PointerBuilder(/* id */ 2, AMOTION_EVENT_TOOL_TYPE_FINGER).x(-5).y(-5)) + .build(); + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(mDispatcher, thirdFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + spy->assertNoEvents(); + window->assertNoEvents(); +} + +/** * Even when a spy window spans over multiple foreground windows, the spy should receive all * pointers that are down within its bounds. */ @@ -6619,6 +6653,76 @@ TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) { spyRight->consumeMotionDown(); } +/** + * The spy window should not be able to affect whether or not touches are split. Only the foreground + * windows should be allowed to control split touch. + */ +TEST_F(InputDispatcherSpyWindowTest, SplitIfNoForegroundWindowTouched) { + // Create a touch modal spy that spies on the entire display. + // This spy window does not set the SPLIT_TOUCH flag. However, we still expect to split touches + // because a foreground window has not disabled splitting. + auto spy = createSpy(static_cast<WindowInfo::Flag>(0)); + + // Create a non touch modal window that supports split touch. + auto window = createForeground(); + window->setFrame(Rect(0, 0, 100, 100)); + window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + + // First finger down, no window touched. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + spy->consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->assertNoEvents(); + + // Second finger down on window, the window should receive touch down. + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(200)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + spy->consumeMotionPointerDown(1 /* pointerIndex */); +} + +/** + * A spy window will usually be implemented as an un-focusable window. Verify that these windows + * do not receive key events. + */ +TEST_F(InputDispatcherSpyWindowTest, UnfocusableSpyDoesNotReceiveKeyEvents) { + auto spy = createSpy(static_cast<WindowInfo::Flag>(0)); + spy->setFocusable(false); + + auto window = createForeground(); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}}); + setFocusedWindow(window); + window->consumeFocusEvent(true); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeKeyDown(ADISPLAY_ID_NONE); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(mDispatcher)) + << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; + window->consumeKeyUp(ADISPLAY_ID_NONE); + + spy->assertNoEvents(); +} + class InputDispatcherStylusInterceptorTest : public InputDispatcherTest { public: std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() { @@ -6665,7 +6769,11 @@ public: } }; -TEST_F(InputDispatcherStylusInterceptorTest, UntrustedOverlay_AbortsDispatcher) { +using InputDispatcherStylusInterceptorDeathTest = InputDispatcherStylusInterceptorTest; + +TEST_F(InputDispatcherStylusInterceptorDeathTest, UntrustedOverlay_AbortsDispatcher) { + ScopedSilentDeath _silentDeath; + auto [overlay, window] = setupStylusOverlayScenario(); overlay->setTrustedOverlay(false); // Configuring an untrusted overlay as a stylus interceptor should cause Dispatcher to abort. @@ -6712,4 +6820,7 @@ TEST_F(InputDispatcherStylusInterceptorTest, SpyWindowStylusInterceptor) { window->assertNoEvents(); } +// TODO(b/198487159): Add permission tests for touch mode switch once the validation is put in +// place. + } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp new file mode 100644 index 0000000000..a3220cc63a --- /dev/null +++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp @@ -0,0 +1,938 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "../UnwantedInteractionBlocker.h" +#include <android-base/silent_death_test.h> +#include <gtest/gtest.h> +#include <gui/constants.h> +#include <linux/input.h> + +#include "TestInputListener.h" + +namespace android { + +constexpr int32_t DEVICE_ID = 3; +constexpr int32_t X_RESOLUTION = 11; +constexpr int32_t Y_RESOLUTION = 11; +constexpr int32_t MAJOR_RESOLUTION = 1; + +constexpr int POINTER_0_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_1_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_2_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_0_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_1_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_2_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int DOWN = AMOTION_EVENT_ACTION_DOWN; +constexpr int MOVE = AMOTION_EVENT_ACTION_MOVE; +constexpr int UP = AMOTION_EVENT_ACTION_UP; +constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL; + +struct PointerData { + float x; + float y; + float major; +}; + +static NotifyMotionArgs generateMotionArgs(nsecs_t downTime, nsecs_t eventTime, int32_t action, + const std::vector<PointerData>& points) { + size_t pointerCount = points.size(); + if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) { + EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer"; + } + + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + pointerCoords[i].clear(); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, points[i].major); + } + + // Define a valid motion event. + NotifyMotionArgs args(/* id */ 0, eventTime, 0 /*readTime*/, DEVICE_ID, + AINPUT_SOURCE_TOUCHSCREEN, 0 /*displayId*/, POLICY_FLAG_PASS_TO_USER, + action, /* actionButton */ 0, + /* flags */ 0, AMETA_NONE, /* buttonState */ 0, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, + pointerProperties, pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /* videoFrames */ {}); + + return args; +} + +static InputDeviceInfo generateTestDeviceInfo() { + InputDeviceIdentifier identifier; + + auto info = InputDeviceInfo(); + info.initialize(DEVICE_ID, /*generation*/ 1, /*controllerNumber*/ 1, identifier, "alias", + /*isExternal*/ false, /*hasMic*/ false); + info.addSource(AINPUT_SOURCE_TOUCHSCREEN); + info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat*/ 0, + /*fuzz*/ 0, X_RESOLUTION); + info.addMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN, 0, 2559, /*flat*/ 0, + /*fuzz*/ 0, Y_RESOLUTION); + info.addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN, 0, 255, + /*flat*/ 0, /*fuzz*/ 0, MAJOR_RESOLUTION); + + return info; +} + +static AndroidPalmFilterDeviceInfo generatePalmFilterDeviceInfo() { + InputDeviceInfo androidInfo = generateTestDeviceInfo(); + std::optional<AndroidPalmFilterDeviceInfo> info = createPalmFilterDeviceInfo(androidInfo); + if (!info) { + ADD_FAILURE() << "Could not convert android device info to ::ui version"; + return {}; + } + return *info; +} + +TEST(DeviceInfoConversionTest, TabletDeviceTest) { + AndroidPalmFilterDeviceInfo info = generatePalmFilterDeviceInfo(); + ASSERT_EQ(X_RESOLUTION, info.x_res); + ASSERT_EQ(Y_RESOLUTION, info.y_res); + ASSERT_EQ(MAJOR_RESOLUTION, info.touch_major_res); + ASSERT_EQ(1599, info.max_x); + ASSERT_EQ(2559, info.max_y); +} + +static void assertArgs(const NotifyMotionArgs& args, int32_t action, + const std::vector<std::pair<int32_t /*pointerId*/, PointerData>>& pointers) { + ASSERT_EQ(action, args.action); + ASSERT_EQ(pointers.size(), args.pointerCount); + for (size_t i = 0; i < args.pointerCount; i++) { + const auto& [pointerId, pointerData] = pointers[i]; + ASSERT_EQ(pointerId, args.pointerProperties[i].id); + ASSERT_EQ(pointerData.x, args.pointerCoords[i].getX()); + ASSERT_EQ(pointerData.y, args.pointerCoords[i].getY()); + ASSERT_EQ(pointerData.major, + args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR)); + } +} + +TEST(RemovePointerIdsTest, RemoveOnePointer) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, + AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}}); + + NotifyMotionArgs pointer1Only = removePointerIds(args, {0}); + assertArgs(pointer1Only, AMOTION_EVENT_ACTION_MOVE, {{1, {4, 5, 6}}}); + + NotifyMotionArgs pointer0Only = removePointerIds(args, {1}); + assertArgs(pointer0Only, AMOTION_EVENT_ACTION_MOVE, {{0, {1, 2, 3}}}); +} + +/** + * Remove 2 out of 3 pointers during a MOVE event. + */ +TEST(RemovePointerIdsTest, RemoveTwoPointers) { + NotifyMotionArgs args = + generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, AMOTION_EVENT_ACTION_MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointer1Only = removePointerIds(args, {0, 2}); + assertArgs(pointer1Only, AMOTION_EVENT_ACTION_MOVE, {{1, {4, 5, 6}}}); +} + +/** + * Remove an active pointer during a POINTER_DOWN event, and also remove a non-active + * pointer during a POINTER_DOWN event. + */ +TEST(RemovePointerIdsTest, ActionPointerDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointers0And2 = removePointerIds(args, {1}); + assertArgs(pointers0And2, ACTION_UNKNOWN, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); + + NotifyMotionArgs pointers1And2 = removePointerIds(args, {0}); + assertArgs(pointers1And2, POINTER_0_DOWN, {{1, {4, 5, 6}}, {2, {7, 8, 9}}}); +} + +/** + * Remove all pointers during a MOVE event. + */ +TEST(RemovePointerIdsTest, RemoveAllPointersDuringMove) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, + AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}}); + + NotifyMotionArgs noPointers = removePointerIds(args, {0, 1}); + ASSERT_EQ(0u, noPointers.pointerCount); +} + +/** + * If we have ACTION_POINTER_DOWN, and we remove all pointers except for the active pointer, + * then we should just have ACTION_DOWN. Likewise, a POINTER_UP event should become an UP event. + */ +TEST(RemovePointerIdsTest, PointerDownBecomesDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointer1 = removePointerIds(args, {0, 2}); + assertArgs(pointer1, DOWN, {{1, {4, 5, 6}}}); + + args.action = POINTER_1_UP; + pointer1 = removePointerIds(args, {0, 2}); + assertArgs(pointer1, UP, {{1, {4, 5, 6}}}); +} + +/** + * If a pointer that is now going down is canceled, then we can just drop the POINTER_DOWN event. + */ +TEST(CancelSuppressedPointersTest, CanceledPointerDownIsDropped) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_TRUE(result.empty()); +} + +/** + * If a pointer is already suppressed, the POINTER_UP event for this pointer should be dropped + */ +TEST(CancelSuppressedPointersTest, SuppressedPointerUpIsDropped) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_TRUE(result.empty()); +} + +/** + * If a pointer is already suppressed, it should be removed from a MOVE event. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointerIsRemovedDuringMove) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); +} + +/** + * If a pointer just got canceled during a MOVE event, we should see two events: + * 1) ACTION_POINTER_UP with FLAG_CANCELED so that this pointer is lifted + * 2) A MOVE event without this pointer + */ +TEST(CancelSuppressedPointersTest, NewlySuppressedPointerIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(2u, result.size()); + assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); + assertArgs(result[1], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); +} + +/** + * If we have a single pointer that gets canceled during a MOVE, the entire gesture + * should be canceled with ACTION_CANCEL. + */ +TEST(CancelSuppressedPointersTest, SingleSuppressedPointerIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * If one of 3 pointers gets canceled during a POINTER_UP event, we should proceed with POINTER_UP, + * but this event should also have FLAG_CANCELED to indicate that this pointer was unintentional. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointer1GoingUpIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * Same test as above, but we change the pointer's index to 0 instead of 1. This helps detect + * errors with handling pointer index inside the action. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointer0GoingUpIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_0_UP, + {{1, 2, 3}, {4, 5, 6}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * If two pointers are canceled simultaneously during MOVE, we should see a single ACTION_CANCEL + * event. This event would cancel the entire gesture. + */ +TEST(CancelSuppressedPointersTest, TwoNewlySuppressedPointersAreBothCanceled) { + NotifyMotionArgs args = + generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}, {4, 5, 6}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * Similar test to above. During a POINTER_UP event, both pointers are detected as 'palm' and + * therefore should be removed. In this case, we should send a single ACTION_CANCEL that + * would undo the entire gesture. + */ +TEST(CancelSuppressedPointersTest, TwoPointersAreCanceledDuringPointerUp) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * When all pointers have been removed from the touch stream, and we have a new POINTER_DOWN, + * this should become a regular DOWN event because it's the only pointer that will be valid now. + */ +TEST(CancelSuppressedPointersTest, NewPointerDownBecomesDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_2_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector<NotifyMotionArgs> result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {0, 1}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], DOWN, {{2, {7, 8, 9}}}); + ASSERT_EQ(0, result[0].flags); +} + +/** + * Call 'getTouches' for a DOWN event and check that the resulting 'InProgressTouchEvdev' + * struct is populated as expected. + */ +TEST(GetTouchesTest, ConvertDownEvent) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, DOWN, {{1, 2, 3}}); + AndroidPalmFilterDeviceInfo deviceInfo = generatePalmFilterDeviceInfo(); + SlotState slotState; + SlotState oldSlotState = slotState; + slotState.update(args); + std::vector<::ui::InProgressTouchEvdev> touches = + getTouches(args, deviceInfo, oldSlotState, slotState); + ASSERT_EQ(1u, touches.size()); + ::ui::InProgressTouchEvdev expected; + + expected.major = 3; + expected.minor = 0; + expected.tool_type = MT_TOOL_FINGER; + expected.altered = true; + expected.was_cancelled = false; + expected.cancelled = false; + expected.delayed = false; + expected.was_delayed = false; + expected.held = false; + expected.was_held = false; + expected.was_touching = false; + expected.touching = true; + expected.x = 1; + expected.y = 2; + expected.tracking_id = 0; + std::optional<size_t> slot = slotState.getSlotForPointerId(0); + ASSERT_TRUE(slot); + expected.slot = *slot; + expected.pressure = 0; + expected.tool_code = BTN_TOOL_FINGER; + expected.reported_tool_type = ::ui::EventPointerType::kTouch; + expected.stylus_button = false; + + ASSERT_EQ(expected, touches[0]) << toString(touches[0]); +} + +// --- UnwantedInteractionBlockerTest --- + +class UnwantedInteractionBlockerTest : public testing::Test { +protected: + TestInputListener mTestListener; + std::unique_ptr<UnwantedInteractionBlockerInterface> mBlocker; + + void SetUp() override { + mBlocker = std::make_unique<UnwantedInteractionBlocker>(mTestListener, + /*enablePalmRejection*/ true); + } +}; + +/** + * Create a basic configuration change and send it to input classifier. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, ConfigurationChangedIsPassedToNextListener) { + // Create a basic configuration change and send to classifier + NotifyConfigurationChangedArgs args(1 /*sequenceNum*/, 2 /*eventTime*/); + + mBlocker->notifyConfigurationChanged(&args); + NotifyConfigurationChangedArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Keys are not handled in 'UnwantedInteractionBlocker' and should be passed + * to next stage unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, KeyIsPassedToNextListener) { + // Create a basic key event and send to classifier + NotifyKeyArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 21 /*readTime*/, 3 /*deviceId*/, + AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, 0 /*policyFlags*/, + AKEY_EVENT_ACTION_DOWN, 4 /*flags*/, AKEYCODE_HOME, 5 /*scanCode*/, + AMETA_NONE, 6 /*downTime*/); + + mBlocker->notifyKey(&args); + NotifyKeyArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Create a basic motion event. Since it's just a DOWN event, it should not + * be detected as palm and should be sent to the next listener stage + * unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, DownEventIsPassedToNextListener) { + NotifyMotionArgs motionArgs = + generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); + mBlocker->notifyMotion(&motionArgs); + NotifyMotionArgs args; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(motionArgs, args); +} + +/** + * Create a basic switch event and send it to the UnwantedInteractionBlocker. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, SwitchIsPassedToNextListener) { + NotifySwitchArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 3 /*policyFlags*/, 4 /*switchValues*/, + 5 /*switchMask*/); + + mBlocker->notifySwitch(&args); + NotifySwitchArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Create a basic device reset event and send it to UnwantedInteractionBlocker. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, DeviceResetIsPassedToNextListener) { + NotifyDeviceResetArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, DEVICE_ID); + + mBlocker->notifyDeviceReset(&args); + NotifyDeviceResetArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * The state should be reset when device reset happens. That means, we can reset in the middle of a + * gesture, and start a new stream. There should be no crash. If the state wasn't reset correctly, + * a crash due to inconsistent event stream could have occurred. + */ +TEST_F(UnwantedInteractionBlockerTest, NoCrashWhenResetHappens) { + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + NotifyDeviceResetArgs resetArgs(1 /*sequenceNum*/, 3 /*eventTime*/, DEVICE_ID); + mBlocker->notifyDeviceReset(&resetArgs); + // Start a new gesture with a DOWN event, even though the previous event stream was incomplete. + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, DOWN, {{7, 8, 9}}))); +} + +/** + * If input devices have changed, but the important device info that's used by the + * UnwantedInteractionBlocker has not changed, there should not be a reset. + */ +TEST_F(UnwantedInteractionBlockerTest, NoResetIfDeviceInfoChanges) { + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + + // Now pretend the device changed, even though nothing is different for DEVICE_ID in practice. + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + + // The MOVE event continues the gesture that started before 'devices changed', so it should not + // cause a crash. + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, MOVE, {{7, 8, 9}}))); +} + +using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest; + +/** + * The state should be reset when device reset happens. If we receive an inconsistent event after + * the reset happens, crash should occur. + */ +TEST_F(UnwantedInteractionBlockerTestDeathTest, InconsistentEventAfterResetCausesACrash) { + ScopedSilentDeath _silentDeath; + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + NotifyDeviceResetArgs resetArgs(1 /*sequenceNum*/, 3 /*eventTime*/, DEVICE_ID); + mBlocker->notifyDeviceReset(&resetArgs); + // Sending MOVE without a DOWN -> should crash! + ASSERT_DEATH( + { + mBlocker->notifyMotion(&(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, + MOVE, {{7, 8, 9}}))); + }, + "Could not find slot"); +} + +/** + * There should be a crash when an inconsistent event is received. + */ +TEST_F(UnwantedInteractionBlockerTestDeathTest, WhenMoveWithoutDownCausesACrash) { + ScopedSilentDeath _silentDeath; + NotifyMotionArgs args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{1, 2, 3}}); + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + ASSERT_DEATH({ mBlocker->notifyMotion(&args); }, "Could not find slot"); +} + +class PalmRejectorTest : public testing::Test { +protected: + std::unique_ptr<PalmRejector> mPalmRejector; + + void SetUp() override { + AndroidPalmFilterDeviceInfo info = generatePalmFilterDeviceInfo(); + mPalmRejector = std::make_unique<PalmRejector>(info); + } +}; + +using PalmRejectorTestDeathTest = PalmRejectorTest; + +TEST_F(PalmRejectorTestDeathTest, InconsistentEventCausesACrash) { + ScopedSilentDeath _silentDeath; + constexpr nsecs_t downTime = 0; + NotifyMotionArgs args = + generateMotionArgs(downTime, 2 /*eventTime*/, MOVE, {{1406.0, 650.0, 52.0}}); + ASSERT_DEATH({ mPalmRejector->processMotion(args); }, "Could not find slot"); +} + +/** + * Use PalmRejector with actual touchscreen data and real model. + * Two pointers that should both be classified as palms. + */ +TEST_F(PalmRejectorTest, TwoPointersAreCanceled) { + std::vector<NotifyMotionArgs> argsList; + constexpr nsecs_t downTime = 255955749837000; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955759313000, MOVE, {{1406.0, 650.0, 52.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955766361000, MOVE, {{1429.0, 672.0, 46.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955775989000, MOVE, {{1417.0, 685.0, 41.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955775989000, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955792536000, MOVE, + {{1415.0, 719.0, 44.0}, {1060.0, 760.0, 11.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955799474000, MOVE, + {{1421.0, 733.0, 42.0}, {1065.0, 769.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955809177000, MOVE, + {{1426.0, 742.0, 43.0}, {1068.0, 771.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955816131000, MOVE, + {{1430.0, 748.0, 45.0}, {1069.0, 772.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955825907000, MOVE, + {{1432.0, 750.0, 44.0}, {1069.0, 772.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(0 /* No FLAG_CANCELED */, argsList[0].flags); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955832736000, MOVE, + {{1433.0, 751.0, 44.0}, {1070.0, 771.0, 13.0}})); + ASSERT_EQ(2u, argsList.size()); + ASSERT_EQ(POINTER_0_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + ASSERT_EQ(MOVE, argsList[1].action); + ASSERT_EQ(1u, argsList[1].pointerCount); + ASSERT_EQ(0, argsList[1].flags); + + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955842432000, MOVE, + {{1433.0, 751.0, 42.0}, {1071.0, 770.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955849380000, MOVE, + {{1433.0, 751.0, 45.0}, {1072.0, 769.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955859046000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 768.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955869823000, MOVE, + {{1433.0, 751.0, 45.0}, {1072.0, 767.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, argsList[0].action); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955875641000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955882693000, MOVE, + {{1433.0, 750.0, 44.0}, {1072.0, 765.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955892324000, MOVE, + {{1433.0, 750.0, 42.0}, {1072.0, 763.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955899425000, MOVE, + {{1434.0, 750.0, 44.0}, {1073.0, 761.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955909400000, MOVE, + {{1435.0, 750.0, 43.0}, {1073.0, 759.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955915885000, MOVE, + {{1436.0, 750.0, 45.0}, {1074.0, 757.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955925607000, MOVE, + {{1436.0, 750.0, 44.0}, {1074.0, 755.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955932580000, MOVE, + {{1436.0, 750.0, 45.0}, {1074.0, 753.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955942231000, MOVE, + {{1436.0, 749.0, 44.0}, {1074.0, 751.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955949204000, MOVE, + {{1435.0, 748.0, 45.0}, {1074.0, 749.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955959103000, MOVE, + {{1434.0, 746.0, 44.0}, {1074.0, 747.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955965884000, MOVE, + {{1433.0, 744.0, 44.0}, {1075.0, 745.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955975649000, MOVE, + {{1431.0, 741.0, 43.0}, {1075.0, 742.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955982537000, MOVE, + {{1428.0, 738.0, 43.0}, {1076.0, 739.0, 12.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955992284000, MOVE, + {{1400.0, 726.0, 54.0}, {1076.0, 739.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955999348000, POINTER_1_UP, + {{1362.0, 716.0, 55.0}, {1076.0, 739.0, 13.0}})); + ASSERT_TRUE(argsList.empty()); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955999348000, MOVE, {{1362.0, 716.0, 55.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956008885000, MOVE, {{1347.0, 707.0, 54.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956015791000, MOVE, {{1340.0, 698.0, 54.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956025804000, MOVE, {{1338.0, 694.0, 55.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956032314000, MOVE, {{1336.0, 690.0, 53.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956042329000, MOVE, {{1334.0, 685.0, 47.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956048979000, MOVE, {{1333.0, 679.0, 46.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956058813000, MOVE, {{1332.0, 672.0, 45.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956065592000, MOVE, {{1333.0, 666.0, 40.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956075276000, MOVE, {{1336.0, 661.0, 24.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956082198000, MOVE, {{1338.0, 656.0, 16.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956092059000, MOVE, {{1341.0, 649.0, 1.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956098764000, UP, {{1341.0, 649.0, 1.0}})); + ASSERT_TRUE(argsList.empty()); +} + +/** + * A test implementation of PalmDetectionFilter that allows you to specify which pointer you want + * the model to consider 'suppressed'. The pointer is specified using its position (x, y). + * Current limitation: + * Pointers may not cross each other in space during motion. Otherwise, any pointer with the + * position matching the suppressed position will be considered "palm". + */ +class TestFilter : public ::ui::PalmDetectionFilter { +public: + TestFilter(::ui::SharedPalmDetectionFilterState* state, + std::vector<std::pair<float, float>>& suppressedPointers) + : ::ui::PalmDetectionFilter(state), mSuppressedPointers(suppressedPointers) {} + + void Filter(const std::vector<::ui::InProgressTouchEvdev>& touches, ::base::TimeTicks time, + std::bitset<::ui::kNumTouchEvdevSlots>* slots_to_hold, + std::bitset<::ui::kNumTouchEvdevSlots>* slots_to_suppress) override { + updateSuppressedSlots(touches); + *slots_to_suppress = mSuppressedSlots; + } + + std::string FilterNameForTesting() const override { return "test filter"; } + +private: + void updateSuppressedSlots(const std::vector<::ui::InProgressTouchEvdev>& touches) { + for (::ui::InProgressTouchEvdev touch : touches) { + for (const auto& [x, y] : mSuppressedPointers) { + const float dx = (touch.x - x); + const float dy = (touch.y - y); + const float distanceSquared = dx * dx + dy * dy; + if (distanceSquared < 1) { + mSuppressedSlots.set(touch.slot, true); + } + } + } + } + + std::bitset<::ui::kNumTouchEvdevSlots> mSuppressedSlots; + std::vector<std::pair<float, float>>& mSuppressedPointers; +}; + +class PalmRejectorFakeFilterTest : public testing::Test { +protected: + std::unique_ptr<PalmRejector> mPalmRejector; + + void SetUp() override { + std::unique_ptr<::ui::PalmDetectionFilter> filter = + std::make_unique<TestFilter>(&mSharedPalmState, /*byref*/ mSuppressedPointers); + mPalmRejector = + std::make_unique<PalmRejector>(generatePalmFilterDeviceInfo(), std::move(filter)); + } + + void suppressPointerAtPosition(float x, float y) { mSuppressedPointers.push_back({x, y}); } + +private: + std::vector<std::pair<float, float>> mSuppressedPointers; + ::ui::SharedPalmDetectionFilterState mSharedPalmState; // unused, but we must retain ownership +}; + +/** + * When a MOVE event happens, the model identifies the pointer as palm. At that time, the palm + * rejector should send a POINTER_UP event for this pointer with FLAG_CANCELED, and subsequent + * events should have this pointer removed. + */ +TEST_F(PalmRejectorFakeFilterTest, OneOfTwoPointersIsCanceled) { + std::vector<NotifyMotionArgs> argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Cancel the second pointer + suppressPointerAtPosition(1059, 731); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + ASSERT_EQ(2u, argsList.size()); + // First event - cancel pointer 1 + ASSERT_EQ(POINTER_1_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + // Second event - send MOVE for the remaining pointer + ASSERT_EQ(MOVE, argsList[1].action); + ASSERT_EQ(0, argsList[1].flags); + + // Future move events only contain 1 pointer, because the second pointer will continue + // to be suppressed + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(MOVE, argsList[0].action); + ASSERT_EQ(1u, argsList[0].pointerCount); + ASSERT_EQ(1433, argsList[0].pointerCoords[0].getX()); + ASSERT_EQ(751, argsList[0].pointerCoords[0].getY()); +} + +/** + * Send two pointers, and suppress both of them. Check that ACTION_CANCEL is generated. + * Afterwards: + * 1) Future MOVE events are ignored. + * 2) When a new pointer goes down, ACTION_DOWN is generated + */ +TEST_F(PalmRejectorFakeFilterTest, NewDownEventAfterCancel) { + std::vector<NotifyMotionArgs> argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Cancel both pointers + suppressPointerAtPosition(1059, 731); + suppressPointerAtPosition(1400, 680); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, MOVE, {{1400, 680, 41}, {1059, 731, 10}})); + ASSERT_EQ(1u, argsList.size()); + // Cancel all + ASSERT_EQ(CANCEL, argsList[0].action); + ASSERT_EQ(2u, argsList[0].pointerCount); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + // Future move events are ignored + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + ASSERT_EQ(0u, argsList.size()); + + // When a new pointer goes down, a new DOWN event is generated + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, POINTER_2_DOWN, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}, {1000, 700, 10}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(DOWN, argsList[0].action); + ASSERT_EQ(1u, argsList[0].pointerCount); + ASSERT_EQ(2, argsList[0].pointerProperties[0].id); +} + +/** + * 2 pointers are classified as palm simultaneously. When they are later + * released by Android, make sure that we drop both of these POINTER_UP events. + * Since they are classified as palm at the same time, we just need to receive a single CANCEL + * event. From MotionEvent docs: """A pointer id remains valid until the pointer eventually goes up + * (indicated by ACTION_UP or ACTION_POINTER_UP) or when the gesture is canceled (indicated by + * ACTION_CANCEL).""" + * This means that generating additional POINTER_UP events is not necessary. + * The risk here is that "oldSuppressedPointerIds" will not be correct, because it will update after + * each motion, but pointers are canceled one at a time by Android. + */ +TEST_F(PalmRejectorFakeFilterTest, TwoPointersCanceledWhenOnePointerGoesUp) { + std::vector<NotifyMotionArgs> argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Suppress both pointers!! + suppressPointerAtPosition(1414, 702); + suppressPointerAtPosition(1059, 731); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, POINTER_1_UP, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(CANCEL, argsList[0].action) << MotionEvent::actionToString(argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + // Future move events should not go to the listener. + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783049000, MOVE, {{1435.0, 755.0, 43.0}})); + ASSERT_EQ(0u, argsList.size()); + + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783059000, UP, {{1436.0, 756.0, 43.0}})); + ASSERT_EQ(0u, argsList.size()); +} + +/** + * Send 3 pointers, and then cancel one of them during a MOVE event. We should see ACTION_POINTER_UP + * generated for that. Next, another pointer is canceled during ACTION_POINTER_DOWN. For that + * pointer, we simply shouldn't send the event. + */ +TEST_F(PalmRejectorFakeFilterTest, CancelTwoPointers) { + std::vector<NotifyMotionArgs> argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + + // Suppress second pointer (pointer 1) + suppressPointerAtPosition(1060, 700); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, MOVE, + {{1417.0, 685.0, 41.0}, {1060, 700, 10.0}})); + ASSERT_EQ(2u, argsList.size()); + ASSERT_EQ(POINTER_1_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + ASSERT_EQ(MOVE, argsList[1].action) << MotionEvent::actionToString(argsList[1].action); + ASSERT_EQ(0, argsList[1].flags); + + // A new pointer goes down and gets suppressed right away. It should just be dropped + suppressPointerAtPosition(1001, 601); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}})); + + ASSERT_EQ(0u, argsList.size()); + // Likewise, pointer that's already canceled should be ignored + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_UP, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}})); + ASSERT_EQ(0u, argsList.size()); + + // Cancel all pointers when pointer 1 goes up. Pointer 1 was already canceled earlier. + suppressPointerAtPosition(1417, 685); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_UP, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(CANCEL, argsList[0].action); +} + +} // namespace android diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp index cdd95ca18f..86c8b0dbea 100644 --- a/services/sensorservice/AidlSensorHalWrapper.cpp +++ b/services/sensorservice/AidlSensorHalWrapper.cpp @@ -250,6 +250,46 @@ void convertToSensorEvent(const Event &src, sensors_event_t *dst) { break; } + case SensorType::ACCELEROMETER_LIMITED_AXES: + case SensorType::GYROSCOPE_LIMITED_AXES: + dst->limited_axes_imu.x = src.payload.get<Event::EventPayload::limitedAxesImu>().x; + dst->limited_axes_imu.y = src.payload.get<Event::EventPayload::limitedAxesImu>().y; + dst->limited_axes_imu.z = src.payload.get<Event::EventPayload::limitedAxesImu>().z; + dst->limited_axes_imu.x_supported = + src.payload.get<Event::EventPayload::limitedAxesImu>().xSupported; + dst->limited_axes_imu.y_supported = + src.payload.get<Event::EventPayload::limitedAxesImu>().ySupported; + dst->limited_axes_imu.z_supported = + src.payload.get<Event::EventPayload::limitedAxesImu>().zSupported; + break; + + case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: + dst->limited_axes_imu_uncalibrated.x_uncalib = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().x; + dst->limited_axes_imu_uncalibrated.y_uncalib = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().y; + dst->limited_axes_imu_uncalibrated.z_uncalib = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().z; + dst->limited_axes_imu_uncalibrated.x_bias = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xBias; + dst->limited_axes_imu_uncalibrated.y_bias = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().yBias; + dst->limited_axes_imu_uncalibrated.z_bias = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zBias; + dst->limited_axes_imu_uncalibrated.x_supported = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().xSupported; + dst->limited_axes_imu_uncalibrated.y_supported = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().ySupported; + dst->limited_axes_imu_uncalibrated.z_supported = + src.payload.get<Event::EventPayload::limitedAxesImuUncal>().zSupported; + break; + + case SensorType::HEADING: + dst->heading.heading = src.payload.get<Event::EventPayload::heading>().heading; + dst->heading.accuracy = src.payload.get<Event::EventPayload::heading>().accuracy; + break; + default: { CHECK_GE((int32_t)src.sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE); @@ -264,7 +304,7 @@ void convertFromSensorEvent(const sensors_event_t &src, Event *dst) { *dst = { .timestamp = src.timestamp, .sensorHandle = src.sensor, - .sensorType = (SensorType) src.type, + .sensorType = (SensorType)src.type, }; switch (dst->sensorType) { @@ -409,6 +449,43 @@ void convertFromSensorEvent(const sensors_event_t &src, Event *dst) { break; } + case SensorType::ACCELEROMETER_LIMITED_AXES: + case SensorType::GYROSCOPE_LIMITED_AXES: { + Event::EventPayload::LimitedAxesImu limitedAxesImu; + limitedAxesImu.x = src.limited_axes_imu.x; + limitedAxesImu.y = src.limited_axes_imu.y; + limitedAxesImu.z = src.limited_axes_imu.z; + limitedAxesImu.xSupported = src.limited_axes_imu.x_supported; + limitedAxesImu.ySupported = src.limited_axes_imu.y_supported; + limitedAxesImu.zSupported = src.limited_axes_imu.z_supported; + dst->payload.set<Event::EventPayload::Tag::limitedAxesImu>(limitedAxesImu); + break; + } + + case SensorType::ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + case SensorType::GYROSCOPE_LIMITED_AXES_UNCALIBRATED: { + Event::EventPayload::LimitedAxesImuUncal limitedAxesImuUncal; + limitedAxesImuUncal.x = src.limited_axes_imu_uncalibrated.x_uncalib; + limitedAxesImuUncal.y = src.limited_axes_imu_uncalibrated.y_uncalib; + limitedAxesImuUncal.z = src.limited_axes_imu_uncalibrated.z_uncalib; + limitedAxesImuUncal.xBias = src.limited_axes_imu_uncalibrated.x_bias; + limitedAxesImuUncal.yBias = src.limited_axes_imu_uncalibrated.y_bias; + limitedAxesImuUncal.zBias = src.limited_axes_imu_uncalibrated.z_bias; + limitedAxesImuUncal.xSupported = src.limited_axes_imu_uncalibrated.x_supported; + limitedAxesImuUncal.ySupported = src.limited_axes_imu_uncalibrated.y_supported; + limitedAxesImuUncal.zSupported = src.limited_axes_imu_uncalibrated.z_supported; + dst->payload.set<Event::EventPayload::Tag::limitedAxesImuUncal>(limitedAxesImuUncal); + break; + } + + case SensorType::HEADING: { + Event::EventPayload::Heading heading; + heading.heading = src.heading.heading; + heading.accuracy = src.heading.accuracy; + dst->payload.set<Event::EventPayload::heading>(heading); + break; + } + default: { CHECK_GE((int32_t)dst->sensorType, (int32_t)SensorType::DEVICE_PRIVATE_BASE); diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index d5b629d564..3c4f8d9bae 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -17,6 +17,7 @@ cc_library_shared { "Fusion.cpp", "GravitySensor.cpp", "HidlSensorHalWrapper.cpp", + "LimitedAxesImuSensor.cpp", "LinearAccelerationSensor.cpp", "OrientationSensor.cpp", "RecentEventLogger.cpp", diff --git a/services/sensorservice/LimitedAxesImuSensor.cpp b/services/sensorservice/LimitedAxesImuSensor.cpp new file mode 100644 index 0000000000..2f91479b38 --- /dev/null +++ b/services/sensorservice/LimitedAxesImuSensor.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <math.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> + +#include <hardware/sensors.h> + +#include "LimitedAxesImuSensor.h" +#include "SensorDevice.h" +#include "SensorFusion.h" +#include "SensorServiceUtils.h" + +namespace android { + +namespace { +const sensor_t DUMMY_SENSOR = {.name = "", + .vendor = "", + .stringType = "", + .requiredPermission = ""}; +} // unnamed namespace + +LimitedAxesImuSensor::LimitedAxesImuSensor(sensor_t const* list, size_t count, + int32_t imu3dSensorType) + : BaseSensor(DUMMY_SENSOR) { + for (size_t i = 0; i < count; i++) { + if (list[i].type == imu3dSensorType) { + mImu3dSensor = Sensor(list + i); + break; + } + } + + const int32_t imuLimitedAxesSensorType = convertImu3dToLimitedAxesSensorType(imu3dSensorType); + + const sensor_t sensor = { + .name = convertLimitedAxesSensorTypeToName(imuLimitedAxesSensorType), + .vendor = "AOSP", + .version = 1, + .handle = convertLimitedAxesSensorTypeToHandle(imuLimitedAxesSensorType), + .type = imuLimitedAxesSensorType, + .maxRange = mImu3dSensor.getMaxValue(), + .resolution = mImu3dSensor.getResolution(), + .power = mImu3dSensor.getPowerUsage(), + .minDelay = mImu3dSensor.getMinDelay(), + }; + mSensor = Sensor(&sensor); +} + +bool LimitedAxesImuSensor::process(sensors_event_t* outEvent, const sensors_event_t& event) { + if (event.type == mImu3dSensor.getType()) { + *outEvent = event; + size_t imu3dDataSize = SensorServiceUtil::eventSizeBySensorType(mImu3dSensor.getType()); + outEvent->data[0 + imu3dDataSize] = 1; + outEvent->data[1 + imu3dDataSize] = 1; + outEvent->data[2 + imu3dDataSize] = 1; + outEvent->sensor = mSensor.getHandle(); + outEvent->type = mSensor.getType(); + return true; + } + return false; +} + +status_t LimitedAxesImuSensor::activate(void* ident, bool enabled) { + return mSensorDevice.activate(ident, mImu3dSensor.getHandle(), enabled); +} + +status_t LimitedAxesImuSensor::setDelay(void* ident, int /*handle*/, int64_t ns) { + return mSensorDevice.setDelay(ident, mImu3dSensor.getHandle(), ns); +} + +int32_t LimitedAxesImuSensor::convertImu3dToLimitedAxesSensorType(int32_t imu3dSensorType) { + switch (imu3dSensorType) { + case SENSOR_TYPE_ACCELEROMETER: + return SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES; + case SENSOR_TYPE_GYROSCOPE: + return SENSOR_TYPE_GYROSCOPE_LIMITED_AXES; + case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED: + return SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED; + case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: + return SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED; + default: + return 0; + } +} + +int32_t LimitedAxesImuSensor::convertLimitedAxesSensorTypeToHandle( + int32_t imuLimitedAxesSensorType) { + switch (imuLimitedAxesSensorType) { + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES: + return '_ala'; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES: + return '_gla'; + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + return '_alc'; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED: + return '_glc'; + default: + return 0; + } +} + +const char* LimitedAxesImuSensor::convertLimitedAxesSensorTypeToName( + int32_t imuLimitedAxesSensorType) { + switch (imuLimitedAxesSensorType) { + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES: + return "Accelerometer Limited Axes Sensor"; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES: + return "Gyroscope Limited Axes Sensor"; + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + return "Accelerometer Limited Axes Uncalibrated Sensor"; + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED: + return "Gyroscope Limited Axes Uncalibrated Sensor"; + default: + return ""; + } +} + +}; // namespace android diff --git a/services/sensorservice/LimitedAxesImuSensor.h b/services/sensorservice/LimitedAxesImuSensor.h new file mode 100644 index 0000000000..fd46a9860a --- /dev/null +++ b/services/sensorservice/LimitedAxesImuSensor.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <stdint.h> +#include <sys/types.h> + +#include <sensor/Sensor.h> + +#include "SensorInterface.h" + +namespace android { + +class SensorDevice; + +class LimitedAxesImuSensor : public BaseSensor { + Sensor mImu3dSensor; + +public: + LimitedAxesImuSensor(sensor_t const* list, size_t count, int32_t imuSensorType); + virtual bool process(sensors_event_t* outEvent, const sensors_event_t& event) override; + virtual status_t activate(void* ident, bool enabled) override; + virtual status_t setDelay(void* ident, int handle, int64_t ns) override; + virtual bool isVirtual() const override { return true; } + +private: + int32_t convertImu3dToLimitedAxesSensorType(int32_t imu3dSensorType); + int32_t convertLimitedAxesSensorTypeToHandle(int32_t imuLimitedAxesSensorType); + const char* convertLimitedAxesSensorTypeToName(int32_t imuLimitedAxesSensorType); +}; + +}; // namespace android
\ No newline at end of file diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 517d383b5a..971491dbef 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -38,6 +38,7 @@ #include "BatteryService.h" #include "CorrectedGyroSensor.h" #include "GravitySensor.h" +#include "LimitedAxesImuSensor.h" #include "LinearAccelerationSensor.h" #include "OrientationSensor.h" #include "RotationVectorSensor.h" @@ -101,6 +102,33 @@ static const String16 sDumpPermission("android.permission.DUMP"); static const String16 sLocationHardwarePermission("android.permission.LOCATION_HARDWARE"); static const String16 sManageSensorsPermission("android.permission.MANAGE_SENSORS"); +static bool isAutomotive() { + sp<IServiceManager> serviceManager = defaultServiceManager(); + if (serviceManager.get() == nullptr) { + ALOGE("%s: unable to access native ServiceManager", __func__); + return false; + } + + sp<content::pm::IPackageManagerNative> packageManager; + sp<IBinder> binder = serviceManager->waitForService(String16("package_native")); + packageManager = interface_cast<content::pm::IPackageManagerNative>(binder); + if (packageManager == nullptr) { + ALOGE("%s: unable to access native PackageManager", __func__); + return false; + } + + bool isAutomotive = false; + binder::Status status = + packageManager->hasSystemFeature(String16("android.hardware.type.automotive"), 0, + &isAutomotive); + if (!status.isOk()) { + ALOGE("%s: hasSystemFeature failed: %s", __func__, status.exceptionMessage().c_str()); + return false; + } + + return isAutomotive; +} + SensorService::SensorService() : mInitCheck(NO_INIT), mSocketBufferSize(SOCKET_BUFFER_SIZE_NON_BATCHED), mWakeLockAcquired(false), mLastReportedProxIsActive(false) { @@ -165,6 +193,8 @@ void SensorService::onFirstRef() { ssize_t count = dev.getSensorList(&list); if (count > 0) { bool hasGyro = false, hasAccel = false, hasMag = false; + bool hasGyroUncalibrated = false; + bool hasAccelUncalibrated = false; uint32_t virtualSensorsNeeds = (1<<SENSOR_TYPE_GRAVITY) | (1<<SENSOR_TYPE_LINEAR_ACCELERATION) | @@ -179,13 +209,18 @@ void SensorService::onFirstRef() { case SENSOR_TYPE_ACCELEROMETER: hasAccel = true; break; + case SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED: + hasAccelUncalibrated = true; + break; case SENSOR_TYPE_MAGNETIC_FIELD: hasMag = true; break; case SENSOR_TYPE_GYROSCOPE: - case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: hasGyro = true; break; + case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: + hasGyroUncalibrated = true; + break; case SENSOR_TYPE_GRAVITY: case SENSOR_TYPE_LINEAR_ACCELERATION: case SENSOR_TYPE_ROTATION_VECTOR: @@ -216,7 +251,7 @@ void SensorService::onFirstRef() { // registered) SensorFusion::getInstance(); - if (hasGyro && hasAccel && hasMag) { + if ((hasGyro || hasGyroUncalibrated) && hasAccel && hasMag) { // Add Android virtual sensors if they're not already // available in the HAL bool needRotationVector = @@ -230,7 +265,7 @@ void SensorService::onFirstRef() { registerSensor( new GyroDriftSensor(), true, true); } - if (hasAccel && hasGyro) { + if (hasAccel && (hasGyro || hasGyroUncalibrated)) { bool needGravitySensor = (virtualSensorsNeeds & (1<<SENSOR_TYPE_GRAVITY)) != 0; registerSensor(new GravitySensor(list, count), !needGravitySensor, true); @@ -250,6 +285,30 @@ void SensorService::onFirstRef() { registerSensor(new GeoMagRotationVectorSensor(), !needGeoMagRotationVector, true); } + if (isAutomotive()) { + if (hasAccel) { + registerSensor(new LimitedAxesImuSensor(list, count, SENSOR_TYPE_ACCELEROMETER), + /*isDebug=*/false, /*isVirtual=*/true); + } + + if (hasGyro) { + registerSensor(new LimitedAxesImuSensor(list, count, SENSOR_TYPE_GYROSCOPE), + /*isDebug=*/false, /*isVirtual=*/true); + } + + if (hasAccelUncalibrated) { + registerSensor(new LimitedAxesImuSensor(list, count, + SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED), + /*isDebug=*/false, /*isVirtual=*/true); + } + + if (hasGyroUncalibrated) { + registerSensor(new LimitedAxesImuSensor(list, count, + SENSOR_TYPE_GYROSCOPE_UNCALIBRATED), + /*isDebug=*/false, /*isVirtual=*/true); + } + } + // Check if the device really supports batching by looking at the FIFO event // counts for each sensor. bool batchingSupported = false; diff --git a/services/sensorservice/SensorServiceUtils.cpp b/services/sensorservice/SensorServiceUtils.cpp index baa01c9ce3..7dd23316b1 100644 --- a/services/sensorservice/SensorServiceUtils.cpp +++ b/services/sensorservice/SensorServiceUtils.cpp @@ -30,12 +30,18 @@ size_t eventSizeBySensorType(int type) { case SENSOR_TYPE_POSE_6DOF: return 16; + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED: + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED: + return 9; + case SENSOR_TYPE_ROTATION_VECTOR: case SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR: return 5; case SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED: case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED: + case SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES: + case SENSOR_TYPE_GYROSCOPE_LIMITED_AXES: return 6; case SENSOR_TYPE_GAME_ROTATION_VECTOR: @@ -61,6 +67,9 @@ size_t eventSizeBySensorType(int type) { case SENSOR_TYPE_HEAD_TRACKER: return 7; + case SENSOR_TYPE_HEADING: + return 2; + default: return 3; } diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index af0f5240d3..d9958f31c5 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -118,9 +118,6 @@ cc_defaults { cc_defaults { name: "libsurfaceflinger_production_defaults", defaults: ["libsurfaceflinger_defaults"], - cflags: [ - "-fvisibility=hidden", - ], lto: { thin: true, }, diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index e797b5d4bb..18a6baeffe 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -306,7 +306,7 @@ void BufferLayer::preparePerFrameCompositionState() { : aidl::android::hardware::graphics::composer3::Composition::DEVICE; } - compositionState->buffer = mBufferInfo.mBuffer->getBuffer(); + compositionState->buffer = getBuffer(); compositionState->bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) ? 0 : mBufferInfo.mBufferSlot; @@ -319,7 +319,6 @@ bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) { Mutex::Autolock lock(mFrameEventHistoryMutex); mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime); } - mRefreshPending = false; return hasReadyFrame(); } namespace { @@ -474,19 +473,6 @@ bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, return refreshRequired; } - if (!hasReadyFrame()) { - return false; - } - - // if we've already called updateTexImage() without going through - // a composition step, we have to skip this layer at this point - // because we cannot call updateTeximage() without a corresponding - // compositionComplete() call. - // we'll trigger an update in onPreComposition(). - if (mRefreshPending) { - return false; - } - // If the head buffer's acquire fence hasn't signaled yet, return and // try again later if (!fenceHasSignaled()) { @@ -518,7 +504,6 @@ bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, gatherBufferInfo(); - mRefreshPending = true; if (oldBufferInfo.mBuffer == nullptr) { // the first time we receive a buffer, we need to trigger a // geometry invalidation. @@ -689,7 +674,6 @@ FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const } void BufferLayer::latchAndReleaseBuffer() { - mRefreshPending = false; if (hasReadyFrame()) { bool ignored = false; latchBuffer(ignored, systemTime(), 0 /* expectedPresentTime */); diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index 99267be673..3e70493101 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -88,9 +88,6 @@ public: // to figure out if the content or size of a surface has changed. bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, nsecs_t expectedPresentTime) override; - - bool isBufferLatched() const override { return mRefreshPending; } - bool hasReadyFrame() const override; // Returns the current scaling mode @@ -158,9 +155,6 @@ protected: // from graphics API const uint32_t mTextureName; - - bool mRefreshPending{false}; - ui::Dataspace translateDataspace(ui::Dataspace dataspace); void setInitialValuesForClone(const sp<Layer>& clonedFrom); void updateCloneBufferInfo() override; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h index 16aebef9f3..ee0c53ded7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -26,8 +26,18 @@ namespace android::compositionengine::mock { // Defines the interface used by the CompositionEngine to make requests // of the front-end layer. class LayerFE : public compositionengine::LayerFE { -public: +private: + // Making the constructor private as this class implements RefBase, + // and constructing it with a different way than sp<LayerFE>::make() causes + // a memory leak of the shared state. LayerFE(); + + // friends class to allow instantiation via sp<LayerFE>::make() and + // sp<StrictMock<LayerFE>>::make() + friend class sp<LayerFE>; + friend class testing::StrictMock<LayerFE>; + +public: virtual ~LayerFE(); MOCK_CONST_METHOD0(getCompositionState, const LayerFECompositionState*()); diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index 1ea13a7b2d..29c146b013 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -59,9 +59,13 @@ void Display::setConfiguration(const compositionengine::DisplayCreationArgs& arg setName(args.name); bool isBootModeSupported = getCompositionEngine().getHwComposer().getBootDisplayModeSupport(); const auto physicalId = PhysicalDisplayId::tryCast(mId); - if (physicalId && isBootModeSupported) { - mPreferredBootDisplayModeId = static_cast<int32_t>( - getCompositionEngine().getHwComposer().getPreferredBootDisplayMode(*physicalId)); + if (!physicalId || !isBootModeSupported) { + return; + } + std::optional<hal::HWConfigId> preferredBootModeId = + getCompositionEngine().getHwComposer().getPreferredBootDisplayMode(*physicalId); + if (preferredBootModeId.has_value()) { + mPreferredBootDisplayModeId = static_cast<int32_t>(preferredBootModeId.value()); } } diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp index 325361b45a..de9de0150d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp @@ -203,9 +203,9 @@ TEST_F(CompositionEngineUpdateCursorAsyncTest, handlesMultipleLayersBeingCursorL */ struct CompositionTestPreComposition : public CompositionEngineTest { - sp<StrictMock<mock::LayerFE>> mLayer1FE{new StrictMock<mock::LayerFE>()}; - sp<StrictMock<mock::LayerFE>> mLayer2FE{new StrictMock<mock::LayerFE>()}; - sp<StrictMock<mock::LayerFE>> mLayer3FE{new StrictMock<mock::LayerFE>()}; + sp<StrictMock<mock::LayerFE>> mLayer1FE = sp<StrictMock<mock::LayerFE>>::make(); + sp<StrictMock<mock::LayerFE>> mLayer2FE = sp<StrictMock<mock::LayerFE>>::make(); + sp<StrictMock<mock::LayerFE>> mLayer3FE = sp<StrictMock<mock::LayerFE>>::make(); }; TEST_F(CompositionTestPreComposition, preCompositionSetsFrameTimestamp) { diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 03c6f8dd81..125ce7468e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -74,7 +74,7 @@ struct Layer { EXPECT_CALL(*outputLayer, getHwcLayer()).WillRepeatedly(Return(&hwc2Layer)); } - sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>(); + sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make(); StrictMock<mock::OutputLayer>* outputLayer = new StrictMock<mock::OutputLayer>(); StrictMock<HWC2::mock::Layer> hwc2Layer; }; @@ -85,7 +85,7 @@ struct LayerNoHWC2Layer { EXPECT_CALL(*outputLayer, getHwcLayer()).WillRepeatedly(Return(nullptr)); } - sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>(); + sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make(); StrictMock<mock::OutputLayer>* outputLayer = new StrictMock<mock::OutputLayer>(); }; @@ -469,7 +469,7 @@ TEST_F(DisplayCreateRenderSurfaceTest, setsRenderSurface) { using DisplayCreateOutputLayerTest = FullDisplayImplTestCommon; TEST_F(DisplayCreateOutputLayerTest, setsHwcLayer) { - sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>(); + sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make(); auto hwcLayer = std::make_shared<StrictMock<HWC2::mock::Layer>>(); EXPECT_CALL(mHwComposer, createLayer(HalDisplayId(DEFAULT_DISPLAY_ID))) diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index bcd4dc2c66..660f66438f 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -108,7 +108,7 @@ public: hal::VsyncPeriodChangeTimeline*)); MOCK_METHOD2(setBootDisplayMode, status_t(PhysicalDisplayId, hal::HWConfigId)); MOCK_METHOD1(clearBootDisplayMode, status_t(PhysicalDisplayId)); - MOCK_METHOD1(getPreferredBootDisplayMode, hal::HWConfigId(PhysicalDisplayId)); + MOCK_METHOD1(getPreferredBootDisplayMode, std::optional<hal::HWConfigId>(PhysicalDisplayId)); MOCK_METHOD0(getBootDisplayModeSupport, bool()); MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool)); MOCK_METHOD2(getSupportedContentTypes, diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index 0b123b12d1..82dcc66833 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -109,8 +109,8 @@ struct OutputLayerTest : public testing::Test { } compositionengine::mock::Output mOutput; - sp<compositionengine::mock::LayerFE> mLayerFE{ - new StrictMock<compositionengine::mock::LayerFE>()}; + sp<StrictMock<compositionengine::mock::LayerFE>> mLayerFE = + sp<StrictMock<compositionengine::mock::LayerFE>>::make(); OutputLayer mOutputLayer{mOutput, mLayerFE}; LayerFECompositionState mLayerFEState; diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index f7d5991fef..e72bc9f66d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -106,7 +106,7 @@ struct InjectedLayer { } mock::OutputLayer* outputLayer = {new StrictMock<mock::OutputLayer>}; - sp<StrictMock<mock::LayerFE>> layerFE = new StrictMock<mock::LayerFE>(); + sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make(); LayerFECompositionState layerFEState; impl::OutputLayerCompositionState outputLayerState; }; @@ -123,7 +123,7 @@ struct NonInjectedLayer { } mock::OutputLayer outputLayer; - sp<StrictMock<mock::LayerFE>> layerFE = new StrictMock<mock::LayerFE>(); + sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make(); LayerFECompositionState layerFEState; impl::OutputLayerCompositionState outputLayerState; }; @@ -722,9 +722,9 @@ TEST_F(OutputTest, getOutputLayerForLayerWorks) { using OutputSetReleasedLayersTest = OutputTest; TEST_F(OutputSetReleasedLayersTest, setReleasedLayersTakesGivenLayers) { - sp<StrictMock<mock::LayerFE>> layer1FE{new StrictMock<mock::LayerFE>()}; - sp<StrictMock<mock::LayerFE>> layer2FE{new StrictMock<mock::LayerFE>()}; - sp<StrictMock<mock::LayerFE>> layer3FE{new StrictMock<mock::LayerFE>()}; + sp<StrictMock<mock::LayerFE>> layer1FE = sp<StrictMock<mock::LayerFE>>::make(); + sp<StrictMock<mock::LayerFE>> layer2FE = sp<StrictMock<mock::LayerFE>>::make(); + sp<StrictMock<mock::LayerFE>> layer3FE = sp<StrictMock<mock::LayerFE>>::make(); Output::ReleasedLayers layers; layers.push_back(layer1FE); @@ -1209,7 +1209,7 @@ struct OutputCollectVisibleLayersTest : public testing::Test { StrictMock<mock::OutputLayer> outputLayer; impl::OutputLayerCompositionState outputLayerState; - sp<StrictMock<mock::LayerFE>> layerFE{new StrictMock<mock::LayerFE>()}; + sp<StrictMock<mock::LayerFE>> layerFE = sp<StrictMock<mock::LayerFE>>::make(); }; OutputCollectVisibleLayersTest() { @@ -2987,9 +2987,9 @@ TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) { EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); // Load up the released layers with some mock instances - sp<StrictMock<mock::LayerFE>> releasedLayer1{new StrictMock<mock::LayerFE>()}; - sp<StrictMock<mock::LayerFE>> releasedLayer2{new StrictMock<mock::LayerFE>()}; - sp<StrictMock<mock::LayerFE>> releasedLayer3{new StrictMock<mock::LayerFE>()}; + sp<StrictMock<mock::LayerFE>> releasedLayer1 = sp<StrictMock<mock::LayerFE>>::make(); + sp<StrictMock<mock::LayerFE>> releasedLayer2 = sp<StrictMock<mock::LayerFE>>::make(); + sp<StrictMock<mock::LayerFE>> releasedLayer3 = sp<StrictMock<mock::LayerFE>>::make(); Output::ReleasedLayers layers; layers.push_back(releasedLayer1); layers.push_back(releasedLayer2); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp index 84b3fc5698..0b1c2626a1 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp @@ -107,7 +107,7 @@ struct LayerStateTest : public testing::Test { EXPECT_EQ(fields, rhs.getDifferingFields(lhs)); } - mock::LayerFE mLayerFE; + sp<mock::LayerFE> mLayerFE = sp<mock::LayerFE>::make(); mock::OutputLayer mOutputLayer; std::unique_ptr<LayerState> mLayerState; }; @@ -115,7 +115,7 @@ struct LayerStateTest : public testing::Test { TEST_F(LayerStateTest, getOutputLayer) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); EXPECT_EQ(&mOutputLayer, mLayerState->getOutputLayer()); @@ -124,14 +124,14 @@ TEST_F(LayerStateTest, getOutputLayer) { TEST_F(LayerStateTest, updateOutputLayer) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); EXPECT_EQ(&mOutputLayer, mLayerState->getOutputLayer()); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState->update(&newOutputLayer); EXPECT_EQ(&newOutputLayer, mLayerState->getOutputLayer()); @@ -140,7 +140,7 @@ TEST_F(LayerStateTest, updateOutputLayer) { TEST_F(LayerStateTest, getId) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); EXPECT_EQ(sSequenceId, mLayerState->getId()); @@ -149,13 +149,13 @@ TEST_F(LayerStateTest, getId) { TEST_F(LayerStateTest, updateId) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionState, sSequenceIdTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(sSequenceIdTwo, mLayerState->getId()); @@ -165,12 +165,12 @@ TEST_F(LayerStateTest, updateId) { TEST_F(LayerStateTest, compareId) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionState, sSequenceIdTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -185,7 +185,7 @@ TEST_F(LayerStateTest, compareId) { TEST_F(LayerStateTest, getName) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); EXPECT_EQ(sDebugName, mLayerState->getName()); @@ -194,13 +194,13 @@ TEST_F(LayerStateTest, getName) { TEST_F(LayerStateTest, updateName) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionState, sSequenceId, sDebugNameTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(sDebugNameTwo, mLayerState->getName()); @@ -210,12 +210,12 @@ TEST_F(LayerStateTest, updateName) { TEST_F(LayerStateTest, compareName) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionState, sSequenceId, sDebugNameTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -231,7 +231,7 @@ TEST_F(LayerStateTest, getDisplayFrame) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.displayFrame = sRectOne; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); EXPECT_EQ(sRectOne, mLayerState->getDisplayFrame()); @@ -241,15 +241,15 @@ TEST_F(LayerStateTest, updateDisplayFrame) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.displayFrame = sRectOne; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.displayFrame = sRectTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(sRectTwo, mLayerState->getDisplayFrame()); @@ -260,14 +260,14 @@ TEST_F(LayerStateTest, compareDisplayFrame) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.displayFrame = sRectOne; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.displayFrame = sRectTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -282,7 +282,7 @@ TEST_F(LayerStateTest, getCompositionType) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.compositionType = Composition::DEVICE; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); EXPECT_EQ(Composition::DEVICE, mLayerState->getCompositionType()); @@ -293,7 +293,7 @@ TEST_F(LayerStateTest, getCompositionType_forcedClient) { outputLayerCompositionState.forceClientComposition = true; LayerFECompositionState layerFECompositionState; layerFECompositionState.compositionType = Composition::DEVICE; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); EXPECT_EQ(Composition::CLIENT, mLayerState->getCompositionType()); @@ -303,15 +303,15 @@ TEST_F(LayerStateTest, updateCompositionType) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.compositionType = Composition::DEVICE; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Composition::SOLID_COLOR, mLayerState->getCompositionType()); @@ -322,14 +322,14 @@ TEST_F(LayerStateTest, compareCompositionType) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.compositionType = Composition::DEVICE; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -345,15 +345,15 @@ TEST_F(LayerStateTest, updateBuffer) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.buffer = new GraphicBuffer(); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.buffer = new GraphicBuffer(); - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer), updates); @@ -363,14 +363,14 @@ TEST_F(LayerStateTest, compareBuffer) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.buffer = new GraphicBuffer(); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.buffer = new GraphicBuffer(); - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -386,15 +386,15 @@ TEST_F(LayerStateTest, updateSourceCrop) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.sourceCrop = sFloatRectOne; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.sourceCrop = sFloatRectTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SourceCrop), updates); @@ -404,14 +404,14 @@ TEST_F(LayerStateTest, compareSourceCrop) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.sourceCrop = sFloatRectOne; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.sourceCrop = sFloatRectTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -425,15 +425,15 @@ TEST_F(LayerStateTest, updateBufferTransform) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.bufferTransform = Hwc2::Transform::FLIP_V; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BufferTransform), updates); @@ -443,14 +443,14 @@ TEST_F(LayerStateTest, compareBufferTransform) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.bufferTransform = Hwc2::Transform::FLIP_H; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.bufferTransform = Hwc2::Transform::FLIP_V; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -465,15 +465,15 @@ TEST_F(LayerStateTest, updateBlendMode) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.blendMode = hal::BlendMode::COVERAGE; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlendMode), updates); @@ -483,14 +483,14 @@ TEST_F(LayerStateTest, compareBlendMode) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.blendMode = hal::BlendMode::COVERAGE; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -504,15 +504,15 @@ TEST_F(LayerStateTest, updateAlpha) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.alpha = sAlphaOne; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.alpha = sAlphaTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Alpha), updates); @@ -522,14 +522,14 @@ TEST_F(LayerStateTest, compareAlpha) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.alpha = sAlphaOne; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.alpha = sAlphaTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -543,15 +543,15 @@ TEST_F(LayerStateTest, updateLayerMetadata) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.metadata[sMetadataKeyOne] = sMetadataValueOne; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.metadata[sMetadataKeyTwo] = sMetadataValueTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::LayerMetadata), updates); @@ -561,14 +561,14 @@ TEST_F(LayerStateTest, compareLayerMetadata) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.metadata[sMetadataKeyOne] = sMetadataValueOne; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.metadata[sMetadataKeyTwo] = sMetadataValueTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -582,7 +582,7 @@ TEST_F(LayerStateTest, getVisibleRegion) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.visibleRegion = sRegionOne; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); EXPECT_TRUE(mLayerState->getVisibleRegion().hasSameRects(sRegionOne)); @@ -592,15 +592,15 @@ TEST_F(LayerStateTest, updateVisibleRegion) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.visibleRegion = sRegionOne; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.visibleRegion = sRegionTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::VisibleRegion), updates); @@ -610,14 +610,14 @@ TEST_F(LayerStateTest, compareVisibleRegion) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.visibleRegion = sRegionOne; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.visibleRegion = sRegionTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -631,15 +631,15 @@ TEST_F(LayerStateTest, updateDataspace) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.dataspace = ui::Dataspace::SRGB; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.dataspace = ui::Dataspace::DISPLAY_P3; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Dataspace), updates); @@ -649,14 +649,14 @@ TEST_F(LayerStateTest, compareDataspace) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.dataspace = ui::Dataspace::SRGB; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.dataspace = ui::Dataspace::DISPLAY_P3; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -674,19 +674,19 @@ TEST_F(LayerStateTest, updatePixelFormat) { AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, "buffer1"); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.buffer = new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, "buffer2"); - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::Buffer) | @@ -702,18 +702,18 @@ TEST_F(LayerStateTest, comparePixelFormat) { AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, "buffer1"); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.buffer = new GraphicBuffer(1, 1, PIXEL_FORMAT_RGBX_8888, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, "buffer2"); - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -729,16 +729,16 @@ TEST_F(LayerStateTest, updateColorTransform) { LayerFECompositionState layerFECompositionState; layerFECompositionState.colorTransformIsIdentity = true; layerFECompositionState.colorTransform = mat4(); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.colorTransformIsIdentity = false; layerFECompositionStateTwo.colorTransform = sMat4One; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::ColorTransform), updates); @@ -749,15 +749,15 @@ TEST_F(LayerStateTest, compareColorTransform) { LayerFECompositionState layerFECompositionState; layerFECompositionState.colorTransformIsIdentity = true; layerFECompositionState.colorTransform = mat4(); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.colorTransformIsIdentity = false; layerFECompositionStateTwo.colorTransform = sMat4One; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -771,15 +771,15 @@ TEST_F(LayerStateTest, updateSidebandStream) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.sidebandStream = NativeHandle::create(sFakeSidebandStreamOne, false); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.sidebandStream = NativeHandle::create(sFakeSidebandStreamTwo, false); - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SidebandStream), updates); @@ -789,14 +789,14 @@ TEST_F(LayerStateTest, compareSidebandStream) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.sidebandStream = NativeHandle::create(sFakeSidebandStreamOne, false); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.sidebandStream = NativeHandle::create(sFakeSidebandStreamTwo, false); - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -810,15 +810,15 @@ TEST_F(LayerStateTest, updateSolidColor) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.color = sHalf4One; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.color = sHalf4Two; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::SolidColor), updates); @@ -828,14 +828,14 @@ TEST_F(LayerStateTest, compareSolidColor) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.color = sHalf4One; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.color = sHalf4Two; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -849,15 +849,15 @@ TEST_F(LayerStateTest, updateBackgroundBlur) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.backgroundBlurRadius = sBgBlurRadiusTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BackgroundBlurRadius), updates); @@ -867,14 +867,14 @@ TEST_F(LayerStateTest, compareBackgroundBlur) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.backgroundBlurRadius = sBgBlurRadiusTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -889,15 +889,15 @@ TEST_F(LayerStateTest, updateBlurRegions) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.blurRegions.push_back(sBlurRegionOne); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.blurRegions.push_back(sBlurRegionTwo); - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); EXPECT_EQ(Flags<LayerStateField>(LayerStateField::BlurRegions), updates); @@ -907,14 +907,14 @@ TEST_F(LayerStateTest, compareBlurRegions) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.blurRegions.push_back(sBlurRegionOne); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.blurRegions.push_back(sBlurRegionTwo); - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -927,7 +927,7 @@ TEST_F(LayerStateTest, compareBlurRegions) { TEST_F(LayerStateTest, hasBlurBehind_noBlur_returnsFalse) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); EXPECT_FALSE(mLayerState->hasBlurBehind()); @@ -937,7 +937,7 @@ TEST_F(LayerStateTest, hasBlurBehind_withBackgroundBlur_returnsTrue) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.backgroundBlurRadius = sBgBlurRadiusOne; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); EXPECT_TRUE(mLayerState->hasBlurBehind()); @@ -947,7 +947,7 @@ TEST_F(LayerStateTest, hasBlurBehind_withBlurRegion_returnsTrue) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.blurRegions.push_back(sBlurRegionOne); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); EXPECT_TRUE(mLayerState->hasBlurBehind()); @@ -956,7 +956,7 @@ TEST_F(LayerStateTest, hasBlurBehind_withBlurRegion_returnsTrue) { TEST_F(LayerStateTest, dumpDoesNotCrash) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); std::string dump; @@ -967,7 +967,7 @@ TEST_F(LayerStateTest, dumpDoesNotCrash) { TEST_F(LayerStateTest, framesSinceBufferUpdate) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); @@ -982,14 +982,14 @@ TEST_F(LayerStateTest, getNonBufferHash_doesNotCommute) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.displayFrame = sRectOne; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.displayFrame = sRectTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -1001,14 +1001,14 @@ TEST_F(LayerStateTest, getNonBufferHash_isIdempotent) { OutputLayerCompositionState outputLayerCompositionState; outputLayerCompositionState.displayFrame = sRectOne; LayerFECompositionState layerFECompositionState; - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; outputLayerCompositionStateTwo.displayFrame = sRectTwo; - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionStateTwo, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionStateTwo, layerFECompositionState); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); @@ -1020,15 +1020,15 @@ TEST_F(LayerStateTest, getNonBufferHash_filtersOutBuffers) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; layerFECompositionState.buffer = new GraphicBuffer(); - setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState, + setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState, layerFECompositionState); mLayerState = std::make_unique<LayerState>(&mOutputLayer); mock::OutputLayer newOutputLayer; - mock::LayerFE newLayerFE; + sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make(); LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.buffer = new GraphicBuffer(); - setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState, + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, layerFECompositionStateTwo); auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp index 6038268a8c..68c72e0945 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp @@ -73,26 +73,26 @@ struct LayerStackTest : public testing::Test { TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchSizeDifferences) { mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne; LayerFECompositionState layerFECompositionStateOne; - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); mock::OutputLayer outputLayerTwo; - mock::LayerFE layerFETwo; + sp<mock::LayerFE> layerFETwo = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; LayerFECompositionState layerFECompositionStateTwo; - setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); mock::OutputLayer outputLayerThree; - mock::LayerFE layerFEThree; + sp<mock::LayerFE> layerFEThree = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateThree; LayerFECompositionState layerFECompositionStateThree; - setupMocksForLayer(outputLayerThree, layerFEThree, outputLayerCompositionStateThree, + setupMocksForLayer(outputLayerThree, *layerFEThree, outputLayerCompositionStateThree, layerFECompositionStateThree); LayerState layerStateThree(&outputLayerThree); @@ -104,20 +104,20 @@ TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchSizeDifferences) { TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchDifferentCompositionTypes) { mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne; LayerFECompositionState layerFECompositionStateOne; layerFECompositionStateOne.compositionType = Composition::DEVICE; - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); mock::OutputLayer outputLayerTwo; - mock::LayerFE layerFETwo; + sp<mock::LayerFE> layerFETwo = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR; - setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); @@ -128,22 +128,22 @@ TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchDifferentCompositionTypes TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInSingleLayer) { mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne{ .sourceCrop = sFloatRectOne, }; LayerFECompositionState layerFECompositionStateOne; - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); mock::OutputLayer outputLayerTwo; - mock::LayerFE layerFETwo; + sp<mock::LayerFE> layerFETwo = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo{ .sourceCrop = sFloatRectTwo, }; LayerFECompositionState layerFECompositionStateTwo; - setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); @@ -159,22 +159,22 @@ TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInSingleLayer) TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInMultiLayerStack) { mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne{ .sourceCrop = sFloatRectOne, }; LayerFECompositionState layerFECompositionStateOne; - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); mock::OutputLayer outputLayerTwo; - mock::LayerFE layerFETwo; + sp<mock::LayerFE> layerFETwo = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo{ .sourceCrop = sFloatRectTwo, }; LayerFECompositionState layerFECompositionStateTwo; - setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); @@ -190,7 +190,7 @@ TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInMultiLayerSt TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchManyDifferences) { mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne{ .visibleRegion = sRegionOne, .displayFrame = sRectOne, @@ -201,12 +201,12 @@ TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchManyDifferences) { layerFECompositionStateOne.alpha = sAlphaOne; layerFECompositionStateOne.colorTransformIsIdentity = true; layerFECompositionStateOne.blendMode = hal::BlendMode::NONE; - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); mock::OutputLayer outputLayerTwo; - mock::LayerFE layerFETwo; + sp<mock::LayerFE> layerFETwo = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo{ .visibleRegion = sRegionTwo, .displayFrame = sRectTwo, @@ -218,7 +218,7 @@ TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchManyDifferences) { layerFECompositionStateTwo.colorTransformIsIdentity = false; layerFECompositionStateTwo.colorTransform = sMat4One; layerFECompositionStateTwo.blendMode = hal::BlendMode::PREMULTIPLIED; - setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); @@ -230,20 +230,20 @@ TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchManyDifferences) { TEST_F(LayerStackTest, getApproximateMatch_exactMatchesSameBuffer) { sp<GraphicBuffer> buffer = new GraphicBuffer(); mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne; LayerFECompositionState layerFECompositionStateOne; layerFECompositionStateOne.buffer = buffer; - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); mock::OutputLayer outputLayerTwo; - mock::LayerFE layerFETwo; + sp<mock::LayerFE> layerFETwo = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo; LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.buffer = buffer; - setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); @@ -259,7 +259,7 @@ TEST_F(LayerStackTest, getApproximateMatch_exactMatchesSameBuffer) { TEST_F(LayerStackTest, getApproximateMatch_alwaysMatchesClientComposition) { mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne{ .visibleRegion = sRegionOne, .forceClientComposition = true, @@ -271,12 +271,12 @@ TEST_F(LayerStackTest, getApproximateMatch_alwaysMatchesClientComposition) { layerFECompositionStateOne.buffer = new GraphicBuffer(); layerFECompositionStateOne.alpha = sAlphaOne; layerFECompositionStateOne.colorTransformIsIdentity = true; - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); mock::OutputLayer outputLayerTwo; - mock::LayerFE layerFETwo; + sp<mock::LayerFE> layerFETwo = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo{ .visibleRegion = sRegionTwo, .forceClientComposition = true, @@ -289,7 +289,7 @@ TEST_F(LayerStackTest, getApproximateMatch_alwaysMatchesClientComposition) { layerFECompositionStateTwo.alpha = sAlphaTwo; layerFECompositionStateTwo.colorTransformIsIdentity = false; layerFECompositionStateTwo.colorTransform = sMat4One; - setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); @@ -305,24 +305,24 @@ TEST_F(LayerStackTest, getApproximateMatch_alwaysMatchesClientComposition) { TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchMultipleApproximations) { mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne{ .sourceCrop = sFloatRectOne, }; LayerFECompositionState layerFECompositionStateOne; layerFECompositionStateOne.buffer = new GraphicBuffer(); - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); mock::OutputLayer outputLayerTwo; - mock::LayerFE layerFETwo; + sp<mock::LayerFE> layerFETwo = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo{ .sourceCrop = sFloatRectTwo, }; LayerFECompositionState layerFECompositionStateTwo; layerFECompositionStateTwo.buffer = new GraphicBuffer(); - setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); @@ -348,22 +348,22 @@ struct PredictionTest : public testing::Test { TEST_F(LayerStackTest, reorderingChangesNonBufferHash) { mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne{ .sourceCrop = sFloatRectOne, }; LayerFECompositionState layerFECompositionStateOne; - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); mock::OutputLayer outputLayerTwo; - mock::LayerFE layerFETwo; + sp<mock::LayerFE> layerFETwo = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo{ .sourceCrop = sFloatRectTwo, }; LayerFECompositionState layerFECompositionStateTwo; - setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); @@ -443,11 +443,11 @@ TEST_F(PredictorTest, getPredictedPlan_emptyLayersWithoutExactMatch_returnsNullo TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveExactMatch) { mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne; LayerFECompositionState layerFECompositionStateOne; layerFECompositionStateOne.compositionType = Composition::DEVICE; - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); @@ -468,22 +468,22 @@ TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveExactMatch) { TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveApproximateMatch) { mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne{ .sourceCrop = sFloatRectOne, }; LayerFECompositionState layerFECompositionStateOne; - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); mock::OutputLayer outputLayerTwo; - mock::LayerFE layerFETwo; + sp<mock::LayerFE> layerFETwo = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo{ .sourceCrop = sFloatRectTwo, }; LayerFECompositionState layerFECompositionStateTwo; - setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); @@ -505,22 +505,22 @@ TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveApproximateMatc TEST_F(PredictorTest, recordMissedPlan_skipsApproximateMatch) { mock::OutputLayer outputLayerOne; - mock::LayerFE layerFEOne; + sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateOne{ .sourceCrop = sFloatRectOne, }; LayerFECompositionState layerFECompositionStateOne; - setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne, + setupMocksForLayer(outputLayerOne, *layerFEOne, outputLayerCompositionStateOne, layerFECompositionStateOne); LayerState layerStateOne(&outputLayerOne); mock::OutputLayer outputLayerTwo; - mock::LayerFE layerFETwo; + sp<mock::LayerFE> layerFETwo = sp<mock::LayerFE>::make(); OutputLayerCompositionState outputLayerCompositionStateTwo{ .sourceCrop = sFloatRectTwo, }; LayerFECompositionState layerFECompositionStateTwo; - setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo, + setupMocksForLayer(outputLayerTwo, *layerFETwo, outputLayerCompositionStateTwo, layerFECompositionStateTwo); LayerState layerStateTwo(&outputLayerTwo); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 9174ec78c9..2696bd8207 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -772,18 +772,16 @@ status_t HWComposer::clearBootDisplayMode(PhysicalDisplayId displayId) { return NO_ERROR; } -hal::HWConfigId HWComposer::getPreferredBootDisplayMode(PhysicalDisplayId displayId) { - RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); - hal::HWConfigId displayModeId = -1; +std::optional<hal::HWConfigId> HWComposer::getPreferredBootDisplayMode( + PhysicalDisplayId displayId) { + RETURN_IF_INVALID_DISPLAY(displayId, std::nullopt); + hal::HWConfigId displayModeId; const auto error = mDisplayData[displayId].hwcDisplay->getPreferredBootDisplayConfig(&displayModeId); - if (error == hal::Error::UNSUPPORTED) { - RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); - } - if (error == hal::Error::BAD_PARAMETER) { - RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); + if (error != hal::Error::NONE) { + LOG_DISPLAY_ERROR(displayId, to_string(error).c_str()); + return std::nullopt; } - RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); return displayModeId; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 3e680280ae..29335d56e5 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -260,7 +260,7 @@ public: virtual bool getBootDisplayModeSupport() = 0; virtual status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) = 0; virtual status_t clearBootDisplayMode(PhysicalDisplayId) = 0; - virtual hal::HWConfigId getPreferredBootDisplayMode(PhysicalDisplayId) = 0; + virtual std::optional<hal::HWConfigId> getPreferredBootDisplayMode(PhysicalDisplayId) = 0; }; namespace impl { @@ -391,7 +391,7 @@ public: bool getBootDisplayModeSupport() override; status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) override; status_t clearBootDisplayMode(PhysicalDisplayId) override; - hal::HWConfigId getPreferredBootDisplayMode(PhysicalDisplayId) override; + std::optional<hal::HWConfigId> getPreferredBootDisplayMode(PhysicalDisplayId) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 645d4d1b44..a039250327 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1398,7 +1398,6 @@ LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const { } } info.mNumQueuedFrames = getQueuedFrameCount(); - info.mRefreshPending = isBufferLatched(); info.mIsOpaque = isOpaque(ds); info.mContentDirty = contentDirty; info.mStretchEffect = getStretchEffect(); @@ -2029,7 +2028,6 @@ void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags, layerInfo->set_is_protected(isProtected()); layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace()))); layerInfo->set_queued_frames(getQueuedFrameCount()); - layerInfo->set_refresh_pending(isBufferLatched()); layerInfo->set_curr_frame(mCurrentFrameNumber); layerInfo->set_effective_scaling_mode(getEffectiveScalingMode()); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 605a27efa7..ddcd641461 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -549,8 +549,6 @@ public: return false; } - virtual bool isBufferLatched() const { return false; } - virtual void latchAndReleaseBuffer() {} /* diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 61da45de88..b3c3a4147b 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2523,6 +2523,7 @@ void SurfaceFlinger::postComposition() { mTimeStats->recordDisplayEventConnectionCount(sfConnections + appConnections); if (isDisplayConnected && !display->isPoweredOn()) { + getRenderEngine().cleanupPostRender(); return; } @@ -3562,11 +3563,9 @@ bool SurfaceFlinger::latchBuffers() { for (const auto& layer : mLayersWithQueuedFrames) { if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) { mLayersPendingRefresh.push_back(layer); - } - layer->useSurfaceDamage(); - if (layer->isBufferLatched()) { newDataLatched = true; } + layer->useSurfaceDamage(); } } @@ -3914,7 +3913,8 @@ void SurfaceFlinger::queueTransaction(TransactionState& state) { // been applied by SF if (state.flags & eAnimation) { while (itr != mPendingTransactionQueues.end()) { - status_t err = mTransactionQueueCV.waitRelative(mQueueLock, s2ns(5)); + status_t err = + mTransactionQueueCV.waitRelative(mQueueLock, mAnimationTransactionTimeout); if (CC_UNLIKELY(err != NO_ERROR)) { ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out " @@ -3950,7 +3950,8 @@ void SurfaceFlinger::waitForSynchronousTransaction( // applyTransactionState is called on the main SF thread. While a given process may wish // to wait on synchronous transactions, the main SF thread should apply the transaction and // set the value to notify this after committed. - if (!transactionCommittedSignal.wait_until(std::chrono::seconds(5))) { + if (!transactionCommittedSignal.wait_until( + std::chrono::nanoseconds(mAnimationTransactionTimeout))) { ALOGE("setTransactionState timed out!"); } } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index c6a4d8501d..77193a6939 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1372,6 +1372,8 @@ private: nsecs_t compositeStart; nsecs_t presentEnd; } mPowerHintSessionData GUARDED_BY(SF_MAIN_THREAD); + + nsecs_t mAnimationTransactionTimeout = s2ns(5); }; } // namespace android diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h index fe3f3fcfd6..04ca347b2f 100644 --- a/services/surfaceflinger/TransactionState.h +++ b/services/surfaceflinger/TransactionState.h @@ -93,7 +93,7 @@ public: } // Return true if triggered. - bool wait_until(const std::chrono::seconds& timeout) const { + bool wait_until(const std::chrono::nanoseconds& timeout) const { std::unique_lock<std::mutex> lock(mMutex); const auto untilTime = std::chrono::system_clock::now() + timeout; while (mFlags != 0) { diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp index 7eebd9b3d8..b0d216e5cd 100644 --- a/services/surfaceflinger/fuzzer/Android.bp +++ b/services/surfaceflinger/fuzzer/Android.bp @@ -78,3 +78,27 @@ cc_fuzz { "surfaceflinger_fuzzer.cpp", ], } + +cc_fuzz { + name: "surfaceflinger_displayhardware_fuzzer", + defaults: [ + "surfaceflinger_fuzz_defaults", + ], + srcs: [ + "surfaceflinger_displayhardware_fuzzer.cpp", + ], + header_libs: [ + "android.hardware.graphics.composer@2.4-command-buffer", + "android.hardware.graphics.composer@2.4-hal", + ], +} + +cc_fuzz { + name: "surfaceflinger_scheduler_fuzzer", + defaults: [ + "surfaceflinger_fuzz_defaults", + ], + srcs: [ + "surfaceflinger_scheduler_fuzzer.cpp", + ], +} diff --git a/services/surfaceflinger/fuzzer/README.md b/services/surfaceflinger/fuzzer/README.md index 7b244fcdbe..6231ca5536 100644 --- a/services/surfaceflinger/fuzzer/README.md +++ b/services/surfaceflinger/fuzzer/README.md @@ -1,6 +1,8 @@ # Fuzzers for SurfaceFlinger ## Table of contents + [SurfaceFlinger](#SurfaceFlinger) ++ [DisplayHardware](#DisplayHardware) ++ [Scheduler](#Scheduler) # <a name="SurfaceFlinger"></a> Fuzzer for SurfaceFlinger @@ -22,3 +24,49 @@ You can find the possible values in the fuzzer's source code. $ adb sync data $ adb shell /data/fuzz/arm64/surfaceflinger_fuzzer/surfaceflinger_fuzzer ``` + +# <a name="DisplayHardware"></a> Fuzzer for DisplayHardware + +DisplayHardware supports the following parameters: +1. Hal Capability (parameter name: `hasCapability`) +2. Hal BlendMode (parameter name: `setBlendMode`) +3. Hal Composition (parameter name: `setCompositionType`) +4. Hal Display Capability (parameter name: `hasDisplayCapability`) +5. Composition Types (parameter name: `prepareFrame`) +6. Color Modes (parameter name: `setActiveColorMode`) +7. Render Intents (parameter name: `setActiveColorMode`) +8. Power Modes (parameter name: `setPowerMode`) +9. Content Types (parameter name: `setContentType`) +10. Data Space (parameter name: `setDataspace`) +11. Transforms (parameter name: `setLayerTransform`) + +You can find the possible values in the fuzzer's source code. + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) surfaceflinger_displayhardware_fuzzer +``` +2. Run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/surfaceflinger_displayhardware_fuzzer/surfaceflinger_displayhardware_fuzzer +``` + +# <a name="Scheduler"></a> Fuzzer for Scheduler + +Scheduler supports the following parameters: +1. VSync Periods (parameter name: `lowFpsPeriod`) + +You can find the possible values in the fuzzer's source code. + +#### Steps to run +1. Build the fuzzer +``` + $ mm -j$(nproc) surfaceflinger_scheduler_fuzzer +``` +2. To run on device +``` + $ adb sync data + $ adb shell /data/fuzz/arm64/surfaceflinger_scheduler_fuzzer/surfaceflinger_scheduler_fuzzer +``` diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp new file mode 100644 index 0000000000..816d2f1f71 --- /dev/null +++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp @@ -0,0 +1,657 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <compositionengine/impl/OutputCompositionState.h> +#include <fuzzer/FuzzedDataProvider.h> +#include <gui/BLASTBufferQueue.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/IProducerListener.h> +#include <gui/LayerDebugInfo.h> +#include <gui/SurfaceComposerClient.h> +#include <hidl/ServiceManagement.h> +#include <hwbinder/ProcessState.h> +#include <ui/DisplayIdentification.h> + +#include "DisplayHardware/AidlComposerHal.h" +#include "DisplayHardware/DisplayMode.h" +#include "DisplayHardware/FramebufferSurface.h" +#include "DisplayHardware/HWComposer.h" +#include "DisplayHardware/PowerAdvisor.h" +#include "DisplayHardware/VirtualDisplaySurface.h" +#include "SurfaceFlinger.h" +#include "surfaceflinger_displayhardware_fuzzer_utils.h" + +#include <FuzzableDataspaces.h> + +namespace android::fuzz { + +using namespace android::hardware::graphics::common; +using namespace android::hardware::graphics::composer; +namespace hal = android::hardware::graphics::composer::hal; +using Config = hal::V2_1::Config; +using Display = hal::V2_1::Display; +using RenderIntent = V1_1::RenderIntent; +using IComposerClient = hal::V2_4::IComposerClient; +using VsyncPeriodChangeTimeline = hal::V2_4::VsyncPeriodChangeTimeline; +using PerFrameMetadata = IComposerClient::PerFrameMetadata; +using PerFrameMetadataBlob = IComposerClient::PerFrameMetadataBlob; +using Vsync = IComposerClient::Vsync; + +static constexpr hal::Transform kTransforms[] = {hal::Transform::FLIP_H, hal::Transform::FLIP_V, + hal::Transform::ROT_90, hal::Transform::ROT_180, + hal::Transform::ROT_270}; + +static constexpr hal::Capability kCapability[] = {hal::Capability::INVALID, + hal::Capability::SIDEBAND_STREAM, + hal::Capability::SKIP_CLIENT_COLOR_TRANSFORM, + hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE}; + +static constexpr hal::BlendMode kBlendModes[] = {hal::BlendMode::INVALID, hal::BlendMode::NONE, + hal::BlendMode::PREMULTIPLIED, + hal::BlendMode::COVERAGE}; + +static constexpr Composition kCompositions[] = {Composition::INVALID, Composition::CLIENT, + Composition::DEVICE, Composition::SOLID_COLOR, + Composition::CURSOR, Composition::SIDEBAND}; + +static constexpr DisplayCapability kDisplayCapability[] = + {DisplayCapability::INVALID, + DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM, + DisplayCapability::DOZE, + DisplayCapability::BRIGHTNESS, + DisplayCapability::PROTECTED_CONTENTS, + DisplayCapability::AUTO_LOW_LATENCY_MODE}; + +static constexpr VirtualDisplaySurface::CompositionType kCompositionTypes[] = + {VirtualDisplaySurface::CompositionType::Unknown, + VirtualDisplaySurface::CompositionType::Gpu, VirtualDisplaySurface::CompositionType::Hwc, + VirtualDisplaySurface::CompositionType::Mixed}; + +static constexpr ui::RenderIntent kRenderIntents[] = {ui::RenderIntent::COLORIMETRIC, + ui::RenderIntent::ENHANCE, + ui::RenderIntent::TONE_MAP_COLORIMETRIC, + ui::RenderIntent::TONE_MAP_ENHANCE}; + +static constexpr hal::PowerMode kPowerModes[] = {hal::PowerMode::OFF, hal::PowerMode::DOZE, + hal::PowerMode::DOZE_SUSPEND, hal::PowerMode::ON, + hal::PowerMode::ON_SUSPEND}; + +static constexpr hal::ContentType kContentTypes[] = {hal::ContentType::NONE, + hal::ContentType::GRAPHICS, + hal::ContentType::PHOTO, + hal::ContentType::CINEMA, + hal::ContentType::GAME}; + +const unsigned char kInternalEdid[] = + "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00" + "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27" + "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" + "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30" + "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53" + "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe" + "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45"; + +static constexpr hal::HWConfigId kActiveConfig = 0; + +class DisplayHardwareFuzzer { +public: + DisplayHardwareFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) { + mPhysicalDisplayId = SurfaceComposerClient::getInternalDisplayId().value(); + }; + void process(); + +private: + void invokeComposer(); + void invokeDisplayIdentification(); + void invokeLayer(HWC2::Layer* layer); + void setSidebandStream(HWC2::Layer* layer); + void setCursorPosition(HWC2::Layer* layer); + void setBuffer(HWC2::Layer* layer); + void setSurfaceDamage(HWC2::Layer* layer); + void setDisplayFrame(HWC2::Layer* layer); + void setVisibleRegion(HWC2::Layer* layer); + void setLayerGenericMetadata(HWC2::Layer* layer); + void invokeFrameBufferSurface(); + void invokeVirtualDisplaySurface(); + void invokeAidlComposer(); + Display createVirtualDisplay(Hwc2::AidlComposer*); + void validateDisplay(Hwc2::AidlComposer*, Display); + void presentOrValidateDisplay(Hwc2::AidlComposer*, Display); + void setOutputBuffer(Hwc2::AidlComposer*, Display); + void setLayerSidebandStream(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); + void invokeComposerHal2_2(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); + void invokeComposerHal2_3(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); + void invokeComposerHal2_4(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); + void getDisplayVsyncPeriod(); + void setActiveModeWithConstraints(); + void getDisplayIdentificationData(); + void dumpHwc(); + void getDisplayedContentSamplingAttributes(HalDisplayId); + void getDeviceCompositionChanges(HalDisplayId); + void getHdrCapabilities(HalDisplayId); + void getDisplayedContentSample(HalDisplayId); + void getSupportedContentTypes(); + ui::Size getFuzzedSize(); + mat4 getFuzzedMatrix(); + + DisplayIdGenerator<HalVirtualDisplayId> mGenerator; + FuzzedDataProvider mFdp; + PhysicalDisplayId mPhysicalDisplayId; + android::impl::HWComposer mHwc{std::make_unique<Hwc2::mock::Composer>()}; +}; + +void DisplayHardwareFuzzer::validateDisplay(Hwc2::AidlComposer* composer, Display display) { + uint32_t outNumTypes, outNumRequests; + composer->validateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), &outNumTypes, + &outNumRequests); +} + +void DisplayHardwareFuzzer::presentOrValidateDisplay(Hwc2::AidlComposer* composer, + Display display) { + int32_t outPresentFence; + uint32_t outNumTypes, outNumRequests, state; + composer->presentOrValidateDisplay(display, mFdp.ConsumeIntegral<nsecs_t>(), &outNumTypes, + &outNumRequests, &outPresentFence, &state); +} + +void DisplayHardwareFuzzer::setOutputBuffer(Hwc2::AidlComposer* composer, Display display) { + const native_handle_t buffer{}; + composer->setOutputBuffer(display, &buffer, mFdp.ConsumeIntegral<int32_t>() /*releaseFence*/); +} + +void DisplayHardwareFuzzer::setLayerSidebandStream(Hwc2::AidlComposer* composer, Display display, + Hwc2::V2_4::hal::Layer outLayer) { + const native_handle_t stream{}; + composer->setLayerSidebandStream(display, outLayer, &stream); +} + +Display DisplayHardwareFuzzer::createVirtualDisplay(Hwc2::AidlComposer* composer) { + namespace types = hardware::graphics::common; + using types::V1_2::PixelFormat; + PixelFormat format{}; + Display display; + composer->createVirtualDisplay(mFdp.ConsumeIntegral<uint32_t>() /*width*/, + mFdp.ConsumeIntegral<uint32_t>() /*height*/, &format, &display); + return display; +} + +void DisplayHardwareFuzzer::getDisplayVsyncPeriod() { + nsecs_t outVsyncPeriod; + mHwc.getDisplayVsyncPeriod(mPhysicalDisplayId, &outVsyncPeriod); +} + +void DisplayHardwareFuzzer::setActiveModeWithConstraints() { + hal::VsyncPeriodChangeTimeline outTimeline; + mHwc.setActiveModeWithConstraints(mPhysicalDisplayId, kActiveConfig, {} /*constraints*/, + &outTimeline); +} + +void DisplayHardwareFuzzer::getDisplayIdentificationData() { + uint8_t outPort; + DisplayIdentificationData outData; + mHwc.getDisplayIdentificationData(kHwDisplayId, &outPort, &outData); +} + +void DisplayHardwareFuzzer::dumpHwc() { + std::string string = mFdp.ConsumeRandomLengthString().c_str(); + mHwc.dump(string); +} + +void DisplayHardwareFuzzer::getDeviceCompositionChanges(HalDisplayId halDisplayID) { + std::optional<impl::HWComposer::DeviceRequestedChanges> outChanges; + mHwc.getDeviceCompositionChanges(halDisplayID, + mFdp.ConsumeBool() /*frameUsesClientComposition*/, + std::chrono::steady_clock::now(), FenceTime::NO_FENCE, + mFdp.ConsumeIntegral<nsecs_t>(), &outChanges); +} + +void DisplayHardwareFuzzer::getDisplayedContentSamplingAttributes(HalDisplayId halDisplayID) { + uint8_t outComponentMask; + ui::Dataspace dataSpace; + ui::PixelFormat pixelFormat; + mHwc.getDisplayedContentSamplingAttributes(halDisplayID, &pixelFormat, &dataSpace, + &outComponentMask); +} + +void DisplayHardwareFuzzer::getHdrCapabilities(HalDisplayId halDisplayID) { + HdrCapabilities outCapabilities; + mHwc.getHdrCapabilities(halDisplayID, &outCapabilities); +} + +void DisplayHardwareFuzzer::getDisplayedContentSample(HalDisplayId halDisplayID) { + DisplayedFrameStats outStats; + mHwc.getDisplayedContentSample(halDisplayID, mFdp.ConsumeIntegral<uint64_t>() /* maxFrames*/, + mFdp.ConsumeIntegral<uint64_t>() /*timestamps*/, &outStats); +} + +void DisplayHardwareFuzzer::getSupportedContentTypes() { + std::vector<hal::ContentType> contentType{}; + mHwc.getSupportedContentTypes(mPhysicalDisplayId, &contentType); +} + +void DisplayHardwareFuzzer::invokeAidlComposer() { + hardware::ProcessState::self()->startThreadPool(); + ProcessState::self()->startThreadPool(); + + if (!Hwc2::AidlComposer::isDeclared("default")) { + return; + } + + Hwc2::AidlComposer composer("default"); + + android::hardware::graphics::composer::hal::TestHWC2ComposerCallback composerCallback{}; + composer.registerCallback(composerCallback); + + Display display = createVirtualDisplay(&composer); + + composer.acceptDisplayChanges(display); + + Hwc2::V2_4::hal::Layer outLayer; + composer.createLayer(display, &outLayer); + + int32_t outPresentFence; + composer.presentDisplay(display, &outPresentFence); + + composer.setActiveConfig(display, Config{}); + + composer.setClientTarget(display, mFdp.ConsumeIntegral<uint32_t>(), sp<GraphicBuffer>(), + mFdp.ConsumeIntegral<int32_t>(), mFdp.PickValueInArray(kDataspaces), + {}); + + composer.setColorMode(display, mFdp.PickValueInArray(kColormodes), + mFdp.PickValueInArray(kRenderIntents)); + + setOutputBuffer(&composer, display); + + composer.setPowerMode(display, mFdp.PickValueInArray(kPowerModes)); + composer.setVsyncEnabled(display, mFdp.ConsumeBool() ? Vsync::ENABLE : Vsync::DISABLE); + + composer.setClientTargetSlotCount(display); + + validateDisplay(&composer, display); + + presentOrValidateDisplay(&composer, display); + + composer.setCursorPosition(display, outLayer, mFdp.ConsumeIntegral<uint8_t>() /*x*/, + mFdp.ConsumeIntegral<uint8_t>() /*y*/); + + composer.setLayerBuffer(display, outLayer, mFdp.ConsumeIntegral<uint32_t>() /*slot*/, + sp<GraphicBuffer>(), mFdp.ConsumeIntegral<int32_t>() /*acquireFence*/); + + composer.setLayerSurfaceDamage(display, outLayer, {} /*damage*/); + + composer.setLayerBlendMode(display, outLayer, mFdp.PickValueInArray(kBlendModes)); + + composer.setLayerColor(display, outLayer, + {mFdp.ConsumeFloatingPoint<float>() /*red*/, + mFdp.ConsumeFloatingPoint<float>() /*green*/, + mFdp.ConsumeFloatingPoint<float>() /*blue*/, + mFdp.ConsumeFloatingPoint<float>() /*alpha*/}); + composer.setLayerCompositionType(display, outLayer, mFdp.PickValueInArray(kCompositions)); + composer.setLayerDataspace(display, outLayer, mFdp.PickValueInArray(kDataspaces)); + composer.setLayerDisplayFrame(display, outLayer, {} /*frame*/); + composer.setLayerPlaneAlpha(display, outLayer, mFdp.ConsumeFloatingPoint<float>()); + + setLayerSidebandStream(&composer, display, outLayer); + + composer.setLayerSourceCrop(display, outLayer, {} /*crop*/); + + composer.setLayerTransform(display, outLayer, mFdp.PickValueInArray(kTransforms)); + + composer.setLayerVisibleRegion(display, outLayer, std::vector<IComposerClient::Rect>{}); + composer.setLayerZOrder(display, outLayer, mFdp.ConsumeIntegral<uint32_t>()); + + invokeComposerHal2_2(&composer, display, outLayer); + invokeComposerHal2_3(&composer, display, outLayer); + invokeComposerHal2_4(&composer, display, outLayer); + + composer.executeCommands(); + composer.resetCommands(); + + composer.destroyLayer(display, outLayer); + composer.destroyVirtualDisplay(display); +} + +void DisplayHardwareFuzzer::invokeComposerHal2_2(Hwc2::AidlComposer* composer, Display display, + Hwc2::V2_4::hal::Layer outLayer) { + const std::vector<PerFrameMetadata> perFrameMetadatas; + composer->setLayerPerFrameMetadata(display, outLayer, perFrameMetadatas); + + composer->getPerFrameMetadataKeys(display); + std::vector<RenderIntent> outRenderIntents; + + composer->getRenderIntents(display, mFdp.PickValueInArray(kColormodes), &outRenderIntents); + mat4 outMatrix; + composer->getDataspaceSaturationMatrix(mFdp.PickValueInArray(kDataspaces), &outMatrix); +} + +void DisplayHardwareFuzzer::invokeComposerHal2_3(Hwc2::AidlComposer* composer, Display display, + Hwc2::V2_4::hal::Layer outLayer) { + composer->setDisplayContentSamplingEnabled(display, mFdp.ConsumeBool() /*enabled*/, + mFdp.ConsumeIntegral<uint8_t>() /*componentMask*/, + mFdp.ConsumeIntegral<uint64_t>() /*maxFrames*/); + + DisplayedFrameStats outStats; + composer->getDisplayedContentSample(display, mFdp.ConsumeIntegral<uint64_t>() /*maxFrames*/, + mFdp.ConsumeIntegral<uint64_t>() /*timestamp*/, &outStats); + + composer->setLayerPerFrameMetadataBlobs(display, outLayer, std::vector<PerFrameMetadataBlob>{}); + + composer->setDisplayBrightness(display, mFdp.ConsumeFloatingPoint<float>(), + Hwc2::Composer::DisplayBrightnessOptions{ + .applyImmediately = mFdp.ConsumeIntegral<bool>()}); +} + +void DisplayHardwareFuzzer::invokeComposerHal2_4(Hwc2::AidlComposer* composer, Display display, + Hwc2::V2_4::hal::Layer outLayer) { + VsyncPeriodChangeTimeline outTimeline; + composer->setActiveConfigWithConstraints(display, Config{}, + IComposerClient::VsyncPeriodChangeConstraints{}, + &outTimeline); + + composer->setAutoLowLatencyMode(display, mFdp.ConsumeBool()); + + composer->setContentType(display, mFdp.PickValueInArray(kContentTypes)); + + std::vector<uint8_t> value; + value.push_back(mFdp.ConsumeIntegral<uint8_t>()); + composer->setLayerGenericMetadata(display, outLayer, mFdp.ConsumeRandomLengthString() /*key*/, + mFdp.ConsumeBool() /*mandatory*/, value); +} + +ui::Size DisplayHardwareFuzzer::getFuzzedSize() { + ui::Size size{mFdp.ConsumeIntegral<int32_t>() /*width*/, + mFdp.ConsumeIntegral<int32_t>() /*height*/}; + return size; +} + +mat4 DisplayHardwareFuzzer::getFuzzedMatrix() { + mat4 matrix{mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>(), + mFdp.ConsumeFloatingPoint<float>(), mFdp.ConsumeFloatingPoint<float>()}; + return matrix; +} + +void DisplayHardwareFuzzer::setCursorPosition(HWC2::Layer* layer) { + layer->setCursorPosition(mFdp.ConsumeIntegral<int32_t>() /*x*/, + mFdp.ConsumeIntegral<int32_t>() /*y*/); +} + +void DisplayHardwareFuzzer::setBuffer(HWC2::Layer* layer) { + layer->setBuffer(mFdp.ConsumeIntegral<uint32_t>() /*slot*/, sp<GraphicBuffer>(), + sp<Fence>::make()); +} + +void DisplayHardwareFuzzer::setSurfaceDamage(HWC2::Layer* layer) { + Rect rhs{mFdp.ConsumeIntegral<uint32_t>() /*width*/, + mFdp.ConsumeIntegral<uint32_t>() /*height*/}; + const Region damage{rhs}; + layer->setSurfaceDamage(damage); +} + +void DisplayHardwareFuzzer::setVisibleRegion(HWC2::Layer* layer) { + uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); + uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); + Rect rect{width, height}; + const Region region{rect}; + layer->setVisibleRegion(region); +} + +void DisplayHardwareFuzzer::setDisplayFrame(HWC2::Layer* layer) { + uint32_t width = mFdp.ConsumeIntegral<uint32_t>(); + uint32_t height = mFdp.ConsumeIntegral<uint32_t>(); + const Rect frame{width, height}; + layer->setDisplayFrame(frame); +} + +void DisplayHardwareFuzzer::setLayerGenericMetadata(HWC2::Layer* layer) { + std::vector<uint8_t> value; + value.push_back(mFdp.ConsumeIntegral<uint8_t>()); + layer->setLayerGenericMetadata(mFdp.ConsumeRandomLengthString().c_str() /*name*/, + mFdp.ConsumeBool() /*mandatory*/, value); +} + +void DisplayHardwareFuzzer::setSidebandStream(HWC2::Layer* layer) { + const native_handle_t stream{}; + layer->setSidebandStream(&stream); +} + +void DisplayHardwareFuzzer::invokeLayer(HWC2::Layer* layer) { + setCursorPosition(layer); + setBuffer(layer); + setSurfaceDamage(layer); + + layer->setBlendMode(mFdp.PickValueInArray(kBlendModes)); + layer->setColor({mFdp.ConsumeFloatingPoint<float>() /*red*/, + mFdp.ConsumeFloatingPoint<float>() /*green*/, + mFdp.ConsumeFloatingPoint<float>() /*blue*/, + mFdp.ConsumeFloatingPoint<float>() /*alpha*/}); + layer->setCompositionType(mFdp.PickValueInArray(kCompositions)); + layer->setDataspace(mFdp.PickValueInArray(kDataspaces)); + + layer->setPerFrameMetadata(mFdp.ConsumeIntegral<int32_t>(), getFuzzedHdrMetadata(&mFdp)); + setDisplayFrame(layer); + + layer->setPlaneAlpha(mFdp.ConsumeFloatingPoint<float>()); + + setSidebandStream(layer); + + layer->setSourceCrop(getFuzzedFloatRect(&mFdp)); + layer->setTransform(mFdp.PickValueInArray(kTransforms)); + + setVisibleRegion(layer); + + layer->setZOrder(mFdp.ConsumeIntegral<uint32_t>()); + + layer->setColorTransform(getFuzzedMatrix()); + + setLayerGenericMetadata(layer); +} + +void DisplayHardwareFuzzer::invokeFrameBufferSurface() { + sp<IGraphicBufferProducer> bqProducer = sp<mock::GraphicBufferProducer>::make(); + sp<IGraphicBufferConsumer> bqConsumer; + BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); + + sp<FramebufferSurface> surface = + new FramebufferSurface(mHwc, mPhysicalDisplayId, bqConsumer, getFuzzedSize() /*size*/, + getFuzzedSize() /*maxSize*/); + surface->beginFrame(mFdp.ConsumeBool()); + + surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes)); + surface->advanceFrame(); + surface->onFrameCommitted(); + String8 result = String8(mFdp.ConsumeRandomLengthString().c_str()); + surface->dumpAsString(result); + surface->resizeBuffers(getFuzzedSize()); + surface->getClientTargetAcquireFence(); +} + +void DisplayHardwareFuzzer::invokeVirtualDisplaySurface() { + DisplayIdGenerator<HalVirtualDisplayId> mGenerator; + VirtualDisplayId VirtualDisplayId = mGenerator.generateId().value(); + + sp<SurfaceComposerClient> mClient = new SurfaceComposerClient(); + sp<SurfaceControl> mSurfaceControl = + mClient->createSurface(String8("TestSurface"), 100, 100, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState, + /*parent*/ nullptr); + + sp<BLASTBufferQueue> mBlastBufferQueueAdapter = + new BLASTBufferQueue("TestBLASTBufferQueue", mSurfaceControl, 100, 100, + PIXEL_FORMAT_RGBA_8888); + + sp<IGraphicBufferProducer> sink = mBlastBufferQueueAdapter->getIGraphicBufferProducer(); + sp<IGraphicBufferProducer> bqProducer = mBlastBufferQueueAdapter->getIGraphicBufferProducer(); + sp<IGraphicBufferConsumer> bqConsumer; + BufferQueue::createBufferQueue(&bqProducer, &bqConsumer); + BufferQueue::createBufferQueue(&sink, &bqConsumer); + + sp<VirtualDisplaySurface> surface = + new VirtualDisplaySurface(mHwc, VirtualDisplayId, sink, bqProducer, bqConsumer, + mFdp.ConsumeRandomLengthString().c_str() /*name*/); + + surface->beginFrame(mFdp.ConsumeBool()); + surface->prepareFrame(mFdp.PickValueInArray(kCompositionTypes)); + surface->resizeBuffers(getFuzzedSize()); + surface->getClientTargetAcquireFence(); + surface->advanceFrame(); + surface->onFrameCommitted(); + String8 result = String8(mFdp.ConsumeRandomLengthString().c_str()); + surface->dumpAsString(result); +} + +void DisplayHardwareFuzzer::invokeComposer() { + HalVirtualDisplayId halVirtualDisplayId = mGenerator.generateId().value(); + HalDisplayId halDisplayID = HalDisplayId{halVirtualDisplayId}; + + android::hardware::graphics::composer::hal::TestHWC2ComposerCallback composerCallback{}; + mHwc.setCallback(composerCallback); + + ui::PixelFormat pixelFormat{}; + if (!mHwc.allocateVirtualDisplay(halVirtualDisplayId, getFuzzedSize(), &pixelFormat)) { + return; + } + + getDisplayIdentificationData(); + + mHwc.hasDisplayCapability(halDisplayID, mFdp.PickValueInArray(kDisplayCapability)); + + mHwc.allocatePhysicalDisplay(kHwDisplayId, mPhysicalDisplayId); + + static auto hwcLayer = mHwc.createLayer(halDisplayID); + HWC2::Layer* layer = hwcLayer.get(); + invokeLayer(layer); + + getDeviceCompositionChanges(halDisplayID); + + mHwc.setClientTarget(halDisplayID, mFdp.ConsumeIntegral<uint32_t>(), Fence::NO_FENCE, + sp<GraphicBuffer>::make(), mFdp.PickValueInArray(kDataspaces)); + + mHwc.presentAndGetReleaseFences(halDisplayID, std::chrono::steady_clock::now(), + FenceTime::NO_FENCE); + + mHwc.setPowerMode(mPhysicalDisplayId, mFdp.PickValueInArray(kPowerModes)); + + mHwc.setColorTransform(halDisplayID, getFuzzedMatrix()); + + mHwc.getPresentFence(halDisplayID); + + mHwc.getLayerReleaseFence(halDisplayID, layer); + + mHwc.setOutputBuffer(halVirtualDisplayId, sp<Fence>::make().get(), sp<GraphicBuffer>::make()); + + mHwc.clearReleaseFences(halDisplayID); + + getHdrCapabilities(halDisplayID); + + mHwc.getSupportedPerFrameMetadata(halDisplayID); + + mHwc.getRenderIntents(halDisplayID, ui::ColorMode()); + + mHwc.getDataspaceSaturationMatrix(halDisplayID, ui::Dataspace()); + + getDisplayedContentSamplingAttributes(halDisplayID); + + mHwc.setDisplayContentSamplingEnabled(halDisplayID, mFdp.ConsumeBool() /*enabled*/, + mFdp.ConsumeIntegral<uint8_t>() /*componentMask*/, + mFdp.ConsumeIntegral<uint64_t>() /*maxFrames*/); + + getDisplayedContentSample(halDisplayID); + + mHwc.setDisplayBrightness(mPhysicalDisplayId, mFdp.ConsumeFloatingPoint<float>(), + Hwc2::Composer::DisplayBrightnessOptions{ + .applyImmediately = mFdp.ConsumeIntegral<bool>()}); + + mHwc.onHotplug(kHwDisplayId, hal::Connection::CONNECTED); + mHwc.updatesDeviceProductInfoOnHotplugReconnect(); + + mHwc.onVsync(kHwDisplayId, mFdp.ConsumeIntegral<int64_t>()); + mHwc.setVsyncEnabled(mPhysicalDisplayId, + mFdp.ConsumeBool() ? hal::Vsync::ENABLE : hal::Vsync::DISABLE); + + mHwc.isConnected(mPhysicalDisplayId); + mHwc.getModes(mPhysicalDisplayId); + mHwc.getActiveMode(mPhysicalDisplayId); + mHwc.getColorModes(mPhysicalDisplayId); + mHwc.hasCapability(mFdp.PickValueInArray(kCapability)); + + mHwc.setActiveColorMode(mPhysicalDisplayId, mFdp.PickValueInArray(kColormodes), + mFdp.PickValueInArray(kRenderIntents)); + + mHwc.getDisplayConnectionType(mPhysicalDisplayId); + mHwc.isVsyncPeriodSwitchSupported(mPhysicalDisplayId); + + getDisplayVsyncPeriod(); + + setActiveModeWithConstraints(); + + mHwc.setAutoLowLatencyMode(mPhysicalDisplayId, mFdp.ConsumeBool()); + + getSupportedContentTypes(); + + mHwc.setContentType(mPhysicalDisplayId, mFdp.PickValueInArray(kContentTypes)); + + dumpHwc(); + + mHwc.toPhysicalDisplayId(kHwDisplayId); + mHwc.fromPhysicalDisplayId(mPhysicalDisplayId); + mHwc.disconnectDisplay(halDisplayID); + + static hal::HWDisplayId displayId = mFdp.ConsumeIntegral<hal::HWDisplayId>(); + mHwc.onHotplug(displayId, + mFdp.ConsumeBool() ? hal::Connection::DISCONNECTED : hal::Connection::CONNECTED); +} + +template <size_t N> +DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&bytes)[N]) { + return DisplayIdentificationData(bytes, bytes + N - 1); +} + +void DisplayHardwareFuzzer::invokeDisplayIdentification() { + static const DisplayIdentificationData data = asDisplayIdentificationData(kInternalEdid); + isEdid(data); + parseEdid(data); + parseDisplayIdentificationData(mFdp.ConsumeIntegral<uint8_t>(), data); + getPnpId(getVirtualDisplayId(mFdp.ConsumeIntegral<uint32_t>())); + getPnpId(mFdp.ConsumeIntegral<uint8_t>()); +} + +void DisplayHardwareFuzzer::process() { + invokeComposer(); + invokeAidlComposer(); + invokeDisplayIdentification(); + invokeFrameBufferSurface(); + invokeVirtualDisplaySurface(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + DisplayHardwareFuzzer displayHardwareFuzzer(data, size); + displayHardwareFuzzer.process(); + return 0; +} + +} // namespace android::fuzz diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h new file mode 100644 index 0000000000..6a6e3db733 --- /dev/null +++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer_utils.h @@ -0,0 +1,104 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <utils/Condition.h> +#include <chrono> +#include <vector> + +#include <android/hardware/graphics/composer/2.4/IComposer.h> +#include <composer-hal/2.1/ComposerClient.h> +#include <composer-hal/2.2/ComposerClient.h> +#include <composer-hal/2.3/ComposerClient.h> +#include <composer-hal/2.4/ComposerClient.h> + +#include "DisplayHardware/HWC2.h" +#include "surfaceflinger_fuzzers_utils.h" + +namespace { +class LayerImpl; +class Frame; +class DelayedEventGenerator; +} // namespace + +namespace android { +class SurfaceComposerClient; +} // namespace android + +namespace android::hardware::graphics::composer::hal { + +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::HWC2::ComposerCallback; + +class ComposerCallbackBridge : public IComposerCallback { +public: + ComposerCallbackBridge(ComposerCallback* callback, bool vsyncSwitchingSupported) + : mCallback(callback), mVsyncSwitchingSupported(vsyncSwitchingSupported) {} + + Return<void> onHotplug(HWDisplayId display, Connection connection) override { + mCallback->onComposerHalHotplug(display, connection); + return Void(); + } + + Return<void> onRefresh(HWDisplayId display) override { + mCallback->onComposerHalRefresh(display); + return Void(); + } + + Return<void> onVsync(HWDisplayId display, int64_t timestamp) override { + if (!mVsyncSwitchingSupported) { + mCallback->onComposerHalVsync(display, timestamp, std::nullopt); + } + return Void(); + } + + Return<void> onVsync_2_4(HWDisplayId display, int64_t timestamp, + VsyncPeriodNanos vsyncPeriodNanos) override { + if (mVsyncSwitchingSupported) { + mCallback->onComposerHalVsync(display, timestamp, vsyncPeriodNanos); + } + return Void(); + } + + Return<void> onVsyncPeriodTimingChanged(HWDisplayId display, + const VsyncPeriodChangeTimeline& timeline) override { + mCallback->onComposerHalVsyncPeriodTimingChanged(display, timeline); + return Void(); + } + + Return<void> onSeamlessPossible(HWDisplayId display) override { + mCallback->onComposerHalSeamlessPossible(display); + return Void(); + } + +private: + ComposerCallback* const mCallback; + const bool mVsyncSwitchingSupported; +}; + +struct TestHWC2ComposerCallback : public HWC2::ComposerCallback { + virtual ~TestHWC2ComposerCallback() = default; + void onComposerHalHotplug(HWDisplayId, Connection){}; + void onComposerHalRefresh(HWDisplayId) {} + void onComposerHalVsync(HWDisplayId, int64_t, std::optional<VsyncPeriodNanos>) {} + void onComposerHalVsyncPeriodTimingChanged(HWDisplayId, const VsyncPeriodChangeTimeline&) {} + void onComposerHalSeamlessPossible(HWDisplayId) {} + void onComposerHalVsyncIdle(HWDisplayId) {} +}; + +} // namespace android::hardware::graphics::composer::hal diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp new file mode 100644 index 0000000000..51a50818ba --- /dev/null +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -0,0 +1,392 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "surfaceflinger_scheduler_fuzzer.h" +#include <fuzzer/FuzzedDataProvider.h> +#include <processgroup/sched_policy.h> +#include "Scheduler/DispSyncSource.h" +#include "Scheduler/OneShotTimer.h" +#include "Scheduler/VSyncDispatchTimerQueue.h" +#include "Scheduler/VSyncPredictor.h" +#include "Scheduler/VSyncReactor.h" +#include "surfaceflinger_fuzzers_utils.h" + +namespace android::fuzz { + +using hardware::graphics::composer::hal::PowerMode; + +static constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF, + PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND}; + +constexpr uint16_t kRandomStringLength = 256; +constexpr std::chrono::duration kSyncPeriod(16ms); + +template <typename T> +void dump(T* component, FuzzedDataProvider* fdp) { + std::string res = fdp->ConsumeRandomLengthString(kRandomStringLength); + component->dump(res); +} + +class SchedulerFuzzer : private VSyncSource::Callback { +public: + SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; + void process(); + +private: + void fuzzRefreshRateSelection(); + void fuzzRefreshRateConfigs(); + void fuzzVSyncModulator(); + void fuzzVSyncPredictor(); + void fuzzVSyncReactor(); + void fuzzLayerHistory(); + void fuzzDispSyncSource(); + void fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch); + void fuzzVSyncDispatchTimerQueue(); + void fuzzOneShotTimer(); + void fuzzEventThread(); + PhysicalDisplayId getPhysicalDisplayId(); + + FuzzedDataProvider mFdp; + +protected: + void onVSyncEvent(nsecs_t /* when */, nsecs_t /* expectedVSyncTimestamp */, + nsecs_t /* deadlineTimestamp */) {} +}; + +PhysicalDisplayId SchedulerFuzzer::getPhysicalDisplayId() { + PhysicalDisplayId internalDispId = PhysicalDisplayId::fromPort(111u); + PhysicalDisplayId externalDispId = PhysicalDisplayId::fromPort(222u); + PhysicalDisplayId randomDispId = PhysicalDisplayId::fromPort(mFdp.ConsumeIntegral<uint16_t>()); + PhysicalDisplayId dispId64Bit = PhysicalDisplayId::fromEdid(0xffu, 0xffffu, 0xffff'ffffu); + PhysicalDisplayId displayId = mFdp.PickValueInArray<PhysicalDisplayId>( + {internalDispId, externalDispId, dispId64Bit, randomDispId}); + return displayId; +} + +void SchedulerFuzzer::fuzzEventThread() { + const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); }; + std::unique_ptr<android::impl::EventThread> thread = std::make_unique< + android::impl::EventThread>(std::move(std::make_unique<FuzzImplVSyncSource>()), nullptr, + nullptr, nullptr, getVsyncPeriod); + + thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool()); + sp<EventThreadConnection> connection = + new EventThreadConnection(thread.get(), mFdp.ConsumeIntegral<uint16_t>(), nullptr, + {} /*eventRegistration*/); + thread->requestNextVsync(connection); + thread->setVsyncRate(mFdp.ConsumeIntegral<uint32_t>() /*rate*/, connection); + + thread->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), + (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()); + thread->registerDisplayEventConnection(connection); + thread->onScreenAcquired(); + thread->onScreenReleased(); + dump<android::impl::EventThread>(thread.get(), &mFdp); +} + +void SchedulerFuzzer::fuzzDispSyncSource() { + std::unique_ptr<FuzzImplVSyncDispatch> vSyncDispatch = + std::make_unique<FuzzImplVSyncDispatch>(); + std::unique_ptr<scheduler::DispSyncSource> dispSyncSource = std::make_unique< + scheduler::DispSyncSource>(*vSyncDispatch, + (std::chrono::nanoseconds) + mFdp.ConsumeIntegral<uint64_t>() /*workDuration*/, + (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>() + /*readyDuration*/, + mFdp.ConsumeBool(), + mFdp.ConsumeRandomLengthString(kRandomStringLength).c_str()); + dispSyncSource->setVSyncEnabled(true); + dispSyncSource->setCallback(this); + dispSyncSource->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), 0ns); + dump<scheduler::DispSyncSource>(dispSyncSource.get(), &mFdp); +} + +void SchedulerFuzzer::fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch) { + scheduler::VSyncDispatch::CallbackToken tmp = dispatch->registerCallback( + [&](auto, auto, auto) { + dispatch->schedule(tmp, + {.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), + .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), + .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}); + }, + "o.o"); + dispatch->schedule(tmp, + {.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), + .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), + .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}); + dispatch->unregisterCallback(tmp); + dispatch->cancel(tmp); +} + +void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { + FuzzImplVSyncTracker stubTracker{mFdp.ConsumeIntegral<nsecs_t>()}; + scheduler::VSyncDispatchTimerQueue + mDispatch{std::make_unique<scheduler::ControllableClock>(), stubTracker, + mFdp.ConsumeIntegral<nsecs_t>() /*dispatchGroupThreshold*/, + mFdp.ConsumeIntegral<nsecs_t>() /*vSyncMoveThreshold*/}; + + fuzzCallbackToken(&mDispatch); + + dump<scheduler::VSyncDispatchTimerQueue>(&mDispatch, &mFdp); + + scheduler::VSyncDispatchTimerQueueEntry entry( + "fuzz", [](auto, auto, auto) {}, + mFdp.ConsumeIntegral<nsecs_t>() /*vSyncMoveThreshold*/); + entry.update(stubTracker, 0); + entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), + .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), + .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}, + stubTracker, 0); + entry.disarm(); + entry.ensureNotRunning(); + entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), + .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), + .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}, + stubTracker, 0); + auto const wakeup = entry.wakeupTime(); + auto const ready = entry.readyTime(); + entry.callback(entry.executing(), *wakeup, *ready); + entry.addPendingWorkloadUpdate({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), + .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), + .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}); + dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp); +} + +void SchedulerFuzzer::fuzzVSyncPredictor() { + uint16_t now = mFdp.ConsumeIntegral<uint16_t>(); + uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); + uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); + scheduler::VSyncPredictor tracker{mFdp.ConsumeIntegral<uint16_t>() /*period*/, historySize, + minimumSamplesForPrediction, + mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/}; + uint16_t period = mFdp.ConsumeIntegral<uint16_t>(); + tracker.setPeriod(period); + for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) { + if (!tracker.needsMoreSamples()) { + break; + } + tracker.addVsyncTimestamp(now += period); + } + tracker.nextAnticipatedVSyncTimeFrom(now); + tracker.resetModel(); +} + +void SchedulerFuzzer::fuzzOneShotTimer() { + FakeClock* clock = new FakeClock(); + std::unique_ptr<scheduler::OneShotTimer> idleTimer = std::make_unique<scheduler::OneShotTimer>( + mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/, + (std::chrono::milliseconds)mFdp.ConsumeIntegral<uint8_t>() /*val*/, + [] {} /*resetCallback*/, [] {} /*timeoutCallback*/, std::unique_ptr<FakeClock>(clock)); + idleTimer->start(); + idleTimer->reset(); + idleTimer->stop(); +} + +void SchedulerFuzzer::fuzzLayerHistory() { + TestableSurfaceFlinger flinger; + flinger.setupScheduler(std::make_unique<android::mock::VsyncController>(), + std::make_unique<android::mock::VSyncTracker>(), + std::make_unique<android::mock::EventThread>(), + std::make_unique<android::mock::EventThread>()); + flinger.setupTimeStats(std::make_unique<android::mock::TimeStats>()); + std::unique_ptr<android::renderengine::RenderEngine> renderEngine = + std::make_unique<android::renderengine::mock::RenderEngine>(); + flinger.setupRenderEngine(std::move(renderEngine)); + flinger.setupComposer(std::make_unique<android::Hwc2::mock::Composer>()); + + scheduler::TestableScheduler* scheduler = flinger.scheduler(); + + scheduler::LayerHistory& historyV1 = scheduler->mutableLayerHistory(); + nsecs_t time1 = systemTime(); + nsecs_t time2 = time1; + uint8_t historySize = mFdp.ConsumeIntegral<uint8_t>(); + + sp<FuzzImplLayer> layer1 = new FuzzImplLayer(flinger.flinger()); + sp<FuzzImplLayer> layer2 = new FuzzImplLayer(flinger.flinger()); + + for (int i = 0; i < historySize; ++i) { + historyV1.record(layer1.get(), time1, time1, + scheduler::LayerHistory::LayerUpdateType::Buffer); + historyV1.record(layer2.get(), time2, time2, + scheduler::LayerHistory::LayerUpdateType::Buffer); + time1 += mFdp.PickValueInArray(kVsyncPeriods); + time2 += mFdp.PickValueInArray(kVsyncPeriods); + } + historyV1.summarize(*scheduler->refreshRateConfigs(), time1); + historyV1.summarize(*scheduler->refreshRateConfigs(), time2); + + scheduler->createConnection(std::make_unique<android::mock::EventThread>()); + + scheduler::ConnectionHandle handle; + scheduler->createDisplayEventConnection(handle); + scheduler->setDuration(handle, (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), + (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()); + + dump<scheduler::TestableScheduler>(scheduler, &mFdp); +} + +void SchedulerFuzzer::fuzzVSyncReactor() { + std::shared_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_shared<FuzzImplVSyncTracker>(); + scheduler::VSyncReactor reactor(std::make_unique<ClockWrapper>( + std::make_shared<FuzzImplClock>()), + *vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/, + false); + + reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>()); + bool periodFlushed = mFdp.ConsumeBool(); + reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed); + reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt, + &periodFlushed); + sp<Fence> fence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING)); + std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence); + vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>()); + FenceTime::Snapshot snap(mFdp.ConsumeIntegral<nsecs_t>()); + ft->applyTrustedSnapshot(snap); + reactor.setIgnorePresentFences(mFdp.ConsumeBool()); + reactor.addPresentFence(ft); + dump<scheduler::VSyncReactor>(&reactor, &mFdp); +} + +void SchedulerFuzzer::fuzzVSyncModulator() { + enum { + SF_OFFSET_LATE, + APP_OFFSET_LATE, + SF_DURATION_LATE, + APP_DURATION_LATE, + SF_OFFSET_EARLY, + APP_OFFSET_EARLY, + SF_DURATION_EARLY, + APP_DURATION_EARLY, + SF_OFFSET_EARLY_GPU, + APP_OFFSET_EARLY_GPU, + SF_DURATION_EARLY_GPU, + APP_DURATION_EARLY_GPU, + HWC_MIN_WORK_DURATION, + }; + using Schedule = scheduler::TransactionSchedule; + using nanos = std::chrono::nanoseconds; + using VsyncModulator = scheduler::VsyncModulator; + using FuzzImplVsyncModulator = scheduler::FuzzImplVsyncModulator; + const VsyncModulator::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY, + nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)}; + const VsyncModulator::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU, + nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)}; + const VsyncModulator::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE, + nanos(SF_DURATION_EARLY_GPU), + nanos(APP_DURATION_EARLY_GPU)}; + const VsyncModulator::VsyncConfigSet offsets = {early, earlyGpu, late, + nanos(HWC_MIN_WORK_DURATION)}; + sp<FuzzImplVsyncModulator> vSyncModulator = + sp<FuzzImplVsyncModulator>::make(offsets, scheduler::Now); + (void)vSyncModulator->setVsyncConfigSet(offsets); + (void)vSyncModulator->setTransactionSchedule(Schedule::Late); + const auto token = sp<BBinder>::make(); + (void)vSyncModulator->setTransactionSchedule(Schedule::EarlyStart, token); + vSyncModulator->binderDied(token); +} + +void SchedulerFuzzer::fuzzRefreshRateSelection() { + TestableSurfaceFlinger flinger; + flinger.setupScheduler(std::make_unique<android::mock::VsyncController>(), + std::make_unique<android::mock::VSyncTracker>(), + std::make_unique<android::mock::EventThread>(), + std::make_unique<android::mock::EventThread>()); + + sp<Client> client; + LayerCreationArgs args(flinger.flinger(), client, + mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/, + mFdp.ConsumeIntegral<uint16_t>() /*layerFlags*/, LayerMetadata()); + sp<Layer> layer = new BufferQueueLayer(args); + + layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral<int16_t>()); +} + +void SchedulerFuzzer::fuzzRefreshRateConfigs() { + using RefreshRateConfigs = scheduler::RefreshRateConfigs; + using LayerRequirement = RefreshRateConfigs::LayerRequirement; + using RefreshRateStats = scheduler::RefreshRateStats; + uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX >> 1); + uint16_t maxRefreshRate = mFdp.ConsumeIntegralInRange<uint16_t>(minRefreshRate + 1, UINT16_MAX); + + DisplayModeId hwcConfigIndexType = DisplayModeId(mFdp.ConsumeIntegralInRange<uint8_t>(0, 10)); + + DisplayModes displayModes; + for (uint16_t fps = minRefreshRate; fps < maxRefreshRate; ++fps) { + constexpr int32_t kGroup = 0; + const auto refreshRate = Fps::fromValue(static_cast<float>(fps)); + displayModes.push_back(scheduler::createDisplayMode(hwcConfigIndexType, kGroup, + refreshRate.getPeriodNsecs())); + } + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(displayModes, hwcConfigIndexType); + const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false}; + auto layers = std::vector<LayerRequirement>{ + LayerRequirement{.weight = mFdp.ConsumeFloatingPoint<float>()}}; + refreshRateConfigs->getBestRefreshRate(layers, globalSignals); + layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength); + layers[0].ownerUid = mFdp.ConsumeIntegral<uint16_t>(); + layers[0].desiredRefreshRate = Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()); + layers[0].vote = mFdp.PickValueInArray(kLayerVoteTypes); + auto frameRateOverrides = + refreshRateConfigs->getFrameRateOverrides(layers, + Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>()), + globalSignals); + + refreshRateConfigs->setDisplayManagerPolicy( + {hwcConfigIndexType, + {Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), + Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())}}); + refreshRateConfigs->setCurrentModeId(hwcConfigIndexType); + + RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>()), + Fps::fromValue( + mFdp.ConsumeFloatingPoint<float>())); + RefreshRateConfigs::getFrameRateDivider(Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), + Fps::fromValue(mFdp.ConsumeFloatingPoint<float>())); + + android::mock::TimeStats timeStats; + std::unique_ptr<RefreshRateStats> refreshRateStats = + std::make_unique<RefreshRateStats>(timeStats, + Fps::fromValue(mFdp.ConsumeFloatingPoint<float>()), + PowerMode::OFF); + refreshRateStats->setRefreshRate( + refreshRateConfigs->getRefreshRateFromModeId(hwcConfigIndexType).getFps()); + refreshRateStats->setPowerMode(mFdp.PickValueInArray(kPowerModes)); +} + +void SchedulerFuzzer::process() { + fuzzRefreshRateSelection(); + fuzzRefreshRateConfigs(); + fuzzVSyncModulator(); + fuzzVSyncPredictor(); + fuzzVSyncReactor(); + fuzzLayerHistory(); + fuzzDispSyncSource(); + fuzzEventThread(); + fuzzVSyncDispatchTimerQueue(); + fuzzOneShotTimer(); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + SchedulerFuzzer schedulerFuzzer(data, size); + schedulerFuzzer.process(); + return 0; +} + +} // namespace android::fuzz diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h new file mode 100644 index 0000000000..89cf819614 --- /dev/null +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h @@ -0,0 +1,229 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + Reference for some of the classes and functions has been taken from unittests + present in frameworks/native/services/surfaceflinger/tests/unittests +*/ + +#pragma once + +#include "Clock.h" +#include "Layer.h" +#include "Scheduler/EventThread.h" +#include "Scheduler/RefreshRateConfigs.h" +#include "Scheduler/Scheduler.h" +#include "Scheduler/VSyncTracker.h" +#include "Scheduler/VsyncModulator.h" +#include "scheduler/TimeKeeper.h" + +namespace android::fuzz { + +constexpr int64_t kVsyncPeriods[] = {static_cast<int64_t>(1e9f / 30), + static_cast<int64_t>(1e9f / 60), + static_cast<int64_t>(1e9f / 72), + static_cast<int64_t>(1e9f / 90), + static_cast<int64_t>(1e9f / 120)}; + +android::scheduler::RefreshRateConfigs::LayerVoteType kLayerVoteTypes[] = + {android::scheduler::RefreshRateConfigs::LayerVoteType::NoVote, + android::scheduler::RefreshRateConfigs::LayerVoteType::Min, + android::scheduler::RefreshRateConfigs::LayerVoteType::Max, + android::scheduler::RefreshRateConfigs::LayerVoteType::Heuristic, + android::scheduler::RefreshRateConfigs::LayerVoteType::ExplicitDefault, + android::scheduler::RefreshRateConfigs::LayerVoteType::ExplicitExactOrMultiple}; + +class FuzzImplClock : public android::scheduler::Clock { +public: + nsecs_t now() const { return 1; } +}; + +class ClockWrapper : public android::scheduler::Clock { +public: + ClockWrapper(std::shared_ptr<android::scheduler::Clock> const& clock) : mClock(clock) {} + + nsecs_t now() const { return mClock->now(); } + +private: + std::shared_ptr<android::scheduler::Clock> const mClock; +}; + +} // namespace android::fuzz + +namespace android { + +using namespace std::chrono_literals; + +class FakeClock : public Clock { +public: + virtual ~FakeClock() = default; + std::chrono::steady_clock::time_point now() const override { return mNow; } + + void advanceTime(std::chrono::nanoseconds delta) { mNow += delta; } + +private: + std::chrono::steady_clock::time_point mNow; +}; + +class FuzzImplLayer : public Layer { +public: + FuzzImplLayer(SurfaceFlinger* flinger, std::string name) + : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {} + explicit FuzzImplLayer(SurfaceFlinger* flinger) : FuzzImplLayer(flinger, "FuzzLayer") {} + + const char* getType() const override { return ""; } + + bool isVisible() const override { return true; } + + sp<Layer> createClone() override { return nullptr; } +}; + +class FuzzImplVSyncSource : public VSyncSource { +public: + const char* getName() const override { return "fuzz"; } + + void setVSyncEnabled(bool /* enable */) override {} + + void setCallback(Callback* /* callback */) override {} + + void setDuration(std::chrono::nanoseconds /* workDuration */, + std::chrono::nanoseconds /* readyDuration */) override {} + + void dump(std::string& /* result */) const override {} +}; + +class FuzzImplVSyncTracker : public scheduler::VSyncTracker { +public: + FuzzImplVSyncTracker(nsecs_t period) { mPeriod = period; } + + FuzzImplVSyncTracker() = default; + + bool addVsyncTimestamp(nsecs_t /* timestamp */) override { return true; } + + nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t /* timePoint */) const override { return 1; } + + nsecs_t currentPeriod() const override { return 1; } + + void setPeriod(nsecs_t /* period */) override {} + + void resetModel() override {} + + bool needsMoreSamples() const override { return true; } + + bool isVSyncInPhase(nsecs_t /* timePoint */, Fps /* frameRate */) const override { + return true; + } + + nsecs_t nextVSyncTime(nsecs_t timePoint) const { + if (timePoint % mPeriod == 0) { + return timePoint; + } + return (timePoint - (timePoint % mPeriod) + mPeriod); + } + + void dump(std::string& /* result */) const override {} + +protected: + nsecs_t mPeriod; +}; + +class FuzzImplVSyncDispatch : public scheduler::VSyncDispatch { +public: + CallbackToken registerCallback(Callback /* callbackFn */, + std::string /* callbackName */) override { + return CallbackToken{}; + } + + void unregisterCallback(CallbackToken /* token */) override {} + + scheduler::ScheduleResult schedule(CallbackToken /* token */, + ScheduleTiming /* scheduleTiming */) override { + return (scheduler::ScheduleResult)0; + } + + scheduler::CancelResult cancel(CallbackToken /* token */) override { + return (scheduler::CancelResult)0; + } + + void dump(std::string& /* result */) const override {} +}; + +} // namespace android + +namespace android::scheduler { + +DisplayModePtr createDisplayMode(DisplayModeId modeId, int32_t group, int64_t vsyncPeriod, + ui::Size resolution = ui::Size()) { + return DisplayMode::Builder(hal::HWConfigId(modeId.value())) + .setId(modeId) + .setPhysicalDisplayId(PhysicalDisplayId::fromPort(0)) + .setVsyncPeriod(int32_t(vsyncPeriod)) + .setGroup(group) + .setHeight(resolution.height) + .setWidth(resolution.width) + .build(); +} + +class ControllableClock : public TimeKeeper { +public: + nsecs_t now() const { return 1; }; + void alarmAt(std::function<void()> /* callback */, nsecs_t /* time */) override {} + void alarmCancel() override {} + void dump(std::string& /* result */) const override {} + + void alarmAtDefaultBehavior(std::function<void()> const& callback, nsecs_t time) { + mCallback = callback; + mNextCallbackTime = time; + } + + nsecs_t fakeTime() const { return mCurrentTime; } + + void advanceToNextCallback() { + mCurrentTime = mNextCallbackTime; + if (mCallback) { + mCallback(); + } + } + + void advanceBy(nsecs_t advancement) { + mCurrentTime += advancement; + if (mCurrentTime >= (mNextCallbackTime + mLag) && mCallback) { + mCallback(); + } + }; + + void setLag(nsecs_t lag) { mLag = lag; } + +private: + std::function<void()> mCallback; + nsecs_t mNextCallbackTime = 0; + nsecs_t mCurrentTime = 0; + nsecs_t mLag = 0; +}; + +static VsyncModulator::TimePoint Now() { + static VsyncModulator::TimePoint now; + return now += VsyncModulator::MIN_EARLY_TRANSACTION_TIME; +} + +class FuzzImplVsyncModulator : public VsyncModulator { +public: + FuzzImplVsyncModulator(const VsyncConfigSet& config, Now now) : VsyncModulator(config, now) {} + + void binderDied(const wp<IBinder>& token) { VsyncModulator::binderDied(token); } +}; +} // namespace android::scheduler diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp index 2841f7c2fd..854084e7f9 100644 --- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp +++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp @@ -305,7 +305,7 @@ std::string LayerProtoParser::Layer::to_string() const { zOrderRelativeOf == nullptr ? "none" : zOrderRelativeOf->name.c_str()); StringAppendF(&result, " activeBuffer=%s,", activeBuffer.to_string().c_str()); StringAppendF(&result, " tr=%s", bufferTransform.to_string().c_str()); - StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d,", queuedFrames, refreshPending); + StringAppendF(&result, " queued-frames=%d", queuedFrames); StringAppendF(&result, " metadata={"); bool first = true; for (const auto& entry : metadata.mMap) { diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index d292e0879d..4fe1e98dbe 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -177,6 +177,11 @@ class TestableSurfaceFlinger { public: using HotplugEvent = SurfaceFlinger::HotplugEvent; + TestableSurfaceFlinger() + : mFlinger(sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization)) { + mFlinger->mAnimationTransactionTimeout = ms2ns(10); + } + SurfaceFlinger* flinger() { return mFlinger.get(); } scheduler::TestableScheduler* scheduler() { return mScheduler; } @@ -466,6 +471,10 @@ public: return static_cast<mock::FrameTracer*>(mFlinger->mFrameTracer.get()); } + nsecs_t getAnimationTransactionTimeout() const { + return mFlinger->mAnimationTransactionTimeout; + } + /* ------------------------------------------------------------------------ * Read-write access to private data to set up preconditions and assert * post-conditions. diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index 1ce0309683..ed23176718 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -89,10 +89,10 @@ public: mock::VsyncController* mVsyncController = new mock::VsyncController(); mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker(); - mock::MockFence* mFenceUnsignaled = new mock::MockFence(); - mock::MockFence* mFenceSignaled = new mock::MockFence(); - mock::MockFence* mFenceUnsignaled2 = new mock::MockFence(); - mock::MockFence* mFenceSignaled2 = new mock::MockFence(); + sp<mock::MockFence> mFenceUnsignaled = sp<mock::MockFence>::make(); + sp<mock::MockFence> mFenceSignaled = sp<mock::MockFence>::make(); + sp<mock::MockFence> mFenceUnsignaled2 = sp<mock::MockFence>::make(); + sp<mock::MockFence> mFenceSignaled2 = sp<mock::MockFence>::make(); struct TransactionInfo { Vector<ComposerState> states; @@ -159,9 +159,9 @@ public: // completed. If this is animation, it should not time out waiting. nsecs_t returnedTime = systemTime(); if (flags & ISurfaceComposer::eSynchronous || syncInputWindows) { - EXPECT_GE(returnedTime, applicationTime + s2ns(5)); + EXPECT_GE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout()); } else { - EXPECT_LE(returnedTime, applicationTime + s2ns(5)); + EXPECT_LE(returnedTime, applicationTime + mFlinger.getAnimationTransactionTimeout()); } // Each transaction should have been placed on the transaction queue auto transactionQueue = mFlinger.getTransactionQueue(); @@ -188,9 +188,11 @@ public: nsecs_t returnedTime = systemTime(); if ((flags & ISurfaceComposer::eSynchronous) || syncInputWindows) { - EXPECT_GE(systemTime(), applicationSentTime + s2ns(5)); + EXPECT_GE(systemTime(), + applicationSentTime + mFlinger.getAnimationTransactionTimeout()); } else { - EXPECT_LE(returnedTime, applicationSentTime + s2ns(5)); + EXPECT_LE(returnedTime, + applicationSentTime + mFlinger.getAnimationTransactionTimeout()); } // This transaction should have been placed on the transaction queue auto transactionQueue = mFlinger.getTransactionQueue(); @@ -228,7 +230,7 @@ public: // This thread should not have been blocked by the above transaction // (5s is the timeout period that applyTransactionState waits for SF to // commit the transaction) - EXPECT_LE(systemTime(), applicationSentTime + s2ns(5)); + EXPECT_LE(systemTime(), applicationSentTime + mFlinger.getAnimationTransactionTimeout()); // transaction that would goes to pending transaciton queue. mFlinger.flushTransactionQueues(); @@ -246,9 +248,11 @@ public: // the transaction should be placed on the pending queue if (flags & (ISurfaceComposer::eAnimation | ISurfaceComposer::eSynchronous) || syncInputWindows) { - EXPECT_GE(systemTime(), applicationSentTime + s2ns(5)); + EXPECT_GE(systemTime(), + applicationSentTime + mFlinger.getAnimationTransactionTimeout()); } else { - EXPECT_LE(systemTime(), applicationSentTime + s2ns(5)); + EXPECT_LE(systemTime(), + applicationSentTime + mFlinger.getAnimationTransactionTimeout()); } // transaction that would goes to pending transaciton queue. |