Merge "SF: fixing typo" into main
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index b22cc2a..372008e 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -118,6 +118,12 @@
],
}
+prebuilt_etc {
+ name: "default_screenshot",
+ src: "res/default_screenshot.png",
+ filename_from_src: true,
+}
+
cc_binary {
name: "dumpstate",
defaults: ["dumpstate_defaults"],
@@ -130,6 +136,7 @@
required: [
"atrace",
"bugreport_procdump",
+ "default_screenshot",
"dmabuf_dump",
"ip",
"iptables",
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index d427ecf..d24edc4 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -206,6 +206,9 @@
static const std::string SHUTDOWN_CHECKPOINTS_DIR = "/data/system/shutdown-checkpoints/";
static const std::string SHUTDOWN_CHECKPOINTS_FILE_PREFIX = "checkpoints-";
+// File path to default screenshot image, that used when failed to capture the real screenshot.
+static const std::string DEFAULT_SCREENSHOT_PATH = "/system/etc/default_screenshot.png";
+
// TODO: temporary variables and functions used during C++ refactoring
#define RETURN_IF_USER_DENIED_CONSENT() \
@@ -765,10 +768,14 @@
bool copy_succeeded = android::os::CopyFileToFd(ds.screenshot_path_,
ds.options_->screenshot_fd.get());
- ds.options_->is_screenshot_copied = copy_succeeded;
if (copy_succeeded) {
android::os::UnlinkAndLogOnError(ds.screenshot_path_);
+ } else {
+ MYLOGE("Failed to copy screenshot to a permanent file.\n");
+ copy_succeeded = android::os::CopyFileToFd(DEFAULT_SCREENSHOT_PATH,
+ ds.options_->screenshot_fd.get());
}
+ ds.options_->is_screenshot_copied = copy_succeeded;
return android::binder::Status::ok();
}
@@ -3442,7 +3449,9 @@
// Do an early return if there were errors. We make an exception for consent
// timing out because it's possible the user got distracted. In this case the
// bugreport is not shared but made available for manual retrieval.
- MYLOGI("User denied consent. Returning\n");
+ MYLOGI("Bug report generation failed, this could have been due to"
+ " several reasons such as BR copy failed, user consent was"
+ " not grated etc. Returning\n");
return status;
}
if (status == Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
@@ -3729,12 +3738,16 @@
if (options_->do_screenshot &&
options_->screenshot_fd.get() != -1 &&
!options_->is_screenshot_copied) {
- copy_succeeded = android::os::CopyFileToFd(screenshot_path_,
+ bool is_screenshot_copied = android::os::CopyFileToFd(screenshot_path_,
options_->screenshot_fd.get());
- options_->is_screenshot_copied = copy_succeeded;
- if (copy_succeeded) {
+ if (is_screenshot_copied) {
android::os::UnlinkAndLogOnError(screenshot_path_);
+ } else {
+ MYLOGE("Failed to copy screenshot to a permanent file.\n");
+ is_screenshot_copied = android::os::CopyFileToFd(DEFAULT_SCREENSHOT_PATH,
+ options_->screenshot_fd.get());
}
+ options_->is_screenshot_copied = is_screenshot_copied;
}
}
return copy_succeeded ? Dumpstate::RunStatus::OK : Dumpstate::RunStatus::ERROR;
@@ -3825,7 +3838,7 @@
DurationReporter::~DurationReporter() {
if (!title_.empty()) {
float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC;
- if (elapsed >= .5f || verbose_) {
+ if (elapsed >= 1.0f || verbose_) {
MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed);
}
if (!logcat_only_) {
diff --git a/cmds/dumpstate/res/default_screenshot.png b/cmds/dumpstate/res/default_screenshot.png
new file mode 100644
index 0000000..10f36aa
--- /dev/null
+++ b/cmds/dumpstate/res/default_screenshot.png
Binary files differ
diff --git a/cmds/evemu-record/README.md b/cmds/evemu-record/README.md
index 5d16d51..cd28520 100644
--- a/cmds/evemu-record/README.md
+++ b/cmds/evemu-record/README.md
@@ -38,10 +38,10 @@
### Timestamp bases
By default, event timestamps are recorded relative to the time of the first event received during
-the recording. Passing `--timestamp-base=boot` causes the timestamps to be recorded relative to the
-system boot time instead. While this does not affect the playback of the recording, it can be useful
-for matching recorded events with other logs that use such timestamps, such as `dmesg` or the
-touchpad gesture debug logs emitted by `TouchpadInputMapper`.
+the recording. Passing `--timestamp-base=epoch` causes the timestamps to be recorded as Unix
+timestamps, relative to the Unix epoch (00:00:00 UTC on 1st January 1970). While this does not
+affect the playback of the recording, it can make the events in the recording easier to match up
+with those from other log sources, like logcat.
[FreeDesktop]: https://gitlab.freedesktop.org/libevdev/evemu
[format]: https://gitlab.freedesktop.org/libevdev/evemu#device-description-format
diff --git a/data/etc/android.software.opengles.deqp.level-2023-03-01.xml b/data/etc/android.software.opengles.deqp.level-2023-03-01.xml
index d0b594c..7aef9ec 100644
--- a/data/etc/android.software.opengles.deqp.level-2023-03-01.xml
+++ b/data/etc/android.software.opengles.deqp.level-2023-03-01.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2021 The Android Open Source Project
+<!-- Copyright 2023 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.
diff --git a/data/etc/android.software.opengles.deqp.level-2024-03-01.xml b/data/etc/android.software.opengles.deqp.level-2024-03-01.xml
index 4eeed2a..3d8a178 100644
--- a/data/etc/android.software.opengles.deqp.level-2024-03-01.xml
+++ b/data/etc/android.software.opengles.deqp.level-2024-03-01.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2021 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.
@@ -15,7 +15,7 @@
-->
<!-- This is the standard feature indicating that the device passes OpenGL ES
- dEQP tests associated with date 2023-03-01 (0x07E70301). -->
+ dEQP tests associated with date 2024-03-01 (0x7e80301). -->
<permissions>
<feature name="android.software.opengles.deqp.level" version="132645633" />
</permissions>
diff --git a/data/etc/android.software.opengles.deqp.level-2025-03-01.xml b/data/etc/android.software.opengles.deqp.level-2025-03-01.xml
new file mode 100644
index 0000000..e005356
--- /dev/null
+++ b/data/etc/android.software.opengles.deqp.level-2025-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes OpenGL ES
+ dEQP tests associated with date 2025-03-01 (0x7e90301). -->
+<permissions>
+ <feature name="android.software.opengles.deqp.level" version="132711169" />
+</permissions>
diff --git a/data/etc/android.software.opengles.deqp.level-latest.xml b/data/etc/android.software.opengles.deqp.level-latest.xml
index 62bb101..1ad35bc 100644
--- a/data/etc/android.software.opengles.deqp.level-latest.xml
+++ b/data/etc/android.software.opengles.deqp.level-latest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2023 The Android Open Source Project
+<!-- Copyright 2025 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.
@@ -17,5 +17,5 @@
<!-- This is the standard feature indicating that the device passes OpenGL ES
dEQP tests associated with the most recent level for this Android version. -->
<permissions>
- <feature name="android.software.opengles.deqp.level" version="132645633" />
+ <feature name="android.software.opengles.deqp.level" version="132711169" />
</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml
index 8b2b4c8..185edbf 100644
--- a/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml
+++ b/data/etc/android.software.vulkan.deqp.level-2024-03-01.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2021 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.
@@ -15,7 +15,7 @@
-->
<!-- This is the standard feature indicating that the device passes Vulkan dEQP
- tests associated with date 2023-03-01 (0x07E70301). -->
+ tests associated with date 2024-03-01 (0x7e80301). -->
<permissions>
<feature name="android.software.vulkan.deqp.level" version="132645633" />
</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-2025-03-01.xml b/data/etc/android.software.vulkan.deqp.level-2025-03-01.xml
new file mode 100644
index 0000000..b424667
--- /dev/null
+++ b/data/etc/android.software.vulkan.deqp.level-2025-03-01.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard feature indicating that the device passes Vulkan dEQP
+ tests associated with date 2025-03-01 (0x7e90301). -->
+<permissions>
+ <feature name="android.software.vulkan.deqp.level" version="132711169" />
+</permissions>
diff --git a/data/etc/android.software.vulkan.deqp.level-latest.xml b/data/etc/android.software.vulkan.deqp.level-latest.xml
index 0fc12b3..4128a95 100644
--- a/data/etc/android.software.vulkan.deqp.level-latest.xml
+++ b/data/etc/android.software.vulkan.deqp.level-latest.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2023 The Android Open Source Project
+<!-- Copyright 2025 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.
@@ -17,5 +17,5 @@
<!-- This is the standard feature indicating that the device passes Vulkan
dEQP tests associated with the most recent level for this Android version. -->
<permissions>
- <feature name="android.software.vulkan.deqp.level" version="132645633" />
+ <feature name="android.software.vulkan.deqp.level" version="132711169" />
</permissions>
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index bf9acb3..8d61e77 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -156,8 +156,6 @@
*
* THREADING
* The transaction completed callback can be invoked on any thread.
- *
- * Available since API level 29.
*/
typedef void (*ASurfaceTransaction_OnComplete)(void* _Null_unspecified context,
ASurfaceTransactionStats* _Nonnull stats);
@@ -184,8 +182,6 @@
*
* THREADING
* The transaction committed callback can be invoked on any thread.
- *
- * Available since API level 31.
*/
typedef void (*ASurfaceTransaction_OnCommit)(void* _Null_unspecified context,
ASurfaceTransactionStats* _Nonnull stats);
@@ -213,8 +209,6 @@
*
* THREADING
* The callback can be invoked on any thread.
- *
- * Available since API level 36.
*/
typedef void (*ASurfaceTransaction_OnBufferRelease)(void* _Null_unspecified context,
int release_fence_fd);
diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h
index 43048db..917d9a7 100644
--- a/include/audiomanager/AudioManager.h
+++ b/include/audiomanager/AudioManager.h
@@ -59,6 +59,7 @@
PLAYER_MUTE_PLAYBACK_RESTRICTED = (1 << 3),
PLAYER_MUTE_CLIENT_VOLUME = (1 << 4),
PLAYER_MUTE_VOLUME_SHAPER = (1 << 5),
+ PLAYER_MUTE_PORT_VOLUME = (1 << 6),
};
struct mute_state_t {
@@ -72,8 +73,10 @@
bool muteFromPlaybackRestricted = false;
/** Flag used when audio track was muted by client volume. */
bool muteFromClientVolume = false;
- /** Flag used when volume is muted by volume shaper. */
+ /** Flag used when volume is muted by volume shaper. */
bool muteFromVolumeShaper = false;
+ /** Flag used when volume is muted by port volume. */
+ bool muteFromPortVolume = false;
explicit operator int() const
{
@@ -83,6 +86,7 @@
result |= muteFromPlaybackRestricted * PLAYER_MUTE_PLAYBACK_RESTRICTED;
result |= muteFromClientVolume * PLAYER_MUTE_CLIENT_VOLUME;
result |= muteFromVolumeShaper * PLAYER_MUTE_VOLUME_SHAPER;
+ result |= muteFromPortVolume * PLAYER_MUTE_PORT_VOLUME;
return result;
}
diff --git a/include/ftl/non_null.h b/include/ftl/non_null.h
index 4a5d8bf..e684288 100644
--- a/include/ftl/non_null.h
+++ b/include/ftl/non_null.h
@@ -68,6 +68,15 @@
constexpr NonNull(const NonNull&) = default;
constexpr NonNull& operator=(const NonNull&) = default;
+ template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, Pointer>>>
+ constexpr NonNull(const NonNull<U>& other) : pointer_(other.get()) {}
+
+ template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, Pointer>>>
+ constexpr NonNull& operator=(const NonNull<U>& other) {
+ pointer_ = other.get();
+ return *this;
+ }
+
[[nodiscard]] constexpr const Pointer& get() const { return pointer_; }
[[nodiscard]] constexpr explicit operator const Pointer&() const { return get(); }
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
index c98b9cf..228347d 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <map>
#include <memory>
#include <optional>
@@ -75,12 +76,13 @@
* the event is ready to consume.
* @param looper needs to be sp and not shared_ptr because it inherits from
* RefBase
- * @param resampler the resampling strategy to use. If null, no resampling will be
- * performed.
+ * @param resamplerCreator callable that returns the resampling strategy to be used. If null, no
+ * resampling will be performed. resamplerCreator must never return nullptr.
*/
- explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
- sp<Looper> looper, InputConsumerCallbacks& callbacks,
- std::unique_ptr<Resampler> resampler);
+ explicit InputConsumerNoResampling(
+ const std::shared_ptr<InputChannel>& channel, sp<Looper> looper,
+ InputConsumerCallbacks& callbacks,
+ std::function<std::unique_ptr<Resampler>()> resamplerCreator);
~InputConsumerNoResampling();
@@ -117,7 +119,13 @@
std::shared_ptr<InputChannel> mChannel;
sp<Looper> mLooper;
InputConsumerCallbacks& mCallbacks;
- std::unique_ptr<Resampler> mResampler;
+ const std::function<std::unique_ptr<Resampler>()> mResamplerCreator;
+
+ /**
+ * A map to manage multidevice resampling. Each contained resampler is never null. This map is
+ * only modified by handleMessages.
+ */
+ std::map<DeviceId, std::unique_ptr<Resampler>> mResamplers;
// Looper-related infrastructure
/**
@@ -190,7 +198,10 @@
/**
* Batch messages that can be batched. When an unbatchable message is encountered, send it
* to the InputConsumerCallbacks immediately. If there are batches remaining,
- * notify InputConsumerCallbacks.
+ * notify InputConsumerCallbacks. If a resampleable ACTION_DOWN message is received, then a
+ * resampler is inserted for that deviceId in mResamplers. If a resampleable ACTION_UP or
+ * ACTION_CANCEL message is received then the resampler associated to that deviceId is erased
+ * from mResamplers.
*/
void handleMessages(std::vector<InputMessage>&& messages);
/**
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 5bd5070..1696a62 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -21,6 +21,7 @@
#include <input/Input.h>
#include <input/InputTransport.h>
#include <ui/LogicalDisplayId.h>
+#include <ui/Transform.h>
#include <utils/Timers.h> // for nsecs_t, systemTime
#include <vector>
@@ -94,16 +95,81 @@
return *this;
}
+ InputMessageBuilder& hmac(const std::array<uint8_t, 32>& hmac) {
+ mHmac = hmac;
+ return *this;
+ }
+
InputMessageBuilder& action(int32_t action) {
mAction = action;
return *this;
}
+ InputMessageBuilder& actionButton(int32_t actionButton) {
+ mActionButton = actionButton;
+ return *this;
+ }
+
+ InputMessageBuilder& flags(int32_t flags) {
+ mFlags = flags;
+ return *this;
+ }
+
+ InputMessageBuilder& metaState(int32_t metaState) {
+ mMetaState = metaState;
+ return *this;
+ }
+
+ InputMessageBuilder& buttonState(int32_t buttonState) {
+ mButtonState = buttonState;
+ return *this;
+ }
+
+ InputMessageBuilder& classification(MotionClassification classification) {
+ mClassification = classification;
+ return *this;
+ }
+
+ InputMessageBuilder& edgeFlags(int32_t edgeFlags) {
+ mEdgeFlags = edgeFlags;
+ return *this;
+ }
+
InputMessageBuilder& downTime(nsecs_t downTime) {
mDownTime = downTime;
return *this;
}
+ InputMessageBuilder& transform(const ui::Transform& transform) {
+ mTransform = transform;
+ return *this;
+ }
+
+ InputMessageBuilder& xPrecision(float xPrecision) {
+ mXPrecision = xPrecision;
+ return *this;
+ }
+
+ InputMessageBuilder& yPrecision(float yPrecision) {
+ mYPrecision = yPrecision;
+ return *this;
+ }
+
+ InputMessageBuilder& xCursorPosition(float xCursorPosition) {
+ mXCursorPosition = xCursorPosition;
+ return *this;
+ }
+
+ InputMessageBuilder& yCursorPosition(float yCursorPosition) {
+ mYCursorPosition = yCursorPosition;
+ return *this;
+ }
+
+ InputMessageBuilder& rawTransform(const ui::Transform& rawTransform) {
+ mRawTransform = rawTransform;
+ return *this;
+ }
+
InputMessageBuilder& pointer(PointerBuilder pointerBuilder) {
mPointers.push_back(pointerBuilder);
return *this;
@@ -121,8 +187,30 @@
message.body.motion.deviceId = mDeviceId;
message.body.motion.source = mSource;
message.body.motion.displayId = mDisplayId.val();
+ message.body.motion.hmac = std::move(mHmac);
message.body.motion.action = mAction;
+ message.body.motion.actionButton = mActionButton;
+ message.body.motion.flags = mFlags;
+ message.body.motion.metaState = mMetaState;
+ message.body.motion.buttonState = mButtonState;
+ message.body.motion.edgeFlags = mEdgeFlags;
message.body.motion.downTime = mDownTime;
+ message.body.motion.dsdx = mTransform.dsdx();
+ message.body.motion.dtdx = mTransform.dtdx();
+ message.body.motion.dtdy = mTransform.dtdy();
+ message.body.motion.dsdy = mTransform.dsdy();
+ message.body.motion.tx = mTransform.ty();
+ message.body.motion.ty = mTransform.tx();
+ message.body.motion.xPrecision = mXPrecision;
+ message.body.motion.yPrecision = mYPrecision;
+ message.body.motion.xCursorPosition = mXCursorPosition;
+ message.body.motion.yCursorPosition = mYCursorPosition;
+ message.body.motion.dsdxRaw = mRawTransform.dsdx();
+ message.body.motion.dtdxRaw = mRawTransform.dtdx();
+ message.body.motion.dtdyRaw = mRawTransform.dtdy();
+ message.body.motion.dsdyRaw = mRawTransform.dsdy();
+ message.body.motion.txRaw = mRawTransform.ty();
+ message.body.motion.tyRaw = mRawTransform.tx();
for (size_t i = 0; i < mPointers.size(); ++i) {
message.body.motion.pointers[i].properties = mPointers[i].buildProperties();
@@ -140,9 +228,21 @@
DeviceId mDeviceId{DEFAULT_DEVICE_ID};
int32_t mSource{AINPUT_SOURCE_TOUCHSCREEN};
ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::DEFAULT};
+ std::array<uint8_t, 32> mHmac{INVALID_HMAC};
int32_t mAction{AMOTION_EVENT_ACTION_MOVE};
+ int32_t mActionButton{0};
+ int32_t mFlags{0};
+ int32_t mMetaState{AMETA_NONE};
+ int32_t mButtonState{0};
+ MotionClassification mClassification{MotionClassification::NONE};
+ int32_t mEdgeFlags{0};
nsecs_t mDownTime{mEventTime};
-
+ ui::Transform mTransform{};
+ float mXPrecision{1.0f};
+ float mYPrecision{1.0f};
+ float mXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ float mYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ ui::Transform mRawTransform{};
std::vector<PointerBuilder> mPointers;
};
@@ -150,6 +250,9 @@
public:
MotionEventBuilder(int32_t action, int32_t source) {
mAction = action;
+ if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+ mFlags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
mSource = source;
mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
mDownTime = mEventTime;
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 67b37b1..7ea34c2 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -137,6 +137,9 @@
/* Returns keycode after applying Android key code remapping defined in mKeyRemapping */
int32_t applyKeyRemapping(int32_t fromKeyCode) const;
+ /** Returns list of keycodes that remap to provided keycode (@see setKeyRemapping()) */
+ std::vector<int32_t> findKeyCodesMappedToKeyCode(int32_t toKeyCode) const;
+
/* Returns the <keyCode, metaState> pair after applying key behavior defined in the kcm file,
* that tries to find a replacement key code based on current meta state */
std::pair<int32_t /*keyCode*/, int32_t /*metaState*/> applyKeyBehavior(int32_t keyCode,
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
index dcb25b7..47519c2 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -65,7 +65,8 @@
* extrapolation takes place and `resampleTime` is too far in the future. If `futureSample` is
* not null, interpolation will occur. If `futureSample` is null and there is enough historical
* data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and
- * `motionEvent` is unmodified.
+ * `motionEvent` is unmodified. Furthermore, motionEvent is not resampled if resampleTime equals
+ * the last sample eventTime of motionEvent.
*/
void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
const InputMessage* futureSample) override;
@@ -92,12 +93,6 @@
};
/**
- * Keeps track of the previous MotionEvent deviceId to enable comparison between the previous
- * and the current deviceId.
- */
- std::optional<DeviceId> mPreviousDeviceId;
-
- /**
* Up to two latest samples from MotionEvent. Updated every time resampleMotionEvent is called.
* Note: We store up to two samples in order to simplify the implementation. Although,
* calculations are possible with only one previous sample.
@@ -105,6 +100,17 @@
RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
/**
+ * Latest sample in mLatestSamples after resampling motion event. Used to compare if a pointer
+ * does not move between samples.
+ */
+ std::optional<Sample> mLastRealSample;
+
+ /**
+ * Latest prediction. Used to overwrite motion event samples if a set of conditions is met.
+ */
+ std::optional<Sample> mPreviousPrediction;
+
+ /**
* Adds up to mLatestSamples.capacity() of motionEvent's latest samples to mLatestSamples. If
* motionEvent has fewer samples than mLatestSamples.capacity(), then the available samples are
* added to mLatestSamples.
@@ -149,6 +155,23 @@
*/
std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const;
+ /**
+ * Iterates through motion event samples, and calls overwriteStillPointers on each sample.
+ */
+ void overwriteMotionEventSamples(MotionEvent& motionEvent) const;
+
+ /**
+ * Overwrites with resampled data the pointer coordinates that did not move between motion event
+ * samples, that is, both x and y values are identical to mLastRealSample.
+ */
+ void overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const;
+
+ /**
+ * Overwrites the pointer coordinates of a sample with event time older than
+ * that of mPreviousPrediction.
+ */
+ void overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const;
+
inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
};
} // namespace android
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index e5eee34..b7308c2 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -83,6 +83,7 @@
HWUI = 2,
GAME = 3,
APP = 4,
+ SYSUI = 5,
};
/**
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 379b609..6903cb5 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -489,6 +489,7 @@
"ProcessState.cpp",
"Static.cpp",
":libbinder_aidl",
+ ":libbinder_accessor_aidl",
":libbinder_device_interface_sources",
],
target: {
@@ -801,7 +802,6 @@
"aidl/android/os/IServiceManager.aidl",
"aidl/android/os/Service.aidl",
"aidl/android/os/ServiceDebugInfo.aidl",
- ":libbinder_accessor_aidl",
],
path: "aidl",
}
@@ -812,26 +812,7 @@
"aidl/android/os/IAccessor.aidl",
],
path: "aidl",
-}
-
-// TODO(b/353492849): Make this interface private to libbinder.
-aidl_interface {
- name: "android.os.accessor",
- srcs: [":libbinder_accessor_aidl"],
- unstable: true,
- backend: {
- rust: {
- enabled: true,
- apex_available: [
- "com.android.virt",
- ],
- },
- },
- visibility: [
- ":__subpackages__",
- "//system/tools/aidl:__subpackages__",
- "//packages/modules/Virtualization:__subpackages__",
- ],
+ visibility: [":__subpackages__"],
}
aidl_interface {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index eae844c..3758b65 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -197,7 +197,9 @@
&& currentValue < sBinderProxyCountHighWatermark
&& ((trackedValue & WARNING_REACHED_MASK) == 0)) [[unlikely]] {
sTrackingMap[trackedUid] |= WARNING_REACHED_MASK;
- if (sWarningCallback) sWarningCallback(trackedUid);
+ if (sWarningCallback) {
+ *postTask = [=]() { sWarningCallback(trackedUid); };
+ }
} else if (currentValue >= sBinderProxyCountHighWatermark) {
ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
getuid(), trackedUid, trackedValue);
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 77b80ef..32388db 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -386,7 +386,7 @@
ALOGE("Binder is null");
return BAD_VALUE;
}
- sp<IAccessor> accessor = interface_cast<IAccessor>(binder);
+ sp<IAccessor> accessor = checked_interface_cast<IAccessor>(binder);
if (accessor == nullptr) {
ALOGE("This binder for %s is not an IAccessor binder", String8(instance).c_str());
return BAD_TYPE;
@@ -420,6 +420,28 @@
return binder;
}
+status_t delegateAccessor(const String16& name, const sp<IBinder>& accessor,
+ sp<IBinder>* delegator) {
+ LOG_ALWAYS_FATAL_IF(delegator == nullptr, "delegateAccessor called with a null out param");
+ if (accessor == nullptr) {
+ ALOGW("Accessor argument to delegateAccessor is null.");
+ *delegator = nullptr;
+ return OK;
+ }
+ status_t status = validateAccessor(name, accessor);
+ if (status != OK) {
+ ALOGE("The provided accessor binder is not an IAccessor for instance %s. Status: "
+ "%s",
+ String8(name).c_str(), statusToString(status).c_str());
+ return status;
+ }
+ // validateAccessor already called checked_interface_cast and made sure this
+ // is a valid accessor object.
+ *delegator = sp<android::os::IAccessorDelegator>::make(interface_cast<IAccessor>(accessor));
+
+ return OK;
+}
+
#if !defined(__ANDROID_VNDK__)
// IPermissionController is not accessible to vendors
diff --git a/libs/binder/OS.h b/libs/binder/OS.h
index 04869a1..64b1fd4 100644
--- a/libs/binder/OS.h
+++ b/libs/binder/OS.h
@@ -27,6 +27,7 @@
LIBBINDER_EXPORTED void trace_begin(uint64_t tag, const char* name);
LIBBINDER_EXPORTED void trace_end(uint64_t tag);
LIBBINDER_EXPORTED void trace_int(uint64_t tag, const char* name, int32_t value);
+LIBBINDER_EXPORTED uint64_t get_trace_enabled_tags();
status_t setNonBlocking(borrowed_fd fd);
diff --git a/libs/binder/OS_android.cpp b/libs/binder/OS_android.cpp
index 893ee15..4e9230c 100644
--- a/libs/binder/OS_android.cpp
+++ b/libs/binder/OS_android.cpp
@@ -48,6 +48,10 @@
atrace_int(tag, name, value);
}
+uint64_t get_trace_enabled_tags() {
+ return atrace_enabled_tags;
+}
+
} // namespace os
// Legacy trace symbol. To be removed once all of downstream rebuilds.
diff --git a/libs/binder/OS_non_android_linux.cpp b/libs/binder/OS_non_android_linux.cpp
index 0c64eb6..6bba823 100644
--- a/libs/binder/OS_non_android_linux.cpp
+++ b/libs/binder/OS_non_android_linux.cpp
@@ -41,6 +41,10 @@
void trace_int(uint64_t, const char*, int32_t) {}
+uint64_t get_trace_enabled_tags() {
+ return 0;
+}
+
uint64_t GetThreadId() {
return syscall(__NR_gettid);
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 4b7af45..18c4134 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -180,7 +180,7 @@
}
}
- ALOGD("Invalid object type 0x%08x", obj.hdr.type);
+ ALOGE("Invalid object type 0x%08x to acquire", obj.hdr.type);
}
static void release_object(const sp<ProcessState>& proc, const flat_binder_object& obj,
@@ -210,7 +210,7 @@
}
}
- ALOGE("Invalid object type 0x%08x", obj.hdr.type);
+ ALOGE("Invalid object type 0x%08x to release", obj.hdr.type);
}
#endif // BINDER_WITH_KERNEL_IPC
@@ -683,7 +683,7 @@
return err;
}
-int Parcel::compareData(const Parcel& other) {
+int Parcel::compareData(const Parcel& other) const {
size_t size = dataSize();
if (size != other.dataSize()) {
return size < other.dataSize() ? -1 : 1;
@@ -1150,31 +1150,6 @@
return NO_ERROR;
}
-status_t Parcel::writeUnpadded(const void* data, size_t len)
-{
- if (len > INT32_MAX) {
- // don't accept size_t values which may have come from an
- // inadvertent conversion from a negative int.
- return BAD_VALUE;
- }
-
- size_t end = mDataPos + len;
- if (end < mDataPos) {
- // integer overflow
- return BAD_VALUE;
- }
-
- if (end <= mDataCapacity) {
-restart_write:
- memcpy(mData+mDataPos, data, len);
- return finishWrite(len);
- }
-
- status_t err = growData(len);
- if (err == NO_ERROR) goto restart_write;
- return err;
-}
-
status_t Parcel::write(const void* data, size_t len)
{
if (len > INT32_MAX) {
@@ -1211,6 +1186,10 @@
//printf("Writing %ld bytes, padded to %ld\n", len, padded);
uint8_t* const data = mData+mDataPos;
+ if (status_t status = validateReadData(mDataPos + padded); status != OK) {
+ return nullptr; // drops status
+ }
+
// Need to pad at end?
if (padded != len) {
#if BYTE_ORDER == BIG_ENDIAN
@@ -1799,6 +1778,10 @@
const bool enoughObjects = kernelFields->mObjectsSize < kernelFields->mObjectsCapacity;
if (enoughData && enoughObjects) {
restart_write:
+ if (status_t status = validateReadData(mDataPos + sizeof(val)); status != OK) {
+ return status;
+ }
+
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
// remember if it's a file descriptor
@@ -1874,7 +1857,10 @@
if (mDataPos < kernelFields->mObjects[nextObject] + sizeof(flat_binder_object)) {
// Requested info overlaps with an object
if (!mServiceFuzzing) {
- ALOGE("Attempt to read from protected data in Parcel %p", this);
+ ALOGE("Attempt to read or write from protected data in Parcel %p. pos: "
+ "%zu, nextObject: %zu, object offset: %llu, object size: %zu",
+ this, mDataPos, nextObject, kernelFields->mObjects[nextObject],
+ sizeof(flat_binder_object));
}
return PERMISSION_DENIED;
}
@@ -2042,6 +2028,10 @@
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
+ if (status_t status = validateReadData(mDataPos + sizeof(val)); status != OK) {
+ return status;
+ }
+
memcpy(mData + mDataPos, &val, sizeof(val));
return finishWrite(sizeof(val));
}
@@ -2678,14 +2668,14 @@
}
#endif // BINDER_WITH_KERNEL_IPC
-void Parcel::closeFileDescriptors() {
+void Parcel::closeFileDescriptors(size_t newObjectsSize) {
if (auto* kernelFields = maybeKernelFields()) {
#ifdef BINDER_WITH_KERNEL_IPC
size_t i = kernelFields->mObjectsSize;
if (i > 0) {
// ALOGI("Closing file descriptors for %zu objects...", i);
}
- while (i > 0) {
+ while (i > newObjectsSize) {
i--;
const flat_binder_object* flat =
reinterpret_cast<flat_binder_object*>(mData + kernelFields->mObjects[i]);
@@ -2696,6 +2686,7 @@
}
}
#else // BINDER_WITH_KERNEL_IPC
+ (void)newObjectsSize;
LOG_ALWAYS_FATAL("Binder kernel driver disabled at build time");
#endif // BINDER_WITH_KERNEL_IPC
} else if (auto* rpcFields = maybeRpcFields()) {
@@ -2920,7 +2911,7 @@
//ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
auto* kernelFields = maybeKernelFields();
// Close FDs before freeing, otherwise they will leak for kernel binder.
- closeFileDescriptors();
+ closeFileDescriptors(/*newObjectsSize=*/0);
mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
kernelFields ? kernelFields->mObjectsSize : 0);
} else {
@@ -2948,6 +2939,14 @@
return BAD_VALUE;
}
+ if (mDataPos > mDataSize) {
+ // b/370831157 - this case used to abort. We also don't expect mDataPos < mDataSize, but
+ // this would only waste a bit of memory, so it's okay.
+ ALOGE("growData only expected at the end of a Parcel. pos: %zu, size: %zu, capacity: %zu",
+ mDataPos, len, mDataCapacity);
+ return BAD_VALUE;
+ }
+
if (len > SIZE_MAX - mDataSize) return NO_MEMORY; // overflow
if (mDataSize + len > SIZE_MAX / 3) return NO_MEMORY; // overflow
size_t newSize = ((mDataSize+len)*3)/2;
@@ -3049,13 +3048,38 @@
objectsSize = 0;
} else {
if (kernelFields) {
+#ifdef BINDER_WITH_KERNEL_IPC
+ validateReadData(mDataSize); // hack to sort the objects
while (objectsSize > 0) {
- if (kernelFields->mObjects[objectsSize - 1] < desired) break;
+ if (kernelFields->mObjects[objectsSize - 1] + sizeof(flat_binder_object) <=
+ desired)
+ break;
objectsSize--;
}
+#endif // BINDER_WITH_KERNEL_IPC
} else {
while (objectsSize > 0) {
- if (rpcFields->mObjectPositions[objectsSize - 1] < desired) break;
+ // Object size varies by type.
+ uint32_t pos = rpcFields->mObjectPositions[objectsSize - 1];
+ size_t size = sizeof(RpcFields::ObjectType);
+ uint32_t minObjectEnd;
+ if (__builtin_add_overflow(pos, sizeof(RpcFields::ObjectType), &minObjectEnd) ||
+ minObjectEnd > mDataSize) {
+ return BAD_VALUE;
+ }
+ const auto type = *reinterpret_cast<const RpcFields::ObjectType*>(mData + pos);
+ switch (type) {
+ case RpcFields::TYPE_BINDER_NULL:
+ break;
+ case RpcFields::TYPE_BINDER:
+ size += sizeof(uint64_t); // address
+ break;
+ case RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR:
+ size += sizeof(int32_t); // fd index
+ break;
+ }
+
+ if (pos + size <= desired) break;
objectsSize--;
}
}
@@ -3104,15 +3128,24 @@
if (mData) {
memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
}
+#ifdef BINDER_WITH_KERNEL_IPC
if (objects && kernelFields && kernelFields->mObjects) {
memcpy(objects, kernelFields->mObjects, objectsSize * sizeof(binder_size_t));
+ // All FDs are owned when `mOwner`, even when `cookie == 0`. When
+ // we switch to `!mOwner`, we need to explicitly mark the FDs as
+ // owned.
+ for (size_t i = 0; i < objectsSize; i++) {
+ flat_binder_object* flat = reinterpret_cast<flat_binder_object*>(data + objects[i]);
+ if (flat->hdr.type == BINDER_TYPE_FD) {
+ flat->cookie = 1;
+ }
+ }
}
// ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
if (kernelFields) {
- // TODO(b/239222407): This seems wrong. We should only free FDs when
- // they are in a truncated section of the parcel.
- closeFileDescriptors();
+ closeFileDescriptors(objectsSize);
}
+#endif // BINDER_WITH_KERNEL_IPC
mOwner(mData, mDataSize, kernelFields ? kernelFields->mObjects : nullptr,
kernelFields ? kernelFields->mObjectsSize : 0);
mOwner = nullptr;
@@ -3239,11 +3272,19 @@
}
while (rpcFields->mObjectPositions.size() > newObjectsSize) {
uint32_t pos = rpcFields->mObjectPositions.back();
- rpcFields->mObjectPositions.pop_back();
+ uint32_t minObjectEnd;
+ if (__builtin_add_overflow(pos, sizeof(RpcFields::ObjectType), &minObjectEnd) ||
+ minObjectEnd > mDataSize) {
+ return BAD_VALUE;
+ }
const auto type = *reinterpret_cast<const RpcFields::ObjectType*>(mData + pos);
if (type == RpcFields::TYPE_NATIVE_FILE_DESCRIPTOR) {
- const auto fdIndex =
- *reinterpret_cast<const int32_t*>(mData + pos + sizeof(RpcFields::ObjectType));
+ uint32_t objectEnd;
+ if (__builtin_add_overflow(minObjectEnd, sizeof(int32_t), &objectEnd) ||
+ objectEnd > mDataSize) {
+ return BAD_VALUE;
+ }
+ const auto fdIndex = *reinterpret_cast<const int32_t*>(mData + minObjectEnd);
if (rpcFields->mFds == nullptr || fdIndex < 0 ||
static_cast<size_t>(fdIndex) >= rpcFields->mFds->size()) {
ALOGE("RPC Parcel contains invalid file descriptor index. index=%d fd_count=%zu",
@@ -3253,6 +3294,7 @@
// In practice, this always removes the last element.
rpcFields->mFds->erase(rpcFields->mFds->begin() + fdIndex);
}
+ rpcFields->mObjectPositions.pop_back();
}
return OK;
}
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index dba6587..9a98097 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -99,27 +99,28 @@
return status;
}
+ if (mException == EX_HAS_NOTED_APPOPS_REPLY_HEADER) {
+ status = skipUnusedHeader(parcel);
+ if (status != OK) {
+ setFromStatusT(status);
+ return status;
+ }
+ // Read next exception code.
+ status = parcel.readInt32(&mException);
+ if (status != OK) {
+ setFromStatusT(status);
+ return status;
+ }
+ }
+
// Skip over fat response headers. Not used (or propagated) in native code.
if (mException == EX_HAS_REPLY_HEADER) {
- // Note that the header size includes the 4 byte size field.
- const size_t header_start = parcel.dataPosition();
- // Get available size before reading more
- const size_t header_avail = parcel.dataAvail();
-
- int32_t header_size;
- status = parcel.readInt32(&header_size);
+ status = skipUnusedHeader(parcel);
if (status != OK) {
setFromStatusT(status);
return status;
}
- if (header_size < 0 || static_cast<size_t>(header_size) > header_avail) {
- android_errorWriteLog(0x534e4554, "132650049");
- setFromStatusT(UNKNOWN_ERROR);
- return UNKNOWN_ERROR;
- }
-
- parcel.setDataPosition(header_start + header_size);
// And fat response headers are currently only used when there are no
// exceptions, so act like there was no error.
mException = EX_NONE;
@@ -257,5 +258,28 @@
return ret;
}
+status_t Status::skipUnusedHeader(const Parcel& parcel) {
+ // Note that the header size includes the 4 byte size field.
+ const size_t header_start = parcel.dataPosition();
+ // Get available size before reading more
+ const size_t header_avail = parcel.dataAvail();
+
+ int32_t header_size;
+ status_t status = parcel.readInt32(&header_size);
+ ALOGD("Skip unused header. exception code: %d, start: %zu, size: %d.",
+ mException, header_start, header_size);
+ if (status != OK) {
+ return status;
+ }
+
+ if (header_size < 0 || static_cast<size_t>(header_size) > header_avail) {
+ android_errorWriteLog(0x534e4554, "132650049");
+ return UNKNOWN_ERROR;
+ }
+
+ parcel.setDataPosition(header_start + header_size);
+ return OK;
+}
+
} // namespace binder
} // namespace android
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index 95a5da2..ab44957 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -94,6 +94,9 @@
},
{
"name": "libbinder_rpc_unstable_bindgen_test"
+ },
+ {
+ "name": "binderCacheUnitTest"
}
],
"presubmit-large": [
@@ -133,9 +136,6 @@
{
"name": "binder_sdk_test",
"host": true
- },
- {
- "name": "binderCacheUnitTest"
}
],
"imports": [
diff --git a/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
index 75f8753..9bac386 100644
--- a/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
+++ b/libs/binder/aidl/android/content/pm/ApexStagedEvent.aidl
@@ -16,6 +16,8 @@
package android.content.pm;
+import android.content.pm.StagedApexInfo;
+
/**
* This event is designed for notification to native code listener about
* any changes to set of apex packages staged for installation on next boot.
@@ -23,5 +25,5 @@
* @hide
*/
parcelable ApexStagedEvent {
- @utf8InCpp String[] stagedApexModuleNames;
+ StagedApexInfo[] stagedApexInfos;
}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 3ddfefa..0f0be2f 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -135,13 +135,7 @@
void unregisterStagedApexObserver(in IStagedApexObserver observer);
/**
- * Get APEX module names of all APEX that are staged ready for installation
+ * Get information of staged APEXes.
*/
- @utf8InCpp String[] getStagedApexModuleNames();
-
- /**
- * Get information of APEX which is staged ready for installation.
- * Returns null if no such APEX is found.
- */
- @nullable StagedApexInfo getStagedApexInfo(in @utf8InCpp String moduleName);
+ StagedApexInfo[] getStagedApexInfos();
}
diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
index 949835b..8f7ad30 100644
--- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
+++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
@@ -22,6 +22,7 @@
*
* @hide
*/
+@JavaDerive(equals=true)
parcelable StagedApexInfo {
@utf8InCpp String moduleName;
@utf8InCpp String diskImagePath;
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 2b23276..81f7cdb 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -291,6 +291,28 @@
* \return OK if the binder is an IAccessor for `instance`
*/
LIBBINDER_EXPORTED status_t validateAccessor(const String16& instance, const sp<IBinder>& binder);
+
+/**
+ * Have libbinder wrap this IAccessor binder in an IAccessorDelegator and return
+ * it.
+ *
+ * This is required only in very specific situations when the process that has
+ * permissions to connect the to RPC service's socket and create the FD for it
+ * is in a separate process from this process that wants to service the Accessor
+ * binder and the communication between these two processes is binder RPC. This
+ * is needed because the binder passed over the binder RPC connection can not be
+ * used as a kernel binder, and needs to be wrapped by a kernel binder that can
+ * then be registered with service manager.
+ *
+ * \param instance name of the Accessor.
+ * \param binder to wrap in a Delegator and register with service manager.
+ * \param outDelegator the wrapped kernel binder for IAccessorDelegator
+ *
+ * \return OK if the binder is an IAccessor for `instance` and the delegator was
+ * successfully created.
+ */
+LIBBINDER_EXPORTED status_t delegateAccessor(const String16& name, const sp<IBinder>& accessor,
+ sp<IBinder>* delegator);
#endif // __TRUSTY__
#ifndef __ANDROID__
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 5cc0830..ceab20a 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -92,7 +92,7 @@
LIBBINDER_EXPORTED status_t appendFrom(const Parcel* parcel, size_t start, size_t len);
- LIBBINDER_EXPORTED int compareData(const Parcel& other);
+ LIBBINDER_EXPORTED int compareData(const Parcel& other) const;
LIBBINDER_EXPORTED status_t compareDataInRange(size_t thisOffset, const Parcel& other,
size_t otherOffset, size_t length,
int* result) const;
@@ -172,7 +172,6 @@
LIBBINDER_EXPORTED status_t write(const void* data, size_t len);
LIBBINDER_EXPORTED void* writeInplace(size_t len);
- LIBBINDER_EXPORTED status_t writeUnpadded(const void* data, size_t len);
LIBBINDER_EXPORTED status_t writeInt32(int32_t val);
LIBBINDER_EXPORTED status_t writeUint32(uint32_t val);
LIBBINDER_EXPORTED status_t writeInt64(int64_t val);
@@ -637,9 +636,6 @@
LIBBINDER_EXPORTED const flat_binder_object* readObject(bool nullMetaData) const;
- // Explicitly close all file descriptors in the parcel.
- LIBBINDER_EXPORTED void closeFileDescriptors();
-
// Debugging: get metrics on current allocations.
LIBBINDER_EXPORTED static size_t getGlobalAllocSize();
LIBBINDER_EXPORTED static size_t getGlobalAllocCount();
@@ -652,6 +648,9 @@
LIBBINDER_EXPORTED void print(std::ostream& to, uint32_t flags = 0) const;
private:
+ // Close all file descriptors in the parcel at object positions >= newObjectsSize.
+ void closeFileDescriptors(size_t newObjectsSize);
+
// `objects` and `objectsSize` always 0 for RPC Parcels.
typedef void (*release_func)(const uint8_t* data, size_t dataSize, const binder_size_t* objects,
size_t objectsSize);
@@ -1240,7 +1239,7 @@
if (__builtin_mul_overflow(size, sizeof(T), &dataLen)) {
return -EOVERFLOW;
}
- auto data = reinterpret_cast<const T*>(readInplace(dataLen));
+ auto data = readInplace(dataLen);
if (data == nullptr) return BAD_VALUE;
// std::vector::insert and similar methods will require type-dependent
// byte alignment when inserting from a const iterator such as `data`,
diff --git a/libs/binder/include/binder/SafeInterface.h b/libs/binder/include/binder/SafeInterface.h
index c671eed..0b4f196 100644
--- a/libs/binder/include/binder/SafeInterface.h
+++ b/libs/binder/include/binder/SafeInterface.h
@@ -152,6 +152,14 @@
return callParcel("writeParcelableVector",
[&]() { return parcel->writeParcelableVector(v); });
}
+
+ status_t read(const Parcel& parcel, std::vector<bool>* v) const {
+ return callParcel("readBoolVector", [&]() { return parcel.readBoolVector(v); });
+ }
+ status_t write(Parcel* parcel, const std::vector<bool>& v) const {
+ return callParcel("writeBoolVector", [&]() { return parcel->writeBoolVector(v); });
+ }
+
status_t read(const Parcel& parcel, float* f) const {
return callParcel("readFloat", [&]() { return parcel.readFloat(f); });
}
diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h
index 49ccf7c..d69f662 100644
--- a/libs/binder/include/binder/Status.h
+++ b/libs/binder/include/binder/Status.h
@@ -67,6 +67,9 @@
EX_SERVICE_SPECIFIC = -8,
EX_PARCELABLE = -9,
+ // See android/os/Parcel.java. We need to handle this in native code.
+ EX_HAS_NOTED_APPOPS_REPLY_HEADER = -127,
+
// This is special and Java specific; see Parcel.java.
EX_HAS_REPLY_HEADER = -128,
// This is special, and indicates to C++ binder proxies that the
@@ -150,6 +153,8 @@
Status(int32_t exceptionCode, int32_t errorCode);
Status(int32_t exceptionCode, int32_t errorCode, const String8& message);
+ status_t skipUnusedHeader(const Parcel& parcel);
+
// If |mException| == EX_TRANSACTION_FAILED, generated code will return
// |mErrorCode| as the result of the transaction rather than write an
// exception to the reply parcel.
diff --git a/libs/binder/include/binder/Trace.h b/libs/binder/include/binder/Trace.h
index 2f450cb..a3e6c8a 100644
--- a/libs/binder/include/binder/Trace.h
+++ b/libs/binder/include/binder/Trace.h
@@ -42,6 +42,7 @@
void trace_begin(uint64_t tag, const char* name);
void trace_end(uint64_t tag);
void trace_int(uint64_t tag, const char* name, int32_t value);
+uint64_t get_trace_enabled_tags();
} // namespace os
class LIBBINDER_EXPORTED ScopedTrace {
diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp
index 2cc5f81..07b8c40 100644
--- a/libs/binder/ndk/binder_rpc.cpp
+++ b/libs/binder/ndk/binder_rpc.cpp
@@ -94,7 +94,7 @@
}
void* mData;
ABinderRpc_AccessorProviderUserData_deleteCallback mOnDelete;
- // needs to be copyable for std::function, but we will never copy it
+ // needs to be copy-able for std::function, but we will never copy it
OnDeleteProviderHolder(const OnDeleteProviderHolder&) {
LOG_ALWAYS_FATAL("This object can't be copied!");
}
@@ -113,7 +113,7 @@
}
if (data && onDelete == nullptr) {
ALOGE("If a non-null data ptr is passed to ABinderRpc_registerAccessorProvider, then a "
- "ABinderRpc_AccessorProviderUserData_deleteCallback must alse be passed to delete "
+ "ABinderRpc_AccessorProviderUserData_deleteCallback must also be passed to delete "
"the data object once the ABinderRpc_AccessorProvider is removed.");
return nullptr;
}
@@ -179,7 +179,7 @@
}
void* mData;
ABinderRpc_ConnectionInfoProviderUserData_delete mOnDelete;
- // needs to be copyable for std::function, but we will never copy it
+ // needs to be copy-able for std::function, but we will never copy it
OnDeleteConnectionInfoHolder(const OnDeleteConnectionInfoHolder&) {
LOG_ALWAYS_FATAL("This object can't be copied!");
}
@@ -197,7 +197,7 @@
}
if (data && onDelete == nullptr) {
ALOGE("If a non-null data ptr is passed to ABinderRpc_Accessor_new, then a "
- "ABinderRpc_ConnectionInfoProviderUserData_delete callback must alse be passed to "
+ "ABinderRpc_ConnectionInfoProviderUserData_delete callback must also be passed to "
"delete "
"the data object once the ABinderRpc_Accessor is deleted.");
return nullptr;
@@ -302,9 +302,31 @@
}
}
+binder_status_t ABinderRpc_Accessor_delegateAccessor(const char* instance, AIBinder* accessor,
+ AIBinder** outDelegator) {
+ LOG_ALWAYS_FATAL_IF(outDelegator == nullptr, "The outDelegator argument is null");
+ if (instance == nullptr || accessor == nullptr) {
+ ALOGW("instance or accessor arguments to ABinderRpc_Accessor_delegateBinder are null");
+ *outDelegator = nullptr;
+ return STATUS_UNEXPECTED_NULL;
+ }
+ sp<IBinder> accessorBinder = accessor->getBinder();
+
+ sp<IBinder> delegator;
+ status_t status = android::delegateAccessor(String16(instance), accessorBinder, &delegator);
+ if (status != OK) {
+ return PruneStatusT(status);
+ }
+ sp<AIBinder> binder = ABpBinder::lookupOrCreateFromBinder(delegator);
+ // This AIBinder needs a strong ref to pass ownership to the caller
+ binder->incStrong(nullptr);
+ *outDelegator = binder.get();
+ return OK;
+}
+
ABinderRpc_ConnectionInfo* ABinderRpc_ConnectionInfo_new(const sockaddr* addr, socklen_t len) {
if (addr == nullptr || len < 0 || static_cast<size_t>(len) < sizeof(sa_family_t)) {
- ALOGE("Invalid arguments in Arpc_Connection_new");
+ ALOGE("Invalid arguments in ABinderRpc_Connection_new");
return nullptr;
}
// socklen_t was int32_t on 32-bit and uint32_t on 64 bit.
@@ -317,8 +339,9 @@
return nullptr;
}
sockaddr_vm vm = *reinterpret_cast<const sockaddr_vm*>(addr);
- LOG_ACCESSOR_DEBUG("Arpc_ConnectionInfo_new found AF_VSOCK. family %d, port %d, cid %d",
- vm.svm_family, vm.svm_port, vm.svm_cid);
+ LOG_ACCESSOR_DEBUG(
+ "ABinderRpc_ConnectionInfo_new found AF_VSOCK. family %d, port %d, cid %d",
+ vm.svm_family, vm.svm_port, vm.svm_cid);
return new ABinderRpc_ConnectionInfo(vm);
} else if (addr->sa_family == AF_UNIX) {
if (len != sizeof(sockaddr_un)) {
@@ -327,7 +350,7 @@
return nullptr;
}
sockaddr_un un = *reinterpret_cast<const sockaddr_un*>(addr);
- LOG_ACCESSOR_DEBUG("Arpc_ConnectionInfo_new found AF_UNIX. family %d, path %s",
+ LOG_ACCESSOR_DEBUG("ABinderRpc_ConnectionInfo_new found AF_UNIX. family %d, path %s",
un.sun_family, un.sun_path);
return new ABinderRpc_ConnectionInfo(un);
} else if (addr->sa_family == AF_INET) {
@@ -337,12 +360,14 @@
return nullptr;
}
sockaddr_in in = *reinterpret_cast<const sockaddr_in*>(addr);
- LOG_ACCESSOR_DEBUG("Arpc_ConnectionInfo_new found AF_INET. family %d, address %s, port %d",
- in.sin_family, inet_ntoa(in.sin_addr), ntohs(in.sin_port));
+ LOG_ACCESSOR_DEBUG(
+ "ABinderRpc_ConnectionInfo_new found AF_INET. family %d, address %s, port %d",
+ in.sin_family, inet_ntoa(in.sin_addr), ntohs(in.sin_port));
return new ABinderRpc_ConnectionInfo(in);
}
- ALOGE("ARpc APIs only support AF_VSOCK right now but the supplied sockadder::sa_family is: %hu",
+ ALOGE("ABinderRpc APIs only support AF_VSOCK right now but the supplied sockaddr::sa_family "
+ "is: %hu",
addr->sa_family);
return nullptr;
}
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index af280d3..ff31dd0 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -18,8 +18,10 @@
#include <android/binder_ibinder_platform.h>
#include <android/binder_stability.h>
#include <android/binder_status.h>
+#include <binder/Functional.h>
#include <binder/IPCThreadState.h>
#include <binder/IResultReceiver.h>
+#include <binder/Trace.h>
#if __has_include(<private/android_filesystem_config.h>)
#include <private/android_filesystem_config.h>
#endif
@@ -40,6 +42,23 @@
using ::android::String16;
using ::android::String8;
using ::android::wp;
+using ::android::binder::impl::make_scope_guard;
+using ::android::binder::impl::scope_guard;
+using ::android::binder::os::get_trace_enabled_tags;
+using ::android::binder::os::trace_begin;
+using ::android::binder::os::trace_end;
+
+// transaction codes for getInterfaceHash and getInterfaceVersion are defined
+// in file : system/tools/aidl/aidl.cpp
+static constexpr int kGetInterfaceVersionId = 0x00fffffe;
+static const char* kInterfaceVersion = "getInterfaceVersion";
+static constexpr int kGetInterfaceHashId = 0x00fffffd;
+static const char* kInterfaceHash = "getInterfaceHash";
+static const char* kNdkTrace = "AIDL::ndk::";
+static const char* kServerTrace = "::server";
+static const char* kClientTrace = "::client";
+static const char* kSeparator = "::";
+static const char* kUnknownCode = "Unknown_Transaction_Code:";
namespace ABBinderTag {
@@ -90,6 +109,51 @@
return sanitized;
}
+const std::string getMethodName(const AIBinder_Class* clazz, transaction_code_t code) {
+ // TODO(b/150155678) - Move getInterfaceHash and getInterfaceVersion to libbinder and remove
+ // hardcoded cases.
+ if (code <= clazz->getTransactionCodeToFunctionLength() && code >= FIRST_CALL_TRANSACTION) {
+ // Codes have FIRST_CALL_TRANSACTION as added offset. Subtract to access function name
+ return clazz->getFunctionName(code);
+ } else if (code == kGetInterfaceVersionId) {
+ return kInterfaceVersion;
+ } else if (code == kGetInterfaceHashId) {
+ return kInterfaceHash;
+ }
+ return kUnknownCode + std::to_string(code);
+}
+
+const std::string getTraceSectionName(const AIBinder_Class* clazz, transaction_code_t code,
+ bool isServer) {
+ if (clazz == nullptr) {
+ ALOGE("class associated with binder is null. Class is needed to add trace with interface "
+ "name and function name");
+ return kNdkTrace;
+ }
+
+ const std::string descriptor = clazz->getInterfaceDescriptorUtf8();
+ const std::string methodName = getMethodName(clazz, code);
+
+ size_t traceSize =
+ strlen(kNdkTrace) + descriptor.size() + strlen(kSeparator) + methodName.size();
+ traceSize += isServer ? strlen(kServerTrace) : strlen(kClientTrace);
+
+ std::string trace;
+ // reserve to avoid repeated allocations
+ trace.reserve(traceSize);
+
+ trace += kNdkTrace;
+ trace += clazz->getInterfaceDescriptorUtf8();
+ trace += kSeparator;
+ trace += methodName;
+ trace += isServer ? kServerTrace : kClientTrace;
+
+ LOG_ALWAYS_FATAL_IF(trace.size() != traceSize, "Trace size mismatch. Expected %zu, got %zu",
+ traceSize, trace.size());
+
+ return trace;
+}
+
bool AIBinder::associateClass(const AIBinder_Class* clazz) {
if (clazz == nullptr) return false;
@@ -203,6 +267,17 @@
status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parcel* reply,
binder_flags_t flags) {
+ std::string sectionName;
+ bool tracingEnabled = get_trace_enabled_tags() & ATRACE_TAG_AIDL;
+ if (tracingEnabled) {
+ sectionName = getTraceSectionName(getClass(), code, true /*isServer*/);
+ trace_begin(ATRACE_TAG_AIDL, sectionName.c_str());
+ }
+
+ scope_guard guard = make_scope_guard([&]() {
+ if (tracingEnabled) trace_end(ATRACE_TAG_AIDL);
+ });
+
if (isUserCommand(code)) {
if (getClass()->writeHeader && !data.checkInterface(this)) {
return STATUS_BAD_TYPE;
@@ -385,6 +460,31 @@
mInterfaceDescriptor(interfaceDescriptor),
mWideInterfaceDescriptor(interfaceDescriptor) {}
+bool AIBinder_Class::setTransactionCodeMap(const char** transactionCodeMap, size_t length) {
+ if (mTransactionCodeToFunction != nullptr) {
+ ALOGE("mTransactionCodeToFunction is already set!");
+ return false;
+ }
+ mTransactionCodeToFunction = transactionCodeMap;
+ mTransactionCodeToFunctionLength = length;
+ return true;
+}
+
+const char* AIBinder_Class::getFunctionName(transaction_code_t code) const {
+ if (mTransactionCodeToFunction == nullptr) {
+ ALOGE("mTransactionCodeToFunction is not set!");
+ return nullptr;
+ }
+
+ if (code < FIRST_CALL_TRANSACTION ||
+ code - FIRST_CALL_TRANSACTION >= mTransactionCodeToFunctionLength) {
+ ALOGE("Function name for requested code not found!");
+ return nullptr;
+ }
+
+ return mTransactionCodeToFunction[code - FIRST_CALL_TRANSACTION];
+}
+
AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor,
AIBinder_Class_onCreate onCreate,
AIBinder_Class_onDestroy onDestroy,
@@ -404,6 +504,24 @@
clazz->onDump = onDump;
}
+void AIBinder_Class_setTransactionCodeToFunctionNameMap(AIBinder_Class* clazz,
+ const char** transactionCodeToFunction,
+ size_t length) {
+ LOG_ALWAYS_FATAL_IF(clazz == nullptr || transactionCodeToFunction == nullptr,
+ "Valid clazz and transactionCodeToFunction are needed to set code to "
+ "function mapping.");
+ LOG_ALWAYS_FATAL_IF(!clazz->setTransactionCodeMap(transactionCodeToFunction, length),
+ "Failed to set transactionCodeToFunction to clazz! Is "
+ "transactionCodeToFunction already set?");
+}
+
+const char* AIBinder_Class_getFunctionName(AIBinder_Class* clazz, transaction_code_t code) {
+ LOG_ALWAYS_FATAL_IF(
+ clazz == nullptr,
+ "Valid clazz is needed to get function name for requested transaction code");
+ return clazz->getFunctionName(code);
+}
+
void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) {
LOG_ALWAYS_FATAL_IF(clazz == nullptr, "disableInterfaceTokenHeader requires non-null clazz");
@@ -734,6 +852,19 @@
binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code, AParcel** in,
AParcel** out, binder_flags_t flags) {
+ const AIBinder_Class* clazz = binder ? binder->getClass() : nullptr;
+
+ std::string sectionName;
+ bool tracingEnabled = get_trace_enabled_tags() & ATRACE_TAG_AIDL;
+ if (tracingEnabled) {
+ sectionName = getTraceSectionName(clazz, code, false /*isServer*/);
+ trace_begin(ATRACE_TAG_AIDL, sectionName.c_str());
+ }
+
+ scope_guard guard = make_scope_guard([&]() {
+ if (tracingEnabled) trace_end(ATRACE_TAG_AIDL);
+ });
+
if (in == nullptr) {
ALOGE("%s: requires non-null in parameter", __func__);
return STATUS_UNEXPECTED_NULL;
@@ -872,4 +1003,4 @@
"AIBinder_setInheritRt must be called on a local binder");
localBinder->setInheritRt(inheritRt);
-}
+}
\ No newline at end of file
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index f5b738c..a93dc1f 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -132,6 +132,9 @@
const ::android::String16& getInterfaceDescriptor() const { return mWideInterfaceDescriptor; }
const char* getInterfaceDescriptorUtf8() const { return mInterfaceDescriptor.c_str(); }
+ bool setTransactionCodeMap(const char** transactionCodeMap, size_t transactionCodeMapSize);
+ const char* getFunctionName(transaction_code_t code) const;
+ size_t getTransactionCodeToFunctionLength() const { return mTransactionCodeToFunctionLength; }
// whether a transaction header should be written
bool writeHeader = true;
@@ -151,6 +154,10 @@
// This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
// one.
const ::android::String16 mWideInterfaceDescriptor;
+ // Array which holds names of the functions
+ const char** mTransactionCodeToFunction = nullptr;
+ // length of mmTransactionCodeToFunctionLength array
+ size_t mTransactionCodeToFunctionLength = 0;
};
// Ownership is like this (when linked to death):
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 af56bf0..0ad110e 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -30,6 +30,17 @@
#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, *))
+#else
+#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
+#endif // __BIONIC__
+#endif // __ANDROID_VENDOR_API__
+
#if __has_include(<android/binder_shell.h>)
#include <android/binder_shell.h>
#define HAS_BINDER_SHELL_COMMAND
@@ -164,6 +175,10 @@
* Helper method to create a class
*/
static inline AIBinder_Class* defineClass(const char* interfaceDescriptor,
+ AIBinder_Class_onTransact onTransact,
+ const char** codeToFunction, size_t functionCount);
+
+ static inline AIBinder_Class* defineClass(const char* interfaceDescriptor,
AIBinder_Class_onTransact onTransact);
private:
@@ -256,6 +271,13 @@
AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor,
AIBinder_Class_onTransact onTransact) {
+
+ return defineClass(interfaceDescriptor, onTransact, nullptr, 0);
+}
+
+AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor,
+ AIBinder_Class_onTransact onTransact,
+ const char** codeToFunction, size_t functionCount) {
AIBinder_Class* clazz = AIBinder_Class_define(interfaceDescriptor, ICInterfaceData::onCreate,
ICInterfaceData::onDestroy, onTransact);
if (clazz == nullptr) {
@@ -274,6 +296,19 @@
AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
}
#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)) {
+ AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction,
+ functionCount);
+ }
+ }
+#else
+ (void)codeToFunction;
+ (void)functionCount;
+#endif // defined(__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) || __ANDROID_API__ >= 36
return clazz;
}
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 c1d0e9f..83976b3 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -22,8 +22,8 @@
#include <set>
#include <sstream>
-// Include llndk-versioning.h only for vendor build as it is not available for NDK headers.
-#if defined(__ANDROID_VENDOR__)
+// 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__)
@@ -32,7 +32,7 @@
#else
#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (true)
#endif // __BIONIC__
-#endif // __ANDROID_VENDOR__
+#endif // __ANDROID_VENDOR_API__
namespace aidl::android::os {
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 72d255e..bd46c47 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -219,6 +219,50 @@
void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29);
/**
+ * Associates a mapping of transaction codes(transaction_code_t) to function names for the given
+ * class.
+ *
+ * Trace messages will use the provided names instead of bare integer codes when set. If not set by
+ * this function, trace messages will only be identified by the bare code. This should be called one
+ * time during clazz initialization. clazz is defined using AIBinder_Class_define and
+ * transactionCodeToFunctionMap should have same scope as clazz. Resetting/clearing the
+ * transactionCodeToFunctionMap is not allowed. Passing null for either clazz or
+ * transactionCodeToFunctionMap will abort.
+ *
+ * Available since API level 36.
+ *
+ * \param clazz class which should use this transaction to code function map.
+ * \param transactionCodeToFunctionMap array of function names indexed by transaction code.
+ * Transaction codes start from 1, functions with transaction code 1 will correspond to index 0 in
+ * transactionCodeToFunctionMap. When defining methods, transaction codes are expected to be
+ * contiguous, and this is required for maximum memory efficiency.
+ * You can use nullptr if certain transaction codes are not used. Lifetime should be same as clazz.
+ * \param length number of elements in the transactionCodeToFunctionMap
+ */
+void AIBinder_Class_setTransactionCodeToFunctionNameMap(AIBinder_Class* clazz,
+ const char** transactionCodeToFunctionMap,
+ size_t length) __INTRODUCED_IN(36);
+
+/**
+ * Get function name associated with transaction code for given class
+ *
+ * This function returns function name associated with provided transaction code for given class.
+ * AIBinder_Class_setTransactionCodeToFunctionNameMap should be called first to associate function
+ * to transaction code mapping.
+ *
+ * Available since API level 36.
+ *
+ * \param clazz class for which function name is requested
+ * \param transactionCode transaction_code_t for which function name is requested.
+ *
+ * \return function name in form of const char* if transaction code is valid for given class.
+ * The value returned is valid for the lifetime of clazz. if transaction code is invalid or
+ * transactionCodeToFunctionMap is not set, nullptr is returned.
+ */
+const char* AIBinder_Class_getFunctionName(AIBinder_Class* clazz, transaction_code_t code)
+ __INTRODUCED_IN(36);
+
+/**
* This tells users of this class not to use a transaction header. By default, libbinder_ndk users
* read/write transaction headers implicitly (in the SDK, this must be manually written by
* android.os.Parcel#writeInterfaceToken, and it is read/checked with
diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h
index 4c5471f..9fe5d78 100644
--- a/libs/binder/ndk/include_platform/android/binder_rpc.h
+++ b/libs/binder/ndk/include_platform/android/binder_rpc.h
@@ -22,6 +22,22 @@
__BEGIN_DECLS
/**
+ * @defgroup ABinderRpc Binder RPC
+ *
+ * This set of APIs makes it possible for a process to use the AServiceManager
+ * APIs to get binder objects for services that are available over sockets
+ * instead of the traditional kernel binder with the extra ServiceManager
+ * process.
+ *
+ * These APIs are used to supply libbinder with enough information to create
+ * and manage the socket connections underneath the ServiceManager APIs so the
+ * clients do not need to know the service implementation details or what
+ * transport they use for communication.
+ *
+ * @{
+ */
+
+/**
* This represents an IAccessor implementation from libbinder that is
* responsible for providing a pre-connected socket file descriptor for a
* specific service. The service is an RpcServer and the pre-connected socket is
@@ -66,7 +82,8 @@
* libbinder.
*
* \param instance name of the service like
- * `android.hardware.vibrator.IVibrator/default`
+ * `android.hardware.vibrator.IVibrator/default`. This string must remain
+ * valid and unchanged for the duration of this function call.
* \param data the data that was associated with this instance when the callback
* was registered.
* \return The ABinderRpc_Accessor associated with the service `instance`. This
@@ -103,13 +120,15 @@
* instance is being registered that was previously registered, this call
* will fail and the ABinderRpc_AccessorProviderUserData_deleteCallback
* will be called to clean up the data.
+ * This array of strings must remain valid and unchanged for the duration
+ * of this function call.
* \param number of instances in the instances array.
* \param data pointer that is passed to the ABinderRpc_AccessorProvider callback.
* IMPORTANT: The ABinderRpc_AccessorProvider now OWNS that object that data
* points to. It can be used as necessary in the callback. The data MUST
* remain valid for the lifetime of the provider callback.
* Do not attempt to give ownership of the same object to different
- * providers throguh multiple calls to this function because the first
+ * providers through multiple calls to this function because the first
* one to be deleted will call the onDelete callback.
* \param onDelete callback used to delete the objects that `data` points to.
* This is called after ABinderRpc_AccessorProvider is guaranteed to never be
@@ -151,8 +170,9 @@
* connect to a socket that a given service is listening on. This is needed to
* create an ABinderRpc_Accessor so it can connect to these services.
*
- * \param instance name of the service to connect to
- * \param data userdata for this callback. The pointer is provided in
+ * \param instance name of the service to connect to. This string must remain
+ * valid and unchanged for the duration of this function call.
+ * \param data user data for this callback. The pointer is provided in
* ABinderRpc_Accessor_new.
* \return ABinderRpc_ConnectionInfo with socket connection information for `instance`
*/
@@ -177,7 +197,9 @@
* that can use the info from the ABinderRpc_ConnectionInfoProvider to connect to a
* socket that the service with `instance` name is listening to.
*
- * \param instance name of the service that is listening on the socket
+ * \param instance name of the service that is listening on the socket. This
+ * string must remain valid and unchanged for the duration of this
+ * function call.
* \param provider callback that can get the socket connection information for the
* instance. This connection information may be dynamic, so the
* provider will be called any time a new connection is required.
@@ -219,24 +241,59 @@
/**
* Return the ABinderRpc_Accessor associated with an AIBinder. The instance must match
- * the ABinderRpc_Accessor implementation, and the AIBinder must a proxy binder for a
- * remote service (ABpBinder).
- * This can be used when receivng an AIBinder from another process that the
+ * the ABinderRpc_Accessor implementation.
+ * This can be used when receiving an AIBinder from another process that the
* other process obtained from ABinderRpc_Accessor_asBinder.
*
* \param instance name of the service that the Accessor is responsible for.
- * \param accessorBinder proxy binder from another processes ABinderRpc_Accessor.
+ * This string must remain valid and unchanged for the duration of this
+ * function call.
+ * \param accessorBinder proxy binder from another process's ABinderRpc_Accessor.
+ * This function preserves the refcount of this binder object and the
+ * caller still owns it.
* \return ABinderRpc_Accessor representing the other processes ABinderRpc_Accessor
- * implementation. This function does not take ownership of the
- * ABinderRpc_Accessor (so the caller needs to delete with
- * ABinderRpc_Accessor_delete), and it preserves the recount of the bidner
- * object.
+ * implementation. The caller owns this ABinderRpc_Accessor instance and
+ * is responsible for deleting it with ABinderRpc_Accessor_delete or
+ * passing ownership of it elsewhere, like returning it through
+ * ABinderRpc_AccessorProvider_getAccessorCallback.
+ * nullptr on error when the accessorBinder is not a valid binder from
+ * an IAccessor implementation or the IAccessor implementation is not
+ * associated with the provided instance.
*/
ABinderRpc_Accessor* _Nullable ABinderRpc_Accessor_fromBinder(const char* _Nonnull instance,
AIBinder* _Nonnull accessorBinder)
__INTRODUCED_IN(36);
/**
+ * Wrap an ABinderRpc_Accessor proxy binder with a delegator binder.
+ *
+ * The IAccessorDelegator binder delegates all calls to the proxy binder.
+ *
+ * This is required only in very specific situations when the process that has
+ * permissions to connect the to RPC service's socket and create the FD for it
+ * is in a separate process from this process that wants to serve the Accessor
+ * binder and the communication between these two processes is binder RPC. This
+ * is needed because the binder passed over the binder RPC connection can not be
+ * used as a kernel binder, and needs to be wrapped by a kernel binder that can
+ * then be registered with service manager.
+ *
+ * \param instance name of the service associated with the Accessor
+ * \param binder the AIBinder* from the ABinderRpc_Accessor from the
+ * ABinderRpc_Accessor_asBinder. The other process across the binder RPC
+ * connection will have called this and passed the AIBinder* across a
+ * binder interface to the process calling this function.
+ * \param outDelegator the AIBinder* for the kernel binder that wraps the
+ * 'binder' argument and delegates all calls to it. The caller now owns
+ * this object with one strong ref count and is responsible for removing
+ * that ref count with with AIBinder_decStrong when the caller wishes to
+ * drop the reference.
+ */
+binder_status_t ABinderRpc_Accessor_delegateAccessor(const char* _Nonnull instance,
+ AIBinder* _Nonnull binder,
+ AIBinder* _Nullable* _Nonnull outDelegator)
+ __INTRODUCED_IN(36);
+
+/**
* Create a new ABinderRpc_ConnectionInfo with sockaddr. This can be supported socket
* types like sockaddr_vm (vsock) and sockaddr_un (Unix Domain Sockets).
*
@@ -258,4 +315,6 @@
*/
void ABinderRpc_ConnectionInfo_delete(ABinderRpc_ConnectionInfo* _Nonnull info) __INTRODUCED_IN(36);
+/** @} */
+
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index c9e669e..4d691f8 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -250,9 +250,14 @@
LIBBINDER_NDK36 { # introduced=36
global:
+ AIBinder_Class_setTransactionCodeToFunctionNameMap;
+ AIBinder_Class_setTransactionCodeToFunctionNameMap; # llndk=202504
+ AIBinder_Class_getFunctionName;
+ AIBinder_Class_getFunctionName; # llndk=202504
ABinderRpc_registerAccessorProvider; # systemapi
ABinderRpc_unregisterAccessorProvider; # systemapi
ABinderRpc_Accessor_new; # systemapi
+ ABinderRpc_Accessor_delegateAccessor; #systemapi
ABinderRpc_Accessor_delete; # systemapi
ABinderRpc_Accessor_asBinder; # systemapi
ABinderRpc_Accessor_fromBinder; # systemapi
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 3cd2b9a..e5a3da4 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -1108,6 +1108,37 @@
EXPECT_EQ(deleteCount, 0);
}
+void* EmptyOnCreate(void* args) {
+ return args;
+}
+void EmptyOnDestroy(void* /*userData*/) {}
+binder_status_t EmptyOnTransact(AIBinder* /*binder*/, transaction_code_t /*code*/,
+ const AParcel* /*in*/, AParcel* /*out*/) {
+ return STATUS_OK;
+}
+
+TEST(NdkBinder_DeathTest, SetCodeMapTwice) {
+ const char* codeToFunction1[] = {"function-1", "function-2", "function-3"};
+ const char* codeToFunction2[] = {"function-4", "function-5"};
+ const char* interfaceName = "interface_descriptor";
+ AIBinder_Class* clazz =
+ AIBinder_Class_define(interfaceName, EmptyOnCreate, EmptyOnDestroy, EmptyOnTransact);
+ AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction1, 3);
+ // Reset/clear is not allowed
+ EXPECT_DEATH(AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, codeToFunction2, 2), "");
+}
+
+TEST(NdkBinder_DeathTest, SetNullCodeMap) {
+ const char* codeToFunction[] = {"function-1", "function-2", "function-3"};
+ const char* interfaceName = "interface_descriptor";
+ AIBinder_Class* clazz =
+ AIBinder_Class_define(interfaceName, EmptyOnCreate, EmptyOnDestroy, EmptyOnTransact);
+ EXPECT_DEATH(AIBinder_Class_setTransactionCodeToFunctionNameMap(nullptr, codeToFunction, 3),
+ "");
+ EXPECT_DEATH(AIBinder_Class_setTransactionCodeToFunctionNameMap(clazz, nullptr, 0), "");
+ EXPECT_DEATH(AIBinder_Class_setTransactionCodeToFunctionNameMap(nullptr, nullptr, 0), "");
+}
+
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 2deb254..4545d7b 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -15,6 +15,8 @@
"libbinder_ndk_sys",
"libdowncast_rs",
"liblibc",
+ "liblog_rust",
+ "libnix",
],
host_supported: true,
vendor_available: true,
@@ -79,6 +81,9 @@
shared_libs: [
"libbinder_ndk",
],
+ rustlibs: [
+ "liblibc",
+ ],
host_supported: true,
vendor_available: true,
product_available: true,
@@ -129,9 +134,18 @@
// rustified
"libbinder_ndk_bindgen_flags.txt",
],
+ bindgen_flags: [
+ "--blocklist-type",
+ "sockaddr",
+ "--raw-line",
+ "use libc::sockaddr;",
+ ],
shared_libs: [
"libbinder_ndk",
],
+ rustlibs: [
+ "liblibc",
+ ],
host_supported: true,
vendor_available: true,
product_available: true,
@@ -185,6 +199,8 @@
"libbinder_ndk_sys",
"libdowncast_rs",
"liblibc",
+ "liblog_rust",
+ "libnix",
],
}
@@ -196,4 +212,7 @@
auto_gen_config: true,
clippy_lints: "none",
lints: "none",
+ rustlibs: [
+ "liblibc",
+ ],
}
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index 9a252b8..23026e5 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -136,6 +136,31 @@
}
}
+/// Same as `Stability`, but in the form of a trait. Used when the stability should be encoded in
+/// the type.
+///
+/// When/if the `adt_const_params` Rust feature is stabilized, this could be replace by using
+/// `Stability` directly with const generics.
+pub trait StabilityType {
+ /// The `Stability` represented by this type.
+ const VALUE: Stability;
+}
+
+/// `Stability::Local`.
+#[derive(Debug)]
+pub enum LocalStabilityType {}
+/// `Stability::Vintf`.
+#[derive(Debug)]
+pub enum VintfStabilityType {}
+
+impl StabilityType for LocalStabilityType {
+ const VALUE: Stability = Stability::Local;
+}
+
+impl StabilityType for VintfStabilityType {
+ const VALUE: Stability = Stability::Vintf;
+}
+
/// A local service that can be remotable via Binder.
///
/// An object that implement this interface made be made into a Binder service
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index e70f4f0..f7f3f35 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -104,6 +104,8 @@
mod service;
#[cfg(not(trusty))]
mod state;
+#[cfg(not(any(android_vendor, android_vndk)))]
+mod system_only;
use binder_ndk_sys as sys;
@@ -120,6 +122,8 @@
};
#[cfg(not(trusty))]
pub use state::{ProcessState, ThreadState};
+#[cfg(not(any(android_vendor, android_vndk)))]
+pub use system_only::{delegate_accessor, Accessor, ConnectionInfo};
/// Binder result containing a [`Status`] on error.
pub type Result<T> = std::result::Result<T, Status>;
@@ -128,9 +132,10 @@
/// without AIDL.
pub mod binder_impl {
pub use crate::binder::{
- IBinderInternal, InterfaceClass, Remotable, Stability, ToAsyncInterface, ToSyncInterface,
- TransactionCode, TransactionFlags, FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY,
- FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+ 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,
};
pub use crate::binder_async::BinderAsyncRuntime;
pub use crate::error::status_t;
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index f906113..87b42ab 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -15,6 +15,7 @@
*/
use crate::binder::Stability;
+use crate::binder::StabilityType;
use crate::error::StatusCode;
use crate::parcel::{
BorrowedParcel, Deserialize, Parcel, Parcelable, Serialize, NON_NULL_PARCELABLE_FLAG,
@@ -60,7 +61,7 @@
/// `Send` nor `Sync`), mainly because it internally contains
/// a `Parcel` which in turn is not thread-safe.
#[derive(Debug)]
-pub struct ParcelableHolder {
+pub struct ParcelableHolder<STABILITY: StabilityType> {
// This is a `Mutex` because of `get_parcelable`
// which takes `&self` for consistency with C++.
// We could make `get_parcelable` take a `&mut self`
@@ -68,13 +69,17 @@
// improvement, but then callers would require a mutable
// `ParcelableHolder` even for that getter method.
data: Mutex<ParcelableHolderData>,
- stability: Stability,
+
+ _stability_phantom: std::marker::PhantomData<STABILITY>,
}
-impl ParcelableHolder {
+impl<STABILITY: StabilityType> ParcelableHolder<STABILITY> {
/// Construct a new `ParcelableHolder` with the given stability.
- pub fn new(stability: Stability) -> Self {
- Self { data: Mutex::new(ParcelableHolderData::Empty), stability }
+ pub fn new() -> Self {
+ Self {
+ data: Mutex::new(ParcelableHolderData::Empty),
+ _stability_phantom: Default::default(),
+ }
}
/// Reset the contents of this `ParcelableHolder`.
@@ -91,7 +96,7 @@
where
T: Any + Parcelable + ParcelableMetadata + std::fmt::Debug + Send + Sync,
{
- if self.stability > p.get_stability() {
+ if STABILITY::VALUE > p.get_stability() {
return Err(StatusCode::BAD_VALUE);
}
@@ -157,30 +162,36 @@
/// Return the stability value of this object.
pub fn get_stability(&self) -> Stability {
- self.stability
+ STABILITY::VALUE
}
}
-impl Clone for ParcelableHolder {
- fn clone(&self) -> ParcelableHolder {
+impl<STABILITY: StabilityType> Default for ParcelableHolder<STABILITY> {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<STABILITY: StabilityType> Clone for ParcelableHolder<STABILITY> {
+ fn clone(&self) -> Self {
ParcelableHolder {
data: Mutex::new(self.data.lock().unwrap().clone()),
- stability: self.stability,
+ _stability_phantom: Default::default(),
}
}
}
-impl Serialize for ParcelableHolder {
+impl<STABILITY: StabilityType> Serialize for ParcelableHolder<STABILITY> {
fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
self.write_to_parcel(parcel)
}
}
-impl Deserialize for ParcelableHolder {
+impl<STABILITY: StabilityType> Deserialize for ParcelableHolder<STABILITY> {
type UninitType = Self;
fn uninit() -> Self::UninitType {
- Self::new(Default::default())
+ Self::new()
}
fn from_init(value: Self) -> Self::UninitType {
value
@@ -191,16 +202,16 @@
if status == NULL_PARCELABLE_FLAG {
Err(StatusCode::UNEXPECTED_NULL)
} else {
- let mut parcelable = ParcelableHolder::new(Default::default());
+ let mut parcelable = Self::new();
parcelable.read_from_parcel(parcel)?;
Ok(parcelable)
}
}
}
-impl Parcelable for ParcelableHolder {
+impl<STABILITY: StabilityType> Parcelable for ParcelableHolder<STABILITY> {
fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<(), StatusCode> {
- parcel.write(&self.stability)?;
+ parcel.write(&STABILITY::VALUE)?;
let mut data = self.data.lock().unwrap();
match *data {
@@ -236,7 +247,7 @@
}
fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<(), StatusCode> {
- if self.stability != parcel.read()? {
+ if self.get_stability() != parcel.read()? {
return Err(StatusCode::BAD_VALUE);
}
diff --git a/libs/binder/rust/src/system_only.rs b/libs/binder/rust/src/system_only.rs
new file mode 100644
index 0000000..08582ab
--- /dev/null
+++ b/libs/binder/rust/src/system_only.rs
@@ -0,0 +1,213 @@
+/*
+ * 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 crate::binder::AsNative;
+use crate::error::{status_result, Result};
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::ffi::{c_void, CStr, CString};
+use std::os::raw::c_char;
+
+use libc::sockaddr;
+use nix::sys::socket::{SockaddrLike, UnixAddr, VsockAddr};
+use std::sync::Arc;
+use std::{fmt, ptr};
+
+/// Rust wrapper around ABinderRpc_Accessor objects for RPC binder service management.
+///
+/// Dropping the `Accessor` will drop the underlying object and the binder it owns.
+pub struct Accessor {
+ accessor: *mut sys::ABinderRpc_Accessor,
+}
+
+impl fmt::Debug for Accessor {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "ABinderRpc_Accessor({:p})", self.accessor)
+ }
+}
+
+/// Socket connection info required for libbinder to connect to a service.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ConnectionInfo {
+ /// For vsock connection
+ Vsock(VsockAddr),
+ /// For unix domain socket connection
+ Unix(UnixAddr),
+}
+
+/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
+/// `Sync` and `Send`. As
+/// `ABinderRpc_Accessor` is threadsafe, this structure is too.
+/// The Fn owned the Accessor has `Sync` and `Send` properties
+unsafe impl Send for Accessor {}
+
+/// Safety: A `Accessor` is a wrapper around `ABinderRpc_Accessor` which is
+/// `Sync` and `Send`. As `ABinderRpc_Accessor` is threadsafe, this structure is too.
+/// The Fn owned the Accessor has `Sync` and `Send` properties
+unsafe impl Sync for Accessor {}
+
+impl Accessor {
+ /// Create a new accessor that will call the given callback when its
+ /// connection info is required.
+ /// The callback object and all objects it captures are owned by the Accessor
+ /// and will be deleted some time after the Accessor is Dropped. If the callback
+ /// is being called when the Accessor is Dropped, the callback will not be deleted
+ /// immediately.
+ pub fn new<F>(instance: &str, callback: F) -> Accessor
+ where
+ F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
+ {
+ let callback: *mut c_void = Arc::into_raw(Arc::new(callback)) as *mut c_void;
+ let inst = CString::new(instance).unwrap();
+
+ // Safety: The function pointer is a valid connection_info callback.
+ // This call returns an owned `ABinderRpc_Accessor` pointer which
+ // must be destroyed via `ABinderRpc_Accessor_delete` when no longer
+ // needed.
+ // When the underlying ABinderRpc_Accessor is deleted, it will call
+ // the cookie_decr_refcount callback to release its strong ref.
+ let accessor = unsafe {
+ sys::ABinderRpc_Accessor_new(
+ inst.as_ptr(),
+ Some(Self::connection_info::<F>),
+ callback,
+ Some(Self::cookie_decr_refcount::<F>),
+ )
+ };
+
+ Accessor { accessor }
+ }
+
+ /// Get the underlying binder for this Accessor for when it needs to be either
+ /// registered with service manager or sent to another process.
+ pub fn as_binder(&self) -> Option<SpIBinder> {
+ // Safety: `ABinderRpc_Accessor_asBinder` returns either a null pointer or a
+ // valid pointer to an owned `AIBinder`. Either of these values is safe to
+ // pass to `SpIBinder::from_raw`.
+ unsafe { SpIBinder::from_raw(sys::ABinderRpc_Accessor_asBinder(self.accessor)) }
+ }
+
+ /// Callback invoked from C++ when the connection info is needed.
+ ///
+ /// # Safety
+ ///
+ /// The `instance` parameter must be a non-null pointer to a valid C string for
+ /// CStr::from_ptr. The memory must contain a valid null terminator at the end of
+ /// the string within isize::MAX from the pointer. The memory must not be mutated for
+ /// the duration of this function call and must be valid for reads from the pointer
+ /// to the null terminator.
+ /// The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the caller must hold a ref-count to it.
+ unsafe extern "C" fn connection_info<F>(
+ instance: *const c_char,
+ cookie: *mut c_void,
+ ) -> *mut binder_ndk_sys::ABinderRpc_ConnectionInfo
+ where
+ F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
+ {
+ if cookie.is_null() || instance.is_null() {
+ log::error!("Cookie({cookie:p}) or instance({instance:p}) is null!");
+ return ptr::null_mut();
+ }
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ let callback = unsafe { (cookie as *const F).as_ref().unwrap() };
+
+ // Safety: The caller in libbinder_ndk will have already verified this is a valid
+ // C string
+ let inst = unsafe {
+ match CStr::from_ptr(instance).to_str() {
+ Ok(s) => s,
+ Err(err) => {
+ log::error!("Failed to get a valid C string! {err:?}");
+ return ptr::null_mut();
+ }
+ }
+ };
+
+ let connection = match callback(inst) {
+ Some(con) => con,
+ None => {
+ return ptr::null_mut();
+ }
+ };
+
+ match connection {
+ ConnectionInfo::Vsock(addr) => {
+ // Safety: The sockaddr is being copied in the NDK API
+ unsafe { sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr(), addr.len()) }
+ }
+ ConnectionInfo::Unix(addr) => {
+ // Safety: The sockaddr is being copied in the NDK API
+ // The cast is from sockaddr_un* to sockaddr*.
+ unsafe {
+ sys::ABinderRpc_ConnectionInfo_new(addr.as_ptr() as *const sockaddr, addr.len())
+ }
+ }
+ }
+ }
+
+ /// Callback that decrements the ref-count.
+ /// This is invoked from C++ when a binder is unlinked.
+ ///
+ /// # Safety
+ ///
+ /// The `cookie` parameter must be the cookie for an `Arc<F>` and
+ /// the owner must give up a ref-count to it.
+ unsafe extern "C" fn cookie_decr_refcount<F>(cookie: *mut c_void)
+ where
+ F: Fn(&str) -> Option<ConnectionInfo> + Send + Sync + 'static,
+ {
+ // Safety: The caller promises that `cookie` is for an Arc<F>.
+ unsafe { Arc::decrement_strong_count(cookie as *const F) };
+ }
+}
+
+impl Drop for Accessor {
+ fn drop(&mut self) {
+ // Safety: `self.accessor` is always a valid, owned
+ // `ABinderRpc_Accessor` pointer returned by
+ // `ABinderRpc_Accessor_new` when `self` was created. This delete
+ // method can only be called once when `self` is dropped.
+ unsafe {
+ sys::ABinderRpc_Accessor_delete(self.accessor);
+ }
+ }
+}
+
+/// Register a new service with the default service manager.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
+pub fn delegate_accessor(name: &str, mut binder: SpIBinder) -> Result<SpIBinder> {
+ let instance = CString::new(name).unwrap();
+ let mut delegator = ptr::null_mut();
+ let status =
+ // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both pointers.
+ // `AServiceManager_addService` creates a new strong reference and copies
+ // the string, so both pointers need only be valid until the call returns.
+ unsafe { sys::ABinderRpc_Accessor_delegateAccessor(instance.as_ptr(),
+ binder.as_native_mut(), &mut delegator) };
+
+ status_result(status)?;
+
+ // Safety: `delegator` is either null or a valid, owned pointer at this
+ // point, so can be safely passed to `SpIBinder::from_raw`.
+ Ok(unsafe { SpIBinder::from_raw(delegator).expect("Expected valid binder at this point") })
+}
diff --git a/libs/binder/rust/sys/BinderBindings.hpp b/libs/binder/rust/sys/BinderBindings.hpp
index 65fa2ca..bd666fe 100644
--- a/libs/binder/rust/sys/BinderBindings.hpp
+++ b/libs/binder/rust/sys/BinderBindings.hpp
@@ -20,6 +20,7 @@
#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>
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 5359832..489fa0a 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -384,8 +384,8 @@
use std::time::Duration;
use binder::{
- BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder, StatusCode,
- Strong,
+ Accessor, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, SpIBinder,
+ StatusCode, Strong,
};
// Import from impl API for testing only, should not be necessary as long as
// you are using AIDL.
@@ -908,6 +908,80 @@
assert_eq!(service.test().unwrap(), service_name);
}
+ struct ToBeDeleted {
+ deleted: Arc<AtomicBool>,
+ }
+
+ impl Drop for ToBeDeleted {
+ fn drop(&mut self) {
+ assert!(!self.deleted.load(Ordering::Relaxed));
+ self.deleted.store(true, Ordering::Relaxed);
+ }
+ }
+
+ #[test]
+ fn test_accessor_callback_destruction() {
+ let deleted: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
+ {
+ let accessor: Accessor;
+ {
+ let helper = ToBeDeleted { deleted: deleted.clone() };
+ let get_connection_info = move |_instance: &str| {
+ // Capture this object so we can see it get destructed
+ // after the parent scope
+ let _ = &helper;
+ None
+ };
+ accessor = Accessor::new("foo.service", get_connection_info);
+ }
+
+ match accessor.as_binder() {
+ Some(_) => {
+ assert!(!deleted.load(Ordering::Relaxed));
+ }
+ None => panic!("failed to get that accessor binder"),
+ }
+ }
+ assert!(deleted.load(Ordering::Relaxed));
+ }
+
+ #[test]
+ fn test_accessor_delegator_new_each_time() {
+ let get_connection_info = move |_instance: &str| None;
+ let accessor = Accessor::new("foo.service", get_connection_info);
+ let delegator_binder =
+ binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+ let delegator_binder2 =
+ binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+
+ // The delegate_accessor creates new delegators each time
+ assert!(delegator_binder != delegator_binder2);
+ }
+
+ #[test]
+ fn test_accessor_delegate_the_delegator() {
+ let get_connection_info = move |_instance: &str| None;
+ let accessor = Accessor::new("foo.service", get_connection_info);
+ let delegator_binder =
+ binder::delegate_accessor("foo.service", accessor.as_binder().unwrap());
+ let delegator_binder2 =
+ binder::delegate_accessor("foo.service", delegator_binder.clone().unwrap());
+
+ assert!(delegator_binder.clone() == delegator_binder);
+ // The delegate_accessor creates new delegators each time. Even when they are delegators
+ // of delegators.
+ assert!(delegator_binder != delegator_binder2);
+ }
+
+ #[test]
+ fn test_accessor_delegator_wrong_name() {
+ let get_connection_info = move |_instance: &str| None;
+ let accessor = Accessor::new("foo.service", get_connection_info);
+ let delegator_binder =
+ binder::delegate_accessor("NOT.foo.service", accessor.as_binder().unwrap());
+ assert_eq!(delegator_binder, Err(StatusCode::NAME_NOT_FOUND));
+ }
+
#[tokio::test]
async fn reassociate_rust_binder_async() {
let service_name = "testing_service";
diff --git a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
index ce0f742..ee20a22 100644
--- a/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
+++ b/libs/binder/rust/tests/parcel_fuzzer/parcel_fuzzer.rs
@@ -21,7 +21,8 @@
use crate::read_utils::READ_FUNCS;
use binder::binder_impl::{
- Binder, BorrowedParcel, IBinderInternal, Parcel, Stability, TransactionCode,
+ Binder, BorrowedParcel, IBinderInternal, LocalStabilityType, Parcel, TransactionCode,
+ VintfStabilityType,
};
use binder::{
declare_binder_interface, BinderFeatures, Interface, Parcelable, ParcelableHolder, SpIBinder,
@@ -121,13 +122,15 @@
}
ReadOperation::ReadParcelableHolder { is_vintf } => {
- let stability = if is_vintf { Stability::Vintf } else { Stability::Local };
- let mut holder: ParcelableHolder = ParcelableHolder::new(stability);
- match holder.read_from_parcel(parcel.borrowed_ref()) {
- Ok(result) => result,
- Err(err) => {
- println!("error occurred while reading from parcel: {:?}", err)
- }
+ let result = if is_vintf {
+ ParcelableHolder::<VintfStabilityType>::new()
+ .read_from_parcel(parcel.borrowed_ref())
+ } else {
+ ParcelableHolder::<LocalStabilityType>::new()
+ .read_from_parcel(parcel.borrowed_ref())
+ };
+ if let Err(e) = result {
+ println!("error occurred while reading from parcel: {e:?}")
}
}
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 8b0dda3..28a3f65 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -731,6 +731,9 @@
"liblog",
"libutils",
],
+ static_libs: [
+ "libgmock",
+ ],
test_suites: [
"general-tests",
"vts",
diff --git a/libs/binder/tests/binderCacheUnitTest.cpp b/libs/binder/tests/binderCacheUnitTest.cpp
index 482d197..c5ad793 100644
--- a/libs/binder/tests/binderCacheUnitTest.cpp
+++ b/libs/binder/tests/binderCacheUnitTest.cpp
@@ -149,7 +149,16 @@
EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder2));
// Confirm that new service is returned instead of old.
- sp<IBinder> result2 = mServiceManager->checkService(kCachedServiceName);
+ int retry_count = 20;
+ sp<IBinder> result2;
+ do {
+ std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ if (retry_count-- == 0) {
+ break;
+ }
+ result2 = mServiceManager->checkService(kCachedServiceName);
+ } while (result2 != binder2);
+
ASSERT_EQ(binder2, result2);
}
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index bcab6de..970852c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -46,6 +46,7 @@
#include <linux/sched.h>
#include <sys/epoll.h>
+#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -110,6 +111,8 @@
BINDER_LIB_TEST_LINK_DEATH_TRANSACTION,
BINDER_LIB_TEST_WRITE_FILE_TRANSACTION,
BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION,
+ BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION,
+ BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION,
BINDER_LIB_TEST_EXIT_TRANSACTION,
BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
BINDER_LIB_TEST_GET_PTR_SIZE_TRANSACTION,
@@ -536,6 +539,30 @@
};
};
+ssize_t countFds() {
+ return std::distance(std::filesystem::directory_iterator("/proc/self/fd"),
+ std::filesystem::directory_iterator{});
+}
+
+struct FdLeakDetector {
+ int startCount;
+
+ FdLeakDetector() {
+ // This log statement is load bearing. We have to log something before
+ // counting FDs to make sure the logging system is initialized, otherwise
+ // the sockets it opens will look like a leak.
+ ALOGW("FdLeakDetector counting FDs.");
+ startCount = countFds();
+ }
+ ~FdLeakDetector() {
+ int endCount = countFds();
+ if (startCount != endCount) {
+ ADD_FAILURE() << "fd count changed (" << startCount << " -> " << endCount
+ << ") fd leak?";
+ }
+ }
+};
+
TEST_F(BinderLibTest, CannotUseBinderAfterFork) {
// EXPECT_DEATH works by forking the process
EXPECT_DEATH({ ProcessState::self(); }, "libbinder ProcessState can not be used after fork");
@@ -1175,6 +1202,100 @@
EXPECT_EQ(0, read(read_end.get(), readbuf.data(), datasize));
}
+TEST_F(BinderLibTest, RecvOwnedFileDescriptors) {
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION, data,
+ &reply));
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvOwnedFileDescriptorsAndWriteInt) {
+ GTEST_SKIP() << "triggers fdsan false positive: b/370824489";
+
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION, data,
+ &reply));
+ reply.setDataPosition(reply.dataSize());
+ reply.writeInt32(0);
+ reply.setDataPosition(0);
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvOwnedFileDescriptorsAndTruncate) {
+ GTEST_SKIP() << "triggers fdsan false positive: b/370824489";
+
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION, data,
+ &reply));
+ reply.setDataSize(reply.dataSize() - sizeof(flat_binder_object));
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(BAD_TYPE, reply.readUniqueFileDescriptor(&b));
+}
+
+TEST_F(BinderLibTest, RecvUnownedFileDescriptors) {
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION, data,
+ &reply));
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvUnownedFileDescriptorsAndWriteInt) {
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION, data,
+ &reply));
+ reply.setDataPosition(reply.dataSize());
+ reply.writeInt32(0);
+ reply.setDataPosition(0);
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&b));
+}
+
+// Used to trigger fdsan error (b/239222407).
+TEST_F(BinderLibTest, RecvUnownedFileDescriptorsAndTruncate) {
+ FdLeakDetector fd_leak_detector;
+
+ Parcel data;
+ Parcel reply;
+ EXPECT_EQ(NO_ERROR,
+ m_server->transact(BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION, data,
+ &reply));
+ reply.setDataSize(reply.dataSize() - sizeof(flat_binder_object));
+ unique_fd a, b;
+ EXPECT_EQ(OK, reply.readUniqueFileDescriptor(&a));
+ EXPECT_EQ(BAD_TYPE, reply.readUniqueFileDescriptor(&b));
+}
+
TEST_F(BinderLibTest, PromoteLocal) {
sp<IBinder> strong = new BBinder();
wp<IBinder> weak = strong;
@@ -2224,6 +2345,40 @@
if (ret != size) return UNKNOWN_ERROR;
return NO_ERROR;
}
+ case BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_OWNED_TRANSACTION: {
+ unique_fd fd1(memfd_create("memfd1", MFD_CLOEXEC));
+ if (!fd1.ok()) {
+ PLOGE("memfd_create failed");
+ return UNKNOWN_ERROR;
+ }
+ unique_fd fd2(memfd_create("memfd2", MFD_CLOEXEC));
+ if (!fd2.ok()) {
+ PLOGE("memfd_create failed");
+ return UNKNOWN_ERROR;
+ }
+ status_t ret;
+ ret = reply->writeFileDescriptor(fd1.release(), true);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ ret = reply->writeFileDescriptor(fd2.release(), true);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ return NO_ERROR;
+ }
+ case BINDER_LIB_TEST_GET_FILE_DESCRIPTORS_UNOWNED_TRANSACTION: {
+ status_t ret;
+ ret = reply->writeFileDescriptor(STDOUT_FILENO, false);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ ret = reply->writeFileDescriptor(STDERR_FILENO, false);
+ if (ret != NO_ERROR) {
+ return ret;
+ }
+ return NO_ERROR;
+ }
case BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION:
alarm(10);
return NO_ERROR;
@@ -2261,7 +2416,7 @@
if (ret != NO_ERROR) {
return ret;
}
- auto event = frozenStateChangeCallback->events.popWithTimeout(10ms);
+ auto event = frozenStateChangeCallback->events.popWithTimeout(1000ms);
if (!event.has_value()) {
return NOT_ENOUGH_DATA;
}
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 11150bc..506fc71 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -75,6 +75,8 @@
constexpr char kTrustyIpcDevice[] = "/dev/trusty-ipc-dev0";
#endif
+constexpr char kKnownAidlService[] = "activity";
+
static std::string WaitStatusToString(int wstatus) {
if (WIFEXITED(wstatus)) {
return "exit status " + std::to_string(WEXITSTATUS(wstatus));
@@ -1544,36 +1546,52 @@
ABinderRpc_Accessor_delete(accessor);
}
-TEST_F(BinderARpcNdk, ARpcDoubleRemoveProvider) {
- ABinderRpc_AccessorProvider* provider =
- ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices,
- kARpcNumSupportedServices, nullptr, nullptr);
- ASSERT_NE(nullptr, provider);
- ABinderRpc_unregisterAccessorProvider(provider);
- EXPECT_DEATH(ABinderRpc_unregisterAccessorProvider(provider), " was already unregistered");
-}
-
TEST_F(BinderARpcNdk, ARpcNullArgs_ConnectionInfo_new) {
sockaddr_storage addr;
EXPECT_EQ(nullptr, ABinderRpc_ConnectionInfo_new(reinterpret_cast<const sockaddr*>(&addr), 0));
}
-TEST_P(BinderRpcAccessor, ARpcGetService) {
+TEST_F(BinderARpcNdk, ARpcDelegateAccessorWrongInstance) {
+ AccessorProviderData* data = new AccessorProviderData();
+ ABinderRpc_Accessor* accessor = getAccessor(kARpcInstance, data);
+ ASSERT_NE(accessor, nullptr);
+ AIBinder* localAccessorBinder = ABinderRpc_Accessor_asBinder(accessor);
+ EXPECT_NE(localAccessorBinder, nullptr);
+
+ AIBinder* delegatorBinder = nullptr;
+ binder_status_t status =
+ ABinderRpc_Accessor_delegateAccessor("bar", localAccessorBinder, &delegatorBinder);
+ EXPECT_EQ(status, NAME_NOT_FOUND);
+
+ AIBinder_decStrong(localAccessorBinder);
+ ABinderRpc_Accessor_delete(accessor);
+ delete data;
+}
+
+TEST_F(BinderARpcNdk, ARpcDelegateNonAccessor) {
+ auto service = defaultServiceManager()->checkService(String16(kKnownAidlService));
+ ASSERT_NE(nullptr, service);
+ ndk::SpAIBinder binder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(service));
+
+ AIBinder* delegatorBinder = nullptr;
+ binder_status_t status =
+ ABinderRpc_Accessor_delegateAccessor("bar", binder.get(), &delegatorBinder);
+
+ EXPECT_EQ(status, BAD_TYPE);
+}
+
+inline void getServiceTest(BinderRpcTestProcessSession& proc,
+ ABinderRpc_AccessorProvider_getAccessorCallback getAccessor) {
constexpr size_t kNumThreads = 10;
bool isDeleted = false;
- auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
- EXPECT_EQ(OK, proc.rootBinder->pingBinder());
-
AccessorProviderData* data =
new AccessorProviderData{proc.proc->sessions[0].addr, proc.proc->sessions[0].addrLen,
&isDeleted};
-
ABinderRpc_AccessorProvider* provider =
ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices,
kARpcNumSupportedServices, data,
accessorProviderDataOnDelete);
-
EXPECT_NE(provider, nullptr);
EXPECT_FALSE(isDeleted);
@@ -1589,6 +1607,45 @@
waitForExtraSessionCleanup(proc);
}
+TEST_P(BinderRpcAccessor, ARpcGetService) {
+ constexpr size_t kNumThreads = 10;
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ getServiceTest(proc, getAccessor);
+}
+
+// Create accessors and wrap each of the accessors in a delegator
+ABinderRpc_Accessor* getDelegatedAccessor(const char* instance, void* cookie) {
+ ABinderRpc_Accessor* accessor = getAccessor(instance, cookie);
+ AIBinder* accessorBinder = ABinderRpc_Accessor_asBinder(accessor);
+ // Once we have a handle to the AIBinder which holds a reference to the
+ // underlying accessor IBinder, we can get rid of the ABinderRpc_Accessor
+ ABinderRpc_Accessor_delete(accessor);
+
+ AIBinder* delegatorBinder = nullptr;
+ binder_status_t status =
+ ABinderRpc_Accessor_delegateAccessor(instance, accessorBinder, &delegatorBinder);
+ // No longer need this AIBinder. The delegator has a reference to the
+ // underlying IBinder on success, and on failure we are done here.
+ AIBinder_decStrong(accessorBinder);
+ if (status != OK || delegatorBinder == nullptr) {
+ ALOGE("Unexpected behavior. Status: %s, delegator ptr: %p", statusToString(status).c_str(),
+ delegatorBinder);
+ return nullptr;
+ }
+
+ return ABinderRpc_Accessor_fromBinder(instance, delegatorBinder);
+}
+
+TEST_P(BinderRpcAccessor, ARpcGetServiceWithDelegator) {
+ constexpr size_t kNumThreads = 10;
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ getServiceTest(proc, getDelegatedAccessor);
+}
+
#endif // BINDER_WITH_KERNEL_IPC
#ifdef BINDER_RPC_TO_TRUSTY_TEST
@@ -1854,7 +1911,7 @@
ASSERT_NE(nullptr, sm);
// Any Java service with non-empty getInterfaceDescriptor() would do.
// Let's pick activity.
- auto binder = sm->checkService(String16("activity"));
+ auto binder = sm->checkService(String16(kKnownAidlService));
ASSERT_NE(nullptr, binder);
auto descriptor = binder->getInterfaceDescriptor();
ASSERT_GE(descriptor.size(), 0u);
diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp
index 0aa678d..849dc7c 100644
--- a/libs/binder/tests/binderSafeInterfaceTest.cpp
+++ b/libs/binder/tests/binderSafeInterfaceTest.cpp
@@ -40,6 +40,8 @@
#include <sys/eventfd.h>
#include <sys/prctl.h>
+#include <gmock/gmock.h>
+
using namespace std::chrono_literals; // NOLINT - google-build-using-namespace
using android::binder::unique_fd;
@@ -222,6 +224,7 @@
SetDeathToken = IBinder::FIRST_CALL_TRANSACTION,
ReturnsNoMemory,
LogicalNot,
+ LogicalNotVector,
ModifyEnum,
IncrementFlattenable,
IncrementLightFlattenable,
@@ -249,6 +252,7 @@
// These are ordered according to their corresponding methods in SafeInterface::ParcelHandler
virtual status_t logicalNot(bool a, bool* notA) const = 0;
+ virtual status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const = 0;
virtual status_t modifyEnum(TestEnum a, TestEnum* b) const = 0;
virtual status_t increment(const TestFlattenable& a, TestFlattenable* aPlusOne) const = 0;
virtual status_t increment(const TestLightFlattenable& a,
@@ -288,7 +292,14 @@
}
status_t logicalNot(bool a, bool* notA) const override {
ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
- return callRemote<decltype(&ISafeInterfaceTest::logicalNot)>(Tag::LogicalNot, a, notA);
+ using Signature = status_t (ISafeInterfaceTest::*)(bool, bool*) const;
+ return callRemote<Signature>(Tag::LogicalNot, a, notA);
+ }
+ status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<bool>&,
+ std::vector<bool>*) const;
+ return callRemote<Signature>(Tag::LogicalNotVector, a, notA);
}
status_t modifyEnum(TestEnum a, TestEnum* b) const override {
ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
@@ -406,6 +417,14 @@
*notA = !a;
return NO_ERROR;
}
+ status_t logicalNot(const std::vector<bool>& a, std::vector<bool>* notA) const override {
+ ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
+ notA->clear();
+ for (bool value : a) {
+ notA->push_back(!value);
+ }
+ return NO_ERROR;
+ }
status_t modifyEnum(TestEnum a, TestEnum* b) const override {
ALOG(LOG_INFO, getLogTag(), "%s", __PRETTY_FUNCTION__);
*b = (a == TestEnum::INITIAL) ? TestEnum::FINAL : TestEnum::INVALID;
@@ -513,7 +532,13 @@
return callLocal(data, reply, &ISafeInterfaceTest::returnsNoMemory);
}
case ISafeInterfaceTest::Tag::LogicalNot: {
- return callLocal(data, reply, &ISafeInterfaceTest::logicalNot);
+ using Signature = status_t (ISafeInterfaceTest::*)(bool a, bool* notA) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::logicalNot);
+ }
+ case ISafeInterfaceTest::Tag::LogicalNotVector: {
+ using Signature = status_t (ISafeInterfaceTest::*)(const std::vector<bool>& a,
+ std::vector<bool>* notA) const;
+ return callLocal<Signature>(data, reply, &ISafeInterfaceTest::logicalNot);
}
case ISafeInterfaceTest::Tag::ModifyEnum: {
return callLocal(data, reply, &ISafeInterfaceTest::modifyEnum);
@@ -639,6 +664,15 @@
ASSERT_EQ(!b, notB);
}
+TEST_F(SafeInterfaceTest, TestLogicalNotVector) {
+ const std::vector<bool> a = {true, false, true};
+ std::vector<bool> notA;
+ status_t result = mSafeInterfaceTest->logicalNot(a, ¬A);
+ ASSERT_EQ(NO_ERROR, result);
+ std::vector<bool> expected = {false, true, false};
+ ASSERT_THAT(notA, testing::ContainerEq(expected));
+}
+
TEST_F(SafeInterfaceTest, TestModifyEnum) {
const TestEnum a = TestEnum::INITIAL;
TestEnum b = TestEnum::INVALID;
diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp
index fbab8f0..cac054e 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -39,6 +39,7 @@
"smoreland@google.com",
"waghpawan@google.com",
],
+ triage_assignee: "smoreland@google.com",
use_for_presubmit: true,
},
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index e378b86..07f0143 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -25,6 +25,8 @@
#include <binder/ParcelableHolder.h>
#include <binder/PersistableBundle.h>
#include <binder/Status.h>
+#include <fuzzbinder/random_binder.h>
+#include <fuzzbinder/random_fd.h>
#include <utils/Flattenable.h>
#include "../../Utils.h"
@@ -115,14 +117,6 @@
p.setDataPosition(pos);
FUZZ_LOG() << "setDataPosition done";
},
- [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
- size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
- std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
- FUZZ_LOG() << "about to setData: " <<(bytes.data() ? HexString(bytes.data(), bytes.size()) : "null");
- // TODO: allow all read and write operations
- (*const_cast<::android::Parcel*>(&p)).setData(bytes.data(), bytes.size());
- FUZZ_LOG() << "setData done";
- },
PARCEL_READ_NO_STATUS(size_t, allowFds),
PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors),
PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders),
@@ -404,5 +398,113 @@
FUZZ_LOG() << " toString() result: " << toString;
},
};
+
+std::vector<ParcelWrite<::android::Parcel>> BINDER_PARCEL_WRITE_FUNCTIONS {
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call setDataSize";
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+ p.setDataSize(len);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call setDataCapacity";
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+ p.setDataCapacity(len);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call setData";
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
+ p.setData(bytes.data(), bytes.size());
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+ FUZZ_LOG() << "about to call appendFrom";
+
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ ::android::Parcel p2;
+ fillRandomParcel(&p2, FuzzedDataProvider(bytes.data(), bytes.size()), options);
+
+ int32_t start = provider.ConsumeIntegral<int32_t>();
+ int32_t len = provider.ConsumeIntegral<int32_t>();
+ p.appendFrom(&p2, start, len);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call pushAllowFds";
+ bool val = provider.ConsumeBool();
+ p.pushAllowFds(val);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call restoreAllowFds";
+ bool val = provider.ConsumeBool();
+ p.restoreAllowFds(val);
+ },
+ // markForBinder - covered by fillRandomParcel, aborts if called multiple times
+ // markForRpc - covered by fillRandomParcel, aborts if called multiple times
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call writeInterfaceToken";
+ std::string interface = provider.ConsumeRandomLengthString();
+ p.writeInterfaceToken(android::String16(interface.c_str()));
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call setEnforceNoDataAvail";
+ p.setEnforceNoDataAvail(provider.ConsumeBool());
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call setServiceFuzzing";
+ p.setServiceFuzzing();
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call freeData";
+ p.freeData();
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call write";
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 256);
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
+ p.write(bytes.data(), bytes.size());
+ },
+ // write* - write functions all implemented by calling 'write' itself.
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+ FUZZ_LOG() << "about to call writeStrongBinder";
+
+ // TODO: this logic is somewhat duplicated with random parcel
+ android::sp<android::IBinder> binder;
+ if (provider.ConsumeBool() && options->extraBinders.size() > 0) {
+ binder = options->extraBinders.at(
+ provider.ConsumeIntegralInRange<size_t>(0, options->extraBinders.size() - 1));
+ } else {
+ binder = android::getRandomBinder(&provider);
+ options->extraBinders.push_back(binder);
+ }
+
+ p.writeStrongBinder(binder);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call writeFileDescriptor (no ownership)";
+ p.writeFileDescriptor(STDERR_FILENO, false /* takeOwnership */);
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+ FUZZ_LOG() << "about to call writeFileDescriptor (take ownership)";
+ std::vector<unique_fd> fds = android::getRandomFds(&provider);
+ if (fds.size() == 0) return;
+
+ p.writeDupFileDescriptor(fds.at(0).get());
+ options->extraFds.insert(options->extraFds.end(),
+ std::make_move_iterator(fds.begin() + 1),
+ std::make_move_iterator(fds.end()));
+ },
+ // TODO: writeBlob
+ // TODO: writeDupImmutableBlobFileDescriptor
+ // TODO: writeObject (or make the API private more likely)
+ [] (::android::Parcel& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call writeNoException";
+ p.writeNoException();
+ },
+ [] (::android::Parcel& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call replaceCallingWorkSourceUid";
+ uid_t uid = provider.ConsumeIntegral<uid_t>();
+ p.replaceCallingWorkSourceUid(uid);
+ },
+};
+
// clang-format on
#pragma clang diagnostic pop
diff --git a/libs/binder/tests/parcel_fuzzer/binder.h b/libs/binder/tests/parcel_fuzzer/binder.h
index 0c51d68..b0ac140 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.h
+++ b/libs/binder/tests/parcel_fuzzer/binder.h
@@ -21,3 +21,4 @@
#include "parcel_fuzzer.h"
extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS;
+extern std::vector<ParcelWrite<::android::Parcel>> BINDER_PARCEL_WRITE_FUNCTIONS;
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index 3a1471e..3f8d71d 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -20,8 +20,11 @@
#include "aidl/parcelables/GenericDataParcelable.h"
#include "aidl/parcelables/SingleDataParcelable.h"
+#include <android/binder_libbinder.h>
#include <android/binder_parcel_utils.h>
#include <android/binder_parcelable_utils.h>
+#include <fuzzbinder/random_binder.h>
+#include <fuzzbinder/random_fd.h>
#include "util.h"
@@ -49,7 +52,8 @@
return STATUS_UNKNOWN_TRANSACTION;
}
-static AIBinder_Class* g_class = ::ndk::ICInterface::defineClass("ISomeInterface", onTransact);
+static AIBinder_Class* g_class =
+ ::ndk::ICInterface::defineClass("ISomeInterface", onTransact, nullptr, 0);
class BpSomeInterface : public ::ndk::BpCInterface<ISomeInterface> {
public:
@@ -210,16 +214,51 @@
binder_status_t status = AParcel_marshal(p.aParcel(), buffer, start, len);
FUZZ_LOG() << "status: " << status;
},
- [](const NdkParcelAdapter& /*p*/, FuzzedDataProvider& provider) {
- FUZZ_LOG() << "about to unmarshal AParcel";
+};
+std::vector<ParcelWrite<NdkParcelAdapter>> BINDER_NDK_PARCEL_WRITE_FUNCTIONS{
+ [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+ FUZZ_LOG() << "about to call AParcel_writeStrongBinder";
+
+ // TODO: this logic is somewhat duplicated with random parcel
+ android::sp<android::IBinder> binder;
+ if (provider.ConsumeBool() && options->extraBinders.size() > 0) {
+ binder = options->extraBinders.at(
+ provider.ConsumeIntegralInRange<size_t>(0, options->extraBinders.size() - 1));
+ } else {
+ binder = android::getRandomBinder(&provider);
+ options->extraBinders.push_back(binder);
+ }
+
+ ndk::SpAIBinder abinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(binder));
+ AParcel_writeStrongBinder(p.aParcel(), abinder.get());
+ },
+ [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* options) {
+ FUZZ_LOG() << "about to call AParcel_writeParcelFileDescriptor";
+
+ auto fds = android::getRandomFds(&provider);
+ if (fds.size() == 0) return;
+
+ AParcel_writeParcelFileDescriptor(p.aParcel(), fds.at(0).get());
+ options->extraFds.insert(options->extraFds.end(),
+ std::make_move_iterator(fds.begin() + 1),
+ std::make_move_iterator(fds.end()));
+ },
+ // all possible data writes can be done as a series of 4-byte reads
+ [] (NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call AParcel_writeInt32";
+ int32_t val = provider.ConsumeIntegral<int32_t>();
+ AParcel_writeInt32(p.aParcel(), val);
+ },
+ [] (NdkParcelAdapter& p, FuzzedDataProvider& /* provider */, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call AParcel_reset";
+ AParcel_reset(p.aParcel());
+ },
+ [](NdkParcelAdapter& p, FuzzedDataProvider& provider, android::RandomParcelOptions* /*options*/) {
+ FUZZ_LOG() << "about to call AParcel_unmarshal";
size_t len = provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes());
- std::vector<uint8_t> parcelData = provider.ConsumeBytes<uint8_t>(len);
- const uint8_t* buffer = parcelData.data();
- const size_t bufferLen = parcelData.size();
- NdkParcelAdapter adapter;
- binder_status_t status = AParcel_unmarshal(adapter.aParcel(), buffer, bufferLen);
+ std::vector<uint8_t> data = provider.ConsumeBytes<uint8_t>(len);
+ binder_status_t status = AParcel_unmarshal(p.aParcel(), data.data(), data.size());
FUZZ_LOG() << "status: " << status;
},
-
};
// clang-format on
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.h b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
index d19f25b..0c8b725 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.h
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.h
@@ -50,3 +50,4 @@
};
extern std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS;
+extern std::vector<ParcelWrite<NdkParcelAdapter>> BINDER_NDK_PARCEL_WRITE_FUNCTIONS;
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index a57d07f..192f9d5 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -80,6 +80,7 @@
(void)binder->transact(code, data, &reply, flag);
}
+// start with a Parcel full of data (e.g. like you get from another process)
template <typename P>
void doReadFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
FuzzedDataProvider&& provider) {
@@ -95,10 +96,10 @@
RandomParcelOptions options;
P p;
- fillRandomParcel(&p, std::move(provider), &options);
+ fillRandomParcel(&p, std::move(provider), &options); // consumes provider
// since we are only using a byte to index
- CHECK(reads.size() <= 255) << reads.size();
+ CHECK_LE(reads.size(), 255u) << reads.size();
FUZZ_LOG() << "backend: " << backend;
FUZZ_LOG() << "input: " << HexString(p.data(), p.dataSize());
@@ -115,26 +116,29 @@
}
}
-// Append two random parcels.
template <typename P>
-void doAppendFuzz(const char* backend, FuzzedDataProvider&& provider) {
- int32_t start = provider.ConsumeIntegral<int32_t>();
- int32_t len = provider.ConsumeIntegral<int32_t>();
-
- std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(
- provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes()));
-
- // same options so that FDs and binders could be shared in both Parcels
+void doReadWriteFuzz(const char* backend, const std::vector<ParcelRead<P>>& reads,
+ const std::vector<ParcelWrite<P>>& writes, FuzzedDataProvider&& provider) {
RandomParcelOptions options;
+ P p;
- P p0, p1;
- fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options);
- fillRandomParcel(&p1, std::move(provider), &options);
+ // since we are only using a byte to index
+ CHECK_LE(reads.size() + writes.size(), 255u) << reads.size();
FUZZ_LOG() << "backend: " << backend;
- FUZZ_LOG() << "start: " << start << " len: " << len;
- p0.appendFrom(&p1, start, len);
+ while (provider.remaining_bytes() > 0) {
+ uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, reads.size() + writes.size() - 1);
+
+ FUZZ_LOG() << "Instruction " << idx << " avail: " << p.dataAvail()
+ << " pos: " << p.dataPosition() << " cap: " << p.dataCapacity();
+
+ if (idx < reads.size()) {
+ reads.at(idx)(p, provider);
+ } else {
+ writes.at(idx - reads.size())(p, provider, &options);
+ }
+ }
}
void* NothingClass_onCreate(void* args) {
@@ -156,7 +160,7 @@
FuzzedDataProvider provider = FuzzedDataProvider(data, size);
- const std::function<void(FuzzedDataProvider &&)> fuzzBackend[] = {
+ const std::function<void(FuzzedDataProvider&&)> fuzzBackend[] = {
[](FuzzedDataProvider&& provider) {
doTransactFuzz<
::android::hardware::Parcel>("hwbinder",
@@ -187,10 +191,14 @@
std::move(provider));
},
[](FuzzedDataProvider&& provider) {
- doAppendFuzz<::android::Parcel>("binder", std::move(provider));
+ doReadWriteFuzz<::android::Parcel>("binder", BINDER_PARCEL_READ_FUNCTIONS,
+ BINDER_PARCEL_WRITE_FUNCTIONS,
+ std::move(provider));
},
[](FuzzedDataProvider&& provider) {
- doAppendFuzz<NdkParcelAdapter>("binder_ndk", std::move(provider));
+ doReadWriteFuzz<NdkParcelAdapter>("binder_ndk", BINDER_NDK_PARCEL_READ_FUNCTIONS,
+ BINDER_NDK_PARCEL_WRITE_FUNCTIONS,
+ std::move(provider));
},
};
diff --git a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
index 765a93e..dbd0cae 100644
--- a/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
+++ b/libs/binder/tests/parcel_fuzzer/parcel_fuzzer.h
@@ -15,9 +15,13 @@
*/
#pragma once
+#include <fuzzbinder/random_parcel.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <functional>
template <typename P>
using ParcelRead = std::function<void(const P& p, FuzzedDataProvider& provider)>;
+template <typename P>
+using ParcelWrite = std::function<void(P& p, FuzzedDataProvider& provider,
+ android::RandomParcelOptions* options)>;
diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp
index 157ab3c..ba9e457 100644
--- a/libs/binder/trusty/OS.cpp
+++ b/libs/binder/trusty/OS.cpp
@@ -42,6 +42,10 @@
void trace_int(uint64_t, const char*, int32_t) {}
+uint64_t get_trace_enabled_tags() {
+ return 0;
+}
+
uint64_t GetThreadId() {
return 0;
}
diff --git a/libs/binder/trusty/ndk/include/android/llndk-versioning.h b/libs/binder/trusty/ndk/include/android/llndk-versioning.h
new file mode 100644
index 0000000..e955a34
--- /dev/null
+++ b/libs/binder/trusty/ndk/include/android/llndk-versioning.h
@@ -0,0 +1,19 @@
+/*
+ * 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
+
+// TODO(b/349936395): set to true for Trusty
+#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) (false)
diff --git a/libs/binder/trusty/rust/binder_rpc_test/main.rs b/libs/binder/trusty/rust/binder_rpc_test/main.rs
index baea5a8..da1a86f 100644
--- a/libs/binder/trusty/rust/binder_rpc_test/main.rs
+++ b/libs/binder/trusty/rust/binder_rpc_test/main.rs
@@ -19,7 +19,7 @@
use binder_rpc_test_aidl::aidl::IBinderRpcSession::{BnBinderRpcSession, IBinderRpcSession};
use binder_rpc_test_aidl::aidl::IBinderRpcTest::{BnBinderRpcTest, IBinderRpcTest};
use binder_rpc_test_session::MyBinderRpcSession;
-use libc::{clock_gettime, CLOCK_REALTIME};
+use libc::{clock_gettime, CLOCK_BOOTTIME};
use rpcbinder::RpcSession;
use trusty_std::ffi::{CString, FallibleCString};
@@ -56,7 +56,7 @@
let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
// Safety: Passing valid pointer to variable ts which lives past end of call
- assert_eq!(unsafe { clock_gettime(CLOCK_REALTIME, &mut ts) }, 0);
+ assert_eq!(unsafe { clock_gettime(CLOCK_BOOTTIME, &mut ts) }, 0);
ts.tv_sec as u64 * 1_000_000_000u64 + ts.tv_nsec as u64
}
diff --git a/libs/binder/trusty/rust/rpcbinder/rules.mk b/libs/binder/trusty/rust/rpcbinder/rules.mk
index 97f5c03..04c63f7 100644
--- a/libs/binder/trusty/rust/rpcbinder/rules.mk
+++ b/libs/binder/trusty/rust/rpcbinder/rules.mk
@@ -29,8 +29,8 @@
$(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
$(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \
$(LIBBINDER_DIR)/trusty/rust/binder_rpc_server_bindgen \
- external/rust/crates/cfg-if \
- external/rust/crates/foreign-types \
+ $(call FIND_CRATE,cfg-if) \
+ $(call FIND_CRATE,foreign-types) \
trusty/user/base/lib/tipc/rust \
trusty/user/base/lib/trusty-sys \
diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk
index 36bd3a2..e622b22 100644
--- a/libs/binder/trusty/rust/rules.mk
+++ b/libs/binder/trusty/rust/rules.mk
@@ -27,8 +27,8 @@
$(LIBBINDER_DIR)/trusty/ndk \
$(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
$(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \
- external/rust/crates/downcast-rs \
- external/rust/crates/libc \
+ $(call FIND_CRATE,downcast-rs) \
+ $(call FIND_CRATE,libc) \
trusty/user/base/lib/trusty-sys \
MODULE_RUSTFLAGS += \
diff --git a/libs/ftl/non_null_test.cpp b/libs/ftl/non_null_test.cpp
index 367b398..457237c 100644
--- a/libs/ftl/non_null_test.cpp
+++ b/libs/ftl/non_null_test.cpp
@@ -81,6 +81,31 @@
static_assert(std::is_same_v<decltype(ftl::as_non_null(std::declval<const int* const>())),
ftl::NonNull<const int*>>);
+class Base {};
+class Derived : public Base {};
+
+static_assert(std::is_constructible_v<ftl::NonNull<void*>, ftl::NonNull<int*>>);
+static_assert(!std::is_constructible_v<ftl::NonNull<int*>, ftl::NonNull<void*>>);
+static_assert(std::is_constructible_v<ftl::NonNull<const int*>, ftl::NonNull<int*>>);
+static_assert(!std::is_constructible_v<ftl::NonNull<int*>, ftl::NonNull<const int*>>);
+static_assert(std::is_constructible_v<ftl::NonNull<Base*>, ftl::NonNull<Derived*>>);
+static_assert(!std::is_constructible_v<ftl::NonNull<Derived*>, ftl::NonNull<Base*>>);
+static_assert(std::is_constructible_v<ftl::NonNull<std::unique_ptr<const int>>,
+ ftl::NonNull<std::unique_ptr<int>>>);
+static_assert(std::is_constructible_v<ftl::NonNull<std::unique_ptr<Base>>,
+ ftl::NonNull<std::unique_ptr<Derived>>>);
+
+static_assert(std::is_assignable_v<ftl::NonNull<void*>, ftl::NonNull<int*>>);
+static_assert(!std::is_assignable_v<ftl::NonNull<int*>, ftl::NonNull<void*>>);
+static_assert(std::is_assignable_v<ftl::NonNull<const int*>, ftl::NonNull<int*>>);
+static_assert(!std::is_assignable_v<ftl::NonNull<int*>, ftl::NonNull<const int*>>);
+static_assert(std::is_assignable_v<ftl::NonNull<Base*>, ftl::NonNull<Derived*>>);
+static_assert(!std::is_assignable_v<ftl::NonNull<Derived*>, ftl::NonNull<Base*>>);
+static_assert(std::is_assignable_v<ftl::NonNull<std::unique_ptr<const int>>,
+ ftl::NonNull<std::unique_ptr<int>>>);
+static_assert(std::is_assignable_v<ftl::NonNull<std::unique_ptr<Base>>,
+ ftl::NonNull<std::unique_ptr<Derived>>>);
+
} // namespace
TEST(NonNull, SwapRawPtr) {
@@ -156,4 +181,14 @@
EXPECT_TRUE(ftl::contains(spi, *spi.begin()));
}
+TEST(NonNull, ImplicitConversion) {
+ int i = 123;
+ int j = 345;
+ auto ip = ftl::as_non_null(&i);
+ ftl::NonNull<void*> vp{ip};
+ EXPECT_EQ(vp.get(), &i);
+ vp = ftl::as_non_null(&j);
+ EXPECT_EQ(vp.get(), &j);
+}
+
} // namespace android::test
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index b109969..422c57b 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -69,7 +69,7 @@
color(0),
bufferTransform(0),
transformToDisplayInverse(false),
- crop(Rect::INVALID_RECT),
+ crop({0, 0, -1, -1}),
dataspace(ui::Dataspace::UNKNOWN),
surfaceDamageRegion(),
api(-1),
@@ -109,7 +109,10 @@
SAFE_PARCEL(output.writeUint32, flags);
SAFE_PARCEL(output.writeUint32, mask);
SAFE_PARCEL(matrix.write, output);
- SAFE_PARCEL(output.write, crop);
+ SAFE_PARCEL(output.writeFloat, crop.top);
+ SAFE_PARCEL(output.writeFloat, crop.left);
+ SAFE_PARCEL(output.writeFloat, crop.bottom);
+ SAFE_PARCEL(output.writeFloat, crop.right);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, relativeLayerSurfaceControl);
SAFE_PARCEL(SurfaceControl::writeNullableToParcel, output, parentSurfaceControlForChild);
SAFE_PARCEL(output.writeFloat, color.r);
@@ -218,7 +221,10 @@
SAFE_PARCEL(input.readUint32, &mask);
SAFE_PARCEL(matrix.read, input);
- SAFE_PARCEL(input.read, crop);
+ SAFE_PARCEL(input.readFloat, &crop.top);
+ SAFE_PARCEL(input.readFloat, &crop.left);
+ SAFE_PARCEL(input.readFloat, &crop.bottom);
+ SAFE_PARCEL(input.readFloat, &crop.right);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &relativeLayerSurfaceControl);
SAFE_PARCEL(SurfaceControl::readNullableFromParcel, input, &parentSurfaceControlForChild);
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index df58df4..eeea80f 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1539,14 +1539,7 @@
mStatus = BAD_INDEX;
return *this;
}
- if ((mask & layer_state_t::eLayerOpaque) || (mask & layer_state_t::eLayerHidden) ||
- (mask & layer_state_t::eLayerSecure) || (mask & layer_state_t::eLayerSkipScreenshot) ||
- (mask & layer_state_t::eEnableBackpressure) ||
- (mask & layer_state_t::eIgnoreDestinationFrame) ||
- (mask & layer_state_t::eLayerIsDisplayDecoration) ||
- (mask & layer_state_t::eLayerIsRefreshRateIndicator)) {
- s->what |= layer_state_t::eFlagsChanged;
- }
+ s->what |= layer_state_t::eFlagsChanged;
s->flags &= ~mask;
s->flags |= (flags & mask);
s->mask |= mask;
@@ -1652,6 +1645,11 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
const sp<SurfaceControl>& sc, const Rect& crop) {
+ return setCrop(sc, crop.toFloatRect());
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCrop(
+ const sp<SurfaceControl>& sc, const FloatRect& crop) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1941,6 +1939,20 @@
return *this;
}
+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*/) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+ // TODO (b/329472856): update layer_state_t for lut(s)
+ registerSurfaceControlForCallback(sc);
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachingHint(
const sp<SurfaceControl>& sc, gui::CachingHint cachingHint) {
layer_state_t* s = getLayerState(sc);
@@ -2795,6 +2807,7 @@
outInfo->autoLowLatencyModeSupported = ginfo.autoLowLatencyModeSupported;
outInfo->gameContentTypeSupported = ginfo.gameContentTypeSupported;
outInfo->preferredBootDisplayMode = ginfo.preferredBootDisplayMode;
+ outInfo->hasArrSupport = ginfo.hasArrSupport;
}
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 3114929..70873b0 100644
--- a/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
+++ b/libs/gui/aidl/android/gui/DynamicDisplayInfo.aidl
@@ -43,4 +43,7 @@
// The boot display mode preferred by the implementation.
int preferredBootDisplayMode;
+
+ // Represents whether display supports ARR.
+ boolean hasArrSupport;
}
diff --git a/libs/gui/aidl/android/gui/Lut.aidl b/libs/gui/aidl/android/gui/Lut.aidl
new file mode 100644
index 0000000..a06e521
--- /dev/null
+++ b/libs/gui/aidl/android/gui/Lut.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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
new file mode 100644
index 0000000..561e0c0
--- /dev/null
+++ b/libs/gui/aidl/android/gui/LutProperties.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * This mirrors aidl::android::hardware::graphics::composer3::LutProperties definition.
+ * @hide
+ */
+parcelable LutProperties {
+ @Backing(type="int")
+ enum Dimension { ONE_D = 1, THREE_D = 3 }
+ Dimension dimension;
+
+ long size;
+ @Backing(type="int")
+ enum SamplingKey { RGB, MAX_RGB }
+ SamplingKey[] samplingKeys;
+}
\ No newline at end of file
diff --git a/libs/gui/aidl/android/gui/OverlayProperties.aidl b/libs/gui/aidl/android/gui/OverlayProperties.aidl
index 5fb1a83..7f41bda 100644
--- a/libs/gui/aidl/android/gui/OverlayProperties.aidl
+++ b/libs/gui/aidl/android/gui/OverlayProperties.aidl
@@ -16,6 +16,8 @@
package android.gui;
+import android.gui.LutProperties;
+
/** @hide */
parcelable OverlayProperties {
parcelable SupportedBufferCombinations {
@@ -27,4 +29,6 @@
SupportedBufferCombinations[] combinations;
boolean supportMixedColorSpaces;
+
+ @nullable LutProperties[] lutProperties;
}
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 2cdde32..00065c8 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -330,7 +330,7 @@
Region transparentRegion;
uint32_t bufferTransform;
bool transformToDisplayInverse;
- Rect crop;
+ FloatRect crop;
std::shared_ptr<BufferData> bufferData = nullptr;
ui::Dataspace dataspace;
HdrMetadata hdrMetadata;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 4f9af16..5ea0c16 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -549,6 +549,7 @@
Transaction& setMatrix(const sp<SurfaceControl>& sc,
float dsdx, float dtdx, float dtdy, float dsdy);
Transaction& setCrop(const sp<SurfaceControl>& sc, const Rect& crop);
+ Transaction& setCrop(const sp<SurfaceControl>& sc, const FloatRect& crop);
Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius);
Transaction& setBackgroundBlurRadius(const sp<SurfaceControl>& sc,
int backgroundBlurRadius);
@@ -601,6 +602,11 @@
Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc,
float currentBufferRatio, float desiredRatio);
Transaction& setDesiredHdrHeadroom(const sp<SurfaceControl>& sc, float desiredRatio);
+ 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);
Transaction& setCachingHint(const sp<SurfaceControl>& sc, gui::CachingHint cachingHint);
Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index d3f2899..1c7e0e4 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -109,6 +109,14 @@
} # wb_libcameraservice
flag {
+ name: "wb_unlimited_slots"
+ namespace: "core_graphics"
+ description: "Adds APIs and updates the implementation of bufferqueues to have a user-defined slot count."
+ bug: "341359814"
+ is_fixed_read_only: true
+} # wb_unlimited_slots
+
+flag {
name: "bq_producer_throttles_only_async_mode"
namespace: "core_graphics"
description: "BufferQueueProducer only CPU throttle on queueBuffer() in async mode."
diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp
index 845a1ca..3b6a66e 100644
--- a/libs/gui/tests/BufferItemConsumer_test.cpp
+++ b/libs/gui/tests/BufferItemConsumer_test.cpp
@@ -30,6 +30,7 @@
static constexpr int kHeight = 100;
static constexpr int kMaxLockedBuffers = 3;
static constexpr int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
+static constexpr int kUsage = GRALLOC_USAGE_SW_READ_RARELY;
static constexpr int kFrameSleepUs = 30 * 1000;
class BufferItemConsumerTest : public ::testing::Test {
@@ -56,7 +57,7 @@
};
void SetUp() override {
- mBIC = new BufferItemConsumer(kFormat, kMaxLockedBuffers, true);
+ mBIC = new BufferItemConsumer(kUsage, kMaxLockedBuffers, true);
String8 name("BufferItemConsumer_Under_Test");
mBIC->setName(name);
mBFL = new BufferFreedListener(this);
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index f98437b..8fea689 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -46,7 +46,8 @@
SurfaceComposerClient::Transaction{}
.setLayer(mButton, 0x7fffffff)
- .setCrop(mButton, {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
+ .setCrop(mButton,
+ Rect{0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING})
.setPosition(mButton, samplingArea.left + BUTTON_PADDING,
samplingArea.top + BUTTON_PADDING)
.setColor(mButton, half3{1, 1, 1})
@@ -59,7 +60,8 @@
SurfaceComposerClient::Transaction{}
.setLayer(mButtonBlend, 0x7ffffffe)
.setCrop(mButtonBlend,
- {0, 0, width - 2 * SAMPLE_AREA_PADDING, height - 2 * SAMPLE_AREA_PADDING})
+ Rect{0, 0, width - 2 * SAMPLE_AREA_PADDING,
+ height - 2 * SAMPLE_AREA_PADDING})
.setPosition(mButtonBlend, samplingArea.left + SAMPLE_AREA_PADDING,
samplingArea.top + SAMPLE_AREA_PADDING)
.setColor(mButtonBlend, half3{1, 1, 1})
@@ -75,7 +77,7 @@
SurfaceComposerClient::Transaction{}
.setLayer(mSamplingArea, 0x7ffffffd)
- .setCrop(mSamplingArea, {0, 0, 100, 32})
+ .setCrop(mSamplingArea, Rect{0, 0, 100, 32})
.setPosition(mSamplingArea, 490, 1606)
.setColor(mSamplingArea, half3{0, 1, 0})
.setAlpha(mSamplingArea, 0.1)
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index cdbc186..9665de7 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -17,8 +17,6 @@
#define LOG_TAG "InputConsumerNoResampling"
#define ATRACE_TAG ATRACE_TAG_INPUT
-#include <chrono>
-
#include <inttypes.h>
#include <android-base/logging.h>
@@ -39,6 +37,8 @@
using std::chrono::nanoseconds;
+using android::base::Result;
+
/**
* Log debug messages relating to the consumer end of the transport channel.
* Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart)
@@ -169,24 +169,18 @@
msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = presentTime;
return msg;
}
-
-bool isPointerEvent(const MotionEvent& motionEvent) {
- return (motionEvent.getSource() & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
-}
} // namespace
-using android::base::Result;
-
// --- InputConsumerNoResampling ---
-InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
- sp<Looper> looper,
- InputConsumerCallbacks& callbacks,
- std::unique_ptr<Resampler> resampler)
+InputConsumerNoResampling::InputConsumerNoResampling(
+ const std::shared_ptr<InputChannel>& channel, sp<Looper> looper,
+ InputConsumerCallbacks& callbacks,
+ std::function<std::unique_ptr<Resampler>()> resamplerCreator)
: mChannel{channel},
mLooper{looper},
mCallbacks{callbacks},
- mResampler{std::move(resampler)},
+ mResamplerCreator{std::move(resamplerCreator)},
mFdEvents(0) {
LOG_ALWAYS_FATAL_IF(mLooper == nullptr);
mCallback = sp<LooperEventCallback>::make(
@@ -199,13 +193,29 @@
InputConsumerNoResampling::~InputConsumerNoResampling() {
ensureCalledOnLooperThread(__func__);
- consumeBatchedInputEvents(/*requestedFrameTime=*/std::nullopt);
while (!mOutboundQueue.empty()) {
processOutboundEvents();
// This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
// so keep trying to send the events as long as they are present in the queue.
}
+
setFdEvents(0);
+ // If there are any remaining unread batches, send an ack for them and don't deliver
+ // them to callbacks.
+ for (auto& [_, batches] : mBatches) {
+ while (!batches.empty()) {
+ finishInputEvent(batches.front().header.seq, /*handled=*/false);
+ batches.pop();
+ }
+ }
+ // However, it is still up to the app to finish any events that have already been delivered
+ // to the callbacks. If we wanted to change that behaviour and auto-finish all unfinished events
+ // that were already sent to callbacks, we could potentially loop through "mConsumeTimes"
+ // instead. We can't use "mBatchedSequenceNumbers" for this purpose, because it only contains
+ // batchable (i.e., ACTION_MOVE) events that were sent to the callbacks.
+ const size_t unfinishedEvents = mConsumeTimes.size();
+ LOG_IF(INFO, unfinishedEvents != 0)
+ << getName() << " has " << unfinishedEvents << " unfinished event(s)";
}
int InputConsumerNoResampling::handleReceiveCallback(int events) {
@@ -319,7 +329,6 @@
}
void InputConsumerNoResampling::handleMessages(std::vector<InputMessage>&& messages) {
- // TODO(b/297226446) : add resampling
for (const InputMessage& msg : messages) {
if (msg.header.type == InputMessage::Type::MOTION) {
const int32_t action = msg.body.motion.action;
@@ -329,12 +338,31 @@
action == AMOTION_EVENT_ACTION_HOVER_MOVE) &&
(isFromSource(source, AINPUT_SOURCE_CLASS_POINTER) ||
isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK));
+
+ const bool canResample = (mResamplerCreator != nullptr) &&
+ (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER));
+ if (canResample) {
+ if (action == AMOTION_EVENT_ACTION_DOWN) {
+ if (std::unique_ptr<Resampler> resampler = mResamplerCreator();
+ resampler != nullptr) {
+ const auto [_, inserted] =
+ mResamplers.insert(std::pair(deviceId, std::move(resampler)));
+ LOG_IF(WARNING, !inserted) << deviceId << "already exists in mResamplers";
+ }
+ }
+ }
+
if (batchableEvent) {
// add it to batch
mBatches[deviceId].emplace(msg);
} else {
// consume all pending batches for this device immediately
consumeBatchedInputEvents(deviceId, /*requestedFrameTime=*/std::nullopt);
+ if (canResample &&
+ (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL)) {
+ LOG_IF(INFO, mResamplers.erase(deviceId) == 0)
+ << deviceId << "does not exist in mResamplers";
+ }
handleMessage(msg);
}
} else {
@@ -456,8 +484,13 @@
std::queue<InputMessage>& messages) {
std::unique_ptr<MotionEvent> motionEvent;
std::optional<uint32_t> firstSeqForBatch;
- const nanoseconds resampleLatency =
- (mResampler != nullptr) ? mResampler->getResampleLatency() : nanoseconds{0};
+
+ LOG_IF(FATAL, messages.empty()) << "messages queue is empty!";
+ const DeviceId deviceId = messages.front().body.motion.deviceId;
+ const auto resampler = mResamplers.find(deviceId);
+ const nanoseconds resampleLatency = (resampler != mResamplers.cend())
+ ? resampler->second->getResampleLatency()
+ : nanoseconds{0};
const nanoseconds adjustedFrameTime = nanoseconds{requestedFrameTime} - resampleLatency;
while (!messages.empty() &&
@@ -474,15 +507,17 @@
}
messages.pop();
}
+
// Check if resampling should be performed.
- if (motionEvent != nullptr && isPointerEvent(*motionEvent) && mResampler != nullptr) {
- InputMessage* futureSample = nullptr;
- if (!messages.empty()) {
- futureSample = &messages.front();
- }
- mResampler->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent,
- futureSample);
+ InputMessage* futureSample = nullptr;
+ if (!messages.empty()) {
+ futureSample = &messages.front();
}
+ if ((motionEvent != nullptr) && (resampler != mResamplers.cend())) {
+ resampler->second->resampleMotionEvent(nanoseconds{requestedFrameTime}, *motionEvent,
+ futureSample);
+ }
+
return std::make_pair(std::move(motionEvent), firstSeqForBatch);
}
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index b0563ab..d775327 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -365,6 +365,17 @@
return toKeyCode;
}
+std::vector<int32_t> KeyCharacterMap::findKeyCodesMappedToKeyCode(int32_t toKeyCode) const {
+ std::vector<int32_t> fromKeyCodes;
+
+ for (const auto& [from, to] : mKeyRemapping) {
+ if (toKeyCode == to) {
+ fromKeyCodes.push_back(from);
+ }
+ }
+ return fromKeyCodes;
+}
+
std::pair<int32_t, int32_t> KeyCharacterMap::applyKeyBehavior(int32_t fromKeyCode,
int32_t fromMetaState) const {
int32_t toKeyCode = fromKeyCode;
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 51fadf8..e2cc6fb 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -18,6 +18,7 @@
#include <algorithm>
#include <chrono>
+#include <ostream>
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -26,10 +27,7 @@
#include <input/Resampler.h>
#include <utils/Timers.h>
-using std::chrono::nanoseconds;
-
namespace android {
-
namespace {
const bool IS_DEBUGGABLE_BUILD =
@@ -49,6 +47,8 @@
return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
}
+using std::chrono::nanoseconds;
+
constexpr std::chrono::milliseconds RESAMPLE_LATENCY{5};
constexpr std::chrono::milliseconds RESAMPLE_MIN_DELTA{2};
@@ -75,6 +75,31 @@
resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, lerp(a.getY(), b.getY(), alpha));
return resampledCoords;
}
+
+bool equalXY(const PointerCoords& a, const PointerCoords& b) {
+ return (a.getX() == b.getX()) && (a.getY() == b.getY());
+}
+
+void setMotionEventPointerCoords(MotionEvent& motionEvent, size_t sampleIndex, size_t pointerIndex,
+ const PointerCoords& pointerCoords) {
+ // Ideally, we should not cast away const. In this particular case, it's safe to cast away const
+ // and dereference getHistoricalRawPointerCoords returned pointer because motionEvent is a
+ // nonconst reference to a MotionEvent object, so mutating the object should not be undefined
+ // behavior; moreover, the invoked method guarantees to return a valid pointer. Otherwise, it
+ // fatally logs. Alternatively, we could've created a new MotionEvent from scratch, but this
+ // approach is simpler and more efficient.
+ PointerCoords& motionEventCoords = const_cast<PointerCoords&>(
+ *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex)));
+ motionEventCoords.setAxisValue(AMOTION_EVENT_AXIS_X, pointerCoords.getX());
+ motionEventCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, pointerCoords.getY());
+ motionEventCoords.isResampled = pointerCoords.isResampled;
+}
+
+std::ostream& operator<<(std::ostream& os, const PointerCoords& pointerCoords) {
+ os << "(" << pointerCoords.getX() << ", " << pointerCoords.getY() << ")";
+ return os;
+}
+
} // namespace
void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
@@ -85,12 +110,9 @@
std::vector<Pointer> pointers;
const size_t numPointers = motionEvent.getPointerCount();
for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) {
- // getSamplePointerCoords is the vector representation of a getHistorySize by
- // getPointerCount matrix.
- const PointerCoords& pointerCoords =
- motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex];
- pointers.push_back(
- Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords});
+ pointers.push_back(Pointer{*(motionEvent.getPointerProperties(pointerIndex)),
+ *(motionEvent.getHistoricalRawPointerCoords(pointerIndex,
+ sampleIndex))});
}
mLatestSamples.pushBack(
Sample{nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)}, pointers});
@@ -245,15 +267,56 @@
return RESAMPLE_LATENCY;
}
+void LegacyResampler::overwriteMotionEventSamples(MotionEvent& motionEvent) const {
+ const size_t numSamples = motionEvent.getHistorySize() + 1;
+ for (size_t sampleIndex = 0; sampleIndex < numSamples; ++sampleIndex) {
+ overwriteStillPointers(motionEvent, sampleIndex);
+ overwriteOldPointers(motionEvent, sampleIndex);
+ }
+}
+
+void LegacyResampler::overwriteStillPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
+ for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount(); ++pointerIndex) {
+ const PointerCoords& pointerCoords =
+ *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, sampleIndex));
+ if (equalXY(mLastRealSample->pointers[pointerIndex].coords, pointerCoords)) {
+ LOG_IF(INFO, debugResampling())
+ << "Pointer ID: " << motionEvent.getPointerId(pointerIndex)
+ << " did not move. Overwriting its coordinates from " << pointerCoords << " to "
+ << mLastRealSample->pointers[pointerIndex].coords;
+ setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
+ mPreviousPrediction->pointers[pointerIndex].coords);
+ }
+ }
+}
+
+void LegacyResampler::overwriteOldPointers(MotionEvent& motionEvent, size_t sampleIndex) const {
+ if (!mPreviousPrediction.has_value()) {
+ return;
+ }
+ if (nanoseconds{motionEvent.getHistoricalEventTime(sampleIndex)} <
+ mPreviousPrediction->eventTime) {
+ LOG_IF(INFO, debugResampling())
+ << "Motion event sample older than predicted sample. Overwriting event time from "
+ << motionEvent.getHistoricalEventTime(sampleIndex) << "ns to "
+ << mPreviousPrediction->eventTime.count() << "ns.";
+ for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
+ ++pointerIndex) {
+ setMotionEventPointerCoords(motionEvent, sampleIndex, pointerIndex,
+ mPreviousPrediction->pointers[pointerIndex].coords);
+ }
+ }
+}
+
void LegacyResampler::resampleMotionEvent(nanoseconds frameTime, MotionEvent& motionEvent,
const InputMessage* futureSample) {
- if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) {
- mLatestSamples.clear();
- }
- mPreviousDeviceId = motionEvent.getDeviceId();
-
const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY;
+ if (resampleTime.count() == motionEvent.getEventTime()) {
+ LOG_IF(INFO, debugResampling()) << "Not resampled. Resample time equals motion event time.";
+ return;
+ }
+
updateLatestSamples(motionEvent);
const std::optional<Sample> sample = (futureSample != nullptr)
@@ -261,6 +324,16 @@
: (attemptExtrapolation(resampleTime));
if (sample.has_value()) {
addSampleToMotionEvent(*sample, motionEvent);
+ if (mPreviousPrediction.has_value()) {
+ overwriteMotionEventSamples(motionEvent);
+ }
+ // mPreviousPrediction is only updated whenever extrapolation occurs because extrapolation
+ // is about predicting upcoming scenarios.
+ if (futureSample == nullptr) {
+ mPreviousPrediction = sample;
+ }
}
+ mLastRealSample = *(mLatestSamples.end() - 1);
}
+
} // namespace android
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index e23fc94..31592cd 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -49,6 +49,12 @@
const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000;
/**
+ * The key event triggered a key gesture. Used in policy flag to notify that a key gesture was
+ * triggered using the event.
+ */
+ const int POLICY_FLAG_KEY_GESTURE_TRIGGERED = 0x40000;
+
+ /**
* Common input event flag used for both motion and key events for a gesture or pointer being
* canceled.
*/
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index 60fb00e..701fb43 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -207,3 +207,10 @@
description: "Allow user to enable key repeats or configure timeout before key repeat and key repeat delay rates."
bug: "336585002"
}
+
+flag {
+ name: "rotary_input_telemetry"
+ namespace: "wear_frameworks"
+ description: "Enable telemetry for rotary input"
+ bug: "370353565"
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 81c6175..661c9f7 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -17,6 +17,7 @@
"IdGenerator_test.cpp",
"InputChannel_test.cpp",
"InputConsumer_test.cpp",
+ "InputConsumerResampling_test.cpp",
"InputDevice_test.cpp",
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
diff --git a/libs/input/tests/InputConsumerResampling_test.cpp b/libs/input/tests/InputConsumerResampling_test.cpp
new file mode 100644
index 0000000..883ca82
--- /dev/null
+++ b/libs/input/tests/InputConsumerResampling_test.cpp
@@ -0,0 +1,569 @@
+/*
+ * 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 <input/InputConsumerNoResampling.h>
+
+#include <chrono>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <TestEventMatchers.h>
+#include <TestInputChannel.h>
+#include <attestation/HmacKeyManager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <input/BlockingQueue.h>
+#include <input/InputEventBuilders.h>
+#include <input/Resampler.h>
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace {
+
+using std::chrono::nanoseconds;
+using namespace std::chrono_literals;
+
+struct Pointer {
+ int32_t id{0};
+ float x{0.0f};
+ float y{0.0f};
+ ToolType toolType{ToolType::FINGER};
+ bool isResampled{false};
+
+ PointerBuilder asPointerBuilder() const {
+ return PointerBuilder{id, toolType}.x(x).y(y).isResampled(isResampled);
+ }
+};
+
+struct InputEventEntry {
+ std::chrono::nanoseconds eventTime{0};
+ std::vector<Pointer> pointers{};
+ int32_t action{-1};
+};
+
+} // namespace
+
+class InputConsumerResamplingTest : public ::testing::Test, public InputConsumerCallbacks {
+protected:
+ InputConsumerResamplingTest()
+ : mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")},
+ mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} {
+ Looper::setForThread(mLooper);
+ mConsumer = std::make_unique<
+ InputConsumerNoResampling>(mClientTestChannel, mLooper, *this,
+ []() { return std::make_unique<LegacyResampler>(); });
+ }
+
+ void invokeLooperCallback() const {
+ sp<LooperCallback> callback;
+ ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr,
+ /*events=*/nullptr, &callback, /*data=*/nullptr));
+ ASSERT_NE(callback, nullptr);
+ callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
+ }
+
+ InputMessage nextPointerMessage(const InputEventEntry& entry);
+
+ void assertReceivedMotionEvent(const std::vector<InputEventEntry>& expectedEntries);
+
+ std::shared_ptr<TestInputChannel> mClientTestChannel;
+ sp<Looper> mLooper;
+ std::unique_ptr<InputConsumerNoResampling> mConsumer;
+
+ BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents;
+ BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents;
+ BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents;
+ BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
+ BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents;
+ BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+
+private:
+ uint32_t mLastSeq{0};
+ size_t mOnBatchedInputEventPendingInvocationCount{0};
+
+ // InputConsumerCallbacks interface
+ void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
+ mKeyEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
+ mMotionEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onBatchedInputEventPending(int32_t pendingBatchSource) override {
+ if (!mConsumer->probablyHasInput()) {
+ ADD_FAILURE() << "should deterministically have input because there is a batch";
+ }
+ ++mOnBatchedInputEventPendingInvocationCount;
+ }
+ void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
+ mFocusEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
+ mCaptureEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
+ mDragEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
+ mTouchModeEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+};
+
+InputMessage InputConsumerResamplingTest::nextPointerMessage(const InputEventEntry& entry) {
+ ++mLastSeq;
+ InputMessageBuilder messageBuilder = InputMessageBuilder{InputMessage::Type::MOTION, mLastSeq}
+ .eventTime(entry.eventTime.count())
+ .deviceId(1)
+ .action(entry.action)
+ .downTime(0);
+ for (const Pointer& pointer : entry.pointers) {
+ messageBuilder.pointer(pointer.asPointerBuilder());
+ }
+ return messageBuilder.build();
+}
+
+void InputConsumerResamplingTest::assertReceivedMotionEvent(
+ const std::vector<InputEventEntry>& expectedEntries) {
+ std::unique_ptr<MotionEvent> motionEvent = mMotionEvents.pop();
+ ASSERT_NE(motionEvent, nullptr);
+
+ ASSERT_EQ(motionEvent->getHistorySize() + 1, expectedEntries.size());
+
+ for (size_t sampleIndex = 0; sampleIndex < expectedEntries.size(); ++sampleIndex) {
+ SCOPED_TRACE("sampleIndex: " + std::to_string(sampleIndex));
+ const InputEventEntry& expectedEntry = expectedEntries[sampleIndex];
+ EXPECT_EQ(motionEvent->getHistoricalEventTime(sampleIndex),
+ expectedEntry.eventTime.count());
+ EXPECT_EQ(motionEvent->getPointerCount(), expectedEntry.pointers.size());
+ EXPECT_EQ(motionEvent->getAction(), expectedEntry.action);
+
+ for (size_t pointerIndex = 0; pointerIndex < expectedEntry.pointers.size();
+ ++pointerIndex) {
+ SCOPED_TRACE("pointerIndex: " + std::to_string(pointerIndex));
+ ssize_t eventPointerIndex =
+ motionEvent->findPointerIndex(expectedEntry.pointers[pointerIndex].id);
+ EXPECT_EQ(motionEvent->getHistoricalRawX(eventPointerIndex, sampleIndex),
+ expectedEntry.pointers[pointerIndex].x);
+ EXPECT_EQ(motionEvent->getHistoricalRawY(eventPointerIndex, sampleIndex),
+ expectedEntry.pointers[pointerIndex].y);
+ EXPECT_EQ(motionEvent->getHistoricalX(eventPointerIndex, sampleIndex),
+ expectedEntry.pointers[pointerIndex].x);
+ EXPECT_EQ(motionEvent->getHistoricalY(eventPointerIndex, sampleIndex),
+ expectedEntry.pointers[pointerIndex].y);
+ EXPECT_EQ(motionEvent->isResampled(pointerIndex, sampleIndex),
+ expectedEntry.pointers[pointerIndex].isResampled);
+ }
+ }
+}
+
+/**
+ * Timeline
+ * ---------+------------------+------------------+--------+-----------------+----------------------
+ * 0 ms 10 ms 20 ms 25 ms 35 ms
+ * ACTION_DOWN ACTION_MOVE ACTION_MOVE ^ ^
+ * | |
+ * resampled value |
+ * frameTime
+ * Typically, the prediction is made for time frameTime - RESAMPLE_LATENCY, or 30 ms in this case,
+ * where RESAMPLE_LATENCY equals 5 milliseconds. However, that would be 10 ms later than the last
+ * real sample (which came in at 20 ms). Therefore, the resampling should happen at 20 ms +
+ * RESAMPLE_MAX_PREDICTION = 28 ms, where RESAMPLE_MAX_PREDICTION equals 8 milliseconds. In this
+ * situation, though, resample time is further limited by taking half of the difference between the
+ * last two real events, which would put this time at: 20 ms + (20 ms - 10 ms) / 2 = 25 ms.
+ */
+TEST_F(InputConsumerResamplingTest, EventIsResampled) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ 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}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ 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(nanoseconds{35ms}.count());
+ 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},
+ InputEventEntry{25ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+/**
+ * Same as above test, but use pointer id=1 instead of 0 to make sure that system does not
+ * have these hardcoded.
+ */
+TEST_F(InputConsumerResamplingTest, EventIsResampledWithDifferentId) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms, {Pointer{.id = 1, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 1, .x = 10.0f, .y = 20.0f}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms, {Pointer{.id = 1, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms, {Pointer{.id = 1, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{10ms,
+ {Pointer{.id = 1, .x = 20.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 1, .x = 30.0f, .y = 30.0f}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{25ms,
+ {Pointer{.id = 1, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+/**
+ * Stylus pointer coordinates are resampled.
+ */
+TEST_F(InputConsumerResamplingTest, StylusEventIsResampled) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0,
+ .x = 10.0f,
+ .y = 20.0f,
+ .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent({InputEventEntry{10ms,
+ {Pointer{.id = 0,
+ .x = 20.0f,
+ .y = 30.0f,
+ .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0,
+ .x = 30.0f,
+ .y = 30.0f,
+ .toolType = ToolType::STYLUS}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{25ms,
+ {Pointer{.id = 0,
+ .x = 35.0f,
+ .y = 30.0f,
+ .toolType = ToolType::STYLUS,
+ .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+/**
+ * Mouse pointer coordinates are resampled.
+ */
+TEST_F(InputConsumerResamplingTest, MouseEventIsResampled) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent({InputEventEntry{0ms,
+ {Pointer{.id = 0,
+ .x = 10.0f,
+ .y = 20.0f,
+ .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent({InputEventEntry{10ms,
+ {Pointer{.id = 0,
+ .x = 20.0f,
+ .y = 30.0f,
+ .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0,
+ .x = 30.0f,
+ .y = 30.0f,
+ .toolType = ToolType::MOUSE}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{25ms,
+ {Pointer{.id = 0,
+ .x = 35.0f,
+ .y = 30.0f,
+ .toolType = ToolType::MOUSE,
+ .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+/**
+ * Motion events with palm tool type are not resampled.
+ */
+TEST_F(InputConsumerResamplingTest, PalmEventIsNotResampled) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_DOWN}));
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent(
+ {InputEventEntry{0ms,
+ {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_DOWN}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_MOVE}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{35ms}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{10ms,
+ {Pointer{.id = 0, .x = 20.0f, .y = 30.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_MOVE},
+ InputEventEntry{20ms,
+ {Pointer{.id = 0, .x = 30.0f, .y = 30.0f, .toolType = ToolType::PALM}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
+/**
+ * Event should not be resampled when sample time is equal to event time.
+ */
+TEST_F(InputConsumerResamplingTest, SampleTimeEqualsEventTime) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ 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}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ 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(nanoseconds{20ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+
+ // MotionEvent should not resampled because the resample time falls exactly on the existing
+ // event time.
+ 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);
+}
+
+/**
+ * Once we send a resampled value to the app, we should continue to send the last predicted value if
+ * a pointer does not move. Only real values are used to determine if a pointer does not move.
+ */
+TEST_F(InputConsumerResamplingTest, ResampledValueIsUsedForIdenticalCoordinates) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ 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}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ 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(nanoseconds{35ms}.count());
+ 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},
+ InputEventEntry{25ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ // Coordinate value 30 has been resampled to 35. When a new event comes in with value 30 again,
+ // the system should still report 35.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {40ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{45ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{40ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+ InputEventEntry{45ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+}
+
+TEST_F(InputConsumerResamplingTest, OldEventReceivedAfterResampleOccurs) {
+ // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+ // InputEvent with a single action.
+ 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}});
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ 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(nanoseconds{35ms}.count());
+ 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},
+ InputEventEntry{25ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}});
+
+ // Above, the resampled event is at 25ms rather than at 30 ms = 35ms - RESAMPLE_LATENCY
+ // because we are further bound by how far we can extrapolate by the "last time delta".
+ // That's 50% of (20 ms - 10ms) => 5ms. So we can't predict more than 5 ms into the future
+ // from the event at 20ms, which is why the resampled event is at t = 25 ms.
+
+ // We resampled the event to 25 ms. Now, an older 'real' event comes in.
+ mClientTestChannel->enqueueMessage(nextPointerMessage(
+ {24ms, {Pointer{.id = 0, .x = 40.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(nanoseconds{50ms}.count());
+ assertReceivedMotionEvent(
+ {InputEventEntry{24ms,
+ {Pointer{.id = 0, .x = 35.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}, // original event, rewritten
+ InputEventEntry{26ms,
+ {Pointer{.id = 0, .x = 45.0f, .y = 30.0f, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE}}); // resampled event, rewritten
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+}
+
+} // namespace android
diff --git a/libs/input/tests/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp
index d708316..6a3bbe5 100644
--- a/libs/input/tests/InputConsumer_test.cpp
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -16,6 +16,9 @@
#include <input/InputConsumerNoResampling.h>
+#include <gtest/gtest.h>
+
+#include <chrono>
#include <memory>
#include <optional>
@@ -25,7 +28,9 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <input/BlockingQueue.h>
+#include <input/Input.h>
#include <input/InputEventBuilders.h>
+#include <input/Resampler.h>
#include <utils/Looper.h>
#include <utils/StrongPointer.h>
@@ -37,8 +42,21 @@
using ::testing::AllOf;
using ::testing::Matcher;
-using ::testing::Not;
+constexpr auto ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
+constexpr auto ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE;
+
+struct Pointer {
+ int32_t id{0};
+ ToolType toolType{ToolType::FINGER};
+ float x{0.0f};
+ float y{0.0f};
+ bool isResampled{false};
+
+ PointerBuilder asPointerBuilder() const {
+ return PointerBuilder{id, toolType}.x(x).y(y).isResampled(isResampled);
+ }
+};
} // namespace
class InputConsumerTest : public testing::Test, public InputConsumerCallbacks {
@@ -47,9 +65,9 @@
: mClientTestChannel{std::make_shared<TestInputChannel>("TestChannel")},
mLooper{sp<Looper>::make(/*allowNonCallbacks=*/false)} {
Looper::setForThread(mLooper);
- mConsumer =
- std::make_unique<InputConsumerNoResampling>(mClientTestChannel, mLooper, *this,
- std::make_unique<LegacyResampler>());
+ mConsumer = std::make_unique<
+ InputConsumerNoResampling>(mClientTestChannel, mLooper, *this,
+ []() { return std::make_unique<LegacyResampler>(); });
}
void invokeLooperCallback() const {
@@ -65,34 +83,52 @@
--mOnBatchedInputEventPendingInvocationCount;
}
- void assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) {
- std::unique_ptr<MotionEvent> motionEvent = mMotionEvents.pop();
- ASSERT_NE(motionEvent, nullptr);
+ std::unique_ptr<MotionEvent> assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher) {
+ if (mMotionEvents.empty()) {
+ ADD_FAILURE() << "No motion events received";
+ return nullptr;
+ }
+ std::unique_ptr<MotionEvent> motionEvent = std::move(mMotionEvents.front());
+ mMotionEvents.pop();
+ if (motionEvent == nullptr) {
+ ADD_FAILURE() << "The consumed motion event should never be null";
+ return nullptr;
+ }
EXPECT_THAT(*motionEvent, matcher);
+ return motionEvent;
}
+ InputMessage nextPointerMessage(std::chrono::nanoseconds eventTime, DeviceId deviceId,
+ int32_t action, const Pointer& pointer);
+
std::shared_ptr<TestInputChannel> mClientTestChannel;
sp<Looper> mLooper;
std::unique_ptr<InputConsumerNoResampling> mConsumer;
- BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents;
- BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents;
- BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents;
- BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
- BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents;
- BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+ std::queue<std::unique_ptr<KeyEvent>> mKeyEvents;
+ std::queue<std::unique_ptr<MotionEvent>> mMotionEvents;
+ std::queue<std::unique_ptr<FocusEvent>> mFocusEvents;
+ std::queue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
+ std::queue<std::unique_ptr<DragEvent>> mDragEvents;
+ std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+
+ // Whether or not to automatically call "finish" whenever a motion event is received.
+ bool mShouldFinishMotions{true};
private:
+ uint32_t mLastSeq{0};
size_t mOnBatchedInputEventPendingInvocationCount{0};
// InputConsumerCallbacks interface
void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
mKeyEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
}
void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
mMotionEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ if (mShouldFinishMotions) {
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
}
void onBatchedInputEventPending(int32_t pendingBatchSource) override {
if (!mConsumer->probablyHasInput()) {
@@ -102,34 +138,47 @@
};
void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
mFocusEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
};
void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
mCaptureEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
};
void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
mDragEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
}
void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
mTouchModeEvents.push(std::move(event));
- mConsumer->finishInputEvent(seq, true);
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
};
};
+InputMessage InputConsumerTest::nextPointerMessage(std::chrono::nanoseconds eventTime,
+ DeviceId deviceId, int32_t action,
+ const Pointer& pointer) {
+ ++mLastSeq;
+ return InputMessageBuilder{InputMessage::Type::MOTION, mLastSeq}
+ .eventTime(eventTime.count())
+ .deviceId(deviceId)
+ .source(AINPUT_SOURCE_TOUCHSCREEN)
+ .action(action)
+ .pointer(pointer.asPointerBuilder())
+ .build();
+}
+
TEST_F(InputConsumerTest, MessageStreamBatchedInMotionEvent) {
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
.eventTime(nanoseconds{0ms}.count())
- .action(AMOTION_EVENT_ACTION_DOWN)
+ .action(ACTION_DOWN)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
.eventTime(nanoseconds{5ms}.count())
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
.eventTime(nanoseconds{10ms}.count())
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->assertNoSentMessages();
@@ -140,10 +189,10 @@
mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt);
- std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop();
- ASSERT_NE(downMotionEvent, nullptr);
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
- std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop();
+ std::unique_ptr<MotionEvent> moveMotionEvent =
+ assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
ASSERT_NE(moveMotionEvent, nullptr);
EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL);
@@ -155,19 +204,19 @@
TEST_F(InputConsumerTest, LastBatchedSampleIsLessThanResampleTime) {
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
.eventTime(nanoseconds{0ms}.count())
- .action(AMOTION_EVENT_ACTION_DOWN)
+ .action(ACTION_DOWN)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
.eventTime(nanoseconds{5ms}.count())
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
.eventTime(nanoseconds{10ms}.count())
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
.eventTime(nanoseconds{15ms}.count())
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->assertNoSentMessages();
@@ -178,56 +227,98 @@
mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/);
- std::unique_ptr<MotionEvent> downMotionEvent = mMotionEvents.pop();
- ASSERT_NE(downMotionEvent, nullptr);
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
- std::unique_ptr<MotionEvent> moveMotionEvent = mMotionEvents.pop();
+ std::unique_ptr<MotionEvent> moveMotionEvent =
+ assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
ASSERT_NE(moveMotionEvent, nullptr);
const size_t numSamples = moveMotionEvent->getHistorySize() + 1;
EXPECT_LT(moveMotionEvent->getHistoricalEventTime(numSamples - 2),
moveMotionEvent->getEventTime());
- // Consume all remaining events before ending the test. Otherwise, the smart pointer that owns
- // consumer is set to null before destroying consumer. This leads to a member function call on a
- // null object.
- // TODO(b/332613662): Remove this workaround.
- mConsumer->consumeBatchedInputEvents(std::nullopt);
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ // The event with seq=3 remains unconsumed, and therefore finish will not be called for it until
+ // after the consumer is destroyed.
+ mConsumer.reset();
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/false);
+ mClientTestChannel->assertNoSentMessages();
+}
- mClientTestChannel->assertFinishMessage(/*seq=*/0, true);
- mClientTestChannel->assertFinishMessage(/*seq=*/1, true);
- mClientTestChannel->assertFinishMessage(/*seq=*/2, true);
- mClientTestChannel->assertFinishMessage(/*seq=*/3, true);
+/**
+ * During normal operation, the user of InputConsumer (callbacks) is expected to call "finish"
+ * for each input event received in InputConsumerCallbacks.
+ * If the InputConsumer is destroyed, the events that were already sent to the callbacks will not
+ * be finished automatically.
+ */
+TEST_F(InputConsumerTest, UnhandledEventsNotFinishedInDestructor) {
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
+ mShouldFinishMotions = false;
+ invokeLooperCallback();
+ assertOnBatchedInputEventPendingWasCalled();
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
+ mClientTestChannel->assertNoSentMessages();
+ // The "finishInputEvent" was not called by the InputConsumerCallbacks.
+ // Now, destroy the consumer and check that the "finish" was not called automatically for the
+ // DOWN event, but was called for the undelivered MOVE event.
+ mConsumer.reset();
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
+ mClientTestChannel->assertNoSentMessages();
+}
+
+/**
+ * Send an event to the InputConsumer, but do not invoke "consumeBatchedInputEvents", thus leaving
+ * the input event unconsumed by the callbacks. Ensure that no crash occurs when the consumer is
+ * destroyed.
+ * This test is similar to the one above, but here we are calling "finish"
+ * automatically for any event received in the callbacks.
+ */
+TEST_F(InputConsumerTest, UnconsumedEventDoesNotCauseACrash) {
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build());
+ invokeLooperCallback();
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+ mClientTestChannel->enqueueMessage(
+ InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build());
+ invokeLooperCallback();
+ mConsumer.reset();
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false);
}
TEST_F(InputConsumerTest, BatchedEventsMultiDeviceConsumption) {
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
.deviceId(0)
- .action(AMOTION_EVENT_ACTION_DOWN)
+ .action(ACTION_DOWN)
.build());
invokeLooperCallback();
- assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+ assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(ACTION_DOWN)));
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
.deviceId(0)
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
.deviceId(0)
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
.deviceId(0)
- .action(AMOTION_EVENT_ACTION_MOVE)
+ .action(ACTION_MOVE)
.build());
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/4}
.deviceId(1)
- .action(AMOTION_EVENT_ACTION_DOWN)
+ .action(ACTION_DOWN)
.build());
invokeLooperCallback();
- assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+ assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(ACTION_DOWN)));
mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/5}
.deviceId(0)
@@ -235,8 +326,7 @@
.build());
invokeLooperCallback();
- assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- Not(MotionEventIsResampled())));
+ assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(ACTION_MOVE)));
mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
@@ -244,4 +334,113 @@
mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
}
+
+/**
+ * The test supposes a 60Hz Vsync rate and a 200Hz input rate. The InputMessages are intertwined as
+ * in a real use cases. The test's two devices should be resampled independently. Moreover, the
+ * InputMessage stream layout for the test is:
+ *
+ * DOWN(0, 0ms)
+ * MOVE(0, 5ms)
+ * MOVE(0, 10ms)
+ * DOWN(1, 15ms)
+ *
+ * CONSUME(16ms)
+ *
+ * MOVE(1, 20ms)
+ * MOVE(1, 25ms)
+ * MOVE(0, 30ms)
+ *
+ * CONSUME(32ms)
+ *
+ * MOVE(0, 35ms)
+ * UP(1, 40ms)
+ * UP(0, 45ms)
+ *
+ * CONSUME(48ms)
+ *
+ * The first field is device ID, and the second field is event time.
+ */
+TEST_F(InputConsumerTest, MultiDeviceResampling) {
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(0ms, /*deviceId=*/0, ACTION_DOWN, Pointer{.x = 0, .y = 0}));
+
+ mClientTestChannel->assertNoSentMessages();
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent(
+ AllOf(WithDeviceId(0), WithMotionAction(ACTION_DOWN), WithSampleCount(1)));
+
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(5ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 1.0f, .y = 2.0f}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(10ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 2.0f, .y = 4.0f}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(15ms, /*deviceId=*/1, ACTION_DOWN, Pointer{.x = 10.0f, .y = 10.0f}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/);
+
+ assertReceivedMotionEvent(
+ AllOf(WithDeviceId(1), WithMotionAction(ACTION_DOWN), WithSampleCount(1)));
+ assertReceivedMotionEvent(
+ AllOf(WithDeviceId(0), WithMotionAction(ACTION_MOVE), WithSampleCount(3),
+ WithSample(/*sampleIndex=*/2,
+ Sample{11ms,
+ {PointerArgs{.x = 2.2f, .y = 4.4f, .isResampled = true}}})));
+
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(20ms, /*deviceId=*/1, ACTION_MOVE, Pointer{.x = 11.0f, .y = 12.0f}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(25ms, /*deviceId=*/1, ACTION_MOVE, Pointer{.x = 12.0f, .y = 14.0f}));
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(30ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 5.0f, .y = 6.0f}));
+
+ invokeLooperCallback();
+ assertOnBatchedInputEventPendingWasCalled();
+ mConsumer->consumeBatchedInputEvents(32'000'000 /*ns*/);
+
+ assertReceivedMotionEvent(
+ AllOf(WithDeviceId(1), WithMotionAction(ACTION_MOVE), WithSampleCount(3),
+ WithSample(/*sampleIndex=*/2,
+ Sample{27ms,
+ {PointerArgs{.x = 12.4f, .y = 14.8f, .isResampled = true}}})));
+
+ mClientTestChannel->enqueueMessage(
+ nextPointerMessage(35ms, /*deviceId=*/0, ACTION_MOVE, Pointer{.x = 8.0f, .y = 9.0f}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(40ms, /*deviceId=*/1,
+ AMOTION_EVENT_ACTION_UP,
+ Pointer{.x = 12.0f, .y = 14.0f}));
+ mClientTestChannel->enqueueMessage(nextPointerMessage(45ms, /*deviceId=*/0,
+ AMOTION_EVENT_ACTION_UP,
+ Pointer{.x = 8.0f, .y = 9.0f}));
+
+ invokeLooperCallback();
+ mConsumer->consumeBatchedInputEvents(48'000'000 /*ns*/);
+
+ assertReceivedMotionEvent(
+ AllOf(WithDeviceId(1), WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSampleCount(1)));
+
+ assertReceivedMotionEvent(
+ AllOf(WithDeviceId(0), WithMotionAction(ACTION_MOVE), WithSampleCount(3),
+ WithSample(/*sampleIndex=*/2,
+ Sample{37'500'000ns,
+ {PointerArgs{.x = 9.5f, .y = 10.5f, .isResampled = true}}})));
+
+ assertReceivedMotionEvent(
+ AllOf(WithDeviceId(0), WithMotionAction(AMOTION_EVENT_ACTION_UP), WithSampleCount(1)));
+
+ // The sequence order is based on the expected consumption. Each sequence number corresponds to
+ // one of the previously enqueued messages.
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/5, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/6, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/9, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/7, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/8, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/10, /*handled=*/true);
+}
} // namespace android
diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp
index 26dee39..fae8518 100644
--- a/libs/input/tests/Resampler_test.cpp
+++ b/libs/input/tests/Resampler_test.cpp
@@ -87,7 +87,6 @@
struct InputStream {
std::vector<InputSample> samples{};
int32_t action{0};
- DeviceId deviceId{0};
/**
* Converts from InputStream to MotionEvent. Enables calling LegacyResampler methods only with
* the relevant data for tests.
@@ -100,8 +99,8 @@
MotionEventBuilder motionEventBuilder =
MotionEventBuilder(action, AINPUT_SOURCE_CLASS_POINTER)
.downTime(0)
- .eventTime(static_cast<std::chrono::nanoseconds>(firstSample.eventTime).count())
- .deviceId(deviceId);
+ .eventTime(
+ static_cast<std::chrono::nanoseconds>(firstSample.eventTime).count());
for (const Pointer& pointer : firstSample.pointers) {
const PointerBuilder pointerBuilder =
PointerBuilder(pointer.id, pointer.toolType).x(pointer.x).y(pointer.y);
@@ -289,28 +288,6 @@
assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
}
-TEST_F(ResamplerTest, SinglePointerDifferentDeviceIdBetweenMotionEvents) {
- MotionEvent motionFromFirstDevice =
- InputStream{{InputSample{4ms, {{.id = 0, .x = 1.0f, .y = 1.0f, .isResampled = false}}},
- InputSample{8ms, {{.id = 0, .x = 2.0f, .y = 2.0f, .isResampled = false}}}},
- AMOTION_EVENT_ACTION_MOVE,
- .deviceId = 0};
-
- mResampler->resampleMotionEvent(10ms, motionFromFirstDevice, nullptr);
-
- MotionEvent motionFromSecondDevice =
- InputStream{{InputSample{11ms,
- {{.id = 0, .x = 3.0f, .y = 3.0f, .isResampled = false}}}},
- AMOTION_EVENT_ACTION_MOVE,
- .deviceId = 1};
- const MotionEvent originalMotionEvent = motionFromSecondDevice;
-
- mResampler->resampleMotionEvent(12ms, motionFromSecondDevice, nullptr);
- // The MotionEvent should not be resampled because the second event came from a different device
- // than the previous event.
- assertMotionEventIsNotResampled(originalMotionEvent, motionFromSecondDevice);
-}
-
TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) {
MotionEvent motionEvent =
InputStream{{InputSample{10ms,
diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h
index dd2e40c..3589de5 100644
--- a/libs/input/tests/TestEventMatchers.h
+++ b/libs/input/tests/TestEventMatchers.h
@@ -16,18 +16,39 @@
#pragma once
+#include <chrono>
#include <ostream>
+#include <vector>
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
#include <input/Input.h>
namespace android {
+namespace {
+
+using ::testing::Matcher;
+
+} // namespace
+
/**
* This file contains a copy of Matchers from .../inputflinger/tests/TestEventMatchers.h. Ideally,
* implementations must not be duplicated.
* TODO(b/365606513): Find a way to share TestEventMatchers.h between inputflinger and libinput.
*/
+struct PointerArgs {
+ float x{0.0f};
+ float y{0.0f};
+ bool isResampled{false};
+};
+
+struct Sample {
+ std::chrono::nanoseconds eventTime{0};
+ std::vector<PointerArgs> pointers{};
+};
+
class WithDeviceIdMatcher {
public:
using is_gtest_matcher = void;
@@ -79,32 +100,88 @@
return WithMotionActionMatcher(action);
}
-class MotionEventIsResampledMatcher {
+class WithSampleCountMatcher {
public:
using is_gtest_matcher = void;
+ explicit WithSampleCountMatcher(size_t sampleCount) : mExpectedSampleCount{sampleCount} {}
bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream*) const {
- const size_t numSamples = motionEvent.getHistorySize() + 1;
- const size_t numPointers = motionEvent.getPointerCount();
- if (numPointers <= 0 || numSamples <= 0) {
+ return (motionEvent.getHistorySize() + 1) == mExpectedSampleCount;
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "sample count " << mExpectedSampleCount; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "different sample count"; }
+
+private:
+ const size_t mExpectedSampleCount;
+};
+
+inline WithSampleCountMatcher WithSampleCount(size_t sampleCount) {
+ return WithSampleCountMatcher(sampleCount);
+}
+
+class WithSampleMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithSampleMatcher(size_t sampleIndex, const Sample& sample)
+ : mSampleIndex{sampleIndex}, mSample{sample} {}
+
+ bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream* os) const {
+ if (motionEvent.getHistorySize() < mSampleIndex) {
+ *os << "sample index out of bounds";
return false;
}
- for (size_t i = 0; i < numPointers; ++i) {
+
+ if (motionEvent.getHistoricalEventTime(mSampleIndex) != mSample.eventTime.count()) {
+ *os << "event time mismatch. sample: "
+ << motionEvent.getHistoricalEventTime(mSampleIndex)
+ << " expected: " << mSample.eventTime.count();
+ return false;
+ }
+
+ if (motionEvent.getPointerCount() != mSample.pointers.size()) {
+ *os << "pointer count mismatch. sample: " << motionEvent.getPointerCount()
+ << " expected: " << mSample.pointers.size();
+ return false;
+ }
+
+ for (size_t pointerIndex = 0; pointerIndex < motionEvent.getPointerCount();
+ ++pointerIndex) {
const PointerCoords& pointerCoords =
- motionEvent.getSamplePointerCoords()[numSamples * numPointers + i];
- if (!pointerCoords.isResampled) {
+ *(motionEvent.getHistoricalRawPointerCoords(pointerIndex, mSampleIndex));
+ if ((pointerCoords.getX() != mSample.pointers[pointerIndex].x) ||
+ (pointerCoords.getY() != mSample.pointers[pointerIndex].y)) {
+ *os << "sample coordinates mismatch at pointer index " << pointerIndex
+ << ". sample: (" << pointerCoords.getX() << ", " << pointerCoords.getY()
+ << ") expected: (" << mSample.pointers[pointerIndex].x << ", "
+ << mSample.pointers[pointerIndex].y << ")";
+ return false;
+ }
+ if (motionEvent.isResampled(pointerIndex, mSampleIndex) !=
+ mSample.pointers[pointerIndex].isResampled) {
+ *os << "resampling flag mismatch. sample: "
+ << motionEvent.isResampled(pointerIndex, mSampleIndex)
+ << " expected: " << mSample.pointers[pointerIndex].isResampled;
return false;
}
}
return true;
}
- void DescribeTo(std::ostream* os) const { *os << "MotionEvent is resampled."; }
+ void DescribeTo(std::ostream* os) const { *os << "motion event sample properties match."; }
- void DescribeNegationTo(std::ostream* os) const { *os << "MotionEvent is not resampled."; }
+ void DescribeNegationTo(std::ostream* os) const {
+ *os << "motion event sample properties do not match expected properties.";
+ }
+
+private:
+ const size_t mSampleIndex;
+ const Sample mSample;
};
-inline MotionEventIsResampledMatcher MotionEventIsResampled() {
- return MotionEventIsResampledMatcher();
+inline WithSampleMatcher WithSample(size_t sampleIndex, const Sample& sample) {
+ return WithSampleMatcher(sampleIndex, sample);
}
+
} // namespace android
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
index d68d6ba..c572ee7 100644
--- a/libs/nativewindow/rust/Android.bp
+++ b/libs/nativewindow/rust/Android.bp
@@ -26,6 +26,7 @@
source_stem: "bindings",
bindgen_flags: [
"--constified-enum-module=AHardwareBuffer_Format",
+ "--bitfield-enum=ADataSpace",
"--bitfield-enum=AHardwareBuffer_UsageFlags",
"--allowlist-file=.*/nativewindow/include/.*\\.h",
@@ -110,6 +111,7 @@
srcs: ["src/lib.rs"],
rustlibs: [
"libbinder_rs",
+ "libbitflags",
"libnativewindow_bindgen",
],
}
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 931c311..f19b908 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -19,10 +19,9 @@
mod handle;
mod surface;
-pub use handle::NativeHandle;
-pub use surface::Surface;
-
pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
+pub use handle::NativeHandle;
+pub use surface::{buffer::Buffer, Surface};
use binder::{
binder_impl::{BorrowedParcel, UnstructuredParcelable},
diff --git a/libs/nativewindow/rust/src/surface.rs b/libs/nativewindow/rust/src/surface.rs
index 25fea80..ed52471 100644
--- a/libs/nativewindow/rust/src/surface.rs
+++ b/libs/nativewindow/rust/src/surface.rs
@@ -14,20 +14,27 @@
//! Rust wrapper for `ANativeWindow` and related types.
+pub(crate) mod buffer;
+
use binder::{
binder_impl::{BorrowedParcel, UnstructuredParcelable},
impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
unstable_api::{status_result, AsNative},
StatusCode,
};
+use bitflags::bitflags;
+use buffer::Buffer;
use nativewindow_bindgen::{
- AHardwareBuffer_Format, ANativeWindow, ANativeWindow_acquire, ANativeWindow_getFormat,
- ANativeWindow_getHeight, ANativeWindow_getWidth, ANativeWindow_readFromParcel,
- ANativeWindow_release, ANativeWindow_writeToParcel,
+ ADataSpace, AHardwareBuffer_Format, ANativeWindow, ANativeWindow_acquire,
+ ANativeWindow_getBuffersDataSpace, ANativeWindow_getBuffersDefaultDataSpace,
+ ANativeWindow_getFormat, ANativeWindow_getHeight, ANativeWindow_getWidth, ANativeWindow_lock,
+ ANativeWindow_readFromParcel, ANativeWindow_release, ANativeWindow_setBuffersDataSpace,
+ ANativeWindow_setBuffersGeometry, ANativeWindow_setBuffersTransform,
+ ANativeWindow_unlockAndPost, ANativeWindow_writeToParcel, ARect,
};
use std::error::Error;
use std::fmt::{self, Debug, Display, Formatter};
-use std::ptr::{null_mut, NonNull};
+use std::ptr::{self, null_mut, NonNull};
/// Wrapper around an opaque C `ANativeWindow`.
#[derive(PartialEq, Eq)]
@@ -60,6 +67,132 @@
let format = unsafe { ANativeWindow_getFormat(self.0.as_ptr()) };
format.try_into().map_err(|_| ErrorCode(format))
}
+
+ /// Changes the format and size of the window buffers.
+ ///
+ /// The width and height control the number of pixels in the buffers, not the dimensions of the
+ /// window on screen. If these are different than the window's physical size, then its buffer
+ /// will be scaled to match that size when compositing it to the screen. The width and height
+ /// must be either both zero or both non-zero. If both are 0 then the window's base value will
+ /// come back in force.
+ pub fn set_buffers_geometry(
+ &mut self,
+ width: i32,
+ height: i32,
+ format: AHardwareBuffer_Format::Type,
+ ) -> Result<(), ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let status = unsafe {
+ ANativeWindow_setBuffersGeometry(
+ self.0.as_ptr(),
+ width,
+ height,
+ format.try_into().expect("Invalid format"),
+ )
+ };
+
+ if status == 0 {
+ Ok(())
+ } else {
+ Err(ErrorCode(status))
+ }
+ }
+
+ /// Sets a transfom that will be applied to future buffers posted to the window.
+ pub fn set_buffers_transform(&mut self, transform: Transform) -> Result<(), ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let status =
+ unsafe { ANativeWindow_setBuffersTransform(self.0.as_ptr(), transform.bits() as i32) };
+
+ if status == 0 {
+ Ok(())
+ } else {
+ Err(ErrorCode(status))
+ }
+ }
+
+ /// Sets the data space that will be applied to future buffers posted to the window.
+ pub fn set_buffers_data_space(&mut self, data_space: ADataSpace) -> Result<(), ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let status = unsafe { ANativeWindow_setBuffersDataSpace(self.0.as_ptr(), data_space.0) };
+
+ if status == 0 {
+ Ok(())
+ } else {
+ Err(ErrorCode(status))
+ }
+ }
+
+ /// Gets the data space of the buffers in the window.
+ pub fn get_buffers_data_space(&mut self) -> Result<ADataSpace, ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let data_space = unsafe { ANativeWindow_getBuffersDataSpace(self.0.as_ptr()) };
+
+ if data_space < 0 {
+ Err(ErrorCode(data_space))
+ } else {
+ Ok(ADataSpace(data_space))
+ }
+ }
+
+ /// Gets the default data space of the buffers in the window as set by the consumer.
+ pub fn get_buffers_default_data_space(&mut self) -> Result<ADataSpace, ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let data_space = unsafe { ANativeWindow_getBuffersDefaultDataSpace(self.0.as_ptr()) };
+
+ if data_space < 0 {
+ Err(ErrorCode(data_space))
+ } else {
+ Ok(ADataSpace(data_space))
+ }
+ }
+
+ /// Locks the window's next drawing surface for writing, and returns it.
+ pub fn lock(&mut self, bounds: Option<&mut ARect>) -> Result<Buffer, ErrorCode> {
+ let mut buffer = buffer::EMPTY;
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it. The other pointers must be valid because the come from
+ // references, and aren't retained after the function returns.
+ let status = unsafe {
+ ANativeWindow_lock(
+ self.0.as_ptr(),
+ &mut buffer,
+ bounds.map(ptr::from_mut).unwrap_or(null_mut()),
+ )
+ };
+ if status != 0 {
+ return Err(ErrorCode(status));
+ }
+
+ Ok(Buffer::new(buffer, self))
+ }
+
+ /// Unlocks the window's drawing surface which was previously locked, posting the new buffer to
+ /// the display.
+ ///
+ /// This shouldn't be called directly but via the [`Buffer`], hence is not public here.
+ fn unlock_and_post(&mut self) -> Result<(), ErrorCode> {
+ // SAFETY: The ANativeWindow pointer we pass is guaranteed to be non-null and valid because
+ // it must have been allocated by `ANativeWindow_allocate` or `ANativeWindow_readFromParcel`
+ // and we have not yet released it.
+ let status = unsafe { ANativeWindow_unlockAndPost(self.0.as_ptr()) };
+ if status == 0 {
+ Ok(())
+ } else {
+ Err(ErrorCode(status))
+ }
+ }
}
impl Drop for Surface {
@@ -141,3 +274,19 @@
write!(f, "Error {}", self.0)
}
}
+
+bitflags! {
+ /// Transforms that can be applied to buffers as they are displayed to a window.
+ #[derive(Copy, Clone, Debug, Eq, PartialEq)]
+ pub struct Transform: u32 {
+ const MIRROR_HORIZONTAL = 0x01;
+ const MIRROR_VERTICAL = 0x02;
+ const ROTATE_90 = 0x04;
+ }
+}
+
+impl Transform {
+ pub const IDENTITY: Self = Self::empty();
+ pub const ROTATE_180: Self = Self::MIRROR_HORIZONTAL.union(Self::MIRROR_VERTICAL);
+ pub const ROTATE_270: Self = Self::ROTATE_180.union(Self::ROTATE_90);
+}
diff --git a/libs/nativewindow/rust/src/surface/buffer.rs b/libs/nativewindow/rust/src/surface/buffer.rs
new file mode 100644
index 0000000..a2d74d4
--- /dev/null
+++ b/libs/nativewindow/rust/src/surface/buffer.rs
@@ -0,0 +1,68 @@
+// 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 super::{ErrorCode, Surface};
+use nativewindow_bindgen::{AHardwareBuffer_Format, ANativeWindow_Buffer};
+use std::ptr::null_mut;
+
+/// An empty `ANativeWindow_Buffer`.
+pub const EMPTY: ANativeWindow_Buffer = ANativeWindow_Buffer {
+ width: 0,
+ height: 0,
+ stride: 0,
+ format: 0,
+ bits: null_mut(),
+ reserved: [0; 6],
+};
+
+/// Rust wrapper for `ANativeWindow_Buffer`, representing a locked buffer from a [`Surface`].
+pub struct Buffer<'a> {
+ /// The wrapped `ANativeWindow_Buffer`.
+ pub buffer: ANativeWindow_Buffer,
+ surface: &'a mut Surface,
+}
+
+impl<'a> Buffer<'a> {
+ pub(crate) fn new(buffer: ANativeWindow_Buffer, surface: &'a mut Surface) -> Self {
+ Self { buffer, surface }
+ }
+
+ /// Unlocks the window's drawing surface which was previously locked to create this buffer,
+ /// posting the buffer to the display.
+ pub fn unlock_and_post(self) -> Result<(), ErrorCode> {
+ self.surface.unlock_and_post()
+ }
+
+ /// The number of pixels that are shown horizontally.
+ pub fn width(&self) -> i32 {
+ self.buffer.width
+ }
+
+ /// The number of pixels that are shown vertically.
+ pub fn height(&self) -> i32 {
+ self.buffer.height
+ }
+
+ /// The number of pixels that a line in the buffer takes in memory.
+ ///
+ /// This may be greater than the width.
+ pub fn stride(&self) -> i32 {
+ self.buffer.stride
+ }
+
+ /// The pixel format of the buffer.
+ pub fn format(&self) -> Result<AHardwareBuffer_Format::Type, ErrorCode> {
+ self.buffer.format.try_into().map_err(|_| ErrorCode(self.buffer.format))
+ }
+}
diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS
index 66e1aa1..17ab29f 100644
--- a/libs/renderengine/OWNERS
+++ b/libs/renderengine/OWNERS
@@ -5,4 +5,6 @@
djsollen@google.com
jreck@google.com
lpy@google.com
+nscobie@google.com
+sallyqi@google.com
scroggo@google.com
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index 659666d..a687a37 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -69,6 +69,7 @@
static_libs: [
"libsensor_flags_c_lib",
+ "android.permission.flags-aconfig-cc",
],
export_include_dirs: ["include"],
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index a1549ea..eddd568 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -21,6 +21,7 @@
#include <binder/AppOpsManager.h>
#include <binder/IPermissionController.h>
#include <binder/IServiceManager.h>
+#include <android_permission_flags.h>
/*
* The permission to use for activity recognition sensors (like step counter).
@@ -121,7 +122,9 @@
break;
case SENSOR_TYPE_HEART_RATE: {
mStringType = SENSOR_STRING_TYPE_HEART_RATE;
- mRequiredPermission = SENSOR_PERMISSION_BODY_SENSORS;
+ mRequiredPermission =
+ android::permission::flags::replace_body_sensor_permission_enabled() ?
+ SENSOR_PERMISSION_READ_HEART_RATE : SENSOR_PERMISSION_BODY_SENSORS;
AppOpsManager appOps;
mRequiredAppOp = appOps.permissionToOpCode(String16(mRequiredPermission));
mFlags |= SENSOR_FLAG_ON_CHANGE_MODE;
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 1ebe597..a9f7ced 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -89,14 +89,14 @@
uint64_t total = 0;
result.append("GraphicBufferAllocator buffers:\n");
const size_t count = list.size();
- StringAppendF(&result, "%14s | %11s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
+ StringAppendF(&result, "%18s | %12s | %18s | %s | %8s | %10s | %s\n", "Handle", "Size",
"W (Stride) x H", "Layers", "Format", "Usage", "Requestor");
for (size_t i = 0; i < count; i++) {
const alloc_rec_t& rec(list.valueAt(i));
std::string sizeStr = (rec.size)
? base::StringPrintf("%7.2f KiB", static_cast<double>(rec.size) / 1024.0)
: "unknown";
- StringAppendF(&result, "%14p | %11s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
+ StringAppendF(&result, "%18p | %12s | %4u (%4u) x %4u | %6u | %8X | 0x%8" PRIx64 " | %s\n",
list.keyAt(i), sizeStr.c_str(), rec.width, rec.stride, rec.height,
rec.layerCount, rec.format, rec.usage, rec.requestorName.c_str());
total += rec.size;
diff --git a/libs/ui/include/ui/DynamicDisplayInfo.h b/libs/ui/include/ui/DynamicDisplayInfo.h
index 0b77754..25a2b6e 100644
--- a/libs/ui/include/ui/DynamicDisplayInfo.h
+++ b/libs/ui/include/ui/DynamicDisplayInfo.h
@@ -53,6 +53,8 @@
ui::DisplayModeId preferredBootDisplayMode;
std::optional<ui::DisplayMode> getActiveDisplayMode() const;
+
+ bool hasArrSupport;
};
} // namespace android::ui
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 4c9c7b7..4366db5 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -51,6 +51,9 @@
float bottom = 0.0f;
constexpr bool isEmpty() const { return !(left < right && top < bottom); }
+
+ // a valid rectangle has a non negative width and height
+ inline bool isValid() const { return (getWidth() >= 0) && (getHeight() >= 0); }
};
inline bool operator==(const FloatRect& a, const FloatRect& b) {
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 2eb9330..2307b44 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -74,12 +74,10 @@
}
inline explicit Rect(const FloatRect& floatRect) {
- // Ideally we would use std::round, but we don't want to add an STL
- // dependency here, so we use an approximation
- left = static_cast<int32_t>(floatRect.left + 0.5f);
- top = static_cast<int32_t>(floatRect.top + 0.5f);
- right = static_cast<int32_t>(floatRect.right + 0.5f);
- bottom = static_cast<int32_t>(floatRect.bottom + 0.5f);
+ left = static_cast<int32_t>(std::round(floatRect.left));
+ top = static_cast<int32_t>(std::round(floatRect.top));
+ right = static_cast<int32_t>(std::round(floatRect.right));
+ bottom = static_cast<int32_t>(std::round(floatRect.bottom));
}
inline explicit Rect(const ui::Size& size) {
diff --git a/libs/ui/tests/Rect_test.cpp b/libs/ui/tests/Rect_test.cpp
index 9cc36bb..c3c8bd9 100644
--- a/libs/ui/tests/Rect_test.cpp
+++ b/libs/ui/tests/Rect_test.cpp
@@ -99,6 +99,16 @@
EXPECT_EQ(30, rect.right);
EXPECT_EQ(40, rect.bottom);
}
+
+ EXPECT_EQ(Rect(0, 1, -1, 0), Rect(FloatRect(0.f, 1.f, -1.f, 0.f)));
+ EXPECT_EQ(Rect(100000, 100000, -100000, -100000),
+ Rect(FloatRect(100000.f, 100000.f, -100000.f, -100000.f)));
+
+ // round down if < .5
+ EXPECT_EQ(Rect(0, 1, -1, 0), Rect(FloatRect(0.4f, 1.1f, -1.499f, 0.1f)));
+
+ // round up if >= .5
+ EXPECT_EQ(Rect(20, 20, -20, -20), Rect(FloatRect(19.5f, 19.9f, -19.5f, -19.9f)));
}
TEST(RectTest, makeInvalid) {
diff --git a/libs/ultrahdr/Android.bp b/libs/ultrahdr/Android.bp
deleted file mode 100644
index eda5ea4..0000000
--- a/libs/ultrahdr/Android.bp
+++ /dev/null
@@ -1,85 +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.
-
-package {
- // See: http://go/android-license-faq
- default_applicable_licenses: [
- "frameworks_native_license",
- "adobe_hdr_gain_map_license",
- ],
-}
-
-cc_library {
- name: "libultrahdr-deprecated",
- enabled: false,
- host_supported: true,
- vendor_available: true,
- export_include_dirs: ["include"],
- local_include_dirs: ["include"],
-
- srcs: [
- "icc.cpp",
- "jpegr.cpp",
- "gainmapmath.cpp",
- "jpegrutils.cpp",
- "multipictureformat.cpp",
- ],
-
- shared_libs: [
- "libimage_io",
- "libjpeg",
- "libjpegencoder",
- "libjpegdecoder",
- "liblog",
- "libutils",
- ],
-}
-
-cc_library {
- name: "libjpegencoder-deprecated",
- enabled: false,
- host_supported: true,
- vendor_available: true,
-
- shared_libs: [
- "libjpeg",
- "liblog",
- "libutils",
- ],
-
- export_include_dirs: ["include"],
-
- srcs: [
- "jpegencoderhelper.cpp",
- ],
-}
-
-cc_library {
- name: "libjpegdecoder-deprecated",
- enabled: false,
- host_supported: true,
- vendor_available: true,
-
- shared_libs: [
- "libjpeg",
- "liblog",
- "libutils",
- ],
-
- export_include_dirs: ["include"],
-
- srcs: [
- "jpegdecoderhelper.cpp",
- ],
-}
diff --git a/libs/ultrahdr/fuzzer/Android.bp b/libs/ultrahdr/fuzzer/Android.bp
deleted file mode 100644
index 8d9132f..0000000
--- a/libs/ultrahdr/fuzzer/Android.bp
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2023 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_defaults {
- name: "ultrahdr_fuzzer_defaults-deprecated",
- enabled: false,
- host_supported: true,
- shared_libs: [
- "libimage_io",
- "libjpeg",
- ],
- static_libs: [
- "libjpegdecoder",
- "libjpegencoder",
- "libultrahdr",
- "libutils",
- "liblog",
- ],
- target: {
- darwin: {
- enabled: false,
- },
- },
- fuzz_config: {
- cc: [
- "android-media-fuzzing-reports@google.com",
- ],
- description: "The fuzzers target the APIs of jpeg hdr",
- service_privilege: "constrained",
- users: "multi_user",
- fuzzed_code_usage: "future_version",
- vector: "local_no_privileges_required",
- },
-}
-
-cc_fuzz {
- name: "ultrahdr_enc_fuzzer-deprecated",
- enabled: false,
- defaults: ["ultrahdr_fuzzer_defaults"],
- srcs: [
- "ultrahdr_enc_fuzzer.cpp",
- ],
-}
-
-cc_fuzz {
- name: "ultrahdr_dec_fuzzer-deprecated",
- enabled: false,
- defaults: ["ultrahdr_fuzzer_defaults"],
- srcs: [
- "ultrahdr_dec_fuzzer.cpp",
- ],
-}
diff --git a/libs/ultrahdr/tests/Android.bp b/libs/ultrahdr/tests/Android.bp
deleted file mode 100644
index 00cc797..0000000
--- a/libs/ultrahdr/tests/Android.bp
+++ /dev/null
@@ -1,51 +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.
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_native_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_native_license"],
-}
-
-cc_test {
- name: "ultrahdr_unit_test-deprecated",
- enabled: false,
- test_suites: ["device-tests"],
- srcs: [
- "gainmapmath_test.cpp",
- "icchelper_test.cpp",
- "jpegr_test.cpp",
- "jpegencoderhelper_test.cpp",
- "jpegdecoderhelper_test.cpp",
- ],
- shared_libs: [
- "libimage_io",
- "libjpeg",
- "liblog",
- ],
- static_libs: [
- "libgmock",
- "libgtest",
- "libjpegdecoder",
- "libjpegencoder",
- "libultrahdr",
- "libutils",
- ],
- data: [
- "./data/*.*",
- ],
-}
diff --git a/libs/vibrator/TEST_MAPPING b/libs/vibrator/TEST_MAPPING
index d782b43..e206761 100644
--- a/libs/vibrator/TEST_MAPPING
+++ b/libs/vibrator/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "libvibrator_test"
}
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index bf0e38e..3be8ddc 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -599,6 +599,9 @@
driver_t* hnd = nullptr;
// ANGLE doesn't ship with GLES library, and thus we skip GLES driver.
+ // b/370113081: if there is no libEGL_angle.so in namespace ns, libEGL_angle.so in system
+ // partition will be loaded instead. If there is no libEGL_angle.so in system partition, no
+ // angle libs are loaded, and app that sets to use ANGLE will crash.
void* dso = load_angle("EGL", ns);
if (dso) {
initialize_api(dso, cnx, EGL);
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index 6e35041..6713a5c 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -2141,6 +2141,10 @@
}
egl_surface_t const* const s = get_surface(surface);
+ if (!s->getNativeWindow()) {
+ setError(EGL_BAD_SURFACE, EGL_FALSE);
+ return EGL_FALSE;
+ }
native_window_set_buffers_timestamp(s->getNativeWindow(), time);
return EGL_TRUE;
@@ -2405,7 +2409,7 @@
case 0:
return EGL_TRUE;
case -ENOENT:
- return setError(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
+ return setErrorQuiet(EGL_BAD_ACCESS, (EGLBoolean)EGL_FALSE);
case -ENOSYS:
return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE);
case -EINVAL:
diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp
index 5fbdc84..493459a 100644
--- a/services/inputflinger/InputFilterCallbacks.cpp
+++ b/services/inputflinger/InputFilterCallbacks.cpp
@@ -44,7 +44,8 @@
InputFilterThread(std::shared_ptr<IInputThreadCallback> callback) : mCallback(callback) {
mLooper = sp<Looper>::make(/*allowNonCallbacks=*/false);
mThread = std::make_unique<InputThread>(
- "InputFilter", [this]() { loopOnce(); }, [this]() { mLooper->wake(); });
+ "InputFilter", [this]() { loopOnce(); }, [this]() { mLooper->wake(); },
+ /*isInCriticalPath=*/false);
}
ndk::ScopedAStatus finish() override {
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
index 449eb45..7cf4e39 100644
--- a/services/inputflinger/InputThread.cpp
+++ b/services/inputflinger/InputThread.cpp
@@ -26,6 +26,16 @@
namespace {
+bool applyInputEventProfile(const Thread& thread) {
+#if defined(__ANDROID__)
+ return SetTaskProfiles(thread.getTid(), {"InputPolicy"});
+#else
+ // Since thread information is not available and there's no benefit of
+ // applying the task profile on host, return directly.
+ return true;
+#endif
+}
+
// Implementation of Thread from libutils.
class InputThreadImpl : public Thread {
public:
@@ -45,12 +55,13 @@
} // namespace
-InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake)
- : mName(name), mThreadWake(wake) {
+InputThread::InputThread(std::string name, std::function<void()> loop, std::function<void()> wake,
+ bool isInCriticalPath)
+ : mThreadWake(wake) {
mThread = sp<InputThreadImpl>::make(loop);
- mThread->run(mName.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
- if (input_flags::enable_input_policy_profile()) {
- if (!applyInputEventProfile()) {
+ mThread->run(name.c_str(), ANDROID_PRIORITY_URGENT_DISPLAY);
+ if (input_flags::enable_input_policy_profile() && isInCriticalPath) {
+ if (!applyInputEventProfile(*mThread)) {
LOG(ERROR) << "Couldn't apply input policy profile for " << name;
}
}
@@ -74,14 +85,4 @@
#endif
}
-bool InputThread::applyInputEventProfile() {
-#if defined(__ANDROID__)
- return SetTaskProfiles(mThread->getTid(), {"InputPolicy"});
-#else
- // Since thread information is not available and there's no benefit of
- // applying the task profile on host, return directly.
- return true;
-#endif
-}
-
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/inputflinger/dispatcher/DragState.h b/services/inputflinger/dispatcher/DragState.h
index 9809148..1ed6c29 100644
--- a/services/inputflinger/dispatcher/DragState.h
+++ b/services/inputflinger/dispatcher/DragState.h
@@ -17,6 +17,7 @@
#pragma once
#include <gui/WindowInfo.h>
+#include <input/Input.h>
#include <utils/StrongPointer.h>
#include <string>
@@ -25,8 +26,9 @@
namespace inputdispatcher {
struct DragState {
- DragState(const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t pointerId)
- : dragWindow(windowHandle), pointerId(pointerId) {}
+ DragState(const sp<android::gui::WindowInfoHandle>& windowHandle, DeviceId deviceId,
+ int32_t pointerId)
+ : dragWindow(windowHandle), deviceId(deviceId), pointerId(pointerId) {}
void dump(std::string& dump, const char* prefix = "");
// The window being dragged.
@@ -37,6 +39,8 @@
bool isStartDrag = false;
// Indicate if the stylus button is down at the start of the drag.
bool isStylusButtonDownAtStart = false;
+ // Indicate which device started this drag and drop.
+ const DeviceId deviceId;
// Indicate which pointer id is tracked by the drag and drop.
const int32_t pointerId;
};
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index b0beeca..602904f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -993,7 +993,8 @@
return ALREADY_EXISTS;
}
mThread = std::make_unique<InputThread>(
- "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); });
+ "InputDispatcher", [this]() { dispatchOnce(); }, [this]() { mLooper->wake(); },
+ /*isInCriticalPath=*/true);
return OK;
}
@@ -1029,9 +1030,8 @@
const nsecs_t nextAnrCheck = processAnrsLocked();
nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
- if (input_flags::enable_per_device_input_latency_metrics()) {
- const nsecs_t nextStatisticsPush = processLatencyStatisticsLocked();
- nextWakeupTime = std::min(nextWakeupTime, nextStatisticsPush);
+ if (mPerDeviceInputLatencyMetricsFlag) {
+ processLatencyStatisticsLocked();
}
// We are about to enter an infinitely long sleep, because we have no commands or
@@ -1116,9 +1116,8 @@
/**
* Check if enough time has passed since the last latency statistics push.
- * Return the time at which we should wake up next.
*/
-nsecs_t InputDispatcher::processLatencyStatisticsLocked() {
+void InputDispatcher::processLatencyStatisticsLocked() {
const nsecs_t currentTime = now();
// Log the atom recording latency statistics if more than 6 hours passed from the last
// push
@@ -1126,7 +1125,6 @@
mInputEventTimelineProcessor->pushLatencyStatistics();
mLastStatisticPushTime = currentTime;
}
- return mLastStatisticPushTime + LATENCY_STATISTICS_PUSH_INTERVAL;
}
std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(
@@ -2897,7 +2895,8 @@
}
void InputDispatcher::addDragEventLocked(const MotionEntry& entry) {
- if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId) {
+ if (!mDragState || mDragState->dragWindow->getInfo()->displayId != entry.displayId ||
+ mDragState->deviceId != entry.deviceId) {
return;
}
@@ -4548,7 +4547,7 @@
newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry);
}
- if (input_flags::enable_per_device_input_latency_metrics()) {
+ if (mPerDeviceInputLatencyMetricsFlag) {
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
!mInputFilterEnabled) {
@@ -4795,6 +4794,39 @@
}
}
+bool InputDispatcher::shouldRejectInjectedMotionLocked(const MotionEvent& motionEvent,
+ DeviceId deviceId,
+ ui::LogicalDisplayId displayId,
+ std::optional<gui::Uid> targetUid,
+ int32_t flags) {
+ // Don't verify targeted injection, since it will only affect the caller's
+ // window, and the windows are typically destroyed at the end of the test.
+ if (targetUid.has_value()) {
+ return false;
+ }
+
+ // Verify all other injected streams, whether the injection is coming from apps or from
+ // input filter. Print an error if the stream becomes inconsistent with this event.
+ // An inconsistent injected event sent could cause a crash in the later stages of
+ // dispatching pipeline.
+ auto [it, _] = mInputFilterVerifiersByDisplay.try_emplace(displayId,
+ std::string("Injection on ") +
+ displayId.toString());
+ InputVerifier& verifier = it->second;
+
+ Result<void> result =
+ verifier.processMovement(deviceId, motionEvent.getSource(), motionEvent.getAction(),
+ motionEvent.getPointerCount(),
+ motionEvent.getPointerProperties(),
+ motionEvent.getSamplePointerCoords(), flags);
+ if (!result.ok()) {
+ logDispatchStateLocked();
+ LOG(ERROR) << "Inconsistent event: " << motionEvent << ", reason: " << result.error();
+ return true;
+ }
+ return false;
+}
+
InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* event,
std::optional<gui::Uid> targetUid,
InputEventInjectionSync syncMode,
@@ -4905,32 +4937,10 @@
mLock.lock();
- {
- // Verify all injected streams, whether the injection is coming from apps or from
- // input filter. Print an error if the stream becomes inconsistent with this event.
- // An inconsistent injected event sent could cause a crash in the later stages of
- // dispatching pipeline.
- auto [it, _] =
- mInputFilterVerifiersByDisplay.try_emplace(displayId,
- std::string("Injection on ") +
- displayId.toString());
- InputVerifier& verifier = it->second;
-
- Result<void> result =
- verifier.processMovement(resolvedDeviceId, motionEvent.getSource(),
- motionEvent.getAction(),
- motionEvent.getPointerCount(),
- motionEvent.getPointerProperties(),
- motionEvent.getSamplePointerCoords(), flags);
- if (!result.ok()) {
- logDispatchStateLocked();
- LOG(ERROR) << "Inconsistent event: " << motionEvent
- << ", reason: " << result.error();
- if (policyFlags & POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY) {
- mLock.unlock();
- return InputEventInjectionResult::FAILED;
- }
- }
+ if (shouldRejectInjectedMotionLocked(motionEvent, resolvedDeviceId, displayId,
+ targetUid, flags)) {
+ mLock.unlock();
+ return InputEventInjectionResult::FAILED;
}
const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
@@ -5822,7 +5832,7 @@
}
// Track the pointer id for drag window and generate the drag state.
const size_t id = pointers.begin()->id;
- mDragState = std::make_unique<DragState>(toWindowHandle, id);
+ mDragState = std::make_unique<DragState>(toWindowHandle, deviceId, id);
}
// Synthesize cancel for old window and down for new window.
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index d90b9de..fade853 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -16,6 +16,8 @@
#pragma once
+#include <com_android_input_flags.h>
+
#include "AnrTracker.h"
#include "CancelationOptions.h"
#include "DragState.h"
@@ -297,6 +299,10 @@
// Event injection and synchronization.
std::condition_variable mInjectionResultAvailable;
+ bool shouldRejectInjectedMotionLocked(const MotionEvent& motion, DeviceId deviceId,
+ ui::LogicalDisplayId displayId,
+ std::optional<gui::Uid> targetUid, int32_t flags)
+ REQUIRES(mLock);
void setInjectionResult(const EventEntry& entry,
android::os::InputEventInjectionResult injectionResult);
void transformMotionEntryForInjectionLocked(MotionEntry&,
@@ -327,7 +333,7 @@
std::chrono::nanoseconds mMonitorDispatchingTimeout GUARDED_BY(mLock);
nsecs_t processAnrsLocked() REQUIRES(mLock);
- nsecs_t processLatencyStatisticsLocked() REQUIRES(mLock);
+ void processLatencyStatisticsLocked() REQUIRES(mLock);
std::chrono::nanoseconds getDispatchingTimeoutLocked(
const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
@@ -741,6 +747,10 @@
sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
+
+ /** Stores the value of the input flag for per device input latency metrics. */
+ const bool mPerDeviceInputLatencyMetricsFlag =
+ com::android::input::flags::enable_per_device_input_latency_metrics();
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
index 3c3c15a..4f61885 100644
--- a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
@@ -41,7 +41,7 @@
: mBackend(std::move(innerBackend)),
mTracerThread(
"InputTracer", [this]() { threadLoop(); },
- [this]() { mThreadWakeCondition.notify_all(); }) {}
+ [this]() { mThreadWakeCondition.notify_all(); }, /*isInCriticalPath=*/false) {}
template <typename Backend>
ThreadedBackend<Backend>::~ThreadedBackend() {
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 2f6c6d7..756a29b 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -96,6 +96,10 @@
// The key remapping has changed.
KEY_REMAPPING = 1u << 14,
+ // The mouse settings changed, this includes mouse reverse vertical scrolling and swap
+ // primary button.
+ MOUSE_SETTINGS = 1u << 15,
+
// All devices must be reopened.
MUST_REOPEN = 1u << 31,
};
@@ -252,6 +256,15 @@
// Keycodes to be remapped.
std::map<int32_t /* fromKeyCode */, int32_t /* toKeyCode */> keyRemapping;
+ // True if the external mouse should have its vertical scrolling reversed, so that rotating the
+ // wheel downwards scrolls the content upwards.
+ bool mouseReverseVerticalScrollingEnabled;
+
+ // True if the connected mouse should have its primary button (default: left click) swapped,
+ // so that the right click will be the primary action button and the left click will be the
+ // secondary action.
+ bool mouseSwapPrimaryButtonEnabled;
+
InputReaderConfiguration()
: virtualKeyQuietTime(0),
defaultPointerDisplayId(ui::LogicalDisplayId::DEFAULT),
@@ -282,7 +295,9 @@
shouldNotifyTouchpadHardwareState(false),
touchpadRightClickZoneEnabled(false),
stylusButtonMotionEventsEnabled(true),
- stylusPointerIconEnabled(false) {}
+ stylusPointerIconEnabled(false),
+ mouseReverseVerticalScrollingEnabled(false),
+ mouseSwapPrimaryButtonEnabled(false) {}
std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
index fcd913d..ed92b8f 100644
--- a/services/inputflinger/include/InputThread.h
+++ b/services/inputflinger/include/InputThread.h
@@ -28,17 +28,15 @@
*/
class InputThread {
public:
- explicit InputThread(std::string name, std::function<void()> loop,
- std::function<void()> wake = nullptr);
+ explicit InputThread(std::string name, std::function<void()> loop, std::function<void()> wake,
+ bool isInCriticalPath);
virtual ~InputThread();
bool isCallingThread();
private:
- std::string mName;
std::function<void()> mThreadWake;
sp<Thread> mThread;
- bool applyInputEventProfile();
};
} // namespace android
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index b76e8c5..b3cd35c 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -90,10 +90,14 @@
"libstatslog",
"libstatspull",
"libutils",
+ "libstatssocket",
],
static_libs: [
"libchrome-gestures",
"libui-types",
+ "libexpresslog",
+ "libtextclassifier_hash_static",
+ "libstatslog_express",
],
header_libs: [
"libbatteryservice_headers",
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0865eed..f8cd973 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -659,6 +659,21 @@
}
bool EventHub::Device::hasKeycodeLocked(int keycode) const {
+ if (hasKeycodeInternalLocked(keycode)) {
+ return true;
+ }
+ if (!keyMap.haveKeyCharacterMap()) {
+ return false;
+ }
+ for (auto& fromKey : getKeyCharacterMap()->findKeyCodesMappedToKeyCode(keycode)) {
+ if (hasKeycodeInternalLocked(fromKey)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool EventHub::Device::hasKeycodeInternalLocked(int keycode) const {
if (!keyMap.haveKeyLayout()) {
return false;
}
@@ -676,7 +691,6 @@
if (usageCodes.size() > 0 && mscBitmask.test(MSC_SCAN)) {
return true;
}
-
return false;
}
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 6185f1a..02eeb0a 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -691,16 +691,6 @@
return result;
}
-void InputDevice::updateMetaState(int32_t keyCode) {
- first_in_mappers<bool>([keyCode](InputMapper& mapper) {
- if (sourcesMatchMask(mapper.getSources(), AINPUT_SOURCE_KEYBOARD) &&
- mapper.updateMetaState(keyCode)) {
- return std::make_optional(true);
- }
- return std::optional<bool>();
- });
-}
-
void InputDevice::bumpGeneration() {
mGeneration = mContext->bumpGeneration();
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index e579390..ab27042 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -122,7 +122,8 @@
return ALREADY_EXISTS;
}
mThread = std::make_unique<InputThread>(
- "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); });
+ "InputReader", [this]() { loopOnce(); }, [this]() { mEventHub->wake(); },
+ /*isInCriticalPath=*/true);
return OK;
}
@@ -583,18 +584,9 @@
void InputReader::toggleCapsLockState(int32_t deviceId) {
std::scoped_lock _l(mLock);
- InputDevice* device = findInputDeviceLocked(deviceId);
- if (!device) {
- ALOGW("Ignoring toggleCapsLock for unknown deviceId %" PRId32 ".", deviceId);
- return;
+ if (mKeyboardClassifier->getKeyboardType(deviceId) == KeyboardType::ALPHABETIC) {
+ updateLedMetaStateLocked(mLedMetaState ^ AMETA_CAPS_LOCK_ON);
}
-
- if (device->isIgnored()) {
- ALOGW("Ignoring toggleCapsLock for ignored deviceId %" PRId32 ".", deviceId);
- return;
- }
-
- device->updateMetaState(AKEYCODE_CAPS_LOCK);
}
bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask,
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index edc3037..dffd8e3 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -680,6 +680,7 @@
void configureFd();
void populateAbsoluteAxisStates();
bool hasKeycodeLocked(int keycode) const;
+ bool hasKeycodeInternalLocked(int keycode) const;
void loadConfigurationLocked();
bool loadVirtualKeyMapLocked();
status_t loadKeyMapLocked();
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 62cc4da..8958d9e 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -122,8 +122,6 @@
std::optional<int32_t> getLightPlayerId(int32_t lightId);
int32_t getMetaState();
- void updateMetaState(int32_t keyCode);
-
void setKeyboardType(KeyboardType keyboardType);
void bumpGeneration();
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 20cdb59..630bd9b 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -164,6 +164,10 @@
changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) {
configureOnChangePointerSpeed(readerConfig);
}
+
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::MOUSE_SETTINGS)) {
+ configureOnChangeMouseSettings(readerConfig);
+ }
return out;
}
@@ -275,7 +279,12 @@
PointerCoords pointerCoords;
pointerCoords.clear();
- float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
+ // A negative value represents inverted scrolling direction.
+ // Applies only if the source is a mouse.
+ const bool isMouse =
+ (mSource == AINPUT_SOURCE_MOUSE) || (mSource == AINPUT_SOURCE_MOUSE_RELATIVE);
+ const int scrollingDirection = (mMouseReverseVerticalScrolling && isMouse) ? -1 : 1;
+ float vscroll = scrollingDirection * mCursorScrollAccumulator.getRelativeVWheel();
float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
bool scrolled = vscroll != 0 || hscroll != 0;
@@ -537,4 +546,9 @@
bumpGeneration();
}
+void CursorInputMapper::configureOnChangeMouseSettings(const InputReaderConfiguration& config) {
+ mMouseReverseVerticalScrolling = config.mouseReverseVerticalScrollingEnabled;
+ mCursorButtonAccumulator.setSwapLeftRightButtons(config.mouseSwapPrimaryButtonEnabled);
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 3fc370c..403e96d 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -121,6 +121,7 @@
nsecs_t mLastEventTime;
const bool mEnableNewMousePointerBallistics;
+ bool mMouseReverseVerticalScrolling = false;
explicit CursorInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
@@ -129,6 +130,7 @@
void configureOnPointerCapture(const InputReaderConfiguration& config);
void configureOnChangePointerSpeed(const InputReaderConfiguration& config);
void configureOnChangeDisplayInfo(const InputReaderConfiguration& config);
+ void configureOnChangeMouseSettings(const InputReaderConfiguration& config);
[[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 627df7f..9e9ed2d 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -109,10 +109,6 @@
return 0;
}
-bool InputMapper::updateMetaState(int32_t keyCode) {
- return false;
-}
-
std::list<NotifyArgs> InputMapper::updateExternalStylusState(const StylusState& state) {
return {};
}
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 75cc4bb..d4a86ac 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -111,11 +111,6 @@
virtual std::optional<int32_t> getLightPlayerId(int32_t lightId) { return std::nullopt; }
virtual int32_t getMetaState();
- /**
- * Process the meta key and update the global meta state when changed.
- * Return true if the meta key could be handled by the InputMapper.
- */
- virtual bool updateMetaState(int32_t keyCode);
[[nodiscard]] virtual std::list<NotifyArgs> updateExternalStylusState(const StylusState& state);
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 38dcd65..567a3e2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -390,15 +390,6 @@
return mMetaState;
}
-bool KeyboardInputMapper::updateMetaState(int32_t keyCode) {
- if (!android::isMetaKey(keyCode) || !getDeviceContext().hasKeyCode(keyCode)) {
- return false;
- }
-
- updateMetaStateIfNeeded(keyCode, false);
- return true;
-}
-
bool KeyboardInputMapper::updateMetaStateIfNeeded(int32_t keyCode, bool down) {
int32_t oldMetaState = mMetaState;
int32_t newMetaState = android::updateMetaState(keyCode, down, oldMetaState);
@@ -434,17 +425,21 @@
mMetaState &= ~(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON);
mMetaState |= getContext()->getLedMetaState();
- constexpr int32_t META_NUM = 3;
- const std::vector<int32_t> keyCodes{AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK,
- AKEYCODE_SCROLL_LOCK};
- const std::array<int32_t, META_NUM> metaCodes = {AMETA_CAPS_LOCK_ON, AMETA_NUM_LOCK_ON,
- AMETA_SCROLL_LOCK_ON};
- std::array<uint8_t, META_NUM> flags = {0, 0, 0};
- bool hasKeyLayout = getDeviceContext().markSupportedKeyCodes(keyCodes, flags.data());
+ std::vector<int32_t> keyCodesToCheck{AKEYCODE_NUM_LOCK, AKEYCODE_SCROLL_LOCK};
+ std::vector<int32_t> metaCodes{AMETA_NUM_LOCK_ON, AMETA_SCROLL_LOCK_ON};
+ // Check for physical CapsLock key only for non-alphabetic keyboards. For Alphabetic
+ // keyboards, we will allow Caps Lock even if there is no physical CapsLock key.
+ if (getDeviceContext().getKeyboardType() != KeyboardType::ALPHABETIC) {
+ keyCodesToCheck.push_back(AKEYCODE_CAPS_LOCK);
+ metaCodes.push_back(AMETA_CAPS_LOCK_ON);
+ }
+ size_t size = keyCodesToCheck.size();
+ std::vector<uint8_t> flags(size, 0);
+ bool hasKeyLayout = getDeviceContext().markSupportedKeyCodes(keyCodesToCheck, flags.data());
// If the device doesn't have the physical meta key it shouldn't generate the corresponding
// meta state.
if (hasKeyLayout) {
- for (int i = 0; i < META_NUM; i++) {
+ for (size_t i = 0; i < size; i++) {
if (!flags[i]) {
mMetaState &= ~metaCodes[i];
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 2df0b85..10bd424 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -45,7 +45,6 @@
int32_t getKeyCodeForKeyLocation(int32_t locationKeyCode) const override;
int32_t getMetaState() override;
- bool updateMetaState(int32_t keyCode) override;
std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
void updateLedState(bool reset) override;
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index b72cc6e..c633b49 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -20,6 +20,8 @@
#include "RotaryEncoderInputMapper.h"
+#include <Counter.h>
+#include <com_android_input_flags.h>
#include <utils/Timers.h>
#include <optional>
@@ -27,14 +29,26 @@
namespace android {
+using android::expresslog::Counter;
+
+constexpr float kDefaultResolution = 0;
constexpr float kDefaultScaleFactor = 1.0f;
+constexpr int32_t kDefaultMinRotationsToLog = 3;
RotaryEncoderInputMapper::RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig)
+ : RotaryEncoderInputMapper(deviceContext, readerConfig,
+ Counter::logIncrement /* telemetryLogCounter */) {}
+
+RotaryEncoderInputMapper::RotaryEncoderInputMapper(
+ InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig,
+ std::function<void(const char*, int64_t)> telemetryLogCounter)
: InputMapper(deviceContext, readerConfig),
mSource(AINPUT_SOURCE_ROTARY_ENCODER),
mScalingFactor(kDefaultScaleFactor),
- mOrientation(ui::ROTATION_0) {}
+ mResolution(kDefaultResolution),
+ mOrientation(ui::ROTATION_0),
+ mTelemetryLogCounter(telemetryLogCounter) {}
RotaryEncoderInputMapper::~RotaryEncoderInputMapper() {}
@@ -51,6 +65,7 @@
if (!res.has_value()) {
ALOGW("Rotary Encoder device configuration file didn't specify resolution!\n");
}
+ mResolution = res.value_or(kDefaultResolution);
std::optional<float> scalingFactor = config.getFloat("device.scalingFactor");
if (!scalingFactor.has_value()) {
ALOGW("Rotary Encoder device configuration file didn't specify scaling factor,"
@@ -59,7 +74,22 @@
}
mScalingFactor = scalingFactor.value_or(kDefaultScaleFactor);
info.addMotionRange(AMOTION_EVENT_AXIS_SCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f,
- res.value_or(0.0f) * mScalingFactor);
+ mResolution * mScalingFactor);
+
+ if (com::android::input::flags::rotary_input_telemetry()) {
+ mMinRotationsToLog = config.getInt("rotary_encoder.min_rotations_to_log");
+ if (!mMinRotationsToLog.has_value()) {
+ ALOGI("Rotary Encoder device configuration file didn't specify min log rotation.");
+ } else if (*mMinRotationsToLog <= 0) {
+ ALOGE("Rotary Encoder device configuration specified non-positive min log rotation "
+ ": %d. Telemetry logging of rotations disabled.",
+ *mMinRotationsToLog);
+ mMinRotationsToLog = {};
+ } else {
+ ALOGD("Rotary Encoder telemetry enabled. mMinRotationsToLog=%d",
+ *mMinRotationsToLog);
+ }
+ }
}
}
@@ -121,10 +151,29 @@
return out;
}
+void RotaryEncoderInputMapper::logScroll(float scroll) {
+ if (mResolution <= 0 || !mMinRotationsToLog) return;
+
+ mUnloggedScrolls += fabs(scroll);
+
+ // unitsPerRotation = (2 * PI * radians) * (units per radian (i.e. resolution))
+ const float unitsPerRotation = 2 * M_PI * mResolution;
+ const float scrollsPerMinRotationsToLog = *mMinRotationsToLog * unitsPerRotation;
+ const int32_t numMinRotationsToLog =
+ static_cast<int32_t>(mUnloggedScrolls / scrollsPerMinRotationsToLog);
+ mUnloggedScrolls = std::fmod(mUnloggedScrolls, scrollsPerMinRotationsToLog);
+ if (numMinRotationsToLog) {
+ mTelemetryLogCounter("input.value_rotary_input_device_full_rotation_count",
+ numMinRotationsToLog * (*mMinRotationsToLog));
+ }
+}
+
std::list<NotifyArgs> RotaryEncoderInputMapper::sync(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out;
float scroll = mRotaryEncoderScrollAccumulator.getRelativeVWheel();
+ logScroll(scroll);
+
if (mSlopController) {
scroll = mSlopController->consumeEvent(when, scroll);
}
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index 7e80415..d74ced1 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -46,13 +46,39 @@
int32_t mSource;
float mScalingFactor;
+ /** Units per rotation, provided via the `device.res` IDC property. */
+ float mResolution;
ui::Rotation mOrientation;
+ /**
+ * The minimum number of rotations to log for telemetry.
+ * Provided via `rotary_encoder.min_rotations_to_log` IDC property. If no value is provided in
+ * the IDC file, or if a non-positive value is provided, the telemetry will be disabled, and
+ * this value is set to the empty optional.
+ */
+ std::optional<int32_t> mMinRotationsToLog;
+ /**
+ * A function to log count for telemetry.
+ * The char* is the logging key, and the int64_t is the value to log.
+ * Abstracting the actual logging APIs via this function is helpful for simple unit testing.
+ */
+ std::function<void(const char*, int64_t)> mTelemetryLogCounter;
ui::LogicalDisplayId mDisplayId = ui::LogicalDisplayId::INVALID;
std::unique_ptr<SlopController> mSlopController;
+ /** Amount of raw scrolls (pre-slop) not yet logged for telemetry. */
+ float mUnloggedScrolls = 0;
+
explicit RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
+
+ /** This is a test constructor that allows injecting the expresslog Counter logic. */
+ RotaryEncoderInputMapper(InputDeviceContext& deviceContext,
+ const InputReaderConfiguration& readerConfig,
+ std::function<void(const char*, int64_t)> expressLogCounter);
[[nodiscard]] std::list<NotifyArgs> sync(nsecs_t when, nsecs_t readTime);
+
+ /** Logs a given amount of scroll for telemetry. */
+ void logScroll(float scroll);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
index 9e722d4..456562c 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
@@ -47,6 +47,10 @@
mBtnTask = 0;
}
+void CursorButtonAccumulator::setSwapLeftRightButtons(bool shouldSwap) {
+ mSwapLeftRightButtons = shouldSwap;
+}
+
void CursorButtonAccumulator::process(const RawEvent& rawEvent) {
if (rawEvent.type == EV_KEY) {
switch (rawEvent.code) {
@@ -81,10 +85,12 @@
uint32_t CursorButtonAccumulator::getButtonState() const {
uint32_t result = 0;
if (mBtnLeft) {
- result |= AMOTION_EVENT_BUTTON_PRIMARY;
+ result |= mSwapLeftRightButtons ? AMOTION_EVENT_BUTTON_SECONDARY
+ : AMOTION_EVENT_BUTTON_PRIMARY;
}
if (mBtnRight) {
- result |= AMOTION_EVENT_BUTTON_SECONDARY;
+ result |= mSwapLeftRightButtons ? AMOTION_EVENT_BUTTON_PRIMARY
+ : AMOTION_EVENT_BUTTON_SECONDARY;
}
if (mBtnMiddle) {
result |= AMOTION_EVENT_BUTTON_TERTIARY;
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index 256b2bb..6990030 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -41,6 +41,8 @@
inline bool isExtraPressed() const { return mBtnExtra; }
inline bool isTaskPressed() const { return mBtnTask; }
+ void setSwapLeftRightButtons(bool shouldSwap);
+
private:
bool mBtnLeft;
bool mBtnRight;
@@ -51,6 +53,8 @@
bool mBtnExtra;
bool mBtnTask;
+ bool mSwapLeftRightButtons = false;
+
void clearButtons();
};
diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs
index e05e8e5..a8959d9 100644
--- a/services/inputflinger/rust/bounce_keys_filter.rs
+++ b/services/inputflinger/rust/bounce_keys_filter.rs
@@ -25,6 +25,7 @@
};
use input::KeyboardType;
use log::debug;
+use std::any::Any;
use std::collections::{HashMap, HashSet};
#[derive(Debug)]
@@ -134,6 +135,17 @@
self.next.destroy();
}
+ fn save(
+ &mut self,
+ state: HashMap<&'static str, Box<dyn Any + Send + Sync>>,
+ ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> {
+ self.next.save(state)
+ }
+
+ fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) {
+ self.next.restore(state);
+ }
+
fn dump(&mut self, dump_str: String) -> String {
let mut result = "Bounce Keys filter: \n".to_string();
result += &format!("\tthreshold = {:?}ns\n", self.bounce_key_threshold_ns);
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index e221244..39c3465 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -33,6 +33,8 @@
use crate::sticky_keys_filter::StickyKeysFilter;
use input::ModifierState;
use log::{error, info};
+use std::any::Any;
+use std::collections::HashMap;
use std::sync::{Arc, Mutex, RwLock};
/// Virtual keyboard device ID
@@ -43,6 +45,11 @@
fn notify_key(&mut self, event: &KeyEvent);
fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]);
fn destroy(&mut self);
+ fn save(
+ &mut self,
+ state: HashMap<&'static str, Box<dyn Any + Send + Sync>>,
+ ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>>;
+ fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>);
fn dump(&mut self, dump_str: String) -> String;
}
@@ -105,6 +112,7 @@
fn notifyConfigurationChanged(&self, config: &InputFilterConfiguration) -> binder::Result<()> {
{
let mut state = self.state.lock().unwrap();
+ let saved_state = state.first_filter.save(HashMap::new());
state.first_filter.destroy();
let mut first_filter: Box<dyn Filter + Send + Sync> =
Box::new(BaseFilter::new(self.callbacks.clone()));
@@ -138,6 +146,7 @@
);
}
state.first_filter = first_filter;
+ state.first_filter.restore(&saved_state);
}
Result::Ok(())
}
@@ -175,6 +184,18 @@
// do nothing
}
+ fn save(
+ &mut self,
+ state: HashMap<&'static str, Box<dyn Any + Send + Sync>>,
+ ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> {
+ // do nothing
+ state
+ }
+
+ fn restore(&mut self, _state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) {
+ // do nothing
+ }
+
fn dump(&mut self, dump_str: String) -> String {
// do nothing
dump_str
@@ -367,6 +388,8 @@
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
DeviceInfo::DeviceInfo, KeyEvent::KeyEvent,
};
+ use std::any::Any;
+ use std::collections::HashMap;
use std::sync::{Arc, RwLock, RwLockWriteGuard};
#[derive(Default)]
@@ -415,6 +438,16 @@
fn destroy(&mut self) {
self.inner().is_destroy_called = true;
}
+ fn save(
+ &mut self,
+ state: HashMap<&'static str, Box<dyn Any + Send + Sync>>,
+ ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> {
+ // do nothing
+ state
+ }
+ fn restore(&mut self, _state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) {
+ // do nothing
+ }
fn dump(&mut self, dump_str: String) -> String {
// do nothing
dump_str
diff --git a/services/inputflinger/rust/slow_keys_filter.rs b/services/inputflinger/rust/slow_keys_filter.rs
index 8830aac..085e80e 100644
--- a/services/inputflinger/rust/slow_keys_filter.rs
+++ b/services/inputflinger/rust/slow_keys_filter.rs
@@ -26,7 +26,8 @@
};
use input::KeyboardType;
use log::debug;
-use std::collections::HashSet;
+use std::any::Any;
+use std::collections::{HashMap, HashSet};
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
// Policy flags from Input.h
@@ -187,6 +188,19 @@
slow_filter.next.destroy();
}
+ fn save(
+ &mut self,
+ state: HashMap<&'static str, Box<dyn Any + Send + Sync>>,
+ ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> {
+ let mut slow_filter = self.write_inner();
+ slow_filter.next.save(state)
+ }
+
+ fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) {
+ let mut slow_filter = self.write_inner();
+ slow_filter.next.restore(state);
+ }
+
fn dump(&mut self, dump_str: String) -> String {
let mut slow_filter = self.write_inner();
let mut result = "Slow Keys filter: \n".to_string();
diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
index 161a5fc..7a1d0ec 100644
--- a/services/inputflinger/rust/sticky_keys_filter.rs
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -24,7 +24,8 @@
DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
};
use input::ModifierState;
-use std::collections::HashSet;
+use std::any::Any;
+use std::collections::{HashMap, HashSet};
// Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h
const KEYCODE_ALT_LEFT: i32 = 57;
@@ -40,10 +41,17 @@
const KEYCODE_META_RIGHT: i32 = 118;
const KEYCODE_FUNCTION: i32 = 119;
const KEYCODE_NUM_LOCK: i32 = 143;
+static STICKY_KEYS_DATA: &str = "sticky_keys_data";
pub struct StickyKeysFilter {
next: Box<dyn Filter + Send + Sync>,
listener: ModifierStateListener,
+ data: Data,
+}
+
+#[derive(Default)]
+/// Data that will be saved and restored across configuration changes
+struct Data {
/// Tracking devices that contributed to the modifier state.
contributing_devices: HashSet<i32>,
/// State describing the current enabled modifiers. This contain both locked and non-locked
@@ -61,21 +69,15 @@
next: Box<dyn Filter + Send + Sync>,
listener: ModifierStateListener,
) -> StickyKeysFilter {
- Self {
- next,
- listener,
- contributing_devices: HashSet::new(),
- modifier_state: ModifierState::None,
- locked_modifier_state: ModifierState::None,
- }
+ Self { next, listener, data: Default::default() }
}
}
impl Filter for StickyKeysFilter {
fn notify_key(&mut self, event: &KeyEvent) {
let up = event.action == KeyEventAction::UP;
- let mut modifier_state = self.modifier_state;
- let mut locked_modifier_state = self.locked_modifier_state;
+ let mut modifier_state = self.data.modifier_state;
+ let mut locked_modifier_state = self.data.locked_modifier_state;
if !is_ephemeral_modifier_key(event.keyCode) {
// If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like
// CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with
@@ -93,7 +95,7 @@
}
} else if up {
// Update contributing devices to track keyboards
- self.contributing_devices.insert(event.deviceId);
+ self.data.contributing_devices.insert(event.deviceId);
// If ephemeral modifier key, capture the key and update the sticky modifier states
let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode);
let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode);
@@ -108,38 +110,62 @@
modifier_state |= modifier_key_mask;
}
}
- if self.modifier_state != modifier_state
- || self.locked_modifier_state != locked_modifier_state
+ if self.data.modifier_state != modifier_state
+ || self.data.locked_modifier_state != locked_modifier_state
{
- self.modifier_state = modifier_state;
- self.locked_modifier_state = locked_modifier_state;
+ self.data.modifier_state = modifier_state;
+ self.data.locked_modifier_state = locked_modifier_state;
self.listener.modifier_state_changed(modifier_state, locked_modifier_state);
}
}
fn notify_devices_changed(&mut self, device_infos: &[DeviceInfo]) {
// Clear state if all contributing devices removed
- self.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId));
- if self.contributing_devices.is_empty()
- && (self.modifier_state != ModifierState::None
- || self.locked_modifier_state != ModifierState::None)
+ self.data.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId));
+ if self.data.contributing_devices.is_empty()
+ && (self.data.modifier_state != ModifierState::None
+ || self.data.locked_modifier_state != ModifierState::None)
{
- self.modifier_state = ModifierState::None;
- self.locked_modifier_state = ModifierState::None;
+ self.data.modifier_state = ModifierState::None;
+ self.data.locked_modifier_state = ModifierState::None;
self.listener.modifier_state_changed(ModifierState::None, ModifierState::None);
}
self.next.notify_devices_changed(device_infos);
}
+ fn save(
+ &mut self,
+ mut state: HashMap<&'static str, Box<dyn Any + Send + Sync>>,
+ ) -> HashMap<&'static str, Box<dyn Any + Send + Sync>> {
+ let data = Data {
+ contributing_devices: self.data.contributing_devices.clone(),
+ modifier_state: self.data.modifier_state,
+ locked_modifier_state: self.data.locked_modifier_state,
+ };
+ state.insert(STICKY_KEYS_DATA, Box::new(data));
+ self.next.save(state)
+ }
+
+ fn restore(&mut self, state: &HashMap<&'static str, Box<dyn Any + Send + Sync>>) {
+ if let Some(value) = state.get(STICKY_KEYS_DATA) {
+ if let Some(data) = value.downcast_ref::<Data>() {
+ self.data.contributing_devices = data.contributing_devices.clone();
+ self.data.modifier_state = data.modifier_state;
+ self.data.locked_modifier_state = data.locked_modifier_state;
+ }
+ }
+ self.next.restore(state)
+ }
+
fn destroy(&mut self) {
self.next.destroy();
}
fn dump(&mut self, dump_str: String) -> String {
let mut result = "Sticky Keys filter: \n".to_string();
- result += &format!("\tmodifier_state = {:?}\n", self.modifier_state);
- result += &format!("\tlocked_modifier_state = {:?}\n", self.locked_modifier_state);
- result += &format!("\tcontributing_devices = {:?}\n", self.contributing_devices);
+ result += &format!("\tmodifier_state = {:?}\n", self.data.modifier_state);
+ result += &format!("\tlocked_modifier_state = {:?}\n", self.data.locked_modifier_state);
+ result += &format!("\tcontributing_devices = {:?}\n", self.data.contributing_devices);
self.next.dump(dump_str + &result)
}
}
@@ -245,6 +271,7 @@
};
use input::KeyboardType;
use input::ModifierState;
+ use std::collections::HashMap;
use std::sync::{Arc, RwLock};
static DEVICE_ID: i32 = 1;
@@ -452,6 +479,45 @@
}
#[test]
+ fn test_modifier_state_restored_on_recreation() {
+ let test_filter = TestFilter::new();
+ let test_callbacks = TestCallbacks::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+
+ let saved_state = sticky_keys_filter.save(HashMap::new());
+ sticky_keys_filter.destroy();
+
+ // Create a new Sticky keys filter
+ let test_filter = TestFilter::new();
+ let mut sticky_keys_filter = setup_filter(
+ Box::new(test_filter.clone()),
+ Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
+ );
+ sticky_keys_filter.restore(&saved_state);
+ assert_eq!(
+ test_callbacks.get_last_modifier_state(),
+ ModifierState::CtrlLeftOn | ModifierState::CtrlOn
+ );
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
+
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
+ sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
+ assert_eq!(
+ test_callbacks.get_last_modifier_state(),
+ ModifierState::CtrlLeftOn | ModifierState::CtrlOn
+ );
+ assert_eq!(
+ test_callbacks.get_last_locked_modifier_state(),
+ ModifierState::CtrlLeftOn | ModifierState::CtrlOn
+ );
+ }
+
+ #[test]
fn test_key_events_have_sticky_modifier_state() {
let test_filter = TestFilter::new();
let test_callbacks = TestCallbacks::new();
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index b27d02d..1762a45 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -205,9 +205,14 @@
args.clear();
args += process(EV_KEY, BTN_LEFT, 1);
args += process(EV_SYN, SYN_REPORT, 0);
+
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
- VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS))));
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY)))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))));
// Move some more.
args.clear();
@@ -221,9 +226,76 @@
args += process(EV_KEY, BTN_LEFT, 0);
args += process(EV_SYN, SYN_REPORT, 0);
ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+}
+
+/**
+ * Test that enabling mouse swap primary button will have the left click result in a
+ * `SECONDARY_BUTTON` event and a right click will result in a `PRIMARY_BUTTON` event.
+ */
+TEST_F(CursorInputMapperUnitTest, SwappedPrimaryButtonPress) {
+ mReaderConfiguration.mouseSwapPrimaryButtonEnabled = true;
+ createMapper();
+ std::list<NotifyArgs> args;
+
+ // Now click the left mouse button , expect a `SECONDARY_BUTTON` button state.
+ args.clear();
+ args += process(EV_KEY, BTN_LEFT, 1);
+ args += process(EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY)))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY))));
+
+ // Release the left button.
+ args.clear();
+ args += process(EV_KEY, BTN_LEFT, 0);
+ args += process(EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY))),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+
+ // Now click the right mouse button , expect a `PRIMARY_BUTTON` button state.
+ args.clear();
+ args += process(EV_KEY, BTN_RIGHT, 1);
+ args += process(EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_PRESS),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY)))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))));
+
+ // Release the right button.
+ args.clear();
+ args += process(EV_KEY, BTN_RIGHT, 0);
+ args += process(EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
+
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
+ VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE))));
}
/**
@@ -882,6 +954,51 @@
WithScroll(0.5f, 0.5f)))));
}
+TEST_F(CursorInputMapperUnitTest, ProcessReversedVerticalScroll) {
+ mReaderConfiguration.mouseReverseVerticalScrollingEnabled = true;
+ 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);
+
+ // 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))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_SCROLL),
+ WithScroll(1.0f, -1.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessHighResReversedVerticalScroll) {
+ mReaderConfiguration.mouseReverseVerticalScrollingEnabled = true;
+ 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)))));
+}
+
/**
* When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
* pointer acceleration or speed processing should not be applied.
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index c5702e9..3413caa 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -5105,9 +5105,7 @@
/**
* Test that invalid HOVER events sent by accessibility do not cause a fatal crash.
*/
-TEST_F_WITH_FLAGS(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash,
- REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(com::android::input::flags,
- a11y_crash_on_inconsistent_event_stream))) {
+TEST_F(InputDispatcherTest, InvalidA11yHoverStreamDoesNotCrash) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
ui::LogicalDisplayId::DEFAULT);
@@ -5123,10 +5121,11 @@
.addFlag(AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, hoverEnterBuilder.build()));
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ // Another HOVER_ENTER would be inconsistent, and should therefore fail to
+ // get injected.
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(*mDispatcher, hoverEnterBuilder.build()));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
- window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
}
/**
@@ -12889,6 +12888,22 @@
// Remove drag window
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
+ // Complete the first event stream, even though the injection will fail because there aren't any
+ // valid targets to dispatch this event to. This is still needed to make the input stream
+ // consistent
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER)
+ .x(150)
+ .y(50))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(50)
+ .y(50))
+ .build(),
+ INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT));
+
// Inject a simple gesture, ensure dispatcher not crashed
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
@@ -12927,6 +12942,87 @@
<< "Drag and drop should not work with a hovering pointer";
}
+/**
+ * Two devices, we use the second pointer of Device A to start the drag, during the drag process, if
+ * we perform a click using Device B, the dispatcher should work well.
+ */
+TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouchAndMultiDevice) {
+ const DeviceId deviceA = 1;
+ const DeviceId deviceB = 2;
+ // First down on second window with deviceA.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .build());
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Second down on first window with deviceA
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Perform drag and drop from first window.
+ ASSERT_TRUE(startDrag(/*sendDown=*/false));
+
+ // Click first window with device B, we should ensure dispatcher work well.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(deviceB)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_MOUSE)
+ .deviceId(deviceB)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ mWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceB),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Move with device A.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(51).y(51))
+ .build());
+
+ mDragWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+ mWindow->consumeDragEvent(false, 51, 51);
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Releasing the drag pointer should cause drop.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(51).y(51))
+ .build());
+ mDragWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+
+ // Release all pointers.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(deviceA)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .build());
+ mSecondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(deviceA),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
+ mWindow->assertNoEvents();
+}
+
class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 17c37d5..6c8b65c 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -3671,40 +3671,6 @@
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
}
-TEST_F(KeyboardInputMapperTest, NoMetaStateWhenMetaKeysNotPresent) {
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_A, 0, AKEYCODE_BUTTON_A, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_B, 0, AKEYCODE_BUTTON_B, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_X, 0, AKEYCODE_BUTTON_X, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_Y, 0, AKEYCODE_BUTTON_Y, 0);
-
- KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
-
- // Meta state should be AMETA_NONE after reset
- std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME);
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
- // Meta state should be AMETA_NONE with update, as device doesn't have the keys.
- mapper.updateMetaState(AKEYCODE_NUM_LOCK);
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
-
- NotifyKeyArgs args;
- // Press button "A"
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_A, 1);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
-
- // Button up.
- process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, BTN_A, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(AMETA_NONE, args.metaState);
- ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(AKEYCODE_BUTTON_A, args.keyCode);
-}
-
TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) {
// keyboard 1.
mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index f41b39a..a43e4e4 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -246,8 +246,6 @@
MOCK_METHOD(std::optional<int32_t>, getLightPlayerId, (int32_t lightId), ());
MOCK_METHOD(int32_t, getMetaState, (), ());
- MOCK_METHOD(void, updateMetaState, (int32_t keyCode), ());
-
MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ());
MOCK_METHOD(void, bumpGeneration, (), ());
diff --git a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
index 6607bc7..157bee3 100644
--- a/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
+++ b/services/inputflinger/tests/RotaryEncoderInputMapper_test.cpp
@@ -23,6 +23,8 @@
#include <android-base/logging.h>
#include <android_companion_virtualdevice_flags.h>
+#include <com_android_input_flags.h>
+#include <flag_macros.h>
#include <gtest/gtest.h>
#include <input/DisplayViewport.h>
#include <linux/input-event-codes.h>
@@ -100,6 +102,15 @@
EXPECT_CALL(mMockEventHub, hasRelativeAxis(EVENTHUB_ID, REL_HWHEEL_HI_RES))
.WillRepeatedly(Return(false));
}
+
+ std::map<std::string, int64_t> mTelemetryLogCounts;
+
+ /**
+ * A fake function for telemetry logging.
+ * Records the log counts in the `mTelemetryLogCounts` map.
+ */
+ std::function<void(const char*, int64_t)> mTelemetryLogCounter =
+ [this](const char* key, int64_t value) { mTelemetryLogCounts[key] += value; };
};
TEST_F(RotaryEncoderInputMapperTest, ConfigureDisplayIdWithAssociatedViewport) {
@@ -187,4 +198,142 @@
WithMotionAction(AMOTION_EVENT_ACTION_SCROLL), WithScroll(0.5f)))));
}
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, RotaryInputTelemetryFlagOff_NoRotationLogging,
+ REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ mPropertyMap.addProperty("device.res", "3");
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 70);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+}
+
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, ZeroResolution_NoRotationLogging,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ mPropertyMap.addProperty("device.res", "-3");
+ mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "2");
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+}
+
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, NegativeMinLogRotation_NoRotationLogging,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ mPropertyMap.addProperty("device.res", "3");
+ mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "-2");
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+}
+
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, ZeroMinLogRotation_NoRotationLogging,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ mPropertyMap.addProperty("device.res", "3");
+ mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "0");
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+}
+
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, NoMinLogRotation_NoRotationLogging,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ // 3 units per radian, 2 * M_PI * 3 = ~18.85 units per rotation.
+ mPropertyMap.addProperty("device.res", "3");
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 700);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+}
+
+TEST_F_WITH_FLAGS(RotaryEncoderInputMapperTest, RotationLogging,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ rotary_input_telemetry))) {
+ // 3 units per radian, 2 * M_PI * 3 = ~18.85 units per rotation.
+ // Multiples of `unitsPerRoation`, to easily follow the assertions below.
+ // [18.85, 37.7, 56.55, 75.4, 94.25, 113.1, 131.95, 150.8]
+ mPropertyMap.addProperty("device.res", "3");
+ mPropertyMap.addProperty("rotary_encoder.min_rotations_to_log", "2");
+
+ mMapper = createInputMapper<RotaryEncoderInputMapper>(*mDeviceContext, mReaderConfiguration,
+ mTelemetryLogCounter);
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 15); // total scroll = 15
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 13); // total scroll = 28
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ // Expect 0 since `min_rotations_to_log` = 2, and total scroll 28 only has 1 rotation.
+ ASSERT_EQ(mTelemetryLogCounts.find("input.value_rotary_input_device_full_rotation_count"),
+ mTelemetryLogCounts.end());
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, 10); // total scroll = 38
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ // Total scroll includes >= `min_rotations_to_log` (2), expect log.
+ ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 2);
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, -22); // total scroll = 60
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ // Expect no additional telemetry. Total rotation is 3, and total unlogged rotation is 1, which
+ // is less than `min_rotations_to_log`.
+ ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 2);
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, -16); // total scroll = 76
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ // Total unlogged rotation >= `min_rotations_to_log` (2), so expect 2 more logged rotation.
+ ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 4);
+
+ args += process(ARBITRARY_TIME, EV_REL, REL_WHEEL, -76); // total scroll = 152
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ // Total unlogged scroll >= 4*`min_rotations_to_log`. Expect *all* unlogged rotations to be
+ // logged, even if that's more than multiple of `min_rotations_to_log`.
+ ASSERT_EQ(mTelemetryLogCounts["input.value_rotary_input_device_full_rotation_count"], 8);
+}
+
} // namespace android
\ No newline at end of file
diff --git a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 9e02502..11b038b 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -99,7 +99,6 @@
nullptr);
},
[&]() -> void { mapper.getMetaState(); },
- [&]() -> void { mapper.updateMetaState(fdp->ConsumeIntegral<int32_t>()); },
[&]() -> void { mapper.getAssociatedDisplayId(); },
})();
}
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 060508c..eabbb39 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -2049,9 +2049,10 @@
}
ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);
- if (mCurrentOperatingMode != NORMAL && mCurrentOperatingMode != REPLAY_DATA_INJECTION &&
- !isAllowListedPackage(connection->getPackageName())) {
- return INVALID_OPERATION;
+ if (mCurrentOperatingMode != NORMAL &&
+ !isInjectionMode(mCurrentOperatingMode) &&
+ !isAllowListedPackage(connection->getPackageName())) {
+ return INVALID_OPERATION;
}
SensorRecord* rec = mActiveSensors.valueFor(handle);
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index c2a9880..7babd17 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -173,7 +173,6 @@
"DisplayHardware/VirtualDisplaySurface.cpp",
"DisplayRenderArea.cpp",
"Effects/Daltonizer.cpp",
- "EventLog/EventLog.cpp",
"FrontEnd/LayerCreationArgs.cpp",
"FrontEnd/LayerHandle.cpp",
"FrontEnd/LayerSnapshot.cpp",
@@ -279,7 +278,7 @@
"libSurfaceFlingerProp",
],
- logtags: ["EventLog/EventLogTags.logtags"],
+ logtags: ["surfaceflinger.logtags"],
}
subdirs = [
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index d1429a2..14a8fd6 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -155,7 +155,7 @@
uint32_t geomBufferTransform{0};
Rect geomBufferSize;
Rect geomContentCrop;
- Rect geomCrop;
+ FloatRect geomCrop;
GenericLayerMetadataMap metadata;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index 4dbf8d2..dcfe21a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -128,6 +128,10 @@
// Applies a HWC device layer request
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;
+
// 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 d1eff24..a39abb4 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -82,11 +82,13 @@
using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests;
using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests;
using ClientTargetProperty = android::HWComposer::DeviceRequestedChanges::ClientTargetProperty;
+ using LayerLuts = android::HWComposer::DeviceRequestedChanges::LayerLuts;
virtual bool allLayersRequireClientComposition() const;
virtual void applyChangedTypesToLayers(const ChangedTypes&);
virtual void applyDisplayRequests(const DisplayRequests&);
virtual void applyLayerRequestsToLayers(const LayerRequests&);
virtual void applyClientTargetRequests(const ClientTargetProperty&);
+ virtual void applyLayerLutsToLayers(const LayerLuts&);
// Internal
virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index f383392..354a441 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -60,6 +60,8 @@
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;
bool needsFiltering() const override;
std::optional<LayerFE::LayerSettings> getOverrideCompositionSettings() const override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 5fef63a..48c2f9c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -56,6 +56,9 @@
MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
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));
MOCK_CONST_METHOD1(dump, void(std::string&));
};
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 77b1940..b0164b7 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -278,6 +278,7 @@
applyDisplayRequests(changes->displayRequests);
applyLayerRequestsToLayers(changes->layerRequests);
applyClientTargetRequests(changes->clientTargetProperty);
+ applyLayerLutsToLayers(changes->layerLuts);
}
// Determine what type of composition we are doing from the final state
@@ -359,6 +360,25 @@
static_cast<ui::PixelFormat>(clientTargetProperty.clientTargetProperty.pixelFormat));
}
+void Display::applyLayerLutsToLayers(const LayerLuts& layerLuts) {
+ auto& mapper = getCompositionEngine().getHwComposer().getLutFileDescriptorMapper();
+ for (auto* layer : getOutputLayersOrderedByZ()) {
+ auto hwcLayer = layer->getHwcLayer();
+ if (!hwcLayer) {
+ continue;
+ }
+
+ 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()));
+ }
+ }
+ }
+
+ mapper.clear();
+}
+
void Display::executeCommands() {
const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
if (mIsDisconnected || !halDisplayIdOpt) {
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index 091c207..2d46dc0 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -24,6 +24,7 @@
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <cstdint>
#include "system/graphics-base-v1.0.h"
+#include "ui/FloatRect.h"
#include <ui/HdrRenderTypeUtils.h>
@@ -37,6 +38,7 @@
#pragma clang diagnostic pop // ignored "-Wconversion"
using aidl::android::hardware::graphics::composer3::Composition;
+using aidl::android::hardware::graphics::composer3::LutProperties;
namespace android::compositionengine {
@@ -185,35 +187,35 @@
const auto& layerState = *getLayerFE().getCompositionState();
const auto& outputState = getOutput().getState();
+ // Convert from layer space to layerStackSpace
// apply the layer's transform, followed by the display's global transform
// here we're guaranteed that the layer's transform preserves rects
- Region activeTransparentRegion = layerState.transparentRegionHint;
const ui::Transform& layerTransform = layerState.geomLayerTransform;
- const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform;
- const Rect& bufferSize = layerState.geomBufferSize;
- Rect activeCrop = layerState.geomCrop;
- if (!activeCrop.isEmpty() && bufferSize.isValid()) {
- activeCrop = layerTransform.transform(activeCrop);
- if (!activeCrop.intersect(outputState.layerStackSpace.getContent(), &activeCrop)) {
- activeCrop.clear();
- }
- activeCrop = inverseLayerTransform.transform(activeCrop, true);
- // This needs to be here as transform.transform(Rect) computes the
- // transformed rect and then takes the bounding box of the result before
- // returning. This means
- // transform.inverse().transform(transform.transform(Rect)) != Rect
- // in which case we need to make sure the final rect is clipped to the
- // display bounds.
- if (!activeCrop.intersect(bufferSize, &activeCrop)) {
- activeCrop.clear();
- }
+ Region activeTransparentRegion = layerTransform.transform(layerState.transparentRegionHint);
+ if (!layerState.geomCrop.isEmpty() && layerState.geomBufferSize.isValid()) {
+ FloatRect activeCrop = layerTransform.transform(layerState.geomCrop);
+ activeCrop = activeCrop.intersect(outputState.layerStackSpace.getContent().toFloatRect());
+ const FloatRect& bufferSize =
+ layerTransform.transform(layerState.geomBufferSize.toFloatRect());
+ activeCrop = activeCrop.intersect(bufferSize);
+
// mark regions outside the crop as transparent
- activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top));
- activeTransparentRegion.orSelf(
- Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight()));
- activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom));
- activeTransparentRegion.orSelf(
- Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom));
+ Rect topRegion = Rect(layerTransform.transform(
+ FloatRect(0, 0, layerState.geomBufferSize.getWidth(), layerState.geomCrop.top)));
+ Rect bottomRegion = Rect(layerTransform.transform(
+ FloatRect(0, layerState.geomCrop.bottom, layerState.geomBufferSize.getWidth(),
+ layerState.geomBufferSize.getHeight())));
+ Rect leftRegion = Rect(layerTransform.transform(FloatRect(0, layerState.geomCrop.top,
+ layerState.geomCrop.left,
+ layerState.geomCrop.bottom)));
+ Rect rightRegion = Rect(layerTransform.transform(
+ FloatRect(layerState.geomCrop.right, layerState.geomCrop.top,
+ layerState.geomBufferSize.getWidth(), layerState.geomCrop.bottom)));
+
+ activeTransparentRegion.orSelf(topRegion);
+ activeTransparentRegion.orSelf(bottomRegion);
+ activeTransparentRegion.orSelf(leftRegion);
+ activeTransparentRegion.orSelf(rightRegion);
}
// reduce uses a FloatRect to provide more accuracy during the
@@ -223,19 +225,22 @@
// Some HWCs may clip client composited input to its displayFrame. Make sure
// that this does not cut off the shadow.
if (layerState.forceClientComposition && layerState.shadowSettings.length > 0.0f) {
- const auto outset = layerState.shadowSettings.length;
+ // RenderEngine currently blurs shadows to smooth out edges, so outset by
+ // 2x the length instead of 1x to compensate
+ const auto outset = layerState.shadowSettings.length * 2;
geomLayerBounds.left -= outset;
geomLayerBounds.top -= outset;
geomLayerBounds.right += outset;
geomLayerBounds.bottom += outset;
}
- Rect frame{layerTransform.transform(reduce(geomLayerBounds, activeTransparentRegion))};
- if (!frame.intersect(outputState.layerStackSpace.getContent(), &frame)) {
- frame.clear();
- }
- const ui::Transform displayTransform{outputState.transform};
- return displayTransform.transform(frame);
+ geomLayerBounds = layerTransform.transform(geomLayerBounds);
+ FloatRect frame = reduce(geomLayerBounds, activeTransparentRegion);
+ frame = frame.intersect(outputState.layerStackSpace.getContent().toFloatRect());
+
+ // convert from layerStackSpace to displaySpace
+ const ui::Transform displayTransform{outputState.transform};
+ return Rect(displayTransform.transform(frame));
}
uint32_t OutputLayer::calculateOutputRelativeBufferTransform(
@@ -844,6 +849,12 @@
}
}
+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
+}
+
bool OutputLayer::needsFiltering() const {
const auto& state = getState();
const auto& sourceCrop = state.sourceCrop;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 39163ea..9c0e62c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -133,6 +133,7 @@
MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&));
MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&));
MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&));
+ MOCK_METHOD1(applyLayerLutsToLayers, void(const impl::Display::LayerLuts&));
const compositionengine::CompositionEngine& mCompositionEngine;
impl::OutputCompositionState mState;
@@ -212,6 +213,7 @@
aidl::android::hardware::graphics::common::Dataspace::UNKNOWN},
-1.f,
DimmingStage::NONE},
+ {},
};
void chooseCompositionStrategy(Display* display) {
@@ -615,6 +617,7 @@
EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
.Times(1);
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mDisplay, applyLayerLutsToLayers(mDeviceRequestedChanges.layerLuts)).Times(1);
chooseCompositionStrategy(mDisplay.get());
@@ -667,6 +670,7 @@
EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(mDeviceRequestedChanges.layerRequests))
.Times(1);
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mDisplay, applyLayerLutsToLayers(mDeviceRequestedChanges.layerLuts)).Times(1);
chooseCompositionStrategy(mDisplay.get());
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index e910c72..5c55ce7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -152,10 +152,7 @@
getOverlaySupport, (), (const, override));
MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool));
MOCK_METHOD(status_t, notifyExpectedPresent, (PhysicalDisplayId, TimePoint, Fps));
- MOCK_METHOD(status_t, getRequestedLuts,
- (PhysicalDisplayId,
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*),
- (override));
+ MOCK_METHOD((HWC2::Display::LutFileDescriptorMapper&), getLutFileDescriptorMapper, (), ());
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 1c54469..b21533a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -30,6 +30,7 @@
#include "MockHWC2.h"
#include "MockHWComposer.h"
#include "RegionMatcher.h"
+#include "ui/FloatRect.h"
#include <aidl/android/hardware/graphics/composer3/Composition.h>
@@ -270,7 +271,7 @@
mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080};
mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
- mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080};
+ mLayerFEState.geomCrop = FloatRect{0, 0, 1920, 1080};
mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f};
mOutputState.layerStackSpace.setContent(Rect{0, 0, 1920, 1080});
@@ -296,20 +297,20 @@
}
TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) {
- mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
+ mLayerFEState.geomCrop = FloatRect{100, 200, 300, 500};
const Rect expected{100, 200, 300, 500};
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) {
- mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
+ mLayerFEState.geomCrop = FloatRect{100, 200, 300, 500};
mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
const Rect expected{1420, 100, 1720, 300};
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) {
- mLayerFEState.geomCrop = Rect{};
+ mLayerFEState.geomCrop = FloatRect{};
const Rect expected{0, 0, 1920, 1080};
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
@@ -339,7 +340,7 @@
mLayerFEState.geomLayerBounds = FloatRect{100.f, 100.f, 200.f, 200.f};
Rect expected{mLayerFEState.geomLayerBounds};
- expected.inset(-kShadowRadius, -kShadowRadius, -kShadowRadius, -kShadowRadius);
+ expected.inset(-2 * kShadowRadius, -2 * kShadowRadius, -2 * kShadowRadius, -2 * kShadowRadius);
EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
index 0e9218c..f8b6c6e 100644
--- a/services/surfaceflinger/Display/DisplayModeController.cpp
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -283,6 +283,8 @@
}
}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
auto DisplayModeController::getKernelIdleTimerState(PhysicalDisplayId displayId) const
-> KernelIdleTimerState {
std::lock_guard lock(mDisplayLock);
@@ -298,4 +300,5 @@
return {desiredModeIdOpt, displayPtr->isKernelIdleTimerEnabled};
}
+#pragma clang diagnostic pop
} // namespace android::display
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 66237b9..77bd804 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -1547,7 +1547,8 @@
return error;
}
-Error AidlComposer::getRequestedLuts(Display display, std::vector<DisplayLuts::LayerLut>* outLuts) {
+Error AidlComposer::getRequestedLuts(Display display, std::vector<Layer>* outLayers,
+ std::vector<DisplayLuts::LayerLut>* outLuts) {
Error error = Error::NONE;
mMutex.lock_shared();
if (auto reader = getReader(display)) {
@@ -1556,6 +1557,11 @@
error = Error::BAD_DISPLAY;
}
mMutex.unlock_shared();
+
+ outLayers->reserve(outLuts->size());
+ for (const auto& layerLut : *outLuts) {
+ outLayers->emplace_back(translate<Layer>(layerLut.layer));
+ }
return error;
}
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index 246223a..cdb67e4 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -245,7 +245,7 @@
Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
int32_t frameIntervalNs) override;
Error getRequestedLuts(
- Display display,
+ Display display, std::vector<Layer>* outLayers,
std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
outLuts) override;
Error setLayerLuts(
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 7db9a94..0905663 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -305,7 +305,7 @@
virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0;
virtual Error notifyExpectedPresent(Display, nsecs_t expectedPresentTime,
int32_t frameIntervalNs) = 0;
- virtual Error getRequestedLuts(Display display,
+ 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;
};
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index f1fa938..1df2ab1 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -609,17 +609,29 @@
return static_cast<Error>(error);
}
-Error Display::getRequestedLuts(std::vector<DisplayLuts::LayerLut>* outLayerLuts) {
- std::vector<DisplayLuts::LayerLut> tmpLayerLuts;
- const auto error = mComposer.getRequestedLuts(mId, &tmpLayerLuts);
- for (DisplayLuts::LayerLut& layerLut : tmpLayerLuts) {
- if (layerLut.lut.pfd.get() >= 0) {
- outLayerLuts->push_back({layerLut.layer,
- Lut{ndk::ScopedFileDescriptor(layerLut.lut.pfd.release()),
- layerLut.lut.lutProperties}});
+Error Display::getRequestedLuts(LayerLuts* outLuts,
+ LutFileDescriptorMapper& lutFileDescriptorMapper) {
+ std::vector<Hwc2::Layer> layerIds;
+ std::vector<DisplayLuts::LayerLut> tmpLuts;
+ const auto error = static_cast<Error>(mComposer.getRequestedLuts(mId, &layerIds, &tmpLuts));
+ if (error != Error::NONE) {
+ return error;
+ }
+
+ uint32_t numElements = layerIds.size();
+ outLuts->clear();
+ for (uint32_t i = 0; i < numElements; ++i) {
+ 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()));
}
}
- return static_cast<Error>(error);
+
+ return Error::NONE;
}
Error Display::getDisplayDecorationSupport(
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 8e2aeaf..61f92f4 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -20,6 +20,7 @@
#include <android-base/thread_annotations.h>
#include <ftl/expected.h>
#include <ftl/future.h>
+#include <ftl/small_map.h>
#include <gui/HdrMetadata.h>
#include <math/mat4.h>
#include <ui/HdrCapabilities.h>
@@ -107,6 +108,13 @@
virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
virtual std::optional<ui::Size> getPhysicalSizeInMm() const = 0;
+ static const int kLutFileDescriptorMapperSize = 20;
+ using LayerLuts =
+ ftl::SmallMap<HWC2::Layer*, aidl::android::hardware::graphics::composer3::LutProperties,
+ kLutFileDescriptorMapperSize>;
+ using LutFileDescriptorMapper =
+ ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, kLutFileDescriptorMapperSize>;
+
[[nodiscard]] virtual hal::Error acceptChanges() = 0;
[[nodiscard]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
createLayer() = 0;
@@ -183,8 +191,7 @@
aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
outClientTargetProperty) = 0;
[[nodiscard]] virtual hal::Error getRequestedLuts(
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
- outLuts) = 0;
+ LayerLuts* outLuts, LutFileDescriptorMapper& lutFileDescriptorMapper) = 0;
[[nodiscard]] virtual hal::Error getDisplayDecorationSupport(
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
support) = 0;
@@ -268,9 +275,8 @@
hal::Error getClientTargetProperty(
aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
outClientTargetProperty) override;
- hal::Error getRequestedLuts(
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*
- outLuts) override;
+ hal::Error getRequestedLuts(LayerLuts* outLuts,
+ LutFileDescriptorMapper& lutFileDescriptorMapper) override;
hal::Error getDisplayDecorationSupport(
std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
support) override;
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 05ba967..7d77634 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -587,9 +587,14 @@
error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
RETURN_IF_HWC_ERROR_FOR("getClientTargetProperty", error, displayId, BAD_INDEX);
+ DeviceRequestedChanges::LayerLuts layerLuts;
+ error = hwcDisplay->getRequestedLuts(&layerLuts, mLutFileDescriptorMapper);
+ RETURN_IF_HWC_ERROR_FOR("getRequestedLuts", error, displayId, BAD_INDEX);
+
outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
std::move(layerRequests),
- std::move(clientTargetProperty)});
+ std::move(clientTargetProperty),
+ std::move(layerLuts)});
error = hwcDisplay->acceptChanges();
RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
@@ -978,21 +983,6 @@
return NO_ERROR;
}
-status_t HWComposer::getRequestedLuts(
- PhysicalDisplayId displayId,
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>* outLuts) {
- RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
- const auto error = mDisplayData[displayId].hwcDisplay->getRequestedLuts(outLuts);
- if (error == hal::Error::UNSUPPORTED) {
- RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
- }
- if (error == hal::Error::BAD_PARAMETER) {
- RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
- }
- RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
- return NO_ERROR;
-}
-
status_t HWComposer::setAutoLowLatencyMode(PhysicalDisplayId displayId, bool on) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
@@ -1036,6 +1026,11 @@
return mSupportedLayerGenericMetadata;
}
+ftl::SmallMap<HWC2::Layer*, ndk::ScopedFileDescriptor, 20>&
+HWComposer::getLutFileDescriptorMapper() {
+ return mLutFileDescriptorMapper;
+}
+
void HWComposer::dumpOverlayProperties(std::string& result) const {
// dump overlay properties
result.append("OverlayProperties:\n");
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index b95c619..7b04d67 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -53,6 +53,7 @@
#include <aidl/android/hardware/graphics/composer3/Composition.h>
#include <aidl/android/hardware/graphics/composer3/DisplayCapability.h>
#include <aidl/android/hardware/graphics/composer3/DisplayLuts.h>
+#include <aidl/android/hardware/graphics/composer3/LutProperties.h>
#include <aidl/android/hardware/graphics/composer3/OverlayProperties.h>
namespace android {
@@ -90,11 +91,14 @@
aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness;
using DisplayRequests = hal::DisplayRequest;
using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
+ using LutProperties = aidl::android::hardware::graphics::composer3::LutProperties;
+ using LayerLuts = HWC2::Display::LayerLuts;
ChangedTypes changedTypes;
DisplayRequests displayRequests;
LayerRequests layerRequests;
ClientTargetProperty clientTargetProperty;
+ LayerLuts layerLuts;
};
struct HWCDisplayMode {
@@ -311,18 +315,15 @@
virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0;
virtual status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
Fps frameInterval) = 0;
-
- // Composer 4.0
- virtual status_t getRequestedLuts(
- PhysicalDisplayId,
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*) = 0;
+ // mapper
+ virtual HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() = 0;
};
static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs,
const android::HWComposer::DeviceRequestedChanges& rhs) {
return lhs.changedTypes == rhs.changedTypes && lhs.displayRequests == rhs.displayRequests &&
lhs.layerRequests == rhs.layerRequests &&
- lhs.clientTargetProperty == rhs.clientTargetProperty;
+ lhs.clientTargetProperty == rhs.clientTargetProperty && lhs.layerLuts == rhs.layerLuts;
}
namespace impl {
@@ -480,11 +481,8 @@
status_t notifyExpectedPresent(PhysicalDisplayId, TimePoint expectedPresentTime,
Fps frameInterval) override;
- // Composer 4.0
- status_t getRequestedLuts(
- PhysicalDisplayId,
- std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)
- override;
+ // get a mapper
+ HWC2::Display::LutFileDescriptorMapper& getLutFileDescriptorMapper() override;
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
@@ -571,6 +569,8 @@
const size_t mMaxVirtualDisplayDimension;
const bool mUpdateDeviceProductInfoOnHotplugReconnect;
bool mEnableVrrTimeout;
+
+ HWC2::Display::LutFileDescriptorMapper mLutFileDescriptorMapper;
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index ee1e07a..056ecd7 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -1410,7 +1410,8 @@
return Error::NONE;
}
-Error HidlComposer::getRequestedLuts(Display, std::vector<DisplayLuts::LayerLut>*) {
+Error HidlComposer::getRequestedLuts(Display, std::vector<Layer>*,
+ std::vector<DisplayLuts::LayerLut>*) {
return Error::NONE;
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index 701a54b..1cc23d1 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -352,7 +352,7 @@
Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override;
Error notifyExpectedPresent(Display, nsecs_t, int32_t) override;
Error getRequestedLuts(
- Display,
+ Display, std::vector<Layer>*,
std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*)
override;
Error setLayerLuts(Display, Layer,
diff --git a/services/surfaceflinger/EventLog/EventLog.cpp b/services/surfaceflinger/EventLog/EventLog.cpp
deleted file mode 100644
index 3b60952..0000000
--- a/services/surfaceflinger/EventLog/EventLog.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright 2013 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.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <log/log.h>
-
-#include "EventLog.h"
-
-namespace android {
-
-ANDROID_SINGLETON_STATIC_INSTANCE(EventLog)
-
-
-EventLog::EventLog() {
-}
-
-void EventLog::doLogFrameDurations(const std::string_view& name, const int32_t* durations,
- size_t numDurations) {
- EventLog::TagBuffer buffer(LOGTAG_SF_FRAME_DUR);
- buffer.startList(1 + numDurations);
- buffer.writeString(name);
- for (size_t i = 0; i < numDurations; i++) {
- buffer.writeInt32(durations[i]);
- }
- buffer.endList();
- buffer.log();
-}
-
-void EventLog::logFrameDurations(const std::string_view& name, const int32_t* durations,
- size_t numDurations) {
- EventLog::getInstance().doLogFrameDurations(name, durations, numDurations);
-}
-
-// ---------------------------------------------------------------------------
-
-EventLog::TagBuffer::TagBuffer(int32_t tag)
- : mPos(0), mTag(tag), mOverflow(false) {
-}
-
-void EventLog::TagBuffer::log() {
- if (mOverflow) {
- ALOGW("couldn't log to binary event log: overflow.");
- } else if (android_bWriteLog(mTag, mStorage, mPos) < 0) {
- ALOGE("couldn't log to EventLog: %s", strerror(errno));
- }
- // purge the buffer
- mPos = 0;
- mOverflow = false;
-}
-
-void EventLog::TagBuffer::startList(int8_t count) {
- if (mOverflow) return;
- const size_t needed = 1 + sizeof(count);
- if (mPos + needed > STORAGE_MAX_SIZE) {
- mOverflow = true;
- return;
- }
- mStorage[mPos + 0] = EVENT_TYPE_LIST;
- mStorage[mPos + 1] = count;
- mPos += needed;
-}
-
-void EventLog::TagBuffer::endList() {
- if (mOverflow) return;
- const size_t needed = 1;
- if (mPos + needed > STORAGE_MAX_SIZE) {
- mOverflow = true;
- return;
- }
- mStorage[mPos + 0] = '\n';
- mPos += needed;
-}
-
-void EventLog::TagBuffer::writeInt32(int32_t value) {
- if (mOverflow) return;
- const size_t needed = 1 + sizeof(value);
- if (mPos + needed > STORAGE_MAX_SIZE) {
- mOverflow = true;
- return;
- }
- mStorage[mPos + 0] = EVENT_TYPE_INT;
- memcpy(&mStorage[mPos + 1], &value, sizeof(value));
- mPos += needed;
-}
-
-void EventLog::TagBuffer::writeInt64(int64_t value) {
- if (mOverflow) return;
- const size_t needed = 1 + sizeof(value);
- if (mPos + needed > STORAGE_MAX_SIZE) {
- mOverflow = true;
- return;
- }
- mStorage[mPos + 0] = EVENT_TYPE_LONG;
- memcpy(&mStorage[mPos + 1], &value, sizeof(value));
- mPos += needed;
-}
-
-void EventLog::TagBuffer::writeString(const std::string_view& value) {
- if (mOverflow) return;
- const size_t stringLen = value.length();
- const size_t needed = 1 + sizeof(int32_t) + stringLen;
- if (mPos + needed > STORAGE_MAX_SIZE) {
- mOverflow = true;
- return;
- }
- mStorage[mPos + 0] = EVENT_TYPE_STRING;
- memcpy(&mStorage[mPos + 1], &stringLen, sizeof(int32_t));
- memcpy(&mStorage[mPos + 5], value.data(), stringLen);
- mPos += needed;
-}
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/EventLog/EventLog.h b/services/surfaceflinger/EventLog/EventLog.h
deleted file mode 100644
index ee3587e..0000000
--- a/services/surfaceflinger/EventLog/EventLog.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <utils/Errors.h>
-#include <utils/Singleton.h>
-
-#include <cstdint>
-#include <string_view>
-
-namespace android {
-
-class EventLog : public Singleton<EventLog> {
-
-public:
- static void logFrameDurations(const std::string_view& name, const int32_t* durations,
- size_t numDurations);
-
-protected:
- EventLog();
-
-private:
- /*
- * EventLogBuffer is a helper class to construct an in-memory event log
- * tag. In this version the buffer is not dynamic, so write operation can
- * fail if there is not enough space in the temporary buffer.
- * Once constructed, the buffer can be logger by calling the log()
- * method.
- */
-
- class TagBuffer {
- enum { STORAGE_MAX_SIZE = 128 };
- int32_t mPos;
- int32_t mTag;
- bool mOverflow;
- char mStorage[STORAGE_MAX_SIZE];
- public:
- explicit TagBuffer(int32_t tag);
-
- void startList(int8_t count);
- void endList();
-
- void writeInt32(int32_t);
- void writeInt64(int64_t);
- void writeString(const std::string_view&);
-
- void log();
- };
-
- friend class Singleton<EventLog>;
- EventLog(const EventLog&);
- EventLog& operator =(const EventLog&);
-
- enum { LOGTAG_SF_FRAME_DUR = 60100 };
- void doLogFrameDurations(const std::string_view& name, const int32_t* durations,
- size_t numDurations);
-};
-
-} // namespace android
diff --git a/services/surfaceflinger/FrameTracker.cpp b/services/surfaceflinger/FrameTracker.cpp
index ca8cdc3..93d0313 100644
--- a/services/surfaceflinger/FrameTracker.cpp
+++ b/services/surfaceflinger/FrameTracker.cpp
@@ -26,16 +26,10 @@
#include <ui/FrameStats.h>
#include "FrameTracker.h"
-#include "EventLog/EventLog.h"
namespace android {
-FrameTracker::FrameTracker() :
- mOffset(0),
- mNumFences(0),
- mDisplayPeriod(0) {
- resetFrameCountersLocked();
-}
+FrameTracker::FrameTracker() : mOffset(0), mNumFences(0), mDisplayPeriod(0) {}
void FrameTracker::setDesiredPresentTime(nsecs_t presentTime) {
Mutex::Autolock lock(mMutex);
@@ -73,9 +67,6 @@
void FrameTracker::advanceFrame() {
Mutex::Autolock lock(mMutex);
- // Update the statistic to include the frame we just finished.
- updateStatsLocked(mOffset);
-
// Advance to the next frame.
mOffset = (mOffset+1) % NUM_FRAME_RECORDS;
mFrameRecords[mOffset].desiredPresentTime = INT64_MAX;
@@ -138,19 +129,12 @@
}
}
-void FrameTracker::logAndResetStats(const std::string_view& name) {
- Mutex::Autolock lock(mMutex);
- logStatsLocked(name);
- resetFrameCountersLocked();
-}
-
void FrameTracker::processFencesLocked() const {
FrameRecord* records = const_cast<FrameRecord*>(mFrameRecords);
int& numFences = const_cast<int&>(mNumFences);
for (int i = 1; i < NUM_FRAME_RECORDS && numFences > 0; i++) {
- size_t idx = (mOffset+NUM_FRAME_RECORDS-i) % NUM_FRAME_RECORDS;
- bool updated = false;
+ size_t idx = (mOffset + NUM_FRAME_RECORDS - i) % NUM_FRAME_RECORDS;
const std::shared_ptr<FenceTime>& rfence = records[idx].frameReadyFence;
if (rfence != nullptr) {
@@ -158,7 +142,6 @@
if (records[idx].frameReadyTime < INT64_MAX) {
records[idx].frameReadyFence = nullptr;
numFences--;
- updated = true;
}
}
@@ -169,59 +152,8 @@
if (records[idx].actualPresentTime < INT64_MAX) {
records[idx].actualPresentFence = nullptr;
numFences--;
- updated = true;
}
}
-
- if (updated) {
- updateStatsLocked(idx);
- }
- }
-}
-
-void FrameTracker::updateStatsLocked(size_t newFrameIdx) const {
- int* numFrames = const_cast<int*>(mNumFrames);
-
- if (mDisplayPeriod > 0 && isFrameValidLocked(newFrameIdx)) {
- size_t prevFrameIdx = (newFrameIdx+NUM_FRAME_RECORDS-1) %
- NUM_FRAME_RECORDS;
-
- if (isFrameValidLocked(prevFrameIdx)) {
- nsecs_t newPresentTime =
- mFrameRecords[newFrameIdx].actualPresentTime;
- nsecs_t prevPresentTime =
- mFrameRecords[prevFrameIdx].actualPresentTime;
-
- nsecs_t duration = newPresentTime - prevPresentTime;
- int numPeriods = int((duration + mDisplayPeriod/2) /
- mDisplayPeriod);
-
- for (int i = 0; i < NUM_FRAME_BUCKETS-1; i++) {
- int nextBucket = 1 << (i+1);
- if (numPeriods < nextBucket) {
- numFrames[i]++;
- return;
- }
- }
-
- // The last duration bucket is a catch-all.
- numFrames[NUM_FRAME_BUCKETS-1]++;
- }
- }
-}
-
-void FrameTracker::resetFrameCountersLocked() {
- for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
- mNumFrames[i] = 0;
- }
-}
-
-void FrameTracker::logStatsLocked(const std::string_view& name) const {
- for (int i = 0; i < NUM_FRAME_BUCKETS; i++) {
- if (mNumFrames[i] > 0) {
- EventLog::logFrameDurations(name, mNumFrames, NUM_FRAME_BUCKETS);
- return;
- }
}
}
diff --git a/services/surfaceflinger/FrameTracker.h b/services/surfaceflinger/FrameTracker.h
index bc412ae..fd6fadc 100644
--- a/services/surfaceflinger/FrameTracker.h
+++ b/services/surfaceflinger/FrameTracker.h
@@ -41,8 +41,6 @@
// frame time history.
enum { NUM_FRAME_RECORDS = 128 };
- enum { NUM_FRAME_BUCKETS = 7 };
-
FrameTracker();
// setDesiredPresentTime sets the time at which the current frame
@@ -142,13 +140,6 @@
// doesn't grow with NUM_FRAME_RECORDS.
int mNumFences;
- // mNumFrames keeps a count of the number of frames with a duration in a
- // particular range of vsync periods. Element n of the array stores the
- // number of frames with duration in the half-inclusive range
- // [2^n, 2^(n+1)). The last element of the array contains the count for
- // all frames with duration greater than 2^(NUM_FRAME_BUCKETS-1).
- int32_t mNumFrames[NUM_FRAME_BUCKETS];
-
// mDisplayPeriod is the display refresh period of the display for which
// this FrameTracker is gathering information.
nsecs_t mDisplayPeriod;
diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
index 0788d1a..07a5724 100644
--- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
+++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h
@@ -61,6 +61,7 @@
ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK;
uint32_t parentId = UNASSIGNED_LAYER_ID;
uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID;
+ std::atomic<int32_t>* pendingBuffers = 0;
};
} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/FrontEnd/LayerHandle.cpp b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
index 75e4e3a..ffd51a4 100644
--- a/services/surfaceflinger/FrontEnd/LayerHandle.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHandle.cpp
@@ -28,7 +28,7 @@
LayerHandle::~LayerHandle() {
if (mFlinger) {
- mFlinger->onHandleDestroyed(this, mLayer, mLayerId);
+ mFlinger->onHandleDestroyed(mLayer, mLayerId);
}
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 398e64a..b7d4cc5 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -72,7 +72,7 @@
bool premultipliedAlpha;
ui::Transform parentTransform;
Rect bufferSize;
- Rect croppedBufferSize;
+ FloatRect croppedBufferSize;
std::shared_ptr<renderengine::ExternalTexture> externalTexture;
gui::LayerMetadata layerMetadata;
gui::LayerMetadata relativeLayerMetadata;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index ee605b7..10e212e 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -116,7 +116,7 @@
* that's already included.
*/
std::pair<FloatRect, bool> getInputBounds(const LayerSnapshot& snapshot, bool fillParentBounds) {
- FloatRect inputBounds = snapshot.croppedBufferSize.toFloatRect();
+ FloatRect inputBounds = snapshot.croppedBufferSize;
if (snapshot.hasBufferOrSidebandStream() && snapshot.croppedBufferSize.isValid() &&
snapshot.localTransform.getType() != ui::Transform::IDENTITY) {
inputBounds = snapshot.localTransform.transform(inputBounds);
@@ -220,7 +220,7 @@
}
// Check if the parent has cropped the buffer
- Rect bufferSize = snapshot.croppedBufferSize;
+ FloatRect bufferSize = snapshot.croppedBufferSize;
if (!bufferSize.isValid()) {
snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
return;
@@ -970,7 +970,7 @@
parentRoundedCorner.radius.y *= t.getScaleY();
}
- FloatRect layerCropRect = snapshot.croppedBufferSize.toFloatRect();
+ FloatRect layerCropRect = snapshot.croppedBufferSize;
const vec2 radius(requested.cornerRadius, requested.cornerRadius);
RoundedCornerState layerSettings(layerCropRect, radius);
const bool layerSettingsValid = layerSettings.hasRoundedCorners() && !layerCropRect.isEmpty();
@@ -1061,7 +1061,7 @@
requested.externalTexture ? snapshot.bufferSize.toFloatRect() : parentBounds;
snapshot.geomLayerCrop = parentBounds;
if (!requested.crop.isEmpty()) {
- snapshot.geomLayerCrop = snapshot.geomLayerCrop.intersect(requested.crop.toFloatRect());
+ snapshot.geomLayerCrop = snapshot.geomLayerCrop.intersect(requested.crop);
}
snapshot.geomLayerBounds = snapshot.geomLayerBounds.intersect(snapshot.geomLayerCrop);
snapshot.transformedBounds = snapshot.geomLayerTransform.transform(snapshot.geomLayerBounds);
@@ -1072,10 +1072,10 @@
snapshot.geomLayerTransform.transform(geomLayerBoundsWithoutTransparentRegion);
snapshot.parentTransform = parentSnapshot.geomLayerTransform;
- // Subtract the transparent region and snap to the bounds
- const Rect bounds =
- RequestedLayerState::reduce(snapshot.croppedBufferSize, requested.transparentRegion);
if (requested.potentialCursor) {
+ // Subtract the transparent region and snap to the bounds
+ const Rect bounds = RequestedLayerState::reduce(Rect(snapshot.croppedBufferSize),
+ requested.transparentRegion);
snapshot.cursorFrame = snapshot.geomLayerTransform.transform(bounds);
}
}
@@ -1192,7 +1192,8 @@
snapshot.inputInfo.inputConfig |= InputConfig::TRUSTED_OVERLAY;
}
- snapshot.inputInfo.contentSize = snapshot.croppedBufferSize.getSize();
+ snapshot.inputInfo.contentSize = {snapshot.croppedBufferSize.getHeight(),
+ snapshot.croppedBufferSize.getWidth()};
// If the layer is a clone, we need to crop the input region to cloned root to prevent
// touches from going outside the cloned area.
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 5734ccf..713a5c5 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -56,7 +56,8 @@
ownerUid(args.ownerUid),
ownerPid(args.ownerPid),
parentId(args.parentId),
- layerIdToMirror(args.layerIdToMirror) {
+ layerIdToMirror(args.layerIdToMirror),
+ pendingBuffers(args.pendingBuffers) {
layerId = static_cast<int32_t>(args.sequence);
changes |= RequestedLayerState::Changes::Created;
metadata.merge(args.metadata);
@@ -96,7 +97,7 @@
LLOGV(layerId, "Created %s flags=%d", getDebugString().c_str(), flags);
color.a = 1.0f;
- crop.makeInvalid();
+ crop = {0, 0, -1, -1};
z = 0;
layerStack = ui::DEFAULT_LAYER_STACK;
transformToDisplayInverse = false;
@@ -473,10 +474,10 @@
return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
}
-Rect RequestedLayerState::getCroppedBufferSize(const Rect& bufferSize) const {
- Rect size = bufferSize;
+FloatRect RequestedLayerState::getCroppedBufferSize(const Rect& bufferSize) const {
+ FloatRect size = bufferSize.toFloatRect();
if (!crop.isEmpty() && size.isValid()) {
- size.intersect(crop, &size);
+ size = size.intersect(crop);
} else if (!crop.isEmpty()) {
size = crop;
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 1d96dff..7ddd7ba 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -79,7 +79,7 @@
bool isHiddenByPolicy() const;
half4 getColor() const;
Rect getBufferSize(uint32_t displayRotationFlags) const;
- Rect getCroppedBufferSize(const Rect& bufferSize) const;
+ FloatRect getCroppedBufferSize(const Rect& bufferSize) const;
Rect getBufferCrop() const;
std::string getDebugString() const;
std::string getDebugStringShort() const;
@@ -131,6 +131,7 @@
uint64_t barrierFrameNumber = 0;
uint32_t barrierProducerId = 0;
std::string debugName;
+ std::atomic<int32_t>* pendingBuffers = 0;
// book keeping states
bool handleAlive = true;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index dcb0812..c14769e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -138,7 +138,7 @@
args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))) {
ALOGV("Creating Layer %s", getDebugName());
- mDrawingState.crop.makeInvalid();
+ mDrawingState.crop = {0, 0, -1, -1};
mDrawingState.sequence = 0;
mDrawingState.transform.set(0, 0);
mDrawingState.frameNumber = 0;
@@ -183,7 +183,6 @@
mFlinger->mTimeStats->onDestroy(layerId);
mFlinger->mFrameTracer->onDestroy(layerId);
- mFrameTracker.logAndResetStats(mName);
mFlinger->onLayerDestroyed(this);
if (mDrawingState.sidebandStream != nullptr) {
@@ -316,7 +315,7 @@
Rect Layer::getCroppedBufferSize(const State& s) const {
Rect size = getBufferSize(s);
- Rect crop = getCrop(s);
+ Rect crop = Rect(getCrop(s));
if (!crop.isEmpty() && size.isValid()) {
size.intersect(crop, &size);
} else if (!crop.isEmpty()) {
@@ -373,7 +372,7 @@
mTransactionFlags |= mask;
}
-bool Layer::setCrop(const Rect& crop) {
+bool Layer::setCrop(const FloatRect& crop) {
if (mDrawingState.crop == crop) return false;
mDrawingState.sequence++;
mDrawingState.crop = crop;
@@ -605,10 +604,6 @@
mFrameTracker.clearStats();
}
-void Layer::logFrameStats() {
- mFrameTracker.logAndResetStats(mName);
-}
-
void Layer::getFrameStats(FrameStats* outStats) const {
mFrameTracker.getStats(outStats);
}
@@ -1347,7 +1342,7 @@
}
void Layer::decrementPendingBufferCount() {
- int32_t pendingBuffers = --mPendingBufferTransactions;
+ int32_t pendingBuffers = --mPendingBuffers;
tracePendingBufferCount(pendingBuffers);
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 9bc557e..ce4b9c4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -95,7 +95,7 @@
struct State {
int32_t sequence; // changes when visible regions can change
// Crop is expressed in layer space coordinate.
- Rect crop;
+ FloatRect crop;
LayerMetadata metadata;
ui::Dataspace dataspace;
@@ -172,7 +172,7 @@
// be delayed until the resize completes.
// Buffer space
- bool setCrop(const Rect& crop);
+ bool setCrop(const FloatRect& crop);
bool setTransform(uint32_t /*transform*/);
bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/);
@@ -198,7 +198,7 @@
Region getVisibleRegion(const DisplayDevice*) const;
void updateLastLatchTime(nsecs_t latchtime);
- Rect getCrop(const Layer::State& s) const { return s.crop; }
+ Rect getCrop(const Layer::State& s) const { return Rect(s.crop); }
// from graphics API
static ui::Dataspace translateDataspace(ui::Dataspace dataspace);
@@ -369,7 +369,7 @@
// See mPendingBufferTransactions
void decrementPendingBufferCount();
- std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; }
+ std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBuffers; }
std::string getPendingBufferCounterName() { return mBlastTransactionName; }
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
@@ -562,7 +562,7 @@
// - If the integer increases, a buffer arrived at the server.
// - If the integer decreases in latchBuffer, that buffer was latched
// - If the integer decreases in setBuffer, a buffer was dropped
- std::atomic<int32_t> mPendingBufferTransactions{0};
+ std::atomic<int32_t> mPendingBuffers{0};
// Contains requested position and matrix updates. This will be applied if the client does
// not specify a destination frame.
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 5eea45b..44cd319 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -106,6 +106,13 @@
outRect.right = proto.right();
}
+void LayerProtoHelper::readFromProto(const perfetto::protos::RectProto& proto, FloatRect& outRect) {
+ outRect.left = proto.left();
+ outRect.top = proto.top();
+ outRect.bottom = proto.bottom();
+ outRect.right = proto.right();
+}
+
void LayerProtoHelper::writeToProto(
const FloatRect& rect,
std::function<perfetto::protos::FloatRectProto*()> getFloatRectProto) {
@@ -180,10 +187,6 @@
void LayerProtoHelper::writeToProto(
const WindowInfo& inputInfo,
std::function<perfetto::protos::InputWindowInfoProto*()> getInputWindowInfoProto) {
- if (inputInfo.token == nullptr) {
- return;
- }
-
perfetto::protos::InputWindowInfoProto* proto = getInputWindowInfoProto();
proto->set_layout_params_flags(inputInfo.layoutParamsFlags.get());
proto->set_input_config(inputInfo.inputConfig.get());
@@ -427,7 +430,7 @@
layerInfo->mutable_color_transform());
}
- LayerProtoHelper::writeToProto(snapshot.croppedBufferSize.toFloatRect(),
+ LayerProtoHelper::writeToProto(snapshot.croppedBufferSize,
[&]() { return layerInfo->mutable_source_bounds(); });
LayerProtoHelper::writeToProto(snapshot.transformedBounds,
[&]() { return layerInfo->mutable_screen_bounds(); });
@@ -455,7 +458,7 @@
return layerInfo->mutable_requested_position();
});
- LayerProtoHelper::writeToProto(requestedState.crop,
+ LayerProtoHelper::writeToProto(Rect(requestedState.crop),
[&]() { return layerInfo->mutable_crop(); });
layerInfo->set_is_opaque(snapshot.contentOpaque);
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index 41ea684..3ca553a 100644
--- a/services/surfaceflinger/LayerProtoHelper.h
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -44,6 +44,7 @@
std::function<perfetto::protos::RectProto*()> getRectProto);
static void writeToProto(const Rect& rect, perfetto::protos::RectProto* rectProto);
static void readFromProto(const perfetto::protos::RectProto& proto, Rect& outRect);
+ static void readFromProto(const perfetto::protos::RectProto& proto, FloatRect& outRect);
static void writeToProto(const FloatRect& rect,
std::function<perfetto::protos::FloatRectProto*()> getFloatRectProto);
static void writeToProto(const Region& region,
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index ffc1dd7..fa0ecee 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -5,6 +5,8 @@
domlaskowski@google.com
jreck@google.com
lpy@google.com
+mattbuckley@google.com
+melodymhsu@google.com
pdwilliams@google.com
racarr@google.com
ramindani@google.com
@@ -12,3 +14,4 @@
sallyqi@google.com
scroggo@google.com
vishnun@google.com
+xwxw@google.com
diff --git a/services/surfaceflinger/PowerAdvisor/OWNERS b/services/surfaceflinger/PowerAdvisor/OWNERS
new file mode 100644
index 0000000..9f40e27
--- /dev/null
+++ b/services/surfaceflinger/PowerAdvisor/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:/ADPF_OWNERS
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 218c56e..e385f18 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -426,8 +426,8 @@
LOG_FATAL_IF(!mVSyncState);
mVsyncTracer = (mVsyncTracer + 1) % 2;
- mPendingEvents.push_back(makeVSync(mVSyncState->displayId, wakeupTime, ++mVSyncState->count,
- vsyncTime, readyTime));
+ mPendingEvents.push_back(makeVSync(mVsyncSchedule->getPhysicalDisplayId(), wakeupTime,
+ ++mVSyncState->count, vsyncTime, readyTime));
mCondition.notify_all();
}
@@ -486,9 +486,9 @@
if (event->header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG) {
if (event->hotplug.connectionError == 0) {
if (event->hotplug.connected && !mVSyncState) {
- mVSyncState.emplace(event->header.displayId);
- } else if (!event->hotplug.connected && mVSyncState &&
- mVSyncState->displayId == event->header.displayId) {
+ mVSyncState.emplace();
+ } else if (!event->hotplug.connected &&
+ mVsyncSchedule->getPhysicalDisplayId() == event->header.displayId) {
mVSyncState.reset();
}
} else {
@@ -559,7 +559,7 @@
const auto now = systemTime(SYSTEM_TIME_MONOTONIC);
const auto deadlineTimestamp = now + timeout.count();
const auto expectedVSyncTime = deadlineTimestamp + timeout.count();
- mPendingEvents.push_back(makeVSync(mVSyncState->displayId, now,
+ mPendingEvents.push_back(makeVSync(mVsyncSchedule->getPhysicalDisplayId(), now,
++mVSyncState->count, expectedVSyncTime,
deadlineTimestamp));
}
@@ -739,7 +739,7 @@
StringAppendF(&result, "%s: state=%s VSyncState=", mThreadName, toCString(mState));
if (mVSyncState) {
StringAppendF(&result, "{displayId=%s, count=%u%s}\n",
- to_string(mVSyncState->displayId).c_str(), mVSyncState->count,
+ to_string(mVsyncSchedule->getPhysicalDisplayId()).c_str(), mVSyncState->count,
mVSyncState->synthetic ? ", synthetic" : "");
} else {
StringAppendF(&result, "none\n");
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index bbe4f9d..c3c7eb0 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -235,10 +235,6 @@
// VSYNC state of connected display.
struct VSyncState {
- explicit VSyncState(PhysicalDisplayId displayId) : displayId(displayId) {}
-
- const PhysicalDisplayId displayId;
-
// Number of VSYNC events since display was connected.
uint32_t count = 0;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 5e13154..b83ff19 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -426,6 +426,8 @@
eventThreadFor(cycle).onHdcpLevelsChanged(displayId, connectedLevel, maxLevel);
}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
bool Scheduler::onDisplayModeChanged(PhysicalDisplayId displayId, const FrameRateMode& mode) {
const bool isPacesetter =
FTL_FAKE_GUARD(kMainThreadContext,
@@ -446,6 +448,7 @@
return isPacesetter;
}
+#pragma clang diagnostic pop
void Scheduler::emitModeChangeIfNeeded() {
if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) {
@@ -483,6 +486,8 @@
}
}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
void Scheduler::updatePhaseConfiguration(PhysicalDisplayId displayId, Fps refreshRate) {
const bool isPacesetter =
FTL_FAKE_GUARD(kMainThreadContext,
@@ -494,6 +499,7 @@
setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
refreshRate.getPeriod());
}
+#pragma clang diagnostic pop
void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) {
mRefreshRateStats->setPowerMode(powerMode);
@@ -909,6 +915,8 @@
}
}
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-value" // b/369277774
void Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) {
const bool changed = (std::scoped_lock(mPolicyLock),
updateFrameRateOverridesLocked(consideredSignals, displayRefreshRate));
@@ -917,6 +925,7 @@
onFrameRateOverridesChanged();
}
}
+#pragma clang diagnostic pop
bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals,
Fps displayRefreshRate) {
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
index 881d678..e63cbb2 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -112,6 +112,8 @@
bool getPendingHardwareVsyncState() const REQUIRES(kMainThreadContext);
+ PhysicalDisplayId getPhysicalDisplayId() const { return mId; }
+
protected:
using ControllerPtr = std::unique_ptr<VsyncController>;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 65a0ed3..cad179c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -946,16 +946,20 @@
}));
}));
- mLayerTracing.setTakeLayersSnapshotProtoFunction([&](uint32_t traceFlags) {
- auto snapshot = perfetto::protos::LayersSnapshotProto{};
- mScheduler
- ->schedule([&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
- snapshot = takeLayersSnapshotProto(traceFlags, TimePoint::now(),
- mLastCommittedVsyncId, true);
- })
- .wait();
- return snapshot;
- });
+ mLayerTracing.setTakeLayersSnapshotProtoFunction(
+ [&](uint32_t traceFlags,
+ const LayerTracing::OnLayersSnapshotCallback& onLayersSnapshot) {
+ // Do not wait the future to avoid deadlocks
+ // between main and Perfetto threads (b/313130597)
+ static_cast<void>(mScheduler->schedule(
+ [&, traceFlags, onLayersSnapshot]() FTL_FAKE_GUARD(mStateLock)
+ FTL_FAKE_GUARD(kMainThreadContext) {
+ auto snapshot =
+ takeLayersSnapshotProto(traceFlags, TimePoint::now(),
+ mLastCommittedVsyncId, true);
+ onLayersSnapshot(std::move(snapshot));
+ }));
+ });
// Commit secondary display(s).
processDisplayChangesLocked();
@@ -1211,6 +1215,7 @@
const auto mode = display->refreshRateSelector().getActiveMode();
info->activeDisplayModeId = ftl::to_underlying(mode.modePtr->getId());
info->renderFrameRate = mode.fps.getValue();
+ info->hasArrSupport = mode.modePtr->getVrrConfig() && FlagManager::getInstance().vrr_config();
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
@@ -1650,6 +1655,22 @@
outProperties->combinations.emplace_back(outCombination);
}
outProperties->supportMixedColorSpaces = aidlProperties.supportMixedColorSpaces;
+ if (aidlProperties.lutProperties.has_value()) {
+ 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));
+ }
+ outProperties->lutProperties.emplace(outLutProperties.begin(), outLutProperties.end());
+ }
return NO_ERROR;
}
@@ -3264,8 +3285,6 @@
// getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger
SFTRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
}
-
- logFrameStats(presentTime);
}
void SurfaceFlinger::commitTransactions() {
@@ -4719,6 +4738,7 @@
for (auto& state : states) {
resolvedStates.emplace_back(std::move(state));
auto& resolvedState = resolvedStates.back();
+ resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
if (resolvedState.state.hasBufferChanges() && resolvedState.state.hasValidBuffer() &&
resolvedState.state.surface) {
sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface);
@@ -4730,9 +4750,8 @@
if (resolvedState.externalTexture) {
resolvedState.state.bufferData->buffer = resolvedState.externalTexture->getBuffer();
}
- mBufferCountTracker.increment(resolvedState.state.surface->localBinder());
+ mBufferCountTracker.increment(resolvedState.layerId);
}
- resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface);
if (resolvedState.state.what & layer_state_t::eReparent) {
resolvedState.parentId =
getLayerIdFromSurfaceControl(resolvedState.state.parentSurfaceControlForChild);
@@ -5175,8 +5194,9 @@
std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
if (pendingBufferCounter) {
std::string counterName = layer->getPendingBufferCounterName();
- mBufferCountTracker.add(outResult.handle->localBinder(), counterName,
+ mBufferCountTracker.add(LayerHandle::getLayerId(outResult.handle), counterName,
pendingBufferCounter);
+ args.pendingBuffers = pendingBufferCounter;
}
} break;
default:
@@ -5223,7 +5243,7 @@
return NO_ERROR;
}
-void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId) {
+void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer, uint32_t layerId) {
{
// Used to remove stalled transactions which uses an internal lock.
ftl::FakeGuard guard(kMainThreadContext);
@@ -5236,7 +5256,7 @@
Mutex::Autolock stateLock(mStateLock);
layer->onHandleDestroyed();
- mBufferCountTracker.remove(handle);
+ mBufferCountTracker.remove(layerId);
layer.clear();
setTransactionFlags(eTransactionFlushNeeded | eTransactionNeeded);
}
@@ -5737,7 +5757,7 @@
void SurfaceFlinger::dumpFrontEnd(std::string& result) {
std::ostringstream out;
- out << "\nComposition list\n";
+ out << "\nComposition list (bottom to top)\n";
ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
for (const auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) {
if (lastPrintedLayerStackHeader != snapshot->outputFilter.layerStack) {
@@ -5765,7 +5785,7 @@
void SurfaceFlinger::dumpVisibleFrontEnd(std::string& result) {
std::ostringstream out;
- out << "\nComposition list\n";
+ out << "\nComposition list (bottom to top)\n";
ui::LayerStack lastPrintedLayerStackHeader = ui::INVALID_LAYER_STACK;
mLayerSnapshotBuilder.forEachVisibleSnapshot(
[&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
@@ -8620,6 +8640,7 @@
outInfo->activeDisplayModeId = info.activeDisplayModeId;
outInfo->renderFrameRate = info.renderFrameRate;
+ outInfo->hasArrSupport = info.hasArrSupport;
outInfo->supportedColorModes.clear();
outInfo->supportedColorModes.reserve(info.supportedColorModes.size());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 3eb72cc..db0e15e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -296,7 +296,7 @@
// Called when all clients have released all their references to
// this layer. The layer may still be kept alive by its parents but
// the client can no longer modify this layer directly.
- void onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32_t layerId);
+ void onHandleDestroyed(sp<Layer>& layer, uint32_t layerId);
TransactionCallbackInvoker& getTransactionCallbackInvoker() {
return mTransactionCallbackInvoker;
@@ -433,32 +433,32 @@
// This is done to avoid lock contention with the main thread.
class BufferCountTracker {
public:
- void increment(BBinder* layerHandle) {
+ void increment(uint32_t layerId) {
std::lock_guard<std::mutex> lock(mLock);
- auto it = mCounterByLayerHandle.find(layerHandle);
- if (it != mCounterByLayerHandle.end()) {
+ auto it = mCounterByLayerId.find(layerId);
+ if (it != mCounterByLayerId.end()) {
auto [name, pendingBuffers] = it->second;
int32_t count = ++(*pendingBuffers);
SFTRACE_INT(name.c_str(), count);
} else {
- ALOGW("Handle not found! %p", layerHandle);
+ ALOGW("Layer ID not found! %d", layerId);
}
}
- void add(BBinder* layerHandle, const std::string& name, std::atomic<int32_t>* counter) {
+ void add(uint32_t layerId, const std::string& name, std::atomic<int32_t>* counter) {
std::lock_guard<std::mutex> lock(mLock);
- mCounterByLayerHandle[layerHandle] = std::make_pair(name, counter);
+ mCounterByLayerId[layerId] = std::make_pair(name, counter);
}
- void remove(BBinder* layerHandle) {
+ void remove(uint32_t layerId) {
std::lock_guard<std::mutex> lock(mLock);
- mCounterByLayerHandle.erase(layerHandle);
+ mCounterByLayerId.erase(layerId);
}
private:
std::mutex mLock;
- std::unordered_map<BBinder*, std::pair<std::string, std::atomic<int32_t>*>>
- mCounterByLayerHandle GUARDED_BY(mLock);
+ std::unordered_map<uint32_t, std::pair<std::string, std::atomic<int32_t>*>>
+ mCounterByLayerId GUARDED_BY(mLock);
};
enum class BootStage {
diff --git a/services/surfaceflinger/Tracing/LayerDataSource.cpp b/services/surfaceflinger/Tracing/LayerDataSource.cpp
index ed1e2ec..cc0063c 100644
--- a/services/surfaceflinger/Tracing/LayerDataSource.cpp
+++ b/services/surfaceflinger/Tracing/LayerDataSource.cpp
@@ -82,10 +82,13 @@
}
}
-void LayerDataSource::OnStop(const LayerDataSource::StopArgs&) {
+void LayerDataSource::OnStop(const LayerDataSource::StopArgs& args) {
ALOGD("Received OnStop event (mode = 0x%02x, flags = 0x%02x)", mMode, mFlags);
if (auto* p = mLayerTracing.load()) {
- p->onStop(mMode);
+ // In dump mode we need to defer the stop (through HandleStopAsynchronously()) till
+ // the layers snapshot has been captured and written to perfetto. We must avoid writing
+ // to perfetto within the OnStop callback to prevent deadlocks (b/313130597).
+ p->onStop(mMode, mFlags, args.HandleStopAsynchronously());
}
}
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index d40b888..d78f9bb 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -32,7 +32,7 @@
namespace android {
LayerTracing::LayerTracing() {
- mTakeLayersSnapshotProto = [](uint32_t) { return perfetto::protos::LayersSnapshotProto{}; };
+ mTakeLayersSnapshotProto = [](uint32_t, const OnLayersSnapshotCallback&) {};
LayerDataSource::Initialize(*this);
}
@@ -45,7 +45,7 @@
}
void LayerTracing::setTakeLayersSnapshotProtoFunction(
- const std::function<perfetto::protos::LayersSnapshotProto(uint32_t)>& callback) {
+ const std::function<void(uint32_t, const OnLayersSnapshotCallback&)>& callback) {
mTakeLayersSnapshotProto = callback;
}
@@ -62,7 +62,10 @@
// It might take a while before a layers change occurs and a "spontaneous" snapshot is
// taken. Let's manually take a snapshot, so that the trace's first entry will contain
// the current layers state.
- addProtoSnapshotToOstream(mTakeLayersSnapshotProto(flags), Mode::MODE_ACTIVE);
+ auto onLayersSnapshot = [this](perfetto::protos::LayersSnapshotProto&& snapshot) {
+ addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_ACTIVE);
+ };
+ mTakeLayersSnapshotProto(flags, onLayersSnapshot);
ALOGD("Started active tracing (traced initial snapshot)");
break;
}
@@ -89,9 +92,7 @@
break;
}
case Mode::MODE_DUMP: {
- auto snapshot = mTakeLayersSnapshotProto(flags);
- addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_DUMP);
- ALOGD("Started dump tracing (dumped single snapshot)");
+ ALOGD("Started dump tracing");
break;
}
default: {
@@ -125,10 +126,27 @@
ALOGD("Flushed generated tracing");
}
-void LayerTracing::onStop(Mode mode) {
- if (mode == Mode::MODE_ACTIVE) {
- mIsActiveTracingStarted.store(false);
- ALOGD("Stopped active tracing");
+void LayerTracing::onStop(Mode mode, uint32_t flags, std::function<void()>&& deferredStopDone) {
+ switch (mode) {
+ case Mode::MODE_ACTIVE: {
+ mIsActiveTracingStarted.store(false);
+ deferredStopDone();
+ ALOGD("Stopped active tracing");
+ break;
+ }
+ case Mode::MODE_DUMP: {
+ auto onLayersSnapshot = [this, deferredStopDone = std::move(deferredStopDone)](
+ perfetto::protos::LayersSnapshotProto&& snapshot) {
+ addProtoSnapshotToOstream(std::move(snapshot), Mode::MODE_DUMP);
+ deferredStopDone();
+ ALOGD("Stopped dump tracing (written single snapshot)");
+ };
+ mTakeLayersSnapshotProto(flags, onLayersSnapshot);
+ break;
+ }
+ default: {
+ deferredStopDone();
+ }
}
}
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
index 2895ba7..e99fe4c 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.h
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -87,6 +87,7 @@
class LayerTracing {
public:
using Mode = perfetto::protos::pbzero::SurfaceFlingerLayersConfig::Mode;
+ using OnLayersSnapshotCallback = std::function<void(perfetto::protos::LayersSnapshotProto&&)>;
enum Flag : uint32_t {
TRACE_INPUT = 1 << 1,
@@ -102,7 +103,7 @@
LayerTracing(std::ostream&);
~LayerTracing();
void setTakeLayersSnapshotProtoFunction(
- const std::function<perfetto::protos::LayersSnapshotProto(uint32_t)>&);
+ const std::function<void(uint32_t, const OnLayersSnapshotCallback&)>&);
void setTransactionTracing(TransactionTracing&);
// Start event from perfetto data source
@@ -110,7 +111,7 @@
// Flush event from perfetto data source
void onFlush(Mode mode, uint32_t flags, bool isBugreport);
// Stop event from perfetto data source
- void onStop(Mode mode);
+ void onStop(Mode mode, uint32_t flags, std::function<void()>&& deferredStopDone);
void addProtoSnapshotToOstream(perfetto::protos::LayersSnapshotProto&& snapshot, Mode mode);
bool isActiveTracingStarted() const;
@@ -123,7 +124,7 @@
void writeSnapshotToPerfetto(const perfetto::protos::LayersSnapshotProto& snapshot, Mode mode);
bool checkAndUpdateLastVsyncIdWrittenToPerfetto(Mode mode, std::int64_t vsyncId);
- std::function<perfetto::protos::LayersSnapshotProto(uint32_t)> mTakeLayersSnapshotProto;
+ std::function<void(uint32_t, const OnLayersSnapshotCallback&)> mTakeLayersSnapshotProto;
TransactionTracing* mTransactionTracing;
std::atomic<bool> mIsActiveTracingStarted{false};
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index b189598..f39a4d2 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -147,7 +147,7 @@
proto.set_transform_to_display_inverse(layer.transformToDisplayInverse);
}
if (layer.what & layer_state_t::eCropChanged) {
- LayerProtoHelper::writeToProto(layer.crop, proto.mutable_crop());
+ LayerProtoHelper::writeToProto(Rect(layer.crop), proto.mutable_crop());
}
if (layer.what & layer_state_t::eBufferChanged) {
perfetto::protos::LayerState_BufferData* bufferProto = proto.mutable_buffer_data();
diff --git a/services/surfaceflinger/EventLog/EventLogTags.logtags b/services/surfaceflinger/surfaceflinger.logtags
similarity index 93%
rename from services/surfaceflinger/EventLog/EventLogTags.logtags
rename to services/surfaceflinger/surfaceflinger.logtags
index 6c851dd..e68d9f5 100644
--- a/services/surfaceflinger/EventLog/EventLogTags.logtags
+++ b/services/surfaceflinger/surfaceflinger.logtags
@@ -35,7 +35,6 @@
# 60100 - 60199 reserved for surfaceflinger
-60100 sf_frame_dur (window|3),(dur0|1),(dur1|1),(dur2|1),(dur3|1),(dur4|1),(dur5|1),(dur6|1)
60110 sf_stop_bootanim (time|2|3)
# NOTE - the range 1000000-2000000 is reserved for partners and others who
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 102e2b6..e40be51 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -11,6 +11,14 @@
} # adpf_gpu_sf
flag {
+ name: "arr_setframerate_api"
+ namespace: "core_graphics"
+ description: "New setFrameRate API for Android 16"
+ bug: "356987016"
+ is_fixed_read_only: true
+} # arr_setframerate_api
+
+flag {
name: "ce_fence_promise"
namespace: "window_surfaces"
description: "Moves logic for buffer release fences into LayerFE"
diff --git a/services/surfaceflinger/tests/benchmarks/Android.bp b/services/surfaceflinger/tests/benchmarks/Android.bp
index 1c47be34..22fca08 100644
--- a/services/surfaceflinger/tests/benchmarks/Android.bp
+++ b/services/surfaceflinger/tests/benchmarks/Android.bp
@@ -22,7 +22,6 @@
static_libs: [
"libgmock",
"libgtest",
- "libc++fs",
],
header_libs: [
"libsurfaceflinger_mocks_headers",
diff --git a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
index ae380ad..b472047 100644
--- a/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
+++ b/services/surfaceflinger/tests/common/LayerLifecycleManagerHelper.h
@@ -182,7 +182,7 @@
mLifecycleManager.applyTransactions(setZTransaction(id, z));
}
- void setCrop(uint32_t id, const Rect& crop) {
+ void setCrop(uint32_t id, const FloatRect& crop) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
@@ -193,6 +193,8 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setCrop(uint32_t id, const Rect& crop) { setCrop(id, crop.toFloatRect()); }
+
void setFlags(uint32_t id, uint32_t mask, uint32_t flags) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 23d3c16..4f72424 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -467,7 +467,7 @@
LayerProperties::FORMAT,
LayerProperties::USAGE |
GraphicBuffer::USAGE_HW_TEXTURE);
- layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+ layer.crop = FloatRect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
layer.externalTexture = buffer;
layer.bufferData->acquireFence = Fence::NO_FENCE;
layer.dataspace = ui::Dataspace::UNKNOWN;
@@ -664,7 +664,8 @@
NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
false);
layer.sidebandStream = stream;
- layer.crop = Rect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
+ layer.crop =
+ FloatRect(0, 0, SidebandLayerProperties::HEIGHT, SidebandLayerProperties::WIDTH);
}
static void setupHwcSetSourceCropBufferCallExpectations(CompositionTest* test) {
@@ -828,7 +829,7 @@
return frontend::RequestedLayerState(args);
});
- layer.crop = Rect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
+ layer.crop = FloatRect(0, 0, LayerProperties::HEIGHT, LayerProperties::WIDTH);
return layer;
}
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 75d2fa3..a35ae15 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -162,12 +162,12 @@
info.info.logicalHeight = 100;
info.info.logicalWidth = 200;
mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), info);
- Rect layerCrop(0, 0, 10, 20);
+ FloatRect layerCrop(0, 0, 10, 20);
setCrop(11, layerCrop);
EXPECT_TRUE(mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Geometry));
UPDATE_AND_VERIFY_WITH_DISPLAY_CHANGES(mSnapshotBuilder, STARTING_ZORDER);
EXPECT_EQ(getSnapshot(11)->geomCrop, layerCrop);
- EXPECT_EQ(getSnapshot(111)->geomLayerBounds, layerCrop.toFloatRect());
+ EXPECT_EQ(getSnapshot(111)->geomLayerBounds, layerCrop);
float maxHeight = static_cast<float>(info.info.logicalHeight * 10);
float maxWidth = static_cast<float>(info.info.logicalWidth * 10);
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 9efe73d..adbd868 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -100,7 +100,9 @@
const std::vector<Fps>& knownFrameRates() const { return mKnownFrameRates; }
using RefreshRateSelector::GetRankedFrameRatesCache;
- auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
+ auto& mutableGetRankedRefreshRatesCache() NO_THREAD_SAFETY_ANALYSIS {
+ return mGetRankedFrameRatesCache;
+ }
auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
GlobalSignals signals = {}, Fps pacesetterFps = {}) const {
@@ -138,7 +140,9 @@
return setPolicy(policy);
}
- const auto& getPrimaryFrameRates() const { return mPrimaryFrameRates; }
+ const auto& getPrimaryFrameRates() const NO_THREAD_SAFETY_ANALYSIS {
+ return mPrimaryFrameRates;
+ }
};
class RefreshRateSelectorTest : public testing::TestWithParam<Config::FrameRateOverride> {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index c043b88..4dec5f6 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -637,7 +637,7 @@
void destroyAllLayerHandles() {
ftl::FakeGuard guard(kMainThreadContext);
for (auto [layerId, legacyLayer] : mFlinger->mLegacyLayers) {
- mFlinger->onHandleDestroyed(nullptr, legacyLayer, layerId);
+ mFlinger->onHandleDestroyed(legacyLayer, layerId);
}
}
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index fab1f6d..1e8cd0a 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -387,7 +387,7 @@
state.state.what = what;
if (what & layer_state_t::eCropChanged) {
- state.state.crop = Rect(1, 2, 3, 4);
+ state.state.crop = FloatRect(1, 2, 3, 4);
}
if (what & layer_state_t::eFlagsChanged) {
state.state.flags = layer_state_t::eEnableBackpressure;
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index f472d8f..615cc94 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -182,7 +182,7 @@
MOCK_METHOD(Error, notifyExpectedPresent, (Display, nsecs_t, int32_t));
MOCK_METHOD(
Error, getRequestedLuts,
- (Display,
+ (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>&));
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index 5edd2cd..53ed2e1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -111,7 +111,7 @@
(aidl::android::hardware::graphics::composer3::OverlayProperties *),
(const override));
MOCK_METHOD(hal::Error, getRequestedLuts,
- (std::vector<aidl::android::hardware::graphics::composer3::DisplayLuts::LayerLut>*),
+ ((HWC2::Display::LayerLuts*), (HWC2::Display::LutFileDescriptorMapper&)),
(override));
};
diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING
index af48673..1eb3a58 100644
--- a/services/vibratorservice/TEST_MAPPING
+++ b/services/vibratorservice/TEST_MAPPING
@@ -4,11 +4,6 @@
"name": "libvibratorservice_test"
}
],
- "postsubmit": [
- {
- "name": "libvibratorservice_test"
- }
- ],
"imports": [
{
"path": "cts/tests/vibrator"
diff --git a/services/vibratorservice/VibratorHalWrapper.cpp b/services/vibratorservice/VibratorHalWrapper.cpp
index 4ac1618..b06ee3b 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -107,6 +107,10 @@
mInfoCache.mMaxEnvelopeEffectControlPointDuration =
getMaxEnvelopeEffectControlPointDurationInternal();
}
+ if (mInfoCache.mFrequencyToOutputAccelerationMap.isFailed()) {
+ mInfoCache.mFrequencyToOutputAccelerationMap =
+ getFrequencyToOutputAccelerationMapInternal();
+ }
return mInfoCache.get();
}
@@ -239,6 +243,13 @@
return HalResult<milliseconds>::unsupported();
}
+HalResult<std::vector<PwleV2OutputMapEntry>>
+HalWrapper::getFrequencyToOutputAccelerationMapInternal() {
+ ALOGV("Skipped getFrequencyToOutputAccelerationMapInternal because it's not "
+ "available in Vibrator HAL");
+ return HalResult<std::vector<PwleV2OutputMapEntry>>::unsupported();
+}
+
// -------------------------------------------------------------------------------------------------
HalResult<void> AidlHalWrapper::ping() {
@@ -487,6 +498,15 @@
return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(durationMs));
}
+HalResult<std::vector<PwleV2OutputMapEntry>>
+AidlHalWrapper::getFrequencyToOutputAccelerationMapInternal() {
+ std::vector<PwleV2OutputMapEntry> frequencyToOutputAccelerationMap;
+ auto status =
+ getHal()->getPwleV2FrequencyToOutputAccelerationMap(&frequencyToOutputAccelerationMap);
+ return HalResultFactory::fromStatus<
+ std::vector<PwleV2OutputMapEntry>>(std::move(status), frequencyToOutputAccelerationMap);
+}
+
std::shared_ptr<Aidl::IVibrator> AidlHalWrapper::getHal() {
std::lock_guard<std::mutex> lock(mHandleMutex);
return mHandle;
diff --git a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
index 4938b15..b2bfffc 100644
--- a/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
+++ b/services/vibratorservice/include/vibratorservice/VibratorHalWrapper.h
@@ -243,6 +243,7 @@
using EffectStrength = aidl::android::hardware::vibrator::EffectStrength;
using CompositePrimitive = aidl::android::hardware::vibrator::CompositePrimitive;
using Braking = aidl::android::hardware::vibrator::Braking;
+ using PwleV2OutputMapEntry = aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
const HalResult<Capabilities> capabilities;
const HalResult<std::vector<Effect>> supportedEffects;
@@ -261,6 +262,7 @@
const HalResult<int32_t> maxEnvelopeEffectSize;
const HalResult<std::chrono::milliseconds> minEnvelopeEffectControlPointDuration;
const HalResult<std::chrono::milliseconds> maxEnvelopeEffectControlPointDuration;
+ const HalResult<std::vector<PwleV2OutputMapEntry>> frequencyToOutputAccelerationMap;
void logFailures() const {
logFailure<Capabilities>(capabilities, "getCapabilities");
@@ -284,6 +286,8 @@
"getMinEnvelopeEffectControlPointDuration");
logFailure<std::chrono::milliseconds>(maxEnvelopeEffectControlPointDuration,
"getMaxEnvelopeEffectControlPointDuration");
+ logFailure<std::vector<PwleV2OutputMapEntry>>(frequencyToOutputAccelerationMap,
+ "getfrequencyToOutputAccelerationMap");
}
bool shouldRetry() const {
@@ -296,7 +300,8 @@
qFactor.shouldRetry() || maxAmplitudes.shouldRetry() ||
maxEnvelopeEffectSize.shouldRetry() ||
minEnvelopeEffectControlPointDuration.shouldRetry() ||
- maxEnvelopeEffectControlPointDuration.shouldRetry();
+ maxEnvelopeEffectControlPointDuration.shouldRetry() ||
+ frequencyToOutputAccelerationMap.shouldRetry();
}
private:
@@ -327,7 +332,8 @@
mMaxAmplitudes,
mMaxEnvelopeEffectSize,
mMinEnvelopeEffectControlPointDuration,
- mMaxEnvelopeEffectControlPointDuration};
+ mMaxEnvelopeEffectControlPointDuration,
+ mFrequencyToOutputAccelerationMap};
}
private:
@@ -359,6 +365,8 @@
HalResult<std::chrono::milliseconds>::transactionFailed(MSG);
HalResult<std::chrono::milliseconds> mMaxEnvelopeEffectControlPointDuration =
HalResult<std::chrono::milliseconds>::transactionFailed(MSG);
+ HalResult<std::vector<Info::PwleV2OutputMapEntry>> mFrequencyToOutputAccelerationMap =
+ HalResult<std::vector<Info::PwleV2OutputMapEntry>>::transactionFailed(MSG);
friend class HalWrapper;
};
@@ -442,6 +450,8 @@
virtual HalResult<int32_t> getMaxEnvelopeEffectSizeInternal();
virtual HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal();
virtual HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal();
+ virtual HalResult<std::vector<PwleV2OutputMapEntry>>
+ getFrequencyToOutputAccelerationMapInternal();
private:
std::mutex mInfoMutex;
@@ -518,12 +528,12 @@
HalResult<float> getQFactorInternal() override final;
HalResult<std::vector<float>> getMaxAmplitudesInternal() override final;
HalResult<int32_t> getMaxEnvelopeEffectSizeInternal() override final;
-
HalResult<std::chrono::milliseconds> getMinEnvelopeEffectControlPointDurationInternal()
override final;
-
HalResult<std::chrono::milliseconds> getMaxEnvelopeEffectControlPointDurationInternal()
override final;
+ HalResult<std::vector<PwleV2OutputMapEntry>> getFrequencyToOutputAccelerationMapInternal()
+ override final;
private:
const reconnect_fn mReconnectFn;
diff --git a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
index 17f384d..d42aa56 100644
--- a/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperAidlTest.cpp
@@ -39,6 +39,7 @@
using aidl::android::hardware::vibrator::IVibrator;
using aidl::android::hardware::vibrator::IVibratorCallback;
using aidl::android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::PwleV2OutputMapEntry;
using aidl::android::hardware::vibrator::PwleV2Primitive;
using aidl::android::hardware::vibrator::VendorEffect;
using aidl::android::os::PersistableBundle;
@@ -242,6 +243,11 @@
std::vector<CompositePrimitive> supportedPrimitives = {CompositePrimitive::CLICK};
std::vector<Braking> supportedBraking = {Braking::CLAB};
std::vector<float> amplitudes = {0.f, 1.f, 0.f};
+ std::vector<PwleV2OutputMapEntry>
+ frequencyToOutputAccelerationMap{PwleV2OutputMapEntry(/*frequency=*/30.0f,
+ /*maxOutputAcceleration=*/0.2),
+ PwleV2OutputMapEntry(/*frequency=*/60.0f,
+ /*maxOutputAcceleration=*/0.8)};
std::vector<std::chrono::milliseconds> primitiveDurations;
constexpr auto primitiveRange = ndk::enum_range<CompositePrimitive>();
@@ -323,6 +329,11 @@
.WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
.WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2FrequencyToOutputAccelerationMap(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(frequencyToOutputAccelerationMap),
+ Return(ndk::ScopedAStatus::ok())));
vibrator::Info failed = mWrapper->getInfo();
ASSERT_TRUE(failed.capabilities.isFailed());
@@ -342,6 +353,7 @@
ASSERT_TRUE(failed.maxEnvelopeEffectSize.isFailed());
ASSERT_TRUE(failed.minEnvelopeEffectControlPointDuration.isFailed());
ASSERT_TRUE(failed.maxEnvelopeEffectControlPointDuration.isFailed());
+ ASSERT_TRUE(failed.frequencyToOutputAccelerationMap.isFailed());
vibrator::Info successful = mWrapper->getInfo();
ASSERT_EQ(vibrator::Capabilities::ON_CALLBACK, successful.capabilities.value());
@@ -364,6 +376,8 @@
successful.minEnvelopeEffectControlPointDuration.value());
ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
successful.maxEnvelopeEffectControlPointDuration.value());
+ ASSERT_EQ(frequencyToOutputAccelerationMap,
+ successful.frequencyToOutputAccelerationMap.value());
}
TEST_F(VibratorHalWrapperAidlTest, TestGetInfoCachesResult) {
@@ -377,6 +391,11 @@
constexpr int32_t PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS = 20;
constexpr int32_t PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS = 1000;
std::vector<Effect> supportedEffects = {Effect::CLICK, Effect::TICK};
+ std::vector<PwleV2OutputMapEntry>
+ frequencyToOutputAccelerationMap{PwleV2OutputMapEntry(/*frequency=*/30.0f,
+ /*maxOutputAcceleration=*/0.2),
+ PwleV2OutputMapEntry(/*frequency=*/60.0f,
+ /*maxOutputAcceleration=*/0.8)};
EXPECT_CALL(*mMockHal.get(), getCapabilities(_))
.Times(Exactly(1))
@@ -432,6 +451,10 @@
.Times(Exactly(1))
.WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2FrequencyToOutputAccelerationMap(_))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<0>(frequencyToOutputAccelerationMap),
+ Return(ndk::ScopedAStatus::ok())));
std::vector<std::thread> threads;
for (int i = 0; i < 10; i++) {
@@ -460,6 +483,7 @@
info.minEnvelopeEffectControlPointDuration.value());
ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MIN_REQUIRED_PRIMITIVE_MAX_DURATION_MS),
info.maxEnvelopeEffectControlPointDuration.value());
+ ASSERT_EQ(frequencyToOutputAccelerationMap, info.frequencyToOutputAccelerationMap.value());
}
TEST_F(VibratorHalWrapperAidlTest, TestPerformEffectWithCallbackSupport) {
diff --git a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
index a09ddec..d6dab8d 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -223,6 +223,7 @@
ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
+ ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
}
TEST_F(VibratorHalWrapperHidlV1_0Test, TestGetInfoWithoutAmplitudeControl) {
@@ -259,6 +260,7 @@
ASSERT_TRUE(info.maxEnvelopeEffectSize.isUnsupported());
ASSERT_TRUE(info.minEnvelopeEffectControlPointDuration.isUnsupported());
ASSERT_TRUE(info.maxEnvelopeEffectControlPointDuration.isUnsupported());
+ ASSERT_TRUE(info.frequencyToOutputAccelerationMap.isUnsupported());
}
TEST_F(VibratorHalWrapperHidlV1_0Test, TestPerformEffect) {
diff --git a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
index 764d9be..ca13c0b 100644
--- a/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
+++ b/services/vibratorservice/test/VibratorManagerHalWrapperAidlTest.cpp
@@ -31,10 +31,12 @@
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::IVibrator;
using aidl::android::hardware::vibrator::IVibratorCallback;
using aidl::android::hardware::vibrator::IVibratorManager;
using aidl::android::hardware::vibrator::PrimitivePwle;
+using aidl::android::hardware::vibrator::VibrationSessionConfig;
using namespace android;
using namespace testing;
@@ -55,6 +57,12 @@
MOCK_METHOD(ndk::ScopedAStatus, triggerSynced, (const std::shared_ptr<IVibratorCallback>& cb),
(override));
MOCK_METHOD(ndk::ScopedAStatus, cancelSynced, (), (override));
+ MOCK_METHOD(ndk::ScopedAStatus, startSession,
+ (const std::vector<int32_t>& ids, const VibrationSessionConfig& s,
+ const std::shared_ptr<IVibratorCallback>& cb,
+ std::shared_ptr<IVibrationSession>* ret),
+ (override));
+ MOCK_METHOD(ndk::ScopedAStatus, clearSessions, (), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t*), (override));
MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string*), (override));
MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index 0284192..bfb7bd6 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -1169,7 +1169,7 @@
return array;
}
-template <typename T, unsigned int N>
+template <typename T, size_t N>
inline Json::Value ToJsonValue(const T (&value)[N]) {
return ArrayToJsonValue(N, value);
}
@@ -1293,7 +1293,7 @@
return true;
}
-template <typename T, unsigned int N>
+template <typename T, size_t N>
inline bool AsValue(Json::Value* json_value, T (*value)[N]) {
return AsArray(json_value, N, *value);
}