diff options
251 files changed, 5567 insertions, 2453 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index d24edc4d82..4758607122 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1841,6 +1841,11 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build()); } + /* Dump USB information */ + RunCommand("typec_connector_class", {"typec_connector_class"}, + CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); + RunCommand("lsusb", {"lsusb"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); + printf("========================================================\n"); printf("== Android Framework Services\n"); printf("========================================================\n"); @@ -4651,7 +4656,7 @@ void Dumpstate::UpdateProgress(int32_t delta_sec) { void Dumpstate::TakeScreenshot(const std::string& path) { const std::string& real_path = path.empty() ? screenshot_path_ : path; int status = - RunCommand("", {"/system/bin/screencap", "-p", real_path}, + RunCommand("", {"screencap", "-p", real_path}, CommandOptions::WithTimeout(10).Always().DropRoot().RedirectStderr().Build()); if (status == 0) { MYLOGD("Screenshot saved on %s\n", real_path.c_str()); diff --git a/data/etc/android.hardware.xr.input.controller.xml b/data/etc/android.hardware.xr.input.controller.xml new file mode 100644 index 0000000000..1fb8b41004 --- /dev/null +++ b/data/etc/android.hardware.xr.input.controller.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature to indicate the device supports + input from XR controllers. --> +<permissions> + <feature name="android.hardware.xr.input.controller" /> +</permissions> diff --git a/data/etc/android.hardware.xr.input.eye_tracking.xml b/data/etc/android.hardware.xr.input.eye_tracking.xml new file mode 100644 index 0000000000..8c6c2ed7a6 --- /dev/null +++ b/data/etc/android.hardware.xr.input.eye_tracking.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature to indicate the device supports + input from an XR user's eyes. --> +<permissions> + <feature name="android.hardware.xr.input.eye_tracking" /> +</permissions> diff --git a/data/etc/android.hardware.xr.input.hand_tracking.xml b/data/etc/android.hardware.xr.input.hand_tracking.xml new file mode 100644 index 0000000000..6de3bee033 --- /dev/null +++ b/data/etc/android.hardware.xr.input.hand_tracking.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature to indicate the device supports + input from an XR user's hands. --> +<permissions> + <feature name="android.hardware.xr.input.hand_tracking" /> +</permissions> diff --git a/data/etc/android.software.xr.api.openxr-1_0.xml b/data/etc/android.software.xr.api.openxr-1_0.xml new file mode 100644 index 0000000000..71c4a94980 --- /dev/null +++ b/data/etc/android.software.xr.api.openxr-1_0.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature to indicate the device has a runtime + that supports OpenXR 1.0 (0x00010000). --> +<permissions> + <feature name="android.software.xr.api.openxr" version="65536" /> +</permissions> diff --git a/data/etc/android.software.xr.api.openxr-1_1.xml b/data/etc/android.software.xr.api.openxr-1_1.xml new file mode 100644 index 0000000000..45c106523d --- /dev/null +++ b/data/etc/android.software.xr.api.openxr-1_1.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature to indicate the device has a runtime + that supports OpenXR 1.1 (0x00010001). --> +<permissions> + <feature name="android.software.xr.api.openxr" version="65537" /> +</permissions> diff --git a/data/etc/android.software.xr.api.openxr-1_2.xml b/data/etc/android.software.xr.api.openxr-1_2.xml new file mode 100644 index 0000000000..ba11b8d498 --- /dev/null +++ b/data/etc/android.software.xr.api.openxr-1_2.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature to indicate the device has a runtime + that supports OpenXR 1.2 (0x00010002). --> +<permissions> + <feature name="android.software.xr.api.openxr" version="65538" /> +</permissions> diff --git a/data/etc/android.software.xr.api.spatial-1.xml b/data/etc/android.software.xr.api.spatial-1.xml new file mode 100644 index 0000000000..ce425aa526 --- /dev/null +++ b/data/etc/android.software.xr.api.spatial-1.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<!-- This is the standard feature to indicate the device has a runtime + that supports Android XR Spatial APIs, version 1. --> +<permissions> + <feature name="android.software.xr.api.spatial" version="1" /> +</permissions> diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml index 8df7fdbb16..a092842fa9 100644 --- a/data/etc/go_handheld_core_hardware.xml +++ b/data/etc/go_handheld_core_hardware.xml @@ -51,6 +51,9 @@ <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" /> + <!-- Feature to specify if the device support managed users. --> + <feature name="android.software.managed_users" /> + <!-- Devices with all optimizations required to support VR Mode and pass all CDD requirements for this feature may include android.hardware.vr.high_performance --> diff --git a/include/android/keycodes.h b/include/android/keycodes.h index 79cdbcaf7b..495e0bdb1f 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -843,6 +843,44 @@ enum { AKEYCODE_EMOJI_PICKER = 317, /** Take Screenshot */ AKEYCODE_SCREENSHOT = 318, + /** To start dictate to an input field */ + AKEYCODE_DICTATE = 319, + /** AC New */ + AKEYCODE_NEW = 320, + /** AC Close */ + AKEYCODE_CLOSE = 321, + /** To toggle 'Do Not Disturb' mode */ + AKEYCODE_DO_NOT_DISTURB = 322, + /** To Print */ + AKEYCODE_PRINT = 323, + /** To Lock the screen */ + AKEYCODE_LOCK = 324, + /** To toggle fullscreen mode (on the current application) */ + AKEYCODE_FULLSCREEN = 325, + /** F13 key */ + AKEYCODE_F13 = 326, + /** F14 key */ + AKEYCODE_F14 = 327, + /** F15 key */ + AKEYCODE_F15 = 328, + /** F16 key */ + AKEYCODE_F16 = 329, + /** F17 key */ + AKEYCODE_F17 = 330, + /** F18 key */ + AKEYCODE_F18 = 331, + /** F19 key */ + AKEYCODE_F19 = 332, + /** F20 key */ + AKEYCODE_F20 = 333, + /** F21 key */ + AKEYCODE_F21 = 334, + /** F22 key */ + AKEYCODE_F22 = 335, + /** F23 key */ + AKEYCODE_F23 = 336, + /** F24 key */ + AKEYCODE_F24 = 337, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 3f32a5abb3..3486e9b1d7 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -52,7 +52,6 @@ * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES */ -#include <android/api-level.h> #include <stdbool.h> #include <stdint.h> #include <unistd.h> diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 8d61e772cc..fe38e86015 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -763,6 +763,69 @@ void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* _No __INTRODUCED_IN(31); /** + * Sets the intended frame rate for the given \a surface_control. + * + * On devices that are capable of running the display at different frame rates, + * the system may choose a display refresh rate to better match this surface's frame + * rate. Usage of this API won't introduce frame rate throttling, or affect other + * aspects of the application's frame production pipeline. However, because the system + * may change the display refresh rate, calls to this function may result in changes + * to Choreographer callback timings, and changes to the time interval at which the + * system releases buffers back to the application. + * + * You can register for changes in the refresh rate using + * \a AChoreographer_registerRefreshRateCallback. + * + * See ASurfaceTransaction_clearFrameRate(). + * + * Available since API level 36. + * + * \param desiredMinRate The desired minimum frame rate (inclusive) for the surface, specifying that + * the surface prefers the device render rate to be at least `desiredMinRate`. + * + * <p>Set `desiredMinRate` = `desiredMaxRate` to indicate the surface prefers an exact frame rate. + * + * <p>Set `desiredMinRate` = 0 to indicate the surface has no preference + * and any frame rate is acceptable. + * + * <p>The value should be greater than or equal to 0. + * + * \param desiredMaxRate The desired maximum frame rate (inclusive) for the surface, specifying that + * the surface prefers the device render rate to be at most `desiredMaxRate`. + * + * <p>Set `desiredMaxRate` = `desiredMinRate` to indicate the surface prefers an exact frame rate. + * + * <p>Set `desiredMaxRate` = positive infinity to indicate the surface has no preference + * and any frame rate is acceptable. + * + * <p>The value should be greater than or equal to `desiredMinRate`. + * + * \param fixedSourceRate The "fixed source" frame rate of the surface if the content has an + * inherently fixed frame rate, e.g. a video that has a specific frame rate. + * + * <p>When the frame rate chosen for the surface is the `fixedSourceRate` or a + * multiple, the surface can render without frame pulldown, for optimal smoothness. For + * example, a 30 fps video (`fixedSourceRate`=30) renders just as smoothly on 30 fps, + * 60 fps, 90 fps, 120 fps, and so on. + * + * <p>Setting the fixed source rate can also be used together with a desired + * frame rate min and max via setting `desiredMinRate` and `desiredMaxRate`. This still + * means the surface's content has a fixed frame rate of `fixedSourceRate`, but additionally + * specifies the preference to be in the range [`desiredMinRate`, `desiredMaxRate`]. For example, an + * app might want to specify there is 30 fps video (`fixedSourceRate`=30) as well as a smooth + * animation on the same surface which looks good when drawing within a frame rate range such as + * [`desiredMinRate`, `desiredMaxRate`] = [60,120]. + * + * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this surface + * should be seamless. A seamless transition is one that doesn't have any visual interruptions, such + * as a black screen for a second or two. + */ +void ASurfaceTransaction_setFrameRateParams( + ASurfaceTransaction* _Nonnull transaction, ASurfaceControl* _Nonnull surface_control, + float desiredMinRate, float desiredMaxRate, float fixedSourceRate, + ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) __INTRODUCED_IN(36); + +/** * Clears the frame rate which is set for \a surface_control. * * This is equivalent to calling diff --git a/include/input/CoordinateFilter.h b/include/input/CoordinateFilter.h new file mode 100644 index 0000000000..f36472dc8c --- /dev/null +++ b/include/input/CoordinateFilter.h @@ -0,0 +1,54 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <chrono> + +#include <input/Input.h> +#include <input/OneEuroFilter.h> + +namespace android { + +/** + * Pair of OneEuroFilters that independently filter X and Y coordinates. Both filters share the same + * constructor's parameters. The minimum cutoff frequency is the base cutoff frequency, that is, the + * resulting cutoff frequency in the absence of signal's speed. Likewise, beta is a scaling factor + * of the signal's speed that sets how much the signal's speed contributes to the resulting cutoff + * frequency. The adaptive cutoff frequency criterion is f_c = f_c_min + β|̇x_filtered| + */ +class CoordinateFilter { +public: + explicit CoordinateFilter(float minCutoffFreq, float beta); + + /** + * Filters in place only the AXIS_X and AXIS_Y fields from coords. Each call to filter must + * provide a timestamp strictly greater than the timestamp of the previous call. The first time + * this method is invoked no filtering takes place. Subsequent calls do overwrite `coords` with + * filtered data. + * + * @param timestamp The timestamps at which to filter. It must be greater than the one passed in + * the previous call. + * @param coords Coordinates to be overwritten by the corresponding filtered coordinates. + */ + void filter(std::chrono::duration<float> timestamp, PointerCoords& coords); + +private: + OneEuroFilter mXFilter; + OneEuroFilter mYFilter; +}; + +} // namespace android
\ No newline at end of file diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h index 228347d818..2e346bb7cd 100644 --- a/include/input/InputConsumerNoResampling.h +++ b/include/input/InputConsumerNoResampling.h @@ -211,16 +211,17 @@ private: * `consumeBatchedInputEvents`. */ std::map<DeviceId, std::queue<InputMessage>> mBatches; + /** - * Creates a MotionEvent by consuming samples from the provided queue. If one message has - * eventTime > adjustedFrameTime, all subsequent messages in the queue will be skipped. It is - * assumed that messages are queued in chronological order. In other words, only events that - * occurred prior to the adjustedFrameTime will be consumed. - * @param requestedFrameTime the time up to which to consume events. - * @param messages the queue of messages to consume from + * Creates a MotionEvent by consuming samples from the provided queue. Consumes all messages + * with eventTime <= requestedFrameTime - resampleLatency, where `resampleLatency` is latency + * introduced by the resampler. Assumes that messages are queued in chronological order. + * @param requestedFrameTime The time up to which consume messages, as given by the inequality + * above. If std::nullopt, everything in messages will be consumed. + * @param messages the queue of messages to consume from. */ std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> createBatchedMotionEvent( - const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages); + const std::optional<nsecs_t> requestedFrameTime, std::queue<InputMessage>& messages); /** * Consumes the batched input events, optionally allowing the caller to specify a device id diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 6a248ef188..6b45dd39dc 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -131,8 +131,9 @@ enum class InputDeviceLightType : int32_t { PLAYER_ID = 1, KEYBOARD_BACKLIGHT = 2, KEYBOARD_MIC_MUTE = 3, + KEYBOARD_VOLUME_MUTE = 4, - ftl_last = KEYBOARD_MIC_MUTE + ftl_last = KEYBOARD_VOLUME_MUTE }; enum class InputDeviceLightCapability : uint32_t { diff --git a/include/input/OneEuroFilter.h b/include/input/OneEuroFilter.h new file mode 100644 index 0000000000..a0168e4f91 --- /dev/null +++ b/include/input/OneEuroFilter.h @@ -0,0 +1,101 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <chrono> +#include <optional> + +#include <input/Input.h> + +namespace android { + +/** + * Low pass filter with adaptive low pass frequency based on the signal's speed. The signal's cutoff + * frequency is determined by f_c = f_c_min + β|̇x_filtered|. Refer to + * https://dl.acm.org/doi/10.1145/2207676.2208639 for details on how the filter works and how to + * tune it. + */ +class OneEuroFilter { +public: + /** + * Default cutoff frequency of the filtered signal's speed. 1.0 Hz is the value in the filter's + * paper. + */ + static constexpr float kDefaultSpeedCutoffFreq = 1.0; + + OneEuroFilter() = delete; + + explicit OneEuroFilter(float minCutoffFreq, float beta, + float speedCutoffFreq = kDefaultSpeedCutoffFreq); + + OneEuroFilter(const OneEuroFilter&) = delete; + OneEuroFilter& operator=(const OneEuroFilter&) = delete; + OneEuroFilter(OneEuroFilter&&) = delete; + OneEuroFilter& operator=(OneEuroFilter&&) = delete; + + /** + * Returns the filtered value of rawPosition. Each call to filter must provide a timestamp + * strictly greater than the timestamp of the previous call. The first time the method is + * called, it returns the value of rawPosition. Any subsequent calls provide a filtered value. + * + * @param timestamp The timestamp at which to filter. It must be strictly greater than the one + * provided in the previous call. + * @param rawPosition Position to be filtered. + */ + float filter(std::chrono::duration<float> timestamp, float rawPosition); + +private: + /** + * Minimum cutoff frequency. This is the constant term in the adaptive cutoff frequency + * criterion. Units are Hertz. + */ + const float mMinCutoffFreq; + + /** + * Slope of the cutoff frequency criterion. This is the term scaling the absolute value of the + * filtered signal's speed. The data member is dimensionless, that is, it does not have units. + */ + const float mBeta; + + /** + * Cutoff frequency of the signal's speed. This is the cutoff frequency applied to the filtering + * of the signal's speed. Units are Hertz. + */ + const float mSpeedCutoffFreq; + + /** + * The timestamp from the previous call. Units are seconds. + */ + std::optional<std::chrono::duration<float>> mPrevTimestamp; + + /** + * The raw position from the previous call. + */ + std::optional<float> mPrevRawPosition; + + /** + * The filtered velocity from the previous call. Units are position per second. + */ + std::optional<float> mPrevFilteredVelocity; + + /** + * The filtered position from the previous call. + */ + std::optional<float> mPrevFilteredPosition; +}; + +} // namespace android diff --git a/include/input/Resampler.h b/include/input/Resampler.h index 6d95ca7e86..155097732c 100644 --- a/include/input/Resampler.h +++ b/include/input/Resampler.h @@ -19,11 +19,13 @@ #include <array> #include <chrono> #include <iterator> +#include <map> #include <optional> #include <vector> #include <android-base/logging.h> #include <ftl/mixins.h> +#include <input/CoordinateFilter.h> #include <input/Input.h> #include <input/InputTransport.h> #include <input/RingBuffer.h> @@ -293,4 +295,43 @@ private: inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent); }; +/** + * Resampler that first applies the LegacyResampler resampling algorithm, then independently filters + * the X and Y coordinates with a pair of One Euro filters. + */ +class FilteredLegacyResampler final : public Resampler { +public: + /** + * Creates a resampler, using the given minCutoffFreq and beta to instantiate its One Euro + * filters. + */ + explicit FilteredLegacyResampler(float minCutoffFreq, float beta); + + void resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime, MotionEvent& motionEvent, + const InputMessage* futureMessage) override; + + std::chrono::nanoseconds getResampleLatency() const override; + +private: + LegacyResampler mResampler; + + /** + * Minimum cutoff frequency of the value's low pass filter. Refer to OneEuroFilter class for a + * more detailed explanation. + */ + const float mMinCutoffFreq; + + /** + * Scaling factor of the adaptive cutoff frequency criterion. Refer to OneEuroFilter class for a + * more detailed explanation. + */ + const float mBeta; + + /* + * Note: an associative array with constant insertion and lookup times would be more efficient. + * When this was implemented, there was no container with these properties. + */ + std::map<int32_t /*pointerId*/, CoordinateFilter> mFilteredPointers; +}; + } // namespace android diff --git a/libs/battery/LongArrayMultiStateCounter.cpp b/libs/battery/LongArrayMultiStateCounter.cpp index 125cfaffa4..334d84b6b5 100644 --- a/libs/battery/LongArrayMultiStateCounter.cpp +++ b/libs/battery/LongArrayMultiStateCounter.cpp @@ -21,58 +21,137 @@ namespace android { namespace battery { -template <> -bool LongArrayMultiStateCounter::delta(const std::vector<uint64_t>& previousValue, - const std::vector<uint64_t>& newValue, - std::vector<uint64_t>* outValue) const { - size_t size = previousValue.size(); - if (newValue.size() != size) { - ALOGE("Incorrect array size: %d, should be %d", (int)newValue.size(), (int)size); - return false; +Uint64ArrayRW::Uint64ArrayRW(const Uint64Array ©) : Uint64Array(copy.size()) { + if (mSize != 0 && copy.data() != nullptr) { + mData = new uint64_t[mSize]; + memcpy(mData, copy.data(), mSize * sizeof(uint64_t)); + } else { + mData = nullptr; } +} - bool is_delta_valid = true; - for (int i = size - 1; i >= 0; i--) { - if (newValue[i] >= previousValue[i]) { - (*outValue)[i] = newValue[i] - previousValue[i]; +uint64_t *Uint64ArrayRW::dataRW() { + if (mData == nullptr) { + mData = new uint64_t[mSize]; + memset(mData, 0, mSize * sizeof(uint64_t)); + } + return mData; +} + +Uint64ArrayRW &Uint64ArrayRW::operator=(const Uint64Array &t) { + if (t.size() != mSize) { + delete[] mData; + mSize = t.size(); + mData = nullptr; + } + if (mSize != 0) { + if (t.data() != nullptr) { + if (mData == nullptr) { + mData = new uint64_t[mSize]; + } + memcpy(mData, t.data(), mSize * sizeof(uint64_t)); } else { - (*outValue)[i] = 0; - is_delta_valid = false; + delete[] mData; + mData = nullptr; } } - return is_delta_valid; + return *this; +} + +std::ostream &operator<<(std::ostream &os, const Uint64Array &v) { + os << "{"; + const uint64_t *data = v.data(); + if (data != nullptr) { + bool first = true; + for (size_t i = 0; i < v.size(); i++) { + if (!first) { + os << ", "; + } + os << data[i]; + first = false; + } + } + os << "}"; + return os; +} + +// Convenience constructor for tests +Uint64ArrayRW::Uint64ArrayRW(std::initializer_list<uint64_t> init) : Uint64Array(init.size()) { + mData = new uint64_t[mSize]; + memcpy(mData, init.begin(), mSize * sizeof(uint64_t)); +} + +// Used in tests only. +bool Uint64Array::operator==(const Uint64Array &other) const { + if (size() != other.size()) { + return false; + } + const uint64_t* thisData = data(); + const uint64_t* thatData = other.data(); + for (size_t i = 0; i < mSize; i++) { + const uint64_t v1 = thisData != nullptr ? thisData[i] : 0; + const uint64_t v2 = thatData != nullptr ? thatData[i] : 0; + if (v1 != v2) { + return false; + } + } + return true; } template <> -void LongArrayMultiStateCounter::add(std::vector<uint64_t>* value1, - const std::vector<uint64_t>& value2, const uint64_t numerator, - const uint64_t denominator) const { +void LongArrayMultiStateCounter::add(Uint64ArrayRW *value1, const Uint64Array &value2, + const uint64_t numerator, const uint64_t denominator) const { + const uint64_t* data2 = value2.data(); + if (data2 == nullptr) { + return; + } + + uint64_t* data1 = value1->dataRW(); + size_t size = value2.size(); if (numerator != denominator) { - for (int i = value2.size() - 1; i >= 0; i--) { + for (size_t i = 0; i < size; i++) { // The caller ensures that denominator != 0 - (*value1)[i] += value2[i] * numerator / denominator; + data1[i] += data2[i] * numerator / denominator; } } else { - for (int i = value2.size() - 1; i >= 0; i--) { - (*value1)[i] += value2[i]; + for (size_t i = 0; i < size; i++) { + data1[i] += data2[i]; } } } -template <> -std::string LongArrayMultiStateCounter::valueToString(const std::vector<uint64_t>& v) const { - std::stringstream s; - s << "{"; - bool first = true; - for (uint64_t n : v) { - if (!first) { - s << ", "; +template<> +bool LongArrayMultiStateCounter::delta(const Uint64ArrayRW &previousValue, + const Uint64Array &newValue, Uint64ArrayRW *outValue) const { + size_t size = previousValue.size(); + if (newValue.size() != size) { + ALOGE("Incorrect array size: %d, should be %d", (int) newValue.size(), (int) size); + return false; + } + if (outValue->size() != size) { + ALOGE("Incorrect outValue size: %d, should be %d", (int) outValue->size(), (int) size); + return false; + } + + bool is_delta_valid = true; + const uint64_t *prevData = previousValue.data(); + const uint64_t *newData = newValue.data(); + uint64_t *outData = outValue->dataRW(); + for (size_t i = 0; i < size; i++) { + if (prevData == nullptr) { + if (newData == nullptr) { + outData[i] = 0; + } else { + outData[i] = newData[i]; + } + } else if (newData == nullptr || newData[i] < prevData[i]) { + outData[i] = 0; + is_delta_valid = false; + } else { + outData[i] = newData[i] - prevData[i]; } - s << n; - first = false; } - s << "}"; - return s.str(); + return is_delta_valid; } } // namespace battery diff --git a/libs/battery/LongArrayMultiStateCounter.h b/libs/battery/LongArrayMultiStateCounter.h index f3439f6a0c..e00c96898e 100644 --- a/libs/battery/LongArrayMultiStateCounter.h +++ b/libs/battery/LongArrayMultiStateCounter.h @@ -23,7 +23,66 @@ namespace android { namespace battery { -typedef MultiStateCounter<std::vector<uint64_t>> LongArrayMultiStateCounter; +/** + * Wrapper for an array of uint64's. + */ +class Uint64Array { + protected: + size_t mSize; + + public: + Uint64Array() : Uint64Array(0) {} + + Uint64Array(size_t size) : mSize(size) {} + + virtual ~Uint64Array() {} + + size_t size() const { return mSize; } + + /** + * Returns the wrapped array. + * + * Nullable! Null should be interpreted the same as an array of zeros + */ + virtual const uint64_t *data() const { return nullptr; } + + friend std::ostream &operator<<(std::ostream &os, const Uint64Array &v); + + // Test API + bool operator==(const Uint64Array &other) const; +}; + +/** + * Mutable version of Uint64Array. + */ +class Uint64ArrayRW: public Uint64Array { + uint64_t* mData; + +public: + Uint64ArrayRW() : Uint64ArrayRW(0) {} + + Uint64ArrayRW(size_t size) : Uint64Array(size), mData(nullptr) {} + + Uint64ArrayRW(const Uint64Array ©); + + // Need an explicit copy constructor. In the initialization context C++ does not understand that + // a Uint64ArrayRW is a Uint64Array. + Uint64ArrayRW(const Uint64ArrayRW ©) : Uint64ArrayRW((const Uint64Array &) copy) {} + + // Test API + Uint64ArrayRW(std::initializer_list<uint64_t> init); + + ~Uint64ArrayRW() override { delete[] mData; } + + const uint64_t *data() const override { return mData; } + + // NonNull. Will initialize the wrapped array if it is null. + uint64_t *dataRW(); + + Uint64ArrayRW &operator=(const Uint64Array &t); +}; + +typedef MultiStateCounter<Uint64ArrayRW, Uint64Array> LongArrayMultiStateCounter; } // namespace battery } // namespace android diff --git a/libs/battery/LongArrayMultiStateCounterTest.cpp b/libs/battery/LongArrayMultiStateCounterTest.cpp index e4e6b2a49f..1c74e3fd14 100644 --- a/libs/battery/LongArrayMultiStateCounterTest.cpp +++ b/libs/battery/LongArrayMultiStateCounterTest.cpp @@ -24,25 +24,25 @@ namespace battery { class LongArrayMultiStateCounterTest : public testing::Test {}; TEST_F(LongArrayMultiStateCounterTest, stateChange) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); // Time was split in half between the two states, so the counts will be split 50:50 too - EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(0)); - EXPECT_EQ(std::vector<uint64_t>({50, 100, 150, 200}), testCounter.getCount(1)); + EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(0)); + EXPECT_EQ(Uint64ArrayRW({50, 100, 150, 200}), testCounter.getCount(1)); } TEST_F(LongArrayMultiStateCounterTest, accumulation) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); testCounter.setState(0, 4000); - testCounter.updateValue(std::vector<uint64_t>({200, 300, 400, 500}), 8000); + testCounter.updateValue(Uint64ArrayRW({200, 300, 400, 500}), 8000); // The first delta is split 50:50: // 0: {50, 100, 150, 200} @@ -50,16 +50,16 @@ TEST_F(LongArrayMultiStateCounterTest, accumulation) { // The second delta is split 4:1 // 0: {80, 80, 80, 80} // 1: {20, 20, 20, 20} - EXPECT_EQ(std::vector<uint64_t>({130, 180, 230, 280}), testCounter.getCount(0)); - EXPECT_EQ(std::vector<uint64_t>({70, 120, 170, 220}), testCounter.getCount(1)); + EXPECT_EQ(Uint64ArrayRW({130, 180, 230, 280}), testCounter.getCount(0)); + EXPECT_EQ(Uint64ArrayRW({70, 120, 170, 220}), testCounter.getCount(1)); } TEST_F(LongArrayMultiStateCounterTest, toString) { - LongArrayMultiStateCounter testCounter(2, std::vector<uint64_t>(4)); - testCounter.updateValue(std::vector<uint64_t>({0, 0, 0, 0}), 1000); + LongArrayMultiStateCounter testCounter(2, Uint64Array(4)); + testCounter.updateValue(Uint64ArrayRW({0, 0, 0, 0}), 1000); testCounter.setState(0, 1000); testCounter.setState(1, 2000); - testCounter.updateValue(std::vector<uint64_t>({100, 200, 300, 400}), 3000); + testCounter.updateValue(Uint64ArrayRW({100, 200, 300, 400}), 3000); EXPECT_STREQ("[0: {50, 100, 150, 200}, 1: {50, 100, 150, 200}] updated: 3000 currentState: 1", testCounter.toString().c_str()); diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h index 04b718698e..fadc4ffd41 100644 --- a/libs/battery/MultiStateCounter.h +++ b/libs/battery/MultiStateCounter.h @@ -35,12 +35,12 @@ namespace battery { typedef uint16_t state_t; -template <class T> +template <class T, class V> class MultiStateCounter { - uint16_t stateCount; + const uint16_t stateCount; + const V emptyValue; state_t currentState; time_t lastStateChangeTimestamp; - T emptyValue; T lastValue; time_t lastUpdateTimestamp; T deltaValue; @@ -54,7 +54,7 @@ class MultiStateCounter { State* states; public: - MultiStateCounter(uint16_t stateCount, const T& emptyValue); + MultiStateCounter(uint16_t stateCount, const V& emptyValue); virtual ~MultiStateCounter(); @@ -66,35 +66,35 @@ public: * Copies the current state and accumulated times-in-state from the source. Resets * the accumulated value. */ - void copyStatesFrom(const MultiStateCounter<T>& source); + void copyStatesFrom(const MultiStateCounter<T, V> &source); - void setValue(state_t state, const T& value); + void setValue(state_t state, const V& value); /** * Updates the value by distributing the delta from the previously set value * among states according to their respective time-in-state. * Returns the delta from the previously set value. */ - const T& updateValue(const T& value, time_t timestamp); + const V& updateValue(const V& value, time_t timestamp); /** * Updates the value by distributing the specified increment among states according * to their respective time-in-state. */ - void incrementValue(const T& increment, time_t timestamp); + void incrementValue(const V& increment, time_t timestamp); /** * Adds the specified increment to the value for the current state, without affecting * the last updated value or timestamp. Ignores partial time-in-state: the entirety of * the increment is given to the current state. */ - void addValue(const T& increment); + void addValue(const V& increment); void reset(); uint16_t getStateCount(); - const T& getCount(state_t state); + const V& getCount(state_t state); std::string toString(); @@ -104,27 +104,25 @@ private: * Returns true iff the combination of previousValue and newValue is valid * (newValue >= prevValue) */ - bool delta(const T& previousValue, const T& newValue, T* outValue) const; + bool delta(const T& previousValue, const V& newValue, T* outValue) const; /** * Adds value2 to value1 and stores the result in value1. Denominator is * guaranteed to be non-zero. */ - void add(T* value1, const T& value2, const uint64_t numerator, + void add(T* value1, const V& value2, const uint64_t numerator, const uint64_t denominator) const; - - std::string valueToString(const T& value) const; }; // ---------------------- MultiStateCounter Implementation ------------------------- // Since MultiStateCounter is a template, the implementation must be inlined. -template <class T> -MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue) +template <class T, class V> +MultiStateCounter<T, V>::MultiStateCounter(uint16_t stateCount, const V& emptyValue) : stateCount(stateCount), + emptyValue(emptyValue), currentState(0), lastStateChangeTimestamp(-1), - emptyValue(emptyValue), lastValue(emptyValue), lastUpdateTimestamp(-1), deltaValue(emptyValue), @@ -136,13 +134,13 @@ MultiStateCounter<T>::MultiStateCounter(uint16_t stateCount, const T& emptyValue } } -template <class T> -MultiStateCounter<T>::~MultiStateCounter() { +template <class T, class V> +MultiStateCounter<T, V>::~MultiStateCounter() { delete[] states; }; -template <class T> -void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::setEnabled(bool enabled, time_t timestamp) { if (enabled == isEnabled) { return; } @@ -167,8 +165,8 @@ void MultiStateCounter<T>::setEnabled(bool enabled, time_t timestamp) { } } -template <class T> -void MultiStateCounter<T>::setState(state_t state, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::setState(state_t state, time_t timestamp) { if (isEnabled && lastStateChangeTimestamp >= 0 && lastUpdateTimestamp >= 0) { // If the update arrived out-of-order, just push back the timestamp to // avoid having the situation where timeInStateSinceUpdate > timeSinceUpdate @@ -198,8 +196,8 @@ void MultiStateCounter<T>::setState(state_t state, time_t timestamp) { lastStateChangeTimestamp = timestamp; } -template <class T> -void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) { +template <class T, class V> +void MultiStateCounter<T, V>::copyStatesFrom(const MultiStateCounter<T, V>& source) { if (stateCount != source.stateCount) { ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount); return; @@ -214,14 +212,14 @@ void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) { lastUpdateTimestamp = source.lastUpdateTimestamp; } -template <class T> -void MultiStateCounter<T>::setValue(state_t state, const T& value) { +template <class T, class V> +void MultiStateCounter<T, V>::setValue(state_t state, const V& value) { states[state].counter = value; } -template <class T> -const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { - T* returnValue = &emptyValue; +template <class T, class V> +const V& MultiStateCounter<T, V>::updateValue(const V& value, time_t timestamp) { + const V* returnValue = &emptyValue; // If the counter is disabled, we ignore the update, except when the counter got disabled after // the previous update, in which case we still need to pick up the residual delta. @@ -250,8 +248,8 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { } } else { std::stringstream str; - str << "updateValue is called with a value " << valueToString(value) - << ", which is lower than the previous value " << valueToString(lastValue) + str << "updateValue is called with a value " << value + << ", which is lower than the previous value " << lastValue << "\n"; ALOGE("%s", str.str().c_str()); @@ -276,23 +274,25 @@ const T& MultiStateCounter<T>::updateValue(const T& value, time_t timestamp) { return *returnValue; } -template <class T> -void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) { +template <class T, class V> +void MultiStateCounter<T, V>::incrementValue(const V& increment, time_t timestamp) { +// T newValue; +// newValue = lastValue; // Copy assignment, not initialization. T newValue = lastValue; add(&newValue, increment, 1 /* numerator */, 1 /* denominator */); updateValue(newValue, timestamp); } -template <class T> -void MultiStateCounter<T>::addValue(const T& value) { +template <class T, class V> +void MultiStateCounter<T, V>::addValue(const V& value) { if (!isEnabled) { return; } add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */); } -template <class T> -void MultiStateCounter<T>::reset() { +template <class T, class V> +void MultiStateCounter<T, V>::reset() { lastStateChangeTimestamp = -1; lastUpdateTimestamp = -1; for (int i = 0; i < stateCount; i++) { @@ -301,25 +301,26 @@ void MultiStateCounter<T>::reset() { } } -template <class T> -uint16_t MultiStateCounter<T>::getStateCount() { +template <class T, class V> +uint16_t MultiStateCounter<T, V>::getStateCount() { return stateCount; } -template <class T> -const T& MultiStateCounter<T>::getCount(state_t state) { +template <class T, class V> +const V& MultiStateCounter<T, V>::getCount(state_t state) { return states[state].counter; } -template <class T> -std::string MultiStateCounter<T>::toString() { +template <class T, class V> +std::string MultiStateCounter<T, V>::toString() { std::stringstream str; +// str << "LAST VALUE: " << valueToString(lastValue); str << "["; for (int i = 0; i < stateCount; i++) { if (i != 0) { str << ", "; } - str << i << ": " << valueToString(states[i].counter); + str << i << ": " << states[i].counter; if (states[i].timeInStateSinceUpdate > 0) { str << " timeInStateSinceUpdate: " << states[i].timeInStateSinceUpdate; } diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp index a51a38a6c7..589b7fe4e3 100644 --- a/libs/battery/MultiStateCounterTest.cpp +++ b/libs/battery/MultiStateCounterTest.cpp @@ -21,7 +21,7 @@ namespace android { namespace battery { -typedef MultiStateCounter<double> DoubleMultiStateCounter; +typedef MultiStateCounter<double, double> DoubleMultiStateCounter; template <> bool DoubleMultiStateCounter::delta(const double& previousValue, const double& newValue, @@ -41,11 +41,6 @@ void DoubleMultiStateCounter::add(double* value1, const double& value2, const ui } } -template <> -std::string DoubleMultiStateCounter::valueToString(const double& v) const { - return std::to_string(v); -} - class MultiStateCounterTest : public testing::Test {}; TEST_F(MultiStateCounterTest, constructor) { diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 6903cb5d68..2ef642a3a0 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -871,6 +871,10 @@ cc_library { symbol_file: "libbinder_rpc_unstable.map.txt", }, + header_abi_checker: { + enabled: false, + }, + // This library is intentionally limited to these targets, and it will be removed later. // Do not expand the visibility. visibility: [ diff --git a/libs/binder/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp index f7b9f059a5..d32eecdc60 100644 --- a/libs/binder/BackendUnifiedServiceManager.cpp +++ b/libs/binder/BackendUnifiedServiceManager.cpp @@ -105,7 +105,8 @@ static const char* kStaticCachableList[] = { }; bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) { - if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() <= 0) { + sp<ProcessState> self = ProcessState::selfOrNull(); + if (!self || self->getThreadPoolMaxTotalThreadCount() <= 0) { ALOGW("Thread Pool max thread count is 0. Cannot cache binder as linkToDeath cannot be " "implemented. serviceName: %s", serviceName.c_str()); @@ -172,10 +173,6 @@ BackendUnifiedServiceManager::BackendUnifiedServiceManager(const sp<AidlServiceM mCacheForGetService = std::make_shared<BinderCacheWithInvalidation>(); } -sp<AidlServiceManager> BackendUnifiedServiceManager::getImpl() { - return mTheRealServiceManager; -} - Status BackendUnifiedServiceManager::getService(const ::std::string& name, sp<IBinder>* _aidl_return) { os::Service service; diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h index feb8470032..abc0eda7eb 100644 --- a/libs/binder/BackendUnifiedServiceManager.h +++ b/libs/binder/BackendUnifiedServiceManager.h @@ -121,7 +121,6 @@ class BackendUnifiedServiceManager : public android::os::BnServiceManager { public: explicit BackendUnifiedServiceManager(const sp<os::IServiceManager>& impl); - sp<os::IServiceManager> getImpl(); binder::Status getService(const ::std::string& name, sp<IBinder>* _aidl_return) override; binder::Status getService2(const ::std::string& name, os::Service* out) override; binder::Status checkService(const ::std::string& name, os::Service* out) override; diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 32388db076..39d8c2446e 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -561,8 +561,9 @@ sp<IBinder> CppBackendShim::getService(const String16& name) const { sp<IBinder> svc = checkService(name); if (svc != nullptr) return svc; + sp<ProcessState> self = ProcessState::selfOrNull(); const bool isVendorService = - strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0; + self && strcmp(self->getDriverName().c_str(), "/dev/vndbinder") == 0; constexpr auto timeout = 5s; const auto startTime = std::chrono::steady_clock::now(); // Vendor code can't access system properties @@ -579,7 +580,7 @@ sp<IBinder> CppBackendShim::getService(const String16& name) const { const useconds_t sleepTime = gSystemBootCompleted ? 1000 : 100; ALOGI("Waiting for service '%s' on '%s'...", String8(name).c_str(), - ProcessState::self()->getDriverName().c_str()); + self ? self->getDriverName().c_str() : "RPC accessors only"); int n = 0; while (std::chrono::steady_clock::now() - startTime < timeout) { @@ -661,7 +662,8 @@ sp<IBinder> CppBackendShim::waitForService(const String16& name16) { if (Status status = realGetService(name, &out); !status.isOk()) { ALOGW("Failed to getService in waitForService for %s: %s", name.c_str(), status.toString8().c_str()); - if (0 == ProcessState::self()->getThreadPoolMaxTotalThreadCount()) { + sp<ProcessState> self = ProcessState::selfOrNull(); + if (self && 0 == self->getThreadPoolMaxTotalThreadCount()) { ALOGW("Got service, but may be racey because we could not wait efficiently for it. " "Threadpool has 0 guaranteed threads. " "Is the threadpool configured properly? " @@ -695,9 +697,10 @@ sp<IBinder> CppBackendShim::waitForService(const String16& name16) { if (waiter->mBinder != nullptr) return waiter->mBinder; } + sp<ProcessState> self = ProcessState::selfOrNull(); ALOGW("Waited one second for %s (is service started? Number of threads started in the " "threadpool: %zu. Are binder threads started and available?)", - name.c_str(), ProcessState::self()->getThreadPoolMaxTotalThreadCount()); + name.c_str(), self ? self->getThreadPoolMaxTotalThreadCount() : 0); // Handle race condition for lazy services. Here is what can happen: // - the service dies (not processed by init yet). diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index 392ebb5b0a..48c0ea636b 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -37,8 +37,12 @@ enum class ARpcSession_FileDescriptorTransportMode { // Set `cid` to VMADDR_CID_LOCAL to only bind to the local vsock interface. // Returns an opaque handle to the running server instance, or null if the server // could not be started. +// Set |port| to VMADDR_PORT_ANY to pick an available ephemeral port. +// |assignedPort| will be set to the assigned port number if it is not null. +// This will be the provided |port|, or the chosen available ephemeral port when +// |port| is VMADDR_PORT_ANY. [[nodiscard]] ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, - unsigned int port); + unsigned int port, unsigned int* assignedPort); // Starts a Unix domain RPC server with an open raw socket file descriptor // and a given root IBinder object. diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index 21537fc50d..a84a0c6e0b 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -81,7 +81,8 @@ RpcSession::FileDescriptorTransportMode toTransportMode( extern "C" { #ifndef __TRUSTY__ -ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port) { +ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned int port, + unsigned int* assignedPort) { auto server = RpcServer::make(); unsigned int bindCid = VMADDR_CID_ANY; // bind to the remote interface @@ -90,7 +91,7 @@ ARpcServer* ARpcServer_newVsock(AIBinder* service, unsigned int cid, unsigned in cid = VMADDR_CID_ANY; // no need for a connection filter } - if (status_t status = server->setupVsockServer(bindCid, port); status != OK) { + if (status_t status = server->setupVsockServer(bindCid, port, assignedPort); status != OK) { ALOGE("Failed to set up vsock server with port %u error: %s", port, statusToString(status).c_str()); return nullptr; diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index a7423b3d2a..5710bbfa9f 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -82,7 +82,6 @@ cc_library { llndk: { symbol_file: "libbinder_ndk.map.txt", - export_llndk_headers: ["libvendorsupport_llndk_headers"], }, cflags: [ @@ -110,11 +109,9 @@ cc_library { ], header_libs: [ - "libvendorsupport_llndk_headers", "jni_headers", ], export_header_lib_headers: [ - "libvendorsupport_llndk_headers", "jni_headers", ], diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp index 886eb4bdf8..53ab68e643 100644 --- a/libs/binder/ndk/binder_rpc.cpp +++ b/libs/binder/ndk/binder_rpc.cpp @@ -104,8 +104,8 @@ struct OnDeleteProviderHolder { }; ABinderRpc_AccessorProvider* ABinderRpc_registerAccessorProvider( - ABinderRpc_AccessorProvider_getAccessorCallback provider, const char** instances, - size_t numInstances, void* data, + ABinderRpc_AccessorProvider_getAccessorCallback provider, + const char* const* const instances, size_t numInstances, void* data, ABinderRpc_AccessorProviderUserData_deleteCallback onDelete) { if (provider == nullptr) { ALOGE("Null provider passed to ABinderRpc_registerAccessorProvider"); diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h index 0ad110ee83..c6518d816f 100644 --- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h @@ -30,16 +30,14 @@ #include <android/binder_auto_utils.h> #include <android/binder_ibinder.h> -#if defined(__ANDROID_VENDOR_API__) -#include <android/llndk-versioning.h> -#elif !defined(API_LEVEL_AT_LEAST) #if defined(__BIONIC__) -#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \ - (__builtin_available(android sdk_api_level, *)) +#define API_LEVEL_AT_LEAST(sdk_api_level) __builtin_available(android sdk_api_level, *) +#elif defined(TRUSTY_USERSPACE) +// TODO(b/349936395): set to true for Trusty +#define API_LEVEL_AT_LEAST(sdk_api_level) (false) #else -#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true) +#define API_LEVEL_AT_LEAST(sdk_api_level) (true) #endif // __BIONIC__ -#endif // __ANDROID_VENDOR_API__ #if __has_include(<android/binder_shell.h>) #include <android/binder_shell.h> @@ -298,9 +296,8 @@ AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor, #endif #if defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36 - if API_LEVEL_AT_LEAST (36, 202504) { - if (codeToFunction != nullptr && - (&AIBinder_Class_setTransactionCodeToFunctionNameMap != nullptr)) { + if (API_LEVEL_AT_LEAST(36)) { + if (codeToFunction != nullptr) { AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction, functionCount); } diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h index 83976b3771..f3f3c3802a 100644 --- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h +++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h @@ -22,17 +22,14 @@ #include <set> #include <sstream> -// Include llndk-versioning.h only for non-system build as it is not available for NDK headers. -#if defined(__ANDROID_VENDOR_API__) -#include <android/llndk-versioning.h> -#elif !defined(API_LEVEL_AT_LEAST) #if defined(__BIONIC__) -#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \ - (__builtin_available(android sdk_api_level, *)) +#define API_LEVEL_AT_LEAST(sdk_api_level) __builtin_available(android sdk_api_level, *) +#elif defined(TRUSTY_USERSPACE) +// TODO(b/349936395): set to true for Trusty +#define API_LEVEL_AT_LEAST(sdk_api_level) (false) #else -#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true) +#define API_LEVEL_AT_LEAST(sdk_api_level) (true) #endif // __BIONIC__ -#endif // __ANDROID_VENDOR_API__ namespace aidl::android::os { @@ -44,7 +41,7 @@ namespace aidl::android::os { class PersistableBundle { public: PersistableBundle() noexcept { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { mPBundle = APersistableBundle_new(); } } @@ -54,13 +51,13 @@ class PersistableBundle { PersistableBundle(PersistableBundle&& other) noexcept : mPBundle(other.release()) {} // duplicates, does not take ownership of the APersistableBundle* PersistableBundle(const PersistableBundle& other) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { mPBundle = APersistableBundle_dup(other.mPBundle); } } // duplicates, does not take ownership of the APersistableBundle* PersistableBundle& operator=(const PersistableBundle& other) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { mPBundle = APersistableBundle_dup(other.mPBundle); } return *this; @@ -70,7 +67,7 @@ class PersistableBundle { binder_status_t readFromParcel(const AParcel* _Nonnull parcel) { reset(); - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return APersistableBundle_readFromParcel(parcel, &mPBundle); } else { return STATUS_INVALID_OPERATION; @@ -81,7 +78,7 @@ class PersistableBundle { if (!mPBundle) { return STATUS_BAD_VALUE; } - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return APersistableBundle_writeToParcel(mPBundle, parcel); } else { return STATUS_INVALID_OPERATION; @@ -96,7 +93,7 @@ class PersistableBundle { */ void reset(APersistableBundle* _Nullable pBundle = nullptr) noexcept { if (mPBundle) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { APersistableBundle_delete(mPBundle); } mPBundle = nullptr; @@ -109,7 +106,7 @@ class PersistableBundle { * what should be used to check for equality. */ bool deepEquals(const PersistableBundle& rhs) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return APersistableBundle_isEqual(get(), rhs.get()); } else { return false; @@ -148,7 +145,7 @@ class PersistableBundle { inline std::string toString() const { if (!mPBundle) { return "<PersistableBundle: null>"; - } else if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + } else if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { std::ostringstream os; os << "<PersistableBundle: "; os << "size: " << std::to_string(APersistableBundle_size(mPBundle)); @@ -159,7 +156,7 @@ class PersistableBundle { } int32_t size() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return APersistableBundle_size(mPBundle); } else { return 0; @@ -167,7 +164,7 @@ class PersistableBundle { } int32_t erase(const std::string& key) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return APersistableBundle_erase(mPBundle, key.c_str()); } else { return 0; @@ -175,37 +172,37 @@ class PersistableBundle { } void putBoolean(const std::string& key, bool val) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { APersistableBundle_putBoolean(mPBundle, key.c_str(), val); } } void putInt(const std::string& key, int32_t val) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { APersistableBundle_putInt(mPBundle, key.c_str(), val); } } void putLong(const std::string& key, int64_t val) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { APersistableBundle_putLong(mPBundle, key.c_str(), val); } } void putDouble(const std::string& key, double val) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { APersistableBundle_putDouble(mPBundle, key.c_str(), val); } } void putString(const std::string& key, const std::string& val) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { APersistableBundle_putString(mPBundle, key.c_str(), val.c_str()); } } void putBooleanVector(const std::string& key, const std::vector<bool>& vec) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { // std::vector<bool> has no ::data(). int32_t num = vec.size(); if (num > 0) { @@ -222,7 +219,7 @@ class PersistableBundle { } void putIntVector(const std::string& key, const std::vector<int32_t>& vec) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { int32_t num = vec.size(); if (num > 0) { APersistableBundle_putIntVector(mPBundle, key.c_str(), vec.data(), num); @@ -230,7 +227,7 @@ class PersistableBundle { } } void putLongVector(const std::string& key, const std::vector<int64_t>& vec) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { int32_t num = vec.size(); if (num > 0) { APersistableBundle_putLongVector(mPBundle, key.c_str(), vec.data(), num); @@ -238,7 +235,7 @@ class PersistableBundle { } } void putDoubleVector(const std::string& key, const std::vector<double>& vec) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { int32_t num = vec.size(); if (num > 0) { APersistableBundle_putDoubleVector(mPBundle, key.c_str(), vec.data(), num); @@ -246,7 +243,7 @@ class PersistableBundle { } } void putStringVector(const std::string& key, const std::vector<std::string>& vec) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { int32_t num = vec.size(); if (num > 0) { char** inVec = (char**)malloc(num * sizeof(char*)); @@ -261,13 +258,13 @@ class PersistableBundle { } } void putPersistableBundle(const std::string& key, const PersistableBundle& pBundle) { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { APersistableBundle_putPersistableBundle(mPBundle, key.c_str(), pBundle.mPBundle); } } bool getBoolean(const std::string& key, bool* _Nonnull val) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return APersistableBundle_getBoolean(mPBundle, key.c_str(), val); } else { return false; @@ -275,7 +272,7 @@ class PersistableBundle { } bool getInt(const std::string& key, int32_t* _Nonnull val) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return APersistableBundle_getInt(mPBundle, key.c_str(), val); } else { return false; @@ -283,7 +280,7 @@ class PersistableBundle { } bool getLong(const std::string& key, int64_t* _Nonnull val) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return APersistableBundle_getLong(mPBundle, key.c_str(), val); } else { return false; @@ -291,7 +288,7 @@ class PersistableBundle { } bool getDouble(const std::string& key, double* _Nonnull val) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return APersistableBundle_getDouble(mPBundle, key.c_str(), val); } else { return false; @@ -303,7 +300,7 @@ class PersistableBundle { } bool getString(const std::string& key, std::string* _Nonnull val) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { char* outString = nullptr; bool ret = APersistableBundle_getString(mPBundle, key.c_str(), &outString, &stringAllocator, nullptr); @@ -321,7 +318,7 @@ class PersistableBundle { const char* _Nonnull, T* _Nullable, int32_t), const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key, std::vector<T>* _Nonnull vec) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { int32_t bytes = 0; // call first with nullptr to get required size in bytes bytes = getVec(pBundle, key, nullptr, 0); @@ -343,28 +340,28 @@ class PersistableBundle { } bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(), vec); } return false; } bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(), vec); } return false; } bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(), vec); } return false; } bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle, key.c_str(), vec); } @@ -389,7 +386,7 @@ class PersistableBundle { } bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0, &stringAllocator, nullptr); if (bytes > 0) { @@ -406,7 +403,7 @@ class PersistableBundle { } bool getPersistableBundle(const std::string& key, PersistableBundle* _Nonnull val) const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { APersistableBundle* bundle = nullptr; bool ret = APersistableBundle_getPersistableBundle(mPBundle, key.c_str(), &bundle); if (ret) { @@ -438,77 +435,77 @@ class PersistableBundle { } std::set<std::string> getBooleanKeys() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getKeys(&APersistableBundle_getBooleanKeys, mPBundle); } else { return {}; } } std::set<std::string> getIntKeys() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getKeys(&APersistableBundle_getIntKeys, mPBundle); } else { return {}; } } std::set<std::string> getLongKeys() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getKeys(&APersistableBundle_getLongKeys, mPBundle); } else { return {}; } } std::set<std::string> getDoubleKeys() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getKeys(&APersistableBundle_getDoubleKeys, mPBundle); } else { return {}; } } std::set<std::string> getStringKeys() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getKeys(&APersistableBundle_getStringKeys, mPBundle); } else { return {}; } } std::set<std::string> getBooleanVectorKeys() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getKeys(&APersistableBundle_getBooleanVectorKeys, mPBundle); } else { return {}; } } std::set<std::string> getIntVectorKeys() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getKeys(&APersistableBundle_getIntVectorKeys, mPBundle); } else { return {}; } } std::set<std::string> getLongVectorKeys() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getKeys(&APersistableBundle_getLongVectorKeys, mPBundle); } else { return {}; } } std::set<std::string> getDoubleVectorKeys() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getKeys(&APersistableBundle_getDoubleVectorKeys, mPBundle); } else { return {}; } } std::set<std::string> getStringVectorKeys() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getKeys(&APersistableBundle_getStringVectorKeys, mPBundle); } else { return {}; } } std::set<std::string> getPersistableBundleKeys() const { - if API_LEVEL_AT_LEAST(__ANDROID_API_V__, 202404) { + if (API_LEVEL_AT_LEAST(__ANDROID_API_V__)) { return getKeys(&APersistableBundle_getPersistableBundleKeys, mPBundle); } else { return {}; diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h index 66667d33bd..7d54e2dad9 100644 --- a/libs/binder/ndk/include_platform/android/binder_rpc.h +++ b/libs/binder/ndk/include_platform/android/binder_rpc.h @@ -144,8 +144,9 @@ typedef void (*ABinderRpc_AccessorProviderUserData_deleteCallback)(void* _Nullab */ ABinderRpc_AccessorProvider* _Nullable ABinderRpc_registerAccessorProvider( ABinderRpc_AccessorProvider_getAccessorCallback _Nonnull provider, - const char* _Nullable* _Nonnull instances, size_t numInstances, void* _Nullable data, - ABinderRpc_AccessorProviderUserData_deleteCallback _Nullable onDelete) __INTRODUCED_IN(36); + const char* _Nullable const* const _Nonnull instances, size_t numInstances, + void* _Nullable data, ABinderRpc_AccessorProviderUserData_deleteCallback _Nullable onDelete) + __INTRODUCED_IN(36); /** * Remove an ABinderRpc_AccessorProvider from libbinder. This will remove references diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index 020ebcce95..8404a48c26 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -139,6 +139,9 @@ rust_bindgen { "--raw-line", "use libc::sockaddr;", ], + cflags: [ + "-DANDROID_PLATFORM", + ], shared_libs: [ "libbinder_ndk", ], @@ -179,6 +182,9 @@ rust_bindgen { // rustified "libbinder_ndk_bindgen_flags.txt", ], + cflags: [ + "-DANDROID_PLATFORM", + ], shared_libs: [ "libbinder_ndk_on_trusty_mock", "libc++", diff --git a/libs/binder/rust/Cargo.toml b/libs/binder/rust/Cargo.toml new file mode 100644 index 0000000000..e5738c574a --- /dev/null +++ b/libs/binder/rust/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "android-binder" +version = "0.1.0" +edition = "2021" +description = "Safe bindings to Android Binder, restricted to the NDK" +license = "Apache-2.0" + +[dependencies] +binder-ndk-sys = { package = "android-binder-ndk-sys", version = "0.1", path = "./sys" } +downcast-rs = "1.2.1" +libc = "0.2.159" + +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = ["cfg(android_vendor)", "cfg(android_ndk)", "cfg(android_vndk)", "cfg(trusty)"] diff --git a/libs/binder/rust/build.rs b/libs/binder/rust/build.rs new file mode 100644 index 0000000000..f3e6b53778 --- /dev/null +++ b/libs/binder/rust/build.rs @@ -0,0 +1,4 @@ +fn main() { + // Anything with cargo is NDK only. If you want to access anything else, use Soong. + println!("cargo::rustc-cfg=android_ndk"); +} diff --git a/libs/binder/rust/rpcbinder/src/server/android.rs b/libs/binder/rust/rpcbinder/src/server/android.rs index 2ab34472a9..74ce315e30 100644 --- a/libs/binder/rust/rpcbinder/src/server/android.rs +++ b/libs/binder/rust/rpcbinder/src/server/android.rs @@ -18,7 +18,7 @@ use crate::session::FileDescriptorTransportMode; use binder::{unstable_api::AsNative, SpIBinder}; use binder_rpc_unstable_bindgen::ARpcServer; use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; -use std::ffi::CString; +use std::ffi::{c_uint, CString}; use std::io::{Error, ErrorKind}; use std::os::unix::io::{IntoRawFd, OwnedFd}; @@ -42,18 +42,29 @@ impl RpcServer { /// Creates a binder RPC server, serving the supplied binder service implementation on the given /// vsock port. Only connections from the given CID are accepted. /// - // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client. - // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface. - pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> { + /// Set `cid` to [`libc::VMADDR_CID_ANY`] to accept connections from any client. + /// Set `cid` to [`libc::VMADDR_CID_LOCAL`] to only bind to the local vsock interface. + /// Set `port` to [`libc::VMADDR_PORT_ANY`] to pick an ephemeral port. + /// The assigned port is returned with RpcServer. + pub fn new_vsock( + mut service: SpIBinder, + cid: u32, + port: u32, + ) -> Result<(RpcServer, u32 /* assigned_port */), Error> { let service = service.as_native_mut(); + let mut assigned_port: c_uint = 0; // SAFETY: Service ownership is transferring to the server and won't be valid afterward. // Plus the binder objects are threadsafe. - unsafe { + let server = unsafe { Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock( - service, cid, port, - )) - } + service, + cid, + port, + &mut assigned_port, + ))? + }; + Ok((server, assigned_port as _)) } /// Creates a binder RPC server, serving the supplied binder service implementation on the given diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 23026e593c..8c0501ba2f 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -207,8 +207,10 @@ pub const LAST_CALL_TRANSACTION: TransactionCode = sys::LAST_CALL_TRANSACTION; /// Corresponds to TF_ONE_WAY -- an asynchronous call. pub const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY; /// Corresponds to TF_CLEAR_BUF -- clear transaction buffers after call is made. +#[cfg(not(android_ndk))] pub const FLAG_CLEAR_BUF: TransactionFlags = sys::FLAG_CLEAR_BUF; /// Set to the vendor flag if we are building for the VNDK, 0 otherwise +#[cfg(not(android_ndk))] pub const FLAG_PRIVATE_LOCAL: TransactionFlags = sys::FLAG_PRIVATE_LOCAL; /// Internal interface of binder local or remote objects for making @@ -221,7 +223,7 @@ pub trait IBinderInternal: IBinder { fn is_binder_alive(&self) -> bool; /// Indicate that the service intends to receive caller security contexts. - #[cfg(not(android_vndk))] + #[cfg(not(any(android_vndk, android_ndk)))] fn set_requesting_sid(&mut self, enable: bool); /// Dump this object to the given file handle @@ -346,7 +348,6 @@ impl InterfaceClass { panic!("Expected non-null class pointer from AIBinder_Class_define!"); } sys::AIBinder_Class_setOnDump(class, Some(I::on_dump)); - sys::AIBinder_Class_setHandleShellCommand(class, None); class }; InterfaceClass(ptr) @@ -714,7 +715,7 @@ unsafe impl<T, V: AsNative<T>> AsNative<T> for Option<V> { pub struct BinderFeatures { /// Indicates that the service intends to receive caller security contexts. This must be true /// for `ThreadState::with_calling_sid` to work. - #[cfg(not(android_vndk))] + #[cfg(not(any(android_vndk, android_ndk)))] pub set_requesting_sid: bool, // Ensure that clients include a ..BinderFeatures::default() to preserve backwards compatibility // when new fields are added. #[non_exhaustive] doesn't work because it prevents struct @@ -916,8 +917,12 @@ macro_rules! declare_binder_interface { impl $native { /// Create a new binder service. pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> { + #[cfg(not(android_ndk))] let mut binder = $crate::binder_impl::Binder::new_with_stability($native(Box::new(inner)), $stability); - #[cfg(not(android_vndk))] + #[cfg(android_ndk)] + let mut binder = $crate::binder_impl::Binder::new($native(Box::new(inner))); + + #[cfg(not(any(android_vndk, android_ndk)))] $crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid); $crate::Strong::new(Box::new(binder)) } diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs index f7f3f35c9f..14493db262 100644 --- a/libs/binder/rust/src/lib.rs +++ b/libs/binder/rust/src/lib.rs @@ -100,11 +100,11 @@ mod error; mod native; mod parcel; mod proxy; -#[cfg(not(trusty))] +#[cfg(not(any(trusty, android_ndk)))] mod service; -#[cfg(not(trusty))] +#[cfg(not(any(trusty, android_ndk)))] mod state; -#[cfg(not(any(android_vendor, android_vndk)))] +#[cfg(not(any(android_vendor, android_ndk, android_vndk)))] mod system_only; use binder_ndk_sys as sys; @@ -114,15 +114,18 @@ pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak}; pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode}; pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder}; pub use proxy::{DeathRecipient, SpIBinder, WpIBinder}; -#[cfg(not(trusty))] +#[cfg(not(any(trusty, android_ndk)))] pub use service::{ add_service, check_interface, check_service, force_lazy_services_persist, - get_declared_instances, get_interface, get_service, is_declared, is_handling_transaction, - register_lazy_service, wait_for_interface, wait_for_service, LazyServiceGuard, + get_declared_instances, is_declared, is_handling_transaction, register_lazy_service, + wait_for_interface, wait_for_service, LazyServiceGuard, }; -#[cfg(not(trusty))] +#[cfg(not(any(trusty, android_ndk)))] +#[allow(deprecated)] +pub use service::{get_interface, get_service}; +#[cfg(not(any(trusty, android_ndk)))] pub use state::{ProcessState, ThreadState}; -#[cfg(not(any(android_vendor, android_vndk)))] +#[cfg(not(any(android_vendor, android_vndk, android_ndk)))] pub use system_only::{delegate_accessor, Accessor, ConnectionInfo}; /// Binder result containing a [`Status`] on error. @@ -134,9 +137,10 @@ pub mod binder_impl { pub use crate::binder::{ IBinderInternal, InterfaceClass, LocalStabilityType, Remotable, Stability, StabilityType, ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, VintfStabilityType, - FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, - LAST_CALL_TRANSACTION, + FIRST_CALL_TRANSACTION, FLAG_ONEWAY, LAST_CALL_TRANSACTION, }; + #[cfg(not(android_ndk))] + pub use crate::binder::{FLAG_CLEAR_BUF, FLAG_PRIVATE_LOCAL}; pub use crate::binder_async::BinderAsyncRuntime; pub use crate::error::status_t; pub use crate::native::Binder; diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index c87cc94973..9e1cfd6b8f 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -14,9 +14,9 @@ * limitations under the License. */ -use crate::binder::{ - AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode, -}; +#[cfg(not(android_ndk))] +use crate::binder::Stability; +use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode}; use crate::error::{status_result, status_t, Result, StatusCode}; use crate::parcel::{BorrowedParcel, Serialize}; use crate::proxy::SpIBinder; @@ -76,14 +76,32 @@ impl<T: Remotable> Binder<T> { /// This moves the `rust_object` into an owned [`Box`] and Binder will /// manage its lifetime. pub fn new(rust_object: T) -> Binder<T> { - Self::new_with_stability(rust_object, Stability::default()) + #[cfg(not(android_ndk))] + { + Self::new_with_stability(rust_object, Stability::default()) + } + #[cfg(android_ndk)] + { + Self::new_unmarked(rust_object) + } } /// Create a new Binder remotable object with the given stability /// /// This moves the `rust_object` into an owned [`Box`] and Binder will /// manage its lifetime. + #[cfg(not(android_ndk))] pub fn new_with_stability(rust_object: T, stability: Stability) -> Binder<T> { + let mut binder = Self::new_unmarked(rust_object); + binder.mark_stability(stability); + binder + } + + /// Creates a new Binder remotable object with unset stability + /// + /// This is internal because normally we want to set the stability explicitly, + /// however for the NDK variant we cannot mark the stability. + fn new_unmarked(rust_object: T) -> Binder<T> { let class = T::get_class(); let rust_object = Box::into_raw(Box::new(rust_object)); // Safety: `AIBinder_new` expects a valid class pointer (which we @@ -93,9 +111,7 @@ impl<T: Remotable> Binder<T> { // decremented via `AIBinder_decStrong` when the reference lifetime // ends. let ibinder = unsafe { sys::AIBinder_new(class.into(), rust_object as *mut c_void) }; - let mut binder = Binder { ibinder, rust_object }; - binder.mark_stability(stability); - binder + Binder { ibinder, rust_object } } /// Set the extension of a binder interface. This allows a downstream @@ -189,6 +205,7 @@ impl<T: Remotable> Binder<T> { } /// Mark this binder object with the given stability guarantee + #[cfg(not(android_ndk))] fn mark_stability(&mut self, stability: Stability) { match stability { Stability::Local => self.mark_local_stability(), @@ -215,7 +232,7 @@ impl<T: Remotable> Binder<T> { /// Mark this binder object with local stability, which is vendor if we are /// building for android_vendor and system otherwise. - #[cfg(not(android_vendor))] + #[cfg(not(any(android_vendor, android_ndk)))] fn mark_local_stability(&mut self) { // Safety: Self always contains a valid `AIBinder` pointer, so we can // always call this C API safely. diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs index 3bfc425ee3..485b0bdb0d 100644 --- a/libs/binder/rust/src/parcel.rs +++ b/libs/binder/rust/src/parcel.rs @@ -197,6 +197,7 @@ unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> { // Data serialization methods impl<'a> BorrowedParcel<'a> { /// Data written to parcelable is zero'd before being deleted or reallocated. + #[cfg(not(android_ndk))] pub fn mark_sensitive(&mut self) { // Safety: guaranteed to have a parcel object, and this method never fails unsafe { sys::AParcel_markSensitive(self.as_native()) } @@ -342,6 +343,7 @@ impl<'a> WritableSubParcel<'a> { impl Parcel { /// Data written to parcelable is zero'd before being deleted or reallocated. + #[cfg(not(android_ndk))] pub fn mark_sensitive(&mut self) { self.borrowed().mark_sensitive() } diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 04f1517556..593d12c1e9 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -298,7 +298,7 @@ impl<T: AsNative<sys::AIBinder>> IBinderInternal for T { unsafe { sys::AIBinder_isAlive(self.as_native()) } } - #[cfg(not(android_vndk))] + #[cfg(not(any(android_vndk, android_ndk)))] fn set_requesting_sid(&mut self, enable: bool) { // Safety: `SpIBinder` guarantees that `self` always contains a valid // pointer to an `AIBinder`. diff --git a/libs/binder/rust/src/service.rs b/libs/binder/rust/src/service.rs index 29dd8e1f58..f4fdcf51c0 100644 --- a/libs/binder/rust/src/service.rs +++ b/libs/binder/rust/src/service.rs @@ -176,6 +176,7 @@ pub fn wait_for_service(name: &str) -> Option<SpIBinder> { /// seconds if it doesn't yet exist. #[deprecated = "this polls 5s, use wait_for_interface or check_interface"] pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> { + #[allow(deprecated)] interface_cast(get_service(name)) } diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp index bd666fe0d7..557f0e895d 100644 --- a/libs/binder/rust/sys/BinderBindings.hpp +++ b/libs/binder/rust/sys/BinderBindings.hpp @@ -15,15 +15,19 @@ */ #include <android/binder_ibinder.h> +#include <android/binder_parcel.h> +#include <android/binder_status.h> + +/* Platform only */ +#if defined(ANDROID_PLATFORM) || defined(__ANDROID_VENDOR__) #include <android/binder_ibinder_platform.h> #include <android/binder_manager.h> -#include <android/binder_parcel.h> #include <android/binder_parcel_platform.h> #include <android/binder_process.h> #include <android/binder_rpc.h> #include <android/binder_shell.h> #include <android/binder_stability.h> -#include <android/binder_status.h> +#endif namespace android { @@ -81,8 +85,10 @@ enum { enum { FLAG_ONEWAY = FLAG_ONEWAY, +#if defined(ANDROID_PLATFORM) || defined(__ANDROID_VENDOR__) FLAG_CLEAR_BUF = FLAG_CLEAR_BUF, FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_LOCAL, +#endif }; } // namespace consts diff --git a/libs/binder/rust/sys/Cargo.toml b/libs/binder/rust/sys/Cargo.toml new file mode 100644 index 0000000000..ad8e9c26f5 --- /dev/null +++ b/libs/binder/rust/sys/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "android-binder-ndk-sys" +version = "0.1.0" +edition = "2021" +description = "Bindgen bindings to android binder, restricted to the NDK" +license = "Apache-2.0" + +[dependencies] + +[lib] +path = "lib.rs" + +[build-dependencies] +bindgen = "0.70.1" diff --git a/libs/binder/rust/sys/build.rs b/libs/binder/rust/sys/build.rs new file mode 100644 index 0000000000..cb9c65ba51 --- /dev/null +++ b/libs/binder/rust/sys/build.rs @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::env; +use std::path::PathBuf; + +fn main() { + let ndk_home = PathBuf::from(env::var("ANDROID_NDK_HOME").unwrap()); + let toolchain = ndk_home.join("toolchains/llvm/prebuilt/linux-x86_64/"); + let sysroot = toolchain.join("sysroot"); + let bindings = bindgen::Builder::default() + .clang_arg(format!("--sysroot={}", sysroot.display())) + // TODO figure out what the "standard" #define is and use that instead + .header("BinderBindings.hpp") + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) + // Keep in sync with libbinder_ndk_bindgen_flags.txt + .default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true }) + .constified_enum("android::c_interface::consts::.*") + .allowlist_type("android::c_interface::.*") + .allowlist_type("AStatus") + .allowlist_type("AIBinder_Class") + .allowlist_type("AIBinder") + .allowlist_type("AIBinder_Weak") + .allowlist_type("AIBinder_DeathRecipient") + .allowlist_type("AParcel") + .allowlist_type("binder_status_t") + .blocklist_function("vprintf") + .blocklist_function("strtold") + .blocklist_function("_vtlog") + .blocklist_function("vscanf") + .blocklist_function("vfprintf_worker") + .blocklist_function("vsprintf") + .blocklist_function("vsnprintf") + .blocklist_function("vsnprintf_filtered") + .blocklist_function("vfscanf") + .blocklist_function("vsscanf") + .blocklist_function("vdprintf") + .blocklist_function("vasprintf") + .blocklist_function("strtold_l") + .allowlist_function(".*") + .generate() + .expect("Couldn't generate bindings"); + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings.write_to_file(out_path.join("bindings.rs")).expect("Couldn't write bindings."); + println!("cargo::rustc-link-lib=binder_ndk"); +} diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs index 5352473272..349e5a9cc8 100644 --- a/libs/binder/rust/sys/lib.rs +++ b/libs/binder/rust/sys/lib.rs @@ -20,6 +20,7 @@ use std::error::Error; use std::fmt; #[cfg(not(target_os = "trusty"))] +#[allow(bad_style)] mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp index 6301c74842..a62ad96be1 100644 --- a/libs/binder/tests/binderUtilsHostTest.cpp +++ b/libs/binder/tests/binderUtilsHostTest.cpp @@ -56,7 +56,7 @@ TEST(UtilsHost, ExecuteLongRunning) { }); auto elapsedMs = millisSince(start); EXPECT_GE(elapsedMs, 1000); - EXPECT_LT(elapsedMs, 2000); + EXPECT_LT(elapsedMs, 3000); // b/377571547: higher to reduce flake ASSERT_TRUE(result.has_value()); EXPECT_EQ(std::nullopt, result->exitCode); @@ -65,7 +65,7 @@ TEST(UtilsHost, ExecuteLongRunning) { // ~CommandResult() called, child process is killed. // Assert that the second sleep does not finish. - EXPECT_LT(millisSince(start), 2000); + EXPECT_LT(millisSince(start), 3000); } TEST(UtilsHost, ExecuteLongRunning2) { diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 1243b214d3..1e33abbdea 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -274,6 +274,7 @@ filegroup { "LayerMetadata.cpp", "LayerStatePermissions.cpp", "LayerState.cpp", + "DisplayLuts.cpp", "OccupancyTracker.cpp", "StreamSplitter.cpp", "ScreenCaptureResults.cpp", @@ -341,6 +342,10 @@ cc_library_shared { "libgui_aidl_headers", ], + static_libs: [ + "libsurfaceflingerflags", + ], + afdo: true, lto: { diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 49f4cba284..7aee90393b 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -52,19 +52,18 @@ using namespace std::chrono_literals; namespace { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) -// RAII wrapper to defer arbitrary work until the Deferred instance is deleted. -template <class F> -class Deferred { +template <class Mutex> +class UnlockGuard { public: - explicit Deferred(F f) : mF{std::move(f)} {} + explicit UnlockGuard(Mutex& lock) : mLock{lock} { mLock.unlock(); } - ~Deferred() { mF(); } + ~UnlockGuard() { mLock.lock(); } - Deferred(const Deferred&) = delete; - Deferred& operator=(const Deferred&) = delete; + UnlockGuard(const UnlockGuard&) = delete; + UnlockGuard& operator=(const UnlockGuard&) = delete; private: - F mF; + Mutex& mLock; }; #endif @@ -271,9 +270,6 @@ BLASTBufferQueue::~BLASTBufferQueue() { void BLASTBufferQueue::onFirstRef() { // safe default, most producers are expected to override this mProducer->setMaxDequeuedBufferCount(2); -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - mBufferReleaseThread.emplace(sp<BLASTBufferQueue>::fromExisting(this)); -#endif } void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, @@ -290,18 +286,23 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, if (surfaceControlChanged && mSurfaceControl != nullptr) { BQA_LOGD("Updating SurfaceControl without recreating BBQ"); } - bool applyTransaction = false; // Always update the native object even though they might have the same layer handle, so we can // get the updated transform hint from WM. mSurfaceControl = surface; SurfaceComposerClient::Transaction t; + bool applyTransaction = false; if (surfaceControlChanged) { - t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, - layer_state_t::eEnableBackpressure); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - t.setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer); + updateBufferReleaseProducer(); #endif + t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, + layer_state_t::eEnableBackpressure); + // Migrate the picture profile handle to the new surface control. + if (com_android_graphics_libgui_flags_apply_picture_profiles() && + mPictureProfileHandle.has_value()) { + t.setPictureProfileHandle(mSurfaceControl, *mPictureProfileHandle); + } applyTransaction = true; } mTransformHint = mSurfaceControl->getTransformHint(); @@ -325,7 +326,7 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, } if (applyTransaction) { // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction - t.setApplyToken(mApplyToken).apply(false, true); + t.setApplyToken(mApplyToken).apply(false /* synchronous */, true /* oneWay */); } } @@ -419,7 +420,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.latchTime, stat.frameEventStats.dequeueReadyTime); } -#if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) auto currFrameNumber = stat.frameEventStats.frameNumber; std::vector<ReleaseCallbackId> staleReleases; for (const auto& [key, value]: mSubmitted) { @@ -435,7 +435,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.currentMaxAcquiredBufferCount, true /* fakeRelease */); } -#endif } else { BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); } @@ -469,6 +468,9 @@ ReleaseBufferCallback BLASTBufferQueue::makeReleaseBufferCallbackThunk() { return; } bbq->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount); +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) + bbq->drainBufferReleaseConsumer(); +#endif }; } @@ -535,8 +537,6 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence) { auto it = mSubmitted.find(callbackId); if (it == mSubmitted.end()) { - BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s", - callbackId.to_string().c_str()); return; } mNumAcquired--; @@ -646,12 +646,7 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, bufferItem.mScalingMode, crop); -#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) - ReleaseBufferCallback releaseBufferCallback = - applyTransaction ? nullptr : makeReleaseBufferCallbackThunk(); -#else auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); -#endif sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; nsecs_t dequeueTime = -1; @@ -689,6 +684,17 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( if (!bufferItem.mIsAutoTimestamp) { t->setDesiredPresentTime(bufferItem.mTimestamp); } + if (com_android_graphics_libgui_flags_apply_picture_profiles() && + bufferItem.mPictureProfileHandle.has_value()) { + t->setPictureProfileHandle(mSurfaceControl, *bufferItem.mPictureProfileHandle); + // The current picture profile must be maintained in case the BBQ gets its + // SurfaceControl switched out. + mPictureProfileHandle = bufferItem.mPictureProfileHandle; + // Clear out the picture profile if the requestor has asked for it to be cleared + if (mPictureProfileHandle == PictureProfileHandle::NONE) { + mPictureProfileHandle = std::nullopt; + } + } // Drop stale frame timeline infos while (!mPendingFrameTimelines.empty() && @@ -1230,12 +1236,7 @@ public: // we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure // we don't miss an interrupt. bbq->mBufferReleaseReader->clearInterrupts(); - bbq->mThreadsBlockingOnDequeue++; - bufferQueueLock.unlock(); - Deferred cleanup{[&]() { - bufferQueueLock.lock(); - bbq->mThreadsBlockingOnDequeue--; - }}; + UnlockGuard unlockGuard{bufferQueueLock}; ATRACE_FORMAT("waiting for free buffer"); ReleaseCallbackId id; @@ -1345,6 +1346,35 @@ void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) +void BLASTBufferQueue::updateBufferReleaseProducer() { + // SELinux policy may prevent this process from sending the BufferReleaseChannel's file + // descriptor to SurfaceFlinger, causing the entire transaction to be dropped. We send this + // transaction independently of any other updates to ensure those updates aren't lost. + SurfaceComposerClient::Transaction t; + status_t status = t.setApplyToken(mApplyToken) + .setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer) + .apply(false /* synchronous */, true /* oneWay */); + if (status != OK) { + ALOGW("[%s] %s - failed to set buffer release channel on %s", mName.c_str(), + statusToString(status).c_str(), mSurfaceControl->getName().c_str()); + } +} + +void BLASTBufferQueue::drainBufferReleaseConsumer() { + ATRACE_CALL(); + while (true) { + ReleaseCallbackId id; + sp<Fence> fence; + uint32_t maxAcquiredBufferCount; + status_t status = + mBufferReleaseConsumer->readReleaseFence(id, fence, maxAcquiredBufferCount); + if (status != OK) { + return; + } + releaseBufferCallback(id, fence, maxAcquiredBufferCount); + } +} + BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(BLASTBufferQueue& bbq) : mBbq{bbq} { mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)}; LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(), @@ -1438,95 +1468,6 @@ void BLASTBufferQueue::BufferReleaseReader::clearInterrupts() { } } -BLASTBufferQueue::BufferReleaseThread::BufferReleaseThread(const sp<BLASTBufferQueue>& bbq) { - android::base::unique_fd epollFd{epoll_create1(EPOLL_CLOEXEC)}; - LOG_ALWAYS_FATAL_IF(!epollFd.ok(), - "Failed to create buffer release background thread epoll file descriptor. " - "errno=%d message='%s'", - errno, strerror(errno)); - - epoll_event registerEndpointFd{}; - registerEndpointFd.events = EPOLLIN; - registerEndpointFd.data.fd = bbq->mBufferReleaseConsumer->getFd(); - status_t status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, bbq->mBufferReleaseConsumer->getFd(), - ®isterEndpointFd); - LOG_ALWAYS_FATAL_IF(status == -1, - "Failed to register background thread buffer release consumer file " - "descriptor with epoll. errno=%d message='%s'", - errno, strerror(errno)); - - // EventFd is used to break the background thread's loop. - android::base::unique_fd eventFd{eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)}; - LOG_ALWAYS_FATAL_IF(!eventFd.ok(), - "Failed to create background thread buffer release event file descriptor. " - "errno=%d message='%s'", - errno, strerror(errno)); - - epoll_event registerEventFd{}; - registerEventFd.events = EPOLLIN; - registerEventFd.data.fd = eventFd.get(); - status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), ®isterEventFd); - LOG_ALWAYS_FATAL_IF(status == -1, - "Failed to register background thread event file descriptor with epoll. " - "errno=%d message='%s'", - errno, strerror(errno)); - - mEventFd = eventFd.get(); - - std::thread([epollFd = std::move(epollFd), eventFd = std::move(eventFd), - weakBbq = wp<BLASTBufferQueue>(bbq)]() { - pthread_setname_np(pthread_self(), "BufferReleaseThread"); - while (true) { - epoll_event event{}; - int eventCount; - do { - eventCount = epoll_wait(epollFd.get(), &event, 1 /*maxevents*/, -1 /*timeout*/); - } while (eventCount == -1 && errno != EINTR); - - if (eventCount == -1) { - ALOGE("epoll_wait error while waiting for buffer release in background thread. " - "errno=%d message='%s'", - errno, strerror(errno)); - continue; - } - - // EventFd is used to join this thread. - if (event.data.fd == eventFd.get()) { - return; - } - - sp<BLASTBufferQueue> bbq = weakBbq.promote(); - if (!bbq) { - return; - } - - // If there are threads blocking on dequeue, give those threads priority for handling - // the release. - if (bbq->mThreadsBlockingOnDequeue > 0) { - std::this_thread::sleep_for(0ms); - continue; - } - - ReleaseCallbackId id; - sp<Fence> fence; - uint32_t maxAcquiredBufferCount; - status_t status = bbq->mBufferReleaseConsumer->readReleaseFence(id, fence, - maxAcquiredBufferCount); - if (status != OK) { - ALOGE("failed to read from buffer release consumer in background thread. errno=%d " - "message='%s'", - errno, strerror(errno)); - continue; - } - bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount); - } - }).detach(); -} - -BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() { - eventfd_write(mEventFd, 1); -} - #endif } // namespace android diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp index 5beba02e63..3b2d337a21 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp @@ -38,26 +38,25 @@ static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { return static_cast<T>(static_cast<uint64_t>(hi)<<32 | lo); } -BufferItem::BufferItem() : - mGraphicBuffer(nullptr), - mFence(nullptr), - mCrop(Rect::INVALID_RECT), - mTransform(0), - mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mTimestamp(0), - mIsAutoTimestamp(false), - mDataSpace(HAL_DATASPACE_UNKNOWN), - mFrameNumber(0), - mSlot(INVALID_BUFFER_SLOT), - mIsDroppable(false), - mAcquireCalled(false), - mTransformToDisplayInverse(false), - mSurfaceDamage(), - mAutoRefresh(false), - mQueuedBuffer(true), - mIsStale(false), - mApi(0) { -} +BufferItem::BufferItem() + : mGraphicBuffer(nullptr), + mFence(nullptr), + mCrop(Rect::INVALID_RECT), + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mIsAutoTimestamp(false), + mDataSpace(HAL_DATASPACE_UNKNOWN), + mFrameNumber(0), + mSlot(INVALID_BUFFER_SLOT), + mIsDroppable(false), + mAcquireCalled(false), + mTransformToDisplayInverse(false), + mSurfaceDamage(), + mAutoRefresh(false), + mQueuedBuffer(true), + mIsStale(false), + mApi(0) {} BufferItem::~BufferItem() {} @@ -76,6 +75,11 @@ size_t BufferItem::getPodSize() const { addAligned(size, high32(mTimestamp)); addAligned(size, mIsAutoTimestamp); addAligned(size, mDataSpace); +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + addAligned(size, mPictureProfileHandle.has_value()); + addAligned(size, low32(PictureProfileHandle::NONE.getId())); + addAligned(size, high32(PictureProfileHandle::NONE.getId())); +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES addAligned(size, low32(mFrameNumber)); addAligned(size, high32(mFrameNumber)); addAligned(size, mSlot); @@ -170,6 +174,16 @@ status_t BufferItem::flatten( writeAligned(buffer, size, high32(mTimestamp)); writeAligned(buffer, size, mIsAutoTimestamp); writeAligned(buffer, size, mDataSpace); +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + writeAligned(buffer, size, mPictureProfileHandle.has_value()); + if (mPictureProfileHandle.has_value()) { + writeAligned(buffer, size, low32(mPictureProfileHandle->getId())); + writeAligned(buffer, size, high32(mPictureProfileHandle->getId())); + } else { + writeAligned(buffer, size, low32(PictureProfileHandle::NONE.getId())); + writeAligned(buffer, size, high32(PictureProfileHandle::NONE.getId())); + } +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES writeAligned(buffer, size, low32(mFrameNumber)); writeAligned(buffer, size, high32(mFrameNumber)); writeAligned(buffer, size, mSlot); @@ -231,6 +245,7 @@ status_t BufferItem::unflatten( uint32_t timestampLo = 0, timestampHi = 0; uint32_t frameNumberLo = 0, frameNumberHi = 0; + int32_t pictureProfileIdLo = 0, pictureProfileIdHi = 0; readAligned(buffer, size, mCrop); readAligned(buffer, size, mTransform); @@ -240,6 +255,16 @@ status_t BufferItem::unflatten( mTimestamp = to64<int64_t>(timestampLo, timestampHi); readAligned(buffer, size, mIsAutoTimestamp); readAligned(buffer, size, mDataSpace); +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + bool hasPictureProfileHandle; + readAligned(buffer, size, hasPictureProfileHandle); + readAligned(buffer, size, pictureProfileIdLo); + readAligned(buffer, size, pictureProfileIdHi); + mPictureProfileHandle = hasPictureProfileHandle + ? std::optional(PictureProfileHandle( + to64<PictureProfileId>(pictureProfileIdLo, pictureProfileIdHi))) + : std::nullopt; +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES readAligned(buffer, size, frameNumberLo); readAligned(buffer, size, frameNumberHi); mFrameNumber = to64<uint64_t>(frameNumberLo, frameNumberHi); diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 473a374a59..39209f9745 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -938,6 +938,8 @@ status_t BufferQueueProducer::queueBuffer(int slot, &getFrameTimestamps); const Region& surfaceDamage = input.getSurfaceDamage(); const HdrMetadata& hdrMetadata = input.getHdrMetadata(); + const std::optional<PictureProfileHandle>& pictureProfileHandle = + input.getPictureProfileHandle(); if (acquireFence == nullptr) { BQ_LOGE("queueBuffer: fence is NULL"); @@ -1044,6 +1046,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, item.mIsAutoTimestamp = isAutoTimestamp; item.mDataSpace = dataSpace; item.mHdrMetadata = hdrMetadata; + item.mPictureProfileHandle = pictureProfileHandle; item.mFrameNumber = currentFrameNumber; item.mSlot = slot; item.mFence = acquireFence; diff --git a/libs/gui/BufferReleaseChannel.cpp b/libs/gui/BufferReleaseChannel.cpp index e9c6ef3512..e9cb013baf 100644 --- a/libs/gui/BufferReleaseChannel.cpp +++ b/libs/gui/BufferReleaseChannel.cpp @@ -35,35 +35,35 @@ namespace android::gui { namespace { template <typename T> -static void readAligned(const void*& buffer, size_t& size, T& value) { +void readAligned(const void*& buffer, size_t& size, T& value) { size -= FlattenableUtils::align<alignof(T)>(buffer); FlattenableUtils::read(buffer, size, value); } template <typename T> -static void writeAligned(void*& buffer, size_t& size, T value) { +void writeAligned(void*& buffer, size_t& size, T value) { size -= FlattenableUtils::align<alignof(T)>(buffer); FlattenableUtils::write(buffer, size, value); } template <typename T> -static void addAligned(size_t& size, T /* value */) { +void addAligned(size_t& size, T /* value */) { size = FlattenableUtils::align<sizeof(T)>(size); size += sizeof(T); } template <typename T> -static inline constexpr uint32_t low32(const T n) { +inline constexpr uint32_t low32(const T n) { return static_cast<uint32_t>(static_cast<uint64_t>(n)); } template <typename T> -static inline constexpr uint32_t high32(const T n) { +inline constexpr uint32_t high32(const T n) { return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32); } template <typename T> -static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { +inline constexpr T to64(const uint32_t lo, const uint32_t hi) { return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo); } @@ -139,19 +139,18 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( std::lock_guard lock{mMutex}; Message message; mFlattenedBuffer.resize(message.getFlattenedSize()); - std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; + std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{}; iovec iov{ .iov_base = mFlattenedBuffer.data(), .iov_len = mFlattenedBuffer.size(), }; - msghdr msg{ - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = controlMessageBuffer.data(), - .msg_controllen = controlMessageBuffer.size(), - }; + msghdr msg{}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = controlMessageBuffer.data(); + msg.msg_controllen = controlMessageBuffer.size(); ssize_t result; do { @@ -161,7 +160,7 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( if (errno == EWOULDBLOCK || errno == EAGAIN) { return WOULD_BLOCK; } - ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno)); + ALOGE("Error reading release fence from socket: error %d (%s)", errno, strerror(errno)); return UNKNOWN_ERROR; } @@ -200,9 +199,9 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( return OK; } -int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId, - const sp<Fence>& fence, - uint32_t maxAcquiredBufferCount) { +status_t BufferReleaseChannel::ProducerEndpoint::writeReleaseFence( + const ReleaseCallbackId& callbackId, const sp<Fence>& fence, + uint32_t maxAcquiredBufferCount) { Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount}; mFlattenedBuffer.resize(message.getFlattenedSize()); int flattenedFd; @@ -213,25 +212,22 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb size_t flattenedBufferSize = mFlattenedBuffer.size(); int* flattenedFdPtr = &flattenedFd; size_t flattenedFdCount = 1; - if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr, - flattenedFdCount); - err != OK) { - ALOGE("Failed to flatten BufferReleaseChannel message."); - return err; + if (status_t status = message.flatten(flattenedBufferPtr, flattenedBufferSize, + flattenedFdPtr, flattenedFdCount); + status != OK) { + return status; } } - iovec iov{ - .iov_base = mFlattenedBuffer.data(), - .iov_len = mFlattenedBuffer.size(), - }; + iovec iov{}; + iov.iov_base = mFlattenedBuffer.data(); + iov.iov_len = mFlattenedBuffer.size(); - msghdr msg{ - .msg_iov = &iov, - .msg_iovlen = 1, - }; + msghdr msg{}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; - std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; + std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{}; if (fence && fence->isValid()) { msg.msg_control = controlMessageBuffer.data(); msg.msg_controllen = controlMessageBuffer.size(); @@ -248,7 +244,6 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb result = sendmsg(mFd, &msg, 0); } while (result == -1 && errno == EINTR); if (result == -1) { - ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno)); return -errno; } @@ -344,13 +339,6 @@ status_t BufferReleaseChannel::open(std::string name, return -errno; } - // Make the producer write-only - if (shutdown(producerFd.get(), SHUT_RD) == -1) { - ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'", - name.c_str(), errno, strerror(errno)); - return -errno; - } - outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd)); outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd)); return STATUS_OK; diff --git a/libs/gui/DisplayLuts.cpp b/libs/gui/DisplayLuts.cpp new file mode 100644 index 0000000000..80429765be --- /dev/null +++ b/libs/gui/DisplayLuts.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "include/gui/DisplayLuts.h" +#include <gui/DisplayLuts.h> +#include <private/gui/ParcelUtils.h> + +namespace android::gui { + +status_t DisplayLuts::Entry::readFromParcel(const android::Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + SAFE_PARCEL(parcel->readInt32, &dimension); + SAFE_PARCEL(parcel->readInt32, &size); + SAFE_PARCEL(parcel->readInt32, &samplingKey); + + return OK; +} + +status_t DisplayLuts::Entry::writeToParcel(android::Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + SAFE_PARCEL(parcel->writeInt32, dimension); + SAFE_PARCEL(parcel->writeInt32, size); + SAFE_PARCEL(parcel->writeInt32, samplingKey); + + return OK; +} + +status_t DisplayLuts::readFromParcel(const android::Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + SAFE_PARCEL(parcel->readUniqueFileDescriptor, &fd); + SAFE_PARCEL(parcel->readInt32Vector, &offsets); + int32_t numLutProperties; + SAFE_PARCEL(parcel->readInt32, &numLutProperties); + lutProperties.reserve(numLutProperties); + for (int32_t i = 0; i < numLutProperties; i++) { + lutProperties.push_back({}); + SAFE_PARCEL(lutProperties.back().readFromParcel, parcel); + } + return OK; +} + +status_t DisplayLuts::writeToParcel(android::Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + + SAFE_PARCEL(parcel->writeUniqueFileDescriptor, fd); + SAFE_PARCEL(parcel->writeInt32Vector, offsets); + SAFE_PARCEL(parcel->writeInt32, static_cast<int32_t>(lutProperties.size())); + for (auto& entry : lutProperties) { + SAFE_PARCEL(entry.writeToParcel, parcel); + } + return OK; +} +} // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp index c8b9b6751d..4e92a39973 100644 --- a/libs/gui/IGraphicBufferProducerFlattenables.cpp +++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp @@ -20,21 +20,19 @@ namespace android { constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() { - return sizeof(timestamp) + - sizeof(isAutoTimestamp) + - sizeof(dataSpace) + - sizeof(crop) + - sizeof(scalingMode) + - sizeof(transform) + - sizeof(stickyTransform) + - sizeof(getFrameTimestamps) + - sizeof(slot); + return sizeof(timestamp) + sizeof(isAutoTimestamp) + sizeof(dataSpace) + sizeof(crop) + + sizeof(scalingMode) + sizeof(transform) + sizeof(stickyTransform) + + sizeof(getFrameTimestamps) + sizeof(slot) + +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + sizeof(decltype(pictureProfileHandle.has_value())) + + sizeof(decltype(pictureProfileHandle.getId())); +#else + 0; +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES } size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const { - return minFlattenedSize() + - fence->getFlattenedSize() + - surfaceDamage.getFlattenedSize() + + return minFlattenedSize() + fence->getFlattenedSize() + surfaceDamage.getFlattenedSize() + hdrMetadata.getFlattenedSize(); } @@ -57,6 +55,12 @@ status_t IGraphicBufferProducer::QueueBufferInput::flatten( FlattenableUtils::write(buffer, size, transform); FlattenableUtils::write(buffer, size, stickyTransform); FlattenableUtils::write(buffer, size, getFrameTimestamps); +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + FlattenableUtils::write(buffer, size, pictureProfileHandle.has_value()); + FlattenableUtils::write(buffer, size, + pictureProfileHandle.has_value() ? pictureProfileHandle->getId() + : PictureProfileHandle::NONE.getId()); +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES status_t result = fence->flatten(buffer, size, fds, count); if (result != NO_ERROR) { @@ -91,6 +95,15 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten( FlattenableUtils::read(buffer, size, transform); FlattenableUtils::read(buffer, size, stickyTransform); FlattenableUtils::read(buffer, size, getFrameTimestamps); +#if COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES + bool hasPictureProfileHandle; + FlattenableUtils::read(buffer, size, hasPictureProfileHandle); + PictureProfileId pictureProfileId; + FlattenableUtils::read(buffer, size, pictureProfileId); + pictureProfileHandle = hasPictureProfileHandle + ? std::optional(PictureProfileHandle(pictureProfileId)) + : std::nullopt; +#endif // COM_ANDROID_GRAPHICS_LIBUI_FLAGS_APPLY_PICTURE_PROFILES fence = new Fence(); status_t result = fence->unflatten(buffer, size, fds, count); diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 422c57bc87..c1a03fcfea 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -21,6 +21,7 @@ #include <android/gui/ISurfaceComposerClient.h> #include <android/native_window.h> #include <binder/Parcel.h> +#include <com_android_graphics_libgui_flags.h> #include <gui/FrameRateUtils.h> #include <gui/IGraphicBufferProducer.h> #include <gui/LayerState.h> @@ -91,7 +92,9 @@ layer_state_t::layer_state_t() trustedOverlay(gui::TrustedOverlay::UNSET), bufferCrop(Rect::INVALID_RECT), destinationFrame(Rect::INVALID_RECT), - dropInputMode(gui::DropInputMode::NONE) { + dropInputMode(gui::DropInputMode::NONE), + pictureProfileHandle(PictureProfileHandle::NONE), + appContentPriority(0) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; hdrMetadata.validTypes = 0; @@ -202,6 +205,16 @@ status_t layer_state_t::write(Parcel& output) const if (hasBufferReleaseChannel) { SAFE_PARCEL(output.writeParcelable, *bufferReleaseChannel); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES + SAFE_PARCEL(output.writeInt64, pictureProfileHandle.getId()); + SAFE_PARCEL(output.writeInt32, appContentPriority); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES + + const bool hasLuts = (luts != nullptr); + SAFE_PARCEL(output.writeBool, hasLuts); + if (hasLuts) { + SAFE_PARCEL(output.writeParcelable, *luts); + } return NO_ERROR; } @@ -357,6 +370,21 @@ status_t layer_state_t::read(const Parcel& input) bufferReleaseChannel = std::make_shared<gui::BufferReleaseChannel::ProducerEndpoint>(); SAFE_PARCEL(input.readParcelable, bufferReleaseChannel.get()); } +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES + int64_t pictureProfileId; + SAFE_PARCEL(input.readInt64, &pictureProfileId); + pictureProfileHandle = PictureProfileHandle(pictureProfileId); + SAFE_PARCEL(input.readInt32, &appContentPriority); +#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_APPLY_PICTURE_PROFILES + + bool hasLuts; + SAFE_PARCEL(input.readBool, &hasLuts); + if (hasLuts) { + luts = std::make_shared<gui::DisplayLuts>(); + SAFE_PARCEL(input.readParcelable, luts.get()); + } else { + luts = nullptr; + } return NO_ERROR; } @@ -664,6 +692,10 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eShadowRadiusChanged; shadowRadius = other.shadowRadius; } + if (other.what & eLutsChanged) { + what |= eLutsChanged; + luts = other.luts; + } if (other.what & eDefaultFrameRateCompatibilityChanged) { what |= eDefaultFrameRateCompatibilityChanged; defaultFrameRateCompatibility = other.defaultFrameRateCompatibility; @@ -741,6 +773,16 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eBufferReleaseChannelChanged; bufferReleaseChannel = other.bufferReleaseChannel; } + if (com_android_graphics_libgui_flags_apply_picture_profiles()) { + if (other.what & ePictureProfileHandleChanged) { + what |= ePictureProfileHandleChanged; + pictureProfileHandle = other.pictureProfileHandle; + } + if (other.what & eAppContentPriorityChanged) { + what |= eAppContentPriorityChanged; + appContentPriority = other.appContentPriority; + } + } if ((other.what & what) != other.what) { ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? " "other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64, @@ -821,6 +863,10 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eColorSpaceAgnosticChanged, other, colorSpaceAgnostic); CHECK_DIFF(diff, eDimmingEnabledChanged, other, dimmingEnabled); if (other.what & eBufferReleaseChannelChanged) diff |= eBufferReleaseChannelChanged; + if (other.what & eLutsChanged) diff |= eLutsChanged; + CHECK_DIFF(diff, ePictureProfileHandleChanged, other, pictureProfileHandle); + CHECK_DIFF(diff, eAppContentPriorityChanged, other, appContentPriority); + return diff; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 66e7ddd915..e41f9bbf43 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -2735,8 +2735,8 @@ status_t Surface::unlockAndPost() bool Surface::waitForNextFrame(uint64_t lastFrame, nsecs_t timeout) { Mutex::Autolock lock(mMutex); - if (mNextFrameNumber > lastFrame) { - return true; + if (mLastFrameNumber > lastFrame) { + return true; } return mQueueBufferCondition.waitRelative(mMutex, timeout) == OK; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index eeea80fa82..61aabaa7f6 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -20,8 +20,6 @@ #include <stdint.h> #include <sys/types.h> -#include <com_android_graphics_libgui_flags.h> - #include <android/gui/BnWindowInfosReportedListener.h> #include <android/gui/DisplayState.h> #include <android/gui/EdgeExtensionParameters.h> @@ -29,6 +27,8 @@ #include <android/gui/IWindowInfosListener.h> #include <android/gui/TrustedPresentationThresholds.h> #include <android/os/IInputConstants.h> +#include <com_android_graphics_libgui_flags.h> +#include <gui/DisplayLuts.h> #include <gui/FrameRateUtils.h> #include <gui/TraceUtils.h> #include <utils/Errors.h> @@ -56,6 +56,7 @@ #include <ui/DisplayMode.h> #include <ui/DisplayState.h> #include <ui/DynamicDisplayInfo.h> +#include <ui/FrameRateCategoryRate.h> #include <android-base/thread_annotations.h> #include <gui/LayerStatePermissions.h> @@ -90,6 +91,7 @@ int64_t generateId() { } constexpr int64_t INVALID_VSYNC = -1; +const constexpr char* LOG_SURFACE_CONTROL_REGISTRY = "SurfaceControlRegistry"; } // namespace @@ -871,6 +873,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel const bool earlyWakeupEnd = parcel->readBool(); const int64_t desiredPresentTime = parcel->readInt64(); const bool isAutoTimestamp = parcel->readBool(); + const bool logCallPoints = parcel->readBool(); FrameTimelineInfo frameTimelineInfo; frameTimelineInfo.readFromParcel(parcel); @@ -998,6 +1001,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const parcel->writeBool(mEarlyWakeupEnd); parcel->writeInt64(mDesiredPresentTime); parcel->writeBool(mIsAutoTimestamp); + parcel->writeBool(mLogCallPoints); mFrameTimelineInfo.writeToParcel(parcel); parcel->writeStrongBinder(mApplyToken); parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size())); @@ -1133,6 +1137,12 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mergeFrameTimelineInfo(mFrameTimelineInfo, other.mFrameTimelineInfo); + mLogCallPoints |= other.mLogCallPoints; + if (mLogCallPoints) { + ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, + "Transaction %" PRIu64 " merged with transaction %" PRIu64, other.getId(), mId); + } + other.clear(); return *this; } @@ -1152,6 +1162,7 @@ void SurfaceComposerClient::Transaction::clear() { mFrameTimelineInfo = {}; mApplyToken = nullptr; mMergedTransactionIds.clear(); + mLogCallPoints = false; } uint64_t SurfaceComposerClient::Transaction::getId() { @@ -1346,21 +1357,26 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay sp<IBinder> applyToken = mApplyToken ? mApplyToken : getDefaultApplyToken(); sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken, - mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp, - mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId, - mMergedTransactionIds); + status_t binderStatus = + sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, + applyToken, mInputWindowCommands, mDesiredPresentTime, + mIsAutoTimestamp, mUncacheBuffers, hasListenerCallbacks, + listenerCallbacks, mId, mMergedTransactionIds); mId = generateId(); // Clear the current states and flags clear(); - if (synchronous) { + if (synchronous && binderStatus == OK) { syncCallback->wait(); } + if (mLogCallPoints) { + ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", mId); + } + mStatus = NO_ERROR; - return NO_ERROR; + return binderStatus; } sp<IBinder> SurfaceComposerClient::Transaction::sApplyToken = new BBinder(); @@ -1374,7 +1390,7 @@ sp<IBinder> SurfaceComposerClient::Transaction::getDefaultApplyToken() { void SurfaceComposerClient::Transaction::setDefaultApplyToken(sp<IBinder> applyToken) { std::scoped_lock lock{sApplyTokenMutex}; - sApplyToken = applyToken; + sApplyToken = std::move(applyToken); } status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction( @@ -1389,6 +1405,11 @@ status_t SurfaceComposerClient::Transaction::sendSurfaceFlushJankDataTransaction t.registerSurfaceControlForCallback(sc); return t.apply(/*sync=*/false, /* oneWay=*/true); } + +void SurfaceComposerClient::Transaction::enableDebugLogCallPoints() { + mLogCallPoints = true; +} + // --------------------------------------------------------------------------- sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName, @@ -1940,15 +1961,23 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesir } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLuts( - const sp<SurfaceControl>& sc, const base::unique_fd& /*lutFd*/, - const std::vector<int32_t>& /*offsets*/, const std::vector<int32_t>& /*dimensions*/, - const std::vector<int32_t>& /*sizes*/, const std::vector<int32_t>& /*samplingKeys*/) { + const sp<SurfaceControl>& sc, const base::unique_fd& lutFd, + const std::vector<int32_t>& offsets, const std::vector<int32_t>& dimensions, + const std::vector<int32_t>& sizes, const std::vector<int32_t>& samplingKeys) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - // TODO (b/329472856): update layer_state_t for lut(s) + + s->what |= layer_state_t::eLutsChanged; + if (lutFd.ok()) { + s->luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(dup(lutFd.get())), offsets, + dimensions, sizes, samplingKeys); + } else { + s->luts = nullptr; + } + registerSurfaceControlForCallback(sc); return *this; } @@ -2103,13 +2132,13 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::notifyPr } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo( - const sp<SurfaceControl>& sc, const WindowInfo& info) { + const sp<SurfaceControl>& sc, sp<WindowInfoHandle> info) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->windowInfoHandle = new WindowInfoHandle(info); + s->windowInfoHandle = std::move(info); s->what |= layer_state_t::eInputInfoChanged; return *this; } @@ -2421,6 +2450,40 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPictureProfileHandle( + const sp<SurfaceControl>& sc, const PictureProfileHandle& pictureProfileHandle) { + if (com_android_graphics_libgui_flags_apply_picture_profiles()) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::ePictureProfileHandleChanged; + s->pictureProfileHandle = pictureProfileHandle; + + registerSurfaceControlForCallback(sc); + } + return *this; +} + +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setContentPriority( + const sp<SurfaceControl>& sc, int32_t priority) { + if (com_android_graphics_libgui_flags_apply_picture_profiles()) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + + s->what |= layer_state_t::eAppContentPriorityChanged; + s->appContentPriority = priority; + + registerSurfaceControlForCallback(sc); + } + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { @@ -2808,6 +2871,8 @@ void SurfaceComposerClient::getDynamicDisplayInfoInternal(gui::DynamicDisplayInf outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported; outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode; outInfo->hasArrSupport = ginfo.hasArrSupport; + outInfo->frameRateCategoryRate = ui::FrameRateCategoryRate(ginfo.frameRateCategoryRate.normal, + ginfo.frameRateCategoryRate.high); } status_t SurfaceComposerClient::getDynamicDisplayInfoFromId(int64_t displayId, diff --git a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl index 70873b001b..67cc273fce 100644 --- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl +++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl @@ -17,6 +17,7 @@ package android.gui; import android.gui.DisplayMode; +import android.gui.FrameRateCategoryRate; import android.gui.HdrCapabilities; // Information about a physical display which may change on hotplug reconnect. @@ -46,4 +47,7 @@ parcelable DynamicDisplayInfo { // Represents whether display supports ARR. boolean hasArrSupport; + + // Represents frame rate for FrameRateCategory Normal and High. + FrameRateCategoryRate frameRateCategoryRate; } diff --git a/libs/binder/trusty/ndk/include/android/llndk-versioning.h b/libs/gui/aidl/android/gui/FrameRateCategoryRate.aidl index e955a34bdf..f30280139f 100644 --- a/libs/binder/trusty/ndk/include/android/llndk-versioning.h +++ b/libs/gui/aidl/android/gui/FrameRateCategoryRate.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#pragma once -// TODO(b/349936395): set to true for Trusty -#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (false) +package android.gui; + +/** @hide */ +// Represents frame rate for FrameRateCategory Normal and High. +parcelable FrameRateCategoryRate { + float normal; + float high; +}
\ No newline at end of file diff --git a/libs/gui/aidl/android/gui/Lut.aidl b/libs/gui/aidl/android/gui/Lut.aidl deleted file mode 100644 index a06e521789..0000000000 --- a/libs/gui/aidl/android/gui/Lut.aidl +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.gui; - -import android.gui.LutProperties; -import android.os.ParcelFileDescriptor; - -/** - * This mirrors aidl::android::hardware::graphics::composer3::Lut definition - * @hide - */ -parcelable Lut { - @nullable ParcelFileDescriptor pfd; - - LutProperties lutProperties; -}
\ No newline at end of file diff --git a/libs/gui/aidl/android/gui/LutProperties.aidl b/libs/gui/aidl/android/gui/LutProperties.aidl index 561e0c069c..87b878c1ca 100644 --- a/libs/gui/aidl/android/gui/LutProperties.aidl +++ b/libs/gui/aidl/android/gui/LutProperties.aidl @@ -25,7 +25,7 @@ parcelable LutProperties { enum Dimension { ONE_D = 1, THREE_D = 3 } Dimension dimension; - long size; + int size; @Backing(type="int") enum SamplingKey { RGB, MAX_RGB } SamplingKey[] samplingKeys; diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 99c64daa15..07558aa49d 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -17,7 +17,9 @@ #ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H #define ANDROID_GUI_BLAST_BUFFER_QUEUE_H -#include <com_android_graphics_libgui_flags.h> +#include <optional> +#include <queue> + #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> #include <gui/IGraphicBufferConsumer.h> @@ -29,7 +31,6 @@ #include <utils/RefBase.h> #include <system/window.h> -#include <queue> #include <com_android_graphics_libgui_flags.h> @@ -222,6 +223,10 @@ private: ui::Size mRequestedSize GUARDED_BY(mMutex); int32_t mFormat GUARDED_BY(mMutex); + // Keep a copy of the current picture profile handle, so it can be moved to a new + // SurfaceControl when BBQ migrates via ::update. + std::optional<PictureProfileHandle> mPictureProfileHandle; + struct BufferInfo { bool hasBuffer = false; uint32_t width; @@ -325,6 +330,14 @@ private: std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer; std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer; + void updateBufferReleaseProducer() REQUIRES(mMutex); + void drainBufferReleaseConsumer(); + + // BufferReleaseReader is used to do blocking but interruptible reads from the buffer + // release channel. To implement this, BufferReleaseReader owns an epoll file descriptor that + // is configured to wake up when either the BufferReleaseReader::ConsumerEndpoint or an eventfd + // becomes readable. Interrupts are necessary because a free buffer may become available for + // reasons other than a buffer release from the producer. class BufferReleaseReader { public: explicit BufferReleaseReader(BLASTBufferQueue&); @@ -353,19 +366,6 @@ private: }; std::optional<BufferReleaseReader> mBufferReleaseReader; - - std::atomic<int> mThreadsBlockingOnDequeue = 0; - - class BufferReleaseThread { - public: - BufferReleaseThread(const sp<BLASTBufferQueue>&); - ~BufferReleaseThread(); - - private: - int mEventFd; - }; - - std::optional<BufferReleaseThread> mBufferReleaseThread; #endif }; diff --git a/libs/gui/include/gui/BufferItem.h b/libs/gui/include/gui/BufferItem.h index 218bb424fb..2f85c62a54 100644 --- a/libs/gui/include/gui/BufferItem.h +++ b/libs/gui/include/gui/BufferItem.h @@ -17,9 +17,12 @@ #ifndef ANDROID_GUI_BUFFERITEM_H #define ANDROID_GUI_BUFFERITEM_H +#include <optional> + #include <gui/HdrMetadata.h> #include <ui/FenceTime.h> +#include <ui/PictureProfileHandle.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -91,6 +94,10 @@ class BufferItem : public Flattenable<BufferItem> { // mHdrMetadata is the HDR metadata associated with this buffer slot. HdrMetadata mHdrMetadata; + // mPictureProfileHandle is a handle that points to a set of parameters that configure picture + // processing hardware to enhance the quality of buffer contents. + std::optional<PictureProfileHandle> mPictureProfileHandle; + // mFrameNumber is the number of the queued frame for this slot. uint64_t mFrameNumber; diff --git a/libs/gui/include/gui/DisplayLuts.h b/libs/gui/include/gui/DisplayLuts.h new file mode 100644 index 0000000000..ab86ac4af8 --- /dev/null +++ b/libs/gui/include/gui/DisplayLuts.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <android-base/unique_fd.h> +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <vector> + +namespace android::gui { + +struct DisplayLuts : public Parcelable { +public: + struct Entry : public Parcelable { + Entry() {}; + Entry(int32_t lutDimension, int32_t lutSize, int32_t lutSamplingKey) + : dimension(lutDimension), size(lutSize), samplingKey(lutSamplingKey) {} + int32_t dimension; + int32_t size; + int32_t samplingKey; + + status_t writeToParcel(android::Parcel* parcel) const override; + status_t readFromParcel(const android::Parcel* parcel) override; + }; + + DisplayLuts() {} + + DisplayLuts(base::unique_fd lutfd, std::vector<int32_t> lutoffsets, + std::vector<int32_t> lutdimensions, std::vector<int32_t> lutsizes, + std::vector<int32_t> lutsamplingKeys) { + fd = std::move(lutfd); + offsets = lutoffsets; + lutProperties.reserve(offsets.size()); + for (size_t i = 0; i < lutoffsets.size(); i++) { + Entry entry{lutdimensions[i], lutsizes[i], lutsamplingKeys[i]}; + lutProperties.emplace_back(entry); + } + } + + status_t writeToParcel(android::Parcel* parcel) const override; + status_t readFromParcel(const android::Parcel* parcel) override; + + const base::unique_fd& getLutFileDescriptor() const { return fd; } + + std::vector<Entry> lutProperties; + std::vector<int32_t> offsets; + +private: + base::unique_fd fd; +}; // struct DisplayLuts + +} // namespace android::gui
\ No newline at end of file diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h index 735375a1e7..34350d2c91 100644 --- a/libs/gui/include/gui/Flags.h +++ b/libs/gui/include/gui/Flags.h @@ -17,8 +17,20 @@ #pragma once #include <com_android_graphics_libgui_flags.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/Surface.h> #define WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES \ (COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CAMERA3_AND_PROCESSORS) && \ COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) && \ - COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS))
\ No newline at end of file + COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS)) + +#define WB_LIBCAMERASERVICE_WITH_DEPENDENCIES \ + (WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES && \ + COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_LIBCAMERASERVICE)) + +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES +typedef android::Surface SurfaceType; +#else +typedef android::IGraphicBufferProducer SurfaceType; +#endif
\ No newline at end of file diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 3aac457a09..001e570982 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -19,6 +19,7 @@ #include <stdint.h> #include <sys/types.h> +#include <optional> #include <utils/Errors.h> #include <utils/RefBase.h> @@ -28,6 +29,7 @@ #include <ui/BufferQueueDefs.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> +#include <ui/PictureProfileHandle.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -365,6 +367,14 @@ public: const HdrMetadata& getHdrMetadata() const { return hdrMetadata; } void setHdrMetadata(const HdrMetadata& metadata) { hdrMetadata = metadata; } + const std::optional<PictureProfileHandle>& getPictureProfileHandle() const { + return pictureProfileHandle; + } + void setPictureProfileHandle(const PictureProfileHandle& profile) { + pictureProfileHandle = profile; + } + void clearPictureProfileHandle() { pictureProfileHandle = std::nullopt; } + int64_t timestamp{0}; int isAutoTimestamp{0}; android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN}; @@ -377,6 +387,7 @@ public: bool getFrameTimestamps{false}; int slot{-1}; HdrMetadata hdrMetadata; + std::optional<PictureProfileHandle> pictureProfileHandle; }; struct QueueBufferOutput : public Flattenable<QueueBufferOutput> { diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 00065c81d8..9098dffa8e 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -26,6 +26,7 @@ #include <android/gui/LayerCaptureArgs.h> #include <android/gui/TrustedPresentationThresholds.h> #include <android/native_window.h> +#include <gui/DisplayLuts.h> #include <gui/IGraphicBufferProducer.h> #include <gui/ITransactionCompletedListener.h> #include <math/mat4.h> @@ -46,6 +47,7 @@ #include <ui/BlurRegion.h> #include <ui/GraphicTypes.h> #include <ui/LayerStack.h> +#include <ui/PictureProfileHandle.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/Rotation.h> @@ -184,6 +186,7 @@ struct layer_state_t { eCachingHintChanged = 0x00000200, eDimmingEnabledChanged = 0x00000400, eShadowRadiusChanged = 0x00000800, + eLutsChanged = 0x00001000, eBufferCropChanged = 0x00002000, eRelativeLayerChanged = 0x00004000, eReparent = 0x00008000, @@ -222,6 +225,8 @@ struct layer_state_t { eExtendedRangeBrightnessChanged = 0x10000'00000000, eEdgeExtensionChanged = 0x20000'00000000, eBufferReleaseChannelChanged = 0x40000'00000000, + ePictureProfileHandleChanged = 0x80000'00000000, + eAppContentPriorityChanged = 0x100000'00000000, }; layer_state_t(); @@ -255,7 +260,7 @@ struct layer_state_t { layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eTransparentRegionChanged | layer_state_t::eExtendedRangeBrightnessChanged | - layer_state_t::eDesiredHdrHeadroomChanged; + layer_state_t::eDesiredHdrHeadroomChanged | layer_state_t::eLutsChanged; // Content updates. static constexpr uint64_t CONTENT_CHANGES = layer_state_t::BUFFER_CHANGES | @@ -265,7 +270,8 @@ struct layer_state_t { layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged | layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged | - layer_state_t::eStretchChanged; + layer_state_t::eStretchChanged | layer_state_t::ePictureProfileHandleChanged | + layer_state_t::eAppContentPriorityChanged; // Changes which invalidates the layer's visible region in CE. static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES | @@ -410,12 +416,23 @@ struct layer_state_t { float currentHdrSdrRatio = 1.f; float desiredHdrSdrRatio = 1.f; + // Enhance the quality of the buffer contents by configurating a picture processing pipeline + // with values as specified by this picture profile. + PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE}; + + // A value indicating the significance of the layer's content to the app's desired user + // experience. A lower priority will result in more likelihood of getting access to limited + // resources, such as picture processing hardware. + int32_t appContentPriority = 0; + gui::CachingHint cachingHint = gui::CachingHint::Enabled; TrustedPresentationThresholds trustedPresentationThresholds; TrustedPresentationListener trustedPresentationListener; std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> bufferReleaseChannel; + + std::shared_ptr<gui::DisplayLuts> luts; }; class ComposerState { diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 5ea0c1619b..0d7f8c2824 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -38,6 +38,7 @@ #include <ui/EdgeExtensionEffect.h> #include <ui/FrameStats.h> #include <ui/GraphicTypes.h> +#include <ui/PictureProfileHandle.h> #include <ui/PixelFormat.h> #include <ui/Rotation.h> #include <ui/StaticDisplayInfo.h> @@ -437,6 +438,8 @@ public: static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other); // Tracks registered callbacks sp<TransactionCompletedListener> mTransactionCompletedListener = nullptr; + // Prints debug logs when enabled. + bool mLogCallPoints = false; protected: std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates; @@ -685,7 +688,8 @@ public: // ONLY FOR BLAST ADAPTER Transaction& notifyProducerDisconnect(const sp<SurfaceControl>& sc); - Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const gui::WindowInfo& info); + Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, + sp<gui::WindowInfoHandle> info); Transaction& setFocusedWindow(const gui::FocusRequest& request); Transaction& addWindowInfosReportedListener( @@ -773,6 +777,20 @@ public: const sp<SurfaceControl>& sc, const std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint>& channel); + /** + * Configures a surface control to use picture processing hardware, configured as specified + * by the picture profile, to enhance the quality of all subsequent buffer contents. + */ + Transaction& setPictureProfileHandle(const sp<SurfaceControl>& sc, + const PictureProfileHandle& pictureProfileHandle); + + /** + * Configures the relative importance of the contents of the layer with respect to the app's + * user experience. A lower priority value will give the layer preferred access to limited + * resources, such as picture processing, over a layer with a higher priority value. + */ + Transaction& setContentPriority(const sp<SurfaceControl>& sc, int32_t contentPriority); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); @@ -809,6 +827,7 @@ public: static void setDefaultApplyToken(sp<IBinder> applyToken); static status_t sendSurfaceFlushJankDataTransaction(const sp<SurfaceControl>& sc); + void enableDebugLogCallPoints(); }; status_t clearLayerFrameStats(const sp<IBinder>& token) const; diff --git a/libs/gui/include/gui/view/Surface.h b/libs/gui/include/gui/view/Surface.h index 7ddac8139a..7c762d3869 100644 --- a/libs/gui/include/gui/view/Surface.h +++ b/libs/gui/include/gui/view/Surface.h @@ -24,7 +24,9 @@ #include <binder/IBinder.h> #include <binder/Parcelable.h> +#include <gui/Flags.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/Surface.h> namespace android { @@ -46,6 +48,14 @@ class Surface : public Parcelable { sp<IGraphicBufferProducer> graphicBufferProducer; sp<IBinder> surfaceControlHandle; +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES + // functions used to convert to a parcelable Surface so it can be passed over binder. + static Surface fromSurface(const sp<android::Surface>& surface); + sp<android::Surface> toSurface() const; + + status_t getUniqueId(/* out */ uint64_t* id) const; +#endif + virtual status_t writeToParcel(Parcel* parcel) const override; virtual status_t readFromParcel(const Parcel* parcel) override; diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 1c7e0e439c..22d32e9769 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -2,6 +2,14 @@ package: "com.android.graphics.libgui.flags" container: "system" flag { + name: "apply_picture_profiles" + namespace: "tv_os_media" + description: "This flag controls sending picture profiles from BBQ to Composer HAL" + bug: "337330263" + is_fixed_read_only: true +} # apply_picture_profiles + +flag { name: "bq_setframerate" namespace: "core_graphics" description: "This flag controls plumbing setFrameRate thru BufferQueue" diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 2e6ffcb57f..b026e640aa 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -27,6 +27,7 @@ #include <gui/Surface.h> #include <ui/GraphicBuffer.h> +#include <ui/PictureProfileHandle.h> #include <android-base/properties.h> @@ -1569,4 +1570,61 @@ TEST_F(BufferQueueTest, TestAdditionalOptions) { EXPECT_EQ(ADATASPACE_UNKNOWN, dataSpace); } +TEST_F(BufferQueueTest, PassesThroughPictureProfileHandle) { + createBufferQueue(); + sp<MockConsumer> mc(new MockConsumer); + mConsumer->consumerConnect(mc, false); + + IGraphicBufferProducer::QueueBufferOutput qbo; + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo); + mProducer->setMaxDequeuedBufferCount(2); + mConsumer->setMaxAcquiredBufferCount(2); + + // First try to pass a valid picture profile handle + { + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buf; + IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN, + Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, + Fence::NO_FENCE); + qbi.setPictureProfileHandle(PictureProfileHandle(1)); + + EXPECT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, + nullptr, nullptr)); + EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buf)); + EXPECT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); + + BufferItem item; + EXPECT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + + ASSERT_TRUE(item.mPictureProfileHandle.has_value()); + ASSERT_EQ(item.mPictureProfileHandle, PictureProfileHandle(1)); + } + + // Then validate that the picture profile handle isn't sticky and is reset for the next buffer + { + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buf; + IGraphicBufferProducer::QueueBufferInput qbi(0, false, HAL_DATASPACE_UNKNOWN, + Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, + Fence::NO_FENCE); + + EXPECT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&slot, &fence, 1, 1, 0, GRALLOC_USAGE_SW_READ_OFTEN, + nullptr, nullptr)); + EXPECT_EQ(OK, mProducer->requestBuffer(slot, &buf)); + EXPECT_EQ(OK, mProducer->queueBuffer(slot, qbi, &qbo)); + + BufferItem item; + EXPECT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + + ASSERT_FALSE(item.mPictureProfileHandle.has_value()); + } +} + } // namespace android diff --git a/libs/gui/tests/BufferReleaseChannel_test.cpp b/libs/gui/tests/BufferReleaseChannel_test.cpp index 11d122b525..74f69e1ff0 100644 --- a/libs/gui/tests/BufferReleaseChannel_test.cpp +++ b/libs/gui/tests/BufferReleaseChannel_test.cpp @@ -29,11 +29,11 @@ namespace { // Helper function to check if two file descriptors point to the same file. bool is_same_file(int fd1, int fd2) { - struct stat stat1; + struct stat stat1 {}; if (fstat(fd1, &stat1) != 0) { return false; } - struct stat stat2; + struct stat stat2 {}; if (fstat(fd2, &stat2) != 0) { return false; } @@ -42,7 +42,18 @@ bool is_same_file(int fd1, int fd2) { } // namespace -TEST(BufferReleaseChannelTest, MessageFlattenable) { +class BufferReleaseChannelTest : public testing::Test { +protected: + std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> mConsumer; + std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> mProducer; + + void SetUp() override { + ASSERT_EQ(OK, + BufferReleaseChannel::open("BufferReleaseChannelTest"s, mConsumer, mProducer)); + } +}; + +TEST_F(BufferReleaseChannelTest, MessageFlattenable) { ReleaseCallbackId releaseCallbackId{1, 2}; sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); uint32_t maxAcquiredBufferCount = 5; @@ -92,31 +103,23 @@ TEST(BufferReleaseChannelTest, MessageFlattenable) { // Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message // available. -TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { - std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; - std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; - ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); - +TEST_F(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { ReleaseCallbackId releaseCallbackId; sp<Fence> releaseFence; uint32_t maxAcquiredBufferCount; ASSERT_EQ(WOULD_BLOCK, - consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount)); + mConsumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount)); } // Verify that we can write a message to the BufferReleaseChannel producer and read that message // using the BufferReleaseChannel consumer. -TEST(BufferReleaseChannelTest, ProduceAndConsume) { - std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; - std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; - ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); - +TEST_F(BufferReleaseChannelTest, ProduceAndConsume) { sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); for (uint64_t i = 0; i < 64; i++) { ReleaseCallbackId producerId{i, i + 1}; uint32_t maxAcquiredBufferCount = i + 2; - ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount)); + ASSERT_EQ(OK, mProducer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount)); } for (uint64_t i = 0; i < 64; i++) { @@ -127,7 +130,7 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) { sp<Fence> consumerFence; uint32_t maxAcquiredBufferCount; ASSERT_EQ(OK, - consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); + mConsumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); ASSERT_EQ(expectedId, consumerId); ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get())); @@ -135,4 +138,16 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) { } } +// Verify that BufferReleaseChannel::ConsumerEndpoint's socket can't be written to. +TEST_F(BufferReleaseChannelTest, ConsumerSocketReadOnly) { + uint64_t data = 0; + ASSERT_EQ(-1, write(mConsumer->getFd().get(), &data, sizeof(uint64_t))); + ASSERT_EQ(errno, EPIPE); +} + +// Verify that BufferReleaseChannel::ProducerEndpoint's socket can't be read from. +TEST_F(BufferReleaseChannelTest, ProducerSocketWriteOnly) { + ASSERT_EQ(0, read(mProducer->getFd().get(), nullptr, sizeof(uint64_t))); +} + } // namespace android
\ No newline at end of file diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index 7d0b512cb4..0e84d68eec 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -112,7 +112,7 @@ public: mInputFlinger = getInputFlinger(); if (noInputChannel) { - mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true); + mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true); } else { android::os::InputChannelCore tempChannel; android::binder::Status result = @@ -121,21 +121,21 @@ public: ADD_FAILURE() << "binder call to createInputChannel failed"; } mClientChannel = InputChannel::create(std::move(tempChannel)); - mInputInfo.token = mClientChannel->getConnectionToken(); + mInputInfo->editInfo()->token = mClientChannel->getConnectionToken(); mInputConsumer = new InputConsumer(mClientChannel); } - mInputInfo.name = "Test info"; - mInputInfo.dispatchingTimeout = 5s; - mInputInfo.globalScaleFactor = 1.0; - mInputInfo.touchableRegion.orSelf(Rect(0, 0, width, height)); + mInputInfo->editInfo()->name = "Test info"; + mInputInfo->editInfo()->dispatchingTimeout = 5s; + mInputInfo->editInfo()->globalScaleFactor = 1.0; + mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, width, height)); InputApplicationInfo aInfo; aInfo.token = new BBinder(); aInfo.name = "Test app info"; aInfo.dispatchingTimeoutMillis = std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count(); - mInputInfo.applicationInfo = aInfo; + mInputInfo->editInfo()->applicationInfo = aInfo; } static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient>& scc, @@ -183,20 +183,6 @@ public: return std::make_unique<InputSurface>(surfaceControl, width, height); } - InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) { - mClientChannel->waitForMessage(timeout); - - InputEvent* ev; - uint32_t seqId; - status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev); - if (consumed != OK) { - return nullptr; - } - status_t status = mInputConsumer->sendFinishedSignal(seqId, true); - EXPECT_EQ(OK, status) << "Could not send finished signal"; - return ev; - } - void assertFocusChange(bool hasFocus) { InputEvent* ev = consumeEvent(); ASSERT_NE(ev, nullptr); @@ -294,7 +280,7 @@ public: transactionBody) { SurfaceComposerClient::Transaction t; transactionBody(t, mSurfaceControl); - t.apply(true); + t.apply(/*synchronously=*/true); } virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) { @@ -307,30 +293,46 @@ public: t.setAlpha(mSurfaceControl, 1); auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make(); t.addWindowInfosReportedListener(reportedListener); - t.apply(); + t.apply(/*synchronously=*/true); reportedListener->wait(); } void requestFocus(ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) { SurfaceComposerClient::Transaction t; FocusRequest request; - request.token = mInputInfo.token; - request.windowName = mInputInfo.name; + request.token = mInputInfo->getInfo()->token; + request.windowName = mInputInfo->getInfo()->name; request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); request.displayId = displayId.val(); t.setFocusedWindow(request); - t.apply(true); + t.apply(/*synchronously=*/true); } public: + // But should be private + sp<gui::WindowInfoHandle> mInputInfo = sp<gui::WindowInfoHandle>::make(); sp<SurfaceControl> mSurfaceControl; + +private: std::shared_ptr<InputChannel> mClientChannel; sp<IInputFlinger> mInputFlinger; - WindowInfo mInputInfo; - PreallocatedInputEventFactory mInputEventFactory; InputConsumer* mInputConsumer; + + InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) { + mClientChannel->waitForMessage(timeout); + + InputEvent* ev; + uint32_t seqId; + status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev); + if (consumed != OK) { + return nullptr; + } + status_t status = mInputConsumer->sendFinishedSignal(seqId, true); + EXPECT_EQ(OK, status) << "Could not send finished signal"; + return ev; + } }; class BlastInputSurface : public InputSurface { @@ -363,7 +365,7 @@ public: transactionBody) override { SurfaceComposerClient::Transaction t; transactionBody(t, mParentSurfaceControl); - t.apply(true); + t.apply(/*synchronously=*/true); } void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override { @@ -377,7 +379,7 @@ public: t.setInputWindowInfo(mSurfaceControl, mInputInfo); t.setCrop(mSurfaceControl, crop); t.setAlpha(mSurfaceControl, 1); - t.apply(true); + t.apply(/*synchronously=*/true); } private: @@ -417,7 +419,7 @@ public: BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test"); - Transaction().setBuffer(layer, buffer).apply(true); + Transaction().setBuffer(layer, buffer).apply(/*synchronously=*/true); usleep(mBufferPostDelay); } @@ -458,7 +460,7 @@ TEST_F(InputSurfacesTest, can_receive_input) { injectTap(101, 101); - EXPECT_NE(surface->consumeEvent(), nullptr); + surface->expectTap(1, 1); } /** @@ -521,7 +523,7 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets) { std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - fgSurface->mInputInfo.surfaceInset = 5; + fgSurface->mInputInfo->editInfo()->surfaceInset = 5; fgSurface->showAt(100, 100); injectTap(106, 106); @@ -536,8 +538,8 @@ TEST_F(InputSurfacesTest, input_respects_surface_insets_with_replaceTouchableReg std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - fgSurface->mInputInfo.surfaceInset = 5; - fgSurface->mInputInfo.replaceTouchableRegionWithCrop = true; + fgSurface->mInputInfo->editInfo()->surfaceInset = 5; + fgSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; fgSurface->showAt(100, 100); injectTap(106, 106); @@ -553,7 +555,7 @@ TEST_F(InputSurfacesTest, input_respects_cropped_surface_insets) { std::unique_ptr<InputSurface> childSurface = makeSurface(100, 100); parentSurface->showAt(100, 100); - childSurface->mInputInfo.surfaceInset = 10; + childSurface->mInputInfo->editInfo()->surfaceInset = 10; childSurface->showAt(100, 100); childSurface->doTransaction([&](auto& t, auto& sc) { @@ -574,7 +576,7 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(100, 100); - fgSurface->mInputInfo.surfaceInset = 5; + fgSurface->mInputInfo->editInfo()->surfaceInset = 5; fgSurface->showAt(100, 100); fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); }); @@ -593,7 +595,7 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { bgSurface->showAt(100, 100); // In case we pass the very big inset without any checking. - fgSurface->mInputInfo.surfaceInset = INT32_MAX; + fgSurface->mInputInfo->editInfo()->surfaceInset = INT32_MAX; fgSurface->showAt(100, 100); fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); @@ -606,13 +608,13 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { TEST_F(InputSurfacesTest, touchable_region) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); - surface->mInputInfo.touchableRegion.set(Rect{19, 29, 21, 31}); + surface->mInputInfo->editInfo()->touchableRegion.set(Rect{19, 29, 21, 31}); surface->showAt(11, 22); // A tap within the surface but outside the touchable region should not be sent to the surface. injectTap(20, 30); - EXPECT_EQ(surface->consumeEvent(/*timeout=*/200ms), nullptr); + surface->assertNoEvent(); injectTap(31, 52); surface->expectTap(20, 30); @@ -627,7 +629,8 @@ TEST_F(InputSurfacesTest, input_respects_touchable_region_offset_overflow) { // Since the surface is offset from the origin, the touchable region will be transformed into // display space, which would trigger an overflow or an underflow. Ensure that we are protected // against such a situation. - fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); + fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf( + Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); fgSurface->showAt(100, 100); @@ -642,7 +645,8 @@ TEST_F(InputSurfacesTest, input_respects_scaled_touchable_region_overflow) { std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); bgSurface->showAt(0, 0); - fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); + fgSurface->mInputInfo->editInfo()->touchableRegion.orSelf( + Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX}); fgSurface->showAt(0, 0); fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); @@ -812,7 +816,7 @@ TEST_F(InputSurfacesTest, rotate_surface_with_scale) { TEST_F(InputSurfacesTest, rotate_surface_with_scale_and_insets) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); - surface->mInputInfo.surfaceInset = 5; + surface->mInputInfo->editInfo()->surfaceInset = 5; surface->showAt(100, 100); surface->doTransaction([](auto& t, auto& sc) { @@ -841,11 +845,12 @@ TEST_F(InputSurfacesTest, touch_flag_obscured) { // Add non touchable window to fully cover touchable window. Window behind gets touch, but // with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222}; + nonTouchableSurface->mInputInfo->editInfo() + ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); + nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; // Overriding occlusion mode otherwise the touch would be discarded at InputDispatcher by // the default obscured/untrusted touch filter introduced in S. - nonTouchableSurface->mInputInfo.touchOcclusionMode = TouchOcclusionMode::ALLOW; + nonTouchableSurface->mInputInfo->editInfo()->touchOcclusionMode = TouchOcclusionMode::ALLOW; nonTouchableSurface->showAt(100, 100); injectTap(190, 199); @@ -861,10 +866,12 @@ TEST_F(InputSurfacesTest, touch_flag_partially_obscured_with_crop) { // AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222}; - parentSurface->mInputInfo.ownerUid = gui::Uid{22222}; + nonTouchableSurface->mInputInfo->editInfo() + ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); + parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; + parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; nonTouchableSurface->showAt(0, 0); parentSurface->showAt(100, 100); @@ -885,10 +892,12 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_crop) { // the touchable window. Window behind gets touch with no obscured flags. std::unique_ptr<InputSurface> parentSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> nonTouchableSurface = makeSurface(100, 100); - nonTouchableSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - parentSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - nonTouchableSurface->mInputInfo.ownerUid = gui::Uid{22222}; - parentSurface->mInputInfo.ownerUid = gui::Uid{22222}; + nonTouchableSurface->mInputInfo->editInfo() + ->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); + parentSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + nonTouchableSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; + parentSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; nonTouchableSurface->showAt(0, 0); parentSurface->showAt(50, 50); @@ -906,8 +915,9 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_bql) { std::unique_ptr<InputSurface> bufferSurface = InputSurface::makeBufferInputSurface(mComposerClient, 0, 0); - bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - bufferSurface->mInputInfo.ownerUid = gui::Uid{22222}; + bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; surface->showAt(10, 10); bufferSurface->showAt(50, 50, Rect::EMPTY_RECT); @@ -921,8 +931,9 @@ TEST_F(InputSurfacesTest, touch_not_obscured_with_zero_sized_blast) { std::unique_ptr<BlastInputSurface> bufferSurface = BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0); - bufferSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - bufferSurface->mInputInfo.ownerUid = gui::Uid{22222}; + bufferSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + bufferSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; surface->showAt(10, 10); bufferSurface->showAt(50, 50, Rect::EMPTY_RECT); @@ -965,13 +976,14 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_scaled_without_crop_window) { TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); - surface->mInputInfo.ownerUid = gui::Uid{11111}; + surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111}; surface->doTransaction( [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); }); surface->showAt(100, 100); std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100); - obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222}; + obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; obscuringSurface->showAt(100, 100); injectTap(101, 101); surface->assertNoEvent(); @@ -984,13 +996,14 @@ TEST_F(InputSurfacesTest, strict_unobscured_input_obscured_window) { TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); - surface->mInputInfo.ownerUid = gui::Uid{11111}; + surface->mInputInfo->editInfo()->ownerUid = gui::Uid{11111}; surface->doTransaction( [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); }); surface->showAt(100, 100); std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100); - obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true); - obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222}; + obscuringSurface->mInputInfo->editInfo()->setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, + true); + obscuringSurface->mInputInfo->editInfo()->ownerUid = gui::Uid{22222}; obscuringSurface->showAt(190, 190); injectTap(101, 101); @@ -1054,7 +1067,7 @@ TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) { BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0); surface->showAt(100, 100); - bufferSurface->mInputInfo.touchableRegion.orSelf(Rect(0, 0, 200, 200)); + bufferSurface->mInputInfo->editInfo()->touchableRegion.orSelf(Rect(0, 0, 200, 200)); bufferSurface->showAt(100, 100, Rect::EMPTY_RECT); injectTap(101, 101); @@ -1097,8 +1110,8 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_ InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->doTransaction( [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); - containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; - containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; + containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr; parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); containerSurface->showAt(10, 10, Rect(0, 0, 5, 5)); @@ -1116,14 +1129,19 @@ TEST_F(InputSurfacesTest, cropped_container_replaces_touchable_region_with_null_ * in its parent's touchable region. The input events should be in the layer's coordinate space. */ TEST_F(InputSurfacesTest, uncropped_container_replaces_touchable_region_with_null_crop) { + std::unique_ptr<InputSurface> bgContainer = + InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); std::unique_ptr<InputSurface> parentContainer = InputSurface::makeContainerInputSurface(mComposerClient, 0, 0); std::unique_ptr<InputSurface> containerSurface = InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); containerSurface->doTransaction( [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); }); - containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; - containerSurface->mInputInfo.touchableRegionCropHandle = nullptr; + containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = nullptr; + parentContainer->doTransaction( + [&](auto& t, auto& sc) { t.reparent(sc, bgContainer->mSurfaceControl); }); + bgContainer->showAt(0, 0, Rect(0, 0, 100, 100)); parentContainer->showAt(10, 10, Rect(0, 0, 20, 20)); containerSurface->showAt(10, 10, Rect::INVALID_RECT); @@ -1147,8 +1165,8 @@ TEST_F(InputSurfacesTest, replace_touchable_region_with_crop) { std::unique_ptr<InputSurface> containerSurface = InputSurface::makeContainerInputSurface(mComposerClient, 100, 100); - containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true; - containerSurface->mInputInfo.touchableRegionCropHandle = + containerSurface->mInputInfo->editInfo()->replaceTouchableRegionWithCrop = true; + containerSurface->mInputInfo->editInfo()->touchableRegionCropHandle = cropLayer->mSurfaceControl->getHandle(); containerSurface->showAt(10, 10, Rect::INVALID_RECT); @@ -1207,7 +1225,7 @@ public: t.setDisplayLayerStack(token, layerStack); t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height}, {offsetX, offsetY, offsetX + width, offsetY + height}); - t.apply(true); + t.apply(/*synchronously=*/true); mVirtualDisplays.push_back(token); } diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp index 84c2a6ac71..9f57923886 100644 --- a/libs/gui/view/Surface.cpp +++ b/libs/gui/view/Surface.cpp @@ -121,6 +121,38 @@ String16 Surface::readMaybeEmptyString16(const Parcel* parcel) { return str.value_or(String16()); } +#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES +Surface Surface::fromSurface(const sp<android::Surface>& surface) { + if (surface == nullptr) { + ALOGE("%s: Error: view::Surface::fromSurface failed due to null surface.", __FUNCTION__); + return Surface(); + } + Surface s; + s.name = String16(surface->getConsumerName()); + s.graphicBufferProducer = surface->getIGraphicBufferProducer(); + s.surfaceControlHandle = surface->getSurfaceControlHandle(); + return s; +} + +sp<android::Surface> Surface::toSurface() const { + if (graphicBufferProducer == nullptr) return nullptr; + return new android::Surface(graphicBufferProducer, false, surfaceControlHandle); +} + +status_t Surface::getUniqueId(uint64_t* out_id) const { + if (graphicBufferProducer == nullptr) { + ALOGE("android::viewSurface::getUniqueId() failed because it's not initialized."); + return UNEXPECTED_NULL; + } + status_t status = graphicBufferProducer->getUniqueId(out_id); + if (status != OK) { + ALOGE("android::viewSurface::getUniqueId() failed."); + return status; + } + return OK; +} +#endif + std::string Surface::toString() const { std::stringstream out; out << name; diff --git a/libs/input/Android.bp b/libs/input/Android.bp index e4e81adf58..a4ae54b351 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -217,6 +217,7 @@ cc_library { ], srcs: [ "AccelerationCurve.cpp", + "CoordinateFilter.cpp", "Input.cpp", "InputConsumer.cpp", "InputConsumerNoResampling.cpp", @@ -230,6 +231,7 @@ cc_library { "KeyLayoutMap.cpp", "MotionPredictor.cpp", "MotionPredictorMetricsManager.cpp", + "OneEuroFilter.cpp", "PrintTools.cpp", "PropertyMap.cpp", "Resampler.cpp", diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp b/libs/input/CoordinateFilter.cpp index 85b94031c3..d231474577 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp +++ b/libs/input/CoordinateFilter.cpp @@ -1,6 +1,6 @@ -/* - * Copyright 2019 The Android Open Source Project - +/** + * Copyright 2024 The Android Open Source Project + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -14,21 +14,18 @@ * limitations under the License. */ -#include "MockPowerAdvisor.h" +#define LOG_TAG "CoordinateFilter" -namespace android { -namespace Hwc2 { +#include <input/CoordinateFilter.h> -// This will go away once PowerAdvisor is moved into the "backend" library -PowerAdvisor::~PowerAdvisor() = default; +namespace android { -namespace mock { +CoordinateFilter::CoordinateFilter(float minCutoffFreq, float beta) + : mXFilter{minCutoffFreq, beta}, mYFilter{minCutoffFreq, beta} {} -// The Google Mock documentation recommends explicit non-header instantiations -// for better compile time performance. -PowerAdvisor::PowerAdvisor() = default; -PowerAdvisor::~PowerAdvisor() = default; +void CoordinateFilter::filter(std::chrono::duration<float> timestamp, PointerCoords& coords) { + coords.setAxisValue(AMOTION_EVENT_AXIS_X, mXFilter.filter(timestamp, coords.getX())); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, mYFilter.filter(timestamp, coords.getY())); +} -} // namespace mock -} // namespace Hwc2 } // namespace android diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp index 9665de799f..d3653cfc45 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -357,7 +357,8 @@ void InputConsumerNoResampling::handleMessages(std::vector<InputMessage>&& messa mBatches[deviceId].emplace(msg); } else { // consume all pending batches for this device immediately - consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt); + consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/ + std::numeric_limits<nsecs_t>::max()); if (canResample && (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL)) { LOG_IF(INFO, mResamplers.erase(deviceId) == 0) @@ -480,7 +481,7 @@ void InputConsumerNoResampling::handleMessage(const InputMessage& msg) const { } std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> -InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrameTime, +InputConsumerNoResampling::createBatchedMotionEvent(const std::optional<nsecs_t> requestedFrameTime, std::queue<InputMessage>& messages) { std::unique_ptr<MotionEvent> motionEvent; std::optional<uint32_t> firstSeqForBatch; @@ -491,7 +492,11 @@ InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrame const nanoseconds resampleLatency = (resampler != mResamplers.cend()) ? resampler->second->getResampleLatency() : nanoseconds{0}; - const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency; + // When batching is not enabled, we want to consume all events. That's equivalent to having an + // infinite requestedFrameTime. + const nanoseconds adjustedFrameTime = (requestedFrameTime.has_value()) + ? (nanoseconds{*requestedFrameTime} - resampleLatency) + : nanoseconds{std::numeric_limits<nsecs_t>::max()}; while (!messages.empty() && (messages.front().body.motion.eventTime <= adjustedFrameTime.count())) { @@ -513,8 +518,9 @@ InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrame if (!messages.empty()) { futureSample = &messages.front(); } - if ((motionEvent != nullptr) && (resampler != mResamplers.cend())) { - resampler->second->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent, + if ((motionEvent != nullptr) && (resampler != mResamplers.cend()) && + (requestedFrameTime.has_value())) { + resampler->second->resampleMotionEvent(nanoseconds{*requestedFrameTime}, *motionEvent, futureSample); } @@ -524,16 +530,13 @@ InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrame bool InputConsumerNoResampling::consumeBatchedInputEvents( std::optional<DeviceId> deviceId, std::optional<nsecs_t> requestedFrameTime) { ensureCalledOnLooperThread(__func__); - // When batching is not enabled, we want to consume all events. That's equivalent to having an - // infinite requestedFrameTime. - requestedFrameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max()); bool producedEvents = false; for (auto deviceIdIter = (deviceId.has_value()) ? (mBatches.find(*deviceId)) : (mBatches.begin()); deviceIdIter != mBatches.cend(); ++deviceIdIter) { std::queue<InputMessage>& messages = deviceIdIter->second; - auto [motion, firstSeqForBatch] = createBatchedMotionEvent(*requestedFrameTime, messages); + auto [motion, firstSeqForBatch] = createBatchedMotionEvent(requestedFrameTime, messages); if (motion != nullptr) { LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value()); mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch); diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp index 8db0ca588b..b537feb68f 100644 --- a/libs/input/InputEventLabels.cpp +++ b/libs/input/InputEventLabels.cpp @@ -350,7 +350,26 @@ namespace android { DEFINE_KEYCODE(MACRO_3), \ DEFINE_KEYCODE(MACRO_4), \ DEFINE_KEYCODE(EMOJI_PICKER), \ - DEFINE_KEYCODE(SCREENSHOT) + DEFINE_KEYCODE(SCREENSHOT), \ + DEFINE_KEYCODE(DICTATE), \ + DEFINE_KEYCODE(NEW), \ + DEFINE_KEYCODE(CLOSE), \ + DEFINE_KEYCODE(DO_NOT_DISTURB), \ + DEFINE_KEYCODE(PRINT), \ + DEFINE_KEYCODE(LOCK), \ + DEFINE_KEYCODE(FULLSCREEN), \ + DEFINE_KEYCODE(F13), \ + DEFINE_KEYCODE(F14), \ + DEFINE_KEYCODE(F15), \ + DEFINE_KEYCODE(F16), \ + DEFINE_KEYCODE(F17), \ + DEFINE_KEYCODE(F18), \ + DEFINE_KEYCODE(F19),\ + DEFINE_KEYCODE(F20), \ + DEFINE_KEYCODE(F21), \ + DEFINE_KEYCODE(F22), \ + DEFINE_KEYCODE(F23), \ + DEFINE_KEYCODE(F24) // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. diff --git a/libs/input/KeyboardClassifier.cpp b/libs/input/KeyboardClassifier.cpp index 0c2c7be582..2a83919283 100644 --- a/libs/input/KeyboardClassifier.cpp +++ b/libs/input/KeyboardClassifier.cpp @@ -57,14 +57,14 @@ void KeyboardClassifier::notifyKeyboardChanged(DeviceId deviceId, uint32_t deviceClasses) { if (mRustClassifier) { RustInputDeviceIdentifier rustIdentifier; - rustIdentifier.name = identifier.name; - rustIdentifier.location = identifier.location; - rustIdentifier.unique_id = identifier.uniqueId; + rustIdentifier.name = rust::String::lossy(identifier.name); + rustIdentifier.location = rust::String::lossy(identifier.location); + rustIdentifier.unique_id = rust::String::lossy(identifier.uniqueId); rustIdentifier.bus = identifier.bus; rustIdentifier.vendor = identifier.vendor; rustIdentifier.product = identifier.product; rustIdentifier.version = identifier.version; - rustIdentifier.descriptor = identifier.descriptor; + rustIdentifier.descriptor = rust::String::lossy(identifier.descriptor); android::input::keyboardClassifier::notifyKeyboardChanged(**mRustClassifier, deviceId, rustIdentifier, deviceClasses); } else { diff --git a/libs/input/OneEuroFilter.cpp b/libs/input/OneEuroFilter.cpp new file mode 100644 index 0000000000..400d7c9ab0 --- /dev/null +++ b/libs/input/OneEuroFilter.cpp @@ -0,0 +1,79 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "OneEuroFilter" + +#include <chrono> +#include <cmath> + +#include <android-base/logging.h> +#include <input/CoordinateFilter.h> + +namespace android { +namespace { + +inline float cutoffFreq(float minCutoffFreq, float beta, float filteredSpeed) { + return minCutoffFreq + beta * std::abs(filteredSpeed); +} + +inline float smoothingFactor(std::chrono::duration<float> samplingPeriod, float cutoffFreq) { + return samplingPeriod.count() / (samplingPeriod.count() + (1.0 / (2.0 * M_PI * cutoffFreq))); +} + +inline float lowPassFilter(float rawPosition, float prevFilteredPosition, float smoothingFactor) { + return smoothingFactor * rawPosition + (1 - smoothingFactor) * prevFilteredPosition; +} + +} // namespace + +OneEuroFilter::OneEuroFilter(float minCutoffFreq, float beta, float speedCutoffFreq) + : mMinCutoffFreq{minCutoffFreq}, mBeta{beta}, mSpeedCutoffFreq{speedCutoffFreq} {} + +float OneEuroFilter::filter(std::chrono::duration<float> timestamp, float rawPosition) { + LOG_IF(FATAL, mPrevFilteredPosition.has_value() && (timestamp <= *mPrevTimestamp)) + << "Timestamp must be greater than mPrevTimestamp"; + + const std::chrono::duration<float> samplingPeriod = (mPrevTimestamp.has_value()) + ? (timestamp - *mPrevTimestamp) + : std::chrono::duration<float>{1.0}; + + const float rawVelocity = (mPrevFilteredPosition.has_value()) + ? ((rawPosition - *mPrevFilteredPosition) / samplingPeriod.count()) + : 0.0; + + const float speedSmoothingFactor = smoothingFactor(samplingPeriod, mSpeedCutoffFreq); + + const float filteredVelocity = (mPrevFilteredVelocity.has_value()) + ? lowPassFilter(rawVelocity, *mPrevFilteredVelocity, speedSmoothingFactor) + : rawVelocity; + + const float positionCutoffFreq = cutoffFreq(mMinCutoffFreq, mBeta, filteredVelocity); + + const float positionSmoothingFactor = smoothingFactor(samplingPeriod, positionCutoffFreq); + + const float filteredPosition = (mPrevFilteredPosition.has_value()) + ? lowPassFilter(rawPosition, *mPrevFilteredPosition, positionSmoothingFactor) + : rawPosition; + + mPrevTimestamp = timestamp; + mPrevRawPosition = rawPosition; + mPrevFilteredVelocity = filteredVelocity; + mPrevFilteredPosition = filteredPosition; + + return filteredPosition; +} + +} // namespace android diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp index 056db093d1..3ab132d550 100644 --- a/libs/input/Resampler.cpp +++ b/libs/input/Resampler.cpp @@ -389,4 +389,34 @@ void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& mo mLastRealSample = *(mLatestSamples.end() - 1); } +// --- FilteredLegacyResampler --- + +FilteredLegacyResampler::FilteredLegacyResampler(float minCutoffFreq, float beta) + : mResampler{}, mMinCutoffFreq{minCutoffFreq}, mBeta{beta} {} + +void FilteredLegacyResampler::resampleMotionEvent(std::chrono::nanoseconds requestedFrameTime, + MotionEvent& motionEvent, + const InputMessage* futureSample) { + mResampler.resampleMotionEvent(requestedFrameTime, motionEvent, futureSample); + const size_t numSamples = motionEvent.getHistorySize() + 1; + for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) { + for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); + ++pointerIndex) { + const int32_t pointerId = motionEvent.getPointerProperties(pointerIndex)->id; + const nanoseconds eventTime = + nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}; + // Refer to the static function `setMotionEventPointerCoords` for a justification of + // casting away const. + PointerCoords& pointerCoords = const_cast<PointerCoords&>( + *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex))); + const auto& [iter, _] = mFilteredPointers.try_emplace(pointerId, mMinCutoffFreq, mBeta); + iter->second.filter(eventTime, pointerCoords); + } + } +} + +std::chrono::nanoseconds FilteredLegacyResampler::getResampleLatency() const { + return mResampler.getResampleLatency(); +} + } // namespace android diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index e93c04df6d..fd7704815f 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -94,13 +94,6 @@ flag { } flag { - name: "enable_new_mouse_pointer_ballistics" - namespace: "input" - description: "Change the acceleration curves for mouse pointer movements to match the touchpad ones" - bug: "315313622" -} - -flag { name: "rate_limit_user_activity_poke_in_dispatcher" namespace: "input" description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher." @@ -221,3 +214,20 @@ flag { description: "Set input device's power/wakeup sysfs node" bug: "372812925" } + +flag { + name: "enable_alphabetic_keyboard_wake" + namespace: "input" + description: "Enable wake from alphabetic keyboards." + bug: "352856881" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "connected_displays_cursor" + namespace: "lse_desktop_experience" + description: "Allow cursor to transition across multiple connected displays" + bug: "362719483" +} diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 661c9f739f..46e819061f 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -25,6 +25,7 @@ cc_test { "InputVerifier_test.cpp", "MotionPredictor_test.cpp", "MotionPredictorMetricsManager_test.cpp", + "OneEuroFilter_test.cpp", "Resampler_test.cpp", "RingBuffer_test.cpp", "TestInputChannel.cpp", diff --git a/libs/input/tests/InputConsumerResampling_test.cpp b/libs/input/tests/InputConsumerResampling_test.cpp index 883ca82fe0..97688a83ae 100644 --- a/libs/input/tests/InputConsumerResampling_test.cpp +++ b/libs/input/tests/InputConsumerResampling_test.cpp @@ -38,6 +38,8 @@ namespace { using std::chrono::nanoseconds; using namespace std::chrono_literals; +const std::chrono::milliseconds RESAMPLE_LATENCY{5}; + struct Pointer { int32_t id{0}; float x{0.0f}; @@ -440,7 +442,7 @@ TEST_F(InputConsumerResamplingTest, SampleTimeEqualsEventTime) { {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE})); invokeLooperCallback(); - mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + 5ms /*RESAMPLE_LATENCY*/}.count()); + mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + RESAMPLE_LATENCY}.count()); // MotionEvent should not resampled because the resample time falls exactly on the existing // event time. @@ -496,14 +498,15 @@ TEST_F(InputConsumerResamplingTest, ResampledValueIsUsedForIdenticalCoordinates) {40ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE})); invokeLooperCallback(); - mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + 5ms /*RESAMPLE_LATENCY*/}.count()); + mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + RESAMPLE_LATENCY}.count()); + // Original and resampled event should be both overwritten. assertReceivedMotionEvent( {InputEventEntry{40ms, {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}}, - AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten + AMOTION_EVENT_ACTION_MOVE}, InputEventEntry{45ms, {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}}, - AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten + AMOTION_EVENT_ACTION_MOVE}}); mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); @@ -552,13 +555,14 @@ TEST_F(InputConsumerResamplingTest, OldEventReceivedAfterResampleOccurs) { invokeLooperCallback(); mConsumer->consumeBatchedInputEvents(nanoseconds{50ms}.count()); + // Original and resampled event should be both overwritten. assertReceivedMotionEvent( {InputEventEntry{24ms, {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}}, - AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten + AMOTION_EVENT_ACTION_MOVE}, InputEventEntry{26ms, {Pointer{.id = 0, .x = 45.0f, .y = 30.0f, .isResampled = true}}, - AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten + AMOTION_EVENT_ACTION_MOVE}}); mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); @@ -566,4 +570,175 @@ TEST_F(InputConsumerResamplingTest, OldEventReceivedAfterResampleOccurs) { mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true); } +TEST_F(InputConsumerResamplingTest, DoNotResampleWhenFrameTimeIsNotAvailable) { + mClientTestChannel->enqueueMessage(nextPointerMessage( + {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN})); + + invokeLooperCallback(); + assertReceivedMotionEvent({InputEventEntry{0ms, + {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, + AMOTION_EVENT_ACTION_DOWN}}); + + mClientTestChannel->enqueueMessage(nextPointerMessage( + {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE})); + mClientTestChannel->enqueueMessage(nextPointerMessage( + {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(std::nullopt); + assertReceivedMotionEvent({InputEventEntry{10ms, + {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, + AMOTION_EVENT_ACTION_MOVE}, + InputEventEntry{20ms, + {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, + AMOTION_EVENT_ACTION_MOVE}}); + + mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true); + mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true); +} + +TEST_F(InputConsumerResamplingTest, TwoPointersAreResampledIndependently) { + // Full action for when a pointer with index=1 appears (some other pointer must already be + // present) + const int32_t actionPointer1Down = + AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + + // Full action for when a pointer with index=0 disappears (some other pointer must still remain) + const int32_t actionPointer0Up = + AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + + mClientTestChannel->enqueueMessage(nextPointerMessage( + {0ms, {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}}, AMOTION_EVENT_ACTION_DOWN})); + + mClientTestChannel->assertNoSentMessages(); + + invokeLooperCallback(); + assertReceivedMotionEvent({InputEventEntry{0ms, + {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}}, + AMOTION_EVENT_ACTION_DOWN}}); + + mClientTestChannel->enqueueMessage(nextPointerMessage( + {10ms, {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}}, AMOTION_EVENT_ACTION_MOVE})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(nanoseconds{10ms + RESAMPLE_LATENCY}.count()); + // Not resampled value because requestedFrameTime - RESAMPLE_LATENCY == eventTime + assertReceivedMotionEvent({InputEventEntry{10ms, + {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}}, + AMOTION_EVENT_ACTION_MOVE}}); + + // Second pointer id=1 appears + mClientTestChannel->enqueueMessage( + nextPointerMessage({15ms, + {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}, + Pointer{.id = 1, .x = 500.0f, .y = 500.0f}}, + actionPointer1Down})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + RESAMPLE_LATENCY}.count()); + // Not resampled value because requestedFrameTime - RESAMPLE_LATENCY == eventTime. + assertReceivedMotionEvent({InputEventEntry{15ms, + {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}, + Pointer{.id = 1, .x = 500.0f, .y = 500.0f}}, + actionPointer1Down}}); + + // Both pointers move + mClientTestChannel->enqueueMessage( + nextPointerMessage({30ms, + {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}, + Pointer{.id = 1, .x = 500.0f, .y = 500.0f}}, + AMOTION_EVENT_ACTION_MOVE})); + mClientTestChannel->enqueueMessage( + nextPointerMessage({40ms, + {Pointer{.id = 0, .x = 120.0f, .y = 120.0f}, + Pointer{.id = 1, .x = 600.0f, .y = 600.0f}}, + AMOTION_EVENT_ACTION_MOVE})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + RESAMPLE_LATENCY}.count()); + assertReceivedMotionEvent( + {InputEventEntry{30ms, + {Pointer{.id = 0, .x = 100.0f, .y = 100.0f}, + Pointer{.id = 1, .x = 500.0f, .y = 500.0f}}, + AMOTION_EVENT_ACTION_MOVE}, + InputEventEntry{40ms, + {Pointer{.id = 0, .x = 120.0f, .y = 120.0f}, + Pointer{.id = 1, .x = 600.0f, .y = 600.0f}}, + AMOTION_EVENT_ACTION_MOVE}, + InputEventEntry{45ms, + {Pointer{.id = 0, .x = 130.0f, .y = 130.0f, .isResampled = true}, + Pointer{.id = 1, .x = 650.0f, .y = 650.0f, .isResampled = true}}, + AMOTION_EVENT_ACTION_MOVE}}); + + // Both pointers move again + mClientTestChannel->enqueueMessage( + nextPointerMessage({60ms, + {Pointer{.id = 0, .x = 120.0f, .y = 120.0f}, + Pointer{.id = 1, .x = 600.0f, .y = 600.0f}}, + AMOTION_EVENT_ACTION_MOVE})); + mClientTestChannel->enqueueMessage( + nextPointerMessage({70ms, + {Pointer{.id = 0, .x = 130.0f, .y = 130.0f}, + Pointer{.id = 1, .x = 700.0f, .y = 700.0f}}, + AMOTION_EVENT_ACTION_MOVE})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(nanoseconds{75ms + RESAMPLE_LATENCY}.count()); + + /* + * The pointer id 0 at t = 60 should not be equal to 120 because the value was received twice, + * and resampled to 130. Therefore, if we reported 130, then we should continue to report it as + * such. Likewise, with pointer id 1. + */ + + // Not 120 because it matches a previous real event. + assertReceivedMotionEvent( + {InputEventEntry{60ms, + {Pointer{.id = 0, .x = 130.0f, .y = 130.0f, .isResampled = true}, + Pointer{.id = 1, .x = 650.0f, .y = 650.0f, .isResampled = true}}, + AMOTION_EVENT_ACTION_MOVE}, + InputEventEntry{70ms, + {Pointer{.id = 0, .x = 130.0f, .y = 130.0f}, + Pointer{.id = 1, .x = 700.0f, .y = 700.0f}}, + AMOTION_EVENT_ACTION_MOVE}, + InputEventEntry{75ms, + {Pointer{.id = 0, .x = 135.0f, .y = 135.0f, .isResampled = true}, + Pointer{.id = 1, .x = 750.0f, .y = 750.0f, .isResampled = true}}, + AMOTION_EVENT_ACTION_MOVE}}); + + // First pointer id=0 leaves the screen + mClientTestChannel->enqueueMessage( + nextPointerMessage({80ms, + {Pointer{.id = 0, .x = 120.0f, .y = 120.0f}, + Pointer{.id = 1, .x = 600.0f, .y = 600.0f}}, + actionPointer0Up})); + + invokeLooperCallback(); + // Not resampled event for ACTION_POINTER_UP + assertReceivedMotionEvent({InputEventEntry{80ms, + {Pointer{.id = 0, .x = 120.0f, .y = 120.0f}, + Pointer{.id = 1, .x = 600.0f, .y = 600.0f}}, + actionPointer0Up}}); + + // Remaining pointer id=1 is still present, but doesn't move + mClientTestChannel->enqueueMessage(nextPointerMessage( + {90ms, {Pointer{.id = 1, .x = 600.0f, .y = 600.0f}}, AMOTION_EVENT_ACTION_MOVE})); + + invokeLooperCallback(); + mConsumer->consumeBatchedInputEvents(nanoseconds{100ms}.count()); + + /* + * The latest event with ACTION_MOVE was at t = 70 with value = 700. Thus, the resampled value + * is 700 + ((95 - 70)/(90 - 70))*(600 - 700) = 575. + */ + assertReceivedMotionEvent( + {InputEventEntry{90ms, + {Pointer{.id = 1, .x = 600.0f, .y = 600.0f}}, + AMOTION_EVENT_ACTION_MOVE}, + InputEventEntry{95ms, + {Pointer{.id = 1, .x = 575.0f, .y = 575.0f, .isResampled = true}}, + AMOTION_EVENT_ACTION_MOVE}}); +} + } // namespace android diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp index 6a3bbe5b36..06e19bb644 100644 --- a/libs/input/tests/InputConsumer_test.cpp +++ b/libs/input/tests/InputConsumer_test.cpp @@ -194,7 +194,7 @@ TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) { std::unique_ptr<MotionEvent> moveMotionEvent = assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE)); ASSERT_NE(moveMotionEvent, nullptr); - EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL); + EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 2UL); mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true); mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true); @@ -443,4 +443,5 @@ TEST_F(InputConsumerTest, MultiDeviceResampling) { mClientTestChannel->assertFinishMessage(/*seq=*/8, /*handled=*/true); mClientTestChannel->assertFinishMessage(/*seq=*/10, /*handled=*/true); } + } // namespace android diff --git a/libs/input/tests/OneEuroFilter_test.cpp b/libs/input/tests/OneEuroFilter_test.cpp new file mode 100644 index 0000000000..270e789c84 --- /dev/null +++ b/libs/input/tests/OneEuroFilter_test.cpp @@ -0,0 +1,134 @@ +/** + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <input/OneEuroFilter.h> + +#include <algorithm> +#include <chrono> +#include <cmath> +#include <numeric> +#include <vector> + +#include <gtest/gtest.h> + +#include <input/Input.h> + +namespace android { +namespace { + +using namespace std::literals::chrono_literals; +using std::chrono::duration; + +struct Sample { + duration<double> timestamp{}; + double value{}; + + friend bool operator<(const Sample& lhs, const Sample& rhs) { return lhs.value < rhs.value; } +}; + +/** + * Generates a sinusoidal signal with the passed frequency and amplitude. + */ +std::vector<Sample> generateSinusoidalSignal(duration<double> signalDuration, + double samplingFrequency, double signalFrequency, + double amplitude) { + std::vector<Sample> signal; + const duration<double> samplingPeriod{1.0 / samplingFrequency}; + for (duration<double> timestamp{0.0}; timestamp < signalDuration; timestamp += samplingPeriod) { + signal.push_back( + Sample{timestamp, + amplitude * std::sin(2.0 * M_PI * signalFrequency * timestamp.count())}); + } + return signal; +} + +double meanAbsoluteError(const std::vector<Sample>& filteredSignal, + const std::vector<Sample>& signal) { + if (filteredSignal.size() != signal.size()) { + ADD_FAILURE() << "filteredSignal and signal do not have equal number of samples"; + return std::numeric_limits<double>::max(); + } + std::vector<double> absoluteError; + for (size_t sampleIndex = 0; sampleIndex < signal.size(); ++sampleIndex) { + absoluteError.push_back( + std::abs(filteredSignal[sampleIndex].value - signal[sampleIndex].value)); + } + if (absoluteError.empty()) { + ADD_FAILURE() << "Zero division. absoluteError is empty"; + return std::numeric_limits<double>::max(); + } + return std::accumulate(absoluteError.begin(), absoluteError.end(), 0.0) / absoluteError.size(); +} + +double maxAbsoluteAmplitude(const std::vector<Sample>& signal) { + if (signal.empty()) { + ADD_FAILURE() << "Max absolute value amplitude does not exist. Signal is empty"; + return std::numeric_limits<double>::max(); + } + std::vector<Sample> absoluteSignal; + for (const Sample& sample : signal) { + absoluteSignal.push_back(Sample{sample.timestamp, std::abs(sample.value)}); + } + return std::max_element(absoluteSignal.begin(), absoluteSignal.end())->value; +} + +} // namespace + +class OneEuroFilterTest : public ::testing::Test { +protected: + // The constructor's parameters are the ones that Chromium's using. The tuning was based on a 60 + // Hz sampling frequency. Refer to their one_euro_filter.h header for additional information + // about these parameters. + OneEuroFilterTest() : mFilter{/*minCutoffFreq=*/4.7, /*beta=*/0.01} {} + + std::vector<Sample> filterSignal(const std::vector<Sample>& signal) { + std::vector<Sample> filteredSignal; + for (const Sample& sample : signal) { + filteredSignal.push_back( + Sample{sample.timestamp, mFilter.filter(sample.timestamp, sample.value)}); + } + return filteredSignal; + } + + OneEuroFilter mFilter; +}; + +TEST_F(OneEuroFilterTest, PassLowFrequencySignal) { + const std::vector<Sample> signal = + generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/1, + /*amplitude=*/1); + + const std::vector<Sample> filteredSignal = filterSignal(signal); + + // The reason behind using the mean absolute error as a metric is that, ideally, a low frequency + // filtered signal is expected to be almost identical to the raw one. Therefore, the error + // between them should be minimal. The constant is heuristically chosen. + EXPECT_LT(meanAbsoluteError(filteredSignal, signal), 0.25); +} + +TEST_F(OneEuroFilterTest, RejectHighFrequencySignal) { + const std::vector<Sample> signal = + generateSinusoidalSignal(1s, /*samplingFrequency=*/60, /*signalFrequency=*/22.5, + /*amplitude=*/1); + + const std::vector<Sample> filteredSignal = filterSignal(signal); + + // The filtered signal should consist of values that are much closer to zero. The comparison + // constant is heuristically chosen. + EXPECT_LT(maxAbsoluteAmplitude(filteredSignal), 0.25); +} + +} // namespace android diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h index 3589de599f..290a97d736 100644 --- a/libs/input/tests/TestEventMatchers.h +++ b/libs/input/tests/TestEventMatchers.h @@ -75,12 +75,18 @@ public: using is_gtest_matcher = void; explicit WithMotionActionMatcher(int32_t action) : mAction(action) {} - bool MatchAndExplain(const MotionEvent& event, std::ostream*) const { - bool matches = mAction == event.getAction(); - if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) { - matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0; + bool MatchAndExplain(const MotionEvent& event, testing::MatchResultListener* listener) const { + if (mAction != event.getAction()) { + *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got " + << MotionEvent::actionToString(event.getAction()); + return false; + } + if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL && + (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) == 0) { + *listener << "event with CANCEL action is missing FLAG_CANCELED"; + return false; } - return matches; + return true; } void DescribeTo(std::ostream* os) const { diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp index c3ac0b7cfb..0c19ebe651 100644 --- a/libs/input/tests/TfLiteMotionPredictor_test.cpp +++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp @@ -89,23 +89,23 @@ TEST(TfLiteMotionPredictorTest, BuffersCopyTo) { buffers.pushSample(/*timestamp=*/1, {.position = {.x = 10, .y = 10}, .pressure = 0, - .orientation = 0, - .tilt = 0.2}); + .tilt = 0.2, + .orientation = 0}); buffers.pushSample(/*timestamp=*/2, {.position = {.x = 10, .y = 50}, .pressure = 0.4, - .orientation = M_PI / 4, - .tilt = 0.3}); + .tilt = 0.3, + .orientation = M_PI / 4}); buffers.pushSample(/*timestamp=*/3, {.position = {.x = 30, .y = 50}, .pressure = 0.5, - .orientation = -M_PI / 4, - .tilt = 0.4}); + .tilt = 0.4, + .orientation = -M_PI / 4}); buffers.pushSample(/*timestamp=*/3, {.position = {.x = 30, .y = 60}, .pressure = 0, - .orientation = 0, - .tilt = 0.5}); + .tilt = 0.5, + .orientation = 0}); buffers.copyTo(*model); const int zeroPadding = model->inputLength() - 3; diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp index 8d8b5300c1..9841c03826 100644 --- a/libs/input/tests/TouchResampling_test.cpp +++ b/libs/input/tests/TouchResampling_test.cpp @@ -571,11 +571,12 @@ TEST_F(TouchResamplingTest, TwoPointersAreResampledIndependently) { std::chrono::nanoseconds frameTime; std::vector<InputEventEntry> entries, expectedEntries; - // full action for when a pointer with id=1 appears (some other pointer must already be present) + // full action for when a pointer with index=1 appears (some other pointer must already be + // present) constexpr int32_t actionPointer1Down = AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - // full action for when a pointer with id=0 disappears (some other pointer must still remain) + // full action for when a pointer with index=0 disappears (some other pointer must still remain) constexpr int32_t actionPointer0Up = AMOTION_EVENT_ACTION_POINTER_UP + (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index f97eed5db3..ac3a832168 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -263,6 +263,16 @@ int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, floa return native_window_set_frame_rate(window, frameRate, compatibility, changeFrameRateStrategy); } +int32_t ANativeWindow_setFrameRateParams( + ANativeWindow* window, float desiredMinRate, float desiredMaxRate, float fixedSourceRate, + ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) { + if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) { + return -EINVAL; + } + return native_window_set_frame_rate_params(window, desiredMinRate, desiredMaxRate, + fixedSourceRate, changeFrameRateStrategy); +} + /************************************************************************************************** * vndk-stable **************************************************************************************************/ diff --git a/libs/nativewindow/include/android/native_window.h b/libs/nativewindow/include/android/native_window.h index be6623ee75..bd8d67a649 100644 --- a/libs/nativewindow/include/android/native_window.h +++ b/libs/nativewindow/include/android/native_window.h @@ -33,8 +33,8 @@ #ifndef ANDROID_NATIVE_WINDOW_H #define ANDROID_NATIVE_WINDOW_H -#include <stdint.h> #include <stdbool.h> +#include <stdint.h> #include <sys/cdefs.h> #include <android/data_space.h> @@ -282,7 +282,7 @@ int32_t ANativeWindow_setFrameRate(ANativeWindow* window, float frameRate, int8_ void ANativeWindow_tryAllocateBuffers(ANativeWindow* window) __INTRODUCED_IN(30); /** Change frame rate strategy value for ANativeWindow_setFrameRate. */ -enum ANativeWindow_ChangeFrameRateStrategy { +typedef enum ANativeWindow_ChangeFrameRateStrategy : int8_t { /** * Change the frame rate only if the transition is going to be seamless. */ @@ -292,7 +292,7 @@ enum ANativeWindow_ChangeFrameRateStrategy { * i.e. with visual interruptions for the user. */ ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS = 1 -} __INTRODUCED_IN(31); +} ANativeWindow_ChangeFrameRateStrategy __INTRODUCED_IN(31); /** * Sets the intended frame rate for this window. @@ -345,6 +345,76 @@ int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, floa __INTRODUCED_IN(31); /** + * Sets the intended frame rate for this window. + * + * On devices that are capable of running the display at different frame rates, + * the system may choose a display refresh rate to better match this surface's frame + * rate. Usage of this API won't introduce frame rate throttling, or affect other + * aspects of the application's frame production pipeline. However, because the system + * may change the display refresh rate, calls to this function may result in changes + * to Choreographer callback timings, and changes to the time interval at which the + * system releases buffers back to the application. + * + * Note that this only has an effect for surfaces presented on the display. If this + * surface is consumed by something other than the system compositor, e.g. a media + * codec, this call has no effect. + * + * You can register for changes in the refresh rate using + * \a AChoreographer_registerRefreshRateCallback. + * + * See ANativeWindow_clearFrameRate(). + * + * Available since API level 36. + * + * \param window pointer to an ANativeWindow object. + * + * \param desiredMinRate The desired minimum frame rate (inclusive) for the window, specifying that + * the surface prefers the device render rate to be at least `desiredMinRate`. + * + * <p>Set `desiredMinRate` = `desiredMaxRate` to indicate the surface prefers an exact frame rate. + * + * <p>Set `desiredMinRate` = 0 to indicate the window has no preference + * and any frame rate is acceptable. + * + * <p>The value should be greater than or equal to 0. + * + * \param desiredMaxRate The desired maximum frame rate (inclusive) for the window, specifying that + * the surface prefers the device render rate to be at most `desiredMaxRate`. + * + * <p>Set `desiredMaxRate` = `desiredMinRate` to indicate the surface prefers an exact frame rate. + * + * <p>Set `desiredMaxRate` = positive infinity to indicate the window has no preference + * and any frame rate is acceptable. + * + * <p>The value should be greater than or equal to `desiredMinRate`. + * + * \param fixedSourceRate The "fixed source" frame rate of the window if the content has an + * inherently fixed frame rate, e.g. a video that has a specific frame rate. + * + * <p>When the frame rate chosen for the surface is the `fixedSourceRate` or a + * multiple, the surface can render without frame pulldown, for optimal smoothness. For + * example, a 30 fps video (`fixedSourceRate`=30) renders just as smoothly on 30 fps, + * 60 fps, 90 fps, 120 fps, and so on. + * + * <p>Setting the fixed source rate can also be used together with a desired + * frame rate min and max via setting `desiredMinRate` and `desiredMaxRate`. This still + * means the window's content has a fixed frame rate of `fixedSourceRate`, but additionally + * specifies the preference to be in the range [`desiredMinRate`, `desiredMaxRate`]. For example, an + * app might want to specify there is 30 fps video (`fixedSourceRate`=30) as well as a smooth + * animation on the same window which looks good when drawing within a frame rate range such as + * [`desiredMinRate`, `desiredMaxRate`] = [60,120]. + * + * \param changeFrameRateStrategy Whether display refresh rate transitions caused by this surface + * should be seamless. A seamless transition is one that doesn't have any visual interruptions, such + * as a black screen for a second or two. + * + * \return 0 for success, -EINVAL if the arguments are invalid. + */ +int32_t ANativeWindow_setFrameRateParams( + ANativeWindow* window, float desiredMinRate, float desiredMaxRate, float fixedSourceRate, + ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) __INTRODUCED_IN(36); + +/** * Clears the frame rate which is set for this window. * * This is equivalent to calling @@ -366,14 +436,13 @@ int32_t ANativeWindow_setFrameRateWithChangeStrategy(ANativeWindow* window, floa * * See ANativeWindow_setFrameRateWithChangeStrategy(). * - * Available since API level 34. + * Available since API level 31. * * \param window pointer to an ANativeWindow object. * * \return 0 for success, -EINVAL if the window value is invalid. */ -inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) - __INTRODUCED_IN(__ANDROID_API_U__) { +inline int32_t ANativeWindow_clearFrameRate(ANativeWindow* window) __INTRODUCED_IN(31) { return ANativeWindow_setFrameRateWithChangeStrategy(window, 0, ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT, ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS); diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 33c303ae71..05f49ad25f 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1156,6 +1156,23 @@ static inline int native_window_set_frame_rate(struct ANativeWindow* window, flo (int)compatibility, (int)changeFrameRateStrategy); } +static inline int native_window_set_frame_rate_params(struct ANativeWindow* window, + float desiredMinRate, float desiredMaxRate, + float fixedSourceRate, + int8_t changeFrameRateStrategy) { + // TODO(b/362798998): Fix plumbing to send whole params + int compatibility = fixedSourceRate == 0 ? ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT + : ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; + double frameRate = compatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE + ? fixedSourceRate + : desiredMinRate; + if (desiredMaxRate < desiredMinRate) { + return -EINVAL; + } + return window->perform(window, NATIVE_WINDOW_SET_FRAME_RATE, frameRate, compatibility, + changeFrameRateStrategy); +} + struct ANativeWindowFrameTimelineInfo { // Frame Id received from ANativeWindow_getNextFrameId. uint64_t frameNumber; diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index e29d5a6bb4..071e3548d0 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -53,6 +53,7 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersTransform; ANativeWindow_setDequeueTimeout; # systemapi introduced=30 ANativeWindow_setFrameRate; # introduced=30 + ANativeWindow_setFrameRateParams; # introduced=36 ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31 ANativeWindow_setSharedBufferMode; # llndk ANativeWindow_setSwapInterval; # llndk diff --git a/libs/nativewindow/rust/src/handle.rs b/libs/nativewindow/rust/src/handle.rs index ee70e3f538..2b08c1bcb9 100644 --- a/libs/nativewindow/rust/src/handle.rs +++ b/libs/nativewindow/rust/src/handle.rs @@ -85,6 +85,12 @@ impl NativeHandle { /// Destroys the `NativeHandle`, taking ownership of the file descriptors it contained. pub fn into_fds(self) -> Vec<OwnedFd> { + // Unset FDSan tag since this `native_handle_t` is no longer the owner of the file + // descriptors after this function. + // SAFETY: Our wrapped `native_handle_t` pointer is always valid. + unsafe { + ffi::native_handle_unset_fdsan_tag(self.as_ref()); + } let fds = self.data()[..self.fd_count()] .iter() .map(|fd| { @@ -261,6 +267,29 @@ mod test { } #[test] + fn to_fds() { + let file = File::open("/dev/null").unwrap(); + let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); + assert_eq!(original.ints(), &[42]); + assert_eq!(original.fds().len(), 1); + + let fds = original.into_fds(); + assert_eq!(fds.len(), 1); + } + + #[test] + fn to_aidl() { + let file = File::open("/dev/null").unwrap(); + let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); + assert_eq!(original.ints(), &[42]); + assert_eq!(original.fds().len(), 1); + + let aidl = AidlNativeHandle::from(original); + assert_eq!(&aidl.ints, &[42]); + assert_eq!(aidl.fds.len(), 1); + } + + #[test] fn to_from_aidl() { let file = File::open("/dev/null").unwrap(); let original = NativeHandle::new(vec![file.into()], &[42]).unwrap(); diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs index a1d986ed2b..014c912a67 100644 --- a/libs/nativewindow/rust/src/lib.rs +++ b/libs/nativewindow/rust/src/lib.rs @@ -203,8 +203,8 @@ impl HardwareBuffer { Self(buffer_ptr) } - /// Creates a new Rust HardwareBuffer to wrap the given AHardwareBuffer without taking ownership - /// of it. + /// Creates a new Rust HardwareBuffer to wrap the given `AHardwareBuffer` without taking + /// ownership of it. /// /// Unlike [`from_raw`](Self::from_raw) this method will increment the refcount on the buffer. /// This means that the caller can continue to use the raw buffer it passed in, and must call @@ -220,14 +220,20 @@ impl HardwareBuffer { Self(buffer) } - /// Get the internal `AHardwareBuffer` pointer that is only valid when this `HardwareBuffer` - /// exists. This can be used to provide a pointer to the AHB for a C/C++ API over the FFI. + /// Returns the internal `AHardwareBuffer` pointer. + /// + /// This is only valid as long as this `HardwareBuffer` exists, so shouldn't be stored. It can + /// be used to provide a pointer for a C/C++ API over FFI. pub fn as_raw(&self) -> NonNull<AHardwareBuffer> { self.0 } - /// Get the internal `AHardwareBuffer` pointer without decrementing the refcount. This can - /// be used to provide a pointer to the AHB for a C/C++ API over the FFI. + /// Gets the internal `AHardwareBuffer` pointer without decrementing the refcount. This can + /// be used for a C/C++ API which takes ownership of the pointer. + /// + /// The caller is responsible for releasing the `AHardwareBuffer` pointer by calling + /// `AHardwareBuffer_release` when it is finished with it, or may convert it back to a Rust + /// `HardwareBuffer` by calling [`HardwareBuffer::from_raw`]. pub fn into_raw(self) -> NonNull<AHardwareBuffer> { let buffer = ManuallyDrop::new(self); buffer.0 diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index d248ea0b84..7f207f0670 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -105,6 +105,7 @@ filegroup { "skia/filters/KawaseBlurDualFilter.cpp", "skia/filters/KawaseBlurFilter.cpp", "skia/filters/LinearEffect.cpp", + "skia/filters/LutShader.cpp", "skia/filters/MouriMap.cpp", "skia/filters/StretchShaderFactory.cpp", "skia/filters/EdgeExtensionShaderFactory.cpp", diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index 859ae8b6e2..ac43da8dcf 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -16,6 +16,7 @@ #pragma once +#include <gui/DisplayLuts.h> #include <math/mat4.h> #include <math/vec3.h> #include <renderengine/ExternalTexture.h> @@ -145,6 +146,8 @@ struct LayerSettings { // If white point nits are unknown, then this layer is assumed to have the // same luminance as the brightest layer in the scene. float whitePointNits = -1.f; + + std::shared_ptr<gui::DisplayLuts> luts; }; // Keep in sync with custom comparison function in @@ -187,7 +190,7 @@ static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs lhs.blurRegionTransform == rhs.blurRegionTransform && lhs.stretchEffect == rhs.stretchEffect && lhs.edgeExtensionEffect == rhs.edgeExtensionEffect && - lhs.whitePointNits == rhs.whitePointNits; + lhs.whitePointNits == rhs.whitePointNits && lhs.luts == rhs.luts; } static inline void PrintTo(const Buffer& settings, ::std::ostream* os) { diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp index a3a43e20be..cc73f405a6 100644 --- a/libs/renderengine/skia/GaneshVkRenderEngine.cpp +++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp @@ -21,12 +21,15 @@ #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h> +#include <android-base/stringprintf.h> #include <common/trace.h> #include <log/log_main.h> #include <sync/sync.h> namespace android::renderengine::skia { +using base::StringAppendF; + std::unique_ptr<GaneshVkRenderEngine> GaneshVkRenderEngine::create( const RenderEngineCreationArgs& args) { std::unique_ptr<GaneshVkRenderEngine> engine(new GaneshVkRenderEngine(args)); @@ -111,4 +114,9 @@ base::unique_fd GaneshVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, return res; } +void GaneshVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { + StringAppendF(&result, "\n ------------RE Vulkan (Ganesh)----------\n"); + SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result); +} + } // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.h b/libs/renderengine/skia/GaneshVkRenderEngine.h index e6123c21bf..ba17f71201 100644 --- a/libs/renderengine/skia/GaneshVkRenderEngine.h +++ b/libs/renderengine/skia/GaneshVkRenderEngine.h @@ -28,6 +28,7 @@ protected: std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override; void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override; base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override; + void appendBackendSpecificInfoToDump(std::string& result) override; private: GaneshVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {} diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp index 390ad6efd1..a9332fa4e1 100644 --- a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp +++ b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp @@ -25,6 +25,7 @@ #include <include/gpu/graphite/Recording.h> #include <include/gpu/graphite/vk/VulkanGraphiteTypes.h> +#include <android-base/stringprintf.h> #include <log/log_main.h> #include <sync/sync.h> @@ -33,6 +34,8 @@ namespace android::renderengine::skia { +using base::StringAppendF; + std::unique_ptr<GraphiteVkRenderEngine> GraphiteVkRenderEngine::create( const RenderEngineCreationArgs& args) { std::unique_ptr<GraphiteVkRenderEngine> engine(new GraphiteVkRenderEngine(args)); @@ -139,4 +142,9 @@ base::unique_fd GraphiteVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, return drawFenceFd; } +void GraphiteVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { + StringAppendF(&result, "\n ------------RE Vulkan (Graphite)----------\n"); + SkiaVkRenderEngine::appendBackendSpecificInfoToDump(result); +} + } // namespace android::renderengine::skia diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.h b/libs/renderengine/skia/GraphiteVkRenderEngine.h index cf24a3b756..33a47f1a7f 100644 --- a/libs/renderengine/skia/GraphiteVkRenderEngine.h +++ b/libs/renderengine/skia/GraphiteVkRenderEngine.h @@ -30,6 +30,7 @@ protected: std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override; void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override; base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override; + void appendBackendSpecificInfoToDump(std::string& result) override; private: GraphiteVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {} diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 4ef7d5bccb..ddae9fc78f 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -541,7 +541,7 @@ int SkiaGLRenderEngine::getContextPriority() { void SkiaGLRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { const GLExtensions& extensions = GLExtensions::getInstance(); - StringAppendF(&result, "\n ------------RE GLES------------\n"); + StringAppendF(&result, "\n ------------RE GLES (Ganesh)------------\n"); StringAppendF(&result, "EGL implementation : %s\n", extensions.getEGLVersion()); StringAppendF(&result, "%s\n", extensions.getEGLExtensions()); StringAppendF(&result, "GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(), diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index ec9d3efb88..5c46c9168d 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -543,6 +543,10 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( } } + if (graphicBuffer && parameters.layer.luts) { + shader = mLutShader.lutShader(shader, parameters.layer.luts); + } + if (parameters.requiresLinearEffect) { const auto format = targetBuffer != nullptr ? std::optional<ui::PixelFormat>( diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index b5f8898263..7be4c253e7 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -39,6 +39,7 @@ #include "filters/BlurFilter.h" #include "filters/EdgeExtensionShaderFactory.h" #include "filters/LinearEffect.h" +#include "filters/LutShader.h" #include "filters/StretchShaderFactory.h" class SkData; @@ -184,6 +185,7 @@ private: StretchShaderFactory mStretchShaderFactory; EdgeExtensionShaderFactory mEdgeExtensionShaderFactory; + LutShader mLutShader; sp<Fence> mLastDrawFence; BlurFilter* mBlurFilter = nullptr; diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index 677a2b63b2..177abe6c9f 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -169,24 +169,26 @@ int SkiaVkRenderEngine::getContextPriority() { } void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) { - StringAppendF(&result, "\n ------------RE Vulkan----------\n"); - StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.isInitialized()); - StringAppendF(&result, "\n Vulkan protected device initialized: %d\n", + // Subclasses will prepend a backend-specific name / section header + StringAppendF(&result, "Vulkan device initialized: %d\n", sVulkanInterface.isInitialized()); + StringAppendF(&result, "Vulkan protected device initialized: %d\n", sProtectedContentVulkanInterface.isInitialized()); if (!sVulkanInterface.isInitialized()) { return; } - StringAppendF(&result, "\n Instance extensions:\n"); + StringAppendF(&result, "Instance extensions: [\n"); for (const auto& name : sVulkanInterface.getInstanceExtensionNames()) { - StringAppendF(&result, "\n %s\n", name.c_str()); + StringAppendF(&result, " %s\n", name.c_str()); } + StringAppendF(&result, "]\n"); - StringAppendF(&result, "\n Device extensions:\n"); + StringAppendF(&result, "Device extensions: [\n"); for (const auto& name : sVulkanInterface.getDeviceExtensionNames()) { - StringAppendF(&result, "\n %s\n", name.c_str()); + StringAppendF(&result, " %s\n", name.c_str()); } + StringAppendF(&result, "]\n"); } } // namespace skia diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h index d2bb3d53cf..88b04df58d 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.h +++ b/libs/renderengine/skia/SkiaVkRenderEngine.h @@ -81,7 +81,7 @@ protected: SkiaRenderEngine::Contexts createContexts() override; bool supportsProtectedContentImpl() const override; bool useProtectedContextImpl(GrProtected isProtected) override; - void appendBackendSpecificInfoToDump(std::string& result) override; + virtual void appendBackendSpecificInfoToDump(std::string& result) override; // TODO: b/300533018 - refactor this to be non-static static VulkanInterface& getVulkanInterface(bool protectedContext); diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp new file mode 100644 index 0000000000..cea46ef40e --- /dev/null +++ b/libs/renderengine/skia/filters/LutShader.cpp @@ -0,0 +1,242 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "LutShader.h" + +#include <SkTileMode.h> +#include <common/trace.h> +#include <cutils/ashmem.h> +#include <math/half.h> +#include <sys/mman.h> + +#include "include/core/SkColorSpace.h" +#include "src/core/SkColorFilterPriv.h" + +using aidl::android::hardware::graphics::composer3::LutProperties; + +namespace android { +namespace renderengine { +namespace skia { + +static const SkString kShader = SkString(R"( + uniform shader image; + uniform shader lut; + uniform int size; + uniform int key; + uniform int dimension; + vec4 main(vec2 xy) { + float4 rgba = image.eval(xy); + float3 linear = toLinearSrgb(rgba.rgb); + if (dimension == 1) { + // RGB + if (key == 0) { + float indexR = linear.r * float(size - 1); + float indexG = linear.g * float(size - 1); + float indexB = linear.b * float(size - 1); + float gainR = lut.eval(vec2(indexR, 0.0) + 0.5).r; + float gainG = lut.eval(vec2(indexG, 0.0) + 0.5).r; + float gainB = lut.eval(vec2(indexB, 0.0) + 0.5).r; + return float4(linear.r * gainR, linear.g * gainG, linear.b * gainB, rgba.a); + // MAX_RGB + } else if (key == 1) { + float4 rgba = image.eval(xy); + float3 linear = toLinearSrgb(rgba.rgb); + float maxRGB = max(linear.r, max(linear.g, linear.b)); + float index = maxRGB * float(size - 1); + float gain = lut.eval(vec2(index, 0.0) + 0.5).r; + return float4(linear * gain, rgba.a); + } + } else if (dimension == 3) { + if (key == 0) { + float tx = linear.r * float(size - 1); + float ty = linear.g * float(size - 1); + float tz = linear.b * float(size - 1); + + // calculate lower and upper bounds for each dimension + int x = int(tx); + int y = int(ty); + int z = int(tz); + + int i000 = x + y * size + z * size * size; + int i100 = i000 + 1; + int i010 = i000 + size; + int i110 = i000 + size + 1; + int i001 = i000 + size * size; + int i101 = i000 + size * size + 1; + int i011 = i000 + size * size + size; + int i111 = i000 + size * size + size + 1; + + // get 1d normalized indices + float c000 = float(i000) / float(size * size * size); + float c100 = float(i100) / float(size * size * size); + float c010 = float(i010) / float(size * size * size); + float c110 = float(i110) / float(size * size * size); + float c001 = float(i001) / float(size * size * size); + float c101 = float(i101) / float(size * size * size); + float c011 = float(i011) / float(size * size * size); + float c111 = float(i111) / float(size * size * size); + + //TODO(b/377984618): support Tetrahedral interpolation + // perform trilinear interpolation + float3 c00 = mix(lut.eval(vec2(c000, 0.0) + 0.5).rgb, + lut.eval(vec2(c100, 0.0) + 0.5).rgb, linear.r); + float3 c01 = mix(lut.eval(vec2(c001, 0.0) + 0.5).rgb, + lut.eval(vec2(c101, 0.0) + 0.5).rgb, linear.r); + float3 c10 = mix(lut.eval(vec2(c010, 0.0) + 0.5).rgb, + lut.eval(vec2(c110, 0.0) + 0.5).rgb, linear.r); + float3 c11 = mix(lut.eval(vec2(c011, 0.0) + 0.5).rgb, + lut.eval(vec2(c111, 0.0) + 0.5).rgb, linear.r); + + float3 c0 = mix(c00, c10, linear.g); + float3 c1 = mix(c01, c11, linear.g); + + float3 val = mix(c0, c1, linear.b); + + return float4(val, rgba.a); + } + } + return rgba; + })"); + +sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input, + const std::vector<float>& buffers, + const int32_t offset, const int32_t length, + const int32_t dimension, const int32_t size, + const int32_t samplingKey) { + SFTRACE_NAME("lut shader"); + std::vector<half> buffer(length * 4); // 4 is for RGBA + auto d = static_cast<LutProperties::Dimension>(dimension); + if (d == LutProperties::Dimension::ONE_D) { + auto it = buffers.begin() + offset; + std::generate(buffer.begin(), buffer.end(), [it, i = 0]() mutable { + float val = (i++ % 4 == 0) ? *it++ : 0.0f; + return half(val); + }); + } else { + for (int i = 0; i < length; i++) { + buffer[i * 4] = half(buffers[offset + i]); + buffer[i * 4 + 1] = half(buffers[offset + length + i]); + buffer[i * 4 + 2] = half(buffers[offset + length * 2 + i]); + buffer[i * 4 + 3] = half(0); + } + } + /** + * 1D Lut(rgba) + * (R0, 0, 0, 0) + * (R1, 0, 0, 0) + * ... + * + * 3D Lut + * (R0, G0, B0, 0) + * (R1, G1, B1, 0) + * ... + */ + SkImageInfo info = SkImageInfo::Make(length /* the number of rgba */ * 4, 1, + kRGBA_F16_SkColorType, kPremul_SkAlphaType); + SkBitmap bitmap; + bitmap.allocPixels(info); + if (!bitmap.installPixels(info, buffer.data(), info.minRowBytes())) { + LOG_ALWAYS_FATAL("unable to install pixels"); + } + + sk_sp<SkImage> lutImage = SkImages::RasterFromBitmap(bitmap); + mBuilder->child("image") = input; + mBuilder->child("lut") = + lutImage->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp, + d == LutProperties::Dimension::ONE_D + ? SkSamplingOptions(SkFilterMode::kLinear) + : SkSamplingOptions()); + + const int uSize = static_cast<int>(size); + const int uKey = static_cast<int>(samplingKey); + const int uDimension = static_cast<int>(dimension); + mBuilder->uniform("size") = uSize; + mBuilder->uniform("key") = uKey; + mBuilder->uniform("dimension") = uDimension; + return mBuilder->makeShader(); +} + +sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input, + std::shared_ptr<gui::DisplayLuts> displayLuts) { + if (mBuilder == nullptr) { + const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(kShader); + mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect); + } + + auto& fd = displayLuts->getLutFileDescriptor(); + if (fd.ok()) { + // de-gamma the image without changing the primaries + SkImage* baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr); + if (baseImage) { + sk_sp<SkColorSpace> baseColorSpace = + baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB(); + sk_sp<SkColorSpace> gainmapMathColorSpace = baseColorSpace->makeLinearGamma(); + auto colorXformSdrToGainmap = + SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace); + input = input->makeWithColorFilter(colorXformSdrToGainmap); + } + + auto& offsets = displayLuts->offsets; + auto& lutProperties = displayLuts->lutProperties; + std::vector<float> buffers; + int fullLength = offsets[lutProperties.size() - 1]; + if (lutProperties[lutProperties.size() - 1].dimension == 1) { + fullLength += lutProperties[lutProperties.size() - 1].size; + } else { + fullLength += (lutProperties[lutProperties.size() - 1].size * + lutProperties[lutProperties.size() - 1].size * + lutProperties[lutProperties.size() - 1].size * 3); + } + size_t bufferSize = fullLength * sizeof(float); + + // decode the shared memory of luts + float* ptr = + (float*)mmap(NULL, bufferSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0); + if (ptr == MAP_FAILED) { + LOG_ALWAYS_FATAL("mmap failed"); + } + buffers = std::vector<float>(ptr, ptr + fullLength); + munmap(ptr, bufferSize); + + for (size_t i = 0; i < offsets.size(); i++) { + int bufferSizePerLut = (i == offsets.size() - 1) ? buffers.size() - offsets[i] + : offsets[i + 1] - offsets[i]; + // divide by 3 for 3d Lut because of 3 (RGB) channels + if (static_cast<LutProperties::Dimension>(lutProperties[i].dimension) == + LutProperties::Dimension::THREE_D) { + bufferSizePerLut /= 3; + } + input = generateLutShader(input, buffers, offsets[i], bufferSizePerLut, + lutProperties[i].dimension, lutProperties[i].size, + lutProperties[i].samplingKey); + } + + // re-gamma + baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr); + if (baseImage) { + sk_sp<SkColorSpace> baseColorSpace = + baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB(); + sk_sp<SkColorSpace> gainmapMathColorSpace = baseColorSpace->makeLinearGamma(); + auto colorXformGainmapToDst = + SkColorFilterPriv::MakeColorSpaceXform(gainmapMathColorSpace, baseColorSpace); + input = input->makeWithColorFilter(colorXformGainmapToDst); + } + } + return input; +} + +} // namespace skia +} // namespace renderengine +} // namespace android
\ No newline at end of file diff --git a/libs/renderengine/skia/filters/LutShader.h b/libs/renderengine/skia/filters/LutShader.h new file mode 100644 index 0000000000..c157904b63 --- /dev/null +++ b/libs/renderengine/skia/filters/LutShader.h @@ -0,0 +1,44 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *- + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <SkBitmap.h> +#include <SkImage.h> +#include <SkRuntimeEffect.h> + +#include <aidl/android/hardware/graphics/composer3/LutProperties.h> +#include <gui/DisplayLuts.h> + +namespace android { +namespace renderengine { +namespace skia { + +class LutShader { +public: + sk_sp<SkShader> lutShader(sk_sp<SkShader>& input, + std::shared_ptr<gui::DisplayLuts> displayLuts); + +private: + sk_sp<SkShader> generateLutShader(sk_sp<SkShader> input, const std::vector<float>& buffers, + const int32_t offset, const int32_t length, + const int32_t dimension, const int32_t size, + const int32_t samplingKey); + std::unique_ptr<SkRuntimeShaderBuilder> mBuilder; +}; + +} // namespace skia +} // namespace renderengine +} // namespace android diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index eddd568fb5..797efbe5df 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -306,7 +306,18 @@ Sensor::Sensor(struct sensor_t const& hwSensor, const uuid_t& uuid, int halVersi } if (halVersion > SENSORS_DEVICE_API_VERSION_1_0 && hwSensor.requiredPermission) { mRequiredPermission = hwSensor.requiredPermission; - if (!strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS)) { + bool requiresBodySensorPermission = + !strcmp(mRequiredPermission, SENSOR_PERMISSION_BODY_SENSORS); + if (android::permission::flags::replace_body_sensor_permission_enabled()) { + if (requiresBodySensorPermission) { + ALOGE("Sensor %s using deprecated Body Sensor permission", mName.c_str()); + } + + AppOpsManager appOps; + // Lookup to see if an AppOp exists for the permission. If none + // does, the default value of -1 is used. + mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission)); + } else if (requiresBodySensorPermission) { AppOpsManager appOps; mRequiredAppOp = appOps.permissionToOpCode(String16(SENSOR_PERMISSION_BODY_SENSORS)); } diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp index b5c56c5c52..9a2d4f7463 100644 --- a/libs/tracing_perfetto/Android.bp +++ b/libs/tracing_perfetto/Android.bp @@ -47,4 +47,6 @@ cc_library_shared { ], host_supported: true, + // for vndbinder + vendor_available: true, } diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp index 9a0042aee5..c4f866338a 100644 --- a/libs/tracing_perfetto/tracing_perfetto_internal.cpp +++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp @@ -253,15 +253,31 @@ void perfettoTraceEnd(const struct PerfettoTeCategory& category) { void perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name, const char* trackName, uint64_t cookie) { PERFETTO_TE( - category, PERFETTO_TE_SLICE_BEGIN(name), - PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); + category, PERFETTO_TE_SLICE_BEGIN(name), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeNamedTrackUuid(trackName, cookie, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + trackName), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()))); } void perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category, const char* trackName, uint64_t cookie) { - PERFETTO_TE( - category, PERFETTO_TE_SLICE_END(), - PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid())); + PERFETTO_TE( + category, PERFETTO_TE_SLICE_END(), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeNamedTrackUuid(trackName, cookie, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + trackName), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()))); } void perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name, @@ -281,14 +297,35 @@ void perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* void perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category, const char* trackName, const char* name) { PERFETTO_TE( - category, PERFETTO_TE_INSTANT(name), - PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid())); + category, PERFETTO_TE_INSTANT(name), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeNamedTrackUuid(trackName, 1, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + trackName), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()))); } void perfettoTraceCounter(const struct PerfettoTeCategory& category, - [[maybe_unused]] const char* name, int64_t value) { - PERFETTO_TE(category, PERFETTO_TE_COUNTER(), - PERFETTO_TE_INT_COUNTER(value)); + const char* name, int64_t value) { + PERFETTO_TE( + category, PERFETTO_TE_COUNTER(), + PERFETTO_TE_PROTO_TRACK( + PerfettoTeCounterTrackUuid(name, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_CSTR( + perfetto_protos_TrackDescriptor_atrace_name_field_number, + name), + PERFETTO_TE_PROTO_FIELD_VARINT( + perfetto_protos_TrackDescriptor_parent_uuid_field_number, + PerfettoTeProcessTrackUuid()), + PERFETTO_TE_PROTO_FIELD_BYTES( + perfetto_protos_TrackDescriptor_counter_field_number, + PERFETTO_NULL, 0)), + PERFETTO_TE_INT_COUNTER(value)); } } // namespace internal diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 12230f99d2..87e213e394 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -136,6 +136,7 @@ cc_library_shared { "GraphicBuffer.cpp", "GraphicBufferAllocator.cpp", "GraphicBufferMapper.cpp", + "PictureProfileHandle.cpp", "PixelFormat.cpp", "PublicFormat.cpp", "StaticAsserts.cpp", diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp index c9ec036186..2143f79f11 100644 --- a/libs/ui/Gralloc5.cpp +++ b/libs/ui/Gralloc5.cpp @@ -23,7 +23,6 @@ #include <aidlcommonsupport/NativeHandle.h> #include <android/binder_manager.h> #include <android/hardware/graphics/mapper/utils/IMapperMetadataTypes.h> -#include <android/llndk-versioning.h> #include <binder/IPCThreadState.h> #include <dlfcn.h> #include <ui/FatVector.h> @@ -91,7 +90,7 @@ static void *loadIMapperLibrary() { } void* so = nullptr; - if API_LEVEL_AT_LEAST (__ANDROID_API_V__, 202404) { + if (__builtin_available(android __ANDROID_API_V__, *)) { so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(), RTLD_LOCAL | RTLD_NOW); } else { diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp b/libs/ui/PictureProfileHandle.cpp index 2323ebb4aa..0701e906f0 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.cpp +++ b/libs/ui/PictureProfileHandle.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,16 @@ * limitations under the License. */ -#include "mock/DisplayHardware/MockIPower.h" +#include <ui/PictureProfileHandle.h> -namespace android::Hwc2::mock { +#include <format> -// Explicit default instantiation is recommended. -MockIPower::MockIPower() = default; +namespace android { -} // namespace android::Hwc2::mock +const PictureProfileHandle PictureProfileHandle::NONE(0); + +::std::string toString(const PictureProfileHandle& handle) { + return std::format("{:#010x}", handle.getId()); +} + +} // namespace android diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h index 25a2b6ee53..af494dcf39 100644 --- a/libs/ui/include/ui/DynamicDisplayInfo.h +++ b/libs/ui/include/ui/DynamicDisplayInfo.h @@ -22,6 +22,7 @@ #include <optional> #include <vector> +#include <ui/FrameRateCategoryRate.h> #include <ui/GraphicTypes.h> #include <ui/HdrCapabilities.h> @@ -55,6 +56,9 @@ struct DynamicDisplayInfo { std::optional<ui::DisplayMode> getActiveDisplayMode() const; bool hasArrSupport; + + // Represents frame rate for FrameRateCategory Normal and High. + ui::FrameRateCategoryRate frameRateCategoryRate; }; } // namespace android::ui diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp b/libs/ui/include/ui/FrameRateCategoryRate.h index 0baa79d0bb..9c392d9bc8 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp +++ b/libs/ui/include/ui/FrameRateCategoryRate.h @@ -1,6 +1,6 @@ /* - * Copyright 2019 The Android Open Source Project - + * Copyright 2024 The Android Open Source Project + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -14,19 +14,22 @@ * limitations under the License. */ -#include "MockHWC2.h" +#pragma once + +namespace android::ui { -namespace android::HWC2 { +// Represents frame rate for FrameRateCategory Normal and High. +class FrameRateCategoryRate { +public: + FrameRateCategoryRate(float normal = 0, float high = 0) : mNormal(normal), mHigh(high) {} -// This will go away once HWC2::Layer is moved into the "backend" library -Layer::~Layer() = default; + float getNormal() const { return mNormal; } -namespace mock { + float getHigh() const { return mHigh; } -// The Google Mock documentation recommends explicit non-header instantiations -// for better compile time performance. -Layer::Layer() = default; -Layer::~Layer() = default; +private: + float mNormal; + float mHigh; +}; -} // namespace mock -} // namespace android::HWC2 +} // namespace android::ui
\ No newline at end of file diff --git a/libs/ui/include/ui/PictureProfileHandle.h b/libs/ui/include/ui/PictureProfileHandle.h new file mode 100644 index 0000000000..f8406501b4 --- /dev/null +++ b/libs/ui/include/ui/PictureProfileHandle.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <array> +#include <string> + +namespace android { + +/** + * An opaque value that uniquely identifies a picture profile, or a set of parameters, which + * describes the configuration of a picture processing pipeline that is applied to a graphic buffer + * to enhance its quality prior to rendering on the display. + */ +typedef int64_t PictureProfileId; + +/** + * A picture profile handle wraps the picture profile ID for type-safety, and represents an opaque + * handle that doesn't have the performance drawbacks of Binders. + */ +class PictureProfileHandle { +public: + // A profile that represents no picture processing. + static const PictureProfileHandle NONE; + + PictureProfileHandle() { *this = NONE; } + explicit PictureProfileHandle(PictureProfileId id) : mId(id) {} + + PictureProfileId const& getId() const { return mId; } + + inline bool operator==(const PictureProfileHandle& rhs) { return mId == rhs.mId; } + inline bool operator!=(const PictureProfileHandle& rhs) { return !(*this == rhs); } + + // Is the picture profile effectively null, or not-specified? + inline bool operator!() const { return mId == NONE.mId; } + + operator bool() const { return !!*this; } + + friend ::std::string toString(const PictureProfileHandle& handle); + +private: + PictureProfileId mId; +}; + +} // namespace android diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index 5159ffe86b..b19a862b6c 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -135,6 +135,9 @@ cc_library_static { "EGL/MultifileBlobCache.cpp", ], export_include_dirs: ["EGL"], + shared_libs: [ + "libz", + ], } cc_library_shared { @@ -169,6 +172,7 @@ cc_library_shared { "libutils", "libSurfaceFlingerProp", "libunwindstack", + "libz", ], static_libs: [ "libEGL_getProcAddress", @@ -199,6 +203,7 @@ cc_test { ], shared_libs: [ "libutils", + "libz", ], } diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp index 4a0fac4ce5..573ca5411a 100644 --- a/opengl/libs/EGL/FileBlobCache.cpp +++ b/opengl/libs/EGL/FileBlobCache.cpp @@ -27,6 +27,7 @@ #include <log/log.h> #include <utils/Trace.h> +#include <zlib.h> // Cache file header static const char* cacheFileMagic = "EGL$"; @@ -34,20 +35,10 @@ static const size_t cacheFileHeaderSize = 8; namespace android { -uint32_t crc32c(const uint8_t* buf, size_t len) { - const uint32_t polyBits = 0x82F63B78; - uint32_t r = 0; - for (size_t i = 0; i < len; i++) { - r ^= buf[i]; - for (int j = 0; j < 8; j++) { - if (r & 1) { - r = (r >> 1) ^ polyBits; - } else { - r >>= 1; - } - } - } - return r; +uint32_t GenerateCRC32(const uint8_t *data, size_t size) +{ + const unsigned long initialValue = crc32_z(0u, nullptr, 0u); + return static_cast<uint32_t>(crc32_z(initialValue, data, size)); } FileBlobCache::FileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize, @@ -101,7 +92,7 @@ FileBlobCache::FileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxT return; } uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4); - if (crc32c(buf + headerSize, cacheSize) != *crc) { + if (GenerateCRC32(buf + headerSize, cacheSize) != *crc) { ALOGE("cache file failed CRC check"); close(fd); return; @@ -175,7 +166,7 @@ void FileBlobCache::writeToFile() { // Write the file magic and CRC memcpy(buf, cacheFileMagic, 4); uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4); - *crc = crc32c(buf + headerSize, cacheSize); + *crc = GenerateCRC32(buf + headerSize, cacheSize); if (write(fd, buf, fileSize) == -1) { ALOGE("error writing cache file: %s (%d)", strerror(errno), diff --git a/opengl/libs/EGL/FileBlobCache.h b/opengl/libs/EGL/FileBlobCache.h index f083b0d6ca..224444da65 100644 --- a/opengl/libs/EGL/FileBlobCache.h +++ b/opengl/libs/EGL/FileBlobCache.h @@ -22,7 +22,7 @@ namespace android { -uint32_t crc32c(const uint8_t* buf, size_t len); +uint32_t GenerateCRC32(const uint8_t *data, size_t size); class FileBlobCache : public BlobCache { public: diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp index 9905210014..f7e33b383f 100644 --- a/opengl/libs/EGL/MultifileBlobCache.cpp +++ b/opengl/libs/EGL/MultifileBlobCache.cpp @@ -214,9 +214,8 @@ MultifileBlobCache::MultifileBlobCache(size_t maxKeySize, size_t maxValueSize, s } // Ensure we have a good CRC - if (header.crc != - crc32c(mappedEntry + sizeof(MultifileHeader), - fileSize - sizeof(MultifileHeader))) { + if (header.crc != GenerateCRC32(mappedEntry + sizeof(MultifileHeader), + fileSize - sizeof(MultifileHeader))) { ALOGV("INIT: Entry %u failed CRC check! Removing.", entryHash); if (remove(fullPath.c_str()) != 0) { ALOGE("Error removing %s: %s", fullPath.c_str(), std::strerror(errno)); @@ -532,9 +531,9 @@ bool MultifileBlobCache::createStatus(const std::string& baseDir) { mBuildId.length() > PROP_VALUE_MAX ? PROP_VALUE_MAX : mBuildId.length()); // Finally update the crc, using cacheVersion and everything the follows - status.crc = - crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion), - sizeof(status) - offsetof(MultifileStatus, cacheVersion)); + status.crc = GenerateCRC32( + reinterpret_cast<uint8_t *>(&status) + offsetof(MultifileStatus, cacheVersion), + sizeof(status) - offsetof(MultifileStatus, cacheVersion)); // Create the status file std::string cacheStatus = baseDir + "/" + kMultifileBlobCacheStatusFile; @@ -599,9 +598,9 @@ bool MultifileBlobCache::checkStatus(const std::string& baseDir) { } // Ensure we have a good CRC - if (status.crc != - crc32c(reinterpret_cast<uint8_t*>(&status) + offsetof(MultifileStatus, cacheVersion), - sizeof(status) - offsetof(MultifileStatus, cacheVersion))) { + if (status.crc != GenerateCRC32(reinterpret_cast<uint8_t *>(&status) + + offsetof(MultifileStatus, cacheVersion), + sizeof(status) - offsetof(MultifileStatus, cacheVersion))) { ALOGE("STATUS(CHECK): Cache status failed CRC check!"); return false; } @@ -840,8 +839,8 @@ void MultifileBlobCache::processTask(DeferredTask& task) { // Add CRC check to the header (always do this last!) MultifileHeader* header = reinterpret_cast<MultifileHeader*>(buffer); - header->crc = - crc32c(buffer + sizeof(MultifileHeader), bufferSize - sizeof(MultifileHeader)); + header->crc = GenerateCRC32(buffer + sizeof(MultifileHeader), + bufferSize - sizeof(MultifileHeader)); ssize_t result = write(fd, buffer, bufferSize); if (result != bufferSize) { diff --git a/opengl/libs/EGL/MultifileBlobCache.h b/opengl/libs/EGL/MultifileBlobCache.h index 18566c2892..65aa2db344 100644 --- a/opengl/libs/EGL/MultifileBlobCache.h +++ b/opengl/libs/EGL/MultifileBlobCache.h @@ -34,7 +34,7 @@ namespace android { -constexpr uint32_t kMultifileBlobCacheVersion = 1; +constexpr uint32_t kMultifileBlobCacheVersion = 2; constexpr char kMultifileBlobCacheStatusFile[] = "cache.status"; struct MultifileHeader { diff --git a/opengl/libs/EGL/fuzzer/Android.bp b/opengl/libs/EGL/fuzzer/Android.bp index 022a2a3f06..4947e5ff6c 100644 --- a/opengl/libs/EGL/fuzzer/Android.bp +++ b/opengl/libs/EGL/fuzzer/Android.bp @@ -36,6 +36,10 @@ cc_fuzz { "libutils", ], + shared_libs: [ + "libz", + ], + srcs: [ "MultifileBlobCache_fuzzer.cpp", ], diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp index 503d7dffdb..839a5cab2f 100644 --- a/opengl/tests/EGLTest/EGL_test.cpp +++ b/opengl/tests/EGLTest/EGL_test.cpp @@ -765,6 +765,30 @@ TEST_F(EGLTest, EGLNoConfigContext) { } } +// Verify that eglCreateContext works when EGL_TELEMETRY_HINT_ANDROID is used with +// NO_HINT = 0, SKIP_TELEMETRY = 1 and an invalid of value of 2 +TEST_F(EGLTest, EGLContextTelemetryHintExt) { + for (int i = 0; i < 3; i++) { + EGLConfig config; + get8BitConfig(config); + std::vector<EGLint> contextAttributes; + contextAttributes.reserve(4); + contextAttributes.push_back(EGL_TELEMETRY_HINT_ANDROID); + contextAttributes.push_back(i); + contextAttributes.push_back(EGL_NONE); + contextAttributes.push_back(EGL_NONE); + + EGLContext eglContext = eglCreateContext(mEglDisplay, config, EGL_NO_CONTEXT, + contextAttributes.data()); + EXPECT_NE(EGL_NO_CONTEXT, eglContext); + EXPECT_EQ(EGL_SUCCESS, eglGetError()); + + if (eglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, eglContext); + } + } +} + // Emulate what a native application would do to create a // 10:10:10:2 surface. TEST_F(EGLTest, EGLConfig1010102) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 4f9d9e43a3..755995c15f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -836,13 +836,9 @@ bool isStylusActiveInDisplay(ui::LogicalDisplayId displayId, } Result<void> validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) { - struct HashFunction { - size_t operator()(const WindowInfo& info) const { return info.id; } - }; - - std::unordered_set<WindowInfo, HashFunction> windowSet; + std::unordered_set<int32_t> windowIds; for (const WindowInfo& info : update.windowInfos) { - const auto [_, inserted] = windowSet.insert(info); + const auto [_, inserted] = windowIds.insert(info.id); if (!inserted) { return Error() << "Duplicate entry for " << info; } diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 6093c91e34..305feab6c7 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -137,19 +137,18 @@ struct InputReaderConfiguration { ui::LogicalDisplayId defaultPointerDisplayId; // The mouse pointer speed, as a number from -7 (slowest) to 7 (fastest). - // - // Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled. int32_t mousePointerSpeed; // Displays on which an acceleration curve shouldn't be applied for pointer movements from mice. - // - // Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled. std::set<ui::LogicalDisplayId> displaysWithMousePointerAccelerationDisabled; - // Velocity control parameters for mouse pointer movements. + // Velocity control parameters for touchpad pointer movements on the old touchpad stack (based + // on TouchInputMapper). + // + // For mice, these are ignored and the values of mousePointerSpeed and + // mousePointerAccelerationEnabled used instead. // - // If the enable_new_mouse_pointer_ballistics flag is enabled, these are ignored and the values - // of mousePointerSpeed and mousePointerAccelerationEnabled used instead. + // TODO(b/281840344): remove this. VelocityControlParameters pointerVelocityControlParameters; // Velocity control parameters for mouse wheel movements. @@ -243,6 +242,9 @@ struct InputReaderConfiguration { // context (a.k.a. "right") clicks. bool touchpadRightClickZoneEnabled; + // True to use three-finger tap as a customizable shortcut; false to use it as a middle-click. + bool touchpadThreeFingerTapShortcutEnabled; + // The set of currently disabled input devices. std::set<int32_t> disabledDevices; @@ -294,6 +296,7 @@ struct InputReaderConfiguration { touchpadTapDraggingEnabled(false), shouldNotifyTouchpadHardwareState(false), touchpadRightClickZoneEnabled(false), + touchpadThreeFingerTapShortcutEnabled(false), stylusButtonMotionEventsEnabled(true), stylusPointerIconEnabled(false), mouseReverseVerticalScrollingEnabled(false), @@ -497,6 +500,9 @@ public: /* Sends the Info of gestures that happen on the touchpad. */ virtual void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) = 0; + /* Notifies the policy that the user has performed a three-finger touchpad tap. */ + virtual void notifyTouchpadThreeFingerTap() = 0; + /* Gets the keyboard layout for a particular input device. */ virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier, diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 8dec9e5f40..013ef862ad 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -43,6 +43,7 @@ #include <android-base/strings.h> #include <cutils/properties.h> #include <ftl/enum.h> +#include <input/InputEventLabels.h> #include <input/KeyCharacterMap.h> #include <input/KeyLayoutMap.h> #include <input/PrintTools.h> @@ -128,7 +129,8 @@ static const std::unordered_map<std::string, InputLightClass> LIGHT_CLASSES = {"multi_intensity", InputLightClass::MULTI_INTENSITY}, {"max_brightness", InputLightClass::MAX_BRIGHTNESS}, {"kbd_backlight", InputLightClass::KEYBOARD_BACKLIGHT}, - {"mic_mute", InputLightClass::KEYBOARD_MIC_MUTE}}; + {"mic_mute", InputLightClass::KEYBOARD_MIC_MUTE}, + {"mute", InputLightClass::KEYBOARD_VOLUME_MUTE}}; // Mapping for input multicolor led class node names. // https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html @@ -1021,6 +1023,8 @@ std::optional<RawAbsoluteAxisInfo> EventHub::getAbsoluteAxisInfo(int32_t deviceI std::scoped_lock _l(mLock); const Device* device = getDeviceLocked(deviceId); if (device == nullptr) { + ALOGE("Couldn't find device with ID %d, so returning null axis info for axis %s", deviceId, + InputEventLookup::getLinuxEvdevLabel(EV_ABS, axis, 0).code.c_str()); return std::nullopt; } // We can read the RawAbsoluteAxisInfo even if the device is disabled and doesn't have a valid diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp index 49ad8b5d69..9eeb2b2a2c 100644 --- a/services/inputflinger/reader/controller/PeripheralController.cpp +++ b/services/inputflinger/reader/controller/PeripheralController.cpp @@ -514,6 +514,8 @@ void PeripheralController::configureLights() { type = InputDeviceLightType::KEYBOARD_BACKLIGHT; } else if (rawInfo.flags.test(InputLightClass::KEYBOARD_MIC_MUTE)) { type = InputDeviceLightType::KEYBOARD_MIC_MUTE; + } else if (rawInfo.flags.test(InputLightClass::KEYBOARD_VOLUME_MUTE)) { + type = InputDeviceLightType::KEYBOARD_VOLUME_MUTE; } else { type = InputDeviceLightType::INPUT; } diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 4336945e96..5839b4c41c 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -179,6 +179,8 @@ enum class InputLightClass : uint32_t { KEYBOARD_BACKLIGHT = 0x00000100, /* The input light has mic_mute name */ KEYBOARD_MIC_MUTE = 0x00000200, + /* The input light has mute name */ + KEYBOARD_VOLUME_MUTE = 0x00000400, }; enum class InputBatteryClass : uint32_t { diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 630bd9bcd8..b33659cae1 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -33,8 +33,6 @@ #include "input/PrintTools.h" -namespace input_flags = com::android::input::flags; - namespace android { // The default velocity control parameters that has no effect. @@ -77,8 +75,7 @@ void CursorMotionAccumulator::finishSync() { CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig) : InputMapper(deviceContext, readerConfig), - mLastEventTime(std::numeric_limits<nsecs_t>::min()), - mEnableNewMousePointerBallistics(input_flags::enable_new_mouse_pointer_ballistics()) {} + mLastEventTime(std::numeric_limits<nsecs_t>::min()) {} uint32_t CursorInputMapper::getSources() const { return mSource; @@ -207,8 +204,7 @@ std::list<NotifyArgs> CursorInputMapper::reset(nsecs_t when) { mDownTime = 0; mLastEventTime = std::numeric_limits<nsecs_t>::min(); - mOldPointerVelocityControl.reset(); - mNewPointerVelocityControl.reset(); + mPointerVelocityControl.reset(); mWheelXVelocityControl.reset(); mWheelYVelocityControl.reset(); @@ -291,11 +287,7 @@ std::list<NotifyArgs> CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { mWheelYVelocityControl.move(when, nullptr, &vscroll); mWheelXVelocityControl.move(when, &hscroll, nullptr); - if (mEnableNewMousePointerBallistics) { - mNewPointerVelocityControl.move(when, &deltaX, &deltaY); - } else { - mOldPointerVelocityControl.move(when, &deltaX, &deltaY); - } + mPointerVelocityControl.move(when, &deltaX, &deltaY); float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; @@ -486,27 +478,15 @@ void CursorInputMapper::configureOnPointerCapture(const InputReaderConfiguration void CursorInputMapper::configureOnChangePointerSpeed(const InputReaderConfiguration& config) { if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) { // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled. - if (mEnableNewMousePointerBallistics) { - mNewPointerVelocityControl.setAccelerationEnabled(false); - } else { - mOldPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); - } + mPointerVelocityControl.setAccelerationEnabled(false); mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); } else { - if (mEnableNewMousePointerBallistics) { - mNewPointerVelocityControl.setAccelerationEnabled( - config.displaysWithMousePointerAccelerationDisabled.count( - mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0); - mNewPointerVelocityControl.setCurve( - createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed)); - } else { - mOldPointerVelocityControl.setParameters( - (config.displaysWithMousePointerAccelerationDisabled.count( - mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0) - ? config.pointerVelocityControlParameters - : FLAT_VELOCITY_CONTROL_PARAMS); - } + mPointerVelocityControl.setAccelerationEnabled( + config.displaysWithMousePointerAccelerationDisabled.count( + mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0); + mPointerVelocityControl.setCurve( + createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed)); mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters); mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters); } diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 403e96d758..83199227b1 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -104,8 +104,7 @@ private: // Velocity controls for mouse pointer and wheel movements. // The controls for X and Y wheel movements are separate to keep them decoupled. - SimpleVelocityControl mOldPointerVelocityControl; - CurvedVelocityControl mNewPointerVelocityControl; + CurvedVelocityControl mPointerVelocityControl; SimpleVelocityControl mWheelXVelocityControl; SimpleVelocityControl mWheelYVelocityControl; @@ -120,7 +119,6 @@ private: nsecs_t mDownTime; nsecs_t mLastEventTime; - const bool mEnableNewMousePointerBallistics; bool mMouseReverseVerticalScrolling = false; explicit CursorInputMapper(InputDeviceContext& deviceContext, diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index a433a72e02..fe3e4c29e5 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -344,12 +344,14 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read } KeyboardType keyboardType = getDeviceContext().getKeyboardType(); - // Any key down on an external keyboard should wake the device. - // We don't do this for internal keyboards to prevent them from waking up in your pocket. + // Any key down on an external keyboard or internal alphanumeric keyboard should wake the + // device. We don't do this for non-alphanumeric internal keyboards to prevent them from + // waking up in your pocket. // For internal keyboards and devices for which the default wake behavior is explicitly // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each // wake key individually. - if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault && + if (down && !mParameters.doNotWakeByDefault && + (getDeviceContext().isExternal() || wakeOnAlphabeticKeyboard(keyboardType)) && !(keyboardType != KeyboardType::ALPHABETIC && isMediaKey(keyCode))) { policyFlags |= POLICY_FLAG_WAKE; } @@ -507,4 +509,8 @@ uint32_t KeyboardInputMapper::getEventSource() const { return deviceSources & ALL_KEYBOARD_SOURCES; } +bool KeyboardInputMapper::wakeOnAlphabeticKeyboard(const KeyboardType keyboardType) const { + return mEnableAlphabeticKeyboardWakeFlag && (KeyboardType::ALPHABETIC == keyboardType); +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index 10bd4242e1..7d9b3e44fa 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -16,6 +16,8 @@ #pragma once +#include <com_android_input_flags.h> + #include "HidUsageAccumulator.h" #include "InputMapper.h" @@ -85,6 +87,10 @@ private: bool doNotWakeByDefault{}; } mParameters{}; + // Store the value of enable wake for alphanumeric keyboard flag. + const bool mEnableAlphabeticKeyboardWakeFlag = + com::android::input::flags::enable_alphabetic_keyboard_wake(); + KeyboardInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig, uint32_t source); void configureParameters(); @@ -109,6 +115,8 @@ private: [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when); void onKeyDownProcessed(nsecs_t downTime); uint32_t getEventSource() const; + + bool wakeOnAlphabeticKeyboard(const KeyboardType keyboardType) const; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index 9a36bfbeaf..ca8266bb82 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -375,6 +375,9 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, mPropertyProvider.getProperty("Button Right Click Zone Enable") .setBoolValues({config.touchpadRightClickZoneEnabled}); mTouchpadHardwareStateNotificationsEnabled = config.shouldNotifyTouchpadHardwareState; + + mGestureConverter.setThreeFingerTapShortcutEnabled( + config.touchpadThreeFingerTapShortcutEnabled); } std::list<NotifyArgs> out; if ((!changes.any() && config.pointerCaptureRequest.isEnable()) || diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index da2c683d95..19594233fa 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -261,6 +261,14 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ } const uint32_t buttonsPressed = gesture.details.buttons.down; + const uint32_t buttonsReleased = gesture.details.buttons.up; + + if (mThreeFingerTapShortcutEnabled && gesture.details.buttons.is_tap && + buttonsPressed == GESTURES_BUTTON_MIDDLE && buttonsReleased == GESTURES_BUTTON_MIDDLE) { + mReaderContext.getPolicy()->notifyTouchpadThreeFingerTap(); + return out; + } + bool pointerDown = isPointerDown(mButtonState) || buttonsPressed & (GESTURES_BUTTON_LEFT | GESTURES_BUTTON_MIDDLE | GESTURES_BUTTON_RIGHT); @@ -291,7 +299,6 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ // changes: a set of buttons going down, followed by a set of buttons going up. mButtonState = newButtonState; - const uint32_t buttonsReleased = gesture.details.buttons.up; for (uint32_t button = 1; button <= GESTURES_BUTTON_FORWARD; button <<= 1) { if (buttonsReleased & button) { uint32_t actionButton = gesturesButtonToMotionEventButton(button); diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index c9a35c151f..ad40721363 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -55,6 +55,10 @@ public: void setBoundsInLogicalDisplay(FloatRect bounds) { mBoundsInLogicalDisplay = bounds; } + void setThreeFingerTapShortcutEnabled(bool enabled) { + mThreeFingerTapShortcutEnabled = enabled; + } + void populateMotionRanges(InputDeviceInfo& info) const; [[nodiscard]] std::list<NotifyArgs> handleGesture(nsecs_t when, nsecs_t readTime, @@ -101,6 +105,8 @@ private: const bool mEnableFlingStop; const bool mEnableNoFocusChange; + bool mThreeFingerTapShortcutEnabled; + std::optional<ui::LogicalDisplayId> mDisplayId; FloatRect mBoundsInLogicalDisplay{}; ui::Rotation mOrientation = ui::ROTATION_0; diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 744cf4a514..600ae526f1 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -78,6 +78,7 @@ cc_test { "PreferStylusOverTouch_test.cpp", "PropertyProvider_test.cpp", "RotaryEncoderInputMapper_test.cpp", + "SensorInputMapper_test.cpp", "SlopController_test.cpp", "SwitchInputMapper_test.cpp", "SyncQueue_test.cpp", diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp index 1762a455ce..d4e8fdfdc5 100644 --- a/services/inputflinger/tests/CursorInputMapper_test.cpp +++ b/services/inputflinger/tests/CursorInputMapper_test.cpp @@ -25,8 +25,10 @@ #include <android-base/logging.h> #include <android_companion_virtualdevice_flags.h> #include <com_android_input_flags.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <input/DisplayViewport.h> +#include <input/InputEventLabels.h> #include <linux/input-event-codes.h> #include <linux/input.h> #include <utils/Timers.h> @@ -52,6 +54,8 @@ constexpr auto BUTTON_PRESS = AMOTION_EVENT_ACTION_BUTTON_PRESS; constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE; constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE; constexpr auto INVALID_CURSOR_POSITION = AMOTION_EVENT_INVALID_CURSOR_POSITION; +constexpr auto AXIS_X = AMOTION_EVENT_AXIS_X; +constexpr auto AXIS_Y = AMOTION_EVENT_AXIS_Y; constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT; constexpr ui::LogicalDisplayId SECONDARY_DISPLAY_ID = ui::LogicalDisplayId{DISPLAY_ID.val() + 1}; constexpr int32_t DISPLAY_WIDTH = 480; @@ -94,9 +98,35 @@ DisplayViewport createSecondaryViewport() { return v; } +// In a number of these tests, we want to check that some pointer motion is reported without +// specifying an exact value, as that would require updating the tests every time the pointer +// ballistics was changed. To do this, we make some matchers that only check the sign of a +// particular axis. +MATCHER_P(WithPositiveAxis, axis, "MotionEvent with a positive axis value") { + *result_listener << "expected 1 pointer with a positive " + << InputEventLookup::getAxisLabel(axis) << " axis but got " + << arg.pointerCoords.size() << " pointers, with axis value " + << arg.pointerCoords[0].getAxisValue(axis); + return arg.pointerCoords.size() == 1 && arg.pointerCoords[0].getAxisValue(axis) > 0; +} + +MATCHER_P(WithZeroAxis, axis, "MotionEvent with a zero axis value") { + *result_listener << "expected 1 pointer with a zero " << InputEventLookup::getAxisLabel(axis) + << " axis but got " << arg.pointerCoords.size() + << " pointers, with axis value " << arg.pointerCoords[0].getAxisValue(axis); + return arg.pointerCoords.size() == 1 && arg.pointerCoords[0].getAxisValue(axis) == 0; +} + +MATCHER_P(WithNegativeAxis, axis, "MotionEvent with a negative axis value") { + *result_listener << "expected 1 pointer with a negative " + << InputEventLookup::getAxisLabel(axis) << " axis but got " + << arg.pointerCoords.size() << " pointers, with axis value " + << arg.pointerCoords[0].getAxisValue(axis); + return arg.pointerCoords.size() == 1 && arg.pointerCoords[0].getAxisValue(axis) < 0; +} + } // namespace -namespace input_flags = com::android::input::flags; namespace vd_flags = android::companion::virtualdevice::flags; /** @@ -150,24 +180,21 @@ protected: ASSERT_GT(mDevice->getGeneration(), generation); } - void testMotionRotation(int32_t originalX, int32_t originalY, int32_t rotatedX, - int32_t rotatedY) { + void testRotation(int32_t originalX, int32_t originalY, + const testing::Matcher<NotifyMotionArgs>& coordsMatcher) { std::list<NotifyArgs> args; args += process(ARBITRARY_TIME, EV_REL, REL_X, originalX); args += process(ARBITRARY_TIME, EV_REL, REL_Y, originalY); args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(ACTION_MOVE), - WithCoords(float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD, - float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD))))); + AllOf(WithMotionAction(ACTION_MOVE), coordsMatcher)))); } }; class CursorInputMapperUnitTest : public CursorInputMapperUnitTestBase { protected: void SetUp() override { - input_flags::enable_new_mouse_pointer_ballistics(false); vd_flags::high_resolution_scroll(false); CursorInputMapperUnitTestBase::SetUp(); } @@ -344,14 +371,12 @@ TEST_F(CursorInputMapperUnitTest, ProcessPointerCapture) { args += process(EV_KEY, BTN_MOUSE, 0); args += process(EV_SYN, SYN_REPORT, 0); ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(BUTTON_RELEASE), - WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), - WithCoords(0.0f, 0.0f), WithPressure(0.0f))), - VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(ACTION_UP), - WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), - WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)))); + ASSERT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), + WithCoords(0.0f, 0.0f), + WithPressure(0.0f))))); // Another move. args.clear(); @@ -377,7 +402,8 @@ TEST_F(CursorInputMapperUnitTest, ProcessPointerCapture) { ElementsAre(VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), expectedCoords, expectedCursorPosition, - WithRelativeMotion(10.0f, 20.0f))))); + WithPositiveAxis(AMOTION_EVENT_AXIS_RELATIVE_X), + WithPositiveAxis(AMOTION_EVENT_AXIS_RELATIVE_Y))))); } TEST_F(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsScaledRangeInNavigationMode) { @@ -411,64 +437,40 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldSetAllFieldsAndIncludeGlobalMetaS args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1); args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID), - WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), - WithEdgeFlags(0), WithPolicyFlags(0), - WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithPointerCount(1), WithPointerId(0, 0), - WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f), - WithPressure(1.0f), - WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, - TRACKBALL_MOVEMENT_THRESHOLD), - WithDownTime(ARBITRARY_TIME))), - VariantWith<NotifyMotionArgs>( - AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID), - WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), - WithEdgeFlags(0), WithPolicyFlags(0), - WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithPointerCount(1), WithPointerId(0, 0), - WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f), - WithPressure(1.0f), - WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, - TRACKBALL_MOVEMENT_THRESHOLD), - WithDownTime(ARBITRARY_TIME))))); + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), + VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID), + WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), WithEdgeFlags(0), + WithPolicyFlags(0), + WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), + WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPointerCount(1), + WithPointerId(0, 0), WithToolType(ToolType::MOUSE), + WithCoords(0.0f, 0.0f), WithPressure(1.0f), + WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, + TRACKBALL_MOVEMENT_THRESHOLD), + WithDownTime(ARBITRARY_TIME))))); args.clear(); // Button release. Should have same down time. args += process(ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0); args += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithEventTime(ARBITRARY_TIME + 1), - WithDeviceId(DEVICE_ID), - WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), - WithEdgeFlags(0), WithPolicyFlags(0), - WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), - WithButtonState(0), WithPointerCount(1), - WithPointerId(0, 0), WithToolType(ToolType::MOUSE), - WithCoords(0.0f, 0.0f), WithPressure(0.0f), - WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, - TRACKBALL_MOVEMENT_THRESHOLD), - WithDownTime(ARBITRARY_TIME))), - VariantWith<NotifyMotionArgs>( - AllOf(WithEventTime(ARBITRARY_TIME + 1), - WithDeviceId(DEVICE_ID), - WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), - WithEdgeFlags(0), WithPolicyFlags(0), - WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), - WithButtonState(0), WithPointerCount(1), - WithPointerId(0, 0), WithToolType(ToolType::MOUSE), - WithCoords(0.0f, 0.0f), WithPressure(0.0f), - WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, - TRACKBALL_MOVEMENT_THRESHOLD), - WithDownTime(ARBITRARY_TIME))))); + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithEventTime(ARBITRARY_TIME + 1), WithDeviceId(DEVICE_ID), + WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0), WithEdgeFlags(0), + WithPolicyFlags(0), + WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON), + WithButtonState(0), WithPointerCount(1), WithPointerId(0, 0), + WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f), + WithPressure(0.0f), + WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD, + TRACKBALL_MOVEMENT_THRESHOLD), + WithDownTime(ARBITRARY_TIME))))); } TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentXYUpdates) { @@ -482,9 +484,8 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentXYUpdates) { args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f), - WithPressure(0.0f))))); + AllOf(WithMotionAction(ACTION_MOVE), WithPressure(0.0f), + WithPositiveAxis(AXIS_X), WithZeroAxis(AXIS_Y))))); args.clear(); // Motion in Y but not X. @@ -492,9 +493,8 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentXYUpdates) { args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD), - WithPressure(0.0f))))); + AllOf(WithMotionAction(ACTION_MOVE), WithPressure(0.0f), + WithZeroAxis(AXIS_X), WithNegativeAxis(AXIS_Y))))); args.clear(); } @@ -508,24 +508,22 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentButtonUpdates) { args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1); args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithCoords(0.0f, 0.0f), WithPressure(1.0f))), - VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), + VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); args.clear(); // Button release. args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0); args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithCoords(0.0f, 0.0f), WithPressure(0.0f))), - VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); } TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleCombinedXYAndButtonUpdates) { @@ -540,16 +538,12 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleCombinedXYAndButtonUpdates) args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1); args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, - -2.0f / TRACKBALL_MOVEMENT_THRESHOLD), - WithPressure(1.0f))), - VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, - -2.0f / TRACKBALL_MOVEMENT_THRESHOLD), - WithPressure(1.0f))))); + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), + VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>(AllOf(WithPositiveAxis(AXIS_X), + WithNegativeAxis(AXIS_Y), + WithPressure(1.0f))))); args.clear(); // Move X, Y a bit while pressed. @@ -558,22 +552,19 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleCombinedXYAndButtonUpdates) args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithCoords(2.0f / TRACKBALL_MOVEMENT_THRESHOLD, - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD), - WithPressure(1.0f))))); + AllOf(WithMotionAction(ACTION_MOVE), WithPressure(1.0f), + WithPositiveAxis(AXIS_X), WithPositiveAxis(AXIS_Y))))); args.clear(); // Release Button. args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0); args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithCoords(0.0f, 0.0f), WithPressure(0.0f))), - VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); args.clear(); } @@ -586,14 +577,16 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldNotRotateMotionsWhenOrientationAw .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90))); mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1)); + constexpr auto X = AXIS_X; + constexpr auto Y = AXIS_Y; + ASSERT_NO_FATAL_FAILURE(testRotation( 0, 1, AllOf(WithZeroAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, 1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, 0, AllOf(WithPositiveAxis(X), WithZeroAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithZeroAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, 0, AllOf(WithNegativeAxis(X), WithZeroAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, 1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y)))); } TEST_F(CursorInputMapperUnitTest, ProcessShouldRotateMotionsWhenNotOrientationAware) { @@ -604,54 +597,56 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldRotateMotionsWhenNotOrientationAw .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation0))); mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1)); + constexpr auto X = AXIS_X; + constexpr auto Y = AXIS_Y; + ASSERT_NO_FATAL_FAILURE(testRotation( 0, 1, AllOf(WithZeroAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, 1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, 0, AllOf(WithPositiveAxis(X), WithZeroAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithZeroAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, 0, AllOf(WithNegativeAxis(X), WithZeroAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, 1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y)))); EXPECT_CALL((*mDevice), getAssociatedViewport) .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation90))); std::list<NotifyArgs> args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, InputReaderConfiguration::Change::DISPLAY_INFO); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, -1)); + ASSERT_NO_FATAL_FAILURE(testRotation( 0, 1, AllOf(WithNegativeAxis(X), WithZeroAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, 1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, 0, AllOf(WithZeroAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithPositiveAxis(X), WithZeroAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, 0, AllOf(WithZeroAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, 1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y)))); EXPECT_CALL((*mDevice), getAssociatedViewport) .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation180))); args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, InputReaderConfiguration::Change::DISPLAY_INFO); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, -1)); + ASSERT_NO_FATAL_FAILURE(testRotation( 0, 1, AllOf(WithZeroAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, 1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, 0, AllOf(WithNegativeAxis(X), WithZeroAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithZeroAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, 0, AllOf(WithPositiveAxis(X), WithZeroAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, 1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y)))); EXPECT_CALL((*mDevice), getAssociatedViewport) .WillRepeatedly(Return(createPrimaryViewport(ui::Rotation::Rotation270))); args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, InputReaderConfiguration::Change::DISPLAY_INFO); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, 1)); + ASSERT_NO_FATAL_FAILURE(testRotation( 0, 1, AllOf(WithPositiveAxis(X), WithZeroAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, 1, AllOf(WithPositiveAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, 0, AllOf(WithZeroAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 1, -1, AllOf(WithNegativeAxis(X), WithNegativeAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation( 0, -1, AllOf(WithNegativeAxis(X), WithZeroAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, -1, AllOf(WithNegativeAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, 0, AllOf(WithZeroAxis(X), WithPositiveAxis(Y)))); + ASSERT_NO_FATAL_FAILURE(testRotation(-1, 1, AllOf(WithPositiveAxis(X), WithPositiveAxis(Y)))); } TEST_F(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsRangeFromPolicy) { @@ -742,30 +737,22 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleAllButtonsWithZeroCoords) { args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1); args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(0.0f, 0.0f), WithPressure(1.0f))), - VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), - WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), + VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithCoords(0.0f, 0.0f), + WithPressure(1.0f))))); args.clear(); args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0); args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithButtonState(0), WithCoords(0.0f, 0.0f), - WithPressure(0.0f))), - VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithButtonState(0), WithCoords(0.0f, 0.0f), - WithPressure(0.0f))), - VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithButtonState(0), WithCoords(0.0f, 0.0f), - WithPressure(0.0f))))); + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(0), WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); args.clear(); // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE @@ -774,49 +761,41 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleAllButtonsWithZeroCoords) { args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + AllOf(WithMotionAction(ACTION_DOWN), WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY | - AMOTION_EVENT_BUTTON_TERTIARY), - WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + AMOTION_EVENT_BUTTON_TERTIARY))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), - WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), - WithCoords(0.0f, 0.0f), WithPressure(1.0f))), + AllOf(WithMotionAction(BUTTON_PRESS), + WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + AllOf(WithMotionAction(BUTTON_PRESS), WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY | - AMOTION_EVENT_BUTTON_TERTIARY), - WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + AMOTION_EVENT_BUTTON_TERTIARY))))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); args.clear(); args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0); args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), - WithCoords(0.0f, 0.0f), WithPressure(1.0f))), - VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), - WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), - WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_MOVE)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY), + WithCoords(0.0f, 0.0f), WithPressure(1.0f))))); args.clear(); args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0); args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithButtonState(0), WithCoords(0.0f, 0.0f), - WithPressure(0.0f))), - VariantWith<NotifyMotionArgs>( - AllOf(WithButtonState(0), - WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(0.0f, 0.0f), WithPressure(0.0f))), - VariantWith<NotifyMotionArgs>( - AllOf(WithButtonState(0), - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + EXPECT_THAT(args, + Each(VariantWith<NotifyMotionArgs>( + AllOf(WithButtonState(0), WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); } class CursorInputMapperButtonKeyTest @@ -838,11 +817,11 @@ TEST_P(CursorInputMapperButtonKeyTest, ProcessShouldHandleButtonKeyWithZeroCoord ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithKeyCode(expectedKeyCode))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(HOVER_MOVE), WithButtonState(expectedButtonState), WithCoords(0.0f, 0.0f), WithPressure(0.0f))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS), + AllOf(WithMotionAction(BUTTON_PRESS), WithButtonState(expectedButtonState), WithCoords(0.0f, 0.0f), WithPressure(0.0f))))); args.clear(); @@ -851,13 +830,11 @@ TEST_P(CursorInputMapperButtonKeyTest, ProcessShouldHandleButtonKeyWithZeroCoord args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE), - WithButtonState(0), WithCoords(0.0f, 0.0f), - WithPressure(0.0f))), + AllOf(WithMotionAction(BUTTON_RELEASE), WithButtonState(0), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithButtonState(0), WithCoords(0.0f, 0.0f), - WithPressure(0.0f))), + AllOf(WithMotionAction(HOVER_MOVE), WithButtonState(0), + WithCoords(0.0f, 0.0f), WithPressure(0.0f))), VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), WithKeyCode(expectedKeyCode))))); } @@ -881,8 +858,7 @@ TEST_F(CursorInputMapperUnitTest, ProcessWhenModeIsPointerShouldKeepZeroCoords) args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithSource(AINPUT_SOURCE_MOUSE), WithMotionAction(HOVER_MOVE), WithCoords(0.0f, 0.0f), WithPressure(0.0f), WithSize(0.0f), WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f), WithOrientation(0.0f), WithDistance(0.0f))))); @@ -897,13 +873,11 @@ TEST_F(CursorInputMapperUnitTest, ProcessRegularScroll) { args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(1.0f, 1.0f))))); + EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE)))); } TEST_F(CursorInputMapperUnitTest, ProcessHighResScroll) { @@ -920,13 +894,11 @@ TEST_F(CursorInputMapperUnitTest, ProcessHighResScroll) { args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f, 0.5f))))); + EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE)))); } TEST_F(CursorInputMapperUnitTest, HighResScrollIgnoresRegularScroll) { @@ -945,13 +917,11 @@ TEST_F(CursorInputMapperUnitTest, HighResScrollIgnoresRegularScroll) { args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f, 0.5f))))); + EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE)))); } TEST_F(CursorInputMapperUnitTest, ProcessReversedVerticalScroll) { @@ -966,13 +936,11 @@ TEST_F(CursorInputMapperUnitTest, ProcessReversedVerticalScroll) { // Reversed vertical scrolling only affects the y-axis, expect it to be -1.0f to indicate the // inverted scroll direction. EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(1.0f, -1.0f))))); + EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE)))); } TEST_F(CursorInputMapperUnitTest, ProcessHighResReversedVerticalScroll) { @@ -990,13 +958,11 @@ TEST_F(CursorInputMapperUnitTest, ProcessHighResReversedVerticalScroll) { args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f, -0.5f))))); + EXPECT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithSource(AINPUT_SOURCE_MOUSE)))); } /** @@ -1005,10 +971,6 @@ TEST_F(CursorInputMapperUnitTest, ProcessHighResReversedVerticalScroll) { */ TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesVelocityProcessing) { mPropertyMap.addProperty("cursor.mode", "pointer"); - const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f, - /*highThreshold=*/100.f, /*acceleration=*/10.f); - mReaderConfiguration.pointerVelocityControlParameters = testParams; - mFakePolicy->setVelocityControlParams(testParams); createMapper(); NotifyMotionArgs motionArgs; @@ -1020,8 +982,7 @@ TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesVelocityProcessing) { args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))))); + AllOf(WithSource(AINPUT_SOURCE_MOUSE), WithMotionAction(HOVER_MOVE))))); motionArgs = std::get<NotifyMotionArgs>(args.front()); const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); @@ -1039,12 +1000,7 @@ TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesVelocityProcessing) { EXPECT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), - WithMotionAction(AMOTION_EVENT_ACTION_MOVE))))); - motionArgs = std::get<NotifyMotionArgs>(args.front()); - const float relX2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const float relY2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - ASSERT_EQ(10, relX2); - ASSERT_EQ(20, relY2); + WithMotionAction(ACTION_MOVE), WithRelativeMotion(10, 20))))); } TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdNoAssociatedViewport) { @@ -1067,54 +1023,12 @@ TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdNoAssociatedViewport) { args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithSource(AINPUT_SOURCE_MOUSE), + AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ui::LogicalDisplayId::INVALID), WithCoords(0.0f, 0.0f))))); } -// TODO(b/320433834): De-duplicate the test cases once the flag is removed. -class CursorInputMapperUnitTestWithNewBallistics : public CursorInputMapperUnitTestBase { -protected: - void SetUp() override { - input_flags::enable_new_mouse_pointer_ballistics(true); - CursorInputMapperUnitTestBase::SetUp(); - } -}; - -TEST_F(CursorInputMapperUnitTestWithNewBallistics, PointerCaptureDisablesVelocityProcessing) { - mPropertyMap.addProperty("cursor.mode", "pointer"); - createMapper(); - - NotifyMotionArgs motionArgs; - std::list<NotifyArgs> args; - - // Move and verify scale is applied. - args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); - args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); - args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); - motionArgs = std::get<NotifyMotionArgs>(args.front()); - const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - ASSERT_GT(relX, 10); - ASSERT_GT(relY, 20); - args.clear(); - - // Enable Pointer Capture - setPointerCapture(true); - - // Move and verify scale is not applied. - args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); - args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); - args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); - motionArgs = std::get<NotifyMotionArgs>(args.front()); - const float relX2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); - const float relY2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); - ASSERT_EQ(10, relX2); - ASSERT_EQ(20, relY2); -} - -TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationWithAssociatedViewport) { +TEST_F(CursorInputMapperUnitTest, ConfigureAccelerationWithAssociatedViewport) { mPropertyMap.addProperty("cursor.mode", "pointer"); DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0); mReaderConfiguration.setDisplayViewports({primaryViewport}); @@ -1149,7 +1063,7 @@ TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationWithAsso WithRelativeMotion(10, 20))))); } -TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationOnDisplayChange) { +TEST_F(CursorInputMapperUnitTest, ConfigureAccelerationOnDisplayChange) { mPropertyMap.addProperty("cursor.mode", "pointer"); DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0); mReaderConfiguration.setDisplayViewports({primaryViewport}); @@ -1186,72 +1100,6 @@ TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationOnDispla WithRelativeMotion(10, 20))))); } -TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessRegularScroll) { - createMapper(); - - std::list<NotifyArgs> args; - args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1); - args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1); - args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); - - EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), - VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), - WithScroll(1.0f, 1.0f))))); -} - -TEST_F(CursorInputMapperUnitTestWithNewBallistics, ProcessHighResScroll) { - vd_flags::high_resolution_scroll(true); - EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES)) - .WillRepeatedly(Return(true)); - createMapper(); - - std::list<NotifyArgs> args; - args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60); - args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60); - args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); - - EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), - VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), - WithScroll(0.5f, 0.5f))))); -} - -TEST_F(CursorInputMapperUnitTestWithNewBallistics, HighResScrollIgnoresRegularScroll) { - vd_flags::high_resolution_scroll(true); - EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_WHEEL_HI_RES)) - .WillRepeatedly(Return(true)); - EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES)) - .WillRepeatedly(Return(true)); - createMapper(); - - std::list<NotifyArgs> args; - args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL_HI_RES, 60); - args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL_HI_RES, 60); - args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 1); - args += process(ARBITRARY_TIME, EV_REL, REL_HWHEEL, 1); - args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); - - EXPECT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))), - VariantWith<NotifyMotionArgs>( - AllOf(WithSource(AINPUT_SOURCE_MOUSE), - WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), - WithScroll(0.5f, 0.5f))))); -} - namespace { // Minimum timestamp separation between subsequent input events from a Bluetooth device. @@ -1279,8 +1127,7 @@ TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmoothening) { argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime))))); + AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime))))); argsList.clear(); // Process several events that come in quick succession, according to their timestamps. @@ -1294,7 +1141,7 @@ TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmoothening) { argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime))))); argsList.clear(); } @@ -1310,8 +1157,7 @@ TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningIsCapped) { argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime))))); + AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime))))); argsList.clear(); // Process several events with the same timestamp from the kernel. @@ -1325,7 +1171,7 @@ TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningIsCapped) { argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime))))); argsList.clear(); } @@ -1338,8 +1184,7 @@ TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningIsCapped) { argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(cappedEventTime))))); + AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(cappedEventTime))))); argsList.clear(); } } @@ -1355,8 +1200,7 @@ TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningNotUsed) { argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime))))); + AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime))))); argsList.clear(); // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp @@ -1368,8 +1212,7 @@ TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningNotUsed) { argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0); EXPECT_THAT(argsList, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithEventTime(expectedEventTime))))); + AllOf(WithMotionAction(HOVER_MOVE), WithEventTime(expectedEventTime))))); argsList.clear(); } diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index f373cac085..67b1e8c249 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -80,6 +80,17 @@ void FakeInputReaderPolicy::assertTouchpadHardwareStateNotified() { ASSERT_TRUE(success) << "Timed out waiting for hardware state to be notified"; } +void FakeInputReaderPolicy::assertTouchpadThreeFingerTapNotified() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + const bool success = + mTouchpadThreeFingerTapNotified.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { + return mTouchpadThreeFingerTapHasBeenReported; + }); + ASSERT_TRUE(success) << "Timed out waiting for three-finger tap to be notified"; +} + void FakeInputReaderPolicy::clearViewports() { mViewports.clear(); mConfig.setDisplayViewports(mViewports); @@ -217,7 +228,6 @@ float FakeInputReaderPolicy::getPointerGestureZoomSpeedRatio() { } void FakeInputReaderPolicy::setVelocityControlParams(const VelocityControlParameters& params) { - mConfig.pointerVelocityControlParameters = params; mConfig.wheelVelocityControlParameters = params; } @@ -260,6 +270,12 @@ void FakeInputReaderPolicy::notifyTouchpadGestureInfo(GestureType type, int32_t std::scoped_lock lock(mLock); } +void FakeInputReaderPolicy::notifyTouchpadThreeFingerTap() { + std::scoped_lock lock(mLock); + mTouchpadThreeFingerTapHasBeenReported = true; + mTouchpadThreeFingerTapNotified.notify_all(); +} + std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay( const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) { return nullptr; diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index 3a2b4e9ed9..42c956789b 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -43,6 +43,7 @@ public: void assertStylusGestureNotified(int32_t deviceId); void assertStylusGestureNotNotified(); void assertTouchpadHardwareStateNotified(); + void assertTouchpadThreeFingerTapNotified(); virtual void clearViewports(); std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueId) const; @@ -86,6 +87,7 @@ private: void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, int32_t deviceId) override; void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override; + void notifyTouchpadThreeFingerTap() override; std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override; std::string getDeviceAlias(const InputDeviceIdentifier&) override; @@ -109,6 +111,9 @@ private: std::condition_variable mTouchpadHardwareStateNotified; std::optional<SelfContainedHardwareState> mTouchpadHardwareState GUARDED_BY(mLock){}; + std::condition_variable mTouchpadThreeFingerTapNotified; + bool mTouchpadThreeFingerTapHasBeenReported{false}; + uint32_t mNextPointerCaptureSequenceNumber{0}; }; diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index 225ae0f67c..fad8f058ed 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -1272,6 +1272,27 @@ TEST_F(GestureConverterTest, Tap) { WithDisplayId(ui::LogicalDisplayId::DEFAULT))))); } +TEST_F(GestureConverterTest, ThreeFingerTap_TriggersShortcut) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ui::LogicalDisplayId::DEFAULT); + converter.setThreeFingerTapShortcutEnabled(true); + + Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*vx=*/0, + /*vy=*/0, GESTURES_FLING_TAP_DOWN); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); + // We don't need to check args here, since it's covered by the FlingTapDown test. + + Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*down=*/GESTURES_BUTTON_MIDDLE, /*up=*/GESTURES_BUTTON_MIDDLE, + /*is_tap=*/true); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture); + + ASSERT_TRUE(args.empty()); + mFakePolicy->assertTouchpadThreeFingerTapNotified(); +} + TEST_F(GestureConverterTest, Click) { // Click should produce button press/release events InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index 7dff144f87..ca797dce95 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -53,13 +53,13 @@ void InputMapperUnitTest::SetUpWithBus(int bus) { } void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max, - int32_t resolution) { + int32_t resolution, int32_t flat, int32_t fuzz) { EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis)) .WillRepeatedly(Return(valid ? std::optional<RawAbsoluteAxisInfo>{{ .minValue = min, .maxValue = max, - .flat = 0, - .fuzz = 0, + .flat = flat, + .fuzz = fuzz, .resolution = resolution, }} : std::nullopt)); diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h index fc27e4fefd..b6c5812ec5 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -43,7 +43,8 @@ protected: virtual void SetUp() override { SetUpWithBus(0); } virtual void SetUpWithBus(int bus); - void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution); + void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution, + int32_t flat = 0, int32_t fuzz = 0); void expectScanCodes(bool present, std::set<int> scanCodes); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 19b738ea9d..ee3b2a24d7 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -28,7 +28,6 @@ #include <MultiTouchInputMapper.h> #include <NotifyArgsBuilders.h> #include <PeripheralController.h> -#include <SensorInputMapper.h> #include <SingleTouchInputMapper.h> #include <TestEventMatchers.h> #include <TestInputListener.h> @@ -36,6 +35,7 @@ #include <UinputDevice.h> #include <android-base/thread_annotations.h> #include <com_android_input_flags.h> +#include <flag_macros.h> #include <ftl/enum.h> #include <gtest/gtest.h> #include <ui/Rotation.h> @@ -3031,159 +3031,6 @@ TEST_F(InputDeviceTest, KernelBufferOverflowResetsMappers) { mapper.assertProcessWasCalled(); } -// --- SensorInputMapperTest --- - -class SensorInputMapperTest : public InputMapperTest { -protected: - static const int32_t ACCEL_RAW_MIN; - static const int32_t ACCEL_RAW_MAX; - static const int32_t ACCEL_RAW_FUZZ; - static const int32_t ACCEL_RAW_FLAT; - static const int32_t ACCEL_RAW_RESOLUTION; - - static const int32_t GYRO_RAW_MIN; - static const int32_t GYRO_RAW_MAX; - static const int32_t GYRO_RAW_FUZZ; - static const int32_t GYRO_RAW_FLAT; - static const int32_t GYRO_RAW_RESOLUTION; - - static const float GRAVITY_MS2_UNIT; - static const float DEGREE_RADIAN_UNIT; - - void prepareAccelAxes(); - void prepareGyroAxes(); - void setAccelProperties(); - void setGyroProperties(); - void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::SENSOR); } -}; - -const int32_t SensorInputMapperTest::ACCEL_RAW_MIN = -32768; -const int32_t SensorInputMapperTest::ACCEL_RAW_MAX = 32768; -const int32_t SensorInputMapperTest::ACCEL_RAW_FUZZ = 16; -const int32_t SensorInputMapperTest::ACCEL_RAW_FLAT = 0; -const int32_t SensorInputMapperTest::ACCEL_RAW_RESOLUTION = 8192; - -const int32_t SensorInputMapperTest::GYRO_RAW_MIN = -2097152; -const int32_t SensorInputMapperTest::GYRO_RAW_MAX = 2097152; -const int32_t SensorInputMapperTest::GYRO_RAW_FUZZ = 16; -const int32_t SensorInputMapperTest::GYRO_RAW_FLAT = 0; -const int32_t SensorInputMapperTest::GYRO_RAW_RESOLUTION = 1024; - -const float SensorInputMapperTest::GRAVITY_MS2_UNIT = 9.80665f; -const float SensorInputMapperTest::DEGREE_RADIAN_UNIT = 0.0174533f; - -void SensorInputMapperTest::prepareAccelAxes() { - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ, - ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION); - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ, - ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION); - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Z, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_FUZZ, - ACCEL_RAW_FLAT, ACCEL_RAW_RESOLUTION); -} - -void SensorInputMapperTest::prepareGyroAxes() { - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RX, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ, - GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION); - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RY, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ, - GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION); - mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_RZ, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_FUZZ, - GYRO_RAW_FLAT, GYRO_RAW_RESOLUTION); -} - -void SensorInputMapperTest::setAccelProperties() { - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 0, InputDeviceSensorType::ACCELEROMETER, - /* sensorDataIndex */ 0); - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 1, InputDeviceSensorType::ACCELEROMETER, - /* sensorDataIndex */ 1); - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 2, InputDeviceSensorType::ACCELEROMETER, - /* sensorDataIndex */ 2); - mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP); - addConfigurationProperty("sensor.accelerometer.reportingMode", "0"); - addConfigurationProperty("sensor.accelerometer.maxDelay", "100000"); - addConfigurationProperty("sensor.accelerometer.minDelay", "5000"); - addConfigurationProperty("sensor.accelerometer.power", "1.5"); -} - -void SensorInputMapperTest::setGyroProperties() { - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 3, InputDeviceSensorType::GYROSCOPE, - /* sensorDataIndex */ 0); - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 4, InputDeviceSensorType::GYROSCOPE, - /* sensorDataIndex */ 1); - mFakeEventHub->addSensorAxis(EVENTHUB_ID, /* absCode */ 5, InputDeviceSensorType::GYROSCOPE, - /* sensorDataIndex */ 2); - mFakeEventHub->setMscEvent(EVENTHUB_ID, MSC_TIMESTAMP); - addConfigurationProperty("sensor.gyroscope.reportingMode", "0"); - addConfigurationProperty("sensor.gyroscope.maxDelay", "100000"); - addConfigurationProperty("sensor.gyroscope.minDelay", "5000"); - addConfigurationProperty("sensor.gyroscope.power", "0.8"); -} - -TEST_F(SensorInputMapperTest, GetSources) { - SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>(); - - ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mapper.getSources()); -} - -TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) { - setAccelProperties(); - prepareAccelAxes(); - SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>(); - - ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::ACCELEROMETER, - std::chrono::microseconds(10000), - std::chrono::microseconds(0))); - ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_X, 20000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Y, -20000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_Z, 40000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - NotifySensorArgs args; - std::vector<float> values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT, - -20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT, - 40000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT}; - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args)); - ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR); - ASSERT_EQ(args.deviceId, DEVICE_ID); - ASSERT_EQ(args.sensorType, InputDeviceSensorType::ACCELEROMETER); - ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH); - ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME); - ASSERT_EQ(args.values, values); - mapper.flushSensor(InputDeviceSensorType::ACCELEROMETER); -} - -TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) { - setGyroProperties(); - prepareGyroAxes(); - SensorInputMapper& mapper = constructAndAddMapper<SensorInputMapper>(); - - ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::GYROSCOPE, - std::chrono::microseconds(10000), - std::chrono::microseconds(0))); - ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RX, 20000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RY, -20000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_ABS, ABS_RZ, 40000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_TIMESTAMP, 1000); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - - NotifySensorArgs args; - std::vector<float> values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT, - -20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT, - 40000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT}; - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifySensorWasCalled(&args)); - ASSERT_EQ(args.source, AINPUT_SOURCE_SENSOR); - ASSERT_EQ(args.deviceId, DEVICE_ID); - ASSERT_EQ(args.sensorType, InputDeviceSensorType::GYROSCOPE); - ASSERT_EQ(args.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH); - ASSERT_EQ(args.hwTimestamp, ARBITRARY_TIME); - ASSERT_EQ(args.values, values); - mapper.flushSensor(InputDeviceSensorType::GYROSCOPE); -} - // --- KeyboardInputMapperTest --- class KeyboardInputMapperTest : public InputMapperTest { @@ -4019,6 +3866,44 @@ TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) { ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags); } +TEST_F_WITH_FLAGS(KeyboardInputMapperTest, WakeBehavior_AlphabeticKeyboard, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + enable_alphabetic_keyboard_wake))) { + // For internal alphabetic devices, keys will trigger wake on key down. + + mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, 0); + + KeyboardInputMapper& mapper = + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1); + NotifyKeyArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + + process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(uint32_t(0), args.policyFlags); +} + /** * When there is more than one KeyboardInputMapper for an InputDevice, each mapper should produce * events that use the shared keyboard source across all mappers. This is to ensure that each diff --git a/services/inputflinger/tests/SensorInputMapper_test.cpp b/services/inputflinger/tests/SensorInputMapper_test.cpp new file mode 100644 index 0000000000..2c1265378a --- /dev/null +++ b/services/inputflinger/tests/SensorInputMapper_test.cpp @@ -0,0 +1,179 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "SensorInputMapper.h" + +#include <cstdint> +#include <list> +#include <optional> +#include <utility> +#include <vector> + +#include <EventHub.h> +#include <NotifyArgs.h> +#include <ftl/enum.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <input/Input.h> +#include <input/InputDevice.h> +#include <input/PrintTools.h> +#include <linux/input-event-codes.h> + +#include "InputMapperTest.h" +#include "TestEventMatchers.h" + +namespace android { + +using testing::AllOf; +using testing::ElementsAre; +using testing::Return; +using testing::VariantWith; + +namespace { + +constexpr int32_t ACCEL_RAW_MIN = -32768; +constexpr int32_t ACCEL_RAW_MAX = 32768; +constexpr int32_t ACCEL_RAW_FUZZ = 16; +constexpr int32_t ACCEL_RAW_FLAT = 0; +constexpr int32_t ACCEL_RAW_RESOLUTION = 8192; + +constexpr int32_t GYRO_RAW_MIN = -2097152; +constexpr int32_t GYRO_RAW_MAX = 2097152; +constexpr int32_t GYRO_RAW_FUZZ = 16; +constexpr int32_t GYRO_RAW_FLAT = 0; +constexpr int32_t GYRO_RAW_RESOLUTION = 1024; + +constexpr float GRAVITY_MS2_UNIT = 9.80665f; +constexpr float DEGREE_RADIAN_UNIT = 0.0174533f; + +} // namespace + +class SensorInputMapperTest : public InputMapperUnitTest { +protected: + void SetUp() override { + InputMapperUnitTest::SetUp(); + EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID)) + .WillRepeatedly(Return(InputDeviceClass::SENSOR)); + // The mapper requests info on all ABS axes, including ones which aren't actually used, so + // just return nullopt for all axes we don't explicitly set up. + EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, testing::_)) + .WillRepeatedly(Return(std::nullopt)); + } + + void setupSensor(int32_t absCode, InputDeviceSensorType type, int32_t sensorDataIndex) { + EXPECT_CALL(mMockEventHub, mapSensor(EVENTHUB_ID, absCode)) + .WillRepeatedly(Return(std::make_pair(type, sensorDataIndex))); + } +}; + +TEST_F(SensorInputMapperTest, GetSources) { + mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext, + mFakePolicy->getReaderConfiguration()); + + ASSERT_EQ(static_cast<uint32_t>(AINPUT_SOURCE_SENSOR), mMapper->getSources()); +} + +TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) { + EXPECT_CALL(mMockEventHub, hasMscEvent(EVENTHUB_ID, MSC_TIMESTAMP)) + .WillRepeatedly(Return(true)); + setupSensor(ABS_X, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/0); + setupSensor(ABS_Y, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/1); + setupSensor(ABS_Z, InputDeviceSensorType::ACCELEROMETER, /*sensorDataIndex=*/2); + setupAxis(ABS_X, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION, + ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ); + setupAxis(ABS_Y, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION, + ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ); + setupAxis(ABS_Z, /*valid=*/true, ACCEL_RAW_MIN, ACCEL_RAW_MAX, ACCEL_RAW_RESOLUTION, + ACCEL_RAW_FLAT, ACCEL_RAW_FUZZ); + mPropertyMap.addProperty("sensor.accelerometer.reportingMode", "0"); + mPropertyMap.addProperty("sensor.accelerometer.maxDelay", "100000"); + mPropertyMap.addProperty("sensor.accelerometer.minDelay", "5000"); + mPropertyMap.addProperty("sensor.accelerometer.power", "1.5"); + mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext, + mFakePolicy->getReaderConfiguration()); + + EXPECT_CALL(mMockEventHub, enableDevice(EVENTHUB_ID)); + ASSERT_TRUE(mMapper->enableSensor(InputDeviceSensorType::ACCELEROMETER, + std::chrono::microseconds(10000), + std::chrono::microseconds(0))); + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_ABS, ABS_X, 20000); + args += process(ARBITRARY_TIME, EV_ABS, ABS_Y, -20000); + args += process(ARBITRARY_TIME, EV_ABS, ABS_Z, 40000); + args += process(ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + std::vector<float> values = {20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT, + -20000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT, + 40000.0f / ACCEL_RAW_RESOLUTION * GRAVITY_MS2_UNIT}; + + ASSERT_EQ(args.size(), 1u); + const NotifySensorArgs& arg = std::get<NotifySensorArgs>(args.front()); + ASSERT_EQ(arg.source, AINPUT_SOURCE_SENSOR); + ASSERT_EQ(arg.deviceId, DEVICE_ID); + ASSERT_EQ(arg.sensorType, InputDeviceSensorType::ACCELEROMETER); + ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH); + ASSERT_EQ(arg.hwTimestamp, ARBITRARY_TIME); + ASSERT_EQ(arg.values, values); + mMapper->flushSensor(InputDeviceSensorType::ACCELEROMETER); +} + +TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) { + EXPECT_CALL(mMockEventHub, hasMscEvent(EVENTHUB_ID, MSC_TIMESTAMP)) + .WillRepeatedly(Return(true)); + setupSensor(ABS_RX, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/0); + setupSensor(ABS_RY, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/1); + setupSensor(ABS_RZ, InputDeviceSensorType::GYROSCOPE, /*sensorDataIndex=*/2); + setupAxis(ABS_RX, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION, + GYRO_RAW_FLAT, GYRO_RAW_FUZZ); + setupAxis(ABS_RY, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION, + GYRO_RAW_FLAT, GYRO_RAW_FUZZ); + setupAxis(ABS_RZ, /*valid=*/true, GYRO_RAW_MIN, GYRO_RAW_MAX, GYRO_RAW_RESOLUTION, + GYRO_RAW_FLAT, GYRO_RAW_FUZZ); + mPropertyMap.addProperty("sensor.gyroscope.reportingMode", "0"); + mPropertyMap.addProperty("sensor.gyroscope.maxDelay", "100000"); + mPropertyMap.addProperty("sensor.gyroscope.minDelay", "5000"); + mPropertyMap.addProperty("sensor.gyroscope.power", "0.8"); + mMapper = createInputMapper<SensorInputMapper>(*mDeviceContext, + mFakePolicy->getReaderConfiguration()); + + EXPECT_CALL(mMockEventHub, enableDevice(EVENTHUB_ID)); + ASSERT_TRUE(mMapper->enableSensor(InputDeviceSensorType::GYROSCOPE, + std::chrono::microseconds(10000), + std::chrono::microseconds(0))); + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_ABS, ABS_RX, 20000); + args += process(ARBITRARY_TIME, EV_ABS, ABS_RY, -20000); + args += process(ARBITRARY_TIME, EV_ABS, ABS_RZ, 40000); + args += process(ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, 1000); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + + std::vector<float> values = {20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT, + -20000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT, + 40000.0f / GYRO_RAW_RESOLUTION * DEGREE_RADIAN_UNIT}; + + ASSERT_EQ(args.size(), 1u); + const NotifySensorArgs& arg = std::get<NotifySensorArgs>(args.front()); + ASSERT_EQ(arg.source, AINPUT_SOURCE_SENSOR); + ASSERT_EQ(arg.deviceId, DEVICE_ID); + ASSERT_EQ(arg.sensorType, InputDeviceSensorType::GYROSCOPE); + ASSERT_EQ(arg.accuracy, InputDeviceSensorAccuracy::ACCURACY_HIGH); + ASSERT_EQ(arg.hwTimestamp, ARBITRARY_TIME); + ASSERT_EQ(arg.values, values); + mMapper->flushSensor(InputDeviceSensorType::GYROSCOPE); +} + +} // namespace android
\ No newline at end of file diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h index f58d8fd47a..7078e49343 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -108,20 +108,33 @@ public: using is_gtest_matcher = void; explicit WithMotionActionMatcher(int32_t action) : mAction(action) {} - bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const { - bool matches = mAction == args.action; - if (args.action == AMOTION_EVENT_ACTION_CANCEL) { - matches &= (args.flags & AMOTION_EVENT_FLAG_CANCELED) != 0; + bool MatchAndExplain(const NotifyMotionArgs& args, + testing::MatchResultListener* listener) const { + if (mAction != args.action) { + *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got " + << MotionEvent::actionToString(args.action); + return false; } - return matches; + if (args.action == AMOTION_EVENT_ACTION_CANCEL && + (args.flags & AMOTION_EVENT_FLAG_CANCELED) == 0) { + *listener << "event with CANCEL action is missing FLAG_CANCELED"; + return false; + } + return true; } - bool MatchAndExplain(const MotionEvent& event, std::ostream*) const { - bool matches = mAction == event.getAction(); - if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) { - matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0; + bool MatchAndExplain(const MotionEvent& event, testing::MatchResultListener* listener) const { + if (mAction != event.getAction()) { + *listener << "expected " << MotionEvent::actionToString(mAction) << ", but got " + << MotionEvent::actionToString(event.getAction()); + return false; } - return matches; + if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL && + (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) == 0) { + *listener << "event with CANCEL action is missing FLAG_CANCELED"; + return false; + } + return true; } void DescribeTo(std::ostream* os) const { diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index 60c676da58..a1da39ae14 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -288,6 +288,7 @@ public: void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs, int32_t deviceId) override {} void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override {} + void notifyTouchpadThreeFingerTap() override {} std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay( const InputDeviceIdentifier& identifier, const std::optional<KeyboardLayoutInfo> layoutInfo) override { diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp index 1589c9937d..f5c4fc547c 100644 --- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp +++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp @@ -35,6 +35,7 @@ using aidl::android::hardware::power::IPowerHintSession; using aidl::android::hardware::power::Mode; using aidl::android::hardware::power::SessionConfig; using aidl::android::hardware::power::SessionTag; +using aidl::android::hardware::power::SupportInfo; using android::binder::Status; using namespace android; @@ -65,6 +66,7 @@ public: (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override)); MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override)); MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getSupportInfo, (SupportInfo * _aidl_return), (override)); MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override)); MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override)); MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index d500ae8f9e..8a667aef72 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -148,6 +148,46 @@ cc_defaults { }, } +// libsurfaceflinger_backend_{headers|sources} are a step towards pulling out +// the "backend" sources to clean up the dependency graph between +// CompositionEngine and SurfaceFlinger. Completing the cleanup would require +// moving the headers in particular so that the dependency can strictly be a +// DAG. There would certainly be additional cleanups: VirtualDisplaySurface.cpp +// and FrameBufferSurface.cpp likely belong in CompositionEngine for example. +cc_library_headers { + name: "libsurfaceflinger_backend_headers", + export_include_dirs: ["."], + static_libs: ["libserviceutils"], + export_static_lib_headers: ["libserviceutils"], + + shared_libs: [ + "android.hardware.configstore-utils", + "android.hardware.configstore@1.0", + "android.hardware.configstore@1.1", + "libbinder_ndk", + ], + export_shared_lib_headers: [ + "android.hardware.configstore-utils", + "android.hardware.configstore@1.0", + "android.hardware.configstore@1.1", + "libbinder_ndk", + ], +} + +filegroup { + name: "libsurfaceflinger_backend_sources", + srcs: [ + "PowerAdvisor/*.cpp", + "DisplayHardware/AidlComposerHal.cpp", + "DisplayHardware/ComposerHal.cpp", + "DisplayHardware/FramebufferSurface.cpp", + "DisplayHardware/HWC2.cpp", + "DisplayHardware/HWComposer.cpp", + "DisplayHardware/HidlComposerHal.cpp", + "DisplayHardware/VirtualDisplaySurface.cpp", + ], +} + cc_library_headers { name: "libsurfaceflinger_headers", export_include_dirs: ["."], @@ -158,20 +198,13 @@ cc_library_headers { filegroup { name: "libsurfaceflinger_sources", srcs: [ + ":libsurfaceflinger_backend_sources", "BackgroundExecutor.cpp", "Client.cpp", "ClientCache.cpp", "Display/DisplayModeController.cpp", "Display/DisplaySnapshot.cpp", "DisplayDevice.cpp", - "DisplayHardware/AidlComposerHal.cpp", - "DisplayHardware/ComposerHal.cpp", - "DisplayHardware/FramebufferSurface.cpp", - "DisplayHardware/HWC2.cpp", - "DisplayHardware/HWComposer.cpp", - "DisplayHardware/HidlComposerHal.cpp", - "DisplayHardware/PowerAdvisor.cpp", - "DisplayHardware/VirtualDisplaySurface.cpp", "DisplayRenderArea.cpp", "Effects/Daltonizer.cpp", "FrontEnd/LayerCreationArgs.cpp", @@ -251,7 +284,6 @@ cc_defaults { ], static_libs: [ "android.frameworks.displayservice@1.0", - "libc++fs", "libdisplayservicehidl", "libserviceutils", ], diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index b4ac9ba741..82eafd4fa8 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -42,6 +42,7 @@ cc_defaults { "libutils", ], static_libs: [ + "libguiflags", "libmath", "librenderengine", "libtimestats", @@ -49,9 +50,7 @@ cc_defaults { "libaidlcommonsupport", "libprocessgroup", "libprocessgroup_util", - "libcgrouprc", "libjsoncpp", - "libcgrouprc_format", ], header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", @@ -59,7 +58,7 @@ cc_defaults { "android.hardware.graphics.composer@2.3-command-buffer", "android.hardware.graphics.composer@2.4-command-buffer", "android.hardware.graphics.composer3-command-buffer", - "libsurfaceflinger_headers", + "libsurfaceflinger_backend_headers", ], } @@ -141,6 +140,8 @@ cc_test { ], srcs: [ ":libcompositionengine_sources", + ":libsurfaceflinger_backend_mock_sources", + ":libsurfaceflinger_backend_sources", "tests/planner/CachedSetTest.cpp", "tests/planner/FlattenerTest.cpp", "tests/planner/LayerStateTest.cpp", @@ -151,14 +152,14 @@ cc_test { "tests/DisplayTest.cpp", "tests/HwcAsyncWorkerTest.cpp", "tests/HwcBufferCacheTest.cpp", - "tests/MockHWC2.cpp", - "tests/MockHWComposer.cpp", - "tests/MockPowerAdvisor.cpp", "tests/OutputLayerTest.cpp", "tests/OutputTest.cpp", "tests/ProjectionSpaceTest.cpp", "tests/RenderSurfaceTest.cpp", ], + header_libs: [ + "libsurfaceflinger_backend_mock_headers", + ], static_libs: [ "libcompositionengine_mocks", "libgui_mocks", @@ -167,6 +168,7 @@ cc_test { "libgtest", ], shared_libs: [ + "libbinder_ndk", // For some reason, libvulkan isn't picked up from librenderengine // Probably ASAN related? "libvulkan", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h index 6e60839dd9..252adaa8e3 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h @@ -24,7 +24,7 @@ #include <ui/Size.h> #include <ui/StaticDisplayInfo.h> -#include "DisplayHardware/PowerAdvisor.h" +#include "PowerAdvisor/PowerAdvisor.h" namespace android::compositionengine { @@ -46,9 +46,15 @@ struct DisplayCreationArgs { // content. bool isProtected = false; + // True if this display has picture processing hardware and pipelines. + bool hasPictureProcessing = false; + + // The number of layer-specific picture-processing pipelines. + int32_t maxLayerPictureProfiles = 0; + // Optional pointer to the power advisor interface, if one is needed for // this display. - Hwc2::PowerAdvisor* powerAdvisor = nullptr; + adpf::PowerAdvisor* powerAdvisor = nullptr; // Debugging. Human readable name for the display. std::string name; @@ -82,7 +88,17 @@ public: return *this; } - DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) { + DisplayCreationArgsBuilder& setHasPictureProcessing(bool hasPictureProcessing) { + mArgs.hasPictureProcessing = hasPictureProcessing; + return *this; + } + + DisplayCreationArgsBuilder& setMaxLayerPictureProfiles(int32_t maxLayerPictureProfiles) { + mArgs.maxLayerPictureProfiles = maxLayerPictureProfiles; + return *this; + } + + DisplayCreationArgsBuilder& setPowerAdvisor(adpf::PowerAdvisor* powerAdvisor) { mArgs.powerAdvisor = powerAdvisor; return *this; } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index 4e080b356b..a5e9dded8b 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -148,9 +148,6 @@ public: virtual std::optional<LayerSettings> prepareClientComposition( ClientCompositionTargetSettings&) const = 0; - // Called after the layer is displayed to update the presentation fence - virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0; - // Initializes a promise for a buffer release fence and provides the future for that // fence. This should only be called when a promise has not yet been created, or // after the previous promise has already been fulfilled. Attempting to call this diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index 14a8fd6ad7..fb8fed0743 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -19,11 +19,13 @@ #include <cstdint> #include <android/gui/CachingHint.h> +#include <gui/DisplayLuts.h> #include <gui/HdrMetadata.h> #include <math/mat4.h> #include <ui/BlurRegion.h> #include <ui/FloatRect.h> #include <ui/LayerStack.h> +#include <ui/PictureProfileHandle.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/ShadowSettings.h> @@ -218,7 +220,18 @@ struct LayerFECompositionState { float currentHdrSdrRatio = 1.f; float desiredHdrSdrRatio = 1.f; + // A picture profile handle refers to a PictureProfile configured on the display, which is a + // set of parameters that configures the picture processing hardware that is used to enhance + // the quality of buffer contents. + PictureProfileHandle pictureProfileHandle{PictureProfileHandle::NONE}; + + // A layer's priority in terms of limited picture processing pipeline utilization. + int64_t pictureProfilePriority; + gui::CachingHint cachingHint = gui::CachingHint::Enabled; + + std::shared_ptr<gui::DisplayLuts> luts; + virtual ~LayerFECompositionState(); // Debugging diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index 191d475e5d..bda7856596 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -16,6 +16,8 @@ #pragma once +#include <ftl/future.h> +#include <ftl/optional.h> #include <cstdint> #include <iterator> #include <optional> @@ -26,18 +28,18 @@ #include <vector> #include <compositionengine/LayerFE.h> -#include <ftl/future.h> #include <renderengine/LayerSettings.h> +#include <ui/DisplayIdentification.h> #include <ui/Fence.h> #include <ui/FenceTime.h> #include <ui/GraphicTypes.h> #include <ui/LayerStack.h> +#include <ui/PictureProfileHandle.h> #include <ui/Region.h> #include <ui/Transform.h> #include <utils/StrongPointer.h> #include <utils/Vector.h> -#include <ui/DisplayIdentification.h> #include "DisplayHardware/HWComposer.h" namespace android { @@ -167,7 +169,7 @@ public: virtual bool isValid() const = 0; // Returns the DisplayId the output represents, if it has one - virtual std::optional<DisplayId> getDisplayId() const = 0; + virtual ftl::Optional<DisplayId> getDisplayId() const = 0; // Enables (or disables) composition on this output virtual void setCompositionEnabled(bool) = 0; @@ -329,6 +331,11 @@ protected: virtual bool isPowerHintSessionGpuReportingEnabled() = 0; virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0; virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0; + virtual const aidl::android::hardware::graphics::composer3::OverlayProperties* + getOverlaySupport() = 0; + virtual bool hasPictureProcessing() const = 0; + virtual int32_t getMaxLayerPictureProfiles() const = 0; + virtual void applyPictureProfile() = 0; }; } // namespace compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h index dcfe21a849..2e7a7d9c5a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h @@ -21,6 +21,7 @@ #include <string> #include <vector> +#include <ui/PictureProfileHandle.h> #include <ui/Transform.h> #include <utils/StrongPointer.h> @@ -86,6 +87,16 @@ public: // longer cares about. virtual void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) = 0; + // Get the relative priority of the layer's picture profile with respect to the importance of + // the visual content to the user experience. Lower is higher priority. + virtual int64_t getPictureProfilePriority() const = 0; + + // The picture profile handle for the layer. + virtual const PictureProfileHandle& getPictureProfileHandle() const = 0; + + // Commit the picture profile to the composition state. + virtual void commitPictureProfileToCompositionState() = 0; + // Recalculates the state of the output layer from the output-independent // layer. If includeGeometry is false, the geometry state can be skipped. // internalDisplayRotationFlags must be set to the rotation flags for the @@ -93,7 +104,10 @@ public: // transform, if needed. virtual void updateCompositionState( bool includeGeometry, bool forceClientComposition, - ui::Transform::RotationFlags internalDisplayRotationFlags) = 0; + ui::Transform::RotationFlags internalDisplayRotationFlags, + const std::optional<std::vector< + std::optional<aidl::android::hardware::graphics::composer3::LutProperties>>> + properties) = 0; // Writes the geometry state to the HWC, or does nothing if this layer does // not use the HWC. If includeGeometry is false, the geometry state can be @@ -129,8 +143,10 @@ public: virtual void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) = 0; // Applies a HWC device layer lut - virtual void applyDeviceLayerLut(aidl::android::hardware::graphics::composer3::LutProperties, - ndk::ScopedFileDescriptor) = 0; + virtual void applyDeviceLayerLut( + ndk::ScopedFileDescriptor, + std::vector<std::pair< + int, aidl::android::hardware::graphics::composer3::LutProperties>>) = 0; // Returns true if the composition settings scale pixels virtual bool needsFiltering() const = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index a39abb40d2..5519aafe11 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -30,7 +30,7 @@ #include <ui/DisplayIdentification.h> #include "DisplayHardware/HWComposer.h" -#include "DisplayHardware/PowerAdvisor.h" +#include "PowerAdvisor/PowerAdvisor.h" namespace android::compositionengine { @@ -45,7 +45,7 @@ public: virtual ~Display(); // compositionengine::Output overrides - std::optional<DisplayId> getDisplayId() const override; + ftl::Optional<DisplayId> getDisplayId() const override; bool isValid() const override; void dump(std::string&) const override; using compositionengine::impl::Output::setReleasedLayers; @@ -100,9 +100,16 @@ private: void setHintSessionGpuStart(TimePoint startTime) override; void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override; + const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport() + override; + bool hasPictureProcessing() const override; + int32_t getMaxLayerPictureProfiles() const override; + DisplayId mId; bool mIsDisconnected = false; - Hwc2::PowerAdvisor* mPowerAdvisor = nullptr; + adpf::PowerAdvisor* mPowerAdvisor = nullptr; + bool mHasPictureProcessing = false; + int32_t mMaxLayerPictureProfiles = 0; }; // This template factory function standardizes the implementation details of the diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 9990a742db..0ccdd22919 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -16,6 +16,11 @@ #pragma once +#include <ftl/optional.h> +#include <memory> +#include <utility> +#include <vector> + #include <compositionengine/CompositionEngine.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/Output.h> @@ -28,10 +33,6 @@ #include <renderengine/DisplaySettings.h> #include <renderengine/LayerSettings.h> -#include <memory> -#include <utility> -#include <vector> - namespace android::compositionengine::impl { // The implementation class contains the common implementation, but does not @@ -43,7 +44,7 @@ public: // compositionengine::Output overrides bool isValid() const override; - std::optional<DisplayId> getDisplayId() const override; + ftl::Optional<DisplayId> getDisplayId() const override; void setCompositionEnabled(bool) override; void setLayerCachingEnabled(bool) override; void setLayerCachingTexturePoolEnabled(bool) override; @@ -84,13 +85,14 @@ public: bool supportsOffloadPresent() const override { return false; } void offloadPresentNextFrame() override; - void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override; void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override; void collectVisibleLayers(const CompositionRefreshArgs&, compositionengine::Output::CoverageState&) override; void ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>&, compositionengine::Output::CoverageState&) override; void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override; + void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override; + void commitPictureProfilesToCompositionState(); void updateCompositionState(const compositionengine::CompositionRefreshArgs&) override; void planComposition() override; @@ -151,6 +153,9 @@ protected: void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override; bool isPowerHintSessionEnabled() override; bool isPowerHintSessionGpuReportingEnabled() override; + bool hasPictureProcessing() const override; + int32_t getMaxLayerPictureProfiles() const override; + void applyPictureProfile() override; void dumpBase(std::string&) const; // Implemented by the final implementation for the final state it uses. @@ -164,6 +169,8 @@ protected: bool mustRecompose() const; const std::string& getNamePlusId() const { return mNamePlusId; } + const aidl::android::hardware::graphics::composer3::OverlayProperties* getOverlaySupport() + override; private: void dirtyEntireOutput(); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h index f8ffde1e51..c76b34481b 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h @@ -35,6 +35,7 @@ #include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/ProjectionSpace.h> #include <ui/LayerStack.h> +#include <ui/PictureProfileHandle.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/Transform.h> @@ -170,6 +171,8 @@ struct OutputCompositionState { ICEPowerCallback* powerCallback = nullptr; + PictureProfileHandle pictureProfileHandle; + // Debugging void dump(std::string& result) const; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index 354a4416f2..712b55123f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -25,12 +25,15 @@ #include <compositionengine/LayerFE.h> #include <compositionengine/OutputLayer.h> #include <ui/FloatRect.h> +#include <ui/PictureProfileHandle.h> #include <ui/Rect.h> #include <ui/DisplayIdentification.h> #include <aidl/android/hardware/graphics/composer3/Composition.h> +using aidl::android::hardware::graphics::composer3::LutProperties; + namespace android::compositionengine { struct LayerFECompositionState; @@ -46,9 +49,14 @@ public: void setHwcLayer(std::shared_ptr<HWC2::Layer>) override; void uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache) override; + int64_t getPictureProfilePriority() const override; + const PictureProfileHandle& getPictureProfileHandle() const override; + void commitPictureProfileToCompositionState() override; void updateCompositionState(bool includeGeometry, bool forceClientComposition, - ui::Transform::RotationFlags) override; + ui::Transform::RotationFlags, + const std::optional<std::vector<std::optional<LutProperties>>> + properties = std::nullopt) override; void writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden, bool isPeekingThrough) override; void writeCursorPositionToHWC() const override; @@ -60,8 +68,8 @@ public: aidl::android::hardware::graphics::composer3::Composition) override; void prepareForDeviceLayerRequests() override; void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override; - void applyDeviceLayerLut(aidl::android::hardware::graphics::composer3::LutProperties, - ndk::ScopedFileDescriptor) override; + void applyDeviceLayerLut(ndk::ScopedFileDescriptor, + std::vector<std::pair<int, LutProperties>>) override; bool needsFiltering() const override; std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override; @@ -92,10 +100,13 @@ private: void writeCompositionTypeToHWC(HWC2::Layer*, aidl::android::hardware::graphics::composer3::Composition, bool isPeekingThrough, bool skipLayer); + void writeLutToHWC(HWC2::Layer*, const LayerFECompositionState&); void detectDisallowedCompositionTypeChange( aidl::android::hardware::graphics::composer3::Composition from, aidl::android::hardware::graphics::composer3::Composition to) const; bool isClientCompositionForced(bool isPeekingThrough) const; + void updateLuts(const LayerFECompositionState&, + const std::optional<std::vector<std::optional<LutProperties>>>& properties); }; // This template factory function standardizes the implementation details of the diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h index 6c419da716..c558739464 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h @@ -18,9 +18,11 @@ #include <compositionengine/ProjectionSpace.h> #include <compositionengine/impl/HwcBufferCache.h> +#include <gui/DisplayLuts.h> #include <renderengine/ExternalTexture.h> #include <ui/FloatRect.h> #include <ui/GraphicTypes.h> +#include <ui/PictureProfileHandle.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -100,6 +102,9 @@ struct OutputLayerCompositionState { // order to save power. Region outputSpaceBlockingRegionHint; + // The picture profile for this layer. + PictureProfileHandle pictureProfileHandle; + // Overrides the buffer, acquire fence, and display frame stored in LayerFECompositionState struct { std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr; @@ -151,6 +156,9 @@ struct OutputLayerCompositionState { // True when this layer was skipped as part of SF-side layer caching. bool layerSkipped = false; + + // lut information + std::shared_ptr<gui::DisplayLuts> luts; }; // The HWC state is optional, and is only set up if there is any potential diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h index 05a5d3838c..c7ff704491 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -50,9 +50,6 @@ public: std::optional<compositionengine::LayerFE::LayerSettings>( compositionengine::LayerFE::ClientCompositionTargetSettings&)); - MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack), - (override)); - MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>()); MOCK_METHOD1(setReleaseFence, void(const FenceResult&)); MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus()); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index d5bf2b5090..f2c265ad2e 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -34,7 +34,7 @@ public: virtual ~Output(); MOCK_CONST_METHOD0(isValid, bool()); - MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>()); + MOCK_CONST_METHOD0(getDisplayId, ftl::Optional<DisplayId>()); MOCK_METHOD1(setCompositionEnabled, void(bool)); MOCK_METHOD1(setLayerCachingEnabled, void(bool)); @@ -140,6 +140,11 @@ public: MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool requiresRenderEngine)); MOCK_METHOD(bool, isPowerHintSessionEnabled, ()); MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, ()); + MOCK_METHOD((const aidl::android::hardware::graphics::composer3::OverlayProperties*), + getOverlaySupport, ()); + MOCK_METHOD(bool, hasPictureProcessing, (), (const)); + MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (), (const)); + MOCK_METHOD(void, applyPictureProfile, ()); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h index 48c2f9c483..9333ebb8cd 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h @@ -43,7 +43,10 @@ public: MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&()); MOCK_METHOD0(editState, impl::OutputLayerCompositionState&()); - MOCK_METHOD3(updateCompositionState, void(bool, bool, ui::Transform::RotationFlags)); + MOCK_METHOD(void, updateCompositionState, + (bool, bool, ui::Transform::RotationFlags, + (const std::optional<std::vector<std::optional< + aidl::android::hardware::graphics::composer3::LutProperties>>>))); MOCK_METHOD5(writeStateToHWC, void(bool, bool, uint32_t, bool, bool)); MOCK_CONST_METHOD0(writeCursorPositionToHWC, void()); @@ -57,9 +60,12 @@ public: MOCK_CONST_METHOD0(needsFiltering, bool()); MOCK_CONST_METHOD0(getOverrideCompositionSettings, std::optional<LayerFE::LayerSettings>()); MOCK_METHOD(void, applyDeviceLayerLut, - (aidl::android::hardware::graphics::composer3::LutProperties, - ndk::ScopedFileDescriptor)); - + (ndk::ScopedFileDescriptor, + (std::vector<std::pair< + int, aidl::android::hardware::graphics::composer3::LutProperties>>))); + MOCK_METHOD(int64_t, getPictureProfilePriority, (), (const)); + MOCK_METHOD(const PictureProfileHandle&, getPictureProfileHandle, (), (const)); + MOCK_METHOD(void, commitPictureProfileToCompositionState, ()); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index b0164b7c33..e37ce0a0eb 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -36,7 +36,7 @@ // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion" -#include "DisplayHardware/PowerAdvisor.h" +#include "PowerAdvisor/PowerAdvisor.h" using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; @@ -54,6 +54,8 @@ Display::~Display() = default; void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) { mId = args.id; mPowerAdvisor = args.powerAdvisor; + mHasPictureProcessing = args.hasPictureProcessing; + mMaxLayerPictureProfiles = args.maxLayerPictureProfiles; editState().isSecure = args.isSecure; editState().isProtected = args.isProtected; editState().displaySpace.setBounds(args.pixels); @@ -80,7 +82,7 @@ bool Display::isVirtual() const { return mId.isVirtual(); } -std::optional<DisplayId> Display::getDisplayId() const { +ftl::Optional<DisplayId> Display::getDisplayId() const { return mId; } @@ -203,15 +205,16 @@ void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& } void Display::applyDisplayBrightness(bool applyImmediately) { - if (const auto displayId = ftl::Optional(getDisplayId()).and_then(PhysicalDisplayId::tryCast); - displayId && getState().displayBrightness) { + if (!getState().displayBrightness) { + return; + } + if (auto displayId = PhysicalDisplayId::tryCast(mId)) { auto& hwc = getCompositionEngine().getHwComposer(); - const status_t result = - hwc.setDisplayBrightness(*displayId, *getState().displayBrightness, - getState().displayBrightnessNits, - Hwc2::Composer::DisplayBrightnessOptions{ - .applyImmediately = applyImmediately}) - .get(); + status_t result = hwc.setDisplayBrightness(*displayId, *getState().displayBrightness, + getState().displayBrightnessNits, + Hwc2::Composer::DisplayBrightnessOptions{ + .applyImmediately = applyImmediately}) + .get(); ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)", getName().c_str(), result, strerror(-result)); } @@ -288,8 +291,8 @@ void Display::applyCompositionStrategy(const std::optional<DeviceRequestedChange } bool Display::getSkipColorTransform() const { - const auto& hwc = getCompositionEngine().getHwComposer(); - if (const auto halDisplayId = HalDisplayId::tryCast(mId)) { + auto& hwc = getCompositionEngine().getHwComposer(); + if (auto halDisplayId = HalDisplayId::tryCast(mId)) { return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::SKIP_CLIENT_COLOR_TRANSFORM); } @@ -370,8 +373,8 @@ void Display::applyLayerLutsToLayers(const LayerLuts& layerLuts) { if (auto lutsIt = layerLuts.find(hwcLayer); lutsIt != layerLuts.end()) { if (auto mapperIt = mapper.find(hwcLayer); mapperIt != mapper.end()) { - layer->applyDeviceLayerLut(lutsIt->second, - ndk::ScopedFileDescriptor(mapperIt->second.release())); + layer->applyDeviceLayerLut(ndk::ScopedFileDescriptor(mapperIt->second.release()), + lutsIt->second); } } } @@ -457,6 +460,19 @@ void Display::setHintSessionRequiresRenderEngine(bool requiresRenderEngine) { mPowerAdvisor->setRequiresRenderEngine(mId, requiresRenderEngine); } +const aidl::android::hardware::graphics::composer3::OverlayProperties* +Display::getOverlaySupport() { + return &getCompositionEngine().getHwComposer().getOverlaySupport(); +} + +bool Display::hasPictureProcessing() const { + return mHasPictureProcessing; +} + +int32_t Display::getMaxLayerPictureProfiles() const { + return mMaxLayerPictureProfiles; +} + void Display::finishFrame(GpuCompositionResult&& result) { // We only need to actually compose the display if: // 1) It is being handled by hardware composer, which may need this to @@ -471,8 +487,8 @@ void Display::finishFrame(GpuCompositionResult&& result) { } bool Display::supportsOffloadPresent() const { - if (const auto halDisplayId = HalDisplayId::tryCast(mId)) { - const auto& hwc = getCompositionEngine().getHwComposer(); + if (auto halDisplayId = HalDisplayId::tryCast(mId)) { + auto& hwc = getCompositionEngine().getHwComposer(); return hwc.hasDisplayCapability(*halDisplayId, DisplayCapability::MULTI_THREADED_PRESENT); } diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp index 2d10866db3..348111d06e 100644 --- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp @@ -127,6 +127,9 @@ void LayerFECompositionState::dump(std::string& out) const { } dumpVal(out, "colorTransform", colorTransform); dumpVal(out, "caching hint", toString(cachingHint)); + if (pictureProfileHandle) { + dumpVal(out, "pictureProfile", toString(pictureProfileHandle)); + } out.append("\n"); } diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 22ab3d953b..98b6666a1f 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -32,9 +32,12 @@ #include <compositionengine/impl/planner/Planner.h> #include <ftl/algorithm.h> #include <ftl/future.h> +#include <ftl/optional.h> #include <scheduler/FrameTargeter.h> #include <scheduler/Time.h> +#include <com_android_graphics_libgui_flags.h> + #include <optional> #include <thread> @@ -111,7 +114,7 @@ bool Output::isValid() const { mRenderSurface->isValid(); } -std::optional<DisplayId> Output::getDisplayId() const { +ftl::Optional<DisplayId> Output::getDisplayId() const { return {}; } @@ -433,7 +436,7 @@ void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArg ftl::Future<std::monostate> Output::present( const compositionengine::CompositionRefreshArgs& refreshArgs) { const auto stringifyExpectedPresentTime = [this, &refreshArgs]() -> std::string { - return ftl::Optional(getDisplayId()) + return getDisplayId() .and_then(PhysicalDisplayId::tryCast) .and_then([&refreshArgs](PhysicalDisplayId id) { return refreshArgs.frameTargets.get(id); @@ -500,15 +503,6 @@ void Output::offloadPresentNextFrame() { updateHwcAsyncWorker(); } -void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) { - if (bufferIdsToUncache.empty()) { - return; - } - for (auto outputLayer : getOutputLayersOrderedByZ()) { - outputLayer->uncacheBuffers(bufferIdsToUncache); - } -} - void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs, LayerFESet& layerFESet) { auto& outputState = editState(); @@ -776,11 +770,11 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, // The layer is visible. Either reuse the existing outputLayer if we have // one, or create a new one if we do not. - auto result = ensureOutputLayer(prevOutputLayerIndex, layerFE); + auto outputLayer = ensureOutputLayer(prevOutputLayerIndex, layerFE); // Store the layer coverage information into the layer state as some of it // is useful later. - auto& outputLayerState = result->editState(); + auto& outputLayerState = outputLayer->editState(); outputLayerState.visibleRegion = visibleRegion; outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion; outputLayerState.coveredRegion = coveredRegion; @@ -798,6 +792,52 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, } } +void Output::uncacheBuffers(std::vector<uint64_t> const& bufferIdsToUncache) { + if (bufferIdsToUncache.empty()) { + return; + } + for (auto outputLayer : getOutputLayersOrderedByZ()) { + outputLayer->uncacheBuffers(bufferIdsToUncache); + } +} + +void Output::commitPictureProfilesToCompositionState() { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + return; + } + if (!hasPictureProcessing()) { + return; + } + auto compare = [](const ::android::compositionengine::OutputLayer* lhs, + const ::android::compositionengine::OutputLayer* rhs) { + return lhs->getPictureProfilePriority() > rhs->getPictureProfilePriority(); + }; + std::priority_queue<::android::compositionengine::OutputLayer*, + std::vector<::android::compositionengine::OutputLayer*>, decltype(compare)> + layersWithProfiles; + for (auto outputLayer : getOutputLayersOrderedByZ()) { + if (outputLayer->getPictureProfileHandle()) { + layersWithProfiles.push(outputLayer); + } + } + + // TODO(b/337330263): Use the default display picture profile from SurfaceFlinger + editState().pictureProfileHandle = PictureProfileHandle::NONE; + + // When layer-specific picture processing is supported, apply as many high priority profiles as + // possible to the layers, and ignore the low priority layers. + if (getMaxLayerPictureProfiles() > 0) { + for (int i = 0; i < getMaxLayerPictureProfiles() && !layersWithProfiles.empty(); + layersWithProfiles.pop(), ++i) { + layersWithProfiles.top()->commitPictureProfileToCompositionState(); + } + // No layer-specific picture processing, so apply the highest priority picture profile to + // the entire display. + } else if (!layersWithProfiles.empty()) { + editState().pictureProfileHandle = layersWithProfiles.top()->getPictureProfileHandle(); + } +} + void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) { // The base class does nothing with this call. } @@ -813,16 +853,20 @@ void Output::updateCompositionState(const compositionengine::CompositionRefreshA mLayerRequestingBackgroundBlur = findLayerRequestingBackgroundComposition(); bool forceClientComposition = mLayerRequestingBackgroundBlur != nullptr; + auto* properties = getOverlaySupport(); + for (auto* layer : getOutputLayersOrderedByZ()) { layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame, refreshArgs.devOptForceClientComposition || forceClientComposition, - refreshArgs.internalDisplayRotationFlags); + refreshArgs.internalDisplayRotationFlags, + properties ? properties->lutProperties : std::nullopt); if (mLayerRequestingBackgroundBlur == layer) { forceClientComposition = false; } } + commitPictureProfilesToCompositionState(); } void Output::planComposition() { @@ -844,7 +888,7 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr return; } - if (auto frameTargetPtrOpt = ftl::Optional(getDisplayId()) + if (auto frameTargetPtrOpt = getDisplayId() .and_then(PhysicalDisplayId::tryCast) .and_then([&refreshArgs](PhysicalDisplayId id) { return refreshArgs.frameTargets.get(id); @@ -855,6 +899,8 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr editState().frameInterval = refreshArgs.frameInterval; editState().powerCallback = refreshArgs.powerCallback; + applyPictureProfile(); + compositionengine::OutputLayer* peekThroughLayer = nullptr; sp<GraphicBuffer> previousOverride = nullptr; bool includeGeometry = refreshArgs.updatingGeometryThisFrame; @@ -1678,6 +1724,10 @@ void Output::setTreat170mAsSrgb(bool enable) { editState().treat170mAsSrgb = enable; } +const aidl::android::hardware::graphics::composer3::OverlayProperties* Output::getOverlaySupport() { + return nullptr; +} + bool Output::canPredictCompositionStrategy(const CompositionRefreshArgs& refreshArgs) { uint64_t lastOutputLayerHash = getState().lastOutputLayerHash; uint64_t outputLayerHash = getState().outputLayerHash; @@ -1756,5 +1806,34 @@ float Output::getHdrSdrRatio(const std::shared_ptr<renderengine::ExternalTexture return getState().displayBrightnessNits / getState().sdrWhitePointNits; } +bool Output::hasPictureProcessing() const { + return false; +} + +int32_t Output::getMaxLayerPictureProfiles() const { + return 0; +} + +void Output::applyPictureProfile() { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + return; + } + + // TODO(b/337330263): Move this into the Display class and add a Display unit test. + if (!getState().pictureProfileHandle) { + return; + } + if (!getDisplayId()) { + return; + } + if (auto displayId = PhysicalDisplayId::tryCast(*getDisplayId())) { + auto& hwc = getCompositionEngine().getHwComposer(); + const status_t error = + hwc.setDisplayPictureProfileHandle(*displayId, getState().pictureProfileHandle); + ALOGE_IF(error, "setDisplayPictureProfileHandle failed for %s: %d, (%s)", getName().c_str(), + error, strerror(-error)); + } +} + } // namespace impl } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 2d46dc0702..f6d9a1ae6c 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include <DisplayHardware/Hal.h> #include <android-base/stringprintf.h> #include <compositionengine/DisplayColorProfile.h> @@ -22,11 +23,12 @@ #include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> +#include <ui/FloatRect.h> +#include <ui/HdrRenderTypeUtils.h> #include <cstdint> #include "system/graphics-base-v1.0.h" -#include "ui/FloatRect.h" -#include <ui/HdrRenderTypeUtils.h> +#include <com_android_graphics_libgui_flags.h> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push @@ -38,7 +40,7 @@ #pragma clang diagnostic pop // ignored "-Wconversion" using aidl::android::hardware::graphics::composer3::Composition; -using aidl::android::hardware::graphics::composer3::LutProperties; +using aidl::android::hardware::graphics::composer3::Luts; namespace android::compositionengine { @@ -285,9 +287,55 @@ uint32_t OutputLayer::calculateOutputRelativeBufferTransform( return transform.getOrientation(); } +void OutputLayer::updateLuts( + const LayerFECompositionState& layerFEState, + const std::optional<std::vector<std::optional<LutProperties>>>& properties) { + auto& luts = layerFEState.luts; + if (!luts) { + return; + } + + auto& state = editState(); + + if (!properties) { + // GPU composition if no Hwc Luts + state.forceClientComposition = true; + return; + } + + std::vector<LutProperties> hwcLutProperties; + for (auto& p : *properties) { + if (p) { + hwcLutProperties.emplace_back(*p); + } + } + + for (const auto& inputLut : luts->lutProperties) { + bool foundInHwcLuts = false; + for (const auto& hwcLut : hwcLutProperties) { + if (static_cast<int32_t>(hwcLut.dimension) == + static_cast<int32_t>(inputLut.dimension) && + hwcLut.size == inputLut.size && + std::find(hwcLut.samplingKeys.begin(), hwcLut.samplingKeys.end(), + static_cast<LutProperties::SamplingKey>(inputLut.samplingKey)) != + hwcLut.samplingKeys.end()) { + foundInHwcLuts = true; + break; + } + } + // if any lut properties of luts can not be found in hwcLutProperties, + // GPU composition instead + if (!foundInHwcLuts) { + state.forceClientComposition = true; + return; + } + } +} + void OutputLayer::updateCompositionState( bool includeGeometry, bool forceClientComposition, - ui::Transform::RotationFlags internalDisplayRotationFlags) { + ui::Transform::RotationFlags internalDisplayRotationFlags, + const std::optional<std::vector<std::optional<LutProperties>>> properties) { const auto* layerFEState = getLayerFE().getCompositionState(); if (!layerFEState) { return; @@ -368,6 +416,8 @@ void OutputLayer::updateCompositionState( state.whitePointNits = layerBrightnessNits; } + updateLuts(*layerFEState, properties); + // These are evaluated every frame as they can potentially change at any // time. if (layerFEState->forceClientComposition || !profile.isDataspaceSupported(state.dataspace) || @@ -376,6 +426,16 @@ void OutputLayer::updateCompositionState( } } +void OutputLayer::commitPictureProfileToCompositionState() { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + return; + } + const auto* layerState = getLayerFE().getCompositionState(); + if (layerState) { + editState().pictureProfileHandle = getLayerFE().getCompositionState()->pictureProfileHandle; + } +} + void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t z, bool zIsOverridden, bool isPeekingThrough) { const auto& state = getState(); @@ -420,6 +480,8 @@ void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough, skipLayer); + writeLutToHWC(hwcLayer.get(), *outputIndependentState); + if (requestedCompositionType == Composition::SOLID_COLOR) { writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState); } @@ -513,6 +575,40 @@ void OutputLayer::writeOutputIndependentGeometryStateToHWC( } } +void OutputLayer::writeLutToHWC(HWC2::Layer* hwcLayer, + const LayerFECompositionState& outputIndependentState) { + if (!outputIndependentState.luts) { + return; + } + auto& lutFileDescriptor = outputIndependentState.luts->getLutFileDescriptor(); + auto lutOffsets = outputIndependentState.luts->offsets; + auto& lutProperties = outputIndependentState.luts->lutProperties; + + std::vector<LutProperties> aidlProperties; + aidlProperties.reserve(lutProperties.size()); + for (size_t i = 0; i < lutOffsets.size(); i++) { + LutProperties properties; + properties.dimension = static_cast<LutProperties::Dimension>(lutProperties[i].dimension); + properties.size = lutProperties[i].size; + properties.samplingKeys = { + static_cast<LutProperties::SamplingKey>(lutProperties[i].samplingKey)}; + aidlProperties.emplace_back(properties); + } + + Luts luts; + luts.pfd = ndk::ScopedFileDescriptor(dup(lutFileDescriptor.get())); + luts.offsets = lutOffsets; + luts.lutProperties = std::move(aidlProperties); + + switch (auto error = hwcLayer->setLuts(luts)) { + case hal::Error::NONE: + break; + default: + ALOGE("[%s] Failed to set Luts: %s (%d)", getLayerFE().getDebugName(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } +} + void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) { const auto& outputDependentState = getState(); @@ -561,6 +657,21 @@ void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) ALOGE("[%s] Failed to set brightness %f: %s (%d)", getLayerFE().getDebugName(), dimmingRatio, to_string(error).c_str(), static_cast<int32_t>(error)); } + + if (com_android_graphics_libgui_flags_apply_picture_profiles() && + outputDependentState.pictureProfileHandle) { + if (auto error = + hwcLayer->setPictureProfileHandle(outputDependentState.pictureProfileHandle); + error != hal::Error::NONE) { + ALOGE("[%s] Failed to set picture profile handle: %s (%d)", getLayerFE().getDebugName(), + toString(outputDependentState.pictureProfileHandle).c_str(), + static_cast<int32_t>(error)); + } + // Reset the picture profile state, as it needs to be re-committed on each present cycle + // when Output decides that the limited picture-processing hardware should be used by this + // layer. + editState().pictureProfileHandle = PictureProfileHandle::NONE; + } } void OutputLayer::writeOutputIndependentPerFrameStateToHWC( @@ -666,6 +777,16 @@ void OutputLayer::uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache } } +int64_t OutputLayer::getPictureProfilePriority() const { + const auto* layerState = getLayerFE().getCompositionState(); + return layerState ? layerState->pictureProfilePriority : 0; +} + +const PictureProfileHandle& OutputLayer::getPictureProfileHandle() const { + const auto* layerState = getLayerFE().getCompositionState(); + return layerState ? layerState->pictureProfileHandle : PictureProfileHandle::NONE; +} + void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState, bool skipLayer) { @@ -748,14 +869,14 @@ void OutputLayer::writeCursorPositionToHWC() const { return; } - const auto* layerFEState = getLayerFE().getCompositionState(); - if (!layerFEState) { + const auto* layerState = getLayerFE().getCompositionState(); + if (!layerState) { return; } const auto& outputState = getOutput().getState(); - Rect frame = layerFEState->cursorFrame; + Rect frame = layerState->cursorFrame; frame.intersect(outputState.layerStackSpace.getContent(), &frame); Rect position = outputState.transform.transform(frame); @@ -849,10 +970,29 @@ void OutputLayer::applyDeviceLayerRequest(hal::LayerRequest request) { } } -void OutputLayer::applyDeviceLayerLut(LutProperties /*lutProperties*/, - ndk::ScopedFileDescriptor /*lutPfd*/) { - // TODO(b/329472856): decode the shared memory of the pfd, and store the lut data into - // OutputLayerCompositionState#hwc struct +void OutputLayer::applyDeviceLayerLut( + ndk::ScopedFileDescriptor lutFileDescriptor, + std::vector<std::pair<int, LutProperties>> lutOffsetsAndProperties) { + auto& state = editState(); + LOG_FATAL_IF(!state.hwc); + auto& hwcState = *state.hwc; + std::vector<int32_t> offsets; + std::vector<int32_t> dimensions; + std::vector<int32_t> sizes; + std::vector<int32_t> samplingKeys; + for (const auto& [offset, properties] : lutOffsetsAndProperties) { + // The Lut(s) that comes back through CommandResultPayload should be + // only one sampling key. + if (properties.samplingKeys.size() == 1) { + offsets.emplace_back(offset); + dimensions.emplace_back(static_cast<int32_t>(properties.dimension)); + sizes.emplace_back(static_cast<int32_t>(properties.size)); + samplingKeys.emplace_back(static_cast<int32_t>(properties.samplingKeys[0])); + } + } + hwcState.luts = std::make_shared<gui::DisplayLuts>(base::unique_fd(lutFileDescriptor.release()), + std::move(offsets), std::move(dimensions), + std::move(sizes), std::move(samplingKeys)); } bool OutputLayer::needsFiltering() const { diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp index da1f7e49f8..deef74728d 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp @@ -72,6 +72,9 @@ void OutputLayerCompositionState::dump(std::string& out) const { dumpVal(out, "dataspace", toString(dataspace), dataspace); dumpVal(out, "whitePointNits", whitePointNits); dumpVal(out, "dimmingRatio", dimmingRatio); + if (pictureProfileHandle) { + dumpVal(out, "pictureProfile", toString(pictureProfileHandle)); + } dumpVal(out, "override buffer", overrideInfo.buffer.get()); dumpVal(out, "override acquire fence", overrideInfo.acquireFence.get()); dumpVal(out, "override display frame", overrideInfo.displayFrame); diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp index 48ebc32eb8..3e0c390a5d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp @@ -26,9 +26,8 @@ #include <gtest/gtest.h> #include <renderengine/mock/RenderEngine.h> -#include "MockHWComposer.h" #include "TimeStats/TimeStats.h" -#include "gmock/gmock.h" +#include "mock/DisplayHardware/MockHWComposer.h" using namespace com::android::graphics::surfaceflinger; diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 9c0e62c643..c1e59d01de 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -36,10 +36,10 @@ #include <ui/Rect.h> #include <ui/StaticDisplayInfo.h> -#include "MockHWC2.h" -#include "MockHWComposer.h" -#include "MockPowerAdvisor.h" #include "ftl/future.h" +#include "mock/DisplayHardware/MockHWC2.h" +#include "mock/DisplayHardware/MockHWComposer.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" #include <aidl/android/hardware/graphics/composer3/Composition.h> @@ -192,7 +192,7 @@ struct DisplayTestCommon : public testing::Test { } StrictMock<android::mock::HWComposer> mHwComposer; - StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor; + StrictMock<adpf::mock::PowerAdvisor> mPowerAdvisor; StrictMock<renderengine::mock::RenderEngine> mRenderEngine; StrictMock<mock::CompositionEngine> mCompositionEngine; sp<mock::NativeWindow> mNativeWindow = sp<StrictMock<mock::NativeWindow>>::make(); @@ -1035,7 +1035,7 @@ struct DisplayFunctionalTest : public testing::Test { } NiceMock<android::mock::HWComposer> mHwComposer; - NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor; + NiceMock<adpf::mock::PowerAdvisor> mPowerAdvisor; NiceMock<mock::CompositionEngine> mCompositionEngine; sp<mock::NativeWindow> mNativeWindow = sp<NiceMock<mock::NativeWindow>>::make(); sp<mock::DisplaySurface> mDisplaySurface = sp<NiceMock<mock::DisplaySurface>>::make(); diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h deleted file mode 100644 index eb6e677117..0000000000 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 <gmock/gmock.h> -#include <ui/Fence.h> -#include <ui/FloatRect.h> -#include <ui/GraphicBuffer.h> -#include <ui/Rect.h> -#include <ui/Region.h> -#include <ui/Transform.h> - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#pragma clang diagnostic ignored "-Wextra" - -#include <ui/GraphicTypes.h> -#include "DisplayHardware/HWC2.h" - -#include <aidl/android/hardware/graphics/composer3/Composition.h> -#include <aidl/android/hardware/graphics/composer3/Lut.h> - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion -Wextra" - -namespace android { -namespace HWC2 { -namespace mock { - -namespace hal = android::hardware::graphics::composer::hal; - -using Error = hal::Error; - -class Layer : public HWC2::Layer { -public: - Layer(); - ~Layer() override; - - MOCK_CONST_METHOD0(getId, hal::HWLayerId()); - - MOCK_METHOD2(setCursorPosition, Error(int32_t, int32_t)); - MOCK_METHOD3(setBuffer, - Error(uint32_t, const android::sp<android::GraphicBuffer>&, - const android::sp<android::Fence>&)); - MOCK_METHOD2(setBufferSlotsToClear, Error(const std::vector<uint32_t>&, uint32_t)); - MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&)); - MOCK_METHOD1(setBlendMode, Error(hal::BlendMode)); - MOCK_METHOD1(setColor, Error(aidl::android::hardware::graphics::composer3::Color)); - MOCK_METHOD1(setCompositionType, - Error(aidl::android::hardware::graphics::composer3::Composition)); - MOCK_METHOD1(setDataspace, Error(android::ui::Dataspace)); - MOCK_METHOD2(setPerFrameMetadata, Error(const int32_t, const android::HdrMetadata&)); - MOCK_METHOD1(setDisplayFrame, Error(const android::Rect&)); - MOCK_METHOD1(setPlaneAlpha, Error(float)); - MOCK_METHOD1(setSidebandStream, Error(const native_handle_t*)); - MOCK_METHOD1(setSourceCrop, Error(const android::FloatRect&)); - MOCK_METHOD1(setTransform, Error(hal::Transform)); - MOCK_METHOD1(setVisibleRegion, Error(const android::Region&)); - MOCK_METHOD1(setZOrder, Error(uint32_t)); - - MOCK_METHOD1(setColorTransform, Error(const android::mat4&)); - MOCK_METHOD3(setLayerGenericMetadata, - Error(const std::string&, bool, const std::vector<uint8_t>&)); - MOCK_METHOD1(setBrightness, Error(float)); - MOCK_METHOD1(setBlockingRegion, Error(const android::Region&)); - MOCK_METHOD(Error, setLuts, (std::vector<aidl::android::hardware::graphics::composer3::Lut>&)); -}; - -} // namespace mock -} // namespace HWC2 -} // namespace android diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h deleted file mode 100644 index 5c55ce739a..0000000000 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <compositionengine/Output.h> -#include <gmock/gmock.h> - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#pragma clang diagnostic ignored "-Wextra" - -#include "DisplayHardware/HWComposer.h" - -// TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wconversion -Wextra" - -namespace android { -namespace mock { - -namespace hal = android::hardware::graphics::composer::hal; - -class HWComposer : public android::HWComposer { -public: - HWComposer(); - ~HWComposer() override; - - MOCK_METHOD1(setCallback, void(HWC2::ComposerCallback&)); - MOCK_CONST_METHOD3(getDisplayIdentificationData, - bool(hal::HWDisplayId, uint8_t*, DisplayIdentificationData*)); - MOCK_CONST_METHOD1(hasCapability, - bool(aidl::android::hardware::graphics::composer3::Capability)); - MOCK_CONST_METHOD2(hasDisplayCapability, - bool(HalDisplayId, - aidl::android::hardware::graphics::composer3::DisplayCapability)); - - MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t()); - MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t()); - MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*)); - MOCK_METHOD3(allocatePhysicalDisplay, - void(hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>)); - - MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId)); - MOCK_METHOD(status_t, getDeviceCompositionChanges, - (HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>, nsecs_t, - Fps, std::optional<android::HWComposer::DeviceRequestedChanges>*)); - MOCK_METHOD(status_t, setClientTarget, - (HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace, - float), - (override)); - MOCK_METHOD2(presentAndGetReleaseFences, - status_t(HalDisplayId, std::optional<std::chrono::steady_clock::time_point>)); - MOCK_METHOD(status_t, executeCommands, (HalDisplayId)); - MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode)); - MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t)); - MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&)); - MOCK_METHOD1(disconnectDisplay, void(HalDisplayId)); - MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&)); - MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(HalDisplayId)); - MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override)); - MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(HalDisplayId, HWC2::Layer*)); - MOCK_METHOD3(setOutputBuffer, - status_t(HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&)); - MOCK_METHOD1(clearReleaseFences, void(HalDisplayId)); - MOCK_METHOD2(getHdrCapabilities, status_t(HalDisplayId, HdrCapabilities*)); - MOCK_CONST_METHOD1(getSupportedPerFrameMetadata, int32_t(HalDisplayId)); - MOCK_CONST_METHOD2(getRenderIntents, - std::vector<ui::RenderIntent>(HalDisplayId, ui::ColorMode)); - MOCK_METHOD2(getDataspaceSaturationMatrix, mat4(HalDisplayId, ui::Dataspace)); - MOCK_METHOD4(getDisplayedContentSamplingAttributes, - status_t(HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*)); - MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(HalDisplayId, bool, uint8_t, uint64_t)); - MOCK_METHOD4(getDisplayedContentSample, - status_t(HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*)); - MOCK_METHOD(ftl::Future<status_t>, setDisplayBrightness, - (PhysicalDisplayId, float, float, const Hwc2::Composer::DisplayBrightnessOptions&), - (override)); - MOCK_METHOD2(getDisplayBrightnessSupport, status_t(PhysicalDisplayId, bool*)); - - MOCK_METHOD2(onHotplug, - std::optional<DisplayIdentificationInfo>(hal::HWDisplayId, hal::Connection)); - MOCK_CONST_METHOD0(updatesDeviceProductInfoOnHotplugReconnect, bool()); - MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t)); - MOCK_METHOD2(setVsyncEnabled, void(PhysicalDisplayId, hal::Vsync)); - MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId)); - MOCK_CONST_METHOD2(getModes, - std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId, int32_t)); - MOCK_CONST_METHOD1(getActiveMode, ftl::Expected<hal::HWConfigId, status_t>(PhysicalDisplayId)); - MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId)); - MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent)); - MOCK_CONST_METHOD0(isUsingVrComposer, bool()); - MOCK_CONST_METHOD1(getDisplayConnectionType, ui::DisplayConnectionType(PhysicalDisplayId)); - MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId)); - MOCK_CONST_METHOD1(getDisplayVsyncPeriod, ftl::Expected<nsecs_t, status_t>(PhysicalDisplayId)); - MOCK_METHOD4(setActiveModeWithConstraints, - status_t(PhysicalDisplayId, hal::HWConfigId, - const hal::VsyncPeriodChangeConstraints&, - hal::VsyncPeriodChangeTimeline*)); - MOCK_METHOD2(setBootDisplayMode, status_t(PhysicalDisplayId, hal::HWConfigId)); - MOCK_METHOD1(clearBootDisplayMode, status_t(PhysicalDisplayId)); - MOCK_METHOD1(getPreferredBootDisplayMode, std::optional<hal::HWConfigId>(PhysicalDisplayId)); - MOCK_METHOD0(getBootDisplayModeSupport, bool()); - MOCK_CONST_METHOD0( - getHdrConversionCapabilities, - std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>()); - MOCK_METHOD2(setHdrConversionStrategy, - status_t(aidl::android::hardware::graphics::common::HdrConversionStrategy, - aidl::android::hardware::graphics::common::Hdr*)); - MOCK_METHOD2(setAutoLowLatencyMode, status_t(PhysicalDisplayId, bool)); - MOCK_METHOD(status_t, getSupportedContentTypes, - (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override)); - MOCK_METHOD2(setContentType, status_t(PhysicalDisplayId, hal::ContentType)); - MOCK_CONST_METHOD0(getSupportedLayerGenericMetadata, - const std::unordered_map<std::string, bool>&()); - - MOCK_CONST_METHOD1(dump, void(std::string&)); - MOCK_CONST_METHOD1(dumpOverlayProperties, void(std::string&)); - MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*()); - - MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override)); - MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override)); - MOCK_METHOD(bool, isHeadless, (), (const, override)); - - MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId), - (const, override)); - MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId), - (const, override)); - MOCK_METHOD2(getDisplayDecorationSupport, - status_t(PhysicalDisplayId, - std::optional<aidl::android::hardware::graphics::common:: - DisplayDecorationSupport>* support)); - MOCK_METHOD2(setIdleTimerEnabled, status_t(PhysicalDisplayId, std::chrono::milliseconds)); - MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override)); - MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId), - (const, override)); - MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override)); - MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&, - getOverlaySupport, (), (const, override)); - MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool)); - MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps)); - MOCK_METHOD((HWC2::Display::LutFileDescriptorMapper&), getLutFileDescriptorMapper, (), ()); -}; - -} // namespace mock -} // namespace android diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h deleted file mode 100644 index ed2ffa9df6..0000000000 --- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 <gmock/gmock.h> - -#include "DisplayHardware/PowerAdvisor.h" - -namespace android { -namespace Hwc2 { -namespace mock { - -class PowerAdvisor : public android::Hwc2::PowerAdvisor { -public: - PowerAdvisor(); - ~PowerAdvisor() override; - - MOCK_METHOD(void, init, (), (override)); - MOCK_METHOD(void, onBootFinished, (), (override)); - MOCK_METHOD(void, setExpensiveRenderingExpected, (DisplayId displayId, bool expected), - (override)); - MOCK_METHOD(bool, isUsingExpensiveRendering, (), (override)); - MOCK_METHOD(void, notifyCpuLoadUp, (), (override)); - MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override)); - MOCK_METHOD(bool, usePowerHintSession, (), (override)); - MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); - MOCK_METHOD(bool, supportsGpuReporting, (), (override)); - MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override)); - MOCK_METHOD(void, reportActualWorkDuration, (), (override)); - MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override)); - MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override)); - MOCK_METHOD(void, setGpuStartTime, (DisplayId displayId, TimePoint startTime), (override)); - MOCK_METHOD(void, setGpuFenceTime, - (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override)); - MOCK_METHOD(void, setHwcValidateTiming, - (DisplayId displayId, TimePoint validateStartTime, TimePoint validateEndTime), - (override)); - MOCK_METHOD(void, setHwcPresentTiming, - (DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime), - (override)); - MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override)); - MOCK_METHOD(void, setRequiresRenderEngine, (DisplayId displayId, bool requiresRenderEngine), - (override)); - MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override)); - MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime), - (override)); - MOCK_METHOD(void, setHwcPresentDelayedTime, - (DisplayId displayId, TimePoint earliestFrameStartTime)); - MOCK_METHOD(void, setFrameDelay, (Duration frameDelayDuration), (override)); - MOCK_METHOD(void, setCommitStart, (TimePoint commitStartTime), (override)); - MOCK_METHOD(void, setCompositeEnd, (TimePoint compositeEndTime), (override)); - MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override)); - MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override)); -}; - -} // namespace mock -} // namespace Hwc2 -} // namespace android diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index b21533a009..dbffe80a3a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <com_android_graphics_libgui_flags.h> #include <compositionengine/impl/HwcBufferCache.h> #include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> @@ -23,14 +24,14 @@ #include <compositionengine/mock/Output.h> #include <gtest/gtest.h> #include <log/log.h> - #include <renderengine/impl/ExternalTexture.h> #include <renderengine/mock/RenderEngine.h> +#include <ui/FloatRect.h> #include <ui/PixelFormat.h> -#include "MockHWC2.h" -#include "MockHWComposer.h" + #include "RegionMatcher.h" -#include "ui/FloatRect.h" +#include "mock/DisplayHardware/MockHWC2.h" +#include "mock/DisplayHardware/MockHWComposer.h" #include <aidl/android/hardware/graphics/composer3/Composition.h> @@ -1332,6 +1333,71 @@ TEST_F(OutputLayerWriteStateToHWCTest, setCompositionTypeRefreshRateIndicator) { /*zIsOverridden*/ false, /*isPeekingThrough*/ false); } +TEST_F(OutputLayerWriteStateToHWCTest, setsPictureProfileWhenCommitted) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Feature flag disabled, skipping"; + } + mLayerFEState.compositionType = Composition::DEVICE; + mLayerFEState.pictureProfileHandle = PictureProfileHandle(1); + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Composition::DEVICE); + + EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))); + + mOutputLayer.commitPictureProfileToCompositionState(); + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommitted) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Feature flag disabled, skipping"; + } + mLayerFEState.compositionType = Composition::DEVICE; + mLayerFEState.pictureProfileHandle = PictureProfileHandle(1); + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Composition::DEVICE); + + EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(_)).Times(0); + + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, doesNotSetPictureProfileWhenNotCommittedLater) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Feature flag disabled, skipping"; + } + mLayerFEState.compositionType = Composition::DEVICE; + mLayerFEState.pictureProfileHandle = PictureProfileHandle(1); + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Composition::DEVICE); + + EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))); + + mOutputLayer.commitPictureProfileToCompositionState(); + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(kExpectedHwcSlot, nullptr, kFence); + + EXPECT_CALL(*mHwcLayer, setPictureProfileHandle(PictureProfileHandle(1))).Times(0); + // No committing of picture profile before writing the state + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); +} + /* * OutputLayer::uncacheBuffers */ diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 1c18cd28c5..99e68eb71a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -15,6 +15,7 @@ */ #include <android-base/stringprintf.h> +#include <com_android_graphics_libgui_flags.h> #include <com_android_graphics_surfaceflinger_flags.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/impl/Output.h> @@ -37,11 +38,14 @@ #include <cstdint> #include <variant> +#include <com_android_graphics_surfaceflinger_flags.h> + #include <common/FlagManager.h> #include <common/test/FlagUtils.h> #include "CallOrderStateMachineHelper.h" -#include "MockHWC2.h" #include "RegionMatcher.h" +#include "mock/DisplayHardware/MockHWC2.h" +#include "mock/DisplayHardware/MockHWComposer.h" namespace android::compositionengine { namespace { @@ -142,6 +146,24 @@ struct OutputTest : public testing::Test { public: using impl::Output::injectOutputLayerForTest; virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0; + + virtual ftl::Optional<DisplayId> getDisplayId() const override { return mId; } + + virtual bool hasPictureProcessing() const override { return mHasPictureProcessing; } + virtual int32_t getMaxLayerPictureProfiles() const override { + return mMaxLayerPictureProfiles; + } + + void setDisplayIdForTest(DisplayId value) { mId = value; } + + void setHasPictureProcessingForTest(bool value) { mHasPictureProcessing = value; } + + void setMaxLayerPictureProfilesForTest(int32_t value) { mMaxLayerPictureProfiles = value; } + + private: + ftl::Optional<DisplayId> mId; + bool mHasPictureProcessing; + int32_t mMaxLayerPictureProfiles; }; static std::shared_ptr<Output> createOutput( @@ -157,6 +179,7 @@ struct OutputTest : public testing::Test { mOutput->editState().displaySpace.setBounds( ui::Size(kDefaultDisplaySize.getWidth(), kDefaultDisplaySize.getHeight())); EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); + EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer)); } void injectOutputLayer(InjectedLayer& layer) { @@ -169,6 +192,7 @@ struct OutputTest : public testing::Test { static const Rect kDefaultDisplaySize; + StrictMock<::android::mock::HWComposer> mHwComposer; StrictMock<mock::CompositionEngine> mCompositionEngine; StrictMock<renderengine::mock::RenderEngine> mRenderEngine; mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>(); @@ -785,17 +809,20 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers InjectedLayer layer3; uint32_t z = 0; - EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180)); + EXPECT_CALL(*layer1.outputLayer, + updateCompositionState(false, false, ui::Transform::ROT_180, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180)); + EXPECT_CALL(*layer2.outputLayer, + updateCompositionState(false, false, ui::Transform::ROT_180, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_180)); + EXPECT_CALL(*layer3.outputLayer, + updateCompositionState(false, false, ui::Transform::ROT_180, _)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); @@ -822,17 +849,17 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerGeometryAndContentF InjectedLayer layer3; uint32_t z = 0; - EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); @@ -858,17 +885,17 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLa InjectedLayer layer3; uint32_t z = 0; - EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); @@ -896,11 +923,11 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, peekThroughLayerChangesOrder) { InjectedLayer layer3; InSequence seq; - EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); - EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer0.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _)); + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); - EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(true, false, ui::Transform::ROT_0, _)); uint32_t z = 0; EXPECT_CALL(*layer0.outputLayer, @@ -4932,12 +4959,12 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, noBackgroundBlurWhenOpaque) { uint32_t z = 0; // Layer requesting blur, or below, should request client composition, unless opaque. - EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); @@ -4966,17 +4993,17 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBackgroundBlurRequests) uint32_t z = 0; // Layer requesting blur, or below, should request client composition. - EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); @@ -5006,17 +5033,17 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) { uint32_t z = 0; // Layer requesting blur, or below, should request client composition. - EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(false, true, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); - EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(false, false, ui::Transform::ROT_0, _)); EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, z++, /*zIsOverridden*/ false, /*isPeekingThrough*/ false)); @@ -5040,6 +5067,123 @@ TEST_F(OutputUpdateAndWriteCompositionStateTest, handlesBlurRegionRequests) { mOutput->writeCompositionState(args); } +TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsDisplayProfileBasedOnLayerPriority) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Feature flag disabled, skipping"; + } + + mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1)); + // Has only one display-global picture processing pipeline + mOutput->setHasPictureProcessingForTest(true); + mOutput->setMaxLayerPictureProfilesForTest(0); + + InjectedLayer layer1; + injectOutputLayer(layer1); + PictureProfileHandle profileForLayer1(1); + EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3)); + EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer1)); + + InjectedLayer layer2; + injectOutputLayer(layer2); + PictureProfileHandle profileForLayer2(2); + EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1)); + EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer2)); + + InjectedLayer layer3; + injectOutputLayer(layer3); + PictureProfileHandle profileForLayer3(3); + EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2)); + EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer3)); + + // Because StrictMock + EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _)); + + // No layer picture profiles should be committed + EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0); + EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState).Times(0); + EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState).Times(0); + + // Sets display picture profile to the highest priority layer's profile + EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle(_, Eq(profileForLayer2))); + + mOutput->editState().isEnabled = true; + CompositionRefreshArgs args; + args.updatingGeometryThisFrame = false; + args.devOptForceClientComposition = false; + mOutput->updateCompositionState(args); + mOutput->planComposition(); + mOutput->writeCompositionState(args); +} + +TEST_F(OutputUpdateAndWriteCompositionStateTest, assignsLayerProfileBasedOnLayerPriority) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Feature flag disabled, skipping"; + } + mOutput->setDisplayIdForTest(PhysicalDisplayId::fromPort(1)); + // Has 2 layer-specific picture processing pipelines + mOutput->setHasPictureProcessingForTest(true); + mOutput->setMaxLayerPictureProfilesForTest(2); + + InjectedLayer layer1; + injectOutputLayer(layer1); + PictureProfileHandle profileForLayer1(1); + EXPECT_CALL(*layer1.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(3)); + EXPECT_CALL(*layer1.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer1)); + + InjectedLayer layer2; + injectOutputLayer(layer2); + PictureProfileHandle profileForLayer2(2); + EXPECT_CALL(*layer2.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(1)); + EXPECT_CALL(*layer2.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer2)); + + InjectedLayer layer3; + injectOutputLayer(layer3); + PictureProfileHandle profileForLayer3(3); + EXPECT_CALL(*layer3.outputLayer, getPictureProfilePriority()).WillRepeatedly(Return(2)); + EXPECT_CALL(*layer3.outputLayer, getPictureProfileHandle()) + .WillRepeatedly(ReturnRef(profileForLayer3)); + + // Because StrictMock + EXPECT_CALL(*layer1.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer1.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer1.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer2.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer2.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer2.outputLayer, writeStateToHWC(_, _, _, _, _)); + EXPECT_CALL(*layer3.outputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(*layer3.outputLayer, updateCompositionState(_, _, _, _)); + EXPECT_CALL(*layer3.outputLayer, writeStateToHWC(_, _, _, _, _)); + + // The two highest priority layers should have their picture profiles committed + EXPECT_CALL(*layer1.outputLayer, commitPictureProfileToCompositionState).Times(0); + EXPECT_CALL(*layer2.outputLayer, commitPictureProfileToCompositionState); + EXPECT_CALL(*layer3.outputLayer, commitPictureProfileToCompositionState); + + // No display picture profile is sent + EXPECT_CALL(mHwComposer, setDisplayPictureProfileHandle).Times(0); + + mOutput->editState().isEnabled = true; + CompositionRefreshArgs args; + args.updatingGeometryThisFrame = false; + args.devOptForceClientComposition = false; + mOutput->updateCompositionState(args); + mOutput->planComposition(); + mOutput->writeCompositionState(args); +} + TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) { // In split-screen landscape mode, the screen is rotated 90 degrees, with // one layer on the left covering the left side of the output, and one layer diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 402a3d2e2f..c743ea2ff4 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -201,6 +201,10 @@ bool DisplayDevice::isPoweredOn() const { return mPowerMode != hal::PowerMode::OFF; } +bool DisplayDevice::isRefreshable() const { + return mPowerMode == hal::PowerMode::DOZE || mPowerMode == hal::PowerMode::ON; +} + ui::Dataspace DisplayDevice::getCompositionDataSpace() const { return mCompositionDisplay->getState().dataspace; } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 3e3f558cee..af2b48fc07 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -42,7 +42,6 @@ #include "DisplayHardware/DisplayMode.h" #include "DisplayHardware/Hal.h" -#include "DisplayHardware/PowerAdvisor.h" #include "FrontEnd/DisplayInfo.h" #include "Scheduler/RefreshRateSelector.h" #include "ThreadContext.h" @@ -173,6 +172,7 @@ public: hardware::graphics::composer::hal::PowerMode getPowerMode() const; void setPowerMode(hardware::graphics::composer::hal::PowerMode); bool isPoweredOn() const; + bool isRefreshable() const; void tracePowerMode(); // Enables layer caching on this DisplayDevice @@ -285,6 +285,8 @@ struct DisplayDeviceState { bool isProtected = false; // Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only Fps requestedRefreshRate; + int32_t maxLayerPictureProfiles = 0; + bool hasPictureProcessing = false; private: static std::atomic<int32_t> sNextSequenceId; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 77bd8040c5..4c8ff58530 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -44,12 +44,11 @@ using hardware::Return; using aidl::android::hardware::graphics::composer3::BnComposerCallback; using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness; -using aidl::android::hardware::graphics::composer3::Lut; +using aidl::android::hardware::graphics::composer3::CommandResultPayload; +using aidl::android::hardware::graphics::composer3::Luts; using aidl::android::hardware::graphics::composer3::PowerMode; using aidl::android::hardware::graphics::composer3::VirtualDisplay; -using aidl::android::hardware::graphics::composer3::CommandResultPayload; - using AidlColorMode = aidl::android::hardware::graphics::composer3::ColorMode; using AidlContentType = aidl::android::hardware::graphics::composer3::ContentType; using AidlDisplayIdentification = @@ -1565,7 +1564,7 @@ Error AidlComposer::getRequestedLuts(Display display, std::vector<Layer>* outLay return error; } -Error AidlComposer::setLayerLuts(Display display, Layer layer, std::vector<Lut>& luts) { +Error AidlComposer::setLayerLuts(Display display, Layer layer, Luts& luts) { Error error = Error::NONE; mMutex.lock_shared(); if (auto writer = getWriter(display)) { @@ -1639,6 +1638,41 @@ Error AidlComposer::getPhysicalDisplayOrientation(Display displayId, return Error::NONE; } +Error AidlComposer::getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) { + const auto status = mAidlComposerClient->getMaxLayerPictureProfiles(translate<int64_t>(display), + outMaxProfiles); + if (!status.isOk()) { + ALOGE("getMaxLayerPictureProfiles failed %s", status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + return Error::NONE; +} + +Error AidlComposer::setDisplayPictureProfileId(Display display, PictureProfileId id) { + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setDisplayPictureProfileId(translate<int64_t>(display), id); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; +} + +Error AidlComposer::setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) { + Error error = Error::NONE; + mMutex.lock_shared(); + if (auto writer = getWriter(display)) { + writer->get().setLayerPictureProfileId(translate<int64_t>(display), + translate<int64_t>(layer), id); + } else { + error = Error::BAD_DISPLAY; + } + mMutex.unlock_shared(); + return error; +} + ftl::Optional<std::reference_wrapper<ComposerClientWriter>> AidlComposer::getWriter(Display display) REQUIRES_SHARED(mMutex) { return mWriters.get(display); diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index cdb67e4e5c..933e8d186f 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -53,6 +53,7 @@ using aidl::android::hardware::graphics::common::HdrConversionCapability; using aidl::android::hardware::graphics::common::HdrConversionStrategy; using aidl::android::hardware::graphics::composer3::ComposerClientReader; using aidl::android::hardware::graphics::composer3::ComposerClientWriter; +using aidl::android::hardware::graphics::composer3::Luts; using aidl::android::hardware::graphics::composer3::OverlayProperties; class AidlIComposerCallbackWrapper; @@ -248,9 +249,10 @@ public: Display display, std::vector<Layer>* outLayers, std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* outLuts) override; - Error setLayerLuts( - Display display, Layer layer, - std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override; + Error setLayerLuts(Display display, Layer layer, Luts& luts) override; + Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override; + Error setDisplayPictureProfileId(Display, PictureProfileId id) override; + Error setLayerPictureProfileId(Display, Layer, PictureProfileId id) override; private: // Many public functions above simply write a command into the command diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 09056631d0..c1333c29b5 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -29,11 +29,13 @@ #include <math/mat4.h> #include <ui/DisplayedFrameStats.h> #include <ui/GraphicBuffer.h> +#include <ui/PictureProfileHandle.h> #include <utils/StrongPointer.h> #include <aidl/android/hardware/graphics/common/DisplayDecorationSupport.h> #include <aidl/android/hardware/graphics/common/HdrConversionCapability.h> #include <aidl/android/hardware/graphics/common/HdrConversionStrategy.h> +#include <aidl/android/hardware/graphics/common/Transform.h> #include <aidl/android/hardware/graphics/composer3/Capability.h> #include <aidl/android/hardware/graphics/composer3/ClientTargetPropertyWithBrightness.h> #include <aidl/android/hardware/graphics/composer3/Color.h> @@ -42,10 +44,8 @@ #include <aidl/android/hardware/graphics/composer3/DisplayConfiguration.h> #include <aidl/android/hardware/graphics/composer3/DisplayLuts.h> #include <aidl/android/hardware/graphics/composer3/IComposerCallback.h> -#include <aidl/android/hardware/graphics/composer3/Lut.h> #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> -#include <aidl/android/hardware/graphics/common/Transform.h> #include <optional> // TODO(b/129481165): remove the #pragma below and fix conversion issues @@ -307,7 +307,10 @@ public: int32_t frameIntervalNs) = 0; virtual Error getRequestedLuts(Display display, std::vector<Layer>* outLayers, std::vector<V3_0::DisplayLuts::LayerLut>* outLuts) = 0; - virtual Error setLayerLuts(Display display, Layer layer, std::vector<V3_0::Lut>& luts) = 0; + virtual Error setLayerLuts(Display display, Layer layer, V3_0::Luts& luts) = 0; + virtual Error getMaxLayerPictureProfiles(Display display, int32_t* outMaxProfiles) = 0; + virtual Error setDisplayPictureProfileId(Display display, PictureProfileId id) = 0; + virtual Error setLayerPictureProfileId(Display display, Layer layer, PictureProfileId id) = 0; }; } // namespace Hwc2 diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 1df2ab12ce..a274995eec 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -31,6 +31,7 @@ #include <ui/Fence.h> #include <ui/FloatRect.h> #include <ui/GraphicBuffer.h> +#include <ui/PictureProfileHandle.h> #include <algorithm> #include <cinttypes> @@ -42,7 +43,8 @@ using aidl::android::hardware::graphics::composer3::Composition; using AidlCapability = aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; using aidl::android::hardware::graphics::composer3::DisplayLuts; -using aidl::android::hardware::graphics::composer3::Lut; +using aidl::android::hardware::graphics::composer3::LutProperties; +using aidl::android::hardware::graphics::composer3::Luts; using aidl::android::hardware::graphics::composer3::OverlayProperties; namespace android { @@ -52,6 +54,7 @@ using android::FloatRect; using android::GraphicBuffer; using android::HdrCapabilities; using android::HdrMetadata; +using android::PictureProfileHandle; using android::Rect; using android::Region; using android::sp; @@ -624,10 +627,18 @@ Error Display::getRequestedLuts(LayerLuts* outLuts, auto layer = getLayerById(layerIds[i]); if (layer) { auto& layerLut = tmpLuts[i]; - outLuts->emplace_or_replace(layer.get(), layerLut.lut.lutProperties); - lutFileDescriptorMapper.emplace_or_replace(layer.get(), - ndk::ScopedFileDescriptor( - layerLut.lut.pfd.release())); + if (layerLut.luts.pfd.get() > 0 && layerLut.luts.offsets.has_value()) { + const auto& offsets = layerLut.luts.offsets.value(); + std::vector<std::pair<int32_t, LutProperties>> lutOffsetsAndProperties; + lutOffsetsAndProperties.reserve(offsets.size()); + std::transform(offsets.begin(), offsets.end(), layerLut.luts.lutProperties.begin(), + std::back_inserter(lutOffsetsAndProperties), + [](int32_t i, LutProperties j) { return std::make_pair(i, j); }); + outLuts->emplace_or_replace(layer.get(), lutOffsetsAndProperties); + lutFileDescriptorMapper.emplace_or_replace(layer.get(), + ndk::ScopedFileDescriptor( + layerLut.luts.pfd.release())); + } } } @@ -646,6 +657,16 @@ Error Display::setIdleTimerEnabled(std::chrono::milliseconds timeout) { return static_cast<Error>(error); } +Error Display::getMaxLayerPictureProfiles(int32_t* outMaxProfiles) { + const auto error = mComposer.getMaxLayerPictureProfiles(mId, outMaxProfiles); + return static_cast<Error>(error); +} + +Error Display::setPictureProfileHandle(const PictureProfileHandle& handle) { + const auto error = mComposer.setDisplayPictureProfileId(mId, handle.getId()); + return static_cast<Error>(error); +} + // For use by Device void Display::setConnected(bool connected) { @@ -1069,7 +1090,7 @@ Error Layer::setBlockingRegion(const Region& region) { return static_cast<Error>(intError); } -Error Layer::setLuts(std::vector<Lut>& luts) { +Error Layer::setLuts(aidl::android::hardware::graphics::composer3::Luts& luts) { if (CC_UNLIKELY(!mDisplay)) { return Error::BAD_DISPLAY; } @@ -1077,6 +1098,15 @@ Error Layer::setLuts(std::vector<Lut>& luts) { return static_cast<Error>(intError); } +Error Layer::setPictureProfileHandle(const PictureProfileHandle& handle) { + if (CC_UNLIKELY(!mDisplay)) { + return Error::BAD_DISPLAY; + } + const auto intError = + mComposer.setLayerPictureProfileId(mDisplay->getId(), mId, handle.getId()); + return static_cast<Error>(intError); +} + } // namespace impl } // namespace HWC2 } // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index 61f92f4d74..6740d8a832 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -24,6 +24,7 @@ #include <gui/HdrMetadata.h> #include <math/mat4.h> #include <ui/HdrCapabilities.h> +#include <ui/PictureProfileHandle.h> #include <ui/Region.h> #include <ui/StaticDisplayInfo.h> #include <utils/Log.h> @@ -46,7 +47,7 @@ #include <aidl/android/hardware/graphics/composer3/Color.h> #include <aidl/android/hardware/graphics/composer3/Composition.h> #include <aidl/android/hardware/graphics/composer3/DisplayCapability.h> -#include <aidl/android/hardware/graphics/composer3/Lut.h> +#include <aidl/android/hardware/graphics/composer3/Luts.h> #include <aidl/android/hardware/graphics/composer3/OverlayProperties.h> #include <aidl/android/hardware/graphics/composer3/RefreshRateChangedDebugData.h> @@ -109,9 +110,10 @@ public: virtual std::optional<ui::Size> getPhysicalSizeInMm() const = 0; static const int kLutFileDescriptorMapperSize = 20; + using LutOffsetAndProperties = std::vector< + std::pair<int32_t, aidl::android::hardware::graphics::composer3::LutProperties>>; using LayerLuts = - ftl::SmallMap<HWC2::Layer*, aidl::android::hardware::graphics::composer3::LutProperties, - kLutFileDescriptorMapperSize>; + ftl::SmallMap<HWC2::Layer*, LutOffsetAndProperties, kLutFileDescriptorMapperSize>; using LutFileDescriptorMapper = ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, kLutFileDescriptorMapperSize>; @@ -198,6 +200,9 @@ public: [[nodiscard]] virtual hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) = 0; [[nodiscard]] virtual hal::Error getPhysicalDisplayOrientation( Hwc2::AidlTransform* outTransform) const = 0; + [[nodiscard]] virtual hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) = 0; + [[nodiscard]] virtual hal::Error setPictureProfileHandle( + const PictureProfileHandle& handle) = 0; }; namespace impl { @@ -281,6 +286,8 @@ public: std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* support) override; hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) override; + hal::Error getMaxLayerPictureProfiles(int32_t* maxProfiles) override; + hal::Error setPictureProfileHandle(const android::PictureProfileHandle& handle) override; // Other Display methods hal::HWDisplayId getId() const override { return mId; } @@ -375,7 +382,9 @@ public: [[nodiscard]] virtual hal::Error setBrightness(float brightness) = 0; [[nodiscard]] virtual hal::Error setBlockingRegion(const android::Region& region) = 0; [[nodiscard]] virtual hal::Error setLuts( - std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) = 0; + aidl::android::hardware::graphics::composer3::Luts& luts) = 0; + [[nodiscard]] virtual hal::Error setPictureProfileHandle( + const PictureProfileHandle& handle) = 0; }; namespace impl { @@ -426,8 +435,8 @@ public: // AIDL HAL hal::Error setBrightness(float brightness) override; hal::Error setBlockingRegion(const android::Region& region) override; - hal::Error setLuts( - std::vector<aidl::android::hardware::graphics::composer3::Lut>& luts) override; + hal::Error setLuts(aidl::android::hardware::graphics::composer3::Luts&) override; + hal::Error setPictureProfileHandle(const PictureProfileHandle& handle) override; private: // These are references to data owned by HWComposer, which will outlive @@ -449,6 +458,7 @@ private: android::HdrMetadata mHdrMetadata; android::mat4 mColorMatrix; uint32_t mBufferSlot; + android::PictureProfileHandle profile; }; } // namespace impl diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 7d77634722..61d4541e77 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -1022,6 +1022,24 @@ status_t HWComposer::setContentType(PhysicalDisplayId displayId, hal::ContentTyp return NO_ERROR; } +int32_t HWComposer::getMaxLayerPictureProfiles(PhysicalDisplayId displayId) { + int32_t maxProfiles = 0; + RETURN_IF_INVALID_DISPLAY(displayId, 0); + const auto error = mDisplayData[displayId].hwcDisplay->getMaxLayerPictureProfiles(&maxProfiles); + RETURN_IF_HWC_ERROR(error, displayId, 0); + return maxProfiles; +} + +status_t HWComposer::setDisplayPictureProfileHandle(PhysicalDisplayId displayId, + const PictureProfileHandle& handle) { + RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + const auto error = mDisplayData[displayId].hwcDisplay->setPictureProfileHandle(handle); + if (error != hal::Error::UNSUPPORTED) { + RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); + } + return NO_ERROR; +} + const std::unordered_map<std::string, bool>& HWComposer::getSupportedLayerGenericMetadata() const { return mSupportedLayerGenericMetadata; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 7b04d6755a..e21ce1d095 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -29,6 +29,7 @@ #include <ftl/future.h> #include <ui/DisplayIdentification.h> #include <ui/FenceTime.h> +#include <ui/PictureProfileHandle.h> // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic push @@ -65,6 +66,7 @@ class GraphicBuffer; class TestableSurfaceFlinger; struct HWComposerTest; struct CompositionInfo; +class PictureProfileHandle; namespace Hwc2 { class Composer; @@ -296,7 +298,7 @@ public: virtual std::optional<PhysicalDisplayId> toPhysicalDisplayId(hal::HWDisplayId) const = 0; virtual std::optional<hal::HWDisplayId> fromPhysicalDisplayId(PhysicalDisplayId) const = 0; - // Composer 3.0 + // AIDL Composer virtual status_t setBootDisplayMode(PhysicalDisplayId, hal::HWConfigId) = 0; virtual status_t clearBootDisplayMode(PhysicalDisplayId) = 0; virtual std::optional<hal::HWConfigId> getPreferredBootDisplayMode(PhysicalDisplayId) = 0; @@ -315,8 +317,10 @@ public: virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) = 0; - // mapper virtual HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() = 0; + virtual int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) = 0; + virtual status_t setDisplayPictureProfileHandle(PhysicalDisplayId, + const PictureProfileHandle& handle) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -480,9 +484,10 @@ public: status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override; status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime, Fps frameInterval) override; - - // get a mapper HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() override; + int32_t getMaxLayerPictureProfiles(PhysicalDisplayId) override; + status_t setDisplayPictureProfileHandle(PhysicalDisplayId, + const android::PictureProfileHandle& profile) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index 056ecd78f4..e359a26f16 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -47,7 +47,7 @@ using aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrig using aidl::android::hardware::graphics::composer3::DimmingStage; using aidl::android::hardware::graphics::composer3::DisplayCapability; using aidl::android::hardware::graphics::composer3::DisplayLuts; -using aidl::android::hardware::graphics::composer3::Lut; +using aidl::android::hardware::graphics::composer3::Luts; using aidl::android::hardware::graphics::composer3::OverlayProperties; namespace android { @@ -1415,7 +1415,7 @@ Error HidlComposer::getRequestedLuts(Display, std::vector<Layer>*, return Error::NONE; } -Error HidlComposer::setLayerLuts(Display, Layer, std::vector<Lut>&) { +Error HidlComposer::setLayerLuts(Display, Layer, Luts&) { return Error::NONE; } @@ -1446,6 +1446,18 @@ Error HidlComposer::getPhysicalDisplayOrientation(Display, AidlTransform*) { "OptionalFeature::PhysicalDisplayOrientation is not supported on HIDL"); } +Error HidlComposer::getMaxLayerPictureProfiles(Display, int32_t*) { + return Error::UNSUPPORTED; +} + +Error HidlComposer::setDisplayPictureProfileId(Display, PictureProfileId) { + return Error::UNSUPPORTED; +} + +Error HidlComposer::setLayerPictureProfileId(Display, Layer, PictureProfileId) { + return Error::UNSUPPORTED; +} + void HidlComposer::registerCallback(ComposerCallback& callback) { const bool vsyncSwitchingSupported = isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching); diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index 1cc23d1409..9a89dbaa9e 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -356,7 +356,10 @@ public: std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*) override; Error setLayerLuts(Display, Layer, - std::vector<aidl::android::hardware::graphics::composer3::Lut>&) override; + aidl::android::hardware::graphics::composer3::Luts&) override; + Error getMaxLayerPictureProfiles(Display, int32_t* outMaxProfiles) override; + Error setDisplayPictureProfileId(Display, PictureProfileId) override; + Error setLayerPictureProfileId(Display, Layer, PictureProfileId) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index 47b811b721..c13e444a99 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -378,6 +378,11 @@ void SurfaceFrame::setAcquireFenceTime(nsecs_t acquireFenceTime) { } } +void SurfaceFrame::setDesiredPresentTime(nsecs_t desiredPresentTime) { + std::scoped_lock lock(mMutex); + mActuals.desiredPresentTime = desiredPresentTime; +} + void SurfaceFrame::setDropTime(nsecs_t dropTime) { std::scoped_lock lock(mMutex); mDropTime = dropTime; @@ -1456,6 +1461,30 @@ float FrameTimeline::computeFps(const std::unordered_set<int32_t>& layerIds) { static_cast<float>(totalPresentToPresentWalls); } +void FrameTimeline::generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const { + std::scoped_lock lock(mMutex); + + // TODO: Include FPS calculation here + for (auto displayFrame : mDisplayFrames) { + if (!count--) { + break; + } + + if (displayFrame->getActuals().presentTime <= 0) { + continue; + } + + for (const auto& surfaceFrame : displayFrame->getSurfaceFrames()) { + if (surfaceFrame->getLayerId() == layer) { + outStats->actualPresentTimesNano.push_back(surfaceFrame->getActuals().presentTime); + outStats->desiredPresentTimesNano.push_back( + surfaceFrame->getActuals().desiredPresentTime); + outStats->frameReadyTimesNano.push_back(surfaceFrame->getActuals().endTime); + } + } + } +} + std::optional<size_t> FrameTimeline::getFirstSignalFenceIndex() const { for (size_t i = 0; i < mPendingPresentFences.size(); i++) { const auto& [fence, _] = mPendingPresentFences[i]; diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index cffb61ee10..6cda309440 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -85,16 +85,20 @@ enum class FrameStartMetadata : int8_t { */ struct TimelineItem { TimelineItem(const nsecs_t startTime = 0, const nsecs_t endTime = 0, - const nsecs_t presentTime = 0) - : startTime(startTime), endTime(endTime), presentTime(presentTime) {} + const nsecs_t presentTime = 0, const nsecs_t desiredPresentTime = 0) + : startTime(startTime), + endTime(endTime), + presentTime(presentTime), + desiredPresentTime(desiredPresentTime) {} nsecs_t startTime; nsecs_t endTime; nsecs_t presentTime; + nsecs_t desiredPresentTime; bool operator==(const TimelineItem& other) const { return startTime == other.startTime && endTime == other.endTime && - presentTime == other.presentTime; + presentTime == other.presentTime && desiredPresentTime != other.desiredPresentTime; } bool operator!=(const TimelineItem& other) const { return !(*this == other); } @@ -183,6 +187,7 @@ public: void setActualStartTime(nsecs_t actualStartTime); void setActualQueueTime(nsecs_t actualQueueTime); void setAcquireFenceTime(nsecs_t acquireFenceTime); + void setDesiredPresentTime(nsecs_t desiredPresentTime); void setDropTime(nsecs_t dropTime); void setPresentState(PresentState presentState, nsecs_t lastLatchTime = 0); void setRenderRate(Fps renderRate); @@ -341,6 +346,9 @@ public: // containing at least one layer ID. virtual float computeFps(const std::unordered_set<int32_t>& layerIds) = 0; + // Supports the legacy FrameStats interface + virtual void generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const = 0; + // Restores the max number of display frames to default. Called by SF backdoor. virtual void reset() = 0; }; @@ -501,6 +509,7 @@ public: void parseArgs(const Vector<String16>& args, std::string& result) override; void setMaxDisplayFrames(uint32_t size) override; float computeFps(const std::unordered_set<int32_t>& layerIds) override; + void generateFrameStats(int32_t layer, size_t count, FrameStats* outStats) const override; void reset() override; // Sets up the perfetto tracing backend and data source. diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp index d709530990..da536b6660 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp @@ -166,7 +166,8 @@ void LayerHierarchy::dump(std::ostream& out, const std::string& prefix, } out << "(Mirroring) "; } - out << *mLayer; + + out << *mLayer << " pid=" << mLayer->ownerPid.val() << " uid=" << mLayer->ownerUid.val(); } for (size_t i = 0; i < mChildren.size(); i++) { diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index e5f6b7bcd1..58f6b96e57 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -250,6 +250,7 @@ std::string LayerSnapshot::getIsVisibleReason() const { if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length; if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius; if (blurRegions.size() > 0) reason << " blurRegions.size()=" << blurRegions.size(); + if (contentDirty) reason << " contentDirty"; return reason.str(); } @@ -359,8 +360,9 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate uint32_t displayRotationFlags) { clientChanges = requested.what; changes = requested.changes; - contentDirty = requested.what & layer_state_t::CONTENT_DIRTY; - hasReadyFrame = requested.autoRefresh; + autoRefresh = requested.autoRefresh; + contentDirty = requested.what & layer_state_t::CONTENT_DIRTY || autoRefresh; + hasReadyFrame = autoRefresh; sidebandStreamHasFrame = requested.hasSidebandStreamFrame(); updateSurfaceDamage(requested, requested.hasReadyFrame(), forceFullDamage, surfaceDamage); @@ -411,6 +413,13 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate if (forceUpdate || requested.what & layer_state_t::eCropChanged) { geomCrop = requested.crop; } + if (forceUpdate || requested.what & layer_state_t::ePictureProfileHandleChanged) { + pictureProfileHandle = requested.pictureProfileHandle; + } + if (forceUpdate || requested.what & layer_state_t::eAppContentPriorityChanged) { + // TODO(b/337330263): Also consider the system-determined priority of the app + pictureProfilePriority = requested.appContentPriority; + } if (forceUpdate || requested.what & layer_state_t::eDefaultFrameRateCompatibilityChanged) { const auto compatibility = @@ -513,6 +522,10 @@ void LayerSnapshot::merge(const RequestedLayerState& requested, bool forceUpdate isOpaque = contentOpaque && !roundedCorner.hasRoundedCorners() && color.a == 1.f; blendMode = getBlendMode(requested); } + + if (forceUpdate || requested.what & layer_state_t::eLutsChanged) { + luts = requested.luts; + } } } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h index b7d4cc5d06..b8df3ed748 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h @@ -77,6 +77,7 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { gui::LayerMetadata layerMetadata; gui::LayerMetadata relativeLayerMetadata; bool hasReadyFrame; // used in post composition to check if there is another frame ready + bool autoRefresh; ui::Transform localTransformInverse; gui::WindowInfo inputInfo; ui::Transform localTransform; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 10e212e7b5..7569c1b8de 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -314,8 +314,8 @@ void updateMetadataAndGameMode(LayerSnapshot& snapshot, const RequestedLayerStat void clearChanges(LayerSnapshot& snapshot) { snapshot.changes.clear(); snapshot.clientChanges = 0; - snapshot.contentDirty = false; - snapshot.hasReadyFrame = false; + snapshot.contentDirty = snapshot.autoRefresh; + snapshot.hasReadyFrame = snapshot.autoRefresh; snapshot.sidebandStreamHasFrame = false; snapshot.surfaceDamage.clear(); } @@ -724,10 +724,12 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a if (args.displayChanges) snapshot.changes |= RequestedLayerState::Changes::Geometry; snapshot.reachablilty = LayerSnapshot::Reachablilty::Reachable; snapshot.clientChanges |= (parentSnapshot.clientChanges & layer_state_t::AFFECTS_CHILDREN); + // mark the content as dirty if the parent state changes can dirty the child's content (for + // example alpha) + snapshot.contentDirty |= (snapshot.clientChanges & layer_state_t::CONTENT_DIRTY) != 0; snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent || parentSnapshot.invalidTransform || requested.isHiddenByPolicy() || (args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end()); - const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL || snapshot.clientChanges & layer_state_t::eReparent || snapshot.changes.any(RequestedLayerState::Changes::Visibility | diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 713a5c509e..ee9302b937 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -163,7 +163,7 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta uint64_t clientChanges = what | layer_state_t::diff(clientState); layer_state_t::merge(clientState); what = clientChanges; - LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges); + LLOGV(layerId, "requested=%" PRIu64 " flags=%" PRIu64 " ", clientState.what, clientChanges); if (clientState.what & layer_state_t::eFlagsChanged) { if ((oldFlags ^ flags) & @@ -633,7 +633,7 @@ bool RequestedLayerState::isSimpleBufferUpdate(const layer_state_t& s) const { layer_state_t::eEdgeExtensionChanged | layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged | layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged | - layer_state_t::eDesiredHdrHeadroomChanged | + layer_state_t::eDesiredHdrHeadroomChanged | layer_state_t::eLutsChanged | (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed() ? layer_state_t::eFlagsChanged : 0); diff --git a/services/surfaceflinger/FrontEnd/readme.md b/services/surfaceflinger/FrontEnd/readme.md index e5f51a5773..6258f7e530 100644 --- a/services/surfaceflinger/FrontEnd/readme.md +++ b/services/surfaceflinger/FrontEnd/readme.md @@ -17,6 +17,29 @@ maintain policies at different levels without needing to understand the entire h This allows control to be delegated to different parts of the system - such as SystemServer, SysUI and Apps. +### Layer Drawing Order +Layers are drawn based on an inorder traversal, treating relative parents as +direct parents. Negative z-values place layers below their parent, while +non-negative values place them above. Layers with the same z-value are drawn +in creation order (newer on top). However, relying on creation order for +z-ordering is discouraged; use unique z-values whenever possible for better +control. + +Traversal pseudo code: +``` +fn traverseBottomToTop(root): + for each child node including relative children, + sorted by z then layer id, with z less than 0: + traverseBottomToTop(childNode) + + visit(root) + + for each child node including relative children, + sorted by z then layer id, with z greater than + or equal to 0: + traverseBottomToTop(childNode) +``` + ### Layer Lifecycle Layer is created by a client. The client receives a strong binder reference to the layer handle, which will keep the layer alive as long as the client holds the reference. The diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index c88092b23f..195461f47e 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -154,7 +154,7 @@ Layer::Layer(const surfaceflinger::LayerCreationArgs& args) mDrawingState.metadata = args.metadata; mDrawingState.frameTimelineInfo = {}; mDrawingState.postTime = -1; - mFrameTracker.setDisplayRefreshPeriod( + mDeprecatedFrameTracker.setDisplayRefreshPeriod( args.flinger->mScheduler->getPacesetterVsyncPeriod().ns()); mOwnerUid = args.ownerUid; @@ -472,6 +472,9 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransac getSequence(), mName, mTransactionName, /*isBuffer*/ false, gameMode); + // Buffer hasn't yet been latched, so use mDrawingState + surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime); + surfaceFrame->setActualStartTime(info.startTimeNanos); // For Transactions, the post time is considered to be both queue and acquire fence time. surfaceFrame->setActualQueueTime(postTime); @@ -490,6 +493,8 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer( mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid, getSequence(), mName, debugName, /*isBuffer*/ true, gameMode); + // Buffer hasn't yet been latched, so use mDrawingState + surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime); surfaceFrame->setActualStartTime(info.startTimeNanos); // For buffers, acquire fence time will set during latch. surfaceFrame->setActualQueueTime(queueTime); @@ -514,6 +519,8 @@ void Layer::setFrameTimelineVsyncForSkippedFrames(const FrameTimelineInfo& info, mOwnerPid, mOwnerUid, getSequence(), mName, debugName, /*isBuffer*/ false, gameMode); + // Buffer hasn't yet been latched, so use mDrawingState + surfaceFrame->setDesiredPresentTime(mDrawingState.desiredPresentTime); surfaceFrame->setActualStartTime(skippedFrameTimelineInfo.skippedFrameStartTimeNanos); // For Transactions, the post time is considered to be both queue and acquire fence time. surfaceFrame->setActualQueueTime(postTime); @@ -605,15 +612,42 @@ void Layer::miniDump(std::string& result, const frontend::LayerSnapshot& snapsho } void Layer::dumpFrameStats(std::string& result) const { - mFrameTracker.dumpStats(result); + if (FlagManager::getInstance().deprecate_frame_tracker()) { + FrameStats fs = FrameStats(); + getFrameStats(&fs); + for (auto desired = fs.desiredPresentTimesNano.begin(), + actual = fs.actualPresentTimesNano.begin(), + ready = fs.frameReadyTimesNano.begin(); + desired != fs.desiredPresentTimesNano.end() && + actual != fs.actualPresentTimesNano.end() && ready != fs.frameReadyTimesNano.end(); + ++desired, ++actual, ++ready) { + result.append(std::format("{}\t{}\t{}\n", *desired, *actual, *ready)); + } + + result.push_back('\n'); + } else { + mDeprecatedFrameTracker.dumpStats(result); + } } void Layer::clearFrameStats() { - mFrameTracker.clearStats(); + if (FlagManager::getInstance().deprecate_frame_tracker()) { + mFrameStatsHistorySize = 0; + } else { + mDeprecatedFrameTracker.clearStats(); + } } void Layer::getFrameStats(FrameStats* outStats) const { - mFrameTracker.getStats(outStats); + if (FlagManager::getInstance().deprecate_frame_tracker()) { + if (auto ftl = getTimeline()) { + float fps = ftl->get().computeFps({getSequence()}); + ftl->get().generateFrameStats(getSequence(), mFrameStatsHistorySize, outStats); + outStats->refreshPeriodNano = Fps::fromValue(fps).getPeriodNsecs(); + } + } else { + mDeprecatedFrameTracker.getStats(outStats); + } } void Layer::onDisconnect() { @@ -689,8 +723,20 @@ void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& l listener->onReleaseBuffer(callbackId, fence, currentMaxAcquiredBufferCount); } - if (mBufferReleaseChannel) { - mBufferReleaseChannel->writeReleaseFence(callbackId, fence, currentMaxAcquiredBufferCount); + if (!mBufferReleaseChannel) { + return; + } + + status_t status = mBufferReleaseChannel->writeReleaseFence(callbackId, fence, + currentMaxAcquiredBufferCount); + if (status != OK) { + int error = -status; + // callReleaseBufferCallback is called during Layer's destructor. In this case, it's + // expected to receive connection errors. + if (error != EPIPE && error != ECONNRESET) { + ALOGD("[%s] writeReleaseFence failed. error %d (%s)", getDebugName(), error, + strerror(error)); + } } } @@ -1336,9 +1382,9 @@ void Layer::onCompositionPresented(const DisplayDevice* display, handle->compositorTiming = compositorTiming; } - // Update mFrameTracker. + // Update mDeprecatedFrameTracker. nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime; - mFrameTracker.setDesiredPresentTime(desiredPresentTime); + mDeprecatedFrameTracker.setDesiredPresentTime(desiredPresentTime); const int32_t layerId = getSequence(); mFlinger->mTimeStats->setDesiredTime(layerId, mCurrentFrameNumber, desiredPresentTime); @@ -1358,15 +1404,15 @@ void Layer::onCompositionPresented(const DisplayDevice* display, } } + // The SurfaceFrame's AcquireFence is the same as this. std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime; if (frameReadyFence->isValid()) { - mFrameTracker.setFrameReadyFence(std::move(frameReadyFence)); + mDeprecatedFrameTracker.setFrameReadyFence(std::move(frameReadyFence)); } else { // There was no fence for this frame, so assume that it was ready // to be presented at the desired present time. - mFrameTracker.setFrameReadyTime(desiredPresentTime); + mDeprecatedFrameTracker.setFrameReadyTime(desiredPresentTime); } - if (display) { const auto activeMode = display->refreshRateSelector().getActiveMode(); const Fps refreshRate = activeMode.fps; @@ -1381,7 +1427,7 @@ void Layer::onCompositionPresented(const DisplayDevice* display, mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber, presentFence, FrameTracer::FrameEvent::PRESENT_FENCE); - mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); + mDeprecatedFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); } else if (const auto displayId = PhysicalDisplayId::tryCast(display->getId()); displayId && mFlinger->getHwComposer().isConnected(*displayId)) { // The HWC doesn't support present fences, so use the present timestamp instead. @@ -1402,11 +1448,12 @@ void Layer::onCompositionPresented(const DisplayDevice* display, mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(), mCurrentFrameNumber, actualPresentTime, FrameTracer::FrameEvent::PRESENT_FENCE); - mFrameTracker.setActualPresentTime(actualPresentTime); + mDeprecatedFrameTracker.setActualPresentTime(actualPresentTime); } } - mFrameTracker.advanceFrame(); + mFrameStatsHistorySize++; + mDeprecatedFrameTracker.advanceFrame(); mBufferInfo.mFrameLatencyNeeded = false; } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index a2716c6b1f..c234a75693 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -18,6 +18,7 @@ #include <android/gui/DropInputMode.h> #include <android/gui/ISurfaceComposerClient.h> +#include <com_android_graphics_surfaceflinger_flags.h> #include <ftl/small_map.h> #include <gui/BufferQueue.h> #include <gui/LayerState.h> @@ -44,6 +45,7 @@ #include <scheduler/Seamlessness.h> #include <cstdint> +#include <functional> #include <optional> #include <vector> @@ -433,8 +435,12 @@ protected: uint32_t mTransactionFlags{0}; + // Leverages FrameTimeline to generate FrameStats. Since FrameTimeline already has the data, + // statistical history needs to only be tracked by count of frames. + // TODO: Deprecate the '--latency-clear' and get rid of this. + std::atomic<uint16_t> mFrameStatsHistorySize; // Timestamp history for UIAutomation. Thread safe. - FrameTracker mFrameTracker; + FrameTracker mDeprecatedFrameTracker; // main thread sp<NativeHandle> mSidebandStream; @@ -556,6 +562,9 @@ private: std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs; bool mHandleAlive = false; + std::optional<std::reference_wrapper<frametimeline::FrameTimeline>> getTimeline() const { + return *mFlinger->mFrameTimeline; + } }; std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate); diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp index f64ba9e900..231b40b0ac 100644 --- a/services/surfaceflinger/LayerFE.cpp +++ b/services/surfaceflinger/LayerFE.cpp @@ -27,7 +27,6 @@ #include "LayerFE.h" #include "SurfaceFlinger.h" #include "ui/FenceResult.h" -#include "ui/LayerStack.h" namespace android { @@ -174,6 +173,7 @@ std::optional<compositionengine::LayerFE::LayerSettings> LayerFE::prepareClientC layerSettings.edgeExtensionEffect = mSnapshot->edgeExtensionEffect; // Record the name of the layer for debugging further down the stack. layerSettings.name = mSnapshot->name; + layerSettings.luts = mSnapshot->luts; if (hasEffect() && !hasBufferOrSidebandStream()) { prepareEffectsClientComposition(layerSettings, targetSettings); @@ -342,11 +342,6 @@ void LayerFE::prepareShadowClientComposition(LayerFE::LayerSettings& caster, caster.shadow = state; } -void LayerFE::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult, - ui::LayerStack layerStack) { - mCompositionResult.releaseFences.emplace_back(std::move(futureFenceResult), layerStack); -} - CompositionResult&& LayerFE::stealCompositionResult() { return std::move(mCompositionResult); } diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h index 658f949640..5081e102b8 100644 --- a/services/surfaceflinger/LayerFE.h +++ b/services/surfaceflinger/LayerFE.h @@ -22,14 +22,12 @@ #include "compositionengine/LayerFE.h" #include "compositionengine/LayerFECompositionState.h" #include "renderengine/LayerSettings.h" -#include "ui/LayerStack.h" #include <ftl/future.h> namespace android { struct CompositionResult { - std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences; sp<Fence> lastClientCompositionFence = nullptr; }; @@ -41,7 +39,6 @@ public: // compositionengine::LayerFE overrides const compositionengine::LayerFECompositionState* getCompositionState() const override; bool onPreComposition(bool updatingOutputGeometryThisFrame) override; - void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack) override; const char* getDebugName() const override; int32_t getSequence() const override; bool hasRoundedCorners() const override; diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp index c914ec3066..c7d0b2c9ef 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -//#define LOG_NDEBUG 0 +// #define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_GRAPHICS @@ -24,6 +24,7 @@ #include <unistd.h> #include <cinttypes> #include <cstdint> +#include <functional> #include <optional> #include <android-base/properties.h> @@ -33,45 +34,29 @@ #include <binder/IServiceManager.h> -#include "../SurfaceFlingerProperties.h" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconversion" +#include <powermanager/PowerHalController.h> +#include <powermanager/PowerHintSessionWrapper.h> +#pragma clang diagnostic pop +#include <common/FlagManager.h> #include "PowerAdvisor.h" -#include "SurfaceFlinger.h" -namespace android { -namespace Hwc2 { +namespace hal = aidl::android::hardware::power; -PowerAdvisor::~PowerAdvisor() = default; - -namespace impl { - -using aidl::android::hardware::power::Boost; -using aidl::android::hardware::power::ChannelConfig; -using aidl::android::hardware::power::Mode; -using aidl::android::hardware::power::SessionHint; -using aidl::android::hardware::power::SessionTag; -using aidl::android::hardware::power::WorkDuration; -using aidl::android::hardware::power::WorkDurationFixedV1; +namespace android::adpf::impl { -using aidl::android::hardware::common::fmq::MQDescriptor; using aidl::android::hardware::common::fmq::SynchronizedReadWrite; -using aidl::android::hardware::power::ChannelMessage; using android::hardware::EventFlag; -using ChannelMessageContents = ChannelMessage::ChannelMessageContents; -using MsgQueue = android::AidlMessageQueue<ChannelMessage, SynchronizedReadWrite>; +using ChannelMessageContents = hal::ChannelMessage::ChannelMessageContents; +using MsgQueue = android::AidlMessageQueue<hal::ChannelMessage, SynchronizedReadWrite>; using FlagQueue = android::AidlMessageQueue<int8_t, SynchronizedReadWrite>; PowerAdvisor::~PowerAdvisor() = default; namespace { -std::chrono::milliseconds getUpdateTimeout() { - // Default to a timeout of 80ms if nothing else is specified - static std::chrono::milliseconds timeout = - std::chrono::milliseconds(sysprop::display_update_imminent_timeout_ms(80)); - return timeout; -} - void traceExpensiveRendering(bool enabled) { if (enabled) { SFTRACE_ASYNC_BEGIN("ExpensiveRendering", 0); @@ -82,28 +67,30 @@ void traceExpensiveRendering(bool enabled) { } // namespace -PowerAdvisor::PowerAdvisor(SurfaceFlinger& flinger) - : mPowerHal(std::make_unique<power::PowerHalController>()), mFlinger(flinger) { - if (getUpdateTimeout() > 0ms) { - mScreenUpdateTimer.emplace("UpdateImminentTimer", getUpdateTimeout(), +PowerAdvisor::PowerAdvisor(std::function<void()>&& sfDisableExpensiveFn, + std::chrono::milliseconds timeout) + : mPowerHal(std::make_unique<power::PowerHalController>()) { + if (timeout > 0ms) { + mScreenUpdateTimer.emplace("UpdateImminentTimer", timeout, /* resetCallback */ nullptr, /* timeoutCallback */ - [this] { + [this, disableExpensiveFn = std::move(sfDisableExpensiveFn), + timeout] { while (true) { auto timeSinceLastUpdate = std::chrono::nanoseconds( systemTime() - mLastScreenUpdatedTime.load()); - if (timeSinceLastUpdate >= getUpdateTimeout()) { + if (timeSinceLastUpdate >= timeout) { break; } // We may try to disable expensive rendering and allow // for sending DISPLAY_UPDATE_IMMINENT hints too early if // we idled very shortly after updating the screen, so // make sure we wait enough time. - std::this_thread::sleep_for(getUpdateTimeout() - + std::this_thread::sleep_for(timeout - timeSinceLastUpdate); } mSendUpdateImminent.store(true); - mFlinger.disableExpensiveRendering(); + disableExpensiveFn(); }); } } @@ -132,7 +119,7 @@ void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expec const bool expectsExpensiveRendering = !mExpensiveDisplays.empty(); if (mNotifiedExpensiveRendering != expectsExpensiveRendering) { - auto ret = getPowerHal().setMode(Mode::EXPENSIVE_RENDERING, expectsExpensiveRendering); + auto ret = getPowerHal().setMode(hal::Mode::EXPENSIVE_RENDERING, expectsExpensiveRendering); if (!ret.isOk()) { if (ret.isUnsupported()) { mHasExpensiveRendering = false; @@ -151,7 +138,7 @@ void PowerAdvisor::notifyCpuLoadUp() { if (!mBootFinished.load()) { return; } - sendHintSessionHint(SessionHint::CPU_LOAD_UP); + sendHintSessionHint(hal::SessionHint::CPU_LOAD_UP); } void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() { @@ -163,12 +150,12 @@ void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() { if (mSendUpdateImminent.exchange(false)) { ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset"); - sendHintSessionHint(SessionHint::CPU_LOAD_RESET); + sendHintSessionHint(hal::SessionHint::CPU_LOAD_RESET); if (!mHasDisplayUpdateImminent) { ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it"); } else { - auto ret = getPowerHal().setBoost(Boost::DISPLAY_UPDATE_IMMINENT, 0); + auto ret = getPowerHal().setBoost(hal::Boost::DISPLAY_UPDATE_IMMINENT, 0); if (ret.isUnsupported()) { mHasDisplayUpdateImminent = false; } @@ -205,7 +192,7 @@ bool PowerAdvisor::shouldCreateSessionWithConfig() { FlagManager::getInstance().adpf_use_fmq_channel(); } -void PowerAdvisor::sendHintSessionHint(SessionHint hint) { +void PowerAdvisor::sendHintSessionHint(hal::SessionHint hint) { if (!mBootFinished || !usePowerHintSession()) { ALOGV("Power hint session is not enabled, skip sending session hint"); return; @@ -236,7 +223,7 @@ bool PowerAdvisor::ensurePowerHintSessionRunning() { static_cast<int32_t>(getuid()), mHintSessionThreadIds, mTargetDuration.ns(), - SessionTag::SURFACEFLINGER, + hal::SessionTag::SURFACEFLINGER, &mSessionConfig); if (ret.isOk()) { mHintSession = ret.value(); @@ -326,7 +313,7 @@ void PowerAdvisor::reportActualWorkDuration() { return; } SFTRACE_CALL(); - std::optional<WorkDuration> actualDuration = estimateWorkDuration(); + std::optional<hal::WorkDuration> actualDuration = estimateWorkDuration(); if (!actualDuration.has_value() || actualDuration->durationNanos < 0) { ALOGV("Failed to send actual work duration, skipping"); return; @@ -377,7 +364,7 @@ void PowerAdvisor::reportActualWorkDuration() { mHintSessionQueue.clear(); } -template <ChannelMessage::ChannelMessageContents::Tag T, class In> +template <hal::ChannelMessage::ChannelMessageContents::Tag T, class In> bool PowerAdvisor::writeHintSessionMessage(In* contents, size_t count) { if (!mMsgQueue) { ALOGV("Skip using FMQ with message tag %hhd as it's not supported", T); @@ -395,13 +382,13 @@ bool PowerAdvisor::writeHintSessionMessage(In* contents, size_t count) { } for (size_t i = 0; i < count; ++i) { if constexpr (T == ChannelMessageContents::Tag::workDuration) { - const WorkDuration& duration = contents[i]; - new (tx.getSlot(i)) ChannelMessage{ + const hal::WorkDuration& duration = contents[i]; + new (tx.getSlot(i)) hal::ChannelMessage{ .sessionID = static_cast<int32_t>(mSessionConfig.id), .timeStampNanos = (i == count - 1) ? ::android::uptimeNanos() : duration.timeStampNanos, .data = ChannelMessageContents::make<ChannelMessageContents::Tag::workDuration, - WorkDurationFixedV1>({ + hal::WorkDurationFixedV1>({ .durationNanos = duration.durationNanos, .workPeriodStartTimestampNanos = duration.workPeriodStartTimestampNanos, .cpuDurationNanos = duration.cpuDurationNanos, @@ -409,7 +396,7 @@ bool PowerAdvisor::writeHintSessionMessage(In* contents, size_t count) { }), }; } else { - new (tx.getSlot(i)) ChannelMessage{ + new (tx.getSlot(i)) hal::ChannelMessage{ .sessionID = static_cast<int32_t>(mSessionConfig.id), .timeStampNanos = ::android::uptimeNanos(), .data = ChannelMessageContents::make<T, In>(std::move(contents[i])), @@ -572,7 +559,7 @@ std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds( return sortedDisplays; } -std::optional<WorkDuration> PowerAdvisor::estimateWorkDuration() { +std::optional<hal::WorkDuration> PowerAdvisor::estimateWorkDuration() { if (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull()) { return std::nullopt; } @@ -657,7 +644,7 @@ std::optional<WorkDuration> PowerAdvisor::estimateWorkDuration() { Duration combinedDuration = combineTimingEstimates(totalDuration, flingerDuration); Duration cpuDuration = combineTimingEstimates(totalDurationWithoutGpu, flingerDuration); - WorkDuration duration{ + hal::WorkDuration duration{ .timeStampNanos = TimePoint::now().ns(), .durationNanos = combinedDuration.ns(), .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(), @@ -760,6 +747,4 @@ power::PowerHalController& PowerAdvisor::getPowerHal() { return *mPowerHal; } -} // namespace impl -} // namespace Hwc2 -} // namespace android +} // namespace android::adpf::impl diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h index 1076b2b79b..458b46d500 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/PowerAdvisor/PowerAdvisor.h @@ -17,7 +17,7 @@ #pragma once #include <atomic> -#include <chrono> +#include <future> #include <unordered_map> #include <unordered_set> @@ -30,10 +30,8 @@ #pragma clang diagnostic ignored "-Wconversion" #include <aidl/android/hardware/power/IPower.h> #include <fmq/AidlMessageQueue.h> -#include <powermanager/PowerHalController.h> #pragma clang diagnostic pop -#include <compositionengine/impl/OutputCompositionState.h> #include <scheduler/Time.h> #include <ui/DisplayIdentification.h> #include "../Scheduler/OneShotTimer.h" @@ -42,13 +40,16 @@ using namespace std::chrono_literals; namespace android { -class SurfaceFlinger; +namespace power { +class PowerHalController; +class PowerHintSessionWrapper; +} // namespace power -namespace Hwc2 { +namespace adpf { class PowerAdvisor { public: - virtual ~PowerAdvisor(); + virtual ~PowerAdvisor() = default; // Initializes resources that cannot be initialized on construction virtual void init() = 0; @@ -113,9 +114,9 @@ namespace impl { // PowerAdvisor is a wrapper around IPower HAL which takes into account the // full state of the system when sending out power hints to things like the GPU. -class PowerAdvisor final : public Hwc2::PowerAdvisor { +class PowerAdvisor final : public adpf::PowerAdvisor { public: - PowerAdvisor(SurfaceFlinger& flinger); + PowerAdvisor(std::function<void()>&& function, std::chrono::milliseconds timeout); ~PowerAdvisor() override; void init() override; @@ -159,7 +160,6 @@ private: std::unordered_set<DisplayId> mExpensiveDisplays; bool mNotifiedExpensiveRendering = false; - SurfaceFlinger& mFlinger; std::atomic_bool mSendUpdateImminent = true; std::atomic<nsecs_t> mLastScreenUpdatedTime = 0; std::optional<scheduler::OneShotTimer> mScreenUpdateTimer; @@ -326,5 +326,5 @@ private: }; } // namespace impl -} // namespace Hwc2 +} // namespace adpf } // namespace android diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 011fd9e20a..21d3396ebe 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -353,22 +353,13 @@ void RegionSamplingThread::captureSample() { sampledBounds.getSize(), ui::Dataspace::V0_SRGB, displayWeak, RenderArea::Options::CAPTURE_SECURE_LAYERS); - FenceResult fenceResult; - if (FlagManager::getInstance().single_hop_screenshot() && - mFlinger.mRenderEngine->isThreaded()) { - std::vector<sp<LayerFE>> layerFEs; - auto displayState = mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, - getLayerSnapshotsFn, layerFEs); - fenceResult = mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, - kGrayscale, kIsProtected, kAttachGainmap, nullptr, - displayState, layerFEs) - .get(); - } else { - fenceResult = mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, - buffer, kRegionSampling, kGrayscale, - kIsProtected, kAttachGainmap, nullptr) - .get(); - } + std::vector<std::pair<Layer*, sp<LayerFE>>> layers; + auto displayState = + mFlinger.getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers); + FenceResult fenceResult = + mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale, + kIsProtected, kAttachGainmap, nullptr, displayState, layers) + .get(); if (fenceResult.ok()) { fenceResult.value()->waitForever(LOG_TAG); } diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index e385f18243..7729671401 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -420,6 +420,16 @@ void EventThread::enableSyntheticVsync(bool enable) { mCondition.notify_all(); } +void EventThread::omitVsyncDispatching(bool omitted) { + std::lock_guard<std::mutex> lock(mMutex); + if (!mVSyncState || mVSyncState->omitted == omitted) { + return; + } + + mVSyncState->omitted = omitted; + mCondition.notify_all(); +} + void EventThread::onVsync(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) { std::lock_guard<std::mutex> lock(mMutex); mLastVsyncCallbackTime = TimePoint::fromNs(vsyncTime); @@ -521,7 +531,17 @@ void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { } if (mVSyncState && vsyncRequested) { - mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync; + const bool vsyncOmitted = + FlagManager::getInstance().no_vsyncs_on_screen_off() && mVSyncState->omitted; + if (vsyncOmitted) { + mState = State::Idle; + SFTRACE_INT("VsyncPendingScreenOn", 1); + } else { + mState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync; + if (FlagManager::getInstance().no_vsyncs_on_screen_off()) { + SFTRACE_INT("VsyncPendingScreenOn", 0); + } + } } else { ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected"); mState = State::Idle; diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index c3c7eb0ee1..2daf126d77 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -106,6 +106,8 @@ public: // Feed clients with fake VSYNC, e.g. while the display is off. virtual void enableSyntheticVsync(bool) = 0; + virtual void omitVsyncDispatching(bool) = 0; + virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0; virtual void onHotplugConnectionError(int32_t connectionError) = 0; @@ -165,6 +167,8 @@ public: void enableSyntheticVsync(bool) override; + void omitVsyncDispatching(bool) override; + void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override; void onHotplugConnectionError(int32_t connectionError) override; @@ -240,6 +244,9 @@ private: // True if VSYNC should be faked, e.g. when display is off. bool synthetic = false; + + // True if VSYNC should not be delivered to apps. Used when the display is off. + bool omitted = false; }; // TODO(b/74619554): Create per-display threads waiting on respective VSYNC signals, diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 64b85c080e..e45bdfce13 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -308,6 +308,12 @@ void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) { const auto setFrameRateVoteType = info->isVisible() ? voteType : LayerVoteType::NoVote; + const bool hasSetFrameRateOpinion = frameRate.isValid() && !frameRate.isNoVote(); + const bool hasCategoryOpinion = + frameRate.category != FrameRateCategory::NoPreference && + frameRate.category != FrameRateCategory::Default; + const bool hasFrameRateOpinion = hasSetFrameRateOpinion || hasCategoryOpinion; + if (gameModeFrameRateOverride.isValid()) { info->setLayerVote({gameFrameRateOverrideVoteType, gameModeFrameRateOverride}); SFTRACE_FORMAT_INSTANT("GameModeFrameRateOverride"); @@ -315,7 +321,7 @@ void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) { trace(*info, gameFrameRateOverrideVoteType, gameModeFrameRateOverride.getIntValue()); } - } else if (frameRate.isValid() && frameRate.isVoteValidForMrr(isVrrDevice)) { + } else if (hasFrameRateOpinion && frameRate.isVoteValidForMrr(isVrrDevice)) { info->setLayerVote({setFrameRateVoteType, isValuelessVote ? 0_Hz : frameRate.vote.rate, frameRate.vote.seamlessness, frameRate.category}); @@ -332,7 +338,7 @@ void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) { gameDefaultFrameRateOverride.getIntValue()); } } else { - if (frameRate.isValid() && !frameRate.isVoteValidForMrr(isVrrDevice)) { + if (hasFrameRateOpinion && !frameRate.isVoteValidForMrr(isVrrDevice)) { SFTRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s " "%s %s", info->getName().c_str(), diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index eca8df27d6..84fa1390a4 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -489,6 +489,20 @@ auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement return mGetRankedFrameRatesCache->result; } +using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>; +using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>; + +PerUidLayerRequirements groupLayersByUid( + const std::vector<RefreshRateSelector::LayerRequirement>& layers) { + PerUidLayerRequirements layersByUid; + for (const auto& layer : layers) { + const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first; + auto& layersWithSameUid = it->second; + layersWithSameUid.push_back(&layer); + } + return layersByUid; +} + auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers, GlobalSignals signals, Fps pacesetterFps) const -> RankedFrameRates { @@ -525,6 +539,43 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return {ranking, GlobalSignals{.powerOnImminent = true}}; } + // A method for UI Toolkit to send the touch signal via "HighHint" category vote, + // which will touch boost when there are no ExplicitDefault layer votes on the app. + // At most one app can have the "HighHint" touch boost vote at a time. + // This accounts for cases such as games that use `setFrameRate` + // with Default compatibility to limit the frame rate and disabling touch boost. + bool isAppTouchBoost = false; + const auto layersByUid = groupLayersByUid(layers); + for (const auto& [uid, layersWithSameUid] : layersByUid) { + bool hasHighHint = false; + bool hasExplicitDefault = false; + for (const auto& layer : layersWithSameUid) { + switch (layer->vote) { + case LayerVoteType::ExplicitDefault: + hasExplicitDefault = true; + break; + case LayerVoteType::ExplicitCategory: + if (layer->frameRateCategory == FrameRateCategory::HighHint) { + hasHighHint = true; + } + break; + default: + // No action + break; + } + if (hasHighHint && hasExplicitDefault) { + break; + } + } + + if (hasHighHint && !hasExplicitDefault) { + // Focused app has touch signal (HighHint) and no frame rate ExplicitDefault votes + // (which prevents touch boost due to games use case). + isAppTouchBoost = true; + break; + } + } + int noVoteLayers = 0; // Layers that prefer the same mode ("no-op"). int noPreferenceLayers = 0; @@ -535,7 +586,6 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi int explicitExact = 0; int explicitGteLayers = 0; int explicitCategoryVoteLayers = 0; - int interactiveLayers = 0; int seamedFocusedLayers = 0; int categorySmoothSwitchOnlyLayers = 0; @@ -563,11 +613,9 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi explicitGteLayers++; break; case LayerVoteType::ExplicitCategory: - if (layer.frameRateCategory == FrameRateCategory::HighHint) { - // HighHint does not count as an explicit signal from an app. It may be - // be a touch signal. - interactiveLayers++; - } else { + // HighHint does not count as an explicit signal from an app. It is a touch signal + // sent from UI Toolkit. + if (layer.frameRateCategory != FrameRateCategory::HighHint) { explicitCategoryVoteLayers++; } if (layer.frameRateCategory == FrameRateCategory::NoPreference) { @@ -882,14 +930,11 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size(); }; - // A method for UI Toolkit to send the touch signal via "HighHint" category vote, - // which will touch boost when there are no ExplicitDefault layer votes. This is an - // incomplete solution but accounts for cases such as games that use `setFrameRate` with default + // This accounts for cases such as games that use `setFrameRate` with Default // compatibility to limit the frame rate, which should not have touch boost. - const bool hasInteraction = signals.touch || interactiveLayers > 0; - - if (hasInteraction && explicitDefaultVoteLayers == 0 && isTouchBoostForExplicitExact() && - isTouchBoostForCategory()) { + const bool isLateGlobalTouchBoost = signals.touch && explicitDefaultVoteLayers == 0; + const bool isLateTouchBoost = isLateGlobalTouchBoost || isAppTouchBoost; + if (isLateTouchBoost && isTouchBoostForExplicitExact() && isTouchBoostForCategory()) { const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); using fps_approx_ops::operator<; @@ -917,42 +962,6 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi return {ranking, kNoSignals}; } -using LayerRequirementPtrs = std::vector<const RefreshRateSelector::LayerRequirement*>; -using PerUidLayerRequirements = std::unordered_map<uid_t, LayerRequirementPtrs>; - -PerUidLayerRequirements groupLayersByUid( - const std::vector<RefreshRateSelector::LayerRequirement>& layers) { - PerUidLayerRequirements layersByUid; - for (const auto& layer : layers) { - const auto it = layersByUid.emplace(layer.ownerUid, LayerRequirementPtrs()).first; - auto& layersWithSameUid = it->second; - layersWithSameUid.push_back(&layer); - } - - // Remove uids that can't have a frame rate override - for (auto it = layersByUid.begin(); it != layersByUid.end();) { - const auto& layersWithSameUid = it->second; - bool skipUid = false; - for (const auto& layer : layersWithSameUid) { - using LayerVoteType = RefreshRateSelector::LayerVoteType; - - if (layer->vote == LayerVoteType::Max || layer->vote == LayerVoteType::Heuristic) { - ALOGV("%s: %s skips uid=%d due to the vote", __func__, - formatLayerInfo(*layer, layer->weight).c_str(), layer->ownerUid); - skipUid = true; - break; - } - } - if (skipUid) { - it = layersByUid.erase(it); - } else { - ++it; - } - } - - return layersByUid; -} - auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequirement>& layers, Fps displayRefreshRate, GlobalSignals globalSignals) const @@ -997,6 +1006,7 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme bool hasExplicitExactOrMultiple = false; bool hasExplicitDefault = false; bool hasHighHint = false; + bool hasSkipOverrideLayer = false; for (const auto& layer : layersWithSameUid) { switch (layer->vote) { case LayerVoteType::ExplicitExactOrMultiple: @@ -1010,15 +1020,25 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme hasHighHint = true; } break; + case LayerVoteType::Max: + case LayerVoteType::Heuristic: + hasSkipOverrideLayer = true; + break; default: // No action break; } - if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint) { + if (hasExplicitExactOrMultiple && hasExplicitDefault && hasHighHint && + hasSkipOverrideLayer) { break; } } + if (hasSkipOverrideLayer) { + ALOGV("%s: Skipping due to vote(s): uid=%d", __func__, uid); + continue; + } + // Layers with ExplicitExactOrMultiple expect touch boost if (globalSignals.touch && hasExplicitExactOrMultiple) { ALOGV("%s: Skipping for touch (input signal): uid=%d", __func__, uid); @@ -1652,9 +1672,9 @@ std::chrono::milliseconds RefreshRateSelector::getIdleTimerTimeout() { FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory category) { switch (category) { case FrameRateCategory::High: - return FpsRange{90_Hz, 120_Hz}; + return FpsRange{kFrameRateCategoryRateHigh, 120_Hz}; case FrameRateCategory::Normal: - return FpsRange{60_Hz, 120_Hz}; + return FpsRange{kFrameRateCategoryRateNormal, 120_Hz}; case FrameRateCategory::Low: return FpsRange{48_Hz, 120_Hz}; case FrameRateCategory::HighHint: diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index a398c01a8f..ee3a4f7bdc 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -52,6 +52,12 @@ public: // The lowest Render Frame Rate that will ever be selected static constexpr Fps kMinSupportedFrameRate = 20_Hz; + // Start range for FrameRateCategory Normal and High. + static constexpr Fps kFrameRateCategoryRateHigh = 90_Hz; + static constexpr Fps kFrameRateCategoryRateNormal = 60_Hz; + static constexpr std::pair<Fps, Fps> kFrameRateCategoryRates = {kFrameRateCategoryRateNormal, + kFrameRateCategoryRateHigh}; + class Policy { static constexpr int kAllowGroupSwitchingDefault = false; @@ -433,6 +439,8 @@ public: bool isVrrDevice() const; + std::pair<Fps, Fps> getFrameRateCategoryRates() const { return kFrameRateCategoryRates; } + private: friend struct TestableRefreshRateSelector; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index b83ff19fe7..274e121a79 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -405,6 +405,14 @@ void Scheduler::enableSyntheticVsync(bool enable) { eventThreadFor(Cycle::Render).enableSyntheticVsync(enable); } +void Scheduler::omitVsyncDispatching(bool omitted) { + eventThreadFor(Cycle::Render).omitVsyncDispatching(omitted); + // Note: If we don't couple Cycle::LastComposite event thread, there is a black screen + // after boot. This is most likely sysui or system_server dependency on sf instance + // Choreographer + eventThreadFor(Cycle::LastComposite).omitVsyncDispatching(omitted); +} + void Scheduler::onFrameRateOverridesChanged() { const auto [pacesetterId, supportsFrameRateOverrideByContent] = [this] { std::scoped_lock lock(mDisplayLock); @@ -428,7 +436,8 @@ void Scheduler::onHdcpLevelsChanged(Cycle cycle, PhysicalDisplayId displayId, #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-value" // b/369277774 -bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) { +bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode, + bool clearContentRequirements) { const bool isPacesetter = FTL_FAKE_GUARD(kMainThreadContext, (std::scoped_lock(mDisplayLock), displayId == mPacesetterDisplayId)); @@ -437,9 +446,11 @@ bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRat std::lock_guard<std::mutex> lock(mPolicyLock); mPolicy.emittedModeOpt = mode; - // Invalidate content based refresh rate selection so it could be calculated - // again for the new refresh rate. - mPolicy.contentRequirements.clear(); + if (clearContentRequirements) { + // Invalidate content based refresh rate selection so it could be calculated + // again for the new refresh rate. + mPolicy.contentRequirements.clear(); + } } if (hasEventThreads()) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index c88b563805..e77af609e2 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -151,9 +151,11 @@ public: void dispatchHotplugError(int32_t errorCode); // Returns true if the PhysicalDisplayId is the pacesetter. - bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&) EXCLUDES(mPolicyLock); + bool onDisplayModeChanged(PhysicalDisplayId, const FrameRateMode&, + bool clearContentRequirements) EXCLUDES(mPolicyLock); void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext); + void omitVsyncDispatching(bool) REQUIRES(kMainThreadContext); void onHdcpLevelsChanged(Cycle, PhysicalDisplayId, int32_t, int32_t); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 09dbc59c69..0d03a6cef3 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -92,6 +92,7 @@ #include <ui/DisplayStatInfo.h> #include <ui/DisplayState.h> #include <ui/DynamicDisplayInfo.h> +#include <ui/FrameRateCategoryRate.h> #include <ui/GraphicBufferAllocator.h> #include <ui/HdrRenderTypeUtils.h> #include <ui/LayerStack.h> @@ -132,7 +133,6 @@ #include "DisplayHardware/FramebufferSurface.h" #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/Hal.h" -#include "DisplayHardware/PowerAdvisor.h" #include "DisplayHardware/VirtualDisplaySurface.h" #include "DisplayRenderArea.h" #include "Effects/Daltonizer.h" @@ -152,6 +152,7 @@ #include "LayerVector.h" #include "MutexUtils.h" #include "NativeWindowSurface.h" +#include "PowerAdvisor/PowerAdvisor.h" #include "RegionSamplingThread.h" #include "RenderAreaBuilder.h" #include "Scheduler/EventThread.h" @@ -425,7 +426,11 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) mEmulatedDisplayDensity(getDensityFromProperty("qemu.sf.lcd_density", false)), mInternalDisplayDensity( getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)), - mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)), + mPowerAdvisor(std::make_unique< + adpf::impl::PowerAdvisor>([this] { disableExpensiveRendering(); }, + std::chrono::milliseconds( + sysprop::display_update_imminent_timeout_ms( + 80)))), mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()), mSkipPowerOnForQuiescent(base::GetBoolProperty("ro.boot.quiescent"s, false)) { ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str()); @@ -1009,7 +1014,8 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { config.cacheUltraHDR = base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false); config.cacheEdgeExtension = - base::GetBoolProperty("debug.sf.edge_extension_shader"s, true); + base::GetBoolProperty("debug.sf.prime_shader_cache.edge_extension_shader"s, + true); return getRenderEngine().primeCache(config); }); @@ -1217,6 +1223,10 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info info->activeDisplayModeId = ftl::to_underlying(mode.modePtr->getId()); info->renderFrameRate = mode.fps.getValue(); info->hasArrSupport = mode.modePtr->getVrrConfig() && FlagManager::getInstance().vrr_config(); + + const auto [normal, high] = display->refreshRateSelector().getFrameRateCategoryRates(); + ui::FrameRateCategoryRate frameRateCategoryRate(normal.getValue(), high.getValue()); + info->frameRateCategoryRate = frameRateCategoryRate; info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities()); @@ -1348,7 +1358,8 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { mScheduler->updatePhaseConfiguration(displayId, mode.fps); if (emitEvent) { - mScheduler->onDisplayModeChanged(displayId, mode); + mScheduler->onDisplayModeChanged(displayId, mode, + /*clearContentRequirements*/ false); } break; case DesiredModeAction::None: @@ -1443,7 +1454,7 @@ void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) { mScheduler->updatePhaseConfiguration(displayId, activeMode.fps); if (pendingModeOpt->emitEvent) { - mScheduler->onDisplayModeChanged(displayId, activeMode); + mScheduler->onDisplayModeChanged(displayId, activeMode, /*clearContentRequirements*/ true); } } @@ -1656,19 +1667,22 @@ status_t SurfaceFlinger::getOverlaySupport(gui::OverlayProperties* outProperties outProperties->combinations.emplace_back(outCombination); } outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces; - if (aidlProperties.lutProperties.has_value()) { + if (aidlProperties.lutProperties) { std::vector<gui::LutProperties> outLutProperties; - for (const auto& properties : aidlProperties.lutProperties.value()) { - gui::LutProperties currentProperties; - currentProperties.dimension = - static_cast<gui::LutProperties::Dimension>(properties->dimension); - currentProperties.size = properties->size; - currentProperties.samplingKeys.reserve(properties->samplingKeys.size()); - std::transform(properties->samplingKeys.cbegin(), properties->samplingKeys.cend(), - std::back_inserter(currentProperties.samplingKeys), [](const auto& val) { - return static_cast<gui::LutProperties::SamplingKey>(val); - }); - outLutProperties.push_back(std::move(currentProperties)); + for (auto properties : *aidlProperties.lutProperties) { + if (!properties) { + gui::LutProperties currentProperties; + currentProperties.dimension = + static_cast<gui::LutProperties::Dimension>(properties->dimension); + currentProperties.size = properties->size; + currentProperties.samplingKeys.reserve(properties->samplingKeys.size()); + std::transform(properties->samplingKeys.cbegin(), properties->samplingKeys.cend(), + std::back_inserter(currentProperties.samplingKeys), + [](const auto& val) { + return static_cast<gui::LutProperties::SamplingKey>(val); + }); + outLutProperties.push_back(std::move(currentProperties)); + } } outProperties->lutProperties.emplace(outLutProperties.begin(), outLutProperties.end()); } @@ -2523,17 +2537,13 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, frontend::LayerSnapshot* snapshot = mLayerSnapshotBuilder.getSnapshot(it->second->sequence); gui::GameMode gameMode = (snapshot) ? snapshot->gameMode : gui::GameMode::Unsupported; mLayersWithQueuedFrames.emplace(it->second, gameMode); - mLayersIdsWithQueuedFrames.emplace(it->second->sequence); } updateLayerHistory(latchTime); mLayerSnapshotBuilder.forEachSnapshot([&](const frontend::LayerSnapshot& snapshot) { - // update output dirty region if we have a queued buffer that is visible or a snapshot - // recently became invisible - // TODO(b/360050020) investigate if we need to update dirty region when layer color changes - if ((snapshot.isVisible && - (mLayersIdsWithQueuedFrames.find(snapshot.path.id) != - mLayersIdsWithQueuedFrames.end())) || + // update output's dirty region if a snapshot is visible and its + // content is dirty or if a snapshot recently became invisible + if ((snapshot.isVisible && snapshot.contentDirty) || (!snapshot.isVisible && snapshot.changes.test(Changes::Visibility))) { Region visibleReg; visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion); @@ -2923,7 +2933,6 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse); mLayersWithQueuedFrames.clear(); - mLayersIdsWithQueuedFrames.clear(); doActiveLayersTracingIfNeeded(true, mVisibleRegionsDirty, pacesetterTarget.frameBeginTime(), vsyncId); @@ -3549,7 +3558,9 @@ std::optional<DisplayModeId> SurfaceFlinger::processHotplugConnect(PhysicalDispl } state.isProtected = true; state.displayName = std::move(info.name); - + state.maxLayerPictureProfiles = getHwComposer().getMaxLayerPictureProfiles(displayId); + state.hasPictureProcessing = + getHwComposer().hasDisplayCapability(displayId, DisplayCapability::PICTURE_PROCESSING); mCurrentState.displays.add(token, state); ALOGI("Connecting %s", displayString); return activeModeId; @@ -3657,6 +3668,26 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( return display; } +void SurfaceFlinger::incRefreshableDisplays() { + if (FlagManager::getInstance().no_vsyncs_on_screen_off()) { + mRefreshableDisplays++; + if (mRefreshableDisplays == 1) { + ftl::FakeGuard guard(kMainThreadContext); + mScheduler->omitVsyncDispatching(false); + } + } +} + +void SurfaceFlinger::decRefreshableDisplays() { + if (FlagManager::getInstance().no_vsyncs_on_screen_off()) { + mRefreshableDisplays--; + if (mRefreshableDisplays == 0) { + ftl::FakeGuard guard(kMainThreadContext); + mScheduler->omitVsyncDispatching(true); + } + } +} + void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState& state) { ui::Size resolution(0, 0); @@ -3690,6 +3721,8 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, builder.setPixels(resolution); builder.setIsSecure(state.isSecure); builder.setIsProtected(state.isProtected); + builder.setHasPictureProcessing(state.hasPictureProcessing); + builder.setMaxLayerPictureProfiles(state.maxLayerPictureProfiles); builder.setPowerAdvisor(mPowerAdvisor.get()); builder.setName(state.displayName); auto compositionDisplay = getCompositionEngine().createDisplay(builder.build()); @@ -3748,6 +3781,10 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, display->adjustRefreshRate(mScheduler->getPacesetterRefreshRate()); } + if (display->isRefreshable()) { + incRefreshableDisplays(); + } + mDisplays.try_emplace(displayToken, std::move(display)); // For an external display, loadDisplayModes already attempted to select the same mode @@ -3782,6 +3819,10 @@ void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { } else { mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId); } + + if (display->isRefreshable()) { + decRefreshableDisplays(); + } } mDisplays.erase(displayToken); @@ -3816,6 +3857,10 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, if (display->isVirtual()) { releaseVirtualDisplay(display->getVirtualId()); } + + if (display->isRefreshable()) { + decRefreshableDisplays(); + } } mDisplays.erase(displayToken); @@ -3980,7 +4025,8 @@ void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) { inputWindowCommands = std::move(mInputWindowCommands), inputFlinger = mInputFlinger, this, - visibleWindowsChanged, vsyncId, frameTime]() { + visibleWindowsChanged, vsyncId, + frameTime]() mutable { SFTRACE_NAME("BackgroundExecutor::updateInputFlinger"); if (updateWindowInfo) { mWindowInfosListenerInvoker @@ -5314,7 +5360,15 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: activeDisplay->isPoweredOn(), "Trying to change power mode on inactive display without powering off active display"); + const bool couldRefresh = display->isRefreshable(); display->setPowerMode(mode); + const bool canRefresh = display->isRefreshable(); + + if (couldRefresh && !canRefresh) { + decRefreshableDisplays(); + } else if (!couldRefresh && canRefresh) { + incRefreshableDisplays(); + } const auto activeMode = display->refreshRateSelector().getActiveMode().modePtr; if (currentMode == hal::PowerMode::OFF) { @@ -7155,9 +7209,10 @@ void SurfaceFlinger::attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* laye // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer. // A protected layer has no implication on whether it's secure, which is explicitly set by // application to avoid being screenshot or drawn via unsecure display. -bool SurfaceFlinger::layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const { +bool SurfaceFlinger::layersHasProtectedLayer( + const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const { bool protectedLayerFound = false; - for (auto& layerFE : layers) { + for (auto& [_, layerFE] : layers) { protectedLayerFound |= (layerFE->mSnapshot->isVisible && layerFE->mSnapshot->hasProtectedContent); if (protectedLayerFound) { @@ -7173,15 +7228,21 @@ bool SurfaceFlinger::layersHasProtectedLayer(const std::vector<sp<LayerFE>>& lay // risk of deadlocks. std::optional<SurfaceFlinger::OutputCompositionState> SurfaceFlinger::getSnapshotsFromMainThread( RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, - std::vector<sp<LayerFE>>& layerFEs) { + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) { return mScheduler - ->schedule([=, this, &renderAreaBuilder, &layerFEs]() REQUIRES(kMainThreadContext) { + ->schedule([=, this, &renderAreaBuilder, &layers]() REQUIRES(kMainThreadContext) { SFTRACE_NAME("getSnapshotsFromMainThread"); - auto layers = getLayerSnapshotsFn(); - for (auto& [layer, layerFE] : layers) { - attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK); + layers = getLayerSnapshotsFn(); + // Non-threaded RenderEngine eventually returns to the main thread a 2nd time + // to complete the screenshot. Release fences should only be added during the 2nd + // hop to main thread in order to avoid potential deadlocks from waiting for the + // the future fence to fire. + if (mRenderEngine->isThreaded()) { + for (auto& [layer, layerFE] : layers) { + attachReleaseFenceFutureToLayer(layer, layerFE.get(), + ui::INVALID_LAYER_STACK); + } } - layerFEs = extractLayerFEs(layers); return getDisplayStateFromRenderAreaBuilder(renderAreaBuilder); }) .get(); @@ -7202,79 +7263,41 @@ void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuil return; } - if (FlagManager::getInstance().single_hop_screenshot() && mRenderEngine->isThreaded()) { - std::vector<sp<LayerFE>> layerFEs; - auto displayState = - getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layerFEs); - - const bool supportsProtected = getRenderEngine().supportsProtectedContent(); - bool hasProtectedLayer = false; - if (allowProtected && supportsProtected) { - hasProtectedLayer = layersHasProtectedLayer(layerFEs); - } - const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected; - const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE | - (isProtected ? GRALLOC_USAGE_PROTECTED - : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); - sp<GraphicBuffer> buffer = - getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), - static_cast<android_pixel_format>(reqPixelFormat), - 1 /* layerCount */, usage, "screenshot"); - - const status_t bufferStatus = buffer->initCheck(); - if (bufferStatus != OK) { - // Animations may end up being really janky, but don't crash here. - // Otherwise an irreponsible process may cause an SF crash by allocating - // too much. - ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus); - invokeScreenCaptureError(bufferStatus, captureListener); - return; - } - const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< - renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), - renderengine::impl::ExternalTexture::Usage:: - WRITEABLE); - auto futureFence = captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, - grayscale, isProtected, attachGainmap, captureListener, - displayState, layerFEs); - futureFence.get(); - - } else { - const bool supportsProtected = getRenderEngine().supportsProtectedContent(); - bool hasProtectedLayer = false; - if (allowProtected && supportsProtected) { - auto layers = mScheduler->schedule([=]() { return getLayerSnapshotsFn(); }).get(); - hasProtectedLayer = layersHasProtectedLayer(extractLayerFEs(layers)); - } - const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected; - const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER | - GRALLOC_USAGE_HW_TEXTURE | - (isProtected ? GRALLOC_USAGE_PROTECTED - : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); - sp<GraphicBuffer> buffer = - getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), - static_cast<android_pixel_format>(reqPixelFormat), - 1 /* layerCount */, usage, "screenshot"); - - const status_t bufferStatus = buffer->initCheck(); - if (bufferStatus != OK) { - // Animations may end up being really janky, but don't crash here. - // Otherwise an irreponsible process may cause an SF crash by allocating - // too much. - ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus); - invokeScreenCaptureError(bufferStatus, captureListener); - return; - } - const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< - renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), - renderengine::impl::ExternalTexture::Usage:: - WRITEABLE); - auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture, - false /* regionSampling */, grayscale, - isProtected, attachGainmap, captureListener); - futureFence.get(); + std::vector<std::pair<Layer*, sp<LayerFE>>> layers; + auto displayState = getSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn, layers); + + const bool supportsProtected = getRenderEngine().supportsProtectedContent(); + bool hasProtectedLayer = false; + if (allowProtected && supportsProtected) { + hasProtectedLayer = layersHasProtectedLayer(layers); + } + const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected; + const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER | + GRALLOC_USAGE_HW_TEXTURE | + (isProtected ? GRALLOC_USAGE_PROTECTED + : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); + sp<GraphicBuffer> buffer = + getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(), + static_cast<android_pixel_format>(reqPixelFormat), + 1 /* layerCount */, usage, "screenshot"); + + const status_t bufferStatus = buffer->initCheck(); + if (bufferStatus != OK) { + // Animations may end up being really janky, but don't crash here. + // Otherwise an irreponsible process may cause an SF crash by allocating + // too much. + ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus); + invokeScreenCaptureError(bufferStatus, captureListener); + return; } + const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< + renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), + renderengine::impl::ExternalTexture::Usage:: + WRITEABLE); + auto futureFence = + captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale, + isProtected, attachGainmap, captureListener, displayState, layers); + futureFence.get(); } std::optional<SurfaceFlinger::OutputCompositionState> @@ -7313,22 +7336,13 @@ SurfaceFlinger::getDisplayStateFromRenderAreaBuilder(RenderAreaBuilderVariant& r return std::nullopt; } -std::vector<sp<LayerFE>> SurfaceFlinger::extractLayerFEs( - const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const { - std::vector<sp<LayerFE>> layerFEs; - layerFEs.reserve(layers.size()); - for (const auto& [_, layerFE] : layers) { - layerFEs.push_back(layerFE); - } - return layerFEs; -} - ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( const RenderAreaBuilderVariant& renderAreaBuilder, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, const sp<IScreenCaptureListener>& captureListener, - std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) { + std::optional<OutputCompositionState>& displayState, + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) { SFTRACE_CALL(); ScreenCaptureResults captureResults; @@ -7347,11 +7361,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( float displayBrightnessNits = displayState.value().displayBrightnessNits; float sdrWhitePointNits = displayState.value().sdrWhitePointNits; - // Empty vector needed to pass into renderScreenImpl for legacy path - std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers; ftl::SharedFuture<FenceResult> renderFuture = renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected, - attachGainmap, captureResults, displayState, layers, layerFEs); + captureResults, displayState, layers); if (captureResults.capturedHdrLayers && attachGainmap && FlagManager::getInstance().true_hdr_screenshots()) { @@ -7386,8 +7398,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( ScreenCaptureResults unusedResults; ftl::SharedFuture<FenceResult> hdrRenderFuture = renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale, - isProtected, attachGainmap, unusedResults, displayState, - layers, layerFEs); + isProtected, unusedResults, displayState, layers); renderFuture = ftl::Future(std::move(renderFuture)) @@ -7433,75 +7444,14 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot( return renderFuture; } -ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy( - RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn, - const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, - bool grayscale, bool isProtected, bool attachGainmap, - const sp<IScreenCaptureListener>& captureListener) { - SFTRACE_CALL(); - - auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES( - kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> { - auto layers = getLayerSnapshotsFn(); - for (auto& [layer, layerFE] : layers) { - attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK); - } - auto displayState = getDisplayStateFromRenderAreaBuilder(renderAreaBuilder); - - ScreenCaptureResults captureResults; - std::unique_ptr<const RenderArea> renderArea = - std::visit([](auto&& arg) -> std::unique_ptr<RenderArea> { return arg.build(); }, - renderAreaBuilder); - - if (!renderArea) { - ALOGW("Skipping screen capture because of invalid render area."); - if (captureListener) { - captureResults.fenceResult = base::unexpected(NO_MEMORY); - captureListener->onScreenCaptureCompleted(captureResults); - } - return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share(); - } - - auto layerFEs = extractLayerFEs(layers); - ftl::SharedFuture<FenceResult> renderFuture = - renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected, - attachGainmap, captureResults, displayState, layers, layerFEs); - - if (captureListener) { - // Defer blocking on renderFuture back to the Binder thread. - return ftl::Future(std::move(renderFuture)) - .then([captureListener, captureResults = std::move(captureResults)]( - FenceResult fenceResult) mutable -> FenceResult { - captureResults.fenceResult = std::move(fenceResult); - captureListener->onScreenCaptureCompleted(captureResults); - return base::unexpected(NO_ERROR); - }) - .share(); - } - return renderFuture; - }; - - // TODO(b/294936197): Run takeScreenshotsFn() in a binder thread to reduce the number - // of calls on the main thread. - auto future = - mScheduler->schedule(FTL_FAKE_GUARD(kMainThreadContext, std::move(takeScreenshotFn))); - - // Flatten nested futures. - auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) { - return future; - }); - - return chain.share(); -} - ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer, - bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, - ScreenCaptureResults& captureResults, std::optional<OutputCompositionState>& displayState, - std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) { + bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults& captureResults, + std::optional<OutputCompositionState>& displayState, + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) { SFTRACE_CALL(); - for (auto& layerFE : layerFEs) { + for (auto& [_, layerFE] : layers) { frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get(); captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure); captureResults.capturedHdrLayers |= isHdrLayer(*snapshot); @@ -7560,29 +7510,32 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( captureResults.buffer = capturedBuffer->getBuffer(); ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK}; - if (!layerFEs.empty()) { - const sp<LayerFE>& layerFE = layerFEs.back(); + if (!layers.empty()) { + const sp<LayerFE>& layerFE = layers.back().second; layerStack = layerFE->getCompositionState()->outputFilter.layerStack; } - auto copyLayerFEs = [&layerFEs]() { - std::vector<sp<compositionengine::LayerFE>> ceLayerFEs; - ceLayerFEs.reserve(layerFEs.size()); - for (const auto& layerFE : layerFEs) { - ceLayerFEs.push_back(layerFE); - } - return ceLayerFEs; - }; - auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace, sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected, - layerFEs = copyLayerFEs(), layerStack, regionSampling, + layers = std::move(layers), layerStack, regionSampling, renderArea = std::move(renderArea), renderIntent, enableLocalTonemapping]() -> FenceResult { std::unique_ptr<compositionengine::CompositionEngine> compositionEngine = mFactory.createCompositionEngine(); compositionEngine->setRenderEngine(mRenderEngine.get()); + std::vector<sp<compositionengine::LayerFE>> layerFEs; + layerFEs.reserve(layers.size()); + for (auto& [layer, layerFE] : layers) { + // Release fences were not yet added for non-threaded render engine. To avoid + // deadlocks between main thread and binder threads waiting for the future fence + // result, fences should be added to layers in the same hop onto the main thread. + if (!mRenderEngine->isThreaded()) { + attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK); + } + layerFEs.push_back(layerFE); + } + compositionengine::Output::ColorProfile colorProfile{.dataspace = dataspace, .renderIntent = renderIntent}; @@ -7640,13 +7593,9 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( // // TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call // to CompositionEngine::present. - ftl::SharedFuture<FenceResult> presentFuture; - if (FlagManager::getInstance().single_hop_screenshot() && mRenderEngine->isThreaded()) { - presentFuture = ftl::yield(present()).share(); - } else { - presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share() - : ftl::yield(present()).share(); - } + ftl::SharedFuture<FenceResult> presentFuture = mRenderEngine->isThreaded() + ? ftl::yield(present()).share() + : mScheduler->schedule(std::move(present)).share(); return presentFuture; } @@ -7710,7 +7659,8 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str()); if (const bool isPacesetter = - mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode())) { + mScheduler->onDisplayModeChanged(displayId, selector.getActiveMode(), + /*clearContentRequirements*/ true)) { mDisplayModeController.updateKernelIdleTimer(displayId); } @@ -8598,6 +8548,9 @@ void SurfaceComposerAIDL::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo& outInfo->activeDisplayModeId = info.activeDisplayModeId; outInfo->renderFrameRate = info.renderFrameRate; outInfo->hasArrSupport = info.hasArrSupport; + gui::FrameRateCategoryRate& frameRateCategoryRate = outInfo->frameRateCategoryRate; + frameRateCategoryRate.normal = info.frameRateCategoryRate.getNormal(); + frameRateCategoryRate.high = info.frameRateCategoryRate.getHigh(); outInfo->supportedColorModes.clear(); outInfo->supportedColorModes.reserve(info.supportedColorModes.size()); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 31218edbe7..d3479b7e52 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -57,6 +57,7 @@ #include <utils/threads.h> #include <compositionengine/OutputColorSetting.h> +#include <compositionengine/impl/OutputCompositionState.h> #include <scheduler/Fps.h> #include <scheduler/PresentLatencyTracker.h> #include <scheduler/Time.h> @@ -70,7 +71,6 @@ #include "Display/PhysicalDisplay.h" #include "DisplayDevice.h" #include "DisplayHardware/HWC2.h" -#include "DisplayHardware/PowerAdvisor.h" #include "DisplayIdGenerator.h" #include "Effects/Daltonizer.h" #include "FrontEnd/DisplayInfo.h" @@ -81,6 +81,7 @@ #include "FrontEnd/TransactionHandler.h" #include "LayerVector.h" #include "MutexUtils.h" +#include "PowerAdvisor/PowerAdvisor.h" #include "Scheduler/ISchedulerCallback.h" #include "Scheduler/RefreshRateSelector.h" #include "Scheduler/Scheduler.h" @@ -851,13 +852,14 @@ private: void attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack); // Checks if a protected layer exists in a list of layers. - bool layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const; + bool layersHasProtectedLayer(const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const; using OutputCompositionState = compositionengine::impl::OutputCompositionState; std::optional<OutputCompositionState> getSnapshotsFromMainThread( RenderAreaBuilderVariant& renderAreaBuilder, - GetLayerSnapshotsFunction getLayerSnapshotsFn, std::vector<sp<LayerFE>>& layerFEs); + GetLayerSnapshotsFunction getLayerSnapshotsFn, + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers); void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction, ui::Size bufferSize, ui::PixelFormat, bool allowProtected, @@ -866,32 +868,19 @@ private: std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder( RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext); - // Legacy layer raw pointer is not safe to access outside the main thread. - // Creates a new vector consisting only of LayerFEs, which can be safely - // accessed outside the main thread. - std::vector<sp<LayerFE>> extractLayerFEs( - const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const; - ftl::SharedFuture<FenceResult> captureScreenshot( const RenderAreaBuilderVariant& renderAreaBuilder, const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, const sp<IScreenCaptureListener>& captureListener, std::optional<OutputCompositionState>& displayState, - std::vector<sp<LayerFE>>& layerFEs); - - ftl::SharedFuture<FenceResult> captureScreenshotLegacy( - RenderAreaBuilderVariant, GetLayerSnapshotsFunction, - const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling, - bool grayscale, bool isProtected, bool attachGainmap, - const sp<IScreenCaptureListener>&); + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers); ftl::SharedFuture<FenceResult> renderScreenImpl( const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&, - bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap, - ScreenCaptureResults&, std::optional<OutputCompositionState>& displayState, - std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, - std::vector<sp<LayerFE>>& layerFEs); + bool regionSampling, bool grayscale, bool isProtected, ScreenCaptureResults&, + std::optional<OutputCompositionState>& displayState, + std::vector<std::pair<Layer*, sp<LayerFE>>>& layers); void readPersistentProperties(); @@ -1253,7 +1242,6 @@ private: // latched. std::unordered_set<std::pair<sp<Layer>, gui::GameMode>, LayerIntHash> mLayersWithQueuedFrames; std::unordered_set<sp<Layer>, SpHash<Layer>> mLayersWithBuffersRemoved; - std::unordered_set<uint32_t> mLayersIdsWithQueuedFrames; // Sorted list of layers that were composed during previous frame. This is used to // avoid an expensive traversal of the layer hierarchy when there are no @@ -1373,7 +1361,7 @@ private: sp<os::IInputFlinger> mInputFlinger; InputWindowCommands mInputWindowCommands; - std::unique_ptr<Hwc2::PowerAdvisor> mPowerAdvisor; + std::unique_ptr<adpf::PowerAdvisor> mPowerAdvisor; void enableRefreshRateOverlay(bool enable) REQUIRES(mStateLock, kMainThreadContext); @@ -1423,6 +1411,11 @@ private: // Whether a display should be turned on when initialized bool mSkipPowerOnForQuiescent; + // used for omitting vsync callbacks to apps when the display is not updatable + int mRefreshableDisplays GUARDED_BY(mStateLock) = 0; + void incRefreshableDisplays() REQUIRES(mStateLock); + void decRefreshableDisplays() REQUIRES(mStateLock); + frontend::LayerLifecycleManager mLayerLifecycleManager GUARDED_BY(kMainThreadContext); frontend::LayerHierarchyBuilder mLayerHierarchyBuilder GUARDED_BY(kMainThreadContext); frontend::LayerSnapshotBuilder mLayerSnapshotBuilder GUARDED_BY(kMainThreadContext); diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp index de4825ba62..b22ec66819 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.cpp +++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp @@ -144,7 +144,7 @@ status_t TransactionCallbackInvoker::addCallbackHandle(const sp<CallbackHandle>& eventStats, handle->previousReleaseCallbackId); if (handle->bufferReleaseChannel && handle->previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) { - mBufferReleases.emplace_back(handle->bufferReleaseChannel, + mBufferReleases.emplace_back(handle->name, handle->bufferReleaseChannel, handle->previousReleaseCallbackId, handle->previousReleaseFence, handle->currentMaxAcquiredBufferCount); @@ -159,8 +159,13 @@ void TransactionCallbackInvoker::addPresentFence(sp<Fence> presentFence) { void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) { for (const auto& bufferRelease : mBufferReleases) { - bufferRelease.channel->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence, - bufferRelease.currentMaxAcquiredBufferCount); + status_t status = bufferRelease.channel + ->writeReleaseFence(bufferRelease.callbackId, bufferRelease.fence, + bufferRelease.currentMaxAcquiredBufferCount); + if (status != OK) { + ALOGE("[%s] writeReleaseFence failed. error %d (%s)", bufferRelease.layerName.c_str(), + -status, strerror(-status)); + } } mBufferReleases.clear(); diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h index d81d8d07f4..178ddbbe79 100644 --- a/services/surfaceflinger/TransactionCallbackInvoker.h +++ b/services/surfaceflinger/TransactionCallbackInvoker.h @@ -83,6 +83,7 @@ private: mCompletedTransactions; struct BufferRelease { + std::string layerName; std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> channel; ReleaseCallbackId callbackId; sp<Fence> fence; diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 8a81c56bc8..12616e3edb 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -139,6 +139,7 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(vrr_bugfix_dropped_frame); DUMP_READ_ONLY_FLAG(restore_blur_step); DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro); + DUMP_READ_ONLY_FLAG(no_vsyncs_on_screen_off); DUMP_READ_ONLY_FLAG(protected_if_client); DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout); DUMP_READ_ONLY_FLAG(graphite_renderengine); @@ -153,9 +154,11 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(override_trusted_overlay); DUMP_READ_ONLY_FLAG(flush_buffer_slots_to_uncache); DUMP_READ_ONLY_FLAG(force_compile_graphite_renderengine); - DUMP_READ_ONLY_FLAG(single_hop_screenshot); DUMP_READ_ONLY_FLAG(trace_frame_rate_override); DUMP_READ_ONLY_FLAG(true_hdr_screenshots); + DUMP_READ_ONLY_FLAG(display_config_error_hal); + DUMP_READ_ONLY_FLAG(connected_display_hdr); + DUMP_READ_ONLY_FLAG(deprecate_frame_tracker); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG @@ -243,6 +246,7 @@ FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan") FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "") FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step") FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "") +FLAG_MANAGER_READ_ONLY_FLAG(no_vsyncs_on_screen_off, "debug.sf.no_vsyncs_on_screen_off") FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "") FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, ""); FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "") @@ -258,8 +262,10 @@ FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_s FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, ""); FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, ""); FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, ""); -FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, ""); FLAG_MANAGER_READ_ONLY_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots"); +FLAG_MANAGER_READ_ONLY_FLAG(display_config_error_hal, ""); +FLAG_MANAGER_READ_ONLY_FLAG(connected_display_hdr, ""); +FLAG_MANAGER_READ_ONLY_FLAG(deprecate_frame_tracker, ""); /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index b097bf9bc9..f5bea7237f 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -77,6 +77,7 @@ public: bool renderable_buffer_usage() const; bool restore_blur_step() const; bool dont_skip_on_early_ro() const; + bool no_vsyncs_on_screen_off() const; bool protected_if_client() const; bool idle_screen_refresh_rate_timeout() const; bool graphite_renderengine() const; @@ -91,9 +92,11 @@ public: bool override_trusted_overlay() const; bool flush_buffer_slots_to_uncache() const; bool force_compile_graphite_renderengine() const; - bool single_hop_screenshot() const; bool trace_frame_rate_override() const; bool true_hdr_screenshots() const; + bool display_config_error_hal() const; + bool connected_display_hdr() const; + bool deprecate_frame_tracker() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp index ae502cf4b4..1de6b4aed9 100644 --- a/services/surfaceflinger/fuzzer/Android.bp +++ b/services/surfaceflinger/fuzzer/Android.bp @@ -28,16 +28,18 @@ package { cc_defaults { name: "surfaceflinger_fuzz_defaults", static_libs: [ - "libc++fs", "libsurfaceflinger_common", ], srcs: [ + ":libsurfaceflinger_backend_mock_sources", + ":libsurfaceflinger_mock_sources", ":libsurfaceflinger_sources", ], defaults: [ "libsurfaceflinger_defaults", ], header_libs: [ + "libsurfaceflinger_backend_mock_headers", "libsurfaceflinger_headers", ], cflags: [ diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index d724dfce5b..014c736a6b 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -21,12 +21,20 @@ flag { flag { name: "arr_setframerate_api" namespace: "core_graphics" - description: "New setFrameRate API for Android 16" + description: "New SDK Surface#setFrameRate API and Surface.FrameRateParams for Android 16" bug: "356987016" is_fixed_read_only: true } # arr_setframerate_api flag { + name: "arr_surfacecontrol_setframerate_api" + namespace: "core_graphics" + description: "New SDK SurfaceControl.Transaction#setFrameRate API for Android 16" + bug: "356987016" + is_fixed_read_only: true +} # arr_surfacecontrol_setframerate_api + +flag { name: "ce_fence_promise" namespace: "window_surfaces" description: "Moves logic for buffer release fences into LayerFE" @@ -49,6 +57,14 @@ flag { } # commit_not_composited flag { + name: "connected_display_hdr" + namespace: "core_graphics" + description: "enable connected display hdr capability" + bug: "374182788" + is_fixed_read_only: true +} # connected_display_hdr + +flag { name: "correct_dpi_with_display_size" namespace: "core_graphics" description: "indicate whether missing or likely incorrect dpi should be corrected using the display size." @@ -60,6 +76,17 @@ flag { } # correct_dpi_with_display_size flag { + name: "deprecate_frame_tracker" + namespace: "core_graphics" + description: "Deprecate using FrameTracker to accumulate and provide FrameStats" + bug: "241394120" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} # deprecate_frame_tracker + +flag { name: "deprecate_vsync_sf" namespace: "core_graphics" description: "Depracate eVsyncSourceSurfaceFlinger and use vsync_app everywhere" @@ -82,6 +109,14 @@ flag { } # detached_mirror flag { + name: "display_config_error_hal" + namespace: "core_graphics" + description: "Report HAL display configuration errors like modeset failure or link training failure" + bug: "374184110" + is_fixed_read_only: true +} # display_config_error_hal + +flag { name: "filter_frames_before_trace_starts" namespace: "core_graphics" description: "Do not trace FrameTimeline events for frames started before the trace started" @@ -142,6 +177,14 @@ flag { } # local_tonemap_screenshots flag { + name: "no_vsyncs_on_screen_off" + namespace: "core_graphics" + description: "Stop vsync / Choreographer callbacks to apps when the screen is off" + bug: "331636736" + is_fixed_read_only: true +} # no_vsyncs_on_screen_off + +flag { name: "single_hop_screenshot" namespace: "window_surfaces" description: "Only access SF main thread once during a screenshot" diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index e6fed63d96..7b6e4bff6a 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -341,9 +341,9 @@ TEST_F(CredentialsTest, TransactionPermissionTest) { WindowInfosListenerUtils windowInfosListenerUtils; std::string name = "Test Layer"; sp<IBinder> token = sp<BBinder>::make(); - WindowInfo windowInfo; - windowInfo.name = name; - windowInfo.token = token; + auto windowInfo = sp<gui::WindowInfoHandle>::make(); + windowInfo->editInfo()->name = name; + windowInfo->editInfo()->token = token; sp<SurfaceControl> surfaceControl = mComposerClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState); @@ -370,7 +370,8 @@ TEST_F(CredentialsTest, TransactionPermissionTest) { UIDFaker f(AID_SYSTEM); auto windowIsPresentAndNotTrusted = [&](const std::vector<WindowInfo>& windowInfos) { auto foundWindowInfo = - WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos); + WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(), + windowInfos); if (!foundWindowInfo) { return false; } @@ -386,7 +387,8 @@ TEST_F(CredentialsTest, TransactionPermissionTest) { Transaction().setTrustedOverlay(surfaceControl, true).apply(/*synchronous=*/true); auto windowIsPresentAndTrusted = [&](const std::vector<WindowInfo>& windowInfos) { auto foundWindowInfo = - WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos); + WindowInfosListenerUtils::findMatchingWindowInfo(*windowInfo->getInfo(), + windowInfos); if (!foundWindowInfo) { return false; } diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp index ad9a674456..2dd0dd9bd3 100644 --- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp +++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp @@ -50,9 +50,9 @@ protected: TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { std::string name = "Test Layer"; sp<IBinder> token = sp<BBinder>::make(); - WindowInfo windowInfo; - windowInfo.name = name; - windowInfo.token = token; + auto windowInfo = sp<gui::WindowInfoHandle>::make(); + windowInfo->editInfo()->name = name; + windowInfo->editInfo()->token = token; sp<SurfaceControl> surfaceControl = mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState); @@ -65,14 +65,14 @@ TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { .apply(); auto windowPresent = [&](const std::vector<WindowInfo>& windowInfos) { - return findMatchingWindowInfo(windowInfo, windowInfos); + return findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); }; ASSERT_TRUE(waitForWindowInfosPredicate(windowPresent)); Transaction().reparent(surfaceControl, nullptr).apply(); auto windowNotPresent = [&](const std::vector<WindowInfo>& windowInfos) { - return !findMatchingWindowInfo(windowInfo, windowInfos); + return !findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); }; ASSERT_TRUE(waitForWindowInfosPredicate(windowNotPresent)); } @@ -80,9 +80,9 @@ TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { TEST_F(WindowInfosListenerTest, WindowInfoChanged) { std::string name = "Test Layer"; sp<IBinder> token = sp<BBinder>::make(); - WindowInfo windowInfo; - windowInfo.name = name; - windowInfo.token = token; + auto windowInfo = sp<gui::WindowInfoHandle>::make(); + windowInfo->editInfo()->name = name; + windowInfo->editInfo()->token = token; sp<SurfaceControl> surfaceControl = mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceBufferState); @@ -96,7 +96,7 @@ TEST_F(WindowInfosListenerTest, WindowInfoChanged) { .apply(); auto windowIsPresentAndTouchableRegionEmpty = [&](const std::vector<WindowInfo>& windowInfos) { - auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); if (!foundWindowInfo) { return false; } @@ -104,19 +104,19 @@ TEST_F(WindowInfosListenerTest, WindowInfoChanged) { }; ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionEmpty)); - windowInfo.addTouchableRegion({0, 0, 50, 50}); + windowInfo->editInfo()->addTouchableRegion({0, 0, 50, 50}); Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply(); auto windowIsPresentAndTouchableRegionMatches = [&](const std::vector<WindowInfo>& windowInfos) { - auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + auto foundWindowInfo = findMatchingWindowInfo(*windowInfo->getInfo(), windowInfos); if (!foundWindowInfo) { return false; } auto touchableRegion = foundWindowInfo->transform.transform(foundWindowInfo->touchableRegion); - return touchableRegion.hasSameRects(windowInfo.touchableRegion); + return touchableRegion.hasSameRects(windowInfo->getInfo()->touchableRegion); }; ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionMatches)); } diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h index b472047bd6..97946205ba 100644 --- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h +++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h @@ -218,6 +218,17 @@ public: mLifecycleManager.applyTransactions(transactions); } + void setAutoRefresh(uint32_t id, bool autoRefresh) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eAutoRefreshChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.autoRefresh = autoRefresh; + mLifecycleManager.applyTransactions(transactions); + } + void hideLayer(uint32_t id) { setFlags(id, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden); } diff --git a/services/surfaceflinger/tests/tracing/Android.bp b/services/surfaceflinger/tests/tracing/Android.bp index bce1406e9c..6eb7f4a220 100644 --- a/services/surfaceflinger/tests/tracing/Android.bp +++ b/services/surfaceflinger/tests/tracing/Android.bp @@ -35,9 +35,6 @@ cc_test { ":libsurfaceflinger_mock_sources", "TransactionTraceTestSuite.cpp", ], - static_libs: [ - "libc++fs", - ], header_libs: [ "libsurfaceflinger_mocks_headers", ], diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 40a6fb857f..6af51435c3 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -22,15 +22,44 @@ package { default_team: "trendy_team_android_core_graphics_stack", } +// This is a step towards pulling out the "backend" sources to clean up the +// dependency graph between CompositionEngine and SurfaceFlinger. +// MockNativeWindow doesn't strictly belong here, but this works for now so +// that CompositionEngine tests can use these mocks. filegroup { - name: "libsurfaceflinger_mock_sources", + name: "libsurfaceflinger_backend_mock_sources", srcs: [ - "mock/DisplayHardware/MockPowerHalController.cpp", + ":poweradvisor_mock_sources", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockHWC2.cpp", - "mock/DisplayHardware/MockIPower.cpp", - "mock/DisplayHardware/MockPowerHintSessionWrapper.cpp", - "mock/DisplayHardware/MockPowerAdvisor.cpp", + "mock/DisplayHardware/MockHWComposer.cpp", + "mock/system/window/MockNativeWindow.cpp", + ], +} + +cc_library_headers { + name: "libsurfaceflinger_backend_mock_headers", + export_include_dirs: ["."], + static_libs: [ + "libgmock", + "libgtest", + ], + export_static_lib_headers: [ + "libgmock", + "libgtest", + ], +} + +filegroup { + name: "poweradvisor_mock_sources", + srcs: [ + "mock/PowerAdvisor/*.cpp", + ], +} + +filegroup { + name: "libsurfaceflinger_mock_sources", + srcs: [ "mock/MockEventThread.cpp", "mock/MockFrameTimeline.cpp", "mock/MockFrameTracer.cpp", @@ -39,7 +68,6 @@ filegroup { "mock/MockVsyncController.cpp", "mock/MockVSyncDispatch.cpp", "mock/MockVSyncTracker.cpp", - "mock/system/window/MockNativeWindow.cpp", ], } @@ -57,84 +85,12 @@ cc_test { "surfaceflinger_defaults", ], test_suites: ["device-tests"], - static_libs: ["libc++fs"], header_libs: ["surfaceflinger_tests_common_headers"], srcs: [ + ":libsurfaceflinger_backend_mock_sources", ":libsurfaceflinger_mock_sources", ":libsurfaceflinger_sources", - "libsurfaceflinger_unittest_main.cpp", - "ActiveDisplayRotationFlagsTest.cpp", - "BackgroundExecutorTest.cpp", - "CommitTest.cpp", - "CompositionTest.cpp", - "DaltonizerTest.cpp", - "DisplayIdGeneratorTest.cpp", - "DisplayTransactionTest.cpp", - "DisplayDevice_GetBestColorModeTest.cpp", - "DisplayDevice_SetDisplayBrightnessTest.cpp", - "DisplayDevice_SetProjectionTest.cpp", - "DisplayModeControllerTest.cpp", - "EventThreadTest.cpp", - "FlagManagerTest.cpp", - "FpsReporterTest.cpp", - "FpsTest.cpp", - "FramebufferSurfaceTest.cpp", - "FrameRateOverrideMappingsTest.cpp", - "FrameTimelineTest.cpp", - "HWComposerTest.cpp", - "JankTrackerTest.cpp", - "OneShotTimerTest.cpp", - "LayerHistoryIntegrationTest.cpp", - "LayerInfoTest.cpp", - "LayerMetadataTest.cpp", - "LayerHierarchyTest.cpp", - "LayerLifecycleManagerTest.cpp", - "LayerSnapshotTest.cpp", - "LayerTestUtils.cpp", - "MessageQueueTest.cpp", - "PowerAdvisorTest.cpp", - "SmallAreaDetectionAllowMappingsTest.cpp", - "SurfaceFlinger_ColorMatrixTest.cpp", - "SurfaceFlinger_CreateDisplayTest.cpp", - "SurfaceFlinger_DestroyDisplayTest.cpp", - "SurfaceFlinger_DisplayModeSwitching.cpp", - "SurfaceFlinger_DisplayTransactionCommitTest.cpp", - "SurfaceFlinger_ExcludeDolbyVisionTest.cpp", - "SurfaceFlinger_FoldableTest.cpp", - "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp", - "SurfaceFlinger_GetDisplayStatsTest.cpp", - "SurfaceFlinger_HdrOutputControlTest.cpp", - "SurfaceFlinger_HotplugTest.cpp", - "SurfaceFlinger_InitializeDisplaysTest.cpp", - "SurfaceFlinger_NotifyExpectedPresentTest.cpp", - "SurfaceFlinger_NotifyPowerBoostTest.cpp", - "SurfaceFlinger_PowerHintTest.cpp", - "SurfaceFlinger_SetDisplayStateTest.cpp", - "SurfaceFlinger_SetPowerModeInternalTest.cpp", - "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp", - "SchedulerTest.cpp", - "RefreshRateSelectorTest.cpp", - "RefreshRateStatsTest.cpp", - "RegionSamplingTest.cpp", - "TestableScheduler.cpp", - "TimeStatsTest.cpp", - "FrameTracerTest.cpp", - "TransactionApplicationTest.cpp", - "TransactionFrameTracerTest.cpp", - "TransactionProtoParserTest.cpp", - "TransactionSurfaceFrameTest.cpp", - "TransactionTraceWriterTest.cpp", - "TransactionTracingTest.cpp", - "TunnelModeEnabledReporterTest.cpp", - "VSyncCallbackRegistrationTest.cpp", - "VSyncDispatchTimerQueueTest.cpp", - "VSyncDispatchRealtimeTest.cpp", - "VsyncModulatorTest.cpp", - "VSyncPredictorTest.cpp", - "VSyncReactorTest.cpp", - "VsyncConfigurationTest.cpp", - "VsyncScheduleTest.cpp", - "WindowInfosListenerInvokerTest.cpp", + "*.cpp", ], } diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h index d4c801f050..b517ff02ad 100644 --- a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h +++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h @@ -22,8 +22,8 @@ #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" #include "mock/MockTimeStats.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" #include "mock/system/window/MockNativeWindow.h" namespace android { @@ -33,11 +33,11 @@ struct CommitAndCompositeTest : testing::Test { void SetUp() override { mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); mComposer = new Hwc2::mock::Composer(); - mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); + mPowerAdvisor = new adpf::mock::PowerAdvisor(); mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); + mFlinger.setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor>(mPowerAdvisor)); constexpr bool kIsPrimary = true; FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) @@ -79,7 +79,7 @@ struct CommitAndCompositeTest : testing::Test { sp<compositionengine::mock::DisplaySurface>::make(); sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); mock::TimeStats* mTimeStats = new mock::TimeStats(); - Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; + adpf::mock::PowerAdvisor* mPowerAdvisor = nullptr; Hwc2::mock::Composer* mComposer = nullptr; }; diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 4f72424bd7..860ad2e013 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -40,10 +40,10 @@ #include "Layer.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" #include "mock/MockEventThread.h" #include "mock/MockTimeStats.h" #include "mock/MockVsyncController.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" #include "mock/system/window/MockNativeWindow.h" namespace android { @@ -110,9 +110,9 @@ public: mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); mComposer = new Hwc2::mock::Composer(); - mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); + mPowerAdvisor = new adpf::mock::PowerAdvisor(); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); + mFlinger.setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor>(mPowerAdvisor)); mFlinger.mutableMaxRenderTargetSize() = 16384; } @@ -158,7 +158,7 @@ public: Hwc2::mock::Composer* mComposer = nullptr; renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); mock::TimeStats* mTimeStats = new mock::TimeStats(); - Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; + adpf::mock::PowerAdvisor* mPowerAdvisor = nullptr; sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE; diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index db3c0a1d69..fa976c8091 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -47,10 +47,10 @@ #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockDisplayMode.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" #include "mock/MockEventThread.h" #include "mock/MockNativeWindowSurface.h" #include "mock/MockVsyncController.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" #include "mock/system/window/MockNativeWindow.h" namespace android { @@ -118,7 +118,7 @@ public: sp<GraphicBuffer> mBuffer = sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN); - Hwc2::mock::PowerAdvisor mPowerAdvisor; + adpf::mock::PowerAdvisor mPowerAdvisor; FakeDisplayInjector mFakeDisplayInjector{mFlinger, mPowerAdvisor, mNativeWindow}; diff --git a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h index 6e4bf2b06e..744c53637a 100644 --- a/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h +++ b/services/surfaceflinger/tests/unittests/FakeDisplayInjector.h @@ -19,14 +19,14 @@ #include <gmock/gmock.h> #include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" #include "mock/system/window/MockNativeWindow.h" namespace android { using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; +using android::adpf::mock::PowerAdvisor; using android::hardware::graphics::composer::hal::HWDisplayId; -using android::Hwc2::mock::PowerAdvisor; struct FakeDisplayInjectorArgs { PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(255u); @@ -36,7 +36,7 @@ struct FakeDisplayInjectorArgs { class FakeDisplayInjector { public: - FakeDisplayInjector(TestableSurfaceFlinger& flinger, Hwc2::mock::PowerAdvisor& powerAdvisor, + FakeDisplayInjector(TestableSurfaceFlinger& flinger, PowerAdvisor& powerAdvisor, sp<mock::NativeWindow> nativeWindow) : mFlinger(flinger), mPowerAdvisor(powerAdvisor), mNativeWindow(nativeWindow) {} @@ -89,7 +89,7 @@ public: } TestableSurfaceFlinger& mFlinger; - Hwc2::mock::PowerAdvisor& mPowerAdvisor; + PowerAdvisor& mPowerAdvisor; sp<mock::NativeWindow> mNativeWindow; }; diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index a35ae15c03..6aec7434de 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -28,7 +28,6 @@ #include "ui/GraphicTypes.h" #include <com_android_graphics_libgui_flags.h> -#include <com_android_graphics_surfaceflinger_flags.h> #define UPDATE_AND_VERIFY(BUILDER, ...) \ ({ \ @@ -1935,4 +1934,95 @@ TEST_F(LayerSnapshotTest, shouldUpdateInputWhenNoInputInfo) { EXPECT_FALSE(getSnapshot(2)->hasInputInfo()); } +// content dirty test +TEST_F(LayerSnapshotTest, contentDirtyWhenParentAlphaChanges) { + setAlpha(1, 0.5); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->contentDirty); + EXPECT_TRUE(getSnapshot(11)->contentDirty); + EXPECT_TRUE(getSnapshot(111)->contentDirty); + + // subsequent updates clear the dirty bit + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_FALSE(getSnapshot(1)->contentDirty); + EXPECT_FALSE(getSnapshot(11)->contentDirty); + EXPECT_FALSE(getSnapshot(111)->contentDirty); +} + +TEST_F(LayerSnapshotTest, contentDirtyWhenAutoRefresh) { + setAutoRefresh(1, true); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->contentDirty); + + // subsequent updates don't clear the dirty bit + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->contentDirty); + + // second update after removing auto refresh will clear content dirty + setAutoRefresh(1, false); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_FALSE(getSnapshot(1)->contentDirty); +} + +TEST_F(LayerSnapshotTest, contentDirtyWhenColorChanges) { + setColor(1, {1, 2, 3}); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->contentDirty); + + // subsequent updates clear the dirty bit + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_FALSE(getSnapshot(1)->contentDirty); +} + +TEST_F(LayerSnapshotTest, contentDirtyWhenParentGeometryChanges) { + setPosition(1, 2, 3); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->contentDirty); + + // subsequent updates clear the dirty bit + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_FALSE(getSnapshot(1)->contentDirty); +} +TEST_F(LayerSnapshotTest, shouldUpdatePictureProfileHandle) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Flag disabled, skipping test"; + } + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().layerId = 1; + transactions.back().states.front().state.layerId = 1; + transactions.back().states.front().state.what = layer_state_t::ePictureProfileHandleChanged; + transactions.back().states.front().state.pictureProfileHandle = PictureProfileHandle(3); + + mLifecycleManager.applyTransactions(transactions); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content); + + update(mSnapshotBuilder); + + EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::ePictureProfileHandleChanged); + EXPECT_EQ(getSnapshot(1)->pictureProfileHandle, PictureProfileHandle(3)); +} + +TEST_F(LayerSnapshotTest, shouldUpdatePictureProfilePriorityFromAppContentPriority) { + if (!com_android_graphics_libgui_flags_apply_picture_profiles()) { + GTEST_SKIP() << "Flag disabled, skipping test"; + } + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().layerId = 1; + transactions.back().states.front().state.layerId = 1; + transactions.back().states.front().state.what = layer_state_t::eAppContentPriorityChanged; + transactions.back().states.front().state.appContentPriority = 3; + + mLifecycleManager.applyTransactions(transactions); + EXPECT_EQ(mLifecycleManager.getGlobalChanges(), RequestedLayerState::Changes::Content); + + update(mSnapshotBuilder); + + EXPECT_EQ(getSnapshot(1)->pictureProfilePriority, 3); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index 71f9f88ba7..908637ae76 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -159,9 +159,9 @@ TEST_F(MessageQueueTest, commitTwiceWithCallback) { constexpr VsyncId vsyncId{42}; EXPECT_CALL(mTokenManager, - generateTokenForPredictions(frametimeline::TimelineItem(kStartTime.ns(), - kEndTime.ns(), - kPresentTime.ns()))) + generateTokenForPredictions( + frametimeline::TimelineItem(kStartTime.ns(), kEndTime.ns(), + kPresentTime.ns(), kPresentTime.ns()))) .WillOnce(Return(ftl::to_underlying(vsyncId))); EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, kPresentTime)).Times(1); EXPECT_NO_FATAL_FAILURE( diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp index 8375bb9a56..5c25f34ae7 100644 --- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp +++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp @@ -17,7 +17,8 @@ #undef LOG_TAG #define LOG_TAG "PowerAdvisorTest" -#include <DisplayHardware/PowerAdvisor.h> +#include "PowerAdvisor/PowerAdvisor.h" + #include <android_os.h> #include <binder/Status.h> #include <com_android_graphics_surfaceflinger_flags.h> @@ -29,18 +30,17 @@ #include <ui/DisplayId.h> #include <chrono> #include <future> -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockPowerHalController.h" -#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h" +#include "mock/PowerAdvisor/MockPowerHalController.h" +#include "mock/PowerAdvisor/MockPowerHintSessionWrapper.h" using namespace android; -using namespace android::Hwc2::mock; +using namespace android::adpf::mock; using namespace android::hardware::power; using namespace std::chrono_literals; using namespace testing; using namespace android::power; -namespace android::Hwc2::impl { +namespace android::adpf::impl { class PowerAdvisorTest : public testing::Test { public: @@ -73,7 +73,6 @@ public: void testGpuScenario(GpuTestConfig& config, WorkDuration& ret); protected: - TestableSurfaceFlinger mFlinger; std::unique_ptr<PowerAdvisor> mPowerAdvisor; MockPowerHalController* mMockPowerHalController; std::shared_ptr<MockPowerHintSessionWrapper> mMockPowerHintSession; @@ -98,7 +97,7 @@ int64_t PowerAdvisorTest::toNanos(Duration d) { } void PowerAdvisorTest::SetUp() { - mPowerAdvisor = std::make_unique<impl::PowerAdvisor>(*mFlinger.flinger()); + mPowerAdvisor = std::make_unique<impl::PowerAdvisor>([]() {}, 80ms); mPowerAdvisor->mPowerHal = std::make_unique<NiceMock<MockPowerHalController>>(); mMockPowerHalController = reinterpret_cast<MockPowerHalController*>(mPowerAdvisor->mPowerHal.get()); @@ -844,4 +843,4 @@ TEST_F(PowerAdvisorTest, fmq_sendHint_queueFull) { } } // namespace -} // namespace android::Hwc2::impl +} // namespace android::adpf::impl diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 29e1c21878..b5be8dba96 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -2240,6 +2240,46 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_Touch EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); } +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_touchBoost_twoUids_arr) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + + SET_FLAG_FOR_TEST(flags::vrr_config, true); + // Device with VRR config mode + auto selector = createSelector(kVrrMode_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, + {.ownerUid = 5678, .weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::Normal; + lr1.name = "ExplicitCategory Normal"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + auto actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true}); + // No global touch boost, for example a game that uses setFrameRate(30, default compatibility). + // However see 60 due to Normal vote. + EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 60_Hz, + actualRankedFrameRates.ranking.front().frameRateMode); + EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + // Gets touch boost because the touched (HighHint) app is different from the 30 Default app. + actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true}); + EXPECT_FRAME_RATE_MODE(kVrrMode120TE240, 120_Hz, + actualRankedFrameRates.ranking.front().frameRateMode); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); +} + TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_idleTimer_60_120_nonVrr) { SET_FLAG_FOR_TEST(flags::vrr_config, false); @@ -3825,6 +3865,51 @@ TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids) { EXPECT_TRUE(frameRateOverrides.empty()); } +TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_twoUids_arr) { + if (GetParam() != Config::FrameRateOverride::Enabled) { + return; + } + + SET_FLAG_FOR_TEST(flags::vrr_config, true); + // Device with VRR config mode + auto selector = createSelector(kVrrMode_120, kModeId120); + + std::vector<LayerRequirement> layers = {{.ownerUid = 1234, .weight = 1.f}, + {.ownerUid = 5678, .weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::Normal; + lr1.name = "ExplicitCategory Normal"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + // No global touch boost, for example a game that uses setFrameRate(30, default compatibility). + // The `displayFrameRate` is 60. + // However 30 Default app still gets frame rate override. + auto frameRateOverrides = selector.getFrameRateOverrides(layers, 60_Hz, {}); + EXPECT_EQ(2u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(1234)); + EXPECT_EQ(60_Hz, frameRateOverrides.at(1234)); + ASSERT_EQ(1u, frameRateOverrides.count(5678)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(5678)); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + // Gets touch boost because the touched (HighHint) app is different from the 30 Default app. + // The `displayFrameRate` is 120 (late touch boost). + // However 30 Default app still gets frame rate override. + frameRateOverrides = selector.getFrameRateOverrides(layers, 120_Hz, {}); + EXPECT_EQ(1u, frameRateOverrides.size()); + ASSERT_EQ(1u, frameRateOverrides.count(5678)); + EXPECT_EQ(30_Hz, frameRateOverrides.at(5678)); +} + TEST_P(RefreshRateSelectorTest, getFrameRateOverrides_withFrameRateCategory) { if (GetParam() == Config::FrameRateOverride::Disabled) { return; diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index ac09cbcea6..1fc874dad8 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -222,7 +222,7 @@ TEST_F(SchedulerTest, emitModeChangeEvent) { const auto selectorPtr = std::make_shared<RefreshRateSelector>(kDisplay1Modes, kDisplay1Mode120->getId()); mScheduler->registerDisplay(kDisplayId1, selectorPtr); - mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120); + mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120, true); mScheduler->setContentRequirements({kLayer}); @@ -250,7 +250,7 @@ TEST_F(SchedulerTest, emitModeChangeEvent) { EXPECT_CALL(*mEventThread, onModeChanged(kDisplay1Mode120_120)).Times(1); mScheduler->touchTimerCallback(TimerState::Reset); - mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120); + mScheduler->onDisplayModeChanged(kDisplayId1, kDisplay1Mode120_120, true); } TEST_F(SchedulerTest, calculateMaxAcquiredBufferCount) { diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 4dec5f6b6a..7f0b7a6585 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -44,22 +44,22 @@ #include "NativeWindowSurface.h" #include "RenderArea.h" #include "Scheduler/RefreshRateSelector.h" +#include "Scheduler/VSyncTracker.h" +#include "Scheduler/VsyncController.h" #include "SurfaceFlinger.h" #include "TestableScheduler.h" #include "android/gui/ISurfaceComposerClient.h" + #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockDisplayMode.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" #include "mock/MockEventThread.h" #include "mock/MockFrameTimeline.h" #include "mock/MockFrameTracer.h" #include "mock/MockSchedulerCallback.h" -#include "mock/system/window/MockNativeWindow.h" - -#include "Scheduler/VSyncTracker.h" -#include "Scheduler/VsyncController.h" #include "mock/MockVSyncTracker.h" #include "mock/MockVsyncController.h" +#include "mock/PowerAdvisor/MockPowerAdvisor.h" +#include "mock/system/window/MockNativeWindow.h" namespace android { @@ -190,7 +190,7 @@ public: &mFlinger->mCompositionEngine->getHwComposer()); } - void setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor> powerAdvisor) { + void setupPowerAdvisor(std::unique_ptr<adpf::PowerAdvisor> powerAdvisor) { mFlinger->mPowerAdvisor = std::move(powerAdvisor); } @@ -472,12 +472,10 @@ public: ScreenCaptureResults captureResults; auto displayState = std::optional{display->getCompositionDisplay()->getState()}; auto layers = getLayerSnapshotsFn(); - auto layerFEs = mFlinger->extractLayerFEs(layers); return mFlinger->renderScreenImpl(renderArea.get(), buffer, regionSampling, false /* grayscale */, false /* isProtected */, - false /* attachGainmap */, captureResults, displayState, - layers, layerFEs); + captureResults, displayState, layers); } auto getLayerSnapshotsForScreenshotsFn(ui::LayerStack layerStack, uint32_t uid) { @@ -1162,7 +1160,7 @@ private: scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback; std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; scheduler::TestableScheduler* mScheduler = nullptr; - Hwc2::mock::PowerAdvisor mPowerAdvisor; + adpf::mock::PowerAdvisor mPowerAdvisor; }; } // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 615cc948ed..88052db7e2 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -185,7 +185,10 @@ public: (Display, std::vector<Layer>*, std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)); MOCK_METHOD(Error, setLayerLuts, - (Display, Layer, std::vector<aidl::android::hardware::graphics::composer3::Lut>&)); + (Display, Layer, aidl::android::hardware::graphics::composer3::Luts&)); + MOCK_METHOD(Error, getMaxLayerPictureProfiles, (Display, int32_t*)); + MOCK_METHOD(Error, setDisplayPictureProfileId, (Display, PictureProfileId id)); + MOCK_METHOD(Error, setLayerPictureProfileId, (Display, Layer, PictureProfileId id)); }; } // namespace Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h index 53ed2e1f20..fa74492ac5 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h @@ -52,7 +52,7 @@ public: (override)); MOCK_METHOD(hal::Error, getName, (std::string *), (const, override)); MOCK_METHOD(hal::Error, getRequests, - (hal::DisplayRequest *, (std::unordered_map<Layer *, hal::LayerRequest> *)), + (hal::DisplayRequest*, (std::unordered_map<Layer*, hal::LayerRequest>*)), (override)); MOCK_METHOD((ftl::Expected<ui::DisplayConnectionType, hal::Error>), getConnectionType, (), (const, override)); @@ -111,7 +111,9 @@ public: (aidl::android::hardware::graphics::composer3::OverlayProperties *), (const override)); MOCK_METHOD(hal::Error, getRequestedLuts, - ((HWC2::Display::LayerLuts*), (HWC2::Display::LutFileDescriptorMapper&)), + (HWC2::Display::LayerLuts*, HWC2::Display::LutFileDescriptorMapper&), (override)); + MOCK_METHOD(hal::Error, getMaxLayerPictureProfiles, (int32_t*), (override)); + MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&), (override)); }; @@ -126,6 +128,8 @@ public: (uint32_t, const android::sp<android::GraphicBuffer> &, const android::sp<android::Fence> &), (override)); + MOCK_METHOD(hal::Error, setBufferSlotsToClear, + (const std::vector<uint32_t>& slotsToClear, uint32_t activeBufferSlot), (override)); MOCK_METHOD(hal::Error, setSurfaceDamage, (const android::Region &), (override)); MOCK_METHOD(hal::Error, setBlendMode, (hal::BlendMode), (override)); MOCK_METHOD(hal::Error, setColor, (aidl::android::hardware::graphics::composer3::Color), @@ -147,8 +151,10 @@ public: (const std::string &, bool, const std::vector<uint8_t> &), (override)); MOCK_METHOD(hal::Error, setBrightness, (float), (override)); MOCK_METHOD(hal::Error, setBlockingRegion, (const android::Region &), (override)); - MOCK_METHOD(hal::Error, setLuts, - (std::vector<aidl::android::hardware::graphics::composer3::Lut>&), (override)); + MOCK_METHOD(hal::Error, setLuts, (aidl::android::hardware::graphics::composer3::Luts&), + (override)); + MOCK_METHOD(hal::Error, setPictureProfileHandle, (const android::PictureProfileHandle&), + (override)); }; } // namespace android::HWC2::mock diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.cpp index ae52670fe2..f3106332fc 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.cpp +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.cpp @@ -16,17 +16,11 @@ #include "MockHWComposer.h" -namespace android { - -// This will go away once HWComposer is moved into the "backend" library -HWComposer::~HWComposer() = default; - -namespace mock { +namespace android::mock { // The Google Mock documentation recommends explicit non-header instantiations // for better compile time performance. HWComposer::HWComposer() = default; HWComposer::~HWComposer() = default; -} // namespace mock -} // namespace android +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h new file mode 100644 index 0000000000..88f83d2e07 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWComposer.h @@ -0,0 +1,156 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <gmock/gmock.h> + +#include "DisplayHardware/HWComposer.h" + +namespace android::mock { + +class HWComposer : public android::HWComposer { +public: + using HWDisplayId = android::hardware::graphics::composer::hal::HWDisplayId; + using PowerMode = android::hardware::graphics::composer::hal::PowerMode; + + HWComposer(); + ~HWComposer() override; + + MOCK_METHOD(void, setCallback, (HWC2::ComposerCallback&), (override)); + MOCK_METHOD(bool, getDisplayIdentificationData, + (HWDisplayId, uint8_t*, DisplayIdentificationData*), (const, override)); + MOCK_METHOD(bool, hasCapability, (aidl::android::hardware::graphics::composer3::Capability), + (const, override)); + MOCK_METHOD(bool, hasDisplayCapability, + (HalDisplayId, aidl::android::hardware::graphics::composer3::DisplayCapability), + (const, override)); + + MOCK_METHOD(size_t, getMaxVirtualDisplayCount, (), (const, override)); + MOCK_METHOD(size_t, getMaxVirtualDisplayDimension, (), (const, override)); + MOCK_METHOD(bool, allocateVirtualDisplay, (HalVirtualDisplayId, ui::Size, ui::PixelFormat*), + (override)); + MOCK_METHOD(void, allocatePhysicalDisplay, + (hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>), (override)); + + MOCK_METHOD(std::shared_ptr<HWC2::Layer>, createLayer, (HalDisplayId), (override)); + MOCK_METHOD(status_t, getDeviceCompositionChanges, + (HalDisplayId, bool, std::optional<std::chrono::steady_clock::time_point>, nsecs_t, + Fps, std::optional<android::HWComposer::DeviceRequestedChanges>*)); + MOCK_METHOD(status_t, setClientTarget, + (HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace, + float), + (override)); + MOCK_METHOD(status_t, presentAndGetReleaseFences, + (HalDisplayId, std::optional<std::chrono::steady_clock::time_point>), (override)); + MOCK_METHOD(status_t, executeCommands, (HalDisplayId)); + MOCK_METHOD(status_t, setPowerMode, (PhysicalDisplayId, PowerMode), (override)); + MOCK_METHOD(status_t, setColorTransform, (HalDisplayId, const mat4&), (override)); + MOCK_METHOD(void, disconnectDisplay, (HalDisplayId), (override)); + MOCK_METHOD(sp<Fence>, getPresentFence, (HalDisplayId), (const, override)); + MOCK_METHOD(nsecs_t, getPresentTimestamp, (PhysicalDisplayId), (const, override)); + MOCK_METHOD(sp<Fence>, getLayerReleaseFence, (HalDisplayId, HWC2::Layer*), (const, override)); + MOCK_METHOD(status_t, setOutputBuffer, + (HalVirtualDisplayId, const sp<Fence>&, const sp<GraphicBuffer>&), (override)); + MOCK_METHOD(void, clearReleaseFences, (HalDisplayId), (override)); + MOCK_METHOD(status_t, getHdrCapabilities, (HalDisplayId, HdrCapabilities*), (override)); + MOCK_METHOD(int32_t, getSupportedPerFrameMetadata, (HalDisplayId), (const, override)); + MOCK_METHOD(std::vector<ui::RenderIntent>, getRenderIntents, (HalDisplayId, ui::ColorMode), + (const, override)); + MOCK_METHOD(mat4, getDataspaceSaturationMatrix, (HalDisplayId, ui::Dataspace), (override)); + MOCK_METHOD(status_t, getDisplayedContentSamplingAttributes, + (HalDisplayId, ui::PixelFormat*, ui::Dataspace*, uint8_t*), (override)); + MOCK_METHOD(status_t, setDisplayContentSamplingEnabled, (HalDisplayId, bool, uint8_t, uint64_t), + (override)); + MOCK_METHOD(status_t, getDisplayedContentSample, + (HalDisplayId, uint64_t, uint64_t, DisplayedFrameStats*), (override)); + MOCK_METHOD(ftl::Future<status_t>, setDisplayBrightness, + (PhysicalDisplayId, float, float, const Hwc2::Composer::DisplayBrightnessOptions&), + (override)); + MOCK_METHOD(std::optional<DisplayIdentificationInfo>, onHotplug, + (hal::HWDisplayId, hal::Connection), (override)); + MOCK_METHOD(bool, updatesDeviceProductInfoOnHotplugReconnect, (), (const, override)); + MOCK_METHOD(std::optional<PhysicalDisplayId>, onVsync, (hal::HWDisplayId, int64_t)); + MOCK_METHOD(void, setVsyncEnabled, (PhysicalDisplayId, hal::Vsync), (override)); + MOCK_METHOD(bool, isConnected, (PhysicalDisplayId), (const, override)); + MOCK_METHOD(std::vector<HWComposer::HWCDisplayMode>, getModes, (PhysicalDisplayId, int32_t), + (const, override)); + MOCK_METHOD((ftl::Expected<hal::HWConfigId, status_t>), getActiveMode, (PhysicalDisplayId), + (const, override)); + MOCK_METHOD(std::vector<ui::ColorMode>, getColorModes, (PhysicalDisplayId), (const, override)); + MOCK_METHOD(status_t, setActiveColorMode, (PhysicalDisplayId, ui::ColorMode, ui::RenderIntent), + (override)); + MOCK_METHOD(ui::DisplayConnectionType, getDisplayConnectionType, (PhysicalDisplayId), + (const, override)); + MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (PhysicalDisplayId), (const, override)); + MOCK_METHOD((ftl::Expected<nsecs_t, status_t>), getDisplayVsyncPeriod, (PhysicalDisplayId), + (const, override)); + MOCK_METHOD(status_t, setActiveModeWithConstraints, + (PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&, + hal::VsyncPeriodChangeTimeline*), + (override)); + MOCK_METHOD(status_t, setBootDisplayMode, (PhysicalDisplayId, hal::HWConfigId), (override)); + MOCK_METHOD(status_t, clearBootDisplayMode, (PhysicalDisplayId), (override)); + MOCK_METHOD(std::optional<hal::HWConfigId>, getPreferredBootDisplayMode, (PhysicalDisplayId), + (override)); + + MOCK_METHOD(std::vector<aidl::android::hardware::graphics::common::HdrConversionCapability>, + getHdrConversionCapabilities, (), (const, override)); + MOCK_METHOD(status_t, setHdrConversionStrategy, + (aidl::android::hardware::graphics::common::HdrConversionStrategy, + aidl::android::hardware::graphics::common::Hdr*), + (override)); + MOCK_METHOD(status_t, setAutoLowLatencyMode, (PhysicalDisplayId, bool), (override)); + MOCK_METHOD(status_t, getSupportedContentTypes, + (PhysicalDisplayId, std::vector<hal::ContentType>*), (const, override)); + MOCK_METHOD(status_t, setContentType, (PhysicalDisplayId, hal::ContentType)), (override); + MOCK_METHOD((const std::unordered_map<std::string, bool>&), getSupportedLayerGenericMetadata, + (), (const, override)); + MOCK_METHOD(void, dump, (std::string&), (const, override)); + MOCK_METHOD(void, dumpOverlayProperties, (std::string&), (const, override)); + MOCK_METHOD(android::Hwc2::Composer*, getComposer, (), (const, override)); + + MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override)); + MOCK_METHOD(PhysicalDisplayId, getPrimaryDisplayId, (), (const, override)); + MOCK_METHOD(bool, isHeadless, (), (const, override)); + + MOCK_METHOD(std::optional<PhysicalDisplayId>, toPhysicalDisplayId, (hal::HWDisplayId), + (const, override)); + MOCK_METHOD(std::optional<hal::HWDisplayId>, fromPhysicalDisplayId, (PhysicalDisplayId), + (const, override)); + MOCK_METHOD(status_t, getDisplayDecorationSupport, + (PhysicalDisplayId, + std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* + support), + (override)); + MOCK_METHOD(status_t, setIdleTimerEnabled, (PhysicalDisplayId, std::chrono::milliseconds), + (override)); + MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override)); + MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId), + (const, override)); + MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override)); + MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&, + getOverlaySupport, (), (const, override)); + MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool)); + MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps)); + MOCK_METHOD(HWC2::Display::LutFileDescriptorMapper&, getLutFileDescriptorMapper, (), + (override)); + MOCK_METHOD(int32_t, getMaxLayerPictureProfiles, (PhysicalDisplayId)); + MOCK_METHOD(status_t, setDisplayPictureProfileHandle, + (PhysicalDisplayId, const PictureProfileHandle&)); +}; + +} // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h deleted file mode 100644 index ed1405b058..0000000000 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPower.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 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 "binder/Status.h" - -// FMQ library in IPower does questionable conversions -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#include <aidl/android/hardware/power/IPower.h> -#pragma clang diagnostic pop - -#include <gmock/gmock.h> - -using aidl::android::hardware::power::Boost; -using aidl::android::hardware::power::ChannelConfig; -using aidl::android::hardware::power::IPower; -using aidl::android::hardware::power::IPowerHintSession; -using aidl::android::hardware::power::SessionConfig; -using aidl::android::hardware::power::SessionTag; - -using aidl::android::hardware::power::Mode; -using android::binder::Status; - -namespace android::Hwc2::mock { - -class MockIPower : public IPower { -public: - MockIPower(); - - MOCK_METHOD(ndk::ScopedAStatus, isBoostSupported, (Boost boost, bool* ret), (override)); - MOCK_METHOD(ndk::ScopedAStatus, setBoost, (Boost boost, int32_t durationMs), (override)); - MOCK_METHOD(ndk::ScopedAStatus, isModeSupported, (Mode mode, bool* ret), (override)); - MOCK_METHOD(ndk::ScopedAStatus, setMode, (Mode mode, bool enabled), (override)); - MOCK_METHOD(ndk::ScopedAStatus, createHintSession, - (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, - int64_t durationNanos, std::shared_ptr<IPowerHintSession>* session), - (override)); - MOCK_METHOD(ndk::ScopedAStatus, getHintSessionPreferredRate, (int64_t * rate), (override)); - MOCK_METHOD(ndk::ScopedAStatus, createHintSessionWithConfig, - (int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, - int64_t durationNanos, SessionTag tag, SessionConfig* config, - std::shared_ptr<IPowerHintSession>* _aidl_return), - (override)); - MOCK_METHOD(ndk::ScopedAStatus, getSessionChannel, - (int32_t tgid, int32_t uid, ChannelConfig* _aidl_return), (override)); - MOCK_METHOD(ndk::ScopedAStatus, closeSessionChannel, (int32_t tgid, int32_t uid), (override)); - MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override)); - MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override)); - MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); - MOCK_METHOD(bool, isRemote, (), (override)); -}; - -} // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 7398cbebe3..82500fef10 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -30,6 +30,7 @@ public: MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (EventRegistrationFlags), (const, override)); MOCK_METHOD(void, enableSyntheticVsync, (bool), (override)); + MOCK_METHOD(void, omitVsyncDispatching, (bool), (override)); MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override)); MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override)); MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override)); diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp index 1ba38a822a..f4c1e52537 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.cpp +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.cpp @@ -16,10 +16,10 @@ #include "MockPowerAdvisor.h" -namespace android::Hwc2::mock { +namespace android::adpf::mock { // Explicit default instantiation is recommended. PowerAdvisor::PowerAdvisor() = default; PowerAdvisor::~PowerAdvisor() = default; -} // namespace android::Hwc2::mock +} // namespace android::adpf::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h index 4efdfe877b..5c4512a2df 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerAdvisor.h @@ -18,11 +18,11 @@ #include <gmock/gmock.h> -#include "DisplayHardware/PowerAdvisor.h" +#include "PowerAdvisor/PowerAdvisor.h" -namespace android::Hwc2::mock { +namespace android::adpf::mock { -class PowerAdvisor : public android::Hwc2::PowerAdvisor { +class PowerAdvisor : public android::adpf::PowerAdvisor { public: PowerAdvisor(); ~PowerAdvisor() override; @@ -65,4 +65,4 @@ public: MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (Duration targetDuration), (override)); }; -} // namespace android::Hwc2::mock +} // namespace android::adpf::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp index 3ec5c2d09b..3b8de55607 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.cpp +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.cpp @@ -16,9 +16,9 @@ #include "MockPowerHalController.h" -namespace android::Hwc2::mock { +namespace android::adpf::mock { MockPowerHalController::MockPowerHalController() = default; MockPowerHalController::~MockPowerHalController() = default; -} // namespace android::Hwc2::mock +} // namespace android::adpf::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h index af1862d1cf..fba4cd82fa 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHalController.h @@ -19,10 +19,7 @@ #include <gmock/gmock.h> #include <scheduler/Time.h> -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" #include <powermanager/PowerHalController.h> -#pragma clang diagnostic pop namespace android { namespace hardware { @@ -32,7 +29,7 @@ class IPower; } // namespace hardware } // namespace android -namespace android::Hwc2::mock { +namespace android::adpf::mock { using android::power::HalResult; @@ -59,4 +56,4 @@ public: MOCK_METHOD(HalResult<void>, closeSessionChannel, (int tgid, int uid), (override)); }; -} // namespace android::Hwc2::mock
\ No newline at end of file +} // namespace android::adpf::mock
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp index d383283d8e..cb39783b77 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h" +#include "MockPowerHintSessionWrapper.h" -namespace android::Hwc2::mock { +namespace android::adpf::mock { // Explicit default instantiation is recommended. MockPowerHintSessionWrapper::MockPowerHintSessionWrapper() : power::PowerHintSessionWrapper(nullptr) {} -} // namespace android::Hwc2::mock +} // namespace android::adpf::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h index bc6950cccb..4518de8c16 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h +++ b/services/surfaceflinger/tests/unittests/mock/PowerAdvisor/MockPowerHintSessionWrapper.h @@ -16,13 +16,7 @@ #pragma once -#include "binder/Status.h" - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wconversion" -#include <aidl/android/hardware/power/IPower.h> #include <powermanager/PowerHintSessionWrapper.h> -#pragma clang diagnostic pop #include <gmock/gmock.h> @@ -34,7 +28,7 @@ using android::binder::Status; using namespace aidl::android::hardware::power; -namespace android::Hwc2::mock { +namespace android::adpf::mock { class MockPowerHintSessionWrapper : public power::PowerHintSessionWrapper { public: @@ -52,4 +46,4 @@ public: MOCK_METHOD(power::HalResult<SessionConfig>, getSessionConfig, (), (override)); }; -} // namespace android::Hwc2::mock +} // namespace android::adpf::mock diff --git a/services/vibratorservice/VibratorManagerHalController.cpp b/services/vibratorservice/VibratorManagerHalController.cpp index ba35d15bf2..494f88f10e 100644 --- a/services/vibratorservice/VibratorManagerHalController.cpp +++ b/services/vibratorservice/VibratorManagerHalController.cpp @@ -150,6 +150,23 @@ HalResult<void> ManagerHalController::cancelSynced() { return apply(cancelSyncedFn, "cancelSynced"); } +HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalController::startSession( + const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config, + const std::function<void()>& completionCallback) { + hal_fn<std::shared_ptr<Aidl::IVibrationSession>> startSessionFn = + [&](std::shared_ptr<ManagerHalWrapper> hal) { + return hal->startSession(ids, config, completionCallback); + }; + return apply(startSessionFn, "startSession"); +} + +HalResult<void> ManagerHalController::clearSessions() { + hal_fn<void> clearSessionsFn = [](std::shared_ptr<ManagerHalWrapper> hal) { + return hal->clearSessions(); + }; + return apply(clearSessionsFn, "clearSessions"); +} + }; // namespace vibrator }; // namespace android diff --git a/services/vibratorservice/VibratorManagerHalWrapper.cpp b/services/vibratorservice/VibratorManagerHalWrapper.cpp index 93ec781b21..3db8ff8699 100644 --- a/services/vibratorservice/VibratorManagerHalWrapper.cpp +++ b/services/vibratorservice/VibratorManagerHalWrapper.cpp @@ -29,6 +29,30 @@ namespace vibrator { constexpr int32_t SINGLE_VIBRATOR_ID = 0; const constexpr char* MISSING_VIBRATOR_MESSAGE_PREFIX = "No vibrator with id="; +HalResult<void> ManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) { + return HalResult<void>::unsupported(); +} + +HalResult<void> ManagerHalWrapper::triggerSynced(const std::function<void()>&) { + return HalResult<void>::unsupported(); +} + +HalResult<void> ManagerHalWrapper::cancelSynced() { + return HalResult<void>::unsupported(); +} + +HalResult<std::shared_ptr<Aidl::IVibrationSession>> ManagerHalWrapper::startSession( + const std::vector<int32_t>&, const Aidl::VibrationSessionConfig&, + const std::function<void()>&) { + return HalResult<std::shared_ptr<Aidl::IVibrationSession>>::unsupported(); +} + +HalResult<void> ManagerHalWrapper::clearSessions() { + return HalResult<void>::unsupported(); +} + +// ------------------------------------------------------------------------------------------------- + HalResult<void> LegacyManagerHalWrapper::ping() { auto pingFn = [](HalWrapper* hal) { return hal->ping(); }; return mController->doWithRetry<void>(pingFn, "ping"); @@ -59,18 +83,6 @@ HalResult<std::shared_ptr<HalController>> LegacyManagerHalWrapper::getVibrator(i (MISSING_VIBRATOR_MESSAGE_PREFIX + std::to_string(id)).c_str()); } -HalResult<void> LegacyManagerHalWrapper::prepareSynced(const std::vector<int32_t>&) { - return HalResult<void>::unsupported(); -} - -HalResult<void> LegacyManagerHalWrapper::triggerSynced(const std::function<void()>&) { - return HalResult<void>::unsupported(); -} - -HalResult<void> LegacyManagerHalWrapper::cancelSynced() { - return HalResult<void>::unsupported(); -} - // ------------------------------------------------------------------------------------------------- std::shared_ptr<HalWrapper> AidlManagerHalWrapper::connectToVibrator( @@ -186,6 +198,17 @@ HalResult<void> AidlManagerHalWrapper::triggerSynced( return HalResultFactory::fromStatus(getHal()->triggerSynced(cb)); } +HalResult<std::shared_ptr<Aidl::IVibrationSession>> AidlManagerHalWrapper::startSession( + const std::vector<int32_t>& ids, const Aidl::VibrationSessionConfig& config, + const std::function<void()>& completionCallback) { + auto cb = ndk::SharedRefBase::make<HalCallbackWrapper>(completionCallback); + std::shared_ptr<Aidl::IVibrationSession> session; + auto status = getHal()->startSession(ids, config, cb, &session); + return HalResultFactory::fromStatus<std::shared_ptr<Aidl::IVibrationSession>>(std::move(status), + std::move( + session)); +} + HalResult<void> AidlManagerHalWrapper::cancelSynced() { auto ret = HalResultFactory::fromStatus(getHal()->cancelSynced()); if (ret.isOk()) { @@ -200,6 +223,10 @@ HalResult<void> AidlManagerHalWrapper::cancelSynced() { return ret; } +HalResult<void> AidlManagerHalWrapper::clearSessions() { + return HalResultFactory::fromStatus(getHal()->clearSessions()); +} + std::shared_ptr<Aidl::IVibratorManager> AidlManagerHalWrapper::getHal() { std::lock_guard<std::mutex> lock(mHandleMutex); return mHandle; diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h index 70c846b4ae..72d4752ba4 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h +++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalController.h @@ -62,6 +62,10 @@ public: HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final; HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final; HalResult<void> cancelSynced() override final; + HalResult<std::shared_ptr<IVibrationSession>> startSession( + const std::vector<int32_t>& ids, const VibrationSessionConfig& config, + const std::function<void()>& completionCallback) override final; + HalResult<void> clearSessions() override final; private: Connector mConnector; diff --git a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h index 9e3f221fa7..8d4ca0eda3 100644 --- a/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h +++ b/services/vibratorservice/include/vibratorservice/VibratorManagerHalWrapper.h @@ -38,7 +38,8 @@ enum class ManagerCapabilities : int32_t { aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_PERFORM, MIXED_TRIGGER_COMPOSE = aidl::android::hardware::vibrator::IVibratorManager::CAP_MIXED_TRIGGER_COMPOSE, - TRIGGER_CALLBACK = aidl::android::hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK + TRIGGER_CALLBACK = aidl::android::hardware::vibrator::IVibratorManager::CAP_TRIGGER_CALLBACK, + START_SESSIONS = aidl::android::hardware::vibrator::IVibratorManager::CAP_START_SESSIONS }; inline ManagerCapabilities operator|(ManagerCapabilities lhs, ManagerCapabilities rhs) { @@ -64,6 +65,9 @@ inline ManagerCapabilities& operator&=(ManagerCapabilities& lhs, ManagerCapabili // Wrapper for VibratorManager HAL handlers. class ManagerHalWrapper { public: + using IVibrationSession = aidl::android::hardware::vibrator::IVibrationSession; + using VibrationSessionConfig = aidl::android::hardware::vibrator::VibrationSessionConfig; + ManagerHalWrapper() = default; virtual ~ManagerHalWrapper() = default; @@ -78,9 +82,13 @@ public: virtual HalResult<std::vector<int32_t>> getVibratorIds() = 0; virtual HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) = 0; - virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids) = 0; - virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback) = 0; - virtual HalResult<void> cancelSynced() = 0; + virtual HalResult<void> prepareSynced(const std::vector<int32_t>& ids); + virtual HalResult<void> triggerSynced(const std::function<void()>& completionCallback); + virtual HalResult<void> cancelSynced(); + virtual HalResult<std::shared_ptr<IVibrationSession>> startSession( + const std::vector<int32_t>& ids, const VibrationSessionConfig& config, + const std::function<void()>& completionCallback); + virtual HalResult<void> clearSessions(); }; // Wrapper for the VibratorManager over single Vibrator HAL. @@ -98,10 +106,6 @@ public: HalResult<std::vector<int32_t>> getVibratorIds() override final; HalResult<std::shared_ptr<HalController>> getVibrator(int32_t id) override final; - HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final; - HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final; - HalResult<void> cancelSynced() override final; - private: const std::shared_ptr<HalController> mController; }; @@ -126,6 +130,10 @@ public: HalResult<void> prepareSynced(const std::vector<int32_t>& ids) override final; HalResult<void> triggerSynced(const std::function<void()>& completionCallback) override final; HalResult<void> cancelSynced() override final; + HalResult<std::shared_ptr<IVibrationSession>> startSession( + const std::vector<int32_t>& ids, const VibrationSessionConfig& config, + const std::function<void()>& completionCallback) override final; + HalResult<void> clearSessions() override final; private: std::mutex mHandleMutex; diff --git a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp index c7214e054e..b201670901 100644 --- a/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalControllerTest.cpp @@ -27,6 +27,8 @@ #include "test_mocks.h" #include "test_utils.h" +using aidl::android::hardware::vibrator::IVibrationSession; +using aidl::android::hardware::vibrator::VibrationSessionConfig; using android::vibrator::HalController; using namespace android; @@ -34,6 +36,7 @@ using namespace testing; static constexpr int MAX_ATTEMPTS = 2; static const std::vector<int32_t> VIBRATOR_IDS = {1, 2}; +static const VibrationSessionConfig SESSION_CONFIG; static constexpr int VIBRATOR_ID = 1; // ------------------------------------------------------------------------------------------------- @@ -52,6 +55,11 @@ public: MOCK_METHOD(vibrator::HalResult<void>, triggerSynced, (const std::function<void()>& completionCallback), (override)); MOCK_METHOD(vibrator::HalResult<void>, cancelSynced, (), (override)); + MOCK_METHOD(vibrator::HalResult<std::shared_ptr<IVibrationSession>>, startSession, + (const std::vector<int32_t>& ids, const VibrationSessionConfig& s, + const std::function<void()>& completionCallback), + (override)); + MOCK_METHOD(vibrator::HalResult<void>, clearSessions, (), (override)); }; // ------------------------------------------------------------------------------------------------- @@ -79,7 +87,8 @@ protected: void setHalExpectations(int32_t cardinality, vibrator::HalResult<void> voidResult, vibrator::HalResult<vibrator::ManagerCapabilities> capabilitiesResult, vibrator::HalResult<std::vector<int32_t>> idsResult, - vibrator::HalResult<std::shared_ptr<HalController>> vibratorResult) { + vibrator::HalResult<std::shared_ptr<HalController>> vibratorResult, + vibrator::HalResult<std::shared_ptr<IVibrationSession>> sessionResult) { EXPECT_CALL(*mMockHal.get(), ping()) .Times(Exactly(cardinality)) .WillRepeatedly(Return(voidResult)); @@ -101,10 +110,16 @@ protected: EXPECT_CALL(*mMockHal.get(), cancelSynced()) .Times(Exactly(cardinality)) .WillRepeatedly(Return(voidResult)); + EXPECT_CALL(*mMockHal.get(), startSession(_, _, _)) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(sessionResult)); + EXPECT_CALL(*mMockHal.get(), clearSessions()) + .Times(Exactly(cardinality)) + .WillRepeatedly(Return(voidResult)); if (cardinality > 1) { // One reconnection for each retry. - EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(7 * (cardinality - 1))); + EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(9 * (cardinality - 1))); } else { EXPECT_CALL(*mMockHal.get(), tryReconnect()).Times(Exactly(0)); } @@ -127,7 +142,8 @@ TEST_F(VibratorManagerHalControllerTest, TestApiCallsAreForwardedToHal) { vibrator::HalResult<vibrator::ManagerCapabilities>::ok( vibrator::ManagerCapabilities::SYNC), vibrator::HalResult<std::vector<int32_t>>::ok(VIBRATOR_IDS), - vibrator::HalResult<std::shared_ptr<HalController>>::ok(nullptr)); + vibrator::HalResult<std::shared_ptr<HalController>>::ok(nullptr), + vibrator::HalResult<std::shared_ptr<IVibrationSession>>::ok(nullptr)); ASSERT_TRUE(mController->ping().isOk()); @@ -146,6 +162,8 @@ TEST_F(VibratorManagerHalControllerTest, TestApiCallsAreForwardedToHal) { ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isOk()); ASSERT_TRUE(mController->triggerSynced([]() {}).isOk()); ASSERT_TRUE(mController->cancelSynced().isOk()); + ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isOk()); + ASSERT_TRUE(mController->clearSessions().isOk()); ASSERT_EQ(1, mConnectCounter); } @@ -154,7 +172,8 @@ TEST_F(VibratorManagerHalControllerTest, TestUnsupportedApiResultDoesNotResetHal setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::unsupported(), vibrator::HalResult<vibrator::ManagerCapabilities>::unsupported(), vibrator::HalResult<std::vector<int32_t>>::unsupported(), - vibrator::HalResult<std::shared_ptr<HalController>>::unsupported()); + vibrator::HalResult<std::shared_ptr<HalController>>::unsupported(), + vibrator::HalResult<std::shared_ptr<IVibrationSession>>::unsupported()); ASSERT_TRUE(mController->ping().isUnsupported()); ASSERT_TRUE(mController->getCapabilities().isUnsupported()); @@ -163,6 +182,8 @@ TEST_F(VibratorManagerHalControllerTest, TestUnsupportedApiResultDoesNotResetHal ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isUnsupported()); ASSERT_TRUE(mController->triggerSynced([]() {}).isUnsupported()); ASSERT_TRUE(mController->cancelSynced().isUnsupported()); + ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isUnsupported()); + ASSERT_TRUE(mController->clearSessions().isUnsupported()); ASSERT_EQ(1, mConnectCounter); } @@ -171,7 +192,8 @@ TEST_F(VibratorManagerHalControllerTest, TestOperationFailedApiResultDoesNotRese setHalExpectations(/* cardinality= */ 1, vibrator::HalResult<void>::failed("msg"), vibrator::HalResult<vibrator::ManagerCapabilities>::failed("msg"), vibrator::HalResult<std::vector<int32_t>>::failed("msg"), - vibrator::HalResult<std::shared_ptr<HalController>>::failed("msg")); + vibrator::HalResult<std::shared_ptr<HalController>>::failed("msg"), + vibrator::HalResult<std::shared_ptr<IVibrationSession>>::failed("msg")); ASSERT_TRUE(mController->ping().isFailed()); ASSERT_TRUE(mController->getCapabilities().isFailed()); @@ -180,6 +202,8 @@ TEST_F(VibratorManagerHalControllerTest, TestOperationFailedApiResultDoesNotRese ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isFailed()); ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed()); ASSERT_TRUE(mController->cancelSynced().isFailed()); + ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isFailed()); + ASSERT_TRUE(mController->clearSessions().isFailed()); ASSERT_EQ(1, mConnectCounter); } @@ -188,7 +212,9 @@ TEST_F(VibratorManagerHalControllerTest, TestTransactionFailedApiResultResetsHal setHalExpectations(MAX_ATTEMPTS, vibrator::HalResult<void>::transactionFailed("m"), vibrator::HalResult<vibrator::ManagerCapabilities>::transactionFailed("m"), vibrator::HalResult<std::vector<int32_t>>::transactionFailed("m"), - vibrator::HalResult<std::shared_ptr<HalController>>::transactionFailed("m")); + vibrator::HalResult<std::shared_ptr<HalController>>::transactionFailed("m"), + vibrator::HalResult<std::shared_ptr<IVibrationSession>>::transactionFailed( + "m")); ASSERT_TRUE(mController->ping().isFailed()); ASSERT_TRUE(mController->getCapabilities().isFailed()); @@ -197,6 +223,8 @@ TEST_F(VibratorManagerHalControllerTest, TestTransactionFailedApiResultResetsHal ASSERT_TRUE(mController->prepareSynced(VIBRATOR_IDS).isFailed()); ASSERT_TRUE(mController->triggerSynced([]() {}).isFailed()); ASSERT_TRUE(mController->cancelSynced().isFailed()); + ASSERT_TRUE(mController->startSession(VIBRATOR_IDS, SESSION_CONFIG, []() {}).isFailed()); + ASSERT_TRUE(mController->clearSessions().isFailed()); ASSERT_EQ(1, mConnectCounter); } diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp index ca13c0b70e..a2f002dc7b 100644 --- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp @@ -69,12 +69,25 @@ public: MOCK_METHOD(bool, isRemote, (), (override)); }; +class MockIVibrationSession : public IVibrationSession { +public: + MockIVibrationSession() = default; + + MOCK_METHOD(ndk::ScopedAStatus, close, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, abort, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t*), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string*), (override)); + MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); + MOCK_METHOD(bool, isRemote, (), (override)); +}; + // ------------------------------------------------------------------------------------------------- class VibratorManagerHalWrapperAidlTest : public Test { public: void SetUp() override { mMockVibrator = ndk::SharedRefBase::make<StrictMock<vibrator::MockIVibrator>>(); + mMockSession = ndk::SharedRefBase::make<StrictMock<MockIVibrationSession>>(); mMockHal = ndk::SharedRefBase::make<StrictMock<MockIVibratorManager>>(); mMockScheduler = std::make_shared<StrictMock<vibrator::MockCallbackScheduler>>(); mWrapper = std::make_unique<vibrator::AidlManagerHalWrapper>(mMockScheduler, mMockHal); @@ -86,11 +99,13 @@ protected: std::unique_ptr<vibrator::ManagerHalWrapper> mWrapper = nullptr; std::shared_ptr<StrictMock<MockIVibratorManager>> mMockHal = nullptr; std::shared_ptr<StrictMock<vibrator::MockIVibrator>> mMockVibrator = nullptr; + std::shared_ptr<StrictMock<MockIVibrationSession>> mMockSession = nullptr; }; // ------------------------------------------------------------------------------------------------- static const std::vector<int32_t> kVibratorIds = {1, 2}; +static const VibrationSessionConfig kSessionConfig; static constexpr int kVibratorId = 1; TEST_F(VibratorManagerHalWrapperAidlTest, TestGetCapabilitiesDoesNotCacheFailedResult) { @@ -319,3 +334,35 @@ TEST_F(VibratorManagerHalWrapperAidlTest, TestCancelSyncedReloadsAllControllers) ASSERT_TRUE(mWrapper->getVibratorIds().isOk()); ASSERT_TRUE(mWrapper->cancelSynced().isOk()); } + +TEST_F(VibratorManagerHalWrapperAidlTest, TestStartSession) { + EXPECT_CALL(*mMockHal.get(), startSession(_, _, _, _)) + .Times(Exactly(3)) + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce( + DoAll(DoAll(SetArgPointee<3>(mMockSession), Return(ndk::ScopedAStatus::ok())))); + + std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>(); + auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get()); + + ASSERT_TRUE(mWrapper->startSession(kVibratorIds, kSessionConfig, callback).isUnsupported()); + ASSERT_TRUE(mWrapper->startSession(kVibratorIds, kSessionConfig, callback).isFailed()); + + auto result = mWrapper->startSession(kVibratorIds, kSessionConfig, callback); + ASSERT_TRUE(result.isOk()); + ASSERT_NE(nullptr, result.value().get()); + ASSERT_EQ(0, *callbackCounter.get()); +} + +TEST_F(VibratorManagerHalWrapperAidlTest, TestClearSessions) { + EXPECT_CALL(*mMockHal.get(), clearSessions()) + .Times(Exactly(3)) + .WillOnce(Return(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))) + .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY))) + .WillOnce(Return(ndk::ScopedAStatus::ok())); + + ASSERT_TRUE(mWrapper->clearSessions().isUnsupported()); + ASSERT_TRUE(mWrapper->clearSessions().isFailed()); + ASSERT_TRUE(mWrapper->clearSessions().isOk()); +} diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp index 78772369bf..52c865e638 100644 --- a/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp +++ b/services/vibratorservice/test/VibratorManagerHalWrapperLegacyTest.cpp @@ -29,6 +29,8 @@ using aidl::android::hardware::vibrator::CompositeEffect; using aidl::android::hardware::vibrator::CompositePrimitive; using aidl::android::hardware::vibrator::Effect; using aidl::android::hardware::vibrator::EffectStrength; +using aidl::android::hardware::vibrator::IVibrationSession; +using aidl::android::hardware::vibrator::VibrationSessionConfig; using std::chrono::milliseconds; @@ -112,3 +114,12 @@ TEST_F(VibratorManagerHalWrapperLegacyTest, TestSyncedOperationsUnsupported) { ASSERT_TRUE(mWrapper->triggerSynced([]() {}).isUnsupported()); ASSERT_TRUE(mWrapper->cancelSynced().isUnsupported()); } + +TEST_F(VibratorManagerHalWrapperLegacyTest, TestSessionOperationsUnsupported) { + std::vector<int32_t> vibratorIds; + vibratorIds.push_back(0); + VibrationSessionConfig config; + + ASSERT_TRUE(mWrapper->startSession(vibratorIds, config, []() {}).isUnsupported()); + ASSERT_TRUE(mWrapper->clearSessions().isUnsupported()); +} |