Merge "Add SYSUI session tag and update PowerHAL version" 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 220fef6..d24edc4 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -170,6 +170,7 @@
#define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0"
#define BLK_DEV_SYS_DIR "/sys/block"
+#define AFLAGS "/system/bin/aflags"
#define RECOVERY_DIR "/cache/recovery"
#define RECOVERY_DATA_DIR "/data/misc/recovery"
#define UPDATE_ENGINE_LOG_DIR "/data/misc/update_engine_log"
@@ -205,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() \
@@ -764,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();
}
@@ -1792,6 +1800,10 @@
RunCommand("ACONFIG FLAGS", {PRINT_FLAGS},
CommandOptions::WithTimeout(10).Always().DropRoot().Build());
+ RunCommand("ACONFIG FLAGS DUMP", {AFLAGS, "list"},
+ CommandOptions::WithTimeout(10).Always().AsRootIfAvailable().Build());
+ RunCommand("WHICH ACONFIG FLAG STORAGE", {AFLAGS, "which-backing"},
+ CommandOptions::WithTimeout(10).Always().AsRootIfAvailable().Build());
RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"});
@@ -3437,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) {
@@ -3533,7 +3547,7 @@
// the dumpstate's own activity which is irrelevant.
RunCommand(
SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", "--save-for-bugreport"},
- CommandOptions::WithTimeout(10).DropRoot().CloseAllFileDescriptorsOnExec().Build(),
+ CommandOptions::WithTimeout(30).DropRoot().CloseAllFileDescriptorsOnExec().Build(),
false, outFd);
// MaybeAddSystemTraceToZip() will take care of copying the trace in the zip
// file in the later stages.
@@ -3724,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;
@@ -3820,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 82cacca..8d61e77 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -145,6 +145,9 @@
* Buffers which are replaced or removed from the scene in the transaction invoking
* this callback may be reused after this point.
*
+ * Starting with API level 36, prefer using \a ASurfaceTransaction_OnBufferRelease to listen
+ * to when a buffer is ready to be reused.
+ *
* \param context Optional context provided by the client that is passed into
* the callback.
*
@@ -153,12 +156,9 @@
*
* 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)
- __INTRODUCED_IN(29);
+ ASurfaceTransactionStats* _Nonnull stats);
/**
* The ASurfaceTransaction_OnCommit callback is invoked when transaction is applied and the updates
@@ -182,12 +182,36 @@
*
* 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)
- __INTRODUCED_IN(31);
+ ASurfaceTransactionStats* _Nonnull stats);
+
+/**
+ * The ASurfaceTransaction_OnBufferRelease callback is invoked when a buffer that was passed in
+ * ASurfaceTransaction_setBuffer is ready to be reused.
+ *
+ * This callback is guaranteed to be invoked if ASurfaceTransaction_setBuffer is called with a non
+ * null buffer. If the buffer in the transaction is replaced via another call to
+ * ASurfaceTransaction_setBuffer, the callback will be invoked immediately. Otherwise the callback
+ * will be invoked before the ASurfaceTransaction_OnComplete callback after the buffer was
+ * presented.
+ *
+ * If this callback is set, caller should not release the buffer using the
+ * ASurfaceTransaction_OnComplete.
+ *
+ * \param context Optional context provided by the client that is passed into the callback.
+ *
+ * \param release_fence_fd Returns the fence file descriptor used to signal the release of buffer
+ * associated with this callback. If this fence is valid (>=0), the buffer has not yet been released
+ * and the fence will signal when the buffer has been released. If the fence is -1 , the buffer is
+ * already released. The recipient of the callback takes ownership of the fence fd and is
+ * responsible for closing it.
+ *
+ * THREADING
+ * The callback can be invoked on any thread.
+ */
+typedef void (*ASurfaceTransaction_OnBufferRelease)(void* _Null_unspecified context,
+ int release_fence_fd);
/**
* Returns the timestamp of when the frame was latched by the framework. Once a frame is
@@ -251,7 +275,7 @@
/**
* The returns the fence used to signal the release of the PREVIOUS buffer set on
* this surface. If this fence is valid (>=0), the PREVIOUS buffer has not yet been released and the
- * fence will signal when the PREVIOUS buffer has been released. If the fence is -1 , the PREVIOUS
+ * fence will signal when the PREVIOUS buffer has been released. If the fence is -1, the PREVIOUS
* buffer is already released. The recipient of the callback takes ownership of the
* previousReleaseFenceFd and is responsible for closing it.
*
@@ -353,6 +377,9 @@
* Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE
* as the surface control might be composited using the GPU.
*
+ * Starting with API level 36, prefer using \a ASurfaceTransaction_setBufferWithRelease to
+ * set a buffer and a callback which will be invoked when the buffer is ready to be reused.
+ *
* Available since API level 29.
*/
void ASurfaceTransaction_setBuffer(ASurfaceTransaction* _Nonnull transaction,
@@ -361,6 +388,29 @@
__INTRODUCED_IN(29);
/**
+ * Updates the AHardwareBuffer displayed for \a surface_control. If not -1, the
+ * acquire_fence_fd should be a file descriptor that is signaled when all pending work
+ * for the buffer is complete and the buffer can be safely read.
+ *
+ * The frameworks takes ownership of the \a acquire_fence_fd passed and is responsible
+ * for closing it.
+ *
+ * Note that the buffer must be allocated with AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE
+ * as the surface control might be composited using the GPU.
+ *
+ * When the buffer is ready to be reused, the ASurfaceTransaction_OnBufferRelease
+ * callback will be invoked. If the buffer is null, the callback will not be invoked.
+ *
+ * Available since API level 36.
+ */
+void ASurfaceTransaction_setBufferWithRelease(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ AHardwareBuffer* _Nonnull buffer,
+ int acquire_fence_fd, void* _Null_unspecified context,
+ ASurfaceTransaction_OnBufferRelease _Nonnull func)
+ __INTRODUCED_IN(36);
+
+/**
* Updates the color for \a surface_control. This will make the background color for the
* ASurfaceControl visible in transparent regions of the surface. Colors \a r, \a g,
* and \a b must be within the range that is valid for \a dataspace. \a dataspace and \a alpha
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 65c2914..228347d 100644
--- a/include/input/InputConsumerNoResampling.h
+++ b/include/input/InputConsumerNoResampling.h
@@ -16,6 +16,12 @@
#pragma once
+#include <functional>
+#include <map>
+#include <memory>
+#include <optional>
+
+#include <input/Input.h>
#include <input/InputTransport.h>
#include <input/Resampler.h>
#include <utils/Looper.h>
@@ -35,7 +41,7 @@
/**
* When you receive this callback, you must (eventually) call "consumeBatchedInputEvents".
* If you don't want batching, then call "consumeBatchedInputEvents" immediately with
- * std::nullopt frameTime to receive the pending motion event(s).
+ * std::nullopt requestedFrameTime to receive the pending motion event(s).
* @param pendingBatchSource the source of the pending batch.
*/
virtual void onBatchedInputEventPending(int32_t pendingBatchSource) = 0;
@@ -70,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();
@@ -85,15 +92,17 @@
void finishInputEvent(uint32_t seq, bool handled);
void reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime);
/**
- * If you want to consume all events immediately (disable batching), the you still must call
- * this. For frameTime, use a std::nullopt.
- * @param frameTime the time up to which consume the events. When there's double (or triple)
- * buffering, you may want to not consume all events currently available, because you could be
- * still working on an older frame, but there could already have been events that arrived that
- * are more recent.
+ * If you want to consume all events immediately (disable batching), then you still must call
+ * this. For requestedFrameTime, use a std::nullopt. It is not guaranteed that the consumption
+ * will occur at requestedFrameTime. The resampling strategy may modify it.
+ * @param requestedFrameTime the time up to which consume the events. When there's double (or
+ * triple) buffering, you may want to not consume all events currently available, because you
+ * could be still working on an older frame, but there could already have been events that
+ * arrived that are more recent.
* @return whether any events were actually consumed
*/
- bool consumeBatchedInputEvents(std::optional<nsecs_t> frameTime);
+ bool consumeBatchedInputEvents(std::optional<nsecs_t> requestedFrameTime);
+
/**
* Returns true when there is *likely* a pending batch or a pending event in the channel.
*
@@ -110,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
/**
@@ -183,26 +198,42 @@
/**
* 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);
/**
* Batched InputMessages, per deviceId.
* For each device, we are storing a queue of batched messages. These will all be collapsed into
- * a single MotionEvent (up to a specific frameTime) when the consumer calls
+ * a single MotionEvent (up to a specific requestedFrameTime) when the consumer calls
* `consumeBatchedInputEvents`.
*/
std::map<DeviceId, std::queue<InputMessage>> mBatches;
/**
* Creates a MotionEvent by consuming samples from the provided queue. If one message has
- * eventTime > frameTime, all subsequent messages in the queue will be skipped. It is assumed
- * that messages are queued in chronological order. In other words, only events that occurred
- * prior to the requested frameTime will be consumed.
- * @param frameTime the time up to which to consume events
+ * eventTime > adjustedFrameTime, all subsequent messages in the queue will be skipped. It is
+ * assumed that messages are queued in chronological order. In other words, only events that
+ * occurred prior to the adjustedFrameTime will be consumed.
+ * @param requestedFrameTime the time up to which to consume events.
* @param messages the queue of messages to consume from
*/
std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>> createBatchedMotionEvent(
- const nsecs_t frameTime, std::queue<InputMessage>& messages);
+ const nsecs_t requestedFrameTime, std::queue<InputMessage>& messages);
+
+ /**
+ * Consumes the batched input events, optionally allowing the caller to specify a device id
+ * and/or requestedFrameTime threshold. It is not guaranteed that consumption will occur at
+ * requestedFrameTime.
+ * @param deviceId The device id from which to consume events. If std::nullopt, consumes events
+ * from any device id.
+ * @param requestedFrameTime The time up to which consume the events. If std::nullopt, consumes
+ * input events with any timestamp.
+ * @return Whether or not any events were consumed.
+ */
+ bool consumeBatchedInputEvents(std::optional<DeviceId> deviceId,
+ std::optional<nsecs_t> requestedFrameTime);
/**
* A map from a single sequence number to several sequence numbers. This is needed because of
* batching. When batching is enabled, a single MotionEvent will contain several samples. Each
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 7d8c19e..1a48239 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -389,6 +389,7 @@
CONFIGURATION = 0, /* .idc file */
KEY_LAYOUT = 1, /* .kl file */
KEY_CHARACTER_MAP = 2, /* .kcm file */
+ ftl_last = KEY_CHARACTER_MAP,
};
/*
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/InputTransport.h b/include/input/InputTransport.h
index 7d11f76..0cd8720 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -263,7 +263,7 @@
* Return DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
- status_t sendMessage(const InputMessage* msg);
+ virtual status_t sendMessage(const InputMessage* msg);
/* Receive a message sent by the other endpoint.
*
@@ -275,14 +275,14 @@
* Return DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
- android::base::Result<InputMessage> receiveMessage();
+ virtual android::base::Result<InputMessage> receiveMessage();
/* Tells whether there is a message in the channel available to be received.
*
* This is only a performance hint and may return false negative results. Clients should not
* rely on availability of the message based on the return value.
*/
- bool probablyHasInput() const;
+ virtual bool probablyHasInput() const;
/* Wait until there is a message in the channel.
*
@@ -323,11 +323,12 @@
*/
sp<IBinder> getConnectionToken() const;
+protected:
+ InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
+
private:
static std::unique_ptr<InputChannel> create(const std::string& name,
android::base::unique_fd fd, sp<IBinder> token);
-
- InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
};
/*
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index 92d5ec4..7ea34c2 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -126,9 +126,9 @@
bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars,
Vector<KeyEvent>& outEvents) const;
- /* Maps an Android key code to another Android key code. This mapping is applied after scanCode
- * and usageCodes are mapped to corresponding Android Keycode */
- void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+ /* Maps some Android key code to another Android key code. This mapping is applied after
+ * scanCode and usageCodes are mapped to corresponding Android Keycode */
+ void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping);
/* Maps a scan code and usage code to a key code, in case this key map overrides
* the mapping in some way. */
@@ -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 2892137..47519c2 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -35,9 +35,9 @@
virtual ~Resampler() = default;
/**
- * Tries to resample motionEvent at resampleTime. The provided resampleTime must be greater than
+ * Tries to resample motionEvent at frameTime. The provided frameTime must be greater than
* the latest sample time of motionEvent. It is not guaranteed that resampling occurs at
- * resampleTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent
+ * frameTime. Interpolation may occur is futureSample is available. Otherwise, motionEvent
* may be resampled by another method, or not resampled at all. Furthermore, it is the
* implementer's responsibility to guarantee the following:
* - If resampling occurs, a single additional sample should be added to motionEvent. That is,
@@ -45,25 +45,34 @@
* samples by the end of the resampling. No other field of motionEvent should be modified.
* - If resampling does not occur, then motionEvent must not be modified in any way.
*/
- virtual void resampleMotionEvent(std::chrono::nanoseconds resampleTime,
- MotionEvent& motionEvent,
+ virtual void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
const InputMessage* futureSample) = 0;
+
+ /**
+ * Returns resample latency. Resample latency is the time difference between frame time and
+ * resample time. More precisely, let frameTime and resampleTime be two timestamps, and
+ * frameTime > resampleTime. Resample latency is defined as frameTime - resampleTime.
+ */
+ virtual std::chrono::nanoseconds getResampleLatency() const = 0;
};
class LegacyResampler final : public Resampler {
public:
/**
- * Tries to resample `motionEvent` at `resampleTime` by adding a resampled sample at the end of
+ * Tries to resample `motionEvent` at `frameTime` by adding a resampled sample at the end of
* `motionEvent` with eventTime equal to `resampleTime` and pointer coordinates determined by
* linear interpolation or linear extrapolation. An earlier `resampleTime` will be used if
* 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 resampleTime, MotionEvent& motionEvent,
+ void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
const InputMessage* futureSample) override;
+ std::chrono::nanoseconds getResampleLatency() const override;
+
private:
struct Pointer {
PointerProperties properties;
@@ -84,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.
@@ -97,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.
@@ -141,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 b6821d6..b7308c2 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -109,6 +109,10 @@
const int32_t* threadIds, size_t size,
int64_t initialTargetWorkDurationNanos, SessionTag tag);
+/**
+ * Forces FMQ to be enabled or disabled, for testing only.
+ */
+void APerformanceHint_setUseFMQForTesting(bool enabled);
__END_DECLS
diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp
index 319716e..cbba711 100644
--- a/libs/arect/Android.bp
+++ b/libs/arect/Android.bp
@@ -40,6 +40,7 @@
cc_library_headers {
name: "libarect_headers",
+ host_supported: true,
vendor_available: true,
min_sdk_version: "29",
// TODO(b/153609531): remove when no longer needed.
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/BackendUnifiedServiceManager.cpp b/libs/binder/BackendUnifiedServiceManager.cpp
index 54f687b..52b485a 100644
--- a/libs/binder/BackendUnifiedServiceManager.cpp
+++ b/libs/binder/BackendUnifiedServiceManager.cpp
@@ -24,11 +24,124 @@
namespace android {
+#ifdef LIBBINDER_CLIENT_CACHE
+constexpr bool kUseCache = true;
+#else
+constexpr bool kUseCache = false;
+#endif
+
using AidlServiceManager = android::os::IServiceManager;
using IAccessor = android::os::IAccessor;
+static const char* kStaticCachableList[] = {
+ // go/keep-sorted start
+ "accessibility",
+ "account",
+ "activity",
+ "alarm",
+ "android.system.keystore2.IKeystoreService/default",
+ "appops",
+ "audio",
+ "batterystats",
+ "carrier_config",
+ "connectivity",
+ "content",
+ "content_capture",
+ "device_policy",
+ "display",
+ "dropbox",
+ "econtroller",
+ "graphicsstats",
+ "input",
+ "input_method",
+ "isub",
+ "jobscheduler",
+ "legacy_permission",
+ "location",
+ "media.extractor",
+ "media.metrics",
+ "media.player",
+ "media.resource_manager",
+ "media_resource_monitor",
+ "mount",
+ "netd_listener",
+ "netstats",
+ "network_management",
+ "nfc",
+ "notification",
+ "package",
+ "package_native",
+ "performance_hint",
+ "permission",
+ "permission_checker",
+ "permissionmgr",
+ "phone",
+ "platform_compat",
+ "power",
+ "role",
+ "sensorservice",
+ "statscompanion",
+ "telephony.registry",
+ "thermalservice",
+ "time_detector",
+ "trust",
+ "uimode",
+ "user",
+ "virtualdevice",
+ "virtualdevice_native",
+ "webviewupdate",
+ "window",
+ // go/keep-sorted end
+};
+
+bool BinderCacheWithInvalidation::isClientSideCachingEnabled(const std::string& serviceName) {
+ if (ProcessState::self()->getThreadPoolMaxTotalThreadCount() <= 0) {
+ ALOGW("Thread Pool max thread count is 0. Cannot cache binder as linkToDeath cannot be "
+ "implemented. serviceName: %s",
+ serviceName.c_str());
+ return false;
+ }
+ for (const char* name : kStaticCachableList) {
+ if (name == serviceName) {
+ return true;
+ }
+ }
+ return false;
+}
+
+binder::Status BackendUnifiedServiceManager::updateCache(const std::string& serviceName,
+ const os::Service& service) {
+ if (!kUseCache) {
+ return binder::Status::ok();
+ }
+ if (service.getTag() == os::Service::Tag::binder) {
+ sp<IBinder> binder = service.get<os::Service::Tag::binder>();
+ if (binder && mCacheForGetService->isClientSideCachingEnabled(serviceName) &&
+ binder->isBinderAlive()) {
+ return mCacheForGetService->setItem(serviceName, binder);
+ }
+ }
+ return binder::Status::ok();
+}
+
+bool BackendUnifiedServiceManager::returnIfCached(const std::string& serviceName,
+ os::Service* _out) {
+ if (!kUseCache) {
+ return false;
+ }
+ sp<IBinder> item = mCacheForGetService->getItem(serviceName);
+ // TODO(b/363177618): Enable caching for binders which are always null.
+ if (item != nullptr && item->isBinderAlive()) {
+ *_out = os::Service::make<os::Service::Tag::binder>(item);
+ return true;
+ }
+ return false;
+}
+
BackendUnifiedServiceManager::BackendUnifiedServiceManager(const sp<AidlServiceManager>& impl)
- : mTheRealServiceManager(impl) {}
+ : mTheRealServiceManager(impl) {
+ mCacheForGetService = std::make_shared<BinderCacheWithInvalidation>();
+}
sp<AidlServiceManager> BackendUnifiedServiceManager::getImpl() {
return mTheRealServiceManager;
@@ -44,25 +157,64 @@
binder::Status BackendUnifiedServiceManager::getService2(const ::std::string& name,
os::Service* _out) {
+ if (returnIfCached(name, _out)) {
+ return binder::Status::ok();
+ }
os::Service service;
binder::Status status = mTheRealServiceManager->getService2(name, &service);
- toBinderService(service, _out);
+
+ if (status.isOk()) {
+ status = toBinderService(name, service, _out);
+ if (status.isOk()) {
+ return updateCache(name, service);
+ }
+ }
return status;
}
binder::Status BackendUnifiedServiceManager::checkService(const ::std::string& name,
os::Service* _out) {
os::Service service;
+ if (returnIfCached(name, _out)) {
+ return binder::Status::ok();
+ }
+
binder::Status status = mTheRealServiceManager->checkService(name, &service);
- toBinderService(service, _out);
+ if (status.isOk()) {
+ status = toBinderService(name, service, _out);
+ if (status.isOk()) {
+ return updateCache(name, service);
+ }
+ }
return status;
}
-void BackendUnifiedServiceManager::toBinderService(const os::Service& in, os::Service* _out) {
+binder::Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name,
+ const os::Service& in,
+ os::Service* _out) {
switch (in.getTag()) {
case os::Service::Tag::binder: {
+ if (in.get<os::Service::Tag::binder>() == nullptr) {
+ // failed to find a service. Check to see if we have any local
+ // injected Accessors for this service.
+ os::Service accessor;
+ binder::Status status = getInjectedAccessor(name, &accessor);
+ if (!status.isOk()) {
+ *_out = os::Service::make<os::Service::Tag::binder>(nullptr);
+ return status;
+ }
+ if (accessor.getTag() == os::Service::Tag::accessor &&
+ accessor.get<os::Service::Tag::accessor>() != nullptr) {
+ ALOGI("Found local injected service for %s, will attempt to create connection",
+ name.c_str());
+ // Call this again using the accessor Service to get the real
+ // service's binder into _out
+ return toBinderService(name, accessor, _out);
+ }
+ }
+
*_out = in;
- break;
+ return binder::Status::ok();
}
case os::Service::Tag::accessor: {
sp<IBinder> accessorBinder = in.get<os::Service::Tag::accessor>();
@@ -70,7 +222,7 @@
if (accessor == nullptr) {
ALOGE("Service#accessor doesn't have accessor. VM is maybe starting...");
*_out = os::Service::make<os::Service::Tag::binder>(nullptr);
- break;
+ return binder::Status::ok();
}
auto request = [=] {
os::ParcelFileDescriptor fd;
@@ -83,10 +235,15 @@
}
};
auto session = RpcSession::make();
- session->setupPreconnectedClient(base::unique_fd{}, request);
+ status_t status = session->setupPreconnectedClient(base::unique_fd{}, request);
+ if (status != OK) {
+ ALOGE("Failed to set up preconnected binder RPC client: %s",
+ statusToString(status).c_str());
+ return binder::Status::fromStatusT(status);
+ }
session->setSessionSpecificRoot(accessorBinder);
*_out = os::Service::make<os::Service::Tag::binder>(session->getRootObject());
- break;
+ return binder::Status::ok();
}
default: {
LOG_ALWAYS_FATAL("Unknown service type: %d", in.getTag());
@@ -177,4 +334,4 @@
return gUnifiedServiceManager;
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/binder/BackendUnifiedServiceManager.h b/libs/binder/BackendUnifiedServiceManager.h
index 8f3839f..47b2ec9 100644
--- a/libs/binder/BackendUnifiedServiceManager.h
+++ b/libs/binder/BackendUnifiedServiceManager.h
@@ -18,9 +18,87 @@
#include <android/os/BnServiceManager.h>
#include <android/os/IServiceManager.h>
#include <binder/IPCThreadState.h>
+#include <map>
+#include <memory>
namespace android {
+class BinderCacheWithInvalidation
+ : public std::enable_shared_from_this<BinderCacheWithInvalidation> {
+ class BinderInvalidation : public IBinder::DeathRecipient {
+ public:
+ BinderInvalidation(std::weak_ptr<BinderCacheWithInvalidation> cache, const std::string& key)
+ : mCache(cache), mKey(key) {}
+
+ void binderDied(const wp<IBinder>& who) override {
+ sp<IBinder> binder = who.promote();
+ if (std::shared_ptr<BinderCacheWithInvalidation> cache = mCache.lock()) {
+ cache->removeItem(mKey, binder);
+ } else {
+ ALOGI("Binder Cache pointer expired: %s", mKey.c_str());
+ }
+ }
+
+ private:
+ std::weak_ptr<BinderCacheWithInvalidation> mCache;
+ std::string mKey;
+ };
+ struct Entry {
+ sp<IBinder> service;
+ sp<BinderInvalidation> deathRecipient;
+ };
+
+public:
+ sp<IBinder> getItem(const std::string& key) const {
+ std::lock_guard<std::mutex> lock(mCacheMutex);
+
+ if (auto it = mCache.find(key); it != mCache.end()) {
+ return it->second.service;
+ }
+ return nullptr;
+ }
+
+ bool removeItem(const std::string& key, const sp<IBinder>& who) {
+ std::lock_guard<std::mutex> lock(mCacheMutex);
+ if (auto it = mCache.find(key); it != mCache.end()) {
+ if (it->second.service == who) {
+ status_t result = who->unlinkToDeath(it->second.deathRecipient);
+ if (result != DEAD_OBJECT) {
+ ALOGW("Unlinking to dead binder resulted in: %d", result);
+ }
+ mCache.erase(key);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ binder::Status setItem(const std::string& key, const sp<IBinder>& item) {
+ sp<BinderInvalidation> deathRecipient =
+ sp<BinderInvalidation>::make(shared_from_this(), key);
+
+ // linkToDeath if binder is a remote binder.
+ if (item->localBinder() == nullptr) {
+ status_t status = item->linkToDeath(deathRecipient);
+ if (status != android::OK) {
+ ALOGE("Failed to linkToDeath binder for service %s. Error: %d", key.c_str(),
+ status);
+ return binder::Status::fromStatusT(status);
+ }
+ }
+ std::lock_guard<std::mutex> lock(mCacheMutex);
+ Entry entry = {.service = item, .deathRecipient = deathRecipient};
+ mCache[key] = entry;
+ return binder::Status::ok();
+ }
+
+ bool isClientSideCachingEnabled(const std::string& serviceName);
+
+private:
+ std::map<std::string, Entry> mCache;
+ mutable std::mutex mCacheMutex;
+};
+
class BackendUnifiedServiceManager : public android::os::BnServiceManager {
public:
explicit BackendUnifiedServiceManager(const sp<os::IServiceManager>& impl);
@@ -58,10 +136,16 @@
}
private:
+ std::shared_ptr<BinderCacheWithInvalidation> mCacheForGetService;
sp<os::IServiceManager> mTheRealServiceManager;
- void toBinderService(const os::Service& in, os::Service* _out);
+ binder::Status toBinderService(const ::std::string& name, const os::Service& in,
+ os::Service* _out);
+ binder::Status updateCache(const std::string& serviceName, const os::Service& service);
+ bool returnIfCached(const std::string& serviceName, os::Service* _out);
};
sp<BackendUnifiedServiceManager> getBackendUnifiedServiceManager();
-} // namespace android
\ No newline at end of file
+android::binder::Status getInjectedAccessor(const std::string& name, android::os::Service* service);
+
+} // namespace android
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/FdTrigger.cpp b/libs/binder/FdTrigger.cpp
index 455a433..7263e23 100644
--- a/libs/binder/FdTrigger.cpp
+++ b/libs/binder/FdTrigger.cpp
@@ -82,7 +82,9 @@
int ret = TEMP_FAILURE_RETRY(poll(pfd, countof(pfd), -1));
if (ret < 0) {
- return -errno;
+ int saved_errno = errno;
+ ALOGE("FdTrigger poll returned error: %d, with error: %s", ret, strerror(saved_errno));
+ return -saved_errno;
}
LOG_ALWAYS_FATAL_IF(ret == 0, "poll(%d) returns 0 with infinite timeout", transportFd.fd.get());
@@ -106,6 +108,7 @@
// POLLNVAL: invalid FD number, e.g. not opened.
if (pfd[0].revents & POLLNVAL) {
+ LOG_ALWAYS_FATAL("Invalid FD number (%d) in FdTrigger (POLLNVAL)", pfd[0].fd);
return BAD_VALUE;
}
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 1d26d85..6698d0c 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -232,6 +232,15 @@
return cmd;
}
+static void printReturnCommandParcel(std::ostream& out, const Parcel& parcel) {
+ const void* cmds = parcel.data();
+ out << "\t" << HexDump(cmds, parcel.dataSize()) << "\n";
+ IF_LOG_COMMANDS() {
+ const void* end = parcel.data() + parcel.dataSize();
+ while (cmds < end) cmds = printReturnCommand(out, cmds);
+ }
+}
+
static const void* printCommand(std::ostream& out, const void* _cmd) {
static const size_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]);
const int32_t* cmd = (const int32_t*)_cmd;
@@ -1235,13 +1244,15 @@
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
- if (bwr.write_consumed < mOut.dataSize())
+ if (bwr.write_consumed < mOut.dataSize()) {
+ std::ostringstream logStream;
+ printReturnCommandParcel(logStream, mIn);
LOG_ALWAYS_FATAL("Driver did not consume write buffer. "
- "err: %s consumed: %zu of %zu",
- statusToString(err).c_str(),
- (size_t)bwr.write_consumed,
- mOut.dataSize());
- else {
+ "err: %s consumed: %zu of %zu.\n"
+ "Return command: %s",
+ statusToString(err).c_str(), (size_t)bwr.write_consumed,
+ mOut.dataSize(), logStream.str().c_str());
+ } else {
mOut.setDataSize(0);
processPostWriteDerefs();
}
@@ -1252,14 +1263,8 @@
}
IF_LOG_COMMANDS() {
std::ostringstream logStream;
- logStream << "Remaining data size: " << mOut.dataSize() << "\n";
- logStream << "Received commands from driver: ";
- const void* cmds = mIn.data();
- const void* end = mIn.data() + mIn.dataSize();
- logStream << "\t" << HexDump(cmds, mIn.dataSize()) << "\n";
- while (cmds < end) cmds = printReturnCommand(logStream, cmds);
- std::string message = logStream.str();
- ALOGI("%s", message.c_str());
+ printReturnCommandParcel(logStream, mIn);
+ ALOGI("%s", logStream.str().c_str());
}
return NO_ERROR;
}
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index c55dd9d..32388db 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -14,9 +14,11 @@
* limitations under the License.
*/
+#include <sys/socket.h>
#define LOG_TAG "ServiceManagerCppClient"
#include <binder/IServiceManager.h>
+#include <binder/IServiceManagerUnitTestHelper.h>
#include "BackendUnifiedServiceManager.h"
#include <inttypes.h>
@@ -24,14 +26,19 @@
#include <chrono>
#include <condition_variable>
+#include <FdTrigger.h>
+#include <RpcSocketAddress.h>
#include <android-base/properties.h>
+#include <android/os/BnAccessor.h>
#include <android/os/BnServiceCallback.h>
+#include <android/os/BnServiceManager.h>
#include <android/os/IAccessor.h>
#include <android/os/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
+#include <binder/RpcSession.h>
#include <utils/String8.h>
-
+#include <variant>
#ifndef __ANDROID_VNDK__
#include <binder/IPermissionController.h>
#endif
@@ -148,8 +155,151 @@
}
};
+class AccessorProvider {
+public:
+ AccessorProvider(std::set<std::string>&& instances, RpcAccessorProvider&& provider)
+ : mInstances(std::move(instances)), mProvider(std::move(provider)) {}
+ sp<IBinder> provide(const String16& name) {
+ if (mInstances.count(String8(name).c_str()) > 0) {
+ return mProvider(name);
+ } else {
+ return nullptr;
+ }
+ }
+ const std::set<std::string>& instances() { return mInstances; }
+
+private:
+ AccessorProvider() = delete;
+
+ std::set<std::string> mInstances;
+ RpcAccessorProvider mProvider;
+};
+
+class AccessorProviderEntry {
+public:
+ AccessorProviderEntry(std::shared_ptr<AccessorProvider>&& provider)
+ : mProvider(std::move(provider)) {}
+ std::shared_ptr<AccessorProvider> mProvider;
+
+private:
+ AccessorProviderEntry() = delete;
+};
+
[[clang::no_destroy]] static std::once_flag gSmOnce;
[[clang::no_destroy]] static sp<IServiceManager> gDefaultServiceManager;
+[[clang::no_destroy]] static std::mutex gAccessorProvidersMutex;
+[[clang::no_destroy]] static std::vector<AccessorProviderEntry> gAccessorProviders;
+
+class LocalAccessor : public android::os::BnAccessor {
+public:
+ LocalAccessor(const String16& instance, RpcSocketAddressProvider&& connectionInfoProvider)
+ : mInstance(instance), mConnectionInfoProvider(std::move(connectionInfoProvider)) {
+ LOG_ALWAYS_FATAL_IF(!mConnectionInfoProvider,
+ "LocalAccessor object needs a valid connection info provider");
+ }
+
+ ~LocalAccessor() {
+ if (mOnDelete) mOnDelete();
+ }
+
+ ::android::binder::Status addConnection(::android::os::ParcelFileDescriptor* outFd) {
+ using android::os::IAccessor;
+ sockaddr_storage addrStorage;
+ std::unique_ptr<FdTrigger> trigger = FdTrigger::make();
+ RpcTransportFd fd;
+ status_t status =
+ mConnectionInfoProvider(mInstance, reinterpret_cast<sockaddr*>(&addrStorage),
+ sizeof(addrStorage));
+ if (status != OK) {
+ const std::string error = "The connection info provider was unable to provide "
+ "connection info for instance " +
+ std::string(String8(mInstance).c_str()) +
+ " with status: " + statusToString(status);
+ ALOGE("%s", error.c_str());
+ return Status::fromServiceSpecificError(IAccessor::ERROR_CONNECTION_INFO_NOT_FOUND,
+ error.c_str());
+ }
+ if (addrStorage.ss_family == AF_VSOCK) {
+ sockaddr_vm* addr = reinterpret_cast<sockaddr_vm*>(&addrStorage);
+ status = singleSocketConnection(VsockSocketAddress(addr->svm_cid, addr->svm_port),
+ trigger, &fd);
+ } else if (addrStorage.ss_family == AF_UNIX) {
+ sockaddr_un* addr = reinterpret_cast<sockaddr_un*>(&addrStorage);
+ status = singleSocketConnection(UnixSocketAddress(addr->sun_path), trigger, &fd);
+ } else if (addrStorage.ss_family == AF_INET) {
+ sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(&addrStorage);
+ status = singleSocketConnection(InetSocketAddress(reinterpret_cast<sockaddr*>(addr),
+ sizeof(sockaddr_in),
+ inet_ntoa(addr->sin_addr),
+ ntohs(addr->sin_port)),
+ trigger, &fd);
+ } else {
+ const std::string error =
+ "Unsupported socket family type or the ConnectionInfoProvider failed to find a "
+ "valid address. Family type: " +
+ std::to_string(addrStorage.ss_family);
+ ALOGE("%s", error.c_str());
+ return Status::fromServiceSpecificError(IAccessor::ERROR_UNSUPPORTED_SOCKET_FAMILY,
+ error.c_str());
+ }
+ if (status != OK) {
+ const std::string error = "Failed to connect to socket for " +
+ std::string(String8(mInstance).c_str()) +
+ " with status: " + statusToString(status);
+ ALOGE("%s", error.c_str());
+ int err = 0;
+ if (status == -EACCES) {
+ err = IAccessor::ERROR_FAILED_TO_CONNECT_EACCES;
+ } else {
+ err = IAccessor::ERROR_FAILED_TO_CONNECT_TO_SOCKET;
+ }
+ return Status::fromServiceSpecificError(err, error.c_str());
+ }
+ *outFd = os::ParcelFileDescriptor(std::move(fd.fd));
+ return Status::ok();
+ }
+
+ ::android::binder::Status getInstanceName(String16* instance) {
+ *instance = mInstance;
+ return Status::ok();
+ }
+
+private:
+ LocalAccessor() = delete;
+ String16 mInstance;
+ RpcSocketAddressProvider mConnectionInfoProvider;
+ std::function<void()> mOnDelete;
+};
+
+android::binder::Status getInjectedAccessor(const std::string& name,
+ android::os::Service* service) {
+ std::vector<AccessorProviderEntry> copiedProviders;
+ {
+ std::lock_guard<std::mutex> lock(gAccessorProvidersMutex);
+ copiedProviders.insert(copiedProviders.begin(), gAccessorProviders.begin(),
+ gAccessorProviders.end());
+ }
+
+ // Unlocked to call the providers. This requires the providers to be
+ // threadsafe and not contain any references to objects that could be
+ // deleted.
+ for (const auto& provider : copiedProviders) {
+ sp<IBinder> binder = provider.mProvider->provide(String16(name.c_str()));
+ if (binder == nullptr) continue;
+ status_t status = validateAccessor(String16(name.c_str()), binder);
+ if (status != OK) {
+ ALOGE("A provider returned a binder that is not an IAccessor for instance %s. Status: "
+ "%s",
+ name.c_str(), statusToString(status).c_str());
+ return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
+ }
+ *service = os::Service::make<os::Service::Tag::accessor>(binder);
+ return android::binder::Status::ok();
+ }
+
+ *service = os::Service::make<os::Service::Tag::accessor>(nullptr);
+ return android::binder::Status::ok();
+}
sp<IServiceManager> defaultServiceManager()
{
@@ -172,6 +322,126 @@
}
}
+sp<IServiceManager> getServiceManagerShimFromAidlServiceManagerForTests(
+ const sp<AidlServiceManager>& sm) {
+ return sp<CppBackendShim>::make(sp<BackendUnifiedServiceManager>::make(sm));
+}
+
+// gAccessorProvidersMutex must be locked already
+static bool isInstanceProvidedLocked(const std::string& instance) {
+ return gAccessorProviders.end() !=
+ std::find_if(gAccessorProviders.begin(), gAccessorProviders.end(),
+ [&instance](const AccessorProviderEntry& entry) {
+ return entry.mProvider->instances().count(instance) > 0;
+ });
+}
+
+std::weak_ptr<AccessorProvider> addAccessorProvider(std::set<std::string>&& instances,
+ RpcAccessorProvider&& providerCallback) {
+ if (instances.empty()) {
+ ALOGE("Set of instances is empty! Need a non empty set of instances to provide for.");
+ return std::weak_ptr<AccessorProvider>();
+ }
+ std::lock_guard<std::mutex> lock(gAccessorProvidersMutex);
+ for (const auto& instance : instances) {
+ if (isInstanceProvidedLocked(instance)) {
+ ALOGE("The instance %s is already provided for by a previously added "
+ "RpcAccessorProvider.",
+ instance.c_str());
+ return std::weak_ptr<AccessorProvider>();
+ }
+ }
+ std::shared_ptr<AccessorProvider> provider =
+ std::make_shared<AccessorProvider>(std::move(instances), std::move(providerCallback));
+ std::weak_ptr<AccessorProvider> receipt = provider;
+ gAccessorProviders.push_back(AccessorProviderEntry(std::move(provider)));
+
+ return receipt;
+}
+
+status_t removeAccessorProvider(std::weak_ptr<AccessorProvider> wProvider) {
+ std::shared_ptr<AccessorProvider> provider = wProvider.lock();
+ if (provider == nullptr) {
+ ALOGE("The provider supplied to removeAccessorProvider has already been removed or the "
+ "argument to this function was nullptr.");
+ return BAD_VALUE;
+ }
+ std::lock_guard<std::mutex> lock(gAccessorProvidersMutex);
+ size_t sizeBefore = gAccessorProviders.size();
+ gAccessorProviders.erase(std::remove_if(gAccessorProviders.begin(), gAccessorProviders.end(),
+ [&](AccessorProviderEntry entry) {
+ return entry.mProvider == provider;
+ }),
+ gAccessorProviders.end());
+ if (sizeBefore == gAccessorProviders.size()) {
+ ALOGE("Failed to find an AccessorProvider for removeAccessorProvider");
+ return NAME_NOT_FOUND;
+ }
+
+ return OK;
+}
+
+status_t validateAccessor(const String16& instance, const sp<IBinder>& binder) {
+ if (binder == nullptr) {
+ ALOGE("Binder is null");
+ return BAD_VALUE;
+ }
+ 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;
+ }
+ String16 reportedInstance;
+ Status status = accessor->getInstanceName(&reportedInstance);
+ if (!status.isOk()) {
+ ALOGE("Failed to validate the binder being used to create a new ARpc_Accessor for %s with "
+ "status: %s",
+ String8(instance).c_str(), status.toString8().c_str());
+ return NAME_NOT_FOUND;
+ }
+ if (reportedInstance != instance) {
+ ALOGE("Instance %s doesn't match the Accessor's instance of %s", String8(instance).c_str(),
+ String8(reportedInstance).c_str());
+ return NAME_NOT_FOUND;
+ }
+ return OK;
+}
+
+sp<IBinder> createAccessor(const String16& instance,
+ RpcSocketAddressProvider&& connectionInfoProvider) {
+ // Try to create a new accessor
+ if (!connectionInfoProvider) {
+ ALOGE("Could not find an Accessor for %s and no ConnectionInfoProvider provided to "
+ "create a new one",
+ String8(instance).c_str());
+ return nullptr;
+ }
+ sp<IBinder> binder = sp<LocalAccessor>::make(instance, std::move(connectionInfoProvider));
+ 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/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 49def82..cd21a91 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -589,6 +589,21 @@
status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr,
const std::vector<uint8_t>& sessionId,
bool incoming) {
+ RpcTransportFd transportFd;
+ status_t status = singleSocketConnection(addr, mShutdownTrigger, &transportFd);
+ if (status != OK) return status;
+
+ return initAndAddConnection(std::move(transportFd), sessionId, incoming);
+}
+
+status_t singleSocketConnection(const RpcSocketAddress& addr,
+ const std::unique_ptr<FdTrigger>& shutdownTrigger,
+ RpcTransportFd* outFd) {
+ LOG_ALWAYS_FATAL_IF(outFd == nullptr,
+ "There is no reason to call this function without an outFd");
+ LOG_ALWAYS_FATAL_IF(shutdownTrigger == nullptr,
+ "FdTrigger argument is required so we don't get stuck in the connect call "
+ "if the server process shuts down.");
for (size_t tries = 0; tries < 5; tries++) {
if (tries > 0) usleep(10000);
@@ -620,7 +635,7 @@
if (connErrno == EAGAIN || connErrno == EINPROGRESS) {
// For non-blocking sockets, connect() may return EAGAIN (for unix domain socket) or
// EINPROGRESS (for others). Call poll() and getsockopt() to get the error.
- status_t pollStatus = mShutdownTrigger->triggerablePoll(transportFd, POLLOUT);
+ status_t pollStatus = shutdownTrigger->triggerablePoll(transportFd, POLLOUT);
if (pollStatus != OK) {
ALOGE("Could not POLLOUT after connect() on non-blocking socket: %s",
statusToString(pollStatus).c_str());
@@ -654,7 +669,8 @@
LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(),
transportFd.fd.get());
- return initAndAddConnection(std::move(transportFd), sessionId, incoming);
+ *outFd = std::move(transportFd);
+ return OK;
}
ALOGE("Ran out of retries to connect to %s", addr.toString().c_str());
diff --git a/libs/binder/RpcSocketAddress.h b/libs/binder/RpcSocketAddress.h
index c7ba5d9..ee7d448 100644
--- a/libs/binder/RpcSocketAddress.h
+++ b/libs/binder/RpcSocketAddress.h
@@ -113,4 +113,11 @@
unsigned int mPort;
};
+/**
+ * Connects to a single socket and produces a RpcTransportFd.
+ */
+status_t singleSocketConnection(const RpcSocketAddress& address,
+ const std::unique_ptr<FdTrigger>& shutdownTrigger,
+ RpcTransportFd* outFd);
+
} // namespace android
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 1256173..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": [
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/aidl/android/os/IAccessor.aidl b/libs/binder/aidl/android/os/IAccessor.aidl
index a3134a3..c06e05c 100644
--- a/libs/binder/aidl/android/os/IAccessor.aidl
+++ b/libs/binder/aidl/android/os/IAccessor.aidl
@@ -25,15 +25,56 @@
*/
interface IAccessor {
/**
+ * The connection info was not available for this service.
+ * This happens when the user-supplied callback fails to produce
+ * valid connection info.
+ * Depending on the implementation of the callback, it might be helpful
+ * to retry.
+ */
+ const int ERROR_CONNECTION_INFO_NOT_FOUND = 0;
+ /**
+ * Failed to create the socket. Often happens when the process trying to create
+ * the socket lacks the permissions to do so.
+ * This may be a temporary issue, so retrying the operation is OK.
+ */
+ const int ERROR_FAILED_TO_CREATE_SOCKET = 1;
+ /**
+ * Failed to connect to the socket. This can happen for many reasons, so be sure
+ * log the error message and check it.
+ * This may be a temporary issue, so retrying the operation is OK.
+ */
+ const int ERROR_FAILED_TO_CONNECT_TO_SOCKET = 2;
+ /**
+ * Failed to connect to the socket with EACCES because this process does not
+ * have perimssions to connect.
+ * There is no need to retry the connection as this access will not be granted
+ * upon retry.
+ */
+ const int ERROR_FAILED_TO_CONNECT_EACCES = 3;
+ /**
+ * Unsupported socket family type returned.
+ * There is no need to retry the connection as this socket family is not
+ * supported.
+ */
+ const int ERROR_UNSUPPORTED_SOCKET_FAMILY = 4;
+
+ /**
* Adds a connection to the RPC server of the service managed by the IAccessor.
*
* This method can be called multiple times to establish multiple distinct
* connections to the same RPC server.
*
+ * @throws ServiceSpecificError with message and one of the IAccessor::ERROR_ values.
+ *
* @return A file descriptor connected to the RPC session of the service managed
* by IAccessor.
*/
ParcelFileDescriptor addConnection();
- // TODO(b/350941051): Add API for debugging.
+ /**
+ * Get the instance name for the service this accessor is responsible for.
+ *
+ * This is used to verify the proxy binder is associated with the expected instance name.
+ */
+ String getInstanceName();
}
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 5fb7307..81f7cdb 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -17,14 +17,17 @@
#pragma once
#include <binder/Common.h>
#include <binder/IInterface.h>
-#include <utils/Vector.h>
+// Trusty has its own definition of socket APIs from trusty_ipc.h
+#ifndef __TRUSTY__
+#include <sys/socket.h>
+#endif // __TRUSTY__
#include <utils/String16.h>
+#include <utils/Vector.h>
#include <optional>
+#include <set>
namespace android {
-// ----------------------------------------------------------------------
-
/**
* Service manager for C++ services.
*
@@ -216,6 +219,102 @@
LIBBINDER_EXPORTED bool checkPermission(const String16& permission, pid_t pid, uid_t uid,
bool logPermissionFailure = true);
+// ----------------------------------------------------------------------
+// Trusty's definition of the socket APIs does not include sockaddr types
+#ifndef __TRUSTY__
+typedef std::function<status_t(const String16& name, sockaddr* outAddr, socklen_t addrSize)>
+ RpcSocketAddressProvider;
+
+/**
+ * This callback provides a way for clients to get access to remote services by
+ * providing an Accessor object from libbinder that can connect to the remote
+ * service over sockets.
+ *
+ * \param instance name of the service that the callback will provide an
+ * Accessor for. The provided accessor will be used to set up a client
+ * RPC connection in libbinder in order to return a binder for the
+ * associated remote service.
+ *
+ * \return IBinder of the Accessor object that libbinder implements.
+ * nullptr if the provider callback doesn't know how to reach the
+ * service or doesn't want to provide access for any other reason.
+ */
+typedef std::function<sp<IBinder>(const String16& instance)> RpcAccessorProvider;
+
+class AccessorProvider;
+
+/**
+ * Register a RpcAccessorProvider for the service manager APIs.
+ *
+ * \param instances that the RpcAccessorProvider knows about and can provide an
+ * Accessor for.
+ * \param provider callback that generates Accessors.
+ *
+ * \return A pointer used as a recept for the successful addition of the
+ * AccessorProvider. This is needed to unregister it later.
+ */
+[[nodiscard]] LIBBINDER_EXPORTED std::weak_ptr<AccessorProvider> addAccessorProvider(
+ std::set<std::string>&& instances, RpcAccessorProvider&& providerCallback);
+
+/**
+ * Remove an accessor provider using the pointer provided by addAccessorProvider
+ * along with the cookie pointer that was used.
+ *
+ * \param provider cookie that was returned by addAccessorProvider to keep track
+ * of this instance.
+ */
+[[nodiscard]] LIBBINDER_EXPORTED status_t
+removeAccessorProvider(std::weak_ptr<AccessorProvider> provider);
+
+/**
+ * Create an Accessor associated with a service that can create a socket connection based
+ * on the connection info from the supplied RpcSocketAddressProvider.
+ *
+ * \param instance name of the service that this Accessor is associated with
+ * \param connectionInfoProvider a callback that returns connection info for
+ * connecting to the service.
+ * \return the binder of the IAccessor implementation from libbinder
+ */
+LIBBINDER_EXPORTED sp<IBinder> createAccessor(const String16& instance,
+ RpcSocketAddressProvider&& connectionInfoProvider);
+
+/**
+ * Check to make sure this binder is the expected binder that is an IAccessor
+ * associated with a specific instance.
+ *
+ * This helper function exists to avoid adding the IAccessor type to
+ * libbinder_ndk.
+ *
+ * \param instance name of the service that this Accessor should be associated with
+ * \param binder to validate
+ *
+ * \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__
// Create an IServiceManager that delegates the service manager on the device via adb.
// This is can be set as the default service manager at program start, so that
diff --git a/libs/binder/include/binder/IServiceManagerUnitTestHelper.h b/libs/binder/include/binder/IServiceManagerUnitTestHelper.h
new file mode 100644
index 0000000..ff25163
--- /dev/null
+++ b/libs/binder/include/binder/IServiceManagerUnitTestHelper.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/os/IServiceManager.h>
+#include "IServiceManager.h"
+namespace android {
+
+/**
+ * Encapsulate an AidlServiceManager in a CppBackendShim. Only used for testing.
+ */
+LIBBINDER_EXPORTED sp<IServiceManager> getServiceManagerShimFromAidlServiceManagerForTests(
+ const sp<os::IServiceManager>& sm);
+
+} // namespace android
\ No newline at end of file
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/Android.bp b/libs/binder/ndk/Android.bp
index 4e02ace..a7423b3 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -95,6 +95,7 @@
"persistable_bundle.cpp",
"process.cpp",
"service_manager.cpp",
+ "binder_rpc.cpp",
],
static_libs: [
@@ -230,12 +231,24 @@
},
apex_available: [
"//apex_available:platform",
+ "//apex_available:anyapex",
"com.android.media",
"com.android.media.swcodec",
],
min_sdk_version: "29",
}
+// TODO: if you try to export libbinder_headers_platform_shared from libbinder_ndk.ndk, it will
+// not select the NDK variant of libbinder_headers_platform_shared and instead, it will error
+// that the NDK can't depend on glibc C++.
+cc_library_headers {
+ name: "libbinder_headers_platform_shared_ndk",
+ export_include_dirs: ["include_cpp"],
+ sdk_version: "29",
+ min_sdk_version: "29",
+ visibility: [":__subpackages__"],
+}
+
ndk_headers {
name: "libbinder_ndk_headers",
from: "include_ndk/android",
@@ -246,26 +259,14 @@
license: "NOTICE",
}
-// TODO(b/160624671): package with the aidl compiler
-ndk_headers {
- name: "libbinder_ndk_helper_headers",
- from: "include_cpp/android",
- to: "android",
- srcs: [
- "include_cpp/android/*.h",
- ],
- license: "NOTICE",
- // These are intentionally not C. It's a mistake that they're in the NDK.
- // See the bug above.
- skip_verification: true,
-}
+// include_cpp are packaged in development/build/sdk.atree with the AIDL compiler
ndk_library {
name: "libbinder_ndk",
symbol_file: "libbinder_ndk.map.txt",
first_version: "29",
export_header_libs: [
- "libbinder_ndk_headers",
- "libbinder_ndk_helper_headers",
+ // used to be part of the NDK, platform things depend on it
+ "libbinder_headers_platform_shared_ndk",
],
}
diff --git a/libs/binder/ndk/binder_rpc.cpp b/libs/binder/ndk/binder_rpc.cpp
new file mode 100644
index 0000000..07b8c40
--- /dev/null
+++ b/libs/binder/ndk/binder_rpc.cpp
@@ -0,0 +1,377 @@
+/*
+ * 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 <android/binder_rpc.h>
+#include <arpa/inet.h>
+#include <binder/IServiceManager.h>
+#include <linux/vm_sockets.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <variant>
+
+#include "ibinder_internal.h"
+#include "status_internal.h"
+
+using ::android::defaultServiceManager;
+using ::android::IBinder;
+using ::android::IServiceManager;
+using ::android::OK;
+using ::android::sp;
+using ::android::status_t;
+using ::android::String16;
+using ::android::String8;
+using ::android::binder::Status;
+
+#define LOG_ACCESSOR_DEBUG(...)
+// #define LOG_ACCESSOR_DEBUG(...) ALOGW(__VA_ARGS__)
+
+struct ABinderRpc_ConnectionInfo {
+ std::variant<sockaddr_vm, sockaddr_un, sockaddr_in> addr;
+};
+
+struct ABinderRpc_Accessor final : public ::android::RefBase {
+ static ABinderRpc_Accessor* make(const char* instance, const sp<IBinder>& binder) {
+ LOG_ALWAYS_FATAL_IF(binder == nullptr, "ABinderRpc_Accessor requires a non-null binder");
+ status_t status = android::validateAccessor(String16(instance), binder);
+ if (status != OK) {
+ ALOGE("The given binder is not a valid IAccessor for %s. Status: %s", instance,
+ android::statusToString(status).c_str());
+ return nullptr;
+ }
+ return new ABinderRpc_Accessor(binder);
+ }
+
+ sp<IBinder> asBinder() { return mAccessorBinder; }
+
+ ~ABinderRpc_Accessor() { LOG_ACCESSOR_DEBUG("ABinderRpc_Accessor dtor"); }
+
+ private:
+ ABinderRpc_Accessor(sp<IBinder> accessor) : mAccessorBinder(accessor) {}
+ ABinderRpc_Accessor() = delete;
+ sp<IBinder> mAccessorBinder;
+};
+
+struct ABinderRpc_AccessorProvider {
+ public:
+ static ABinderRpc_AccessorProvider* make(std::weak_ptr<android::AccessorProvider> cookie) {
+ if (cookie.expired()) {
+ ALOGE("Null AccessorProvider cookie from libbinder");
+ return nullptr;
+ }
+ return new ABinderRpc_AccessorProvider(cookie);
+ }
+ std::weak_ptr<android::AccessorProvider> mProviderCookie;
+
+ private:
+ ABinderRpc_AccessorProvider() = delete;
+
+ ABinderRpc_AccessorProvider(std::weak_ptr<android::AccessorProvider> provider)
+ : mProviderCookie(provider) {}
+};
+
+struct OnDeleteProviderHolder {
+ OnDeleteProviderHolder(void* data, ABinderRpc_AccessorProviderUserData_deleteCallback onDelete)
+ : mData(data), mOnDelete(onDelete) {}
+ ~OnDeleteProviderHolder() {
+ if (mOnDelete) {
+ mOnDelete(mData);
+ }
+ }
+ void* mData;
+ ABinderRpc_AccessorProviderUserData_deleteCallback mOnDelete;
+ // 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!");
+ }
+
+ private:
+ OnDeleteProviderHolder() = delete;
+};
+
+ABinderRpc_AccessorProvider* ABinderRpc_registerAccessorProvider(
+ ABinderRpc_AccessorProvider_getAccessorCallback provider, const char** instances,
+ size_t numInstances, void* data,
+ ABinderRpc_AccessorProviderUserData_deleteCallback onDelete) {
+ if (provider == nullptr) {
+ ALOGE("Null provider passed to ABinderRpc_registerAccessorProvider");
+ return nullptr;
+ }
+ if (data && onDelete == nullptr) {
+ ALOGE("If a non-null data ptr is passed to ABinderRpc_registerAccessorProvider, then a "
+ "ABinderRpc_AccessorProviderUserData_deleteCallback must also be passed to delete "
+ "the data object once the ABinderRpc_AccessorProvider is removed.");
+ return nullptr;
+ }
+ if (numInstances == 0 || instances == nullptr) {
+ ALOGE("No instances passed to ABinderRpc_registerAccessorProvider. numInstances: %zu",
+ numInstances);
+ return nullptr;
+ }
+ std::set<std::string> instanceStrings;
+ for (size_t i = 0; i < numInstances; i++) {
+ instanceStrings.emplace(instances[i]);
+ }
+ // call the onDelete when the last reference of this goes away (when the
+ // last reference to the generate std::function goes away).
+ std::shared_ptr<OnDeleteProviderHolder> onDeleteHolder =
+ std::make_shared<OnDeleteProviderHolder>(data, onDelete);
+ android::RpcAccessorProvider generate = [provider,
+ onDeleteHolder](const String16& name) -> sp<IBinder> {
+ ABinderRpc_Accessor* accessor = provider(String8(name).c_str(), onDeleteHolder->mData);
+ if (accessor == nullptr) {
+ ALOGE("The supplied ABinderRpc_AccessorProvider_getAccessorCallback returned nullptr");
+ return nullptr;
+ }
+ sp<IBinder> binder = accessor->asBinder();
+ ABinderRpc_Accessor_delete(accessor);
+ return binder;
+ };
+
+ std::weak_ptr<android::AccessorProvider> cookie =
+ android::addAccessorProvider(std::move(instanceStrings), std::move(generate));
+ return ABinderRpc_AccessorProvider::make(cookie);
+}
+
+void ABinderRpc_unregisterAccessorProvider(ABinderRpc_AccessorProvider* provider) {
+ if (provider == nullptr) {
+ LOG_ALWAYS_FATAL("Attempting to remove a null ABinderRpc_AccessorProvider");
+ }
+
+ status_t status = android::removeAccessorProvider(provider->mProviderCookie);
+ // There shouldn't be a way to get here because the caller won't have a
+ // ABinderRpc_AccessorProvider* without calling ABinderRpc_registerAccessorProvider
+ LOG_ALWAYS_FATAL_IF(status == android::BAD_VALUE, "Provider (%p) is not valid. Status: %s",
+ provider, android::statusToString(status).c_str());
+ LOG_ALWAYS_FATAL_IF(status == android::NAME_NOT_FOUND,
+ "Provider (%p) was already unregistered. Status: %s", provider,
+ android::statusToString(status).c_str());
+ LOG_ALWAYS_FATAL_IF(status != OK,
+ "Unknown error when attempting to unregister ABinderRpc_AccessorProvider "
+ "(%p). Status: %s",
+ provider, android::statusToString(status).c_str());
+
+ delete provider;
+}
+
+struct OnDeleteConnectionInfoHolder {
+ OnDeleteConnectionInfoHolder(void* data,
+ ABinderRpc_ConnectionInfoProviderUserData_delete onDelete)
+ : mData(data), mOnDelete(onDelete) {}
+ ~OnDeleteConnectionInfoHolder() {
+ if (mOnDelete) {
+ mOnDelete(mData);
+ }
+ }
+ void* mData;
+ ABinderRpc_ConnectionInfoProviderUserData_delete mOnDelete;
+ // 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!");
+ }
+
+ private:
+ OnDeleteConnectionInfoHolder() = delete;
+};
+
+ABinderRpc_Accessor* ABinderRpc_Accessor_new(
+ const char* instance, ABinderRpc_ConnectionInfoProvider provider, void* data,
+ ABinderRpc_ConnectionInfoProviderUserData_delete onDelete) {
+ if (instance == nullptr) {
+ ALOGE("Instance argument must be valid when calling ABinderRpc_Accessor_new");
+ return nullptr;
+ }
+ if (data && onDelete == nullptr) {
+ ALOGE("If a non-null data ptr is passed to ABinderRpc_Accessor_new, then a "
+ "ABinderRpc_ConnectionInfoProviderUserData_delete callback must also be passed to "
+ "delete "
+ "the data object once the ABinderRpc_Accessor is deleted.");
+ return nullptr;
+ }
+ std::shared_ptr<OnDeleteConnectionInfoHolder> onDeleteHolder =
+ std::make_shared<OnDeleteConnectionInfoHolder>(data, onDelete);
+ if (provider == nullptr) {
+ ALOGE("Can't create a new ABinderRpc_Accessor without a ABinderRpc_ConnectionInfoProvider "
+ "and it is "
+ "null");
+ return nullptr;
+ }
+ android::RpcSocketAddressProvider generate = [provider, onDeleteHolder](
+ const String16& name, sockaddr* outAddr,
+ size_t addrLen) -> status_t {
+ std::unique_ptr<ABinderRpc_ConnectionInfo> info(
+ provider(String8(name).c_str(), onDeleteHolder->mData));
+ if (info == nullptr) {
+ ALOGE("The supplied ABinderRpc_ConnectionInfoProvider returned nullptr");
+ return android::NAME_NOT_FOUND;
+ }
+ if (auto addr = std::get_if<sockaddr_vm>(&info->addr)) {
+ LOG_ALWAYS_FATAL_IF(addr->svm_family != AF_VSOCK,
+ "ABinderRpc_ConnectionInfo invalid family");
+ if (addrLen < sizeof(sockaddr_vm)) {
+ ALOGE("Provided outAddr is too small! Expecting %zu, got %zu", sizeof(sockaddr_vm),
+ addrLen);
+ return android::BAD_VALUE;
+ }
+ LOG_ACCESSOR_DEBUG(
+ "Connection info provider found AF_VSOCK. family %d, port %d, cid %d",
+ addr->svm_family, addr->svm_port, addr->svm_cid);
+ *reinterpret_cast<sockaddr_vm*>(outAddr) = *addr;
+ } else if (auto addr = std::get_if<sockaddr_un>(&info->addr)) {
+ LOG_ALWAYS_FATAL_IF(addr->sun_family != AF_UNIX,
+ "ABinderRpc_ConnectionInfo invalid family");
+ if (addrLen < sizeof(sockaddr_un)) {
+ ALOGE("Provided outAddr is too small! Expecting %zu, got %zu", sizeof(sockaddr_un),
+ addrLen);
+ return android::BAD_VALUE;
+ }
+ *reinterpret_cast<sockaddr_un*>(outAddr) = *addr;
+ } else if (auto addr = std::get_if<sockaddr_in>(&info->addr)) {
+ LOG_ALWAYS_FATAL_IF(addr->sin_family != AF_INET,
+ "ABinderRpc_ConnectionInfo invalid family");
+ if (addrLen < sizeof(sockaddr_in)) {
+ ALOGE("Provided outAddr is too small! Expecting %zu, got %zu", sizeof(sockaddr_in),
+ addrLen);
+ return android::BAD_VALUE;
+ }
+ *reinterpret_cast<sockaddr_in*>(outAddr) = *addr;
+ } else {
+ LOG_ALWAYS_FATAL(
+ "Unsupported address family type when trying to get ARpcConnection info. A "
+ "new variant was added to the ABinderRpc_ConnectionInfo and this needs to be "
+ "updated.");
+ }
+ return OK;
+ };
+ sp<IBinder> accessorBinder = android::createAccessor(String16(instance), std::move(generate));
+ if (accessorBinder == nullptr) {
+ ALOGE("service manager did not get us an accessor");
+ return nullptr;
+ }
+ LOG_ACCESSOR_DEBUG("service manager found an accessor, so returning one now from _new");
+ return ABinderRpc_Accessor::make(instance, accessorBinder);
+}
+
+void ABinderRpc_Accessor_delete(ABinderRpc_Accessor* accessor) {
+ delete accessor;
+}
+
+AIBinder* ABinderRpc_Accessor_asBinder(ABinderRpc_Accessor* accessor) {
+ if (!accessor) {
+ ALOGE("ABinderRpc_Accessor argument is null.");
+ return nullptr;
+ }
+
+ sp<IBinder> binder = accessor->asBinder();
+ sp<AIBinder> aBinder = ABpBinder::lookupOrCreateFromBinder(binder);
+ AIBinder* ptr = aBinder.get();
+ if (ptr == nullptr) {
+ LOG_ALWAYS_FATAL("Failed to lookupOrCreateFromBinder");
+ }
+ ptr->incStrong(nullptr);
+ return ptr;
+}
+
+ABinderRpc_Accessor* ABinderRpc_Accessor_fromBinder(const char* instance, AIBinder* binder) {
+ if (!binder) {
+ ALOGE("binder argument is null");
+ return nullptr;
+ }
+ sp<IBinder> accessorBinder = binder->getBinder();
+ if (accessorBinder) {
+ return ABinderRpc_Accessor::make(instance, accessorBinder);
+ } else {
+ ALOGE("Attempting to get an ABinderRpc_Accessor for %s but AIBinder::getBinder returned "
+ "null",
+ instance);
+ return nullptr;
+ }
+}
+
+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 ABinderRpc_Connection_new");
+ return nullptr;
+ }
+ // socklen_t was int32_t on 32-bit and uint32_t on 64 bit.
+ size_t socklen = len < 0 || static_cast<uintmax_t>(len) > SIZE_MAX ? 0 : len;
+
+ if (addr->sa_family == AF_VSOCK) {
+ if (len != sizeof(sockaddr_vm)) {
+ ALOGE("Incorrect size of %zu for AF_VSOCK sockaddr_vm. Expecting %zu", socklen,
+ sizeof(sockaddr_vm));
+ return nullptr;
+ }
+ sockaddr_vm vm = *reinterpret_cast<const sockaddr_vm*>(addr);
+ 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)) {
+ ALOGE("Incorrect size of %zu for AF_UNIX sockaddr_un. Expecting %zu", socklen,
+ sizeof(sockaddr_un));
+ return nullptr;
+ }
+ sockaddr_un un = *reinterpret_cast<const sockaddr_un*>(addr);
+ 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) {
+ if (len != sizeof(sockaddr_in)) {
+ ALOGE("Incorrect size of %zu for AF_INET sockaddr_in. Expecting %zu", socklen,
+ sizeof(sockaddr_in));
+ return nullptr;
+ }
+ sockaddr_in in = *reinterpret_cast<const sockaddr_in*>(addr);
+ 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("ABinderRpc APIs only support AF_VSOCK right now but the supplied sockaddr::sa_family "
+ "is: %hu",
+ addr->sa_family);
+ return nullptr;
+}
+
+void ABinderRpc_ConnectionInfo_delete(ABinderRpc_ConnectionInfo* info) {
+ delete info;
+}
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 6273804..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:
@@ -225,6 +240,8 @@
SpAIBinder asBinder() override final;
+ const SpAIBinder& asBinderReference() { return mBinder; }
+
bool isRemote() override final { return AIBinder_isRemote(mBinder.get()); }
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override {
@@ -254,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) {
@@ -272,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_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
index 5e0d4da..1d516ae 100644
--- a/libs/binder/ndk/include_ndk/android/persistable_bundle.h
+++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
@@ -17,13 +17,6 @@
#pragma once
#include <android/binder_parcel.h>
-#if defined(__ANDROID_VENDOR__)
-#include <android/llndk-versioning.h>
-#else
-#if !defined(__INTRODUCED_IN_LLNDK)
-#define __INTRODUCED_IN_LLNDK(level) __attribute__((annotate("introduced_in_llndk=" #level)))
-#endif
-#endif // __ANDROID_VENDOR__
#include <stdbool.h>
#include <stdint.h>
#include <sys/cdefs.h>
@@ -83,8 +76,7 @@
*
* \return Pointer to a new APersistableBundle
*/
-APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+APersistableBundle* _Nullable APersistableBundle_new() __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Create a new APersistableBundle based off an existing APersistableBundle.
@@ -98,7 +90,7 @@
* \return Pointer to a new APersistableBundle
*/
APersistableBundle* _Nullable APersistableBundle_dup(const APersistableBundle* _Nonnull pBundle)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Delete an APersistableBundle. This must always be called when finished using
@@ -109,7 +101,7 @@
* Available since API level 202404.
*/
void APersistableBundle_delete(APersistableBundle* _Nullable pBundle)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Check for equality of APersistableBundles.
@@ -123,7 +115,7 @@
*/
bool APersistableBundle_isEqual(const APersistableBundle* _Nonnull lhs,
const APersistableBundle* _Nonnull rhs)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Read an APersistableBundle from an AParcel.
@@ -142,7 +134,7 @@
*/
binder_status_t APersistableBundle_readFromParcel(
const AParcel* _Nonnull parcel, APersistableBundle* _Nullable* _Nonnull outPBundle)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Write an APersistableBundle to an AParcel.
@@ -162,7 +154,7 @@
*/
binder_status_t APersistableBundle_writeToParcel(const APersistableBundle* _Nonnull pBundle,
AParcel* _Nonnull parcel)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get the size of an APersistableBundle. This is the number of mappings in the
@@ -175,7 +167,7 @@
* \return number of mappings in the object
*/
int32_t APersistableBundle_size(const APersistableBundle* _Nonnull pBundle)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Erase any entries added with the provided key.
@@ -188,7 +180,7 @@
* \return number of entries erased. Either 0 or 1.
*/
int32_t APersistableBundle_erase(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Put a boolean associated with the provided key.
@@ -201,8 +193,7 @@
* Available since API level 202404.
*/
void APersistableBundle_putBoolean(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
- bool val) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ bool val) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Put an int32_t associated with the provided key.
@@ -215,8 +206,7 @@
* Available since API level 202404.
*/
void APersistableBundle_putInt(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
- int32_t val) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ int32_t val) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Put an int64_t associated with the provided key.
@@ -229,8 +219,7 @@
* Available since API level 202404.
*/
void APersistableBundle_putLong(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
- int64_t val) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ int64_t val) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Put a double associated with the provided key.
@@ -243,8 +232,7 @@
* Available since API level 202404.
*/
void APersistableBundle_putDouble(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
- double val) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ double val) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Put a string associated with the provided key.
@@ -258,8 +246,7 @@
* Available since API level 202404.
*/
void APersistableBundle_putString(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
- const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ const char* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Put a boolean vector associated with the provided key.
@@ -275,8 +262,7 @@
*/
void APersistableBundle_putBooleanVector(APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, const bool* _Nonnull vec,
- int32_t num) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Put an int32_t vector associated with the provided key.
@@ -292,7 +278,7 @@
*/
void APersistableBundle_putIntVector(APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
const int32_t* _Nonnull vec, int32_t num)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Put an int64_t vector associated with the provided key.
@@ -308,8 +294,7 @@
*/
void APersistableBundle_putLongVector(APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, const int64_t* _Nonnull vec,
- int32_t num) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Put a double vector associated with the provided key.
@@ -325,8 +310,7 @@
*/
void APersistableBundle_putDoubleVector(APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, const double* _Nonnull vec,
- int32_t num) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ int32_t num) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Put a string vector associated with the provided key.
@@ -343,7 +327,7 @@
void APersistableBundle_putStringVector(APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key,
const char* _Nullable const* _Nullable vec, int32_t num)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Put an APersistableBundle associated with the provided key.
@@ -359,7 +343,7 @@
void APersistableBundle_putPersistableBundle(APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key,
const APersistableBundle* _Nonnull val)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get a boolean associated with the provided key.
@@ -374,7 +358,7 @@
*/
bool APersistableBundle_getBoolean(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, bool* _Nonnull val)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get an int32_t associated with the provided key.
@@ -388,8 +372,7 @@
* \return true if a value exists for the provided key
*/
bool APersistableBundle_getInt(const APersistableBundle* _Nonnull pBundle, const char* _Nonnull key,
- int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ int32_t* _Nonnull val) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get an int64_t associated with the provided key.
@@ -404,7 +387,7 @@
*/
bool APersistableBundle_getLong(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, int64_t* _Nonnull val)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get a double associated with the provided key.
@@ -419,7 +402,7 @@
*/
bool APersistableBundle_getDouble(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, double* _Nonnull val)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get a string associated with the provided key.
@@ -440,8 +423,7 @@
int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, char* _Nullable* _Nonnull val,
APersistableBundle_stringAllocator stringAllocator,
- void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get a boolean vector associated with the provided key and place it in the
@@ -468,7 +450,7 @@
int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, bool* _Nullable buffer,
int32_t bufferSizeBytes)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get an int32_t vector associated with the provided key and place it in the
@@ -494,8 +476,7 @@
*/
int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, int32_t* _Nullable buffer,
- int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get an int64_t vector associated with the provided key and place it in the
@@ -521,8 +502,8 @@
*/
int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, int64_t* _Nullable buffer,
- int32_t bufferSizeBytes) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ int32_t bufferSizeBytes)
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get a double vector associated with the provided key and place it in the
@@ -549,7 +530,7 @@
int32_t APersistableBundle_getDoubleVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, double* _Nullable buffer,
int32_t bufferSizeBytes)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get a string vector associated with the provided key and place it in the
@@ -586,7 +567,7 @@
int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
void* _Nullable context)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get an APersistableBundle* associated with the provided key.
@@ -605,7 +586,7 @@
bool APersistableBundle_getPersistableBundle(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key,
APersistableBundle* _Nullable* _Nonnull outBundle)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get all of the keys associated with this specific type and place it in the
@@ -638,7 +619,7 @@
int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
void* _Nullable context)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get all of the keys associated with this specific type and place it in the
@@ -669,8 +650,7 @@
int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
- void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get all of the keys associated with this specific type and place it in the
@@ -701,8 +681,7 @@
int32_t APersistableBundle_getLongKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
- void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get all of the keys associated with this specific type and place it in the
@@ -734,8 +713,8 @@
char* _Nullable* _Nullable outKeys,
int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
- void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get all of the keys associated with this specific type and place it in the
@@ -767,8 +746,8 @@
char* _Nullable* _Nullable outKeys,
int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
- void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get all of the keys associated with this specific type and place it in the
@@ -801,7 +780,7 @@
int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
void* _Nullable context)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get all of the keys associated with this specific type and place it in the
@@ -834,7 +813,7 @@
int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
void* _Nullable context)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get all of the keys associated with this specific type and place it in the
@@ -867,7 +846,7 @@
int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
void* _Nullable context)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get all of the keys associated with this specific type and place it in the
@@ -899,7 +878,7 @@
int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
void* _Nullable context)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get all of the keys associated with this specific type and place it in the
@@ -932,7 +911,7 @@
int32_t bufferSizeBytes,
APersistableBundle_stringAllocator stringAllocator,
void* _Nullable context)
- __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Get all of the keys associated with this specific type and place it in the
@@ -963,6 +942,6 @@
int32_t APersistableBundle_getPersistableBundleKeys(
const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys,
int32_t bufferSizeBytes, APersistableBundle_stringAllocator stringAllocator,
- void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__) __INTRODUCED_IN_LLNDK(202404);
+ void* _Nullable context) __INTRODUCED_IN(__ANDROID_API_V__);
__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 41b30a0..cc4943b 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -18,7 +18,6 @@
#include <android/binder_ibinder.h>
#include <android/binder_status.h>
-#include <android/llndk-versioning.h>
#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -257,8 +256,7 @@
* \return the result of dlopen of the specified HAL
*/
void* AServiceManager_openDeclaredPassthroughHal(const char* interface, const char* instance,
- int flag) __INTRODUCED_IN(__ANDROID_API_V__)
- __INTRODUCED_IN_LLNDK(202404);
+ int flag) __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Prevent lazy services without client from shutting down their process
diff --git a/libs/binder/ndk/include_platform/android/binder_rpc.h b/libs/binder/ndk/include_platform/android/binder_rpc.h
new file mode 100644
index 0000000..9fe5d78
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_rpc.h
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/binder_ibinder.h>
+#include <sys/socket.h>
+
+__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
+ * used to set up a client RpcSession underneath libbinder's IServiceManager APIs
+ * to provide the client with the service's binder for remote communication.
+ */
+typedef struct ABinderRpc_Accessor ABinderRpc_Accessor;
+
+/**
+ * This represents an object that supplies ABinderRpc_Accessors to libbinder
+ * when they are requested. They are requested any time a client is attempting
+ * to get a service through IServiceManager APIs when the services aren't known by
+ * servicemanager.
+ */
+typedef struct ABinderRpc_AccessorProvider ABinderRpc_AccessorProvider;
+
+/**
+ * This represents information necessary for libbinder to be able to connect to a
+ * remote service.
+ * It supports connecting to linux sockets and is created using sockaddr
+ * types for sockets supported by libbinder like sockaddr_in, sockaddr_un,
+ * sockaddr_vm.
+ */
+typedef struct ABinderRpc_ConnectionInfo ABinderRpc_ConnectionInfo;
+
+/**
+ * These APIs provide a way for clients of binder services to be able to get a
+ * binder object of that service through the existing libbinder/libbinder_ndk
+ * Service Manager APIs when that service is using RPC Binder over sockets
+ * instead kernel binder.
+ *
+ * Some of these APIs are used on Android hosts when kernel binder is supported
+ * and the usual servicemanager process is available. Some of these APIs are
+ * only required when there is no kernel binder or extra servicemanager process
+ * such as the case of microdroid or similar VMs.
+ */
+
+/**
+ * This callback is responsible for returning ABinderRpc_Accessor objects for a given
+ * service instance. These ABinderRpc_Accessor objects are implemented by
+ * libbinder_ndk and backed by implementations of android::os::IAccessor in
+ * libbinder.
+ *
+ * \param instance name of the service like
+ * `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
+ * callback gives up ownership of the object once it returns it. The
+ * caller of this callback (libbinder_ndk) is responsible for deleting it
+ * with ABinderRpc_Accessor_delete.
+ */
+typedef ABinderRpc_Accessor* _Nullable (*ABinderRpc_AccessorProvider_getAccessorCallback)(
+ const char* _Nonnull instance, void* _Nullable data);
+
+/**
+ * This callback is responsible deleting the `void* data` object that is passed
+ * in to ABinderRpc_registerAccessorProvider for the ABinderRpc_AccessorProvider_getAccessorCallback
+ * to use. That object is owned by the ABinderRpc_AccessorProvider and must remain valid for the
+ * lifetime of the callback because it may be called and use the object.
+ * This _delete callback is called after the ABinderRpc_AccessorProvider is remove and
+ * is guaranteed never to be called again.
+ *
+ * \param data a pointer to data that the ABinderRpc_AccessorProvider_getAccessorCallback uses which
+ * is to be deleted by this call.
+ */
+typedef void (*ABinderRpc_AccessorProviderUserData_deleteCallback)(void* _Nullable data);
+
+/**
+ * Inject an ABinderRpc_AccessorProvider_getAccessorCallback into the process for
+ * the Service Manager APIs to use to retrieve ABinderRpc_Accessor objects associated
+ * with different RPC Binder services.
+ *
+ * \param provider callback that returns ABinderRpc_Accessors for libbinder to set up
+ * RPC clients with.
+ * \param instances array of instances that are supported by this provider. It
+ * will only be called if the client is looking for an instance that is
+ * in this list. These instances must be unique per-process. If an
+ * 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 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
+ * called again. Before this callback is called, `data` must remain
+ * valid.
+ * \return nullptr on error if the data pointer is non-null and the onDelete
+ * callback is null or if an instance in the instances list was previously
+ * registered. In the error case of duplicate instances, if data was
+ * provided with a ABinderRpc_AccessorProviderUserData_deleteCallback,
+ * the callback will be called to delete the data.
+ * Otherwise returns a pointer to the ABinderRpc_AccessorProvider that
+ * can be used to remove with ABinderRpc_unregisterAccessorProvider.
+ */
+ABinderRpc_AccessorProvider* _Nullable ABinderRpc_registerAccessorProvider(
+ ABinderRpc_AccessorProvider_getAccessorCallback _Nonnull provider,
+ const char* _Nullable* _Nonnull instances, size_t numInstances, void* _Nullable data,
+ ABinderRpc_AccessorProviderUserData_deleteCallback _Nullable onDelete) __INTRODUCED_IN(36);
+
+/**
+ * Remove an ABinderRpc_AccessorProvider from libbinder. This will remove references
+ * from the ABinderRpc_AccessorProvider and will no longer call the
+ * ABinderRpc_AccessorProvider_getAccessorCallback.
+ *
+ * Note: The `data` object that was used when adding the accessor will be
+ * deleted by the ABinderRpc_AccessorProviderUserData_deleteCallback at some
+ * point after this call. Do not use the object and do not try to delete
+ * it through any other means.
+ * Note: This will abort when used incorrectly if this provider was never
+ * registered or if it were already unregistered.
+ *
+ * \param provider to be removed and deleted
+ *
+ */
+void ABinderRpc_unregisterAccessorProvider(ABinderRpc_AccessorProvider* _Nonnull provider)
+ __INTRODUCED_IN(36);
+
+/**
+ * Callback which returns the RPC connection information for libbinder to use to
+ * 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. 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`
+ */
+typedef ABinderRpc_ConnectionInfo* _Nullable (*ABinderRpc_ConnectionInfoProvider)(
+ const char* _Nonnull instance, void* _Nullable data) __INTRODUCED_IN(36);
+/**
+ * This callback is responsible deleting the `void* data` object that is passed
+ * in to ABinderRpc_Accessor_new for the ABinderRpc_ConnectionInfoProvider to use. That
+ * object is owned by the ABinderRpc_Accessor and must remain valid for the
+ * lifetime the Accessor because it may be used by the connection info provider
+ * callback.
+ * This _delete callback is called after the ABinderRpc_Accessor is removed and
+ * is guaranteed never to be called again.
+ *
+ * \param data a pointer to data that the ABinderRpc_AccessorProvider uses which is to
+ * be deleted by this call.
+ */
+typedef void (*ABinderRpc_ConnectionInfoProviderUserData_delete)(void* _Nullable data);
+
+/**
+ * Create a new ABinderRpc_Accessor. This creates an IAccessor object in libbinder
+ * 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. 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.
+ * \param data pointer that is passed to the ABinderRpc_ConnectionInfoProvider callback.
+ * IMPORTANT: The ABinderRpc_ConnectionInfoProvider 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 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_ConnectionInfoProvider is guaranteed to never be
+ * called again. Before this callback is called, `data` must remain
+ * valid.
+ * \return an ABinderRpc_Accessor instance. This is deleted by the caller once it is
+ * no longer needed.
+ */
+ABinderRpc_Accessor* _Nullable ABinderRpc_Accessor_new(
+ const char* _Nonnull instance, ABinderRpc_ConnectionInfoProvider _Nonnull provider,
+ void* _Nullable data, ABinderRpc_ConnectionInfoProviderUserData_delete _Nullable onDelete)
+ __INTRODUCED_IN(36);
+
+/**
+ * Delete an ABinderRpc_Accessor
+ *
+ * \param accessor to delete
+ */
+void ABinderRpc_Accessor_delete(ABinderRpc_Accessor* _Nonnull accessor) __INTRODUCED_IN(36);
+
+/**
+ * Return the AIBinder associated with an ABinderRpc_Accessor. This can be used to
+ * send the Accessor to another process or even register it with servicemanager.
+ *
+ * \param accessor to get the AIBinder for
+ * \return binder of the supplied accessor with one strong ref count
+ */
+AIBinder* _Nullable ABinderRpc_Accessor_asBinder(ABinderRpc_Accessor* _Nonnull accessor)
+ __INTRODUCED_IN(36);
+
+/**
+ * Return the ABinderRpc_Accessor associated with an AIBinder. The instance must match
+ * 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.
+ * 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. 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).
+ *
+ * \param addr sockaddr pointer that can come from supported socket
+ * types like sockaddr_vm (vsock) and sockaddr_un (Unix Domain Sockets).
+ * \param len length of the concrete sockaddr type being used. Like
+ * sizeof(sockaddr_vm) when sockaddr_vm is used.
+ * \return the connection info based on the given sockaddr
+ */
+ABinderRpc_ConnectionInfo* _Nullable ABinderRpc_ConnectionInfo_new(const sockaddr* _Nonnull addr,
+ socklen_t len)
+ __INTRODUCED_IN(36);
+
+/**
+ * Delete an ABinderRpc_ConnectionInfo object that was created with
+ * ABinderRpc_ConnectionInfo_new.
+ *
+ * \param info object to be deleted
+ */
+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 826e199..4d691f8 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -248,6 +248,23 @@
AServiceManager_openDeclaredPassthroughHal; # systemapi llndk=202404
};
+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
+ ABinderRpc_ConnectionInfo_new; # systemapi
+ ABinderRpc_ConnectionInfo_delete; # systemapi
+};
+
LIBBINDER_NDK_PLATFORM {
global:
AParcel_getAllowFds;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index f518a22..e5a3da4 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -46,6 +46,7 @@
#include "android/binder_ibinder.h"
using namespace android;
+using namespace std::chrono_literals;
constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
@@ -54,7 +55,7 @@
constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService";
constexpr char kBinderNdkUnitTestServiceFlagged[] = "BinderNdkUnitTestFlagged";
-constexpr unsigned int kShutdownWaitTime = 11;
+constexpr auto kShutdownWaitTime = 30s;
constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715;
class MyTestFoo : public IFoo {
@@ -253,12 +254,22 @@
}
bool isServiceRunning(const char* serviceName) {
- AIBinder* binder = AServiceManager_checkService(serviceName);
- if (binder == nullptr) {
- return false;
+ static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+ const Vector<String16> services = sm->listServices();
+ for (const auto service : services) {
+ if (service == String16(serviceName)) return true;
}
- AIBinder_decStrong(binder);
+ return false;
+}
+bool isServiceShutdownWithWait(const char* serviceName) {
+ LOG(INFO) << "About to check and wait for shutdown of " << std::string(serviceName);
+ const auto before = std::chrono::steady_clock::now();
+ while (isServiceRunning(serviceName)) {
+ sleep(1);
+ const auto after = std::chrono::steady_clock::now();
+ if (after - before >= kShutdownWaitTime) return false;
+ }
return true;
}
@@ -450,8 +461,8 @@
service = nullptr;
IPCThreadState::self()->flushCommands();
// Make sure the service is dead after some time of no use
- sleep(kShutdownWaitTime);
- ASSERT_EQ(nullptr, AServiceManager_checkService(kLazyBinderNdkUnitTestService));
+ ASSERT_TRUE(isServiceShutdownWithWait(kLazyBinderNdkUnitTestService))
+ << "Service failed to shut down";
}
TEST(NdkBinder, ForcedPersistenceTest) {
@@ -466,14 +477,12 @@
service = nullptr;
IPCThreadState::self()->flushCommands();
- sleep(kShutdownWaitTime);
-
- bool isRunning = isServiceRunning(kForcePersistNdkUnitTestService);
-
if (i == 0) {
- ASSERT_TRUE(isRunning) << "Service shut down when it shouldn't have.";
+ ASSERT_TRUE(isServiceRunning(kForcePersistNdkUnitTestService))
+ << "Service shut down when it shouldn't have.";
} else {
- ASSERT_FALSE(isRunning) << "Service failed to shut down.";
+ ASSERT_TRUE(isServiceShutdownWithWait(kForcePersistNdkUnitTestService))
+ << "Service failed to shut down";
}
}
}
@@ -491,10 +500,7 @@
service = nullptr;
IPCThreadState::self()->flushCommands();
- LOG(INFO) << "ActiveServicesCallbackTest about to sleep";
- sleep(kShutdownWaitTime);
-
- ASSERT_FALSE(isServiceRunning(kActiveServicesNdkUnitTestService))
+ ASSERT_TRUE(isServiceShutdownWithWait(kActiveServicesNdkUnitTestService))
<< "Service failed to shut down.";
}
@@ -1102,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/proxy.rs b/libs/binder/rust/src/proxy.rs
index 340014a..04f1517 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -195,7 +195,7 @@
impl PartialEq for SpIBinder {
fn eq(&self, other: &Self) -> bool {
- ptr::eq(self.0.as_ptr(), other.0.as_ptr())
+ self.cmp(other) == Ordering::Equal
}
}
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 1e463a4..28a3f65 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -51,6 +51,30 @@
],
}
+cc_test {
+ name: "binderCacheUnitTest",
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ srcs: [
+ "binderCacheUnitTest.cpp",
+ ],
+ shared_libs: [
+ "liblog",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ ],
+ static_libs: [
+ "libfakeservicemanager",
+ ],
+ defaults: ["libbinder_client_cache_flag"],
+ test_suites: ["general-tests"],
+ require_root: true,
+}
+
// unit test only, which can run on host and doesn't use /dev/binder
cc_test {
name: "binderUnitTest",
@@ -508,6 +532,9 @@
static_libs: [
"libbinder_rpc_single_threaded",
],
+ shared_libs: [
+ "libbinder_ndk",
+ ],
}
cc_test {
@@ -704,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
new file mode 100644
index 0000000..c5ad793
--- /dev/null
+++ b/libs/binder/tests/binderCacheUnitTest.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include <android-base/logging.h>
+#include <android/os/IServiceManager.h>
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IServiceManagerUnitTestHelper.h>
+#include "fakeservicemanager/FakeServiceManager.h"
+
+#include <sys/prctl.h>
+#include <thread>
+
+using namespace android;
+
+#ifdef LIBBINDER_CLIENT_CACHE
+constexpr bool kUseLibbinderCache = true;
+#else
+constexpr bool kUseLibbinderCache = false;
+#endif
+
+// A service name which is in the static list of cachable services
+const String16 kCachedServiceName = String16("isub");
+
+#define EXPECT_OK(status) \
+ do { \
+ binder::Status stat = (status); \
+ EXPECT_TRUE(stat.isOk()) << stat; \
+ } while (false)
+
+const String16 kServerName = String16("binderCacheUnitTest");
+
+class FooBar : public BBinder {
+public:
+ status_t onTransact(uint32_t, const Parcel&, Parcel*, uint32_t) {
+ // exit the server
+ std::thread([] { exit(EXIT_FAILURE); }).detach();
+ return OK;
+ }
+ void killServer(sp<IBinder> binder) {
+ Parcel data, reply;
+ binder->transact(0, data, &reply, 0);
+ }
+};
+
+class MockAidlServiceManager : public os::IServiceManagerDefault {
+public:
+ MockAidlServiceManager() : innerSm() {}
+
+ binder::Status checkService(const ::std::string& name, os::Service* _out) override {
+ sp<IBinder> binder = innerSm.getService(String16(name.c_str()));
+ *_out = os::Service::make<os::Service::Tag::binder>(binder);
+ return binder::Status::ok();
+ }
+
+ binder::Status addService(const std::string& name, const sp<IBinder>& service,
+ bool allowIsolated, int32_t dumpPriority) override {
+ return binder::Status::fromStatusT(
+ innerSm.addService(String16(name.c_str()), service, allowIsolated, dumpPriority));
+ }
+
+ FakeServiceManager innerSm;
+};
+
+class LibbinderCacheTest : public ::testing::Test {
+protected:
+ void SetUp() override {
+ sp<MockAidlServiceManager> sm = sp<MockAidlServiceManager>::make();
+ mServiceManager = getServiceManagerShimFromAidlServiceManagerForTests(sm);
+ }
+
+ void TearDown() override {}
+
+public:
+ void cacheAndConfirmCacheHit(const sp<IBinder>& binder1, const sp<IBinder>& binder2) {
+ // Add a service
+ EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder1));
+ // Get the service. This caches it.
+ sp<IBinder> result = mServiceManager->checkService(kCachedServiceName);
+ ASSERT_EQ(binder1, result);
+
+ // Add the different binder and replace the service.
+ // The cache should still hold the original binder.
+ EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder2));
+
+ result = mServiceManager->checkService(kCachedServiceName);
+ if (kUseLibbinderCache) {
+ // If cache is enabled, we should get the binder to Service Manager.
+ EXPECT_EQ(binder1, result);
+ } else {
+ // If cache is disabled, then we should get the newer binder
+ EXPECT_EQ(binder2, result);
+ }
+ }
+
+ sp<android::IServiceManager> mServiceManager;
+};
+
+TEST_F(LibbinderCacheTest, AddLocalServiceAndConfirmCacheHit) {
+ sp<IBinder> binder1 = sp<BBinder>::make();
+ sp<IBinder> binder2 = sp<BBinder>::make();
+
+ cacheAndConfirmCacheHit(binder1, binder2);
+}
+
+TEST_F(LibbinderCacheTest, AddRemoteServiceAndConfirmCacheHit) {
+ sp<IBinder> binder1 = defaultServiceManager()->checkService(kServerName);
+ ASSERT_NE(binder1, nullptr);
+ sp<IBinder> binder2 = IInterface::asBinder(mServiceManager);
+
+ cacheAndConfirmCacheHit(binder1, binder2);
+}
+
+TEST_F(LibbinderCacheTest, RemoveFromCacheOnServerDeath) {
+ sp<IBinder> binder1 = defaultServiceManager()->checkService(kServerName);
+ FooBar foo = FooBar();
+
+ EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder1));
+
+ // Check Service, this caches the binder
+ sp<IBinder> result = mServiceManager->checkService(kCachedServiceName);
+ ASSERT_EQ(binder1, result);
+
+ // Kill the server, this should remove from cache.
+ pid_t pid;
+ ASSERT_EQ(OK, binder1->getDebugPid(&pid));
+ foo.killServer(binder1);
+ system(("kill -9 " + std::to_string(pid)).c_str());
+
+ sp<IBinder> binder2 = sp<BBinder>::make();
+
+ // Add new service with the same name.
+ // This will replace the service in FakeServiceManager.
+ EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder2));
+
+ // Confirm that new service is returned instead of old.
+ 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);
+}
+
+TEST_F(LibbinderCacheTest, NullBinderNotCached) {
+ sp<IBinder> binder1 = nullptr;
+ sp<IBinder> binder2 = sp<BBinder>::make();
+
+ // Check for a cacheble service which isn't registered.
+ // FakeServiceManager should return nullptr.
+ // This shouldn't be cached.
+ sp<IBinder> result = mServiceManager->checkService(kCachedServiceName);
+ ASSERT_EQ(binder1, result);
+
+ // Add the same service
+ EXPECT_EQ(OK, mServiceManager->addService(kCachedServiceName, binder2));
+
+ // This should return the newly added service.
+ result = mServiceManager->checkService(kCachedServiceName);
+ EXPECT_EQ(binder2, result);
+}
+
+TEST_F(LibbinderCacheTest, DoNotCacheServiceNotInList) {
+ sp<IBinder> binder1 = sp<BBinder>::make();
+ sp<IBinder> binder2 = sp<BBinder>::make();
+ String16 serviceName = String16("NewLibbinderCacheTest");
+ // Add a service
+ EXPECT_EQ(OK, mServiceManager->addService(serviceName, binder1));
+ // Get the service. This shouldn't caches it.
+ sp<IBinder> result = mServiceManager->checkService(serviceName);
+ ASSERT_EQ(binder1, result);
+
+ // Add the different binder and replace the service.
+ EXPECT_EQ(OK, mServiceManager->addService(serviceName, binder2));
+
+ // Confirm that we get the new service
+ result = mServiceManager->checkService(serviceName);
+ EXPECT_EQ(binder2, result);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+
+ // Start a FooBar service and add it to the servicemanager.
+ sp<IBinder> server = new FooBar();
+ defaultServiceManager()->addService(kServerName, server);
+
+ IPCThreadState::self()->joinThreadPool(true);
+ exit(1); // should not reach
+ }
+
+ status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(3);
+ ProcessState::self()->startThreadPool();
+ CHECK_EQ(ProcessState::self()->isThreadPoolStarted(), true);
+ CHECK_GT(ProcessState::self()->getThreadPoolMaxTotalThreadCount(), 0);
+
+ auto binder = defaultServiceManager()->waitForService(kServerName);
+ CHECK_NE(nullptr, binder.get());
+ return RUN_ALL_TESTS();
+}
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 3038de9..506fc71 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -46,6 +46,13 @@
#include "binderRpcTestCommon.h"
#include "binderRpcTestFixture.h"
+// TODO need to add IServiceManager.cpp/.h to libbinder_no_kernel
+#ifdef BINDER_WITH_KERNEL_IPC
+#include "android-base/logging.h"
+#include "android/binder_manager.h"
+#include "android/binder_rpc.h"
+#endif // BINDER_WITH_KERNEL_IPC
+
using namespace std::chrono_literals;
using namespace std::placeholders;
using android::binder::borrowed_fd;
@@ -68,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));
@@ -365,26 +374,57 @@
session->setMaxOutgoingConnections(options.numOutgoingConnections);
session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode);
+ sockaddr_storage addr{};
+ socklen_t addrLen = 0;
+
switch (socketType) {
- case SocketType::PRECONNECTED:
+ case SocketType::PRECONNECTED: {
+ sockaddr_un addr_un{};
+ addr_un.sun_family = AF_UNIX;
+ strcpy(addr_un.sun_path, serverConfig.addr.c_str());
+ addr = *reinterpret_cast<sockaddr_storage*>(&addr_un);
+ addrLen = sizeof(sockaddr_un);
+
status = session->setupPreconnectedClient({}, [=]() {
return connectTo(UnixSocketAddress(serverConfig.addr.c_str()));
});
- break;
+ } break;
case SocketType::UNIX_RAW:
- case SocketType::UNIX:
+ case SocketType::UNIX: {
+ sockaddr_un addr_un{};
+ addr_un.sun_family = AF_UNIX;
+ strcpy(addr_un.sun_path, serverConfig.addr.c_str());
+ addr = *reinterpret_cast<sockaddr_storage*>(&addr_un);
+ addrLen = sizeof(sockaddr_un);
+
status = session->setupUnixDomainClient(serverConfig.addr.c_str());
- break;
+ } break;
case SocketType::UNIX_BOOTSTRAP:
status = session->setupUnixDomainSocketBootstrapClient(
unique_fd(dup(bootstrapClientFd.get())));
break;
- case SocketType::VSOCK:
+ case SocketType::VSOCK: {
+ sockaddr_vm addr_vm{
+ .svm_family = AF_VSOCK,
+ .svm_port = static_cast<unsigned int>(serverInfo.port),
+ .svm_cid = VMADDR_CID_LOCAL,
+ };
+ addr = *reinterpret_cast<sockaddr_storage*>(&addr_vm);
+ addrLen = sizeof(sockaddr_vm);
+
status = session->setupVsockClient(VMADDR_CID_LOCAL, serverInfo.port);
- break;
- case SocketType::INET:
- status = session->setupInetClient("127.0.0.1", serverInfo.port);
- break;
+ } break;
+ case SocketType::INET: {
+ const std::string ip_addr = "127.0.0.1";
+ sockaddr_in addr_in{};
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_port = htons(serverInfo.port);
+ inet_aton(ip_addr.c_str(), &addr_in.sin_addr);
+ addr = *reinterpret_cast<sockaddr_storage*>(&addr_in);
+ addrLen = sizeof(sockaddr_in);
+
+ status = session->setupInetClient(ip_addr.c_str(), serverInfo.port);
+ } break;
case SocketType::TIPC:
status = session->setupPreconnectedClient({}, [=]() {
#ifdef BINDER_RPC_TO_TRUSTY_TEST
@@ -413,7 +453,7 @@
break;
}
LOG_ALWAYS_FATAL_IF(status != OK, "Could not connect: %s", statusToString(status).c_str());
- ret->sessions.push_back({session, session->getRootObject()});
+ ret->sessions.push_back({session, session->getRootObject(), addr, addrLen});
}
return ret;
}
@@ -423,7 +463,7 @@
GTEST_SKIP() << "This test requires multiple threads";
}
- constexpr size_t kNumThreads = 10;
+ constexpr size_t kNumThreads = 5;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
@@ -468,11 +508,11 @@
EXPECT_GE(epochMsAfter, epochMsBefore + 2 * sleepMs);
- // Potential flake, but make sure calls are handled in parallel. Due
- // to past flakes, this only checks that the amount of time taken has
- // some parallelism. Other tests such as ThreadPoolGreaterThanEqualRequested
- // check this more exactly.
- EXPECT_LE(epochMsAfter, epochMsBefore + (numCalls - 1) * sleepMs);
+ // b/272429574, b/365294257
+ // This flakes too much to test. Parallelization is tested
+ // in ThreadPoolGreaterThanEqualRequested and other tests.
+ // Test to make sure calls are handled in parallel.
+ // EXPECT_LE(epochMsAfter, epochMsBefore + (numCalls - 1) * sleepMs);
}
TEST_P(BinderRpc, ThreadPoolOverSaturated) {
@@ -484,8 +524,7 @@
constexpr size_t kNumCalls = kNumThreads + 3;
auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
- // b/272429574 - below 500ms, the test fails
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 500 /*ms*/);
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 200 /*ms*/);
}
TEST_P(BinderRpc, ThreadPoolLimitOutgoing) {
@@ -499,8 +538,7 @@
auto proc = createRpcTestSocketServerProcess(
{.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections});
- // b/272429574 - below 500ms, the test fails
- testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 500 /*ms*/);
+ testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 200 /*ms*/);
}
TEST_P(BinderRpc, ThreadingStressTest) {
@@ -1127,6 +1165,489 @@
ASSERT_EQ(beforeFds, countFds()) << (system("ls -l /proc/self/fd/"), "fd leak?");
}
+// TODO need to add IServiceManager.cpp/.h to libbinder_no_kernel
+#ifdef BINDER_WITH_KERNEL_IPC
+
+class BinderRpcAccessor : public BinderRpc {
+ void SetUp() override {
+ if (serverSingleThreaded()) {
+ // This blocks on android::FdTrigger::triggerablePoll when attempting to set
+ // up the client RpcSession
+ GTEST_SKIP() << "Accessors are not supported for single threaded libbinder";
+ }
+ if (rpcSecurity() == RpcSecurity::TLS) {
+ GTEST_SKIP() << "Accessors are not supported with TLS";
+ // ... for now
+ }
+
+ if (socketType() == SocketType::UNIX_BOOTSTRAP) {
+ GTEST_SKIP() << "Accessors do not support UNIX_BOOTSTRAP because no connection "
+ "information is known";
+ }
+ if (socketType() == SocketType::TIPC) {
+ GTEST_SKIP() << "Accessors do not support TIPC because the socket transport is not "
+ "known in libbinder";
+ }
+ BinderRpc::SetUp();
+ }
+};
+
+inline void waitForExtraSessionCleanup(const BinderRpcTestProcessSession& proc) {
+ // Need to give the server some time to delete its RpcSession after our last
+ // reference is dropped, closing the connection. Check for up to 1 second,
+ // every 10 ms.
+ for (size_t i = 0; i < 100; i++) {
+ std::vector<int32_t> remoteCounts;
+ EXPECT_OK(proc.rootIface->countBinders(&remoteCounts));
+ // We exect the original binder to still be alive, we just want to wait
+ // for this extra session to be cleaned up.
+ if (remoteCounts.size() == proc.proc->sessions.size()) break;
+ usleep(10000);
+ }
+}
+
+TEST_P(BinderRpcAccessor, InjectAndGetServiceHappyPath) {
+ constexpr size_t kNumThreads = 10;
+ const String16 kInstanceName("super.cool.service/better_than_default");
+
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ auto receipt = addAccessorProvider(
+ {String8(kInstanceName).c_str()}, [&](const String16& name) -> sp<IBinder> {
+ return createAccessor(name,
+ [&](const String16& name, sockaddr* outAddr,
+ socklen_t addrSize) -> status_t {
+ if (outAddr == nullptr ||
+ addrSize < proc.proc->sessions[0].addrLen) {
+ return BAD_VALUE;
+ }
+ if (name == kInstanceName) {
+ if (proc.proc->sessions[0].addr.ss_family ==
+ AF_UNIX) {
+ sockaddr_un* un = reinterpret_cast<sockaddr_un*>(
+ &proc.proc->sessions[0].addr);
+ ALOGE("inside callback: %s", un->sun_path);
+ }
+ std::memcpy(outAddr, &proc.proc->sessions[0].addr,
+ proc.proc->sessions[0].addrLen);
+ return OK;
+ }
+ return NAME_NOT_FOUND;
+ });
+ });
+
+ EXPECT_FALSE(receipt.expired());
+
+ sp<IBinder> binder = defaultServiceManager()->checkService(kInstanceName);
+ sp<IBinderRpcTest> service = checked_interface_cast<IBinderRpcTest>(binder);
+ EXPECT_NE(service, nullptr);
+
+ sp<IBinder> out;
+ EXPECT_OK(service->repeatBinder(binder, &out));
+ EXPECT_EQ(binder, out);
+
+ out.clear();
+ binder.clear();
+ service.clear();
+
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+
+ waitForExtraSessionCleanup(proc);
+}
+
+TEST_P(BinderRpcAccessor, InjectNoAccessorProvided) {
+ const String16 kInstanceName("doesnt_matter_nothing_checks");
+
+ bool isProviderDeleted = false;
+
+ auto receipt = addAccessorProvider({String8(kInstanceName).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+
+ sp<IBinder> binder = defaultServiceManager()->checkService(kInstanceName);
+ EXPECT_EQ(binder, nullptr);
+
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
+TEST_P(BinderRpcAccessor, InjectDuplicateAccessorProvider) {
+ const String16 kInstanceName("super.cool.service/better_than_default");
+ const String16 kInstanceName2("super.cool.service/better_than_default2");
+
+ auto receipt =
+ addAccessorProvider({String8(kInstanceName).c_str(), String8(kInstanceName2).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_FALSE(receipt.expired());
+ // reject this because it's associated with an already used instance name
+ auto receipt2 = addAccessorProvider({String8(kInstanceName).c_str()},
+ [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_TRUE(receipt2.expired());
+
+ // the first provider should still be usable
+ sp<IBinder> binder = defaultServiceManager()->checkService(kInstanceName);
+ EXPECT_EQ(binder, nullptr);
+
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
+TEST_P(BinderRpcAccessor, InjectAccessorProviderNoInstance) {
+ auto receipt = addAccessorProvider({}, [&](const String16&) -> sp<IBinder> { return nullptr; });
+ EXPECT_TRUE(receipt.expired());
+}
+
+TEST_P(BinderRpcAccessor, InjectNoSockaddrProvided) {
+ constexpr size_t kNumThreads = 10;
+ const String16 kInstanceName("super.cool.service/better_than_default");
+
+ auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads});
+ EXPECT_EQ(OK, proc.rootBinder->pingBinder());
+
+ bool isProviderDeleted = false;
+ bool isAccessorDeleted = false;
+
+ auto receipt = addAccessorProvider({String8(kInstanceName).c_str()},
+ [&](const String16& name) -> sp<IBinder> {
+ return createAccessor(name,
+ [&](const String16&, sockaddr*,
+ socklen_t) -> status_t {
+ // don't fill in outAddr
+ return NAME_NOT_FOUND;
+ });
+ });
+
+ EXPECT_FALSE(receipt.expired());
+
+ sp<IBinder> binder = defaultServiceManager()->checkService(kInstanceName);
+ EXPECT_EQ(binder, nullptr);
+
+ status_t status = removeAccessorProvider(receipt);
+ EXPECT_EQ(status, OK);
+}
+
+constexpr const char* kARpcInstance = "some.instance.name.IFoo/default";
+const char* kARpcSupportedServices[] = {
+ kARpcInstance,
+};
+const uint32_t kARpcNumSupportedServices = 1;
+
+struct ConnectionInfoData {
+ sockaddr_storage addr;
+ socklen_t len;
+ bool* isDeleted;
+ ~ConnectionInfoData() {
+ if (isDeleted) *isDeleted = true;
+ }
+};
+
+struct AccessorProviderData {
+ sockaddr_storage addr;
+ socklen_t len;
+ bool* isDeleted;
+ ~AccessorProviderData() {
+ if (isDeleted) *isDeleted = true;
+ }
+};
+
+void accessorProviderDataOnDelete(void* data) {
+ delete reinterpret_cast<AccessorProviderData*>(data);
+}
+void infoProviderDataOnDelete(void* data) {
+ delete reinterpret_cast<ConnectionInfoData*>(data);
+}
+
+ABinderRpc_ConnectionInfo* infoProvider(const char* instance, void* cookie) {
+ if (instance == nullptr || cookie == nullptr) return nullptr;
+ ConnectionInfoData* data = reinterpret_cast<ConnectionInfoData*>(cookie);
+ return ABinderRpc_ConnectionInfo_new(reinterpret_cast<const sockaddr*>(&data->addr), data->len);
+}
+
+ABinderRpc_Accessor* getAccessor(const char* instance, void* cookie) {
+ if (instance == nullptr || cookie == nullptr) return nullptr;
+ if (0 != strcmp(instance, kARpcInstance)) return nullptr;
+
+ AccessorProviderData* data = reinterpret_cast<AccessorProviderData*>(cookie);
+
+ ConnectionInfoData* info = new ConnectionInfoData{
+ .addr = data->addr,
+ .len = data->len,
+ .isDeleted = nullptr,
+ };
+
+ return ABinderRpc_Accessor_new(instance, infoProvider, info, infoProviderDataOnDelete);
+}
+
+class BinderARpcNdk : public ::testing::Test {};
+
+TEST_F(BinderARpcNdk, ARpcProviderNewDelete) {
+ bool isDeleted = false;
+
+ AccessorProviderData* data = new AccessorProviderData{{}, 0, &isDeleted};
+
+ ABinderRpc_AccessorProvider* provider =
+ ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices,
+ kARpcNumSupportedServices, data,
+ accessorProviderDataOnDelete);
+
+ ASSERT_NE(provider, nullptr);
+ EXPECT_FALSE(isDeleted);
+
+ ABinderRpc_unregisterAccessorProvider(provider);
+
+ EXPECT_TRUE(isDeleted);
+}
+
+TEST_F(BinderARpcNdk, ARpcProviderDuplicateInstance) {
+ const char* instance = "some.instance.name.IFoo/default";
+ const uint32_t numInstances = 2;
+ const char* instances[numInstances] = {
+ instance,
+ "some.other.instance/default",
+ };
+
+ bool isDeleted = false;
+
+ AccessorProviderData* data = new AccessorProviderData{{}, 0, &isDeleted};
+
+ ABinderRpc_AccessorProvider* provider =
+ ABinderRpc_registerAccessorProvider(getAccessor, instances, numInstances, data,
+ accessorProviderDataOnDelete);
+
+ ASSERT_NE(provider, nullptr);
+ EXPECT_FALSE(isDeleted);
+
+ const uint32_t numInstances2 = 1;
+ const char* instances2[numInstances2] = {
+ instance,
+ };
+ bool isDeleted2 = false;
+ AccessorProviderData* data2 = new AccessorProviderData{{}, 0, &isDeleted2};
+ ABinderRpc_AccessorProvider* provider2 =
+ ABinderRpc_registerAccessorProvider(getAccessor, instances2, numInstances2, data2,
+ accessorProviderDataOnDelete);
+
+ EXPECT_EQ(provider2, nullptr);
+ // If it fails to be registered, the data is still cleaned up with
+ // accessorProviderDataOnDelete
+ EXPECT_TRUE(isDeleted2);
+
+ ABinderRpc_unregisterAccessorProvider(provider);
+
+ EXPECT_TRUE(isDeleted);
+}
+
+TEST_F(BinderARpcNdk, ARpcProviderRegisterNoInstance) {
+ const uint32_t numInstances = 0;
+ const char* instances[numInstances] = {};
+
+ bool isDeleted = false;
+ AccessorProviderData* data = new AccessorProviderData{{}, 0, &isDeleted};
+
+ ABinderRpc_AccessorProvider* provider =
+ ABinderRpc_registerAccessorProvider(getAccessor, instances, numInstances, data,
+ accessorProviderDataOnDelete);
+ ASSERT_EQ(provider, nullptr);
+}
+
+TEST_F(BinderARpcNdk, ARpcAccessorNewDelete) {
+ bool isDeleted = false;
+
+ ConnectionInfoData* data = new ConnectionInfoData{{}, 0, &isDeleted};
+
+ ABinderRpc_Accessor* accessor =
+ ABinderRpc_Accessor_new("gshoe_service", infoProvider, data, infoProviderDataOnDelete);
+ ASSERT_NE(accessor, nullptr);
+ EXPECT_FALSE(isDeleted);
+
+ ABinderRpc_Accessor_delete(accessor);
+ EXPECT_TRUE(isDeleted);
+}
+
+TEST_F(BinderARpcNdk, ARpcConnectionInfoNewDelete) {
+ sockaddr_vm addr{
+ .svm_family = AF_VSOCK,
+ .svm_port = VMADDR_PORT_ANY,
+ .svm_cid = VMADDR_CID_ANY,
+ };
+
+ ABinderRpc_ConnectionInfo* info =
+ ABinderRpc_ConnectionInfo_new(reinterpret_cast<sockaddr*>(&addr), sizeof(sockaddr_vm));
+ EXPECT_NE(info, nullptr);
+
+ ABinderRpc_ConnectionInfo_delete(info);
+}
+
+TEST_F(BinderARpcNdk, ARpcAsFromBinderAsBinder) {
+ bool isDeleted = false;
+
+ ConnectionInfoData* data = new ConnectionInfoData{{}, 0, &isDeleted};
+
+ ABinderRpc_Accessor* accessor =
+ ABinderRpc_Accessor_new("gshoe_service", infoProvider, data, infoProviderDataOnDelete);
+ ASSERT_NE(accessor, nullptr);
+ EXPECT_FALSE(isDeleted);
+
+ {
+ ndk::SpAIBinder binder = ndk::SpAIBinder(ABinderRpc_Accessor_asBinder(accessor));
+ EXPECT_NE(binder.get(), nullptr);
+
+ ABinderRpc_Accessor* accessor2 =
+ ABinderRpc_Accessor_fromBinder("wrong_service_name", binder.get());
+ // The API checks for the expected service name that is associated with
+ // the accessor!
+ EXPECT_EQ(accessor2, nullptr);
+
+ accessor2 = ABinderRpc_Accessor_fromBinder("gshoe_service", binder.get());
+ EXPECT_NE(accessor2, nullptr);
+
+ // this is a new ABinderRpc_Accessor object that wraps the underlying
+ // libbinder object.
+ EXPECT_NE(accessor, accessor2);
+
+ ndk::SpAIBinder binder2 = ndk::SpAIBinder(ABinderRpc_Accessor_asBinder(accessor2));
+ EXPECT_EQ(binder.get(), binder2.get());
+
+ ABinderRpc_Accessor_delete(accessor2);
+ }
+
+ EXPECT_FALSE(isDeleted);
+ ABinderRpc_Accessor_delete(accessor);
+ EXPECT_TRUE(isDeleted);
+}
+
+TEST_F(BinderARpcNdk, ARpcRequireProviderOnDeleteCallback) {
+ EXPECT_EQ(nullptr,
+ ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices,
+ kARpcNumSupportedServices,
+ reinterpret_cast<void*>(1), nullptr));
+}
+
+TEST_F(BinderARpcNdk, ARpcRequireInfoOnDeleteCallback) {
+ EXPECT_EQ(nullptr,
+ ABinderRpc_Accessor_new("the_best_service_name", infoProvider,
+ reinterpret_cast<void*>(1), nullptr));
+}
+
+TEST_F(BinderARpcNdk, ARpcNoDataNoProviderOnDeleteCallback) {
+ ABinderRpc_AccessorProvider* provider =
+ ABinderRpc_registerAccessorProvider(getAccessor, kARpcSupportedServices,
+ kARpcNumSupportedServices, nullptr, nullptr);
+ ASSERT_NE(nullptr, provider);
+ ABinderRpc_unregisterAccessorProvider(provider);
+}
+
+TEST_F(BinderARpcNdk, ARpcNoDataNoInfoOnDeleteCallback) {
+ ABinderRpc_Accessor* accessor =
+ ABinderRpc_Accessor_new("the_best_service_name", infoProvider, nullptr, nullptr);
+ ASSERT_NE(nullptr, accessor);
+ ABinderRpc_Accessor_delete(accessor);
+}
+
+TEST_F(BinderARpcNdk, ARpcNullArgs_ConnectionInfo_new) {
+ sockaddr_storage addr;
+ EXPECT_EQ(nullptr, ABinderRpc_ConnectionInfo_new(reinterpret_cast<const sockaddr*>(&addr), 0));
+}
+
+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;
+
+ 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);
+
+ {
+ ndk::SpAIBinder binder = ndk::SpAIBinder(AServiceManager_checkService(kARpcInstance));
+ ASSERT_NE(binder.get(), nullptr);
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get()));
+ }
+
+ ABinderRpc_unregisterAccessorProvider(provider);
+ EXPECT_TRUE(isDeleted);
+
+ 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
static std::vector<BinderRpc::ParamType> getTrustyBinderRpcParams() {
@@ -1315,6 +1836,11 @@
INSTANTIATE_TEST_SUITE_P(PerSocket, BinderRpc, ::testing::ValuesIn(getBinderRpcParams()),
BinderRpc::PrintParamInfo);
+#ifdef BINDER_WITH_KERNEL_IPC
+INSTANTIATE_TEST_SUITE_P(PerSocket, BinderRpcAccessor, ::testing::ValuesIn(getBinderRpcParams()),
+ BinderRpc::PrintParamInfo);
+#endif // BINDER_WITH_KERNEL_IPC
+
class BinderRpcServerRootObject
: public ::testing::TestWithParam<std::tuple<bool, bool, RpcSecurity>> {};
@@ -1385,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/binderRpcTestFixture.h b/libs/binder/tests/binderRpcTestFixture.h
index 2c9646b..c8a8acc 100644
--- a/libs/binder/tests/binderRpcTestFixture.h
+++ b/libs/binder/tests/binderRpcTestFixture.h
@@ -35,6 +35,12 @@
struct SessionInfo {
sp<RpcSession> session;
sp<IBinder> root;
+// Trusty defines its own socket APIs in trusty_ipc.h but doesn't include
+// sockaddr types.
+#ifndef __TRUSTY__
+ sockaddr_storage addr;
+ socklen_t addrLen;
+#endif
};
// client session objects associated with other process
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
index 3ae3d8f..e955a34 100644
--- a/libs/binder/trusty/ndk/include/android/llndk-versioning.h
+++ b/libs/binder/trusty/ndk/include/android/llndk-versioning.h
@@ -15,4 +15,5 @@
*/
#pragma once
-#define __INTRODUCED_IN_LLNDK(x) /* nothing on Trusty */
+// 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/bufferstreams/rust/src/stream_config.rs b/libs/bufferstreams/rust/src/stream_config.rs
index 454bdf1..8288f9f 100644
--- a/libs/bufferstreams/rust/src/stream_config.rs
+++ b/libs/bufferstreams/rust/src/stream_config.rs
@@ -32,10 +32,23 @@
pub stride: u32,
}
+impl From<StreamConfig> for HardwareBufferDescription {
+ fn from(config: StreamConfig) -> Self {
+ HardwareBufferDescription::new(
+ config.width,
+ config.height,
+ config.layers,
+ config.format,
+ config.usage,
+ config.stride,
+ )
+ }
+}
+
impl StreamConfig {
/// Tries to create a new HardwareBuffer from settings in a [StreamConfig].
pub fn create_hardware_buffer(&self) -> Option<HardwareBuffer> {
- HardwareBuffer::new(self.width, self.height, self.layers, self.format, self.usage)
+ HardwareBuffer::new(&(*self).into())
}
}
@@ -59,9 +72,10 @@
assert!(maybe_buffer.is_some());
let buffer = maybe_buffer.unwrap();
- assert_eq!(config.width, buffer.width());
- assert_eq!(config.height, buffer.height());
- assert_eq!(config.format, buffer.format());
- assert_eq!(config.usage, buffer.usage());
+ let description = buffer.description();
+ assert_eq!(config.width, description.width());
+ assert_eq!(config.height, description.height());
+ assert_eq!(config.format, description.format());
+ assert_eq!(config.usage, description.usage());
}
}
diff --git a/libs/debugstore/OWNERS b/libs/debugstore/OWNERS
index 428a1a2..c8e22b7 100644
--- a/libs/debugstore/OWNERS
+++ b/libs/debugstore/OWNERS
@@ -1,3 +1,2 @@
benmiles@google.com
-gaillard@google.com
mohamadmahmoud@google.com
diff --git a/libs/debugstore/rust/Android.bp b/libs/debugstore/rust/Android.bp
index 55ba3c3..9475333 100644
--- a/libs/debugstore/rust/Android.bp
+++ b/libs/debugstore/rust/Android.bp
@@ -23,7 +23,6 @@
rustlibs: [
"libcrossbeam_queue",
"libparking_lot",
- "libonce_cell",
"libcxx",
],
shared_libs: ["libutils"],
diff --git a/libs/debugstore/rust/src/core.rs b/libs/debugstore/rust/src/core.rs
index 1dfa512..6bf79d4 100644
--- a/libs/debugstore/rust/src/core.rs
+++ b/libs/debugstore/rust/src/core.rs
@@ -17,12 +17,14 @@
use super::event_type::EventType;
use super::storage::Storage;
use crate::cxxffi::uptimeMillis;
-use once_cell::sync::Lazy;
use std::fmt;
-use std::sync::atomic::{AtomicU64, Ordering};
+use std::sync::{
+ atomic::{AtomicU64, Ordering},
+ LazyLock,
+};
// Lazily initialized static instance of DebugStore.
-static INSTANCE: Lazy<DebugStore> = Lazy::new(DebugStore::new);
+static INSTANCE: LazyLock<DebugStore> = LazyLock::new(DebugStore::new);
/// The `DebugStore` struct is responsible for managing debug events and data.
pub struct DebugStore {
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/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 3c1971f..25e6a52 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -1124,6 +1124,17 @@
AsyncWorker::getInstance().post(
[listener = mListener, slots = slots]() { listener->onBuffersDiscarded(slots); });
}
+
+ void onBufferDetached(int slot) override {
+ AsyncWorker::getInstance().post(
+ [listener = mListener, slot = slot]() { listener->onBufferDetached(slot); });
+ }
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ void onBufferAttached() override {
+ AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferAttached(); });
+ }
+#endif
};
// Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls
@@ -1255,6 +1266,11 @@
mTransactionHangCallback = std::move(callback);
}
+void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) {
+ std::lock_guard _lock{mMutex};
+ mApplyToken = std::move(applyToken);
+}
+
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a4d105d..da74e9c 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -45,7 +45,10 @@
#include <system/window.h>
+#include <com_android_graphics_libgui_flags.h>
+
namespace android {
+using namespace com::android::graphics::libgui;
// Macros for include BufferQueueCore information in log messages
#define BQ_LOGV(x, ...) \
@@ -924,6 +927,7 @@
uint64_t currentFrameNumber = 0;
BufferItem item;
int connectedApi;
+ bool enableEglCpuThrottling = true;
sp<Fence> lastQueuedFence;
{ // Autolock scope
@@ -1097,6 +1101,9 @@
VALIDATE_CONSISTENCY();
connectedApi = mCore->mConnectedApi;
+ if (flags::bq_producer_throttles_only_async_mode()) {
+ enableEglCpuThrottling = mCore->mAsyncMode || mCore->mDequeueBufferCannotBlock;
+ }
lastQueuedFence = std::move(mLastQueueBufferFence);
mLastQueueBufferFence = std::move(acquireFence);
@@ -1142,7 +1149,7 @@
}
// Wait without lock held
- if (connectedApi == NATIVE_WINDOW_API_EGL) {
+ if (connectedApi == NATIVE_WINDOW_API_EGL && enableEglCpuThrottling) {
// Waiting here allows for two full buffers to be queued but not a
// third. In the event that frames take varying time, this makes a
// small trade-off in favor of latency rather than throughput.
diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp
index 7700795..8b9b090 100644
--- a/libs/gui/IProducerListener.cpp
+++ b/libs/gui/IProducerListener.cpp
@@ -184,4 +184,10 @@
void BnProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*discardedSlots*/) {
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+bool BnProducerListener::needsAttachNotify() {
+ return true;
+}
+#endif
+
} // namespace android
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/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp
index 601a5f9..2de023e 100644
--- a/libs/gui/ScreenCaptureResults.cpp
+++ b/libs/gui/ScreenCaptureResults.cpp
@@ -40,6 +40,13 @@
SAFE_PARCEL(parcel->writeBool, capturedSecureLayers);
SAFE_PARCEL(parcel->writeBool, capturedHdrLayers);
SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(capturedDataspace));
+ if (optionalGainMap != nullptr) {
+ SAFE_PARCEL(parcel->writeBool, true);
+ SAFE_PARCEL(parcel->write, *optionalGainMap);
+ } else {
+ SAFE_PARCEL(parcel->writeBool, false);
+ }
+ SAFE_PARCEL(parcel->writeFloat, hdrSdrRatio);
return NO_ERROR;
}
@@ -68,6 +75,14 @@
uint32_t dataspace = 0;
SAFE_PARCEL(parcel->readUint32, &dataspace);
capturedDataspace = static_cast<ui::Dataspace>(dataspace);
+
+ bool hasGainmap;
+ SAFE_PARCEL(parcel->readBool, &hasGainmap);
+ if (hasGainmap) {
+ optionalGainMap = new GraphicBuffer();
+ SAFE_PARCEL(parcel->read, *optionalGainMap);
+ }
+ SAFE_PARCEL(parcel->readFloat, &hdrSdrRatio);
return NO_ERROR;
}
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/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp
index c5f9c38..f126c0b 100644
--- a/libs/gui/SurfaceControl.cpp
+++ b/libs/gui/SurfaceControl.cpp
@@ -139,9 +139,9 @@
uint32_t ignore;
auto flags = mCreateFlags & (ISurfaceComposerClient::eCursorWindow |
ISurfaceComposerClient::eOpaque);
- mBbqChild = mClient->createSurface(String8("bbq-wrapper"), 0, 0, mFormat,
+ mBbqChild = mClient->createSurface(String8::format("[BBQ] %s", mName.c_str()), 0, 0, mFormat,
flags, mHandle, {}, &ignore);
- mBbq = sp<BLASTBufferQueue>::make("bbq-adapter", mBbqChild, mWidth, mHeight, mFormat);
+ mBbq = sp<BLASTBufferQueue>::make("[BBQ]" + mName, mBbqChild, mWidth, mHeight, mFormat);
// This surface is always consumed by SurfaceFlinger, so the
// producerControlledByApp value doesn't matter; using false.
diff --git a/libs/gui/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl
index 2bbed2b..4920344 100644
--- a/libs/gui/aidl/android/gui/CaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl
@@ -69,5 +69,10 @@
// exact colorspace is not an appropriate intermediate result.
// Note that if the caller is requesting a specific dataspace, this hint does nothing.
boolean hintForSeamlessTransition = false;
+
+ // Allows the screenshot to attach a gainmap, which allows for a per-pixel
+ // transformation of the screenshot to another luminance range, typically
+ // mapping an SDR base image into HDR.
+ boolean attachGainmap = false;
}
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/JankData.aidl b/libs/gui/aidl/android/gui/JankData.aidl
index 7ea9d22..ec13681 100644
--- a/libs/gui/aidl/android/gui/JankData.aidl
+++ b/libs/gui/aidl/android/gui/JankData.aidl
@@ -29,7 +29,17 @@
int jankType;
/**
- * Expected duration in nanoseconds of this frame.
+ * Time between frames in nanoseconds.
*/
long frameIntervalNs;
+
+ /**
+ * Time allocated to the application to render this frame.
+ */
+ long scheduledAppFrameTimeNs;
+
+ /**
+ * Time taken by the application to render this frame.
+ */
+ long actualAppFrameTimeNs;
}
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/aidl/android/gui/ScreenCaptureResults.aidl b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
index 97a9035..f4ef16d 100644
--- a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
+++ b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
@@ -16,4 +16,4 @@
package android.gui;
-parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h" rust_type "gui_aidl_types_rs::ScreenCaptureResults";
\ No newline at end of file
+parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h" rust_type "gui_aidl_types_rs::ScreenCaptureResults";
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index d787d6c..8592cff 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -142,7 +142,7 @@
* indicates the reason for the hang.
*/
void setTransactionHangCallback(std::function<void(const std::string&)> callback);
-
+ void setApplyToken(sp<IBinder>);
virtual ~BLASTBufferQueue();
void onFirstRef() override;
@@ -271,7 +271,7 @@
// Queues up transactions using this token in SurfaceFlinger. This prevents queued up
// transactions from other parts of the client from blocking this transaction.
- const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = sp<BBinder>::make();
+ sp<IBinder> mApplyToken GUARDED_BY(mMutex) = sp<BBinder>::make();
// Guards access to mDequeueTimestamps since we cannot hold to mMutex in onFrameDequeued or
// we will deadlock.
diff --git a/libs/gui/include/gui/Flags.h b/libs/gui/include/gui/Flags.h
new file mode 100644
index 0000000..735375a
--- /dev/null
+++ b/libs/gui/include/gui/Flags.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <com_android_graphics_libgui_flags.h>
+
+#define WB_CAMERA3_AND_PROCESSORS_WITH_DEPENDENCIES \
+ (COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CAMERA3_AND_PROCESSORS) && \
+ COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ) && \
+ COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_PLATFORM_API_IMPROVEMENTS))
\ No newline at end of file
diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h
index 3dcc6b6..43bf6a7 100644
--- a/libs/gui/include/gui/IProducerListener.h
+++ b/libs/gui/include/gui/IProducerListener.h
@@ -90,6 +90,9 @@
Parcel* reply, uint32_t flags = 0);
virtual bool needsReleaseNotify();
virtual void onBuffersDiscarded(const std::vector<int32_t>& slots);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ virtual bool needsAttachNotify();
+#endif
};
#else
@@ -103,6 +106,9 @@
virtual ~StubProducerListener();
virtual void onBufferReleased() {}
virtual bool needsReleaseNotify() { return false; }
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_CONSUMER_ATTACH_CALLBACK)
+ virtual bool needsAttachNotify() { return false; }
+#endif
};
} // namespace android
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/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h
index 6e17791..f176f48 100644
--- a/libs/gui/include/gui/ScreenCaptureResults.h
+++ b/libs/gui/include/gui/ScreenCaptureResults.h
@@ -36,6 +36,11 @@
bool capturedSecureLayers{false};
bool capturedHdrLayers{false};
ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB};
+ // A gainmap that can be used to "lift" the screenshot into HDR
+ sp<GraphicBuffer> optionalGainMap;
+ // HDR/SDR ratio value that fully applies the gainmap.
+ // Note that we use 1/64 epsilon offsets to eliminate precision issues
+ float hdrSdrRatio{1.0f};
};
} // namespace android::gui
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index e74f9ad..14a3513 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -34,8 +34,6 @@
#include <shared_mutex>
#include <unordered_set>
-#include <com_android_graphics_libgui_flags.h>
-
namespace android {
class GraphicBuffer;
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 7468401..1c7e0e4 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -98,4 +98,28 @@
description: "Remove usage of IGBPs in the *Processor and Camera3*"
bug: "342199002"
is_fixed_read_only: true
-} # wb_camera3_and_processors
\ No newline at end of file
+} # wb_camera3_and_processors
+
+flag {
+ name: "wb_libcameraservice"
+ namespace: "core_graphics"
+ description: "Remove usage of IGBPs in the libcameraservice."
+ bug: "342197849"
+ is_fixed_read_only: true
+} # 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."
+ bug: "359252619"
+ is_fixed_read_only: true
+} # bq_producer_throttles_only_async_mode
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index eb2a61d..53f4a36 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -186,6 +186,10 @@
mBlastBufferQueueAdapter->mergeWithNextTransaction(merge, frameNumber);
}
+ void setApplyToken(sp<IBinder> applyToken) {
+ mBlastBufferQueueAdapter->setApplyToken(std::move(applyToken));
+ }
+
private:
sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter;
};
@@ -511,6 +515,69 @@
adapter.waitForCallbacks();
}
+class WaitForCommittedCallback {
+public:
+ WaitForCommittedCallback() = default;
+ ~WaitForCommittedCallback() = default;
+
+ void wait() {
+ std::unique_lock lock(mMutex);
+ cv.wait(lock, [this] { return mCallbackReceived; });
+ }
+
+ void notify() {
+ std::unique_lock lock(mMutex);
+ mCallbackReceived = true;
+ cv.notify_one();
+ mCallbackReceivedTimeStamp = std::chrono::system_clock::now();
+ }
+ auto getCallback() {
+ return [this](void* /* unused context */, nsecs_t /* latchTime */,
+ const sp<Fence>& /* presentFence */,
+ const std::vector<SurfaceControlStats>& /* stats */) { notify(); };
+ }
+ std::chrono::time_point<std::chrono::system_clock> mCallbackReceivedTimeStamp;
+
+private:
+ std::mutex mMutex;
+ std::condition_variable cv;
+ bool mCallbackReceived = false;
+};
+
+TEST_F(BLASTBufferQueueTest, setApplyToken) {
+ sp<IBinder> applyToken = sp<BBinder>::make();
+ WaitForCommittedCallback firstTransaction;
+ WaitForCommittedCallback secondTransaction;
+ {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ adapter.setApplyToken(applyToken);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction t;
+ t.addTransactionCommittedCallback(firstTransaction.getCallback(), nullptr);
+ adapter.mergeWithNextTransaction(&t, 1);
+ queueBuffer(igbProducer, 127, 127, 127,
+ /*presentTimeDelay*/ std::chrono::nanoseconds(500ms).count());
+ }
+ {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ adapter.setApplyToken(applyToken);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction t;
+ t.addTransactionCommittedCallback(secondTransaction.getCallback(), nullptr);
+ adapter.mergeWithNextTransaction(&t, 1);
+ queueBuffer(igbProducer, 127, 127, 127, /*presentTimeDelay*/ 0);
+ }
+
+ firstTransaction.wait();
+ secondTransaction.wait();
+ EXPECT_GT(secondTransaction.mCallbackReceivedTimeStamp,
+ firstTransaction.mCallbackReceivedTimeStamp);
+}
+
TEST_F(BLASTBufferQueueTest, SetCrop_Item) {
uint8_t r = 255;
uint8_t g = 0;
diff --git a/libs/gui/tests/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp
index 2ac2550..8db48d2 100644
--- a/libs/gui/tests/Choreographer_test.cpp
+++ b/libs/gui/tests/Choreographer_test.cpp
@@ -52,25 +52,23 @@
sp<Looper> looper = Looper::prepare(0);
Choreographer* choreographer = Choreographer::getForThread();
VsyncCallback animationCb;
- VsyncCallback inputCb;
-
choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0,
CALLBACK_ANIMATION);
+ VsyncCallback inputCb;
choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &inputCb, 0,
CALLBACK_INPUT);
-
- nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
- nsecs_t currTime;
- int pollResult;
+ auto startTime = std::chrono::system_clock::now();
do {
- pollResult = looper->pollOnce(16);
- currTime = systemTime(SYSTEM_TIME_MONOTONIC);
- } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()) &&
- (pollResult != Looper::POLL_TIMEOUT && pollResult != Looper::POLL_ERROR) &&
- (currTime - startTime < 3000));
-
- ASSERT_TRUE(inputCb.callbackReceived()) << "did not receive input callback";
- ASSERT_TRUE(animationCb.callbackReceived()) << "did not receive animation callback";
+ static constexpr int32_t timeoutMs = 1000;
+ int pollResult = looper->pollOnce(timeoutMs);
+ ASSERT_TRUE((pollResult != Looper::POLL_TIMEOUT) && (pollResult != Looper::POLL_ERROR))
+ << "Failed to poll looper. Poll result = " << pollResult;
+ auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::system_clock::now() - startTime);
+ ASSERT_LE(elapsedMs.count(), timeoutMs)
+ << "Timed out waiting for callbacks. inputCb=" << inputCb.callbackReceived()
+ << " animationCb=" << animationCb.callbackReceived();
+ } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()));
ASSERT_EQ(inputCb.frameTime, animationCb.frameTime)
<< android::base::StringPrintf("input and animation callback frame times don't match. "
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 99ffa68..9665de7 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-#define LOG_TAG "InputTransport"
+#define LOG_TAG "InputConsumerNoResampling"
#define ATRACE_TAG ATRACE_TAG_INPUT
-#include <chrono>
-
#include <inttypes.h>
#include <android-base/logging.h>
@@ -33,12 +31,14 @@
#include <input/PrintTools.h>
#include <input/TraceTools.h>
-namespace input_flags = com::android::input::flags;
-
namespace android {
namespace {
+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,26 +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;
-using android::base::StringPrintf;
-
// --- InputConsumerNoResampling ---
-InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
- sp<Looper> looper,
- InputConsumerCallbacks& callbacks,
- std::unique_ptr<Resampler> resampler)
- : mChannel(channel),
- mLooper(looper),
- mCallbacks(callbacks),
- mResampler(std::move(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},
+ mResamplerCreator{std::move(resamplerCreator)},
mFdEvents(0) {
LOG_ALWAYS_FATAL_IF(mLooper == nullptr);
mCallback = sp<LooperEventCallback>::make(
@@ -201,13 +193,29 @@
InputConsumerNoResampling::~InputConsumerNoResampling() {
ensureCalledOnLooperThread(__func__);
- consumeBatchedInputEvents(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) {
@@ -227,8 +235,7 @@
int handledEvents = 0;
if (events & ALOOPER_EVENT_INPUT) {
- std::vector<InputMessage> messages = readAllMessages();
- handleMessages(std::move(messages));
+ handleMessages(readAllMessages());
handledEvents |= ALOOPER_EVENT_INPUT;
}
@@ -322,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;
@@ -332,14 +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 event immediately
- // TODO(b/329776327): figure out if this could be smarter by limiting the
- // consumption only to the current device.
- consumeBatchedInputEvents(std::nullopt);
+ // 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 {
@@ -457,11 +480,21 @@
}
std::pair<std::unique_ptr<MotionEvent>, std::optional<uint32_t>>
-InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t frameTime,
+InputConsumerNoResampling::createBatchedMotionEvent(const nsecs_t requestedFrameTime,
std::queue<InputMessage>& messages) {
std::unique_ptr<MotionEvent> motionEvent;
std::optional<uint32_t> firstSeqForBatch;
- while (!messages.empty() && !(messages.front().body.motion.eventTime > frameTime)) {
+
+ 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() &&
+ (messages.front().body.motion.eventTime <= adjustedFrameTime.count())) {
if (motionEvent == nullptr) {
motionEvent = createMotionEvent(messages.front());
firstSeqForBatch = messages.front().header.seq;
@@ -474,43 +507,60 @@
}
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(static_cast<std::chrono::nanoseconds>(frameTime),
- *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);
}
bool InputConsumerNoResampling::consumeBatchedInputEvents(
- std::optional<nsecs_t> requestedFrameTime) {
+ std::optional<DeviceId> deviceId, std::optional<nsecs_t> requestedFrameTime) {
ensureCalledOnLooperThread(__func__);
// When batching is not enabled, we want to consume all events. That's equivalent to having an
- // infinite frameTime.
- const nsecs_t frameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
+ // infinite requestedFrameTime.
+ requestedFrameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
bool producedEvents = false;
- for (auto& [_, messages] : mBatches) {
- auto [motion, firstSeqForBatch] = createBatchedMotionEvent(frameTime, messages);
+
+ for (auto deviceIdIter = (deviceId.has_value()) ? (mBatches.find(*deviceId))
+ : (mBatches.begin());
+ deviceIdIter != mBatches.cend(); ++deviceIdIter) {
+ std::queue<InputMessage>& messages = deviceIdIter->second;
+ auto [motion, firstSeqForBatch] = createBatchedMotionEvent(*requestedFrameTime, messages);
if (motion != nullptr) {
LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value());
mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch);
producedEvents = true;
} else {
- // This is OK, it just means that the frameTime is too old (all events that we have
- // pending are in the future of the frametime). Maybe print a
- // warning? If there are multiple devices active though, this might be normal and can
- // just be ignored, unless none of them resulted in any consumption (in that case, this
- // function would already return "false" so we could just leave it up to the caller).
+ // This is OK, it just means that the requestedFrameTime is too old (all events that we
+ // have pending are in the future of the requestedFrameTime). Maybe print a warning? If
+ // there are multiple devices active though, this might be normal and can just be
+ // ignored, unless none of them resulted in any consumption (in that case, this function
+ // would already return "false" so we could just leave it up to the caller).
+ }
+
+ if (deviceId.has_value()) {
+ // We already consumed events for this device. Break here to prevent iterating over the
+ // other devices.
+ break;
}
}
std::erase_if(mBatches, [](const auto& pair) { return pair.second.empty(); });
return producedEvents;
}
+bool InputConsumerNoResampling::consumeBatchedInputEvents(
+ std::optional<nsecs_t> requestedFrameTime) {
+ return consumeBatchedInputEvents(/*deviceId=*/std::nullopt, requestedFrameTime);
+}
+
void InputConsumerNoResampling::ensureCalledOnLooperThread(const char* func) const {
sp<Looper> callingThreadLooper = Looper::getForThread();
if (callingThreadLooper != mLooper) {
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 9333ab8..c903031 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -20,6 +20,7 @@
#include <unistd.h>
#include <ctype.h>
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <ftl/enum.h>
@@ -31,6 +32,9 @@
namespace android {
+// Set to true to log detailed debugging messages about IDC file probing.
+static constexpr bool DEBUG_PROBE = false;
+
static const char* CONFIGURATION_FILE_DIR[] = {
"idc/",
"keylayout/",
@@ -114,15 +118,18 @@
for (const auto& prefix : pathPrefixes) {
path = prefix;
appendInputDeviceConfigurationFileRelativePath(path, name, type);
-#if DEBUG_PROBE
- ALOGD("Probing for system provided input device configuration file: path='%s'",
- path.c_str());
-#endif
if (!access(path.c_str(), R_OK)) {
-#if DEBUG_PROBE
- ALOGD("Found");
-#endif
+ LOG_IF(INFO, DEBUG_PROBE)
+ << "Found system-provided input device configuration file at " << path;
return path;
+ } else if (errno != ENOENT) {
+ LOG(WARNING) << "Couldn't find a system-provided input device configuration file at "
+ << path << " due to error " << errno << " (" << strerror(errno)
+ << "); there may be an IDC file there that cannot be loaded.";
+ } else {
+ LOG_IF(ERROR, DEBUG_PROBE)
+ << "Didn't find system-provided input device configuration file at " << path
+ << ": " << strerror(errno);
}
}
@@ -135,21 +142,22 @@
}
path += "/system/devices/";
appendInputDeviceConfigurationFileRelativePath(path, name, type);
-#if DEBUG_PROBE
- ALOGD("Probing for system user input device configuration file: path='%s'", path.c_str());
-#endif
if (!access(path.c_str(), R_OK)) {
-#if DEBUG_PROBE
- ALOGD("Found");
-#endif
+ LOG_IF(INFO, DEBUG_PROBE) << "Found system user input device configuration file at "
+ << path;
return path;
+ } else if (errno != ENOENT) {
+ LOG(WARNING) << "Couldn't find a system user input device configuration file at " << path
+ << " due to error " << errno << " (" << strerror(errno)
+ << "); there may be an IDC file there that cannot be loaded.";
+ } else {
+ LOG_IF(ERROR, DEBUG_PROBE) << "Didn't find system user input device configuration file at "
+ << path << ": " << strerror(errno);
}
// Not found.
-#if DEBUG_PROBE
- ALOGD("Probe failed to find input device configuration file: name='%s', type=%d",
- name.c_str(), type);
-#endif
+ LOG_IF(INFO, DEBUG_PROBE) << "Probe failed to find input device configuration file with name '"
+ << name << "' and type " << ftl::enum_string(type);
return "";
}
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index 1cf5612..d775327 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -317,19 +317,8 @@
return true;
}
-void KeyCharacterMap::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
- if (fromKeyCode == toKeyCode) {
- mKeyRemapping.erase(fromKeyCode);
-#if DEBUG_MAPPING
- ALOGD("addKeyRemapping: Cleared remapping forKeyCode=%d ~ Result Successful.", fromKeyCode);
-#endif
- return;
- }
- mKeyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
-#if DEBUG_MAPPING
- ALOGD("addKeyRemapping: fromKeyCode=%d, toKeyCode=%d ~ Result Successful.", fromKeyCode,
- toKeyCode);
-#endif
+void KeyCharacterMap::setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) {
+ mKeyRemapping = keyRemapping;
}
status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const {
@@ -376,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 c663649..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});
@@ -241,12 +263,59 @@
motionEvent.getId());
}
-void LegacyResampler::resampleMotionEvent(nanoseconds resampleTime, MotionEvent& motionEvent,
- const InputMessage* futureSample) {
- if (mPreviousDeviceId && *mPreviousDeviceId != motionEvent.getDeviceId()) {
- mLatestSamples.clear();
+nanoseconds LegacyResampler::getResampleLatency() const {
+ 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);
}
- mPreviousDeviceId = motionEvent.getDeviceId();
+}
+
+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) {
+ 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);
@@ -255,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 b8a8d76..701fb43 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -185,3 +185,32 @@
description: "Collect quality metrics on framework palm rejection."
bug: "341717757"
}
+
+flag {
+ name: "enable_touchpad_no_focus_change"
+ namespace: "input"
+ description: "Prevents touchpad gesture changing window focus."
+ bug: "364460018"
+}
+
+flag {
+ name: "enable_input_policy_profile"
+ namespace: "input"
+ description: "Apply input policy profile for input threads."
+ bug: "347122505"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "keyboard_repeat_keys"
+ namespace: "input"
+ 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/rust/lib.rs b/libs/input/rust/lib.rs
index 9b6fe3c..4f4ea85 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -31,6 +31,7 @@
pub use keyboard_classifier::KeyboardClassifier;
#[cxx::bridge(namespace = "android::input")]
+#[allow(clippy::needless_maybe_sized)]
#[allow(unsafe_op_in_unsafe_fn)]
mod ffi {
#[namespace = "android"]
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 132866b..661c9f7 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -16,6 +16,8 @@
"BlockingQueue_test.cpp",
"IdGenerator_test.cpp",
"InputChannel_test.cpp",
+ "InputConsumer_test.cpp",
+ "InputConsumerResampling_test.cpp",
"InputDevice_test.cpp",
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
@@ -25,6 +27,7 @@
"MotionPredictorMetricsManager_test.cpp",
"Resampler_test.cpp",
"RingBuffer_test.cpp",
+ "TestInputChannel.cpp",
"TfLiteMotionPredictor_test.cpp",
"TouchResampling_test.cpp",
"TouchVideoFrame_test.cpp",
@@ -92,6 +95,7 @@
},
},
},
+ native_coverage: false,
}
// NOTE: This is a compile time test, and does not need to be
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
new file mode 100644
index 0000000..6a3bbe5
--- /dev/null
+++ b/libs/input/tests/InputConsumer_test.cpp
@@ -0,0 +1,446 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <input/InputConsumerNoResampling.h>
+
+#include <gtest/gtest.h>
+
+#include <chrono>
+#include <memory>
+#include <optional>
+
+#include <TestEventMatchers.h>
+#include <TestInputChannel.h>
+#include <android-base/logging.h>
+#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>
+
+namespace android {
+
+namespace {
+
+using std::chrono::nanoseconds;
+
+using ::testing::AllOf;
+using ::testing::Matcher;
+
+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 {
+protected:
+ InputConsumerTest()
+ : 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));
+ callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr);
+ }
+
+ void assertOnBatchedInputEventPendingWasCalled() {
+ ASSERT_GT(mOnBatchedInputEventPendingInvocationCount, 0UL)
+ << "onBatchedInputEventPending has not been called.";
+ --mOnBatchedInputEventPendingInvocationCount;
+ }
+
+ 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;
+
+ 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, /*handled=*/true);
+ }
+ void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
+ mMotionEvents.push(std::move(event));
+ if (mShouldFinishMotions) {
+ mConsumer->finishInputEvent(seq, /*handled=*/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, /*handled=*/true);
+ };
+ void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
+ mCaptureEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ };
+ void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
+ mDragEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, /*handled=*/true);
+ }
+ void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
+ mTouchModeEvents.push(std::move(event));
+ 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(ACTION_DOWN)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
+ .eventTime(nanoseconds{5ms}.count())
+ .action(ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
+ .eventTime(nanoseconds{10ms}.count())
+ .action(ACTION_MOVE)
+ .build());
+
+ mClientTestChannel->assertNoSentMessages();
+
+ invokeLooperCallback();
+
+ assertOnBatchedInputEventPendingWasCalled();
+
+ mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt);
+
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ std::unique_ptr<MotionEvent> moveMotionEvent =
+ assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE));
+ ASSERT_NE(moveMotionEvent, nullptr);
+ EXPECT_EQ(moveMotionEvent->getHistorySize() + 1, 3UL);
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+}
+
+TEST_F(InputConsumerTest, LastBatchedSampleIsLessThanResampleTime) {
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}
+ .eventTime(nanoseconds{0ms}.count())
+ .action(ACTION_DOWN)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
+ .eventTime(nanoseconds{5ms}.count())
+ .action(ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
+ .eventTime(nanoseconds{10ms}.count())
+ .action(ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
+ .eventTime(nanoseconds{15ms}.count())
+ .action(ACTION_MOVE)
+ .build());
+
+ mClientTestChannel->assertNoSentMessages();
+
+ invokeLooperCallback();
+
+ assertOnBatchedInputEventPendingWasCalled();
+
+ mConsumer->consumeBatchedInputEvents(16'000'000 /*ns*/);
+
+ assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ 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());
+
+ 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();
+}
+
+/**
+ * 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(ACTION_DOWN)
+ .build());
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(ACTION_DOWN)));
+
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}
+ .deviceId(0)
+ .action(ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/2}
+ .deviceId(0)
+ .action(ACTION_MOVE)
+ .build());
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/3}
+ .deviceId(0)
+ .action(ACTION_MOVE)
+ .build());
+
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/4}
+ .deviceId(1)
+ .action(ACTION_DOWN)
+ .build());
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent(AllOf(WithDeviceId(1), WithMotionAction(ACTION_DOWN)));
+
+ mClientTestChannel->enqueueMessage(InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/5}
+ .deviceId(0)
+ .action(AMOTION_EVENT_ACTION_UP)
+ .build());
+
+ invokeLooperCallback();
+ assertReceivedMotionEvent(AllOf(WithDeviceId(0), WithMotionAction(ACTION_MOVE)));
+
+ mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/4, /*handled=*/true);
+ mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+ 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/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index 467c3b4..1210f71 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -364,7 +364,7 @@
if (!mConsumer->probablyHasInput()) {
ADD_FAILURE() << "should deterministically have input because there is a batch";
}
- mConsumer->consumeBatchedInputEvents(std::nullopt);
+ mConsumer->consumeBatchedInputEvents(/*frameTime=*/std::nullopt);
};
void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
mFocusEvents.push(std::move(event));
diff --git a/libs/input/tests/Resampler_test.cpp b/libs/input/tests/Resampler_test.cpp
index 7ae9a28..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);
@@ -120,6 +119,47 @@
} // namespace
+/**
+ * The testing setup assumes an input rate of 200 Hz and a display rate of 60 Hz. This implies that
+ * input events are received every 5 milliseconds, while the display consumes batched events every
+ * ~16 milliseconds. The resampler's RESAMPLE_LATENCY constant determines the resample time, which
+ * is calculated as frameTime - RESAMPLE_LATENCY. resampleTime specifies the time used for
+ * resampling. For example, if the desired frame time consumption is ~16 milliseconds, the resample
+ * time would be ~11 milliseconds. Consequenly, the last added sample to the motion event has an
+ * event time of ~11 milliseconds. Note that there are specific scenarios where resampleMotionEvent
+ * is not called with a multiple of ~16 milliseconds. These cases are primarily for data addition
+ * or to test other functionalities of the resampler.
+ *
+ * Coordinates are calculated using linear interpolation (lerp) based on the last two available
+ * samples. Linear interpolation is defined as (a + alpha*(b - a)). Let t_b and t_a be the
+ * timestamps of samples a and b, respectively. The interpolation factor alpha is calculated as
+ * (resampleTime - t_a) / (t_b - t_a). The value of alpha determines whether the resampled
+ * coordinates are interpolated or extrapolated. If alpha falls within the semi-closed interval [0,
+ * 1), the coordinates are interpolated. If alpha is greater than or equal to 1, the coordinates are
+ * extrapolated.
+ *
+ * The timeline below depics an interpolation scenario
+ * -----------------------------------|---------|---------|---------|----------
+ * 10ms 11ms 15ms 16ms
+ * MOVE | MOVE |
+ * resampleTime frameTime
+ * Based on the timeline alpha is (11 - 10)/(15 - 10) = 1/5. Thus, coordinates are interpolated.
+ *
+ * The following timeline portrays an extrapolation scenario
+ * -------------------------|---------|---------|-------------------|----------
+ * 5ms 10ms 11ms 16ms
+ * MOVE MOVE | |
+ * resampleTime frameTime
+ * Likewise, alpha = (11 - 5)/(10 - 5) = 6/5. Hence, coordinates are extrapolated.
+ *
+ * If a motion event was resampled, the tests will check that the following conditions are satisfied
+ * to guarantee resampling correctness:
+ * - The motion event metadata must not change.
+ * - The number of samples in the motion event must only increment by 1.
+ * - The resampled values must be at the end of motion event coordinates.
+ * - The rasamples values must be near the hand calculations.
+ * - The resampled time must be the most recent one in motion event.
+ */
class ResamplerTest : public testing::Test {
protected:
ResamplerTest() : mResampler(std::make_unique<LegacyResampler>()) {}
@@ -225,7 +265,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
EXPECT_EQ(motionEvent.getTouchMajor(0), TOUCH_MAJOR_VALUE);
@@ -243,50 +283,11 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr);
+ mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr);
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);
-}
-
-// Increments of 16 ms for display refresh rate
-// Increments of 6 ms for input frequency
-// Resampling latency is known to be 5 ms
-// Therefore, first resampling time will be 11 ms
-
-/**
- * Timeline
- * ----+----------------------+---------+---------+---------+----------
- * 0ms 10ms 11ms 15ms 16ms
- * DOWN MOVE | MSG |
- * resample frame
- * Resampling occurs at 11ms. It is possible to interpolate because there is a sample available
- * after the resample time. It is assumed that the InputMessage frequency is 100Hz, and the frame
- * frequency is 60Hz. This means the time between InputMessage samples is 10ms, and the time between
- * frames is ~16ms. Resample time is frameTime - RESAMPLE_LATENCY. The resampled sample must be the
- * last one in the batch to consume.
- */
TEST_F(ResamplerTest, SinglePointerSingleSampleInterpolation) {
MotionEvent motionEvent =
InputStream{{InputSample{10ms,
@@ -297,7 +298,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
{Pointer{.id = 0,
@@ -338,18 +339,13 @@
const MotionEvent originalMotionEvent = secondMotionEvent;
- mResampler->resampleMotionEvent(11ms, secondMotionEvent, nullptr);
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, nullptr);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent,
{Pointer{.id = 0,
.x = 2.2f,
.y = 4.4f,
.isResampled = true}});
- // Integrity of the whole motionEvent
- // History size should increment by 1
- // Check if the resampled value is the last one
- // Check if the resampleTime is correct
- // Check if the PointerCoords are consistent with the other computations
}
TEST_F(ResamplerTest, SinglePointerMultipleSampleInterpolation) {
@@ -364,7 +360,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
{Pointer{.id = 0,
@@ -382,7 +378,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+ mResampler->resampleMotionEvent(16ms, motionEvent, nullptr);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
{Pointer{.id = 0,
@@ -400,7 +396,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, nullptr);
+ mResampler->resampleMotionEvent(16ms, motionEvent, nullptr);
assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
}
@@ -414,7 +410,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(27ms, motionEvent, nullptr);
+ mResampler->resampleMotionEvent(32ms, motionEvent, nullptr);
assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
}
@@ -428,7 +424,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(43ms, motionEvent, nullptr);
+ mResampler->resampleMotionEvent(48ms, motionEvent, nullptr);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
{Pointer{.id = 0,
@@ -451,7 +447,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
{Pointer{.x = 2.2f, .y = 2.2f, .isResampled = true},
@@ -475,7 +471,7 @@
const MotionEvent originalMotionEvent = secondMotionEvent;
- mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, secondMotionEvent,
{Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
@@ -498,7 +494,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
{Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
@@ -517,7 +513,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr);
+ mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
{Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
@@ -539,7 +535,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
assertMotionEventIsResampledAndCoordsNear(originalMotionEvent, motionEvent,
{Pointer{.x = 1.4f, .y = 1.4f, .isResampled = true},
@@ -560,7 +556,7 @@
const MotionEvent originalSecondMotionEvent = secondMotionEvent;
- mResampler->resampleMotionEvent(27ms, secondMotionEvent, &secondFutureSample);
+ mResampler->resampleMotionEvent(32ms, secondMotionEvent, &secondFutureSample);
assertMotionEventIsResampledAndCoordsNear(originalSecondMotionEvent, secondMotionEvent,
{Pointer{.x = 3.8f, .y = 3.8f, .isResampled = true},
@@ -586,7 +582,7 @@
const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
- mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
}
@@ -606,7 +602,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
}
@@ -629,7 +625,7 @@
const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
- mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
assertMotionEventIsResampledAndCoordsNear(secondOriginalMotionEvent, secondMotionEvent,
{Pointer{.x = 3.4f, .y = 3.4f, .isResampled = true},
@@ -650,7 +646,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
}
@@ -672,7 +668,7 @@
const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
- mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
}
@@ -691,7 +687,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
}
@@ -713,7 +709,7 @@
const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
- mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
}
@@ -746,7 +742,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, &futureSample);
+ mResampler->resampleMotionEvent(16ms, motionEvent, &futureSample);
assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
}
@@ -782,7 +778,7 @@
const MotionEvent secondOriginalMotionEvent = secondMotionEvent;
- mResampler->resampleMotionEvent(11ms, secondMotionEvent, /*futureSample=*/nullptr);
+ mResampler->resampleMotionEvent(16ms, secondMotionEvent, /*futureSample=*/nullptr);
assertMotionEventIsNotResampled(secondOriginalMotionEvent, secondMotionEvent);
}
@@ -815,7 +811,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr);
+ mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr);
assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
}
@@ -847,7 +843,7 @@
const MotionEvent originalMotionEvent = motionEvent;
- mResampler->resampleMotionEvent(11ms, motionEvent, /*futureSample=*/nullptr);
+ mResampler->resampleMotionEvent(16ms, motionEvent, /*futureSample=*/nullptr);
assertMotionEventIsNotResampled(originalMotionEvent, motionEvent);
}
diff --git a/libs/input/tests/TestEventMatchers.h b/libs/input/tests/TestEventMatchers.h
new file mode 100644
index 0000000..3589de5
--- /dev/null
+++ b/libs/input/tests/TestEventMatchers.h
@@ -0,0 +1,187 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <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;
+ explicit WithDeviceIdMatcher(DeviceId deviceId) : mDeviceId(deviceId) {}
+
+ bool MatchAndExplain(const InputEvent& event, std::ostream*) const {
+ return mDeviceId == event.getDeviceId();
+ }
+
+ void DescribeTo(std::ostream* os) const { *os << "with device id " << mDeviceId; }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong device id"; }
+
+private:
+ const DeviceId mDeviceId;
+};
+
+inline WithDeviceIdMatcher WithDeviceId(int32_t deviceId) {
+ return WithDeviceIdMatcher(deviceId);
+}
+
+class WithMotionActionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithMotionActionMatcher(int32_t action) : mAction(action) {}
+
+ bool MatchAndExplain(const MotionEvent& event, std::ostream*) const {
+ bool matches = mAction == event.getAction();
+ if (event.getAction() == AMOTION_EVENT_ACTION_CANCEL) {
+ matches &= (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) != 0;
+ }
+ return matches;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with motion action " << MotionEvent::actionToString(mAction);
+ if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+ *os << " and FLAG_CANCELED";
+ }
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong action"; }
+
+private:
+ const int32_t mAction;
+};
+
+inline WithMotionActionMatcher WithMotionAction(int32_t action) {
+ return WithMotionActionMatcher(action);
+}
+
+class WithSampleCountMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithSampleCountMatcher(size_t sampleCount) : mExpectedSampleCount{sampleCount} {}
+
+ bool MatchAndExplain(const MotionEvent& motionEvent, std::ostream*) const {
+ 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;
+ }
+
+ 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.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 << "motion event sample properties match."; }
+
+ 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 WithSampleMatcher WithSample(size_t sampleIndex, const Sample& sample) {
+ return WithSampleMatcher(sampleIndex, sample);
+}
+
+} // namespace android
diff --git a/libs/input/tests/TestInputChannel.cpp b/libs/input/tests/TestInputChannel.cpp
new file mode 100644
index 0000000..26a0ca2
--- /dev/null
+++ b/libs/input/tests/TestInputChannel.cpp
@@ -0,0 +1,102 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TestInputChannel"
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#include <TestInputChannel.h>
+
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <array>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace {
+
+/**
+ * Returns a stub file descriptor by opening a socket pair and closing one of the fds. The returned
+ * fd can be used to construct an InputChannel.
+ */
+base::unique_fd generateFileDescriptor() {
+ std::array<int, 2> kFileDescriptors;
+ LOG_IF(FATAL, ::socketpair(AF_UNIX, SOCK_SEQPACKET, 0, kFileDescriptors.data()) != 0)
+ << "TestInputChannel. Failed to create socket pair.";
+ LOG_IF(FATAL, ::close(kFileDescriptors[1]) != 0)
+ << "TestInputChannel. Failed to close file descriptor.";
+ return base::unique_fd{kFileDescriptors[0]};
+}
+} // namespace
+
+// --- TestInputChannel ---
+
+TestInputChannel::TestInputChannel(const std::string& name)
+ : InputChannel{name, generateFileDescriptor(), sp<BBinder>::make()} {}
+
+void TestInputChannel::enqueueMessage(const InputMessage& message) {
+ mReceivedMessages.push(message);
+}
+
+status_t TestInputChannel::sendMessage(const InputMessage* message) {
+ LOG_IF(FATAL, message == nullptr)
+ << "TestInputChannel " << getName() << ". No message was passed to sendMessage.";
+
+ mSentMessages.push(*message);
+ return OK;
+}
+
+base::Result<InputMessage> TestInputChannel::receiveMessage() {
+ if (mReceivedMessages.empty()) {
+ return base::Error(WOULD_BLOCK);
+ }
+ InputMessage message = mReceivedMessages.front();
+ mReceivedMessages.pop();
+ return message;
+}
+
+bool TestInputChannel::probablyHasInput() const {
+ return !mReceivedMessages.empty();
+}
+
+void TestInputChannel::assertFinishMessage(uint32_t seq, bool handled) {
+ ASSERT_FALSE(mSentMessages.empty())
+ << "TestInputChannel " << getName() << ". Cannot assert. mSentMessages is empty.";
+
+ const InputMessage& finishMessage = mSentMessages.front();
+
+ EXPECT_EQ(finishMessage.header.seq, seq)
+ << "TestInputChannel " << getName()
+ << ". Sequence mismatch. Message seq: " << finishMessage.header.seq
+ << " Expected seq: " << seq;
+
+ EXPECT_EQ(finishMessage.body.finished.handled, handled)
+ << "TestInputChannel " << getName()
+ << ". Handled value mismatch. Message val: " << std::boolalpha
+ << finishMessage.body.finished.handled << "Expected val: " << handled
+ << std::noboolalpha;
+ mSentMessages.pop();
+}
+
+void TestInputChannel::assertNoSentMessages() const {
+ ASSERT_TRUE(mSentMessages.empty());
+}
+} // namespace android
\ No newline at end of file
diff --git a/libs/input/tests/TestInputChannel.h b/libs/input/tests/TestInputChannel.h
new file mode 100644
index 0000000..43253ec
--- /dev/null
+++ b/libs/input/tests/TestInputChannel.h
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <queue>
+#include <string>
+
+#include <android-base/result.h>
+#include <gtest/gtest.h>
+#include <input/InputTransport.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class TestInputChannel final : public InputChannel {
+public:
+ explicit TestInputChannel(const std::string& name);
+
+ /**
+ * Enqueues a message in mReceivedMessages.
+ */
+ void enqueueMessage(const InputMessage& message);
+
+ /**
+ * Pushes message to mSentMessages. In the default implementation, InputChannel sends messages
+ * through a file descriptor. TestInputChannel, on the contrary, stores sent messages in
+ * mSentMessages for assertion reasons.
+ */
+ status_t sendMessage(const InputMessage* message) override;
+
+ /**
+ * Returns an InputMessage from mReceivedMessages. This is done instead of retrieving data
+ * directly from fd.
+ */
+ base::Result<InputMessage> receiveMessage() override;
+
+ /**
+ * Returns if mReceivedMessages is not empty.
+ */
+ bool probablyHasInput() const override;
+
+ void assertFinishMessage(uint32_t seq, bool handled);
+
+ void assertNoSentMessages() const;
+
+private:
+ // InputMessages received by the endpoint.
+ std::queue<InputMessage> mReceivedMessages;
+ // InputMessages sent by the endpoint.
+ std::queue<InputMessage> mSentMessages;
+};
+} // namespace android
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index e3be3bc..d0ca78e 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -129,7 +129,7 @@
std::vector<DisplayConfigImpl> modesPerDisplay[size];
ui::DisplayConnectionType displayConnectionTypes[size];
int numModes = 0;
- for (int i = 0; i < size; ++i) {
+ for (size_t i = 0; i < size; ++i) {
ui::StaticDisplayInfo staticInfo;
if (const status_t status =
SurfaceComposerClient::getStaticDisplayInfo(ids[i].value, &staticInfo);
@@ -151,7 +151,7 @@
numModes += modes.size();
modesPerDisplay[i].reserve(modes.size());
- for (int j = 0; j < modes.size(); ++j) {
+ for (size_t j = 0; j < modes.size(); ++j) {
const ui::DisplayMode& mode = modes[j];
modesPerDisplay[i].emplace_back(
DisplayConfigImpl{static_cast<size_t>(mode.id), mode.resolution.getWidth(),
@@ -224,7 +224,7 @@
CHECK_NOT_NULL(display);
DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
float maxFps = 0.0;
- for (int i = 0; i < impl->numConfigs; ++i) {
+ for (size_t i = 0; i < impl->numConfigs; ++i) {
maxFps = std::max(maxFps, impl->configs[i].fps);
}
return maxFps;
@@ -261,7 +261,7 @@
for (size_t i = 0; i < impl->numConfigs; i++) {
auto* config = impl->configs + i;
- if (config->id == info.activeDisplayModeId) {
+ if (info.activeDisplayModeId >= 0 && config->id == (size_t)info.activeDisplayModeId) {
*outConfig = reinterpret_cast<ADisplayConfig*>(config);
return OK;
}
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 8558074..a8a86ba 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -67,9 +67,6 @@
// Android O
first_version: "26",
- export_header_libs: [
- "libnativewindow_ndk_headers",
- ],
}
cc_library {
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
index 97740db..c572ee7 100644
--- a/libs/nativewindow/rust/Android.bp
+++ b/libs/nativewindow/rust/Android.bp
@@ -26,9 +26,12 @@
source_stem: "bindings",
bindgen_flags: [
"--constified-enum-module=AHardwareBuffer_Format",
+ "--bitfield-enum=ADataSpace",
"--bitfield-enum=AHardwareBuffer_UsageFlags",
"--allowlist-file=.*/nativewindow/include/.*\\.h",
+ "--allowlist-file=.*/include/cutils/.*\\.h",
+ "--allowlist-file=.*/include_outside_system/cutils/.*\\.h",
"--blocklist-type",
"AParcel",
"--raw-line",
@@ -39,6 +42,7 @@
],
shared_libs: [
"libbinder_ndk",
+ "libcutils",
"libnativewindow",
],
rustlibs: [
@@ -66,6 +70,7 @@
srcs: [":libnativewindow_bindgen_internal"],
shared_libs: [
"libbinder_ndk",
+ "libcutils",
"libnativewindow",
],
rustlibs: [
@@ -106,6 +111,7 @@
srcs: ["src/lib.rs"],
rustlibs: [
"libbinder_rs",
+ "libbitflags",
"libnativewindow_bindgen",
],
}
diff --git a/libs/nativewindow/rust/src/handle.rs b/libs/nativewindow/rust/src/handle.rs
new file mode 100644
index 0000000..c41ab8d
--- /dev/null
+++ b/libs/nativewindow/rust/src/handle.rs
@@ -0,0 +1,243 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::{
+ ffi::c_int,
+ mem::forget,
+ os::fd::{BorrowedFd, FromRawFd, IntoRawFd, OwnedFd},
+ ptr::NonNull,
+};
+
+/// Rust wrapper around `native_handle_t`.
+///
+/// This owns the `native_handle_t` and its file descriptors, and will close them and free it when
+/// it is dropped.
+#[derive(Debug)]
+pub struct NativeHandle(NonNull<ffi::native_handle_t>);
+
+impl NativeHandle {
+ /// Creates a new `NativeHandle` with the given file descriptors and integer values.
+ ///
+ /// The `NativeHandle` will take ownership of the file descriptors and close them when it is
+ /// dropped.
+ pub fn new(fds: Vec<OwnedFd>, ints: &[c_int]) -> Option<Self> {
+ let fd_count = fds.len();
+ // SAFETY: native_handle_create doesn't have any safety requirements.
+ let handle = unsafe {
+ ffi::native_handle_create(fd_count.try_into().unwrap(), ints.len().try_into().unwrap())
+ };
+ let handle = NonNull::new(handle)?;
+ for (i, fd) in fds.into_iter().enumerate() {
+ // SAFETY: `handle` must be valid because it was just created, and the array offset is
+ // within the bounds of what we allocated above.
+ unsafe {
+ *(*handle.as_ptr()).data.as_mut_ptr().add(i) = fd.into_raw_fd();
+ }
+ }
+ for (i, value) in ints.iter().enumerate() {
+ // SAFETY: `handle` must be valid because it was just created, and the array offset is
+ // within the bounds of what we allocated above. Note that `data` is uninitialized
+ // until after this so we can't use `slice::from_raw_parts_mut` or similar to create a
+ // reference to it so we use raw pointers arithmetic instead.
+ unsafe {
+ *(*handle.as_ptr()).data.as_mut_ptr().add(fd_count + i) = *value;
+ }
+ }
+ // SAFETY: `handle` must be valid because it was just created.
+ unsafe {
+ ffi::native_handle_set_fdsan_tag(handle.as_ptr());
+ }
+ Some(Self(handle))
+ }
+
+ /// Returns a borrowed view of all the file descriptors in this native handle.
+ pub fn fds(&self) -> Vec<BorrowedFd> {
+ self.data()[..self.fd_count()]
+ .iter()
+ .map(|fd| {
+ // SAFETY: The `native_handle_t` maintains ownership of the file descriptor so it
+ // won't be closed until this `NativeHandle` is destroyed. The `BorrowedFd` will
+ // have a lifetime constrained to that of `&self`, so it can't outlive it.
+ unsafe { BorrowedFd::borrow_raw(*fd) }
+ })
+ .collect()
+ }
+
+ /// Returns the integer values in this native handle.
+ pub fn ints(&self) -> &[c_int] {
+ &self.data()[self.fd_count()..]
+ }
+
+ /// Destroys the `NativeHandle`, taking ownership of the file descriptors it contained.
+ pub fn into_fds(self) -> Vec<OwnedFd> {
+ let fds = self.data()[..self.fd_count()]
+ .iter()
+ .map(|fd| {
+ // SAFETY: The `native_handle_t` has ownership of the file descriptor, and
+ // after this we destroy it without closing the file descriptor so we can take over
+ // ownership of it.
+ unsafe { OwnedFd::from_raw_fd(*fd) }
+ })
+ .collect();
+
+ // SAFETY: Our wrapped `native_handle_t` pointer is always valid, and it won't be accessed
+ // after this because we own it and forget it.
+ unsafe {
+ assert_eq!(ffi::native_handle_delete(self.0.as_ptr()), 0);
+ }
+ // Don't drop self, as that would cause `native_handle_close` to be called and close the
+ // file descriptors.
+ forget(self);
+ fds
+ }
+
+ /// Returns a reference to the underlying `native_handle_t`.
+ fn as_ref(&self) -> &ffi::native_handle_t {
+ // SAFETY: All the ways of creating a `NativeHandle` ensure that the `native_handle_t` is
+ // valid and initialised, and lives as long as the `NativeHandle`. We enforce Rust's
+ // aliasing rules by giving the reference a lifetime matching that of `&self`.
+ unsafe { self.0.as_ref() }
+ }
+
+ /// Returns the number of file descriptors included in the native handle.
+ fn fd_count(&self) -> usize {
+ self.as_ref().numFds.try_into().unwrap()
+ }
+
+ /// Returns the number of integer values included in the native handle.
+ fn int_count(&self) -> usize {
+ self.as_ref().numInts.try_into().unwrap()
+ }
+
+ /// Returns a slice reference for all the used `data` field of the native handle, including both
+ /// file descriptors and integers.
+ fn data(&self) -> &[c_int] {
+ let total_count = self.fd_count() + self.int_count();
+ // SAFETY: The data must have been initialised with this number of elements when the
+ // `NativeHandle` was created.
+ unsafe { self.as_ref().data.as_slice(total_count) }
+ }
+
+ /// Wraps a raw `native_handle_t` pointer, taking ownership of it.
+ ///
+ /// # Safety
+ ///
+ /// `native_handle` must be a valid pointer to a `native_handle_t`, and must not be used
+ /// anywhere else after calling this method.
+ pub unsafe fn from_raw(native_handle: NonNull<ffi::native_handle_t>) -> Self {
+ Self(native_handle)
+ }
+
+ /// Creates a new `NativeHandle` wrapping a clone of the given `native_handle_t` pointer.
+ ///
+ /// Unlike [`from_raw`](Self::from_raw) this doesn't take ownership of the pointer passed in, so
+ /// the caller remains responsible for closing and freeing it.
+ ///
+ /// # Safety
+ ///
+ /// `native_handle` must be a valid pointer to a `native_handle_t`.
+ pub unsafe fn clone_from_raw(native_handle: NonNull<ffi::native_handle_t>) -> Option<Self> {
+ // SAFETY: The caller promised that `native_handle` was valid.
+ let cloned = unsafe { ffi::native_handle_clone(native_handle.as_ptr()) };
+ NonNull::new(cloned).map(Self)
+ }
+
+ /// Returns a raw pointer to the wrapped `native_handle_t`.
+ ///
+ /// This is only valid as long as this `NativeHandle` exists, so shouldn't be stored. It mustn't
+ /// be closed or deleted.
+ pub fn as_raw(&self) -> NonNull<ffi::native_handle_t> {
+ self.0
+ }
+
+ /// Turns the `NativeHandle` into a raw `native_handle_t`.
+ ///
+ /// The caller takes ownership of the `native_handle_t` and its file descriptors, so is
+ /// responsible for closing and freeing it.
+ pub fn into_raw(self) -> NonNull<ffi::native_handle_t> {
+ let raw = self.0;
+ forget(self);
+ raw
+ }
+}
+
+impl Clone for NativeHandle {
+ fn clone(&self) -> Self {
+ // SAFETY: Our wrapped `native_handle_t` pointer is always valid.
+ unsafe { Self::clone_from_raw(self.0) }.expect("native_handle_clone returned null")
+ }
+}
+
+impl Drop for NativeHandle {
+ fn drop(&mut self) {
+ // SAFETY: Our wrapped `native_handle_t` pointer is always valid, and it won't be accessed
+ // after this because we own it and are being dropped.
+ unsafe {
+ assert_eq!(ffi::native_handle_close(self.0.as_ptr()), 0);
+ assert_eq!(ffi::native_handle_delete(self.0.as_ptr()), 0);
+ }
+ }
+}
+
+// SAFETY: `NativeHandle` owns the `native_handle_t`, which just contains some integers and file
+// descriptors, which aren't tied to any particular thread.
+unsafe impl Send for NativeHandle {}
+
+// SAFETY: A `NativeHandle` can be used from different threads simultaneously, as is is just
+// integers and file descriptors.
+unsafe impl Sync for NativeHandle {}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::fs::File;
+
+ #[test]
+ fn create_empty() {
+ let handle = NativeHandle::new(vec![], &[]).unwrap();
+ assert_eq!(handle.fds().len(), 0);
+ assert_eq!(handle.ints(), &[]);
+ }
+
+ #[test]
+ fn create_with_ints() {
+ let handle = NativeHandle::new(vec![], &[1, 2, 42]).unwrap();
+ assert_eq!(handle.fds().len(), 0);
+ assert_eq!(handle.ints(), &[1, 2, 42]);
+ }
+
+ #[test]
+ fn create_with_fd() {
+ let file = File::open("/dev/null").unwrap();
+ let handle = NativeHandle::new(vec![file.into()], &[]).unwrap();
+ assert_eq!(handle.fds().len(), 1);
+ assert_eq!(handle.ints(), &[]);
+ }
+
+ #[test]
+ fn clone() {
+ let file = File::open("/dev/null").unwrap();
+ let original = NativeHandle::new(vec![file.into()], &[42]).unwrap();
+ assert_eq!(original.ints(), &[42]);
+ assert_eq!(original.fds().len(), 1);
+
+ let cloned = original.clone();
+ drop(original);
+
+ assert_eq!(cloned.ints(), &[42]);
+ assert_eq!(cloned.fds().len(), 1);
+
+ drop(cloned);
+ }
+}
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index dc3f51f..f19b908 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -16,10 +16,12 @@
extern crate nativewindow_bindgen as ffi;
+mod handle;
mod surface;
-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},
@@ -27,11 +29,86 @@
unstable_api::{status_result, AsNative},
StatusCode,
};
-use ffi::{AHardwareBuffer, AHardwareBuffer_readFromParcel, AHardwareBuffer_writeToParcel};
+use ffi::{
+ AHardwareBuffer, AHardwareBuffer_Desc, AHardwareBuffer_readFromParcel,
+ AHardwareBuffer_writeToParcel,
+};
use std::fmt::{self, Debug, Formatter};
use std::mem::ManuallyDrop;
use std::ptr::{self, null_mut, NonNull};
+/// Wrapper around a C `AHardwareBuffer_Desc`.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct HardwareBufferDescription(AHardwareBuffer_Desc);
+
+impl HardwareBufferDescription {
+ /// Creates a new `HardwareBufferDescription` with the given parameters.
+ pub fn new(
+ width: u32,
+ height: u32,
+ layers: u32,
+ format: AHardwareBuffer_Format::Type,
+ usage: AHardwareBuffer_UsageFlags,
+ stride: u32,
+ ) -> Self {
+ Self(AHardwareBuffer_Desc {
+ width,
+ height,
+ layers,
+ format,
+ usage: usage.0,
+ stride,
+ rfu0: 0,
+ rfu1: 0,
+ })
+ }
+
+ /// Returns the width from the buffer description.
+ pub fn width(&self) -> u32 {
+ self.0.width
+ }
+
+ /// Returns the height from the buffer description.
+ pub fn height(&self) -> u32 {
+ self.0.height
+ }
+
+ /// Returns the number from layers from the buffer description.
+ pub fn layers(&self) -> u32 {
+ self.0.layers
+ }
+
+ /// Returns the format from the buffer description.
+ pub fn format(&self) -> AHardwareBuffer_Format::Type {
+ self.0.format
+ }
+
+ /// Returns the usage bitvector from the buffer description.
+ pub fn usage(&self) -> AHardwareBuffer_UsageFlags {
+ AHardwareBuffer_UsageFlags(self.0.usage)
+ }
+
+ /// Returns the stride from the buffer description.
+ pub fn stride(&self) -> u32 {
+ self.0.stride
+ }
+}
+
+impl Default for HardwareBufferDescription {
+ fn default() -> Self {
+ Self(AHardwareBuffer_Desc {
+ width: 0,
+ height: 0,
+ layers: 0,
+ format: 0,
+ usage: 0,
+ stride: 0,
+ rfu0: 0,
+ rfu1: 0,
+ })
+ }
+}
+
/// Wrapper around an opaque C `AHardwareBuffer`.
#[derive(PartialEq, Eq)]
pub struct HardwareBuffer(NonNull<AHardwareBuffer>);
@@ -43,26 +120,9 @@
/// that the allocation of the given description will never succeed.
///
/// Available since API 29
- pub fn is_supported(
- width: u32,
- height: u32,
- layers: u32,
- format: AHardwareBuffer_Format::Type,
- usage: AHardwareBuffer_UsageFlags,
- stride: u32,
- ) -> bool {
- let buffer_desc = ffi::AHardwareBuffer_Desc {
- width,
- height,
- layers,
- format,
- usage: usage.0,
- stride,
- rfu0: 0,
- rfu1: 0,
- };
- // SAFETY: *buffer_desc will never be null.
- let status = unsafe { ffi::AHardwareBuffer_isSupported(&buffer_desc) };
+ pub fn is_supported(buffer_description: &HardwareBufferDescription) -> bool {
+ // SAFETY: The pointer comes from a reference so must be valid.
+ let status = unsafe { ffi::AHardwareBuffer_isSupported(&buffer_description.0) };
status == 1
}
@@ -74,27 +134,11 @@
///
/// Available since API level 26.
#[inline]
- pub fn new(
- width: u32,
- height: u32,
- layers: u32,
- format: AHardwareBuffer_Format::Type,
- usage: AHardwareBuffer_UsageFlags,
- ) -> Option<Self> {
- let buffer_desc = ffi::AHardwareBuffer_Desc {
- width,
- height,
- layers,
- format,
- usage: usage.0,
- stride: 0,
- rfu0: 0,
- rfu1: 0,
- };
+ pub fn new(buffer_description: &HardwareBufferDescription) -> Option<Self> {
let mut ptr = ptr::null_mut();
// SAFETY: The returned pointer is valid until we drop/deallocate it. The function may fail
// and return a status, but we check it later.
- let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_desc, &mut ptr) };
+ let status = unsafe { ffi::AHardwareBuffer_allocate(&buffer_description.0, &mut ptr) };
if status == 0 {
Some(Self(NonNull::new(ptr).expect("Allocated AHardwareBuffer was null")))
@@ -103,6 +147,50 @@
}
}
+ /// Creates a `HardwareBuffer` from a native handle.
+ ///
+ /// The native handle is cloned, so this doesn't take ownership of the original handle passed
+ /// in.
+ pub fn create_from_handle(
+ handle: &NativeHandle,
+ buffer_description: &HardwareBufferDescription,
+ ) -> Result<Self, StatusCode> {
+ let mut buffer = ptr::null_mut();
+ // SAFETY: The caller guarantees that `handle` is valid, and the buffer pointer is valid
+ // because it comes from a reference. The method we pass means that
+ // `AHardwareBuffer_createFromHandle` will clone the handle rather than taking ownership of
+ // it.
+ let status = unsafe {
+ ffi::AHardwareBuffer_createFromHandle(
+ &buffer_description.0,
+ handle.as_raw().as_ptr(),
+ ffi::CreateFromHandleMethod_AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE
+ .try_into()
+ .unwrap(),
+ &mut buffer,
+ )
+ };
+ status_result(status)?;
+ Ok(Self(NonNull::new(buffer).expect("Allocated AHardwareBuffer was null")))
+ }
+
+ /// Returns a clone of the native handle of the buffer.
+ ///
+ /// Returns `None` if the operation fails for any reason.
+ pub fn cloned_native_handle(&self) -> Option<NativeHandle> {
+ // SAFETY: The AHardwareBuffer pointer we pass is guaranteed to be non-null and valid
+ // because it must have been allocated by `AHardwareBuffer_allocate`,
+ // `AHardwareBuffer_readFromParcel` or the caller of `from_raw` and we have not yet
+ // released it.
+ let native_handle = unsafe { ffi::AHardwareBuffer_getNativeHandle(self.0.as_ptr()) };
+ NonNull::new(native_handle.cast_mut()).and_then(|native_handle| {
+ // SAFETY: `AHardwareBuffer_getNativeHandle` should have returned a valid pointer which
+ // is valid at least as long as the buffer is, and `clone_from_raw` clones it rather
+ // than taking ownership of it so the original `native_handle` isn't stored.
+ unsafe { NativeHandle::clone_from_raw(native_handle) }
+ })
+ }
+
/// Adopts the given raw pointer and wraps it in a Rust HardwareBuffer.
///
/// # Safety
@@ -155,37 +243,8 @@
out_id
}
- /// Get the width of this buffer
- pub fn width(&self) -> u32 {
- self.description().width
- }
-
- /// Get the height of this buffer
- pub fn height(&self) -> u32 {
- self.description().height
- }
-
- /// Get the number of layers of this buffer
- pub fn layers(&self) -> u32 {
- self.description().layers
- }
-
- /// Get the format of this buffer
- pub fn format(&self) -> AHardwareBuffer_Format::Type {
- self.description().format
- }
-
- /// Get the usage bitvector of this buffer
- pub fn usage(&self) -> AHardwareBuffer_UsageFlags {
- AHardwareBuffer_UsageFlags(self.description().usage)
- }
-
- /// Get the stride of this buffer
- pub fn stride(&self) -> u32 {
- self.description().stride
- }
-
- fn description(&self) -> ffi::AHardwareBuffer_Desc {
+ /// Returns the description of this buffer.
+ pub fn description(&self) -> HardwareBufferDescription {
let mut buffer_desc = ffi::AHardwareBuffer_Desc {
width: 0,
height: 0,
@@ -198,7 +257,7 @@
};
// SAFETY: neither the buffer nor AHardwareBuffer_Desc pointers will be null.
unsafe { ffi::AHardwareBuffer_describe(self.0.as_ref(), &mut buffer_desc) };
- buffer_desc
+ HardwareBufferDescription(buffer_desc)
}
}
@@ -281,19 +340,27 @@
#[test]
fn create_valid_buffer_returns_ok() {
- let buffer = HardwareBuffer::new(
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
512,
512,
1,
AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
- );
+ 0,
+ ));
assert!(buffer.is_some());
}
#[test]
fn create_invalid_buffer_returns_err() {
- let buffer = HardwareBuffer::new(512, 512, 1, 0, AHardwareBuffer_UsageFlags(0));
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
+ 512,
+ 512,
+ 1,
+ 0,
+ AHardwareBuffer_UsageFlags(0),
+ 0,
+ ));
assert!(buffer.is_none());
}
@@ -319,39 +386,45 @@
// SAFETY: The pointer must be valid because it was just allocated successfully, and we
// don't use it after calling this.
let buffer = unsafe { HardwareBuffer::from_raw(NonNull::new(raw_buffer_ptr).unwrap()) };
- assert_eq!(buffer.width(), 1024);
+ assert_eq!(buffer.description().width(), 1024);
}
#[test]
fn basic_getters() {
- let buffer = HardwareBuffer::new(
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
1024,
512,
1,
AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
- )
+ 0,
+ ))
.expect("Buffer with some basic parameters was not created successfully");
- assert_eq!(buffer.width(), 1024);
- assert_eq!(buffer.height(), 512);
- assert_eq!(buffer.layers(), 1);
- assert_eq!(buffer.format(), AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM);
+ let description = buffer.description();
+ assert_eq!(description.width(), 1024);
+ assert_eq!(description.height(), 512);
+ assert_eq!(description.layers(), 1);
assert_eq!(
- buffer.usage(),
+ description.format(),
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM
+ );
+ assert_eq!(
+ description.usage(),
AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN
);
}
#[test]
fn id_getter() {
- let buffer = HardwareBuffer::new(
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
1024,
512,
1,
AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
- )
+ 0,
+ ))
.expect("Buffer with some basic parameters was not created successfully");
assert_ne!(0, buffer.id());
@@ -359,13 +432,14 @@
#[test]
fn clone() {
- let buffer = HardwareBuffer::new(
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
1024,
512,
1,
AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
- )
+ 0,
+ ))
.expect("Buffer with some basic parameters was not created successfully");
let buffer2 = buffer.clone();
@@ -374,13 +448,14 @@
#[test]
fn into_raw() {
- let buffer = HardwareBuffer::new(
+ let buffer = HardwareBuffer::new(&HardwareBufferDescription::new(
1024,
512,
1,
AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
- )
+ 0,
+ ))
.expect("Buffer with some basic parameters was not created successfully");
let buffer2 = buffer.clone();
@@ -390,4 +465,26 @@
assert_eq!(remade_buffer, buffer2);
}
+
+ #[test]
+ fn native_handle_and_back() {
+ let buffer_description = HardwareBufferDescription::new(
+ 1024,
+ 512,
+ 1,
+ AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
+ AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ 1024,
+ );
+ let buffer = HardwareBuffer::new(&buffer_description)
+ .expect("Buffer with some basic parameters was not created successfully");
+
+ let native_handle =
+ buffer.cloned_native_handle().expect("Failed to get native handle for buffer");
+ let buffer2 = HardwareBuffer::create_from_handle(&native_handle, &buffer_description)
+ .expect("Failed to create buffer from native handle");
+
+ assert_eq!(buffer.description(), buffer_description);
+ assert_eq!(buffer2.description(), buffer_description);
+ }
}
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/nativewindow/rust/sys/nativewindow_bindings.h b/libs/nativewindow/rust/sys/nativewindow_bindings.h
index 5689f7d..5046a80 100644
--- a/libs/nativewindow/rust/sys/nativewindow_bindings.h
+++ b/libs/nativewindow/rust/sys/nativewindow_bindings.h
@@ -20,3 +20,5 @@
#include <android/hdr_metadata.h>
#include <android/native_window.h>
#include <android/native_window_aidl.h>
+#include <cutils/native_handle.h>
+#include <vndk/hardware_buffer.h>
diff --git a/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs
index 876f6c8..73a7e95 100644
--- a/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs
+++ b/libs/nativewindow/tests/benchmark/buffer_benchmarks.rs
@@ -22,13 +22,14 @@
#[inline]
fn create_720p_buffer() -> HardwareBuffer {
- HardwareBuffer::new(
+ HardwareBuffer::new(&HardwareBufferDescription::new(
1280,
720,
1,
AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
- )
+ 0,
+ ))
.unwrap()
}
@@ -51,7 +52,7 @@
// underlying call to AHardwareBuffer_describe.
c.bench_with_input(BenchmarkId::new("desc", "buffer"), &buffer, |b, buffer| {
b.iter(|| {
- buffer.width();
+ buffer.description().width();
})
});
}
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 7639fab..d248ea0 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -100,6 +100,7 @@
"skia/debug/SkiaCapture.cpp",
"skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
+ "skia/filters/GainmapFactory.cpp",
"skia/filters/GaussianBlurFilter.cpp",
"skia/filters/KawaseBlurDualFilter.cpp",
"skia/filters/KawaseBlurFilter.cpp",
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/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index bc3976d..907590a 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -21,6 +21,7 @@
#include "skia/GraphiteVkRenderEngine.h"
#include "skia/SkiaGLRenderEngine.h"
#include "threaded/RenderEngineThreaded.h"
+#include "ui/GraphicTypes.h"
#include <com_android_graphics_surfaceflinger_flags.h>
#include <cutils/properties.h>
@@ -101,17 +102,34 @@
base::unique_fd&& bufferFence) {
const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
std::future<FenceResult> resultFuture = resultPromise->get_future();
- updateProtectedContext(layers, buffer);
+ updateProtectedContext(layers, {buffer.get()});
drawLayersInternal(std::move(resultPromise), display, layers, buffer, std::move(bufferFence));
return resultFuture;
}
+ftl::Future<FenceResult> RenderEngine::drawGainmap(
+ const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) {
+ const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
+ std::future<FenceResult> resultFuture = resultPromise->get_future();
+ updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()});
+ drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr,
+ std::move(hdrFence), hdrSdrRatio, dataspace, gainmap);
+ return resultFuture;
+}
+
void RenderEngine::updateProtectedContext(const std::vector<LayerSettings>& layers,
- const std::shared_ptr<ExternalTexture>& buffer) {
+ vector<const ExternalTexture*> buffers) {
const bool needsProtectedContext =
- (buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED)) ||
- std::any_of(layers.begin(), layers.end(), [](const LayerSettings& layer) {
- const std::shared_ptr<ExternalTexture>& buffer = layer.source.buffer.buffer;
+ std::any_of(layers.begin(), layers.end(),
+ [](const LayerSettings& layer) {
+ const std::shared_ptr<ExternalTexture>& buffer =
+ layer.source.buffer.buffer;
+ return buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
+ }) ||
+ std::any_of(buffers.begin(), buffers.end(), [](const ExternalTexture* buffer) {
return buffer && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED);
});
useProtectedContext(needsProtectedContext);
diff --git a/libs/renderengine/benchmark/AndroidTest.xml b/libs/renderengine/benchmark/AndroidTest.xml
new file mode 100644
index 0000000..3b923cb
--- /dev/null
+++ b/libs/renderengine/benchmark/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for librenderengine_bench.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native-metric" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="librenderengine_bench->/data/local/tmp/librenderengine_bench" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GoogleBenchmarkTest" >
+ <option name="native-benchmark-device-path" value="/data/local/tmp" />
+ <option name="benchmark-module-name" value="librenderengine_bench" />
+ <option name="file-exclusion-filter-regex" value=".*\.config$" />
+ <option name="file-exclusion-filter-regex" value=".*/resources/.*" />
+ </test>
+</configuration>
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index 326d1ce..595573d 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -17,6 +17,7 @@
#include <RenderEngineBench.h>
#include <android-base/file.h>
#include <benchmark/benchmark.h>
+#include <com_android_graphics_libgui_flags.h>
#include <gui/SurfaceComposerClient.h>
#include <log/log.h>
#include <renderengine/ExternalTexture.h>
@@ -29,6 +30,16 @@
using namespace android;
using namespace android::renderengine;
+// To run tests:
+/**
+ * mmm frameworks/native/libs/renderengine/benchmark;\
+ * adb push $OUT/data/benchmarktest/librenderengine_bench/librenderengine_bench
+ * /data/benchmarktest/librenderengine_bench/librenderengine_bench;\
+ * adb shell /data/benchmarktest/librenderengine_bench/librenderengine_bench
+ *
+ * (64-bit devices: out directory contains benchmarktest64 instead of benchmarktest)
+ */
+
///////////////////////////////////////////////////////////////////////////////
// Helpers for calling drawLayers
///////////////////////////////////////////////////////////////////////////////
@@ -173,29 +184,67 @@
}
}
+/**
+ * Return a buffer with the image in the provided path, relative to the executable directory
+ */
+static std::shared_ptr<ExternalTexture> createTexture(RenderEngine& re, const char* relPathImg) {
+ // Initially use cpu access so we can decode into it with AImageDecoder.
+ auto [width, height] = getDisplaySize();
+ auto srcBuffer =
+ allocateBuffer(re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source");
+ std::string fileName = base::GetExecutableDirectory().append(relPathImg);
+ renderenginebench::decode(fileName.c_str(), srcBuffer->getBuffer());
+ // Now copy into GPU-only buffer for more realistic timing.
+ srcBuffer = copyBuffer(re, srcBuffer, 0, "source");
+ return srcBuffer;
+}
+
///////////////////////////////////////////////////////////////////////////////
// Benchmarks
///////////////////////////////////////////////////////////////////////////////
+constexpr char kHomescreenPath[] = "/resources/homescreen.png";
+
+/**
+ * Draw a layer with texture and no additional shaders as a baseline to evaluate a shader's impact
+ * on performance
+ */
template <class... Args>
-void BM_blur(benchmark::State& benchState, Args&&... args) {
+void BM_homescreen(benchmark::State& benchState, Args&&... args) {
auto args_tuple = std::make_tuple(std::move(args)...);
auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)),
- static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)),
- static_cast<RenderEngine::BlurAlgorithm>(std::get<2>(args_tuple)));
+ static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)));
- // Initially use cpu access so we can decode into it with AImageDecoder.
auto [width, height] = getDisplaySize();
- auto srcBuffer =
- allocateBuffer(*re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source");
- {
- std::string srcImage = base::GetExecutableDirectory();
- srcImage.append("/resources/homescreen.png");
- renderenginebench::decode(srcImage.c_str(), srcBuffer->getBuffer());
+ auto srcBuffer = createTexture(*re, kHomescreenPath);
- // Now copy into GPU-only buffer for more realistic timing.
- srcBuffer = copyBuffer(*re, srcBuffer, 0, "source");
- }
+ const FloatRect layerRect(0, 0, width, height);
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = layerRect,
+ },
+ .source =
+ PixelSource{
+ .buffer =
+ Buffer{
+ .buffer = srcBuffer,
+ },
+ },
+ .alpha = half(1.0f),
+ };
+ auto layers = std::vector<LayerSettings>{layer};
+ benchDrawLayers(*re, layers, benchState, "homescreen");
+}
+
+template <class... Args>
+void BM_homescreen_blur(benchmark::State& benchState, Args&&... args) {
+ auto args_tuple = std::make_tuple(std::move(args)...);
+ auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)),
+ static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)));
+
+ auto [width, height] = getDisplaySize();
+ auto srcBuffer = createTexture(*re, kHomescreenPath);
const FloatRect layerRect(0, 0, width, height);
LayerSettings layer{
@@ -223,14 +272,57 @@
};
auto layers = std::vector<LayerSettings>{layer, blurLayer};
- benchDrawLayers(*re, layers, benchState, "blurred");
+ benchDrawLayers(*re, layers, benchState, "homescreen_blurred");
}
-BENCHMARK_CAPTURE(BM_blur, gaussian, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL,
- RenderEngine::BlurAlgorithm::GAUSSIAN);
+template <class... Args>
+void BM_homescreen_edgeExtension(benchmark::State& benchState, Args&&... args) {
+ auto args_tuple = std::make_tuple(std::move(args)...);
+ auto re = createRenderEngine(static_cast<RenderEngine::Threaded>(std::get<0>(args_tuple)),
+ static_cast<RenderEngine::GraphicsApi>(std::get<1>(args_tuple)));
-BENCHMARK_CAPTURE(BM_blur, kawase, RenderEngine::Threaded::YES, RenderEngine::GraphicsApi::GL,
- RenderEngine::BlurAlgorithm::KAWASE);
+ auto [width, height] = getDisplaySize();
+ auto srcBuffer = createTexture(*re, kHomescreenPath);
-BENCHMARK_CAPTURE(BM_blur, kawase_dual_filter, RenderEngine::Threaded::YES,
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = FloatRect(0, 0, width, height),
+ },
+ .source =
+ PixelSource{
+ .buffer =
+ Buffer{
+ .buffer = srcBuffer,
+ // Part of the screen is not covered by the texture but
+ // will be filled in by the shader
+ .textureTransform =
+ mat4(mat3(),
+ vec3(width * 0.3f, height * 0.3f, 0.0f)),
+ },
+ },
+ .alpha = half(1.0f),
+ .edgeExtensionEffect =
+ EdgeExtensionEffect(/* left */ true,
+ /* right */ false, /* top */ true, /* bottom */ false),
+ };
+ auto layers = std::vector<LayerSettings>{layer};
+ benchDrawLayers(*re, layers, benchState, "homescreen_edge_extension");
+}
+
+BENCHMARK_CAPTURE(BM_homescreen_blur, gaussian, RenderEngine::Threaded::YES,
+ RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::GAUSSIAN);
+
+BENCHMARK_CAPTURE(BM_homescreen_blur, kawase, RenderEngine::Threaded::YES,
+ RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE);
+
+BENCHMARK_CAPTURE(BM_homescreen_blur, kawase_dual_filter, RenderEngine::Threaded::YES,
RenderEngine::GraphicsApi::GL, RenderEngine::BlurAlgorithm::KAWASE_DUAL_FILTER);
+
+BENCHMARK_CAPTURE(BM_homescreen, SkiaGLThreaded, RenderEngine::Threaded::YES,
+ RenderEngine::GraphicsApi::GL);
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS_EDGE_EXTENSION_SHADER
+BENCHMARK_CAPTURE(BM_homescreen_edgeExtension, SkiaGLThreaded, RenderEngine::Threaded::YES,
+ RenderEngine::GraphicsApi::GL);
+#endif
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index b640983..280ec19 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -102,6 +102,9 @@
Local,
};
TonemapStrategy tonemapStrategy = TonemapStrategy::Libtonemap;
+
+ // For now, meaningful primarily when the TonemappingStrategy is Local
+ float targetHdrSdrRatio = 1.f;
};
static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 9bc2c48..0fd982e 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -97,6 +97,7 @@
bool cacheImageDimmedLayers = true;
bool cacheClippedLayers = true;
bool cacheShadowLayers = true;
+ bool cacheEdgeExtension = true;
bool cachePIPImageLayers = true;
bool cacheTransparentImageDimmedLayers = true;
bool cacheClippedDimmedImageLayers = true;
@@ -208,6 +209,13 @@
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence);
+ virtual ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr,
+ base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr,
+ base::borrowed_fd&& hdrFence, float hdrSdrRatio,
+ ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap);
+
// Clean-up method that should be called on the main thread after the
// drawFence returned by drawLayers fires. This method will free up
// resources used by the most recently drawn frame. If the frame is still
@@ -285,8 +293,7 @@
// Update protectedContext mode depending on whether or not any layer has a protected buffer.
void updateProtectedContext(const std::vector<LayerSettings>&,
- const std::shared_ptr<ExternalTexture>&);
-
+ std::vector<const ExternalTexture*>);
// Attempt to switch RenderEngine into and out of protectedContext mode
virtual void useProtectedContext(bool useProtectedContext) = 0;
@@ -294,6 +301,13 @@
const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) = 0;
+
+ virtual void drawGainmapInternal(
+ const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) = 0;
};
struct RenderEngineCreationArgs {
diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index a8c242a..fb8331d 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -46,6 +46,17 @@
ftl::Future<FenceResult>(const DisplaySettings&, const std::vector<LayerSettings>&,
const std::shared_ptr<ExternalTexture>&,
base::unique_fd&&));
+ MOCK_METHOD7(drawGainmap,
+ ftl::Future<FenceResult>(const std::shared_ptr<ExternalTexture>&,
+ base::borrowed_fd&&,
+ const std::shared_ptr<ExternalTexture>&,
+ base::borrowed_fd&&, float, ui::Dataspace,
+ const std::shared_ptr<ExternalTexture>&));
+ MOCK_METHOD8(drawGainmapInternal,
+ void(const std::shared_ptr<std::promise<FenceResult>>&&,
+ const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&,
+ const std::shared_ptr<ExternalTexture>&, base::borrowed_fd&&, float,
+ ui::Dataspace, const std::shared_ptr<ExternalTexture>&));
MOCK_METHOD5(drawLayersInternal,
void(const std::shared_ptr<std::promise<FenceResult>>&&, const DisplaySettings&,
const std::vector<LayerSettings>&, const std::shared_ptr<ExternalTexture>&,
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 74daf47..a570ad0 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -16,9 +16,9 @@
#pragma once
-#include <GrDirectContext.h>
#include <SkImage.h>
#include <SkSurface.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <sys/types.h>
#include <ui/GraphicTypes.h>
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 59b0656..57041ee 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -27,6 +27,8 @@
#include "ui/Rect.h"
#include "utils/Timers.h"
+#include <com_android_graphics_libgui_flags.h>
+
namespace android::renderengine::skia {
namespace {
@@ -619,6 +621,32 @@
}
}
+static void drawEdgeExtensionLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display,
+ const std::shared_ptr<ExternalTexture>& dstTexture,
+ const std::shared_ptr<ExternalTexture>& srcTexture) {
+ const Rect& displayRect = display.physicalDisplay;
+ // Make the layer
+ LayerSettings layer{
+ // Make the layer bigger than the texture
+ .geometry = Geometry{.boundaries = FloatRect(0, 0, displayRect.width(),
+ displayRect.height())},
+ .source = PixelSource{.buffer =
+ Buffer{
+ .buffer = srcTexture,
+ .isOpaque = 1,
+ }},
+ // The type of effect does not affect the shader's uniforms, but the layer must have a
+ // valid EdgeExtensionEffect to apply the shader
+ .edgeExtensionEffect =
+ EdgeExtensionEffect(true /* left */, false, false, true /* bottom */),
+ };
+ for (float alpha : {0.5, 0.0, 1.0}) {
+ layer.alpha = alpha;
+ auto layers = std::vector<LayerSettings>{layer};
+ renderengine->drawLayers(display, layers, dstTexture, base::unique_fd());
+ }
+}
+
//
// The collection of shaders cached here were found by using perfetto to record shader compiles
// during actions that involve RenderEngine, logging the layer settings, and the shader code
@@ -761,6 +789,12 @@
// Draw layers for b/185569240.
drawClippedLayers(renderengine, display, dstTexture, texture);
}
+
+ if (com::android::graphics::libgui::flags::edge_extension_shader() &&
+ config.cacheEdgeExtension) {
+ drawEdgeExtensionLayers(renderengine, display, dstTexture, texture);
+ drawEdgeExtensionLayers(renderengine, p3Display, dstTexture, texture);
+ }
}
if (config.cachePIPImageLayers) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index af24600..4ef7d5b 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -21,16 +21,15 @@
#include "SkiaGLRenderEngine.h"
-#include "compat/SkiaGpuContext.h"
-
#include <EGL/egl.h>
#include <EGL/eglext.h>
-#include <GrContextOptions.h>
-#include <GrTypes.h>
#include <android-base/stringprintf.h>
#include <common/trace.h>
-#include <gl/GrGLInterface.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
+#include <include/gpu/ganesh/GrTypes.h>
#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
+#include <include/gpu/ganesh/gl/GrGLInterface.h>
+#include <log/log_main.h>
#include <sync/sync.h>
#include <ui/DebugUtils.h>
@@ -40,7 +39,7 @@
#include <numeric>
#include "GLExtensions.h"
-#include "log/log_main.h"
+#include "compat/SkiaGpuContext.h"
namespace android {
namespace renderengine {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index bd177e6..7651038 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -20,9 +20,10 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
-#include <GrDirectContext.h>
#include <SkSurface.h>
#include <android-base/thread_annotations.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <renderengine/ExternalTexture.h>
#include <renderengine/RenderEngine.h>
#include <sys/types.h>
@@ -32,7 +33,6 @@
#include "AutoBackendTexture.h"
#include "EGL/egl.h"
-#include "GrContextOptions.h"
#include "SkImageInfo.h"
#include "SkiaRenderEngine.h"
#include "android-base/macros.h"
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index d58f303..ec9d3ef 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -20,9 +20,6 @@
#include "SkiaRenderEngine.h"
-#include <GrBackendSemaphore.h>
-#include <GrContextOptions.h>
-#include <GrTypes.h>
#include <SkBlendMode.h>
#include <SkCanvas.h>
#include <SkColor.h>
@@ -56,6 +53,9 @@
#include <common/FlagManager.h>
#include <common/trace.h>
#include <gui/FenceMonitor.h>
+#include <include/gpu/ganesh/GrBackendSemaphore.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
+#include <include/gpu/ganesh/GrTypes.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <pthread.h>
#include <src/core/SkTraceEventCommon.h>
@@ -75,6 +75,7 @@
#include "ColorSpaces.h"
#include "compat/SkiaGpuContext.h"
#include "filters/BlurFilter.h"
+#include "filters/GainmapFactory.h"
#include "filters/GaussianBlurFilter.h"
#include "filters/KawaseBlurDualFilter.h"
#include "filters/KawaseBlurFilter.h"
@@ -238,12 +239,22 @@
static inline SkPoint3 getSkPoint3(const android::vec3& vector) {
return SkPoint3::Make(vector.x, vector.y, vector.z);
}
+
} // namespace
namespace android {
namespace renderengine {
namespace skia {
+namespace {
+void trace(sp<Fence> fence) {
+ if (SFTRACE_ENABLED()) {
+ static gui::FenceMonitor sMonitor("RE Completion");
+ sMonitor.queueFence(std::move(fence));
+ }
+}
+} // namespace
+
using base::StringAppendF;
std::future<void> SkiaRenderEngine::primeCache(PrimeCacheConfig config) {
@@ -544,13 +555,15 @@
const auto usingLocalTonemap =
parameters.display.tonemapStrategy == DisplaySettings::TonemapStrategy::Local &&
hdrType != HdrRenderType::SDR &&
- shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
-
+ shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr) &&
+ (hdrType != HdrRenderType::DISPLAY_HDR ||
+ parameters.display.targetHdrSdrRatio < parameters.layerDimmingRatio);
if (usingLocalTonemap) {
- static MouriMap kMapper;
- const float ratio =
+ const float inputRatio =
hdrType == HdrRenderType::GENERIC_HDR ? 1.0f : parameters.layerDimmingRatio;
- shader = kMapper.mouriMap(getActiveContext(), shader, ratio);
+ static MouriMap kMapper;
+ shader = kMapper.mouriMap(getActiveContext(), shader, inputRatio,
+ parameters.display.targetHdrSdrRatio);
}
// disable tonemapping if we already locally tonemapped
@@ -1187,11 +1200,48 @@
LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
+ trace(drawFence);
+ resultPromise->set_value(std::move(drawFence));
+}
- if (SFTRACE_ENABLED()) {
- static gui::FenceMonitor sMonitor("RE Completion");
- sMonitor.queueFence(drawFence);
- }
+void SkiaRenderEngine::drawGainmapInternal(
+ const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) {
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ auto context = getActiveContext();
+ auto surfaceTextureRef = getOrCreateBackendTexture(gainmap->getBuffer(), true);
+ sk_sp<SkSurface> dstSurface =
+ surfaceTextureRef->getOrCreateSurface(ui::Dataspace::V0_SRGB_LINEAR);
+
+ waitFence(context, sdrFence);
+ const auto sdrTextureRef = getOrCreateBackendTexture(sdr->getBuffer(), false);
+ const auto sdrImage = sdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType);
+ const auto sdrShader =
+ sdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}),
+ nullptr);
+ waitFence(context, hdrFence);
+ const auto hdrTextureRef = getOrCreateBackendTexture(hdr->getBuffer(), false);
+ const auto hdrImage = hdrTextureRef->makeImage(dataspace, kPremul_SkAlphaType);
+ const auto hdrShader =
+ hdrImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions({SkFilterMode::kLinear, SkMipmapMode::kNone}),
+ nullptr);
+
+ static GainmapFactory kGainmapFactory;
+ const auto gainmapShader = kGainmapFactory.createSkShader(sdrShader, hdrShader, hdrSdrRatio);
+
+ const auto canvas = dstSurface->getCanvas();
+ SkPaint paint;
+ paint.setShader(gainmapShader);
+ paint.setBlendMode(SkBlendMode::kSrc);
+ canvas->drawPaint(paint);
+
+ auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
+ trace(drawFence);
resultPromise->set_value(std::move(drawFence));
}
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index 224a1ca..b5f8898 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -18,11 +18,12 @@
#define SF_SKIARENDERENGINE_H_
#include <renderengine/RenderEngine.h>
-#include <sys/types.h>
-#include <GrBackendSemaphore.h>
-#include <SkSurface.h>
#include <android-base/thread_annotations.h>
+#include <include/core/SkImageInfo.h>
+#include <include/core/SkSurface.h>
+#include <include/gpu/ganesh/GrBackendSemaphore.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
#include <renderengine/ExternalTexture.h>
#include <renderengine/RenderEngine.h>
#include <sys/types.h>
@@ -32,8 +33,6 @@
#include <unordered_map>
#include "AutoBackendTexture.h"
-#include "GrContextOptions.h"
-#include "SkImageInfo.h"
#include "android-base/macros.h"
#include "compat/SkiaGpuContext.h"
#include "debug/SkiaCapture.h"
@@ -143,6 +142,13 @@
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence) override final;
+ void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& sdr,
+ base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr,
+ base::borrowed_fd&& hdrFence, float hdrSdrRatio,
+ ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) override final;
void dump(std::string& result) override final;
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index d89e818..677a2b6 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -24,12 +24,12 @@
#include "GaneshVkRenderEngine.h"
#include "compat/SkiaGpuContext.h"
-#include <GrBackendSemaphore.h>
-#include <GrContextOptions.h>
-#include <GrDirectContext.h>
+#include <include/gpu/ganesh/GrBackendSemaphore.h>
+#include <include/gpu/ganesh/GrContextOptions.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
-#include <vk/GrVkTypes.h>
+#include <include/gpu/ganesh/vk/GrVkTypes.h>
#include <android-base/stringprintf.h>
#include <common/trace.h>
diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
index 3fbc6ca..88282e7 100644
--- a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
+++ b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
@@ -21,12 +21,12 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <include/core/SkImage.h>
-#include <include/gpu/GrDirectContext.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <include/gpu/ganesh/SkImageGanesh.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
#include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
-#include <include/gpu/vk/GrVkTypes.h>
+#include <include/gpu/ganesh/vk/GrVkTypes.h>
#include "skia/ColorSpaces.h"
#include "skia/compat/SkiaBackendTexture.h"
diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.h b/libs/renderengine/skia/compat/GaneshBackendTexture.h
index 5cf8647..4337df1 100644
--- a/libs/renderengine/skia/compat/GaneshBackendTexture.h
+++ b/libs/renderengine/skia/compat/GaneshBackendTexture.h
@@ -21,7 +21,7 @@
#include <include/android/GrAHardwareBufferUtils.h>
#include <include/core/SkColorSpace.h>
-#include <include/gpu/GrDirectContext.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <android-base/macros.h>
diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
index b121fe8..931f843 100644
--- a/libs/renderengine/skia/compat/GaneshGpuContext.cpp
+++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
@@ -19,12 +19,12 @@
#include <include/core/SkImageInfo.h>
#include <include/core/SkSurface.h>
#include <include/core/SkTraceMemoryDump.h>
-#include <include/gpu/GrDirectContext.h>
-#include <include/gpu/GrTypes.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/GrTypes.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
+#include <include/gpu/ganesh/gl/GrGLInterface.h>
#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
-#include <include/gpu/gl/GrGLInterface.h>
#include <include/gpu/vk/VulkanBackendContext.h>
#include "../AutoBackendTexture.h"
diff --git a/libs/renderengine/skia/compat/SkiaBackendTexture.h b/libs/renderengine/skia/compat/SkiaBackendTexture.h
index 09877a5..fa12624 100644
--- a/libs/renderengine/skia/compat/SkiaBackendTexture.h
+++ b/libs/renderengine/skia/compat/SkiaBackendTexture.h
@@ -18,7 +18,7 @@
#include <include/android/GrAHardwareBufferUtils.h>
#include <include/core/SkColorSpace.h>
-#include <include/gpu/GrDirectContext.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
#include <android/hardware_buffer.h>
#include <ui/GraphicTypes.h>
diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h
index 9fa6fb8..0bd8283 100644
--- a/libs/renderengine/skia/compat/SkiaGpuContext.h
+++ b/libs/renderengine/skia/compat/SkiaGpuContext.h
@@ -20,10 +20,10 @@
#define LOG_TAG "RenderEngine"
#include <include/core/SkSurface.h>
-#include <include/gpu/GrDirectContext.h>
-#include <include/gpu/gl/GrGLInterface.h>
+#include <include/gpu/ganesh/GrDirectContext.h>
+#include <include/gpu/ganesh/gl/GrGLInterface.h>
#include <include/gpu/graphite/Context.h>
-#include "include/gpu/vk/VulkanBackendContext.h"
+#include <include/gpu/vk/VulkanBackendContext.h>
#include "SkiaBackendTexture.h"
diff --git a/libs/renderengine/skia/filters/GainmapFactory.cpp b/libs/renderengine/skia/filters/GainmapFactory.cpp
new file mode 100644
index 0000000..e4d4fe9
--- /dev/null
+++ b/libs/renderengine/skia/filters/GainmapFactory.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GainmapFactory.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+namespace {
+
+sk_sp<SkRuntimeEffect> makeEffect(const SkString& sksl) {
+ auto [effect, error] = SkRuntimeEffect::MakeForShader(sksl);
+ LOG_ALWAYS_FATAL_IF(!effect, "RuntimeShader error: %s", error.c_str());
+ return effect;
+}
+
+// Please refer to https://developer.android.com/media/platform/hdr-image-format#gain_map-generation
+static const SkString kGainmapShader = SkString(R"(
+ uniform shader sdr;
+ uniform shader hdr;
+ uniform float mapMaxLog2;
+
+ const float mapMinLog2 = 0.0;
+ const float mapGamma = 1.0;
+ const float offsetSdr = 0.015625;
+ const float offsetHdr = 0.015625;
+
+ float luminance(vec3 linearColor) {
+ return 0.2126 * linearColor.r + 0.7152 * linearColor.g + 0.0722 * linearColor.b;
+ }
+
+ vec4 main(vec2 xy) {
+ float sdrY = luminance(toLinearSrgb(sdr.eval(xy).rgb));
+ float hdrY = luminance(toLinearSrgb(hdr.eval(xy).rgb));
+ float pixelGain = (hdrY + offsetHdr) / (sdrY + offsetSdr);
+ float logRecovery = (log2(pixelGain) - mapMinLog2) / (mapMaxLog2 - mapMinLog2);
+ return vec4(pow(clamp(logRecovery, 0.0, 1.0), mapGamma));
+ }
+)");
+} // namespace
+
+const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
+
+GainmapFactory::GainmapFactory() : mEffect(makeEffect(kGainmapShader)) {}
+
+sk_sp<SkShader> GainmapFactory::createSkShader(const sk_sp<SkShader>& sdr,
+ const sk_sp<SkShader>& hdr, float hdrSdrRatio) {
+ SkRuntimeShaderBuilder shaderBuilder(mEffect);
+ shaderBuilder.child("sdr") = sdr;
+ shaderBuilder.child("hdr") = hdr;
+ shaderBuilder.uniform("mapMaxLog2") = std::log2(hdrSdrRatio);
+ return shaderBuilder.makeShader();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/GainmapFactory.h b/libs/renderengine/skia/filters/GainmapFactory.h
new file mode 100644
index 0000000..7aea5e2
--- /dev/null
+++ b/libs/renderengine/skia/filters/GainmapFactory.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * Generates a shader for computing a gainmap, given an SDR base image and its idealized HDR
+ * rendition. The shader follows the procedure in the UltraHDR spec:
+ * https://developer.android.com/media/platform/hdr-image-format#gain_map-generation, but makes some
+ * simplifying assumptions about metadata typical for RenderEngine's usage.
+ */
+class GainmapFactory {
+public:
+ GainmapFactory();
+ // Generates the gainmap shader. The hdrSdrRatio is the max_content_boost in the UltraHDR
+ // specification.
+ sk_sp<SkShader> createSkShader(const sk_sp<SkShader>& sdr, const sk_sp<SkShader>& hdr,
+ float hdrSdrRatio);
+
+private:
+ sk_sp<SkRuntimeEffect> mEffect;
+};
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp
index b458939..b099bcf 100644
--- a/libs/renderengine/skia/filters/MouriMap.cpp
+++ b/libs/renderengine/skia/filters/MouriMap.cpp
@@ -67,7 +67,7 @@
float result = 0.0;
for (int y = -2; y <= 2; y++) {
for (int x = -2; x <= 2; x++) {
- result += C[y + 2] * C[x + 2] * bitmap.eval(xy + vec2(x, y)).r;
+ result += C[y + 2] * C[x + 2] * bitmap.eval(xy + vec2(x, y)).r;
}
}
return float4(float3(exp2(result)), 1.0);
@@ -78,18 +78,20 @@
uniform shader lux;
uniform float scaleFactor;
uniform float hdrSdrRatio;
+ uniform float targetHdrSdrRatio;
vec4 main(vec2 xy) {
float localMax = lux.eval(xy * scaleFactor).r;
float4 rgba = image.eval(xy);
float3 linear = toLinearSrgb(rgba.rgb) * hdrSdrRatio;
- if (localMax <= 1.0) {
+ if (localMax <= targetHdrSdrRatio) {
return float4(fromLinearSrgb(linear), rgba.a);
}
float maxRGB = max(linear.r, max(linear.g, linear.b));
localMax = max(localMax, maxRGB);
- float gain = (1 + maxRGB / (localMax * localMax)) / (1 + maxRGB);
+ float gain = (1 + maxRGB * (targetHdrSdrRatio / (localMax * localMax)))
+ / (1 + maxRGB / targetHdrSdrRatio);
return float4(fromLinearSrgb(linear * gain), rgba.a);
}
)");
@@ -114,10 +116,10 @@
mTonemap(makeEffect(kTonemap)) {}
sk_sp<SkShader> MouriMap::mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input,
- float hdrSdrRatio) {
+ float hdrSdrRatio, float targetHdrSdrRatio) {
auto downchunked = downchunk(context, input, hdrSdrRatio);
auto localLux = blur(context, downchunked.get());
- return tonemap(input, localLux.get(), hdrSdrRatio);
+ return tonemap(input, localLux.get(), hdrSdrRatio, targetHdrSdrRatio);
}
sk_sp<SkImage> MouriMap::downchunk(SkiaGpuContext* context, sk_sp<SkShader> input,
@@ -166,8 +168,8 @@
LOG_ALWAYS_FATAL_IF(!blurSurface, "%s: Failed to create surface!", __func__);
return makeImage(blurSurface.get(), blurBuilder);
}
-sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux,
- float hdrSdrRatio) const {
+sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio,
+ float targetHdrSdrRatio) const {
static constexpr float kScaleFactor = 1.0f / 128.0f;
SkRuntimeShaderBuilder tonemapBuilder(mTonemap);
tonemapBuilder.child("image") = input;
@@ -176,8 +178,9 @@
SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone));
tonemapBuilder.uniform("scaleFactor") = kScaleFactor;
tonemapBuilder.uniform("hdrSdrRatio") = hdrSdrRatio;
+ tonemapBuilder.uniform("targetHdrSdrRatio") = targetHdrSdrRatio;
return tonemapBuilder.makeShader();
}
} // namespace skia
} // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/skia/filters/MouriMap.h b/libs/renderengine/skia/filters/MouriMap.h
index 3c0df8a..9ba2b6f 100644
--- a/libs/renderengine/skia/filters/MouriMap.h
+++ b/libs/renderengine/skia/filters/MouriMap.h
@@ -64,13 +64,16 @@
// Apply the MouriMap tonemmaping operator to the input.
// The HDR/SDR ratio describes the luminace range of the input. 1.0 means SDR. Anything larger
// then 1.0 means that there is headroom above the SDR region.
- sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float hdrSdrRatio);
+ // Similarly, the target HDR/SDR ratio describes the luminance range of the output.
+ sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float inputHdrSdrRatio,
+ float targetHdrSdrRatio);
private:
sk_sp<SkImage> downchunk(SkiaGpuContext* context, sk_sp<SkShader> input,
float hdrSdrRatio) const;
sk_sp<SkImage> blur(SkiaGpuContext* context, SkImage* input) const;
- sk_sp<SkShader> tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio) const;
+ sk_sp<SkShader> tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio,
+ float targetHdrSdrRatio) const;
const sk_sp<SkRuntimeEffect> mCrosstalkAndChunk16x16;
const sk_sp<SkRuntimeEffect> mChunk8x8;
const sk_sp<SkRuntimeEffect> mBlur;
@@ -78,4 +81,4 @@
};
} // namespace skia
} // namespace renderengine
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index f5a90fd..c187f93 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -249,6 +249,16 @@
return;
}
+void RenderEngineThreaded::drawGainmapInternal(
+ const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) {
+ resultPromise->set_value(Fence::NO_FENCE);
+ return;
+}
+
ftl::Future<FenceResult> RenderEngineThreaded::drawLayers(
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer, base::unique_fd&& bufferFence) {
@@ -262,7 +272,7 @@
mFunctionCalls.push(
[resultPromise, display, layers, buffer, fd](renderengine::RenderEngine& instance) {
SFTRACE_NAME("REThreaded::drawLayers");
- instance.updateProtectedContext(layers, buffer);
+ instance.updateProtectedContext(layers, {buffer.get()});
instance.drawLayersInternal(std::move(resultPromise), display, layers, buffer,
base::unique_fd(fd));
});
@@ -271,6 +281,30 @@
return resultFuture;
}
+ftl::Future<FenceResult> RenderEngineThreaded::drawGainmap(
+ const std::shared_ptr<ExternalTexture>& sdr, base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr, base::borrowed_fd&& hdrFence,
+ float hdrSdrRatio, ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) {
+ SFTRACE_CALL();
+ const auto resultPromise = std::make_shared<std::promise<FenceResult>>();
+ std::future<FenceResult> resultFuture = resultPromise->get_future();
+ {
+ std::lock_guard lock(mThreadMutex);
+ mNeedsPostRenderCleanup = true;
+ mFunctionCalls.push([resultPromise, sdr, sdrFence = std::move(sdrFence), hdr,
+ hdrFence = std::move(hdrFence), hdrSdrRatio, dataspace,
+ gainmap](renderengine::RenderEngine& instance) mutable {
+ SFTRACE_NAME("REThreaded::drawGainmap");
+ instance.updateProtectedContext({}, {sdr.get(), hdr.get(), gainmap.get()});
+ instance.drawGainmapInternal(std::move(resultPromise), sdr, std::move(sdrFence), hdr,
+ std::move(hdrFence), hdrSdrRatio, dataspace, gainmap);
+ });
+ }
+ mCondition.notify_one();
+ return resultFuture;
+}
+
int RenderEngineThreaded::getContextPriority() {
std::promise<int> resultPromise;
std::future<int> resultFuture = resultPromise.get_future();
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index d4997d6..cb6e924 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -55,6 +55,12 @@
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence) override;
+ ftl::Future<FenceResult> drawGainmap(const std::shared_ptr<ExternalTexture>& sdr,
+ base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr,
+ base::borrowed_fd&& hdrFence, float hdrSdrRatio,
+ ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) override;
int getContextPriority() override;
bool supportsBackgroundBlur() override;
@@ -71,6 +77,13 @@
const std::vector<LayerSettings>& layers,
const std::shared_ptr<ExternalTexture>& buffer,
base::unique_fd&& bufferFence) override;
+ void drawGainmapInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise,
+ const std::shared_ptr<ExternalTexture>& sdr,
+ base::borrowed_fd&& sdrFence,
+ const std::shared_ptr<ExternalTexture>& hdr,
+ base::borrowed_fd&& hdrFence, float hdrSdrRatio,
+ ui::Dataspace dataspace,
+ const std::shared_ptr<ExternalTexture>& gainmap) override;
private:
void threadMain(CreateInstanceFactory factory);
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/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
index e5af740..8b13d78 100644
--- a/libs/ui/DisplayIdentification.cpp
+++ b/libs/ui/DisplayIdentification.cpp
@@ -26,6 +26,7 @@
#include <ftl/hash.h>
#include <log/log.h>
#include <ui/DisplayIdentification.h>
+#include <ui/Size.h>
namespace android {
namespace {
@@ -46,6 +47,10 @@
return view[3];
}
+bool isDetailedTimingDescriptor(const byte_view& view) {
+ return view[0] != 0 && view[1] != 0;
+}
+
std::string_view parseEdidText(const byte_view& view) {
std::string_view text(reinterpret_cast<const char*>(view.data()), view.size());
text = text.substr(0, text.find('\n'));
@@ -219,6 +224,8 @@
std::string_view displayName;
std::string_view serialNumber;
std::string_view asciiText;
+ ui::Size preferredDTDPixelSize;
+ ui::Size preferredDTDPhysicalSize;
constexpr size_t kDescriptorCount = 4;
constexpr size_t kDescriptorLength = 18;
@@ -243,6 +250,35 @@
serialNumber = parseEdidText(descriptor);
break;
}
+ } else if (isDetailedTimingDescriptor(view)) {
+ static constexpr size_t kHorizontalPhysicalLsbOffset = 12;
+ static constexpr size_t kHorizontalPhysicalMsbOffset = 14;
+ static constexpr size_t kVerticalPhysicalLsbOffset = 13;
+ static constexpr size_t kVerticalPhysicalMsbOffset = 14;
+ const uint32_t hSize =
+ static_cast<uint32_t>(view[kHorizontalPhysicalLsbOffset] |
+ ((view[kHorizontalPhysicalMsbOffset] >> 4) << 8));
+ const uint32_t vSize =
+ static_cast<uint32_t>(view[kVerticalPhysicalLsbOffset] |
+ ((view[kVerticalPhysicalMsbOffset] & 0b1111) << 8));
+
+ static constexpr size_t kHorizontalPixelLsbOffset = 2;
+ static constexpr size_t kHorizontalPixelMsbOffset = 4;
+ static constexpr size_t kVerticalPixelLsbOffset = 5;
+ static constexpr size_t kVerticalPixelMsbOffset = 7;
+
+ const uint8_t hLsb = view[kHorizontalPixelLsbOffset];
+ const uint8_t hMsb = view[kHorizontalPixelMsbOffset];
+ const int32_t hPixel = hLsb + ((hMsb & 0xF0) << 4);
+
+ const uint8_t vLsb = view[kVerticalPixelLsbOffset];
+ const uint8_t vMsb = view[kVerticalPixelMsbOffset];
+ const int32_t vPixel = vLsb + ((vMsb & 0xF0) << 4);
+
+ preferredDTDPixelSize.setWidth(hPixel);
+ preferredDTDPixelSize.setHeight(vPixel);
+ preferredDTDPhysicalSize.setWidth(hSize);
+ preferredDTDPhysicalSize.setHeight(vSize);
}
view = view.subspan(kDescriptorLength);
@@ -297,14 +333,22 @@
}
}
- return Edid{.manufacturerId = manufacturerId,
- .productId = productId,
- .pnpId = *pnpId,
- .modelHash = modelHash,
- .displayName = displayName,
- .manufactureOrModelYear = manufactureOrModelYear,
- .manufactureWeek = manufactureWeek,
- .cea861Block = cea861Block};
+ DetailedTimingDescriptor preferredDetailedTimingDescriptor{
+ .pixelSizeCount = preferredDTDPixelSize,
+ .physicalSizeInMm = preferredDTDPhysicalSize,
+ };
+
+ return Edid{
+ .manufacturerId = manufacturerId,
+ .productId = productId,
+ .pnpId = *pnpId,
+ .modelHash = modelHash,
+ .displayName = displayName,
+ .manufactureOrModelYear = manufactureOrModelYear,
+ .manufactureWeek = manufactureWeek,
+ .cea861Block = cea861Block,
+ .preferredDetailedTimingDescriptor = preferredDetailedTimingDescriptor,
+ };
}
std::optional<PnpId> getPnpId(uint16_t manufacturerId) {
@@ -336,9 +380,12 @@
}
const auto displayId = PhysicalDisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
- return DisplayIdentificationInfo{.id = displayId,
- .name = std::string(edid->displayName),
- .deviceProductInfo = buildDeviceProductInfo(*edid)};
+ return DisplayIdentificationInfo{
+ .id = displayId,
+ .name = std::string(edid->displayName),
+ .deviceProductInfo = buildDeviceProductInfo(*edid),
+ .preferredDetailedTimingDescriptor = edid->preferredDetailedTimingDescriptor,
+ };
}
PhysicalDisplayId getVirtualDisplayId(uint32_t id) {
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index ffb6cdb..b0c6e44 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -388,8 +388,8 @@
}
}
- const uint64_t usage = static_cast<uint64_t>(
- android_convertGralloc1To0Usage(inProducerUsage, inConsumerUsage));
+ const uint64_t usage = static_cast<uint64_t>(ANDROID_NATIVE_UNSIGNED_CAST(
+ android_convertGralloc1To0Usage(inProducerUsage, inConsumerUsage)));
auto result = getBufferMapper().lock(handle, usage, rect, base::unique_fd{fenceFd});
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/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index b6ab2f5..7b5a27d 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -208,8 +208,10 @@
status_t GraphicBufferMapper::lockAsync(buffer_handle_t handle, uint64_t producerUsage,
uint64_t consumerUsage, const Rect& bounds, void** vaddr,
int fenceFd) {
- return lockAsync(handle, android_convertGralloc1To0Usage(producerUsage, consumerUsage), bounds,
- vaddr, fenceFd);
+ return lockAsync(handle,
+ ANDROID_NATIVE_UNSIGNED_CAST(
+ android_convertGralloc1To0Usage(producerUsage, consumerUsage)),
+ bounds, vaddr, fenceFd);
}
status_t GraphicBufferMapper::lockAsyncYCbCr(buffer_handle_t handle, uint32_t usage,
diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
index 8bc2017..648e024 100644
--- a/libs/ui/include/ui/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -25,6 +25,7 @@
#include <ui/DeviceProductInfo.h>
#include <ui/DisplayId.h>
+#include <ui/Size.h>
#define LEGACY_DISPLAY_TYPE_PRIMARY 0
#define LEGACY_DISPLAY_TYPE_EXTERNAL 1
@@ -33,10 +34,16 @@
using DisplayIdentificationData = std::vector<uint8_t>;
+struct DetailedTimingDescriptor {
+ ui::Size pixelSizeCount;
+ ui::Size physicalSizeInMm;
+};
+
struct DisplayIdentificationInfo {
PhysicalDisplayId id;
std::string name;
std::optional<DeviceProductInfo> deviceProductInfo;
+ std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor;
};
struct ExtensionBlock {
@@ -68,6 +75,7 @@
uint8_t manufactureOrModelYear;
uint8_t manufactureWeek;
std::optional<Cea861ExtensionBlock> cea861Block;
+ std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor;
};
bool isEdid(const DisplayIdentificationData&);
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/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp
index 721b466..76e3f66 100644
--- a/libs/ui/tests/DisplayIdentification_test.cpp
+++ b/libs/ui/tests/DisplayIdentification_test.cpp
@@ -194,6 +194,10 @@
EXPECT_EQ(21, edid->manufactureOrModelYear);
EXPECT_EQ(0, edid->manufactureWeek);
EXPECT_FALSE(edid->cea861Block);
+ EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(261, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(163, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
edid = parseEdid(getExternalEdid());
ASSERT_TRUE(edid);
@@ -206,6 +210,10 @@
EXPECT_EQ(22, edid->manufactureOrModelYear);
EXPECT_EQ(2, edid->manufactureWeek);
EXPECT_FALSE(edid->cea861Block);
+ EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(641, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(400, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
edid = parseEdid(getExternalEedid());
ASSERT_TRUE(edid);
@@ -224,6 +232,10 @@
EXPECT_EQ(0, physicalAddress.b);
EXPECT_EQ(0, physicalAddress.c);
EXPECT_EQ(0, physicalAddress.d);
+ EXPECT_EQ(1366, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(768, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(160, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(90, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
edid = parseEdid(getPanasonicTvEdid());
ASSERT_TRUE(edid);
@@ -242,6 +254,10 @@
EXPECT_EQ(0, physicalAddress.b);
EXPECT_EQ(0, physicalAddress.c);
EXPECT_EQ(0, physicalAddress.d);
+ EXPECT_EQ(1920, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(1080, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(698, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(392, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
edid = parseEdid(getHisenseTvEdid());
ASSERT_TRUE(edid);
@@ -260,6 +276,10 @@
EXPECT_EQ(2, physicalAddress.b);
EXPECT_EQ(3, physicalAddress.c);
EXPECT_EQ(4, physicalAddress.d);
+ EXPECT_EQ(1920, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(1080, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(575, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(323, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
edid = parseEdid(getCtlDisplayEdid());
ASSERT_TRUE(edid);
@@ -273,6 +293,10 @@
EXPECT_EQ(0xff, edid->manufactureWeek);
ASSERT_TRUE(edid->cea861Block);
EXPECT_FALSE(edid->cea861Block->hdmiVendorDataBlock);
+ EXPECT_EQ(1360, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width);
+ EXPECT_EQ(768, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height);
+ EXPECT_EQ(521, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.width);
+ EXPECT_EQ(293, edid->preferredDetailedTimingDescriptor->physicalSizeInMm.height);
}
TEST(DisplayIdentificationTest, parseInvalidEdid) {
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/OWNERS b/opengl/OWNERS
index 3d60a1d..645a578 100644
--- a/opengl/OWNERS
+++ b/opengl/OWNERS
@@ -2,5 +2,4 @@
cnorthrop@google.com
ianelliott@google.com
jessehall@google.com
-lpy@google.com
-vantablack@google.com
+tomnom@google.com
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 16de390..5159ffe 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -40,9 +40,6 @@
symbol_file: "libEGL.map.txt",
first_version: "9",
unversioned_until: "current",
- export_header_libs: [
- "libEGL_headers",
- ],
}
ndk_library {
@@ -50,9 +47,6 @@
symbol_file: "libGLESv1_CM.map.txt",
first_version: "9",
unversioned_until: "current",
- export_header_libs: [
- "libGLESv1_CM_headers",
- ],
}
ndk_library {
@@ -60,9 +54,6 @@
symbol_file: "libGLESv2.map.txt",
first_version: "9",
unversioned_until: "current",
- export_header_libs: [
- "libGLESv2_headers",
- ],
}
ndk_library {
@@ -70,9 +61,6 @@
symbol_file: "libGLESv3.map.txt",
first_version: "18",
unversioned_until: "current",
- export_header_libs: [
- "libGLESv3_headers",
- ],
}
cc_defaults {
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/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp
index 00161e6..7628745 100644
--- a/services/gpuservice/gpuwork/GpuWork.cpp
+++ b/services/gpuservice/gpuwork/GpuWork.cpp
@@ -44,7 +44,7 @@
#include "gpuwork/gpuWork.h"
-#define ONE_MS_IN_NS (10000000)
+#define MSEC_PER_NSEC (1000LU * 1000LU)
namespace android {
namespace gpuwork {
@@ -385,10 +385,11 @@
ALOGI("pullWorkAtoms: after random selection: uids.size() == %zu", uids.size());
auto now = std::chrono::steady_clock::now();
- long long duration =
- std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint)
- .count();
- if (duration > std::numeric_limits<int32_t>::max() || duration < 0) {
+ int32_t duration =
+ static_cast<int32_t>(
+ std::chrono::duration_cast<std::chrono::seconds>(now - mPreviousMapClearTimePoint)
+ .count());
+ if (duration < 0) {
// This is essentially impossible. If it does somehow happen, give up,
// but still clear the map.
clearMap();
@@ -404,13 +405,14 @@
}
const UidTrackingInfo& info = it->second;
- uint64_t total_active_duration_ms = info.total_active_duration_ns / ONE_MS_IN_NS;
- uint64_t total_inactive_duration_ms = info.total_inactive_duration_ns / ONE_MS_IN_NS;
+ int32_t total_active_duration_ms =
+ static_cast<int32_t>(info.total_active_duration_ns / MSEC_PER_NSEC);
+ int32_t total_inactive_duration_ms =
+ static_cast<int32_t>(info.total_inactive_duration_ns / MSEC_PER_NSEC);
// Skip this atom if any numbers are out of range. |duration| is
// already checked above.
- if (total_active_duration_ms > std::numeric_limits<int32_t>::max() ||
- total_inactive_duration_ms > std::numeric_limits<int32_t>::max()) {
+ if (total_active_duration_ms < 0 || total_inactive_duration_ms < 0) {
continue;
}
@@ -421,11 +423,11 @@
// gpu_id
bitcast_int32(gpuId),
// time_duration_seconds
- static_cast<int32_t>(duration),
+ duration,
// total_active_duration_millis
- static_cast<int32_t>(total_active_duration_ms),
+ total_active_duration_ms,
// total_inactive_duration_millis
- static_cast<int32_t>(total_inactive_duration_ms));
+ total_inactive_duration_ms);
}
}
clearMap();
diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
index e70da54..60cd2c7 100644
--- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
+++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
@@ -125,7 +125,7 @@
static constexpr size_t kNumGpusHardLimit = 32;
// The minimum GPU time needed to actually log stats for a UID.
- static constexpr uint64_t kMinGpuTimeNanoseconds = 30U * 1000000000U; // 30 seconds.
+ static constexpr uint64_t kMinGpuTimeNanoseconds = 10LLU * 1000000000LLU; // 10 seconds.
// The previous time point at which |mGpuWorkMap| was cleared.
std::chrono::steady_clock::time_point mPreviousMapClearTimePoint GUARDED_BY(mMutex);
diff --git a/services/gpuservice/vts/TEST_MAPPING b/services/gpuservice/vts/TEST_MAPPING
index b33e962..a809be1 100644
--- a/services/gpuservice/vts/TEST_MAPPING
+++ b/services/gpuservice/vts/TEST_MAPPING
@@ -1,7 +1,13 @@
{
"presubmit": [
{
- "name": "GpuServiceVendorTests"
+ "name": "GpuServiceVendorTests",
+ "options": [
+ {
+ // Exclude test methods that require a physical device to run.
+ "exclude-annotation": "android.platform.test.annotations.RequiresDevice"
+ }
+ ]
}
]
}
diff --git a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
index 6c16335..5c12323 100644
--- a/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
+++ b/services/gpuservice/vts/src/com/android/tests/gpuservice/GpuWorkTracepointTest.java
@@ -19,7 +19,7 @@
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
-import android.platform.test.annotations.RestrictedBuildTest;
+import android.platform.test.annotations.RequiresDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -63,7 +63,7 @@
}
@VsrTest(requirements={"VSR-3.3-004"})
- @RestrictedBuildTest
+ @RequiresDevice
@Test
public void testGpuWorkPeriodTracepointFormat() throws Exception {
CommandResult commandResult = getDevice().executeShellV2Command(
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index cb220ab..ca92ab5 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -217,6 +217,7 @@
"libcutils",
"libinput",
"liblog",
+ "libprocessgroup",
"libstatslog",
"libutils",
],
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 2d5803b..2ef94fb 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -146,6 +146,12 @@
void InputFilter::dump(std::string& dump) {
dump += "InputFilter:\n";
+ if (isFilterEnabled()) {
+ std::string result;
+ LOG_ALWAYS_FATAL_IF(!mInputFilterRust->dumpFilter(&result).isOk());
+ dump += result;
+ dump += "\n";
+ }
}
} // namespace android
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/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 41e5247..b155122 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -250,6 +250,10 @@
mCollector->dump(dump);
dump += '\n';
}
+ if (ENABLE_INPUT_FILTER_RUST) {
+ mInputFilter->dump(dump);
+ dump += '\n';
+ }
mDispatcher->dump(dump);
dump += '\n';
}
diff --git a/services/inputflinger/InputThread.cpp b/services/inputflinger/InputThread.cpp
index e74f258..7cf4e39 100644
--- a/services/inputflinger/InputThread.cpp
+++ b/services/inputflinger/InputThread.cpp
@@ -16,10 +16,26 @@
#include "InputThread.h"
+#include <android-base/logging.h>
+#include <com_android_input_flags.h>
+#include <processgroup/processgroup.h>
+
namespace android {
+namespace input_flags = com::android::input::flags;
+
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:
@@ -39,10 +55,16 @@
} // 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);
+ 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;
+ }
+ }
}
InputThread::~InputThread() {
@@ -63,4 +85,4 @@
#endif
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 397feda..006d507 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -407,7 +407,8 @@
// TODO(b/315815559): Do not fade and reset the icon if the hover exit will be followed
// immediately by a DOWN event.
pc.fade(PointerControllerInterface::Transition::IMMEDIATE);
- pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED);
+ pc.updatePointerIcon(mShowTouchesEnabled ? PointerIconStyle::TYPE_SPOT_HOVER
+ : PointerIconStyle::TYPE_NOT_SPECIFIED);
} else if (canUnfadeOnDisplay(args.displayId)) {
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
@@ -792,6 +793,13 @@
if (isFromSource(sources, AINPUT_SOURCE_STYLUS)) {
auto it = mStylusPointersByDevice.find(deviceId);
if (it != mStylusPointersByDevice.end()) {
+ if (mShowTouchesEnabled) {
+ // If an app doesn't override the icon for the hovering stylus, show the hover icon.
+ auto* style = std::get_if<PointerIconStyle>(&icon);
+ if (style != nullptr && *style == PointerIconStyle::TYPE_NOT_SPECIFIED) {
+ *style = PointerIconStyle::TYPE_SPOT_HOVER;
+ }
+ }
setIconForController(icon, *it->second);
return true;
}
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index af9d2eb..10fec74 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -298,9 +298,14 @@
"name": "CtsInputRootTestCases"
}
],
- "staged-platinum-postsubmit": [
+ "platinum-postsubmit": [
{
"name": "inputflinger_tests"
}
+ ],
+ "staged-platinum-postsubmit": [
+ {
+ "name": "libinput_tests"
+ }
]
}
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
index 994d1c4..31b7231 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputFilter.aidl
@@ -54,5 +54,7 @@
/** Notifies when configuration changes */
void notifyConfigurationChanged(in InputFilterConfiguration config);
+
+ String dumpFilter();
}
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 1a0ec48..8b2b843 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -46,6 +46,7 @@
"InputState.cpp",
"InputTarget.cpp",
"LatencyAggregator.cpp",
+ "LatencyAggregatorWithHistograms.cpp",
"LatencyTracker.cpp",
"Monitor.cpp",
"TouchedWindow.cpp",
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
index 4a0889f..568d348 100644
--- a/services/inputflinger/dispatcher/CancelationOptions.h
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -32,7 +32,8 @@
CANCEL_POINTER_EVENTS = 1,
CANCEL_NON_POINTER_EVENTS = 2,
CANCEL_FALLBACK_EVENTS = 3,
- ftl_last = CANCEL_FALLBACK_EVENTS,
+ CANCEL_HOVER_EVENTS = 4,
+ ftl_last = CANCEL_HOVER_EVENTS
};
// The criterion to use to determine which events should be canceled.
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 250e72c..602904f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -155,6 +155,10 @@
// Number of recent events to keep for debugging purposes.
constexpr size_t RECENT_QUEUE_MAX_SIZE = 10;
+// Interval at which we should push the atom gathering input event latencies in
+// LatencyAggregatorWithHistograms
+constexpr nsecs_t LATENCY_STATISTICS_PUSH_INTERVAL = 6 * 3600 * 1000000000LL; // 6 hours
+
// Event log tags. See EventLogTags.logtags for reference.
constexpr int LOGTAG_INPUT_INTERACTION = 62000;
constexpr int LOGTAG_INPUT_FOCUS = 62001;
@@ -748,7 +752,8 @@
}
touchedWindow.dispatchMode = InputTarget::DispatchMode::AS_IS;
}
- touchedWindow.addHoveringPointer(entry.deviceId, pointer);
+ const auto [x, y] = resolveTouchedPosition(entry);
+ touchedWindow.addHoveringPointer(entry.deviceId, pointer, x, y);
if (canReceiveForegroundTouches(*newWindow->getInfo())) {
touchedWindow.targetFlags |= InputTarget::Flags::FOREGROUND;
}
@@ -875,6 +880,8 @@
return {false, true};
case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS:
return {false, true};
+ case CancelationOptions::Mode::CANCEL_HOVER_EVENTS:
+ return {true, false};
}
}
@@ -944,8 +951,13 @@
mFocusedDisplayId(ui::LogicalDisplayId::DEFAULT),
mWindowTokenWithPointerCapture(nullptr),
mAwaitedApplicationDisplayId(ui::LogicalDisplayId::INVALID),
- mLatencyAggregator(),
- mLatencyTracker(&mLatencyAggregator) {
+ mInputEventTimelineProcessor(
+ input_flags::enable_per_device_input_latency_metrics()
+ ? std::move(std::unique_ptr<InputEventTimelineProcessor>(
+ new LatencyAggregatorWithHistograms()))
+ : std::move(std::unique_ptr<InputEventTimelineProcessor>(
+ new LatencyAggregator()))),
+ mLatencyTracker(*mInputEventTimelineProcessor) {
mLooper = sp<Looper>::make(false);
mReporter = createInputReporter();
@@ -981,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;
}
@@ -1017,6 +1030,10 @@
const nsecs_t nextAnrCheck = processAnrsLocked();
nextWakeupTime = std::min(nextWakeupTime, nextAnrCheck);
+ if (mPerDeviceInputLatencyMetricsFlag) {
+ processLatencyStatisticsLocked();
+ }
+
// We are about to enter an infinitely long sleep, because we have no commands or
// pending or queued events
if (nextWakeupTime == LLONG_MAX) {
@@ -1097,6 +1114,19 @@
return LLONG_MIN;
}
+/**
+ * Check if enough time has passed since the last latency statistics push.
+ */
+void InputDispatcher::processLatencyStatisticsLocked() {
+ const nsecs_t currentTime = now();
+ // Log the atom recording latency statistics if more than 6 hours passed from the last
+ // push
+ if (currentTime - mLastStatisticPushTime >= LATENCY_STATISTICS_PUSH_INTERVAL) {
+ mInputEventTimelineProcessor->pushLatencyStatistics();
+ mLastStatisticPushTime = currentTime;
+ }
+}
+
std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(
const std::shared_ptr<Connection>& connection) {
if (connection->monitor) {
@@ -2511,7 +2541,8 @@
if (isHoverAction) {
// The "windowHandle" is the target of this hovering pointer.
- tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer);
+ tempTouchState.addHoveringPointerToWindow(windowHandle, entry.deviceId, pointer, x,
+ y);
}
// Set target flags.
@@ -2864,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;
}
@@ -4491,6 +4523,10 @@
{ // acquire lock
mLock.lock();
+ if (input_flags::keyboard_repeat_keys() && !mConfig.keyRepeatEnabled) {
+ policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
+ }
+
if (shouldSendKeyToInputFilterLocked(args)) {
mLock.unlock();
@@ -4511,6 +4547,14 @@
newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry);
}
+ if (mPerDeviceInputLatencyMetricsFlag) {
+ if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
+ IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
+ !mInputFilterEnabled) {
+ mLatencyTracker.trackNotifyKey(args);
+ }
+ }
+
needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
} // release lock
@@ -4643,9 +4687,7 @@
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
!mInputFilterEnabled) {
- std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args);
- mLatencyTracker.trackListener(args.id, args.eventTime, args.readTime, args.deviceId,
- sources, args.action, InputEventType::MOTION);
+ mLatencyTracker.trackNotifyMotion(args);
}
needWake = enqueueInboundEventLocked(std::move(newEntry));
@@ -4752,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,
@@ -4862,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();
@@ -5415,6 +5468,32 @@
}
}
+ // Check if the hovering should stop because the window is no longer eligible to receive it
+ // (for example, if the touchable region changed)
+ if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
+ TouchState& state = it->second;
+ for (TouchedWindow& touchedWindow : state.windows) {
+ std::vector<DeviceId> erasedDevices = touchedWindow.eraseHoveringPointersIf(
+ [this, displayId, &touchedWindow](const PointerProperties& properties, float x,
+ float y) REQUIRES(mLock) {
+ const bool isStylus = properties.toolType == ToolType::STYLUS;
+ const ui::Transform displayTransform = getTransformLocked(displayId);
+ const bool stillAcceptsTouch =
+ windowAcceptsTouchAt(*touchedWindow.windowHandle->getInfo(),
+ displayId, x, y, isStylus, displayTransform);
+ return !stillAcceptsTouch;
+ });
+
+ for (DeviceId deviceId : erasedDevices) {
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_HOVER_EVENTS,
+ "WindowInfo changed",
+ traceContext.getTracker());
+ options.deviceId = deviceId;
+ synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options);
+ }
+ }
+ }
+
// Release information for windows that are no longer present.
// This ensures that unused input channels are released promptly.
// Otherwise, they might stick around until the window handle is destroyed
@@ -5753,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.
@@ -6055,7 +6134,7 @@
dump += StringPrintf(INDENT2 "KeyRepeatTimeout: %" PRId64 "ms\n",
ns2ms(mConfig.keyRepeatTimeout));
dump += mLatencyTracker.dump(INDENT2);
- dump += mLatencyAggregator.dump(INDENT2);
+ dump += mInputEventTimelineProcessor->dump(INDENT2);
dump += INDENT "InputTracer: ";
dump += mTracer == nullptr ? "Disabled" : "Enabled";
}
@@ -7209,11 +7288,13 @@
}
void InputDispatcher::setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
- std::chrono::nanoseconds delay) {
+ std::chrono::nanoseconds delay,
+ bool keyRepeatEnabled) {
std::scoped_lock _l(mLock);
mConfig.keyRepeatTimeout = timeout.count();
mConfig.keyRepeatDelay = delay.count();
+ mConfig.keyRepeatEnabled = keyRepeatEnabled;
}
bool InputDispatcher::isPointerInWindow(const sp<android::IBinder>& token,
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 87dfd1d..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"
@@ -28,6 +30,7 @@
#include "InputTarget.h"
#include "InputThread.h"
#include "LatencyAggregator.h"
+#include "LatencyAggregatorWithHistograms.h"
#include "LatencyTracker.h"
#include "Monitor.h"
#include "TouchState.h"
@@ -153,8 +156,8 @@
// Public to allow tests to verify that a Monitor can get ANR.
void setMonitorDispatchingTimeoutForTest(std::chrono::nanoseconds timeout);
- void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
- std::chrono::nanoseconds delay) override;
+ void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout, std::chrono::nanoseconds delay,
+ bool keyRepeatEnabled) override;
bool isPointerInWindow(const sp<IBinder>& token, ui::LogicalDisplayId displayId,
DeviceId deviceId, int32_t pointerId) override;
@@ -296,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&,
@@ -326,6 +333,7 @@
std::chrono::nanoseconds mMonitorDispatchingTimeout GUARDED_BY(mLock);
nsecs_t processAnrsLocked() REQUIRES(mLock);
+ void processLatencyStatisticsLocked() REQUIRES(mLock);
std::chrono::nanoseconds getDispatchingTimeoutLocked(
const std::shared_ptr<Connection>& connection) REQUIRES(mLock);
@@ -697,7 +705,8 @@
DeviceId deviceId) const REQUIRES(mLock);
// Statistics gathering.
- LatencyAggregator mLatencyAggregator GUARDED_BY(mLock);
+ nsecs_t mLastStatisticPushTime = 0;
+ std::unique_ptr<InputEventTimelineProcessor> mInputEventTimelineProcessor GUARDED_BY(mLock);
LatencyTracker mLatencyTracker GUARDED_BY(mLock);
void traceInboundQueueLengthLocked() REQUIRES(mLock);
void traceOutboundQueueLength(const Connection& connection);
@@ -738,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/InputEventTimeline.cpp b/services/inputflinger/dispatcher/InputEventTimeline.cpp
index 31ceb8d..6881964 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.cpp
+++ b/services/inputflinger/dispatcher/InputEventTimeline.cpp
@@ -66,12 +66,11 @@
return !operator==(rhs);
}
-InputEventTimeline::InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime,
- uint16_t vendorId, uint16_t productId,
+InputEventTimeline::InputEventTimeline(nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId,
+ uint16_t productId,
const std::set<InputDeviceUsageSource>& sources,
InputEventActionType inputEventActionType)
- : isDown(isDown),
- eventTime(eventTime),
+ : eventTime(eventTime),
readTime(readTime),
vendorId(vendorId),
productId(productId),
@@ -91,8 +90,8 @@
return false;
}
}
- return isDown == rhs.isDown && eventTime == rhs.eventTime && readTime == rhs.readTime &&
- vendorId == rhs.vendorId && productId == rhs.productId && sources == rhs.sources &&
+ return eventTime == rhs.eventTime && readTime == rhs.readTime && vendorId == rhs.vendorId &&
+ productId == rhs.productId && sources == rhs.sources &&
inputEventActionType == rhs.inputEventActionType;
}
diff --git a/services/inputflinger/dispatcher/InputEventTimeline.h b/services/inputflinger/dispatcher/InputEventTimeline.h
index 6668399..4552708 100644
--- a/services/inputflinger/dispatcher/InputEventTimeline.h
+++ b/services/inputflinger/dispatcher/InputEventTimeline.h
@@ -97,10 +97,9 @@
};
struct InputEventTimeline {
- InputEventTimeline(bool isDown, nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId,
- uint16_t productId, const std::set<InputDeviceUsageSource>& sources,
+ InputEventTimeline(nsecs_t eventTime, nsecs_t readTime, uint16_t vendorId, uint16_t productId,
+ const std::set<InputDeviceUsageSource>& sources,
InputEventActionType inputEventActionType);
- const bool isDown; // True if this is an ACTION_DOWN event
const nsecs_t eventTime;
const nsecs_t readTime;
const uint16_t vendorId;
@@ -122,13 +121,21 @@
class InputEventTimelineProcessor {
protected:
InputEventTimelineProcessor() {}
- virtual ~InputEventTimelineProcessor() {}
public:
+ virtual ~InputEventTimelineProcessor() {}
+
/**
* Process the provided timeline
*/
virtual void processTimeline(const InputEventTimeline& timeline) = 0;
+
+ /**
+ * Trigger a push for the input event latency statistics
+ */
+ virtual void pushLatencyStatistics() = 0;
+
+ virtual std::string dump(const char* prefix) const = 0;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 4df23c5..9b5a79b 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -638,6 +638,8 @@
return memento.source & AINPUT_SOURCE_CLASS_POINTER;
case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS:
return !(memento.source & AINPUT_SOURCE_CLASS_POINTER);
+ case CancelationOptions::Mode::CANCEL_HOVER_EVENTS:
+ return memento.hovering;
default:
return false;
}
diff --git a/services/inputflinger/dispatcher/LatencyAggregator.cpp b/services/inputflinger/dispatcher/LatencyAggregator.cpp
index e09d97a..d0e9d7c 100644
--- a/services/inputflinger/dispatcher/LatencyAggregator.cpp
+++ b/services/inputflinger/dispatcher/LatencyAggregator.cpp
@@ -28,6 +28,8 @@
using dist_proc::aggregation::KllQuantile;
using std::chrono_literals::operator""ms;
+namespace {
+
// Convert the provided nanoseconds into hundreds of microseconds.
// Use hundreds of microseconds (as opposed to microseconds) to preserve space.
static inline int64_t ns2hus(nsecs_t nanos) {
@@ -74,6 +76,8 @@
return std::chrono::milliseconds(std::stoi(millis));
}
+} // namespace
+
namespace android::inputdispatcher {
/**
@@ -125,6 +129,9 @@
processSlowEvent(timeline);
}
+// This version of LatencyAggregator doesn't push any atoms
+void LatencyAggregator::pushLatencyStatistics() {}
+
void LatencyAggregator::processStatistics(const InputEventTimeline& timeline) {
std::scoped_lock lock(mLock);
// Before we do any processing, check that we have not yet exceeded MAX_SIZE
@@ -134,7 +141,9 @@
mNumSketchEventsProcessed++;
std::array<std::unique_ptr<KllQuantile>, SketchIndex::SIZE>& sketches =
- timeline.isDown ? mDownSketches : mMoveSketches;
+ timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_DOWN
+ ? mDownSketches
+ : mMoveSketches;
// Process common ones first
const nsecs_t eventToRead = timeline.readTime - timeline.eventTime;
@@ -242,7 +251,9 @@
const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime;
const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime;
- android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED, timeline.isDown,
+ android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED,
+ timeline.inputEventActionType ==
+ InputEventActionType::MOTION_ACTION_DOWN,
static_cast<int32_t>(ns2us(eventToRead)),
static_cast<int32_t>(ns2us(readToDeliver)),
static_cast<int32_t>(ns2us(deliverToConsume)),
diff --git a/services/inputflinger/dispatcher/LatencyAggregator.h b/services/inputflinger/dispatcher/LatencyAggregator.h
index d6d1686..468add1 100644
--- a/services/inputflinger/dispatcher/LatencyAggregator.h
+++ b/services/inputflinger/dispatcher/LatencyAggregator.h
@@ -57,6 +57,8 @@
*/
void processTimeline(const InputEventTimeline& timeline) override;
+ void pushLatencyStatistics() override;
+
std::string dump(const char* prefix) const;
~LatencyAggregator();
diff --git a/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp
new file mode 100644
index 0000000..881a96b
--- /dev/null
+++ b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.cpp
@@ -0,0 +1,345 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LatencyAggregatorWithHistograms"
+#include "../InputDeviceMetricsSource.h"
+#include "InputDispatcher.h"
+
+#include <inttypes.h>
+#include <log/log_event_list.h>
+#include <statslog.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <input/Input.h>
+#include <log/log.h>
+#include <server_configurable_flags/get_flags.h>
+
+using android::base::StringPrintf;
+using std::chrono_literals::operator""ms;
+
+namespace {
+
+// Convert the provided nanoseconds into hundreds of microseconds.
+// Use hundreds of microseconds (as opposed to microseconds) to preserve space.
+static inline int64_t ns2hus(nsecs_t nanos) {
+ return ns2us(nanos) / 100;
+}
+
+// Category (=namespace) name for the input settings that are applied at boot time
+static const char* INPUT_NATIVE_BOOT = "input_native_boot";
+// Feature flag name for the threshold of end-to-end touch latency that would trigger
+// SlowEventReported atom to be pushed
+static const char* SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS =
+ "slow_event_min_reporting_latency_millis";
+// Feature flag name for the minimum delay before reporting a slow event after having just reported
+// a slow event. This helps limit the amount of data sent to the server
+static const char* SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS =
+ "slow_event_min_reporting_interval_millis";
+
+// If an event has end-to-end latency > 200 ms, it will get reported as a slow event.
+std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY = 200ms;
+// If we receive two slow events less than 1 min apart, we will only report 1 of them.
+std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL = 60000ms;
+
+static std::chrono::milliseconds getSlowEventMinReportingLatency() {
+ std::string millis = server_configurable_flags::
+ GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS,
+ std::to_string(
+ DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY.count()));
+ return std::chrono::milliseconds(std::stoi(millis));
+}
+
+static std::chrono::milliseconds getSlowEventMinReportingInterval() {
+ std::string millis = server_configurable_flags::
+ GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS,
+ std::to_string(
+ DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL.count()));
+ return std::chrono::milliseconds(std::stoi(millis));
+}
+
+} // namespace
+
+namespace android::inputdispatcher {
+
+int32_t LatencyStageIndexToAtomEnum(LatencyStageIndex latencyStageIndex) {
+ switch (latencyStageIndex) {
+ case LatencyStageIndex::EVENT_TO_READ:
+ return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__EVENT_TO_READ;
+ case LatencyStageIndex::READ_TO_DELIVER:
+ return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__READ_TO_DELIVER;
+ case LatencyStageIndex::DELIVER_TO_CONSUME:
+ return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__DELIVER_TO_CONSUME;
+ case LatencyStageIndex::CONSUME_TO_FINISH:
+ return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__CONSUME_TO_FINISH;
+ case LatencyStageIndex::CONSUME_TO_GPU_COMPLETE:
+ return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__CONSUME_TO_GPU_COMPLETE;
+ case LatencyStageIndex::GPU_COMPLETE_TO_PRESENT:
+ return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__GPU_COMPLETE_TO_PRESENT;
+ case LatencyStageIndex::END_TO_END:
+ return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__END_TO_END;
+ default:
+ return util::INPUT_EVENT_LATENCY_REPORTED__LATENCY_STAGE__UNKNOWN_LATENCY_STAGE;
+ }
+}
+
+int32_t InputEventTypeEnumToAtomEnum(InputEventActionType inputEventActionType) {
+ switch (inputEventActionType) {
+ case InputEventActionType::UNKNOWN_INPUT_EVENT:
+ return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__UNKNOWN_INPUT_EVENT;
+ case InputEventActionType::MOTION_ACTION_DOWN:
+ return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_DOWN;
+ case InputEventActionType::MOTION_ACTION_MOVE:
+ return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_MOVE;
+ case InputEventActionType::MOTION_ACTION_UP:
+ return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_UP;
+ case InputEventActionType::MOTION_ACTION_HOVER_MOVE:
+ return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_HOVER_MOVE;
+ case InputEventActionType::MOTION_ACTION_SCROLL:
+ return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__MOTION_ACTION_SCROLL;
+ case InputEventActionType::KEY:
+ return util::INPUT_EVENT_LATENCY_REPORTED__INPUT_EVENT_TYPE__KEY;
+ }
+}
+
+void LatencyAggregatorWithHistograms::processTimeline(const InputEventTimeline& timeline) {
+ processStatistics(timeline);
+ processSlowEvent(timeline);
+}
+
+void LatencyAggregatorWithHistograms::addSampleToHistogram(
+ const InputEventLatencyIdentifier& identifier, LatencyStageIndex latencyStageIndex,
+ nsecs_t latency) {
+ // Only record positive values for the statistics
+ if (latency > 0) {
+ auto it = mHistograms.find(identifier);
+ if (it != mHistograms.end()) {
+ it->second[static_cast<size_t>(latencyStageIndex)].addSample(ns2hus(latency));
+ }
+ }
+}
+
+void LatencyAggregatorWithHistograms::processStatistics(const InputEventTimeline& timeline) {
+ // Only gather data for Down, Move and Up motion events and Key events
+ if (!(timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_DOWN ||
+ timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_MOVE ||
+ timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_UP ||
+ timeline.inputEventActionType == InputEventActionType::KEY))
+ return;
+
+ // Don't collect data for unidentified devices. This situation can occur for the first few input
+ // events produced when an input device is first connected
+ if (timeline.vendorId == 0xFFFF && timeline.productId == 0xFFFF) return;
+
+ InputEventLatencyIdentifier identifier = {timeline.vendorId, timeline.productId,
+ timeline.sources, timeline.inputEventActionType};
+ // Check if there's a value in mHistograms map associated to identifier.
+ // If not, add an array with 7 empty histograms as an entry
+ if (mHistograms.count(identifier) == 0) {
+ if (static_cast<int32_t>(timeline.inputEventActionType) - 1 < 0) {
+ LOG(FATAL) << "Action index is smaller than 0. Action type: "
+ << ftl::enum_string(timeline.inputEventActionType);
+ return;
+ }
+ size_t actionIndex =
+ static_cast<size_t>(static_cast<int32_t>(timeline.inputEventActionType) - 1);
+ if (actionIndex >= NUM_INPUT_EVENT_TYPES) {
+ LOG(FATAL) << "Action index greater than the number of input event types. Action Type: "
+ << ftl::enum_string(timeline.inputEventActionType)
+ << "; Action Type Index: " << actionIndex;
+ return;
+ }
+
+ std::array<Histogram, 7> histograms =
+ {Histogram(allBinSizes[binSizesMappings[0][actionIndex]]),
+ Histogram(allBinSizes[binSizesMappings[1][actionIndex]]),
+ Histogram(allBinSizes[binSizesMappings[2][actionIndex]]),
+ Histogram(allBinSizes[binSizesMappings[3][actionIndex]]),
+ Histogram(allBinSizes[binSizesMappings[4][actionIndex]]),
+ Histogram(allBinSizes[binSizesMappings[5][actionIndex]]),
+ Histogram(allBinSizes[binSizesMappings[6][actionIndex]])};
+ mHistograms.insert({identifier, histograms});
+ }
+
+ // Process common ones first
+ const nsecs_t eventToRead = timeline.readTime - timeline.eventTime;
+ addSampleToHistogram(identifier, LatencyStageIndex::EVENT_TO_READ, eventToRead);
+
+ // Now process per-connection ones
+ for (const auto& [connectionToken, connectionTimeline] : timeline.connectionTimelines) {
+ if (!connectionTimeline.isComplete()) {
+ continue;
+ }
+ const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime;
+ const nsecs_t deliverToConsume =
+ connectionTimeline.consumeTime - connectionTimeline.deliveryTime;
+ const nsecs_t consumeToFinish =
+ connectionTimeline.finishTime - connectionTimeline.consumeTime;
+ const nsecs_t gpuCompletedTime =
+ connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t presentTime =
+ connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime;
+ const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime;
+ const nsecs_t endToEnd = presentTime - timeline.eventTime;
+
+ addSampleToHistogram(identifier, LatencyStageIndex::READ_TO_DELIVER, readToDeliver);
+ addSampleToHistogram(identifier, LatencyStageIndex::DELIVER_TO_CONSUME, deliverToConsume);
+ addSampleToHistogram(identifier, LatencyStageIndex::CONSUME_TO_FINISH, consumeToFinish);
+ addSampleToHistogram(identifier, LatencyStageIndex::CONSUME_TO_GPU_COMPLETE,
+ consumeToGpuComplete);
+ addSampleToHistogram(identifier, LatencyStageIndex::GPU_COMPLETE_TO_PRESENT,
+ gpuCompleteToPresent);
+ addSampleToHistogram(identifier, LatencyStageIndex::END_TO_END, endToEnd);
+ }
+}
+
+void LatencyAggregatorWithHistograms::pushLatencyStatistics() {
+ for (auto& [id, histograms] : mHistograms) {
+ auto [vendorId, productId, sources, action] = id;
+ for (size_t latencyStageIndex = static_cast<size_t>(LatencyStageIndex::EVENT_TO_READ);
+ latencyStageIndex < static_cast<size_t>(LatencyStageIndex::SIZE);
+ ++latencyStageIndex) {
+ // Convert sources set to vector for atom logging:
+ std::vector<int32_t> sourcesVector = {};
+ for (auto& elem : sources) {
+ sourcesVector.push_back(static_cast<int32_t>(elem));
+ }
+
+ // convert histogram bin counts array to vector for atom logging:
+ std::array arr = histograms[latencyStageIndex].getBinCounts();
+ std::vector<int32_t> binCountsVector(arr.begin(), arr.end());
+
+ if (static_cast<int32_t>(action) - 1 < 0) {
+ ALOGW("Action index is smaller than 0. Action type: %s",
+ ftl::enum_string(action).c_str());
+ continue;
+ }
+ size_t actionIndex = static_cast<size_t>(static_cast<int32_t>(action) - 1);
+ if (actionIndex >= NUM_INPUT_EVENT_TYPES) {
+ ALOGW("Action index greater than the number of input event types. Action Type: %s; "
+ "Action Type Index: %zu",
+ ftl::enum_string(action).c_str(), actionIndex);
+ continue;
+ }
+
+ stats_write(android::util::INPUT_EVENT_LATENCY_REPORTED, vendorId, productId,
+ sourcesVector, InputEventTypeEnumToAtomEnum(action),
+ LatencyStageIndexToAtomEnum(
+ static_cast<LatencyStageIndex>(latencyStageIndex)),
+ histogramVersions[latencyStageIndex][actionIndex], binCountsVector);
+ }
+ }
+ mHistograms.clear();
+}
+
+// TODO (b/270049345): For now, we just copied the code from LatencyAggregator to populate the old
+// atom, but eventually we should migrate this to use the new SlowEventReported atom
+void LatencyAggregatorWithHistograms::processSlowEvent(const InputEventTimeline& timeline) {
+ static const std::chrono::duration sSlowEventThreshold = getSlowEventMinReportingLatency();
+ static const std::chrono::duration sSlowEventReportingInterval =
+ getSlowEventMinReportingInterval();
+ for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) {
+ if (!connectionTimeline.isComplete()) {
+ continue;
+ }
+ mNumEventsSinceLastSlowEventReport++;
+ const nsecs_t presentTime =
+ connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ const std::chrono::nanoseconds endToEndLatency =
+ std::chrono::nanoseconds(presentTime - timeline.eventTime);
+ if (endToEndLatency < sSlowEventThreshold) {
+ continue;
+ }
+ // This is a slow event. Before we report it, check if we are reporting too often
+ const std::chrono::duration elapsedSinceLastReport =
+ std::chrono::nanoseconds(timeline.eventTime - mLastSlowEventTime);
+ if (elapsedSinceLastReport < sSlowEventReportingInterval) {
+ mNumSkippedSlowEvents++;
+ continue;
+ }
+
+ const nsecs_t eventToRead = timeline.readTime - timeline.eventTime;
+ const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime;
+ const nsecs_t deliverToConsume =
+ connectionTimeline.consumeTime - connectionTimeline.deliveryTime;
+ const nsecs_t consumeToFinish =
+ connectionTimeline.finishTime - connectionTimeline.consumeTime;
+ const nsecs_t gpuCompletedTime =
+ connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime;
+ const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime;
+
+ android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED,
+ timeline.inputEventActionType ==
+ InputEventActionType::MOTION_ACTION_DOWN,
+ static_cast<int32_t>(ns2us(eventToRead)),
+ static_cast<int32_t>(ns2us(readToDeliver)),
+ static_cast<int32_t>(ns2us(deliverToConsume)),
+ static_cast<int32_t>(ns2us(consumeToFinish)),
+ static_cast<int32_t>(ns2us(consumeToGpuComplete)),
+ static_cast<int32_t>(ns2us(gpuCompleteToPresent)),
+ static_cast<int32_t>(ns2us(endToEndLatency.count())),
+ static_cast<int32_t>(mNumEventsSinceLastSlowEventReport),
+ static_cast<int32_t>(mNumSkippedSlowEvents));
+ mNumEventsSinceLastSlowEventReport = 0;
+ mNumSkippedSlowEvents = 0;
+ mLastSlowEventTime = timeline.readTime;
+ }
+}
+
+std::string LatencyAggregatorWithHistograms::dump(const char* prefix) const {
+ std::string statisticsStr = StringPrintf("%s Histograms:\n", prefix);
+ for (const auto& [id, histograms] : mHistograms) {
+ auto [vendorId, productId, sources, action] = id;
+
+ std::string identifierStr =
+ StringPrintf("%s Identifier: vendor %d, product %d, sources: {", prefix, vendorId,
+ productId);
+ bool firstSource = true;
+ for (const auto& source : sources) {
+ if (!firstSource) {
+ identifierStr += ", ";
+ }
+ identifierStr += StringPrintf("%d", static_cast<int32_t>(source));
+ firstSource = false;
+ }
+ identifierStr += StringPrintf("}, action: %d\n", static_cast<int32_t>(action));
+
+ std::string histogramsStr;
+ for (size_t stageIndex = 0; stageIndex < static_cast<size_t>(LatencyStageIndex::SIZE);
+ stageIndex++) {
+ const auto& histogram = histograms[stageIndex];
+ const std::array<int, NUM_BINS>& binCounts = histogram.getBinCounts();
+
+ histogramsStr += StringPrintf("%s %zu: ", prefix, stageIndex);
+ histogramsStr += StringPrintf("%d", binCounts[0]);
+ for (size_t bin = 1; bin < NUM_BINS; bin++) {
+ histogramsStr += StringPrintf(", %d", binCounts[bin]);
+ }
+ histogramsStr += StringPrintf("\n");
+ }
+ statisticsStr += identifierStr + histogramsStr;
+ }
+
+ return StringPrintf("%sLatencyAggregatorWithHistograms:\n", prefix) + statisticsStr +
+ StringPrintf("%s mLastSlowEventTime=%" PRId64 "\n", prefix, mLastSlowEventTime) +
+ StringPrintf("%s mNumEventsSinceLastSlowEventReport = %zu\n", prefix,
+ mNumEventsSinceLastSlowEventReport) +
+ StringPrintf("%s mNumSkippedSlowEvents = %zu\n", prefix, mNumSkippedSlowEvents);
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.h b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.h
new file mode 100644
index 0000000..2ceb0e7
--- /dev/null
+++ b/services/inputflinger/dispatcher/LatencyAggregatorWithHistograms.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <utils/Timers.h>
+
+#include "InputEventTimeline.h"
+
+namespace android::inputdispatcher {
+
+static constexpr size_t NUM_BINS = 20;
+static constexpr size_t NUM_INPUT_EVENT_TYPES = 6;
+
+enum class LatencyStageIndex : size_t {
+ EVENT_TO_READ = 0,
+ READ_TO_DELIVER = 1,
+ DELIVER_TO_CONSUME = 2,
+ CONSUME_TO_FINISH = 3,
+ CONSUME_TO_GPU_COMPLETE = 4,
+ GPU_COMPLETE_TO_PRESENT = 5,
+ END_TO_END = 6,
+ SIZE = 7, // must be last
+};
+
+// Let's create a full timeline here:
+// eventTime
+// readTime
+// <---- after this point, the data becomes per-connection
+// deliveryTime // time at which the event was sent to the receiver
+// consumeTime // time at which the receiver read the event
+// finishTime // time at which the dispatcher reads the response from the receiver that the event
+// was processed
+// GraphicsTimeline::GPU_COMPLETED_TIME
+// GraphicsTimeline::PRESENT_TIME
+
+/**
+ * Keep histograms with latencies of the provided events
+ */
+class LatencyAggregatorWithHistograms final : public InputEventTimelineProcessor {
+public:
+ /**
+ * Record a complete event timeline
+ */
+ void processTimeline(const InputEventTimeline& timeline) override;
+
+ void pushLatencyStatistics() override;
+
+ std::string dump(const char* prefix) const override;
+
+private:
+ // ---------- Slow event handling ----------
+ void processSlowEvent(const InputEventTimeline& timeline);
+ nsecs_t mLastSlowEventTime = 0;
+ // How many slow events have been skipped due to rate limiting
+ size_t mNumSkippedSlowEvents = 0;
+ // How many events have been received since the last time we reported a slow event
+ size_t mNumEventsSinceLastSlowEventReport = 0;
+
+ // ---------- Statistics handling ----------
+ /**
+ * Data structure to gather time samples into NUM_BINS buckets
+ */
+ class Histogram {
+ public:
+ Histogram(const std::array<int, NUM_BINS - 1>& binSizes) : mBinSizes(binSizes) {
+ mBinCounts.fill(0);
+ }
+
+ // Increments binCounts of the appropriate bin when adding a new sample
+ void addSample(int64_t sample) {
+ size_t binIndex = getSampleBinIndex(sample);
+ mBinCounts[binIndex]++;
+ }
+
+ const std::array<int32_t, NUM_BINS>& getBinCounts() const { return mBinCounts; }
+
+ private:
+ // reference to an array that represents the range of values each bin holds.
+ // in bin i+1 live samples such that *mBinSizes[i] <= sample < *mBinSizes[i+1]
+ const std::array<int, NUM_BINS - 1>& mBinSizes;
+ std::array<int32_t, NUM_BINS>
+ mBinCounts; // the number of samples that currently live in each bin
+
+ size_t getSampleBinIndex(int64_t sample) {
+ auto it = std::upper_bound(mBinSizes.begin(), mBinSizes.end(), sample);
+ return std::distance(mBinSizes.begin(), it);
+ }
+ };
+
+ void processStatistics(const InputEventTimeline& timeline);
+
+ // Identifier for the an input event. If two input events have the same identifiers we
+ // want to use the same histograms to count the latency samples
+ using InputEventLatencyIdentifier =
+ std::tuple<uint16_t /*vendorId*/, uint16_t /*productId*/,
+ const std::set<InputDeviceUsageSource> /*sources*/,
+ InputEventActionType /*inputEventActionType*/>;
+
+ // Maps an input event identifier to an array of 7 histograms, one for each latency
+ // stage. It is cleared after an atom push
+ std::map<InputEventLatencyIdentifier, std::array<Histogram, 7>> mHistograms;
+
+ void addSampleToHistogram(const InputEventLatencyIdentifier& identifier,
+ LatencyStageIndex latencyStageIndex, nsecs_t time);
+
+ // Stores all possible arrays of bin sizes. The order in the vector does not matter, as long
+ // as binSizesMappings points to the right index
+ static constexpr std::array<std::array<int, NUM_BINS - 1>, 6> allBinSizes = {
+ {{10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100},
+ {1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32},
+ {15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270,
+ 285},
+ {40, 80, 120, 160, 200, 240, 280, 320, 360, 400, 440, 480, 520, 560, 600, 640, 680,
+ 720, 760},
+ {20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300, 320, 340, 360,
+ 380},
+ {200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600,
+ 1700, 1800, 1900, 2000}}};
+
+ // Stores indexes in allBinSizes to use with each {LatencyStage, InputEventType} pair.
+ // Bin sizes for a certain latencyStage and inputEventType are at:
+ // *(allBinSizes[binSizesMappings[latencyStageIndex][inputEventTypeIndex]])
+ // inputEventTypeIndex is the int value of InputEventActionType enum decreased by 1 since we
+ // don't want to record latencies for unknown events.
+ // e.g. MOTION_ACTION_DOWN is 0, MOTION_ACTION_MOVE is 1...
+ static constexpr std::array<std::array<int8_t, NUM_INPUT_EVENT_TYPES>,
+ static_cast<size_t>(LatencyStageIndex::SIZE)>
+ binSizesMappings = {{{0, 0, 0, 0, 0, 0},
+ {1, 1, 1, 1, 1, 1},
+ {1, 1, 1, 1, 1, 1},
+ {2, 2, 2, 2, 2, 2},
+ {3, 3, 3, 3, 3, 3},
+ {4, 4, 4, 4, 4, 4},
+ {5, 5, 5, 5, 5, 5}}};
+
+ // Similar to binSizesMappings, but holds the index of the array of bin ranges to use on the
+ // server. The index gets pushed with the atom within the histogram_version field.
+ static constexpr std::array<std::array<int8_t, NUM_INPUT_EVENT_TYPES>,
+ static_cast<size_t>(LatencyStageIndex::SIZE)>
+ histogramVersions = {{{0, 0, 0, 0, 0, 0},
+ {1, 1, 1, 1, 1, 1},
+ {1, 1, 1, 1, 1, 1},
+ {2, 2, 2, 2, 2, 2},
+ {3, 3, 3, 3, 3, 3},
+ {4, 4, 4, 4, 4, 4},
+ {5, 5, 5, 5, 5, 5}}};
+};
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index 721d009..0852026 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -62,15 +62,33 @@
}
}
-LatencyTracker::LatencyTracker(InputEventTimelineProcessor* processor)
- : mTimelineProcessor(processor) {
- LOG_ALWAYS_FATAL_IF(processor == nullptr);
+LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor)
+ : mTimelineProcessor(&processor) {}
+
+void LatencyTracker::trackNotifyMotion(const NotifyMotionArgs& args) {
+ std::set<InputDeviceUsageSource> sources = getUsageSourcesForMotionArgs(args);
+ trackListener(args.id, args.eventTime, args.readTime, args.deviceId, sources, args.action,
+ InputEventType::MOTION);
+}
+
+void LatencyTracker::trackNotifyKey(const NotifyKeyArgs& args) {
+ int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NONE;
+ for (auto& inputDevice : mInputDevices) {
+ if (args.deviceId == inputDevice.getId()) {
+ keyboardType = inputDevice.getKeyboardType();
+ break;
+ }
+ }
+ std::set<InputDeviceUsageSource> sources =
+ std::set{getUsageSourceForKeyArgs(keyboardType, args)};
+ trackListener(args.id, args.eventTime, args.readTime, args.deviceId, sources, args.action,
+ InputEventType::KEY);
}
void LatencyTracker::trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime,
DeviceId deviceId,
const std::set<InputDeviceUsageSource>& sources,
- int inputEventAction, InputEventType inputEventType) {
+ int32_t inputEventAction, InputEventType inputEventType) {
reportAndPruneMatureRecords(eventTime);
const auto it = mTimelines.find(inputEventId);
if (it != mTimelines.end()) {
@@ -105,7 +123,7 @@
const InputEventActionType inputEventActionType = [&]() {
switch (inputEventType) {
case InputEventType::MOTION: {
- switch (inputEventAction) {
+ switch (MotionEvent::getActionMasked(inputEventAction)) {
case AMOTION_EVENT_ACTION_DOWN:
return InputEventActionType::MOTION_ACTION_DOWN;
case AMOTION_EVENT_ACTION_MOVE:
@@ -134,10 +152,8 @@
}
}();
- bool isDown = inputEventType == InputEventType::MOTION &&
- inputEventAction == AMOTION_EVENT_ACTION_DOWN;
mTimelines.emplace(inputEventId,
- InputEventTimeline(isDown, eventTime, readTime, identifier->vendor,
+ InputEventTimeline(eventTime, readTime, identifier->vendor,
identifier->product, sources, inputEventActionType));
mEventTimes.emplace(eventTime, inputEventId);
}
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 532f422..eb58222 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -42,7 +42,7 @@
* Create a LatencyTracker.
* param reportingFunction: the function that will be called in order to report full latency.
*/
- LatencyTracker(InputEventTimelineProcessor* processor);
+ LatencyTracker(InputEventTimelineProcessor& processor);
/**
* Start keeping track of an event identified by inputEventId. This must be called first.
* If duplicate events are encountered (events that have the same eventId), none of them will be
@@ -53,12 +53,19 @@
* must drop all duplicate data.
*/
void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId,
- const std::set<InputDeviceUsageSource>& sources, int inputEventActionType,
+ const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction,
InputEventType inputEventType);
void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
nsecs_t deliveryTime, nsecs_t consumeTime, nsecs_t finishTime);
void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
+ /**
+ * trackNotifyMotion and trackNotifyKeys are intermediates between InputDispatcher and
+ * trackListener. They compute the InputDeviceUsageSource set and call trackListener with
+ * the relevant parameters for latency computation.
+ */
+ void trackNotifyMotion(const NotifyMotionArgs& args);
+ void trackNotifyKey(const NotifyKeyArgs& args);
std::string dump(const char* prefix) const;
void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices);
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 0c9ad3c..2bf63be 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -112,17 +112,18 @@
}
void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
- DeviceId deviceId, const PointerProperties& pointer) {
+ DeviceId deviceId, const PointerProperties& pointer,
+ float x, float y) {
for (TouchedWindow& touchedWindow : windows) {
if (touchedWindow.windowHandle == windowHandle) {
- touchedWindow.addHoveringPointer(deviceId, pointer);
+ touchedWindow.addHoveringPointer(deviceId, pointer, x, y);
return;
}
}
TouchedWindow touchedWindow;
touchedWindow.windowHandle = windowHandle;
- touchedWindow.addHoveringPointer(deviceId, pointer);
+ touchedWindow.addHoveringPointer(deviceId, pointer, x, y);
windows.push_back(touchedWindow);
}
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 5a70dd5..451d917 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -49,7 +49,8 @@
DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers,
std::optional<nsecs_t> firstDownTimeInTarget);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- DeviceId deviceId, const PointerProperties& pointer);
+ DeviceId deviceId, const PointerProperties& pointer, float x,
+ float y);
void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
void clearHoveringPointers(DeviceId deviceId);
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index 1f86f66..fa5be1a 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -36,6 +36,13 @@
}) != pointers.end();
}
+bool hasPointerId(const std::vector<TouchedWindow::HoveringPointer>& pointers, int32_t pointerId) {
+ return std::find_if(pointers.begin(), pointers.end(),
+ [&pointerId](const TouchedWindow::HoveringPointer& pointer) {
+ return pointer.properties.id == pointerId;
+ }) != pointers.end();
+}
+
} // namespace
bool TouchedWindow::hasHoveringPointers() const {
@@ -78,16 +85,18 @@
return hasPointerId(state.hoveringPointers, pointerId);
}
-void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer) {
- std::vector<PointerProperties>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers;
+void TouchedWindow::addHoveringPointer(DeviceId deviceId, const PointerProperties& properties,
+ float x, float y) {
+ std::vector<HoveringPointer>& hoveringPointers = mDeviceStates[deviceId].hoveringPointers;
const size_t initialSize = hoveringPointers.size();
- std::erase_if(hoveringPointers, [&pointer](const PointerProperties& properties) {
- return properties.id == pointer.id;
+ std::erase_if(hoveringPointers, [&properties](const HoveringPointer& pointer) {
+ return pointer.properties.id == properties.id;
});
if (hoveringPointers.size() != initialSize) {
- LOG(ERROR) << __func__ << ": " << pointer << ", device " << deviceId << " was in " << *this;
+ LOG(ERROR) << __func__ << ": " << properties << ", device " << deviceId << " was in "
+ << *this;
}
- hoveringPointers.push_back(pointer);
+ hoveringPointers.push_back({properties, x, y});
}
Result<void> TouchedWindow::addTouchingPointers(DeviceId deviceId,
@@ -173,8 +182,8 @@
return true;
}
}
- for (const PointerProperties& properties : state.hoveringPointers) {
- if (properties.toolType == ToolType::STYLUS) {
+ for (const HoveringPointer& pointer : state.hoveringPointers) {
+ if (pointer.properties.toolType == ToolType::STYLUS) {
return true;
}
}
@@ -270,8 +279,8 @@
}
DeviceState& state = stateIt->second;
- std::erase_if(state.hoveringPointers, [&pointerId](const PointerProperties& properties) {
- return properties.id == pointerId;
+ std::erase_if(state.hoveringPointers, [&pointerId](const HoveringPointer& pointer) {
+ return pointer.properties.id == pointerId;
});
if (!state.hasPointers()) {
@@ -279,6 +288,22 @@
}
}
+std::vector<DeviceId> TouchedWindow::eraseHoveringPointersIf(
+ std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition) {
+ std::vector<DeviceId> erasedDevices;
+ for (auto& [deviceId, state] : mDeviceStates) {
+ std::erase_if(state.hoveringPointers, [&](const HoveringPointer& pointer) {
+ if (condition(pointer.properties, pointer.x, pointer.y)) {
+ erasedDevices.push_back(deviceId);
+ return true;
+ }
+ return false;
+ });
+ }
+
+ return erasedDevices;
+}
+
void TouchedWindow::removeAllHoveringPointersForDevice(DeviceId deviceId) {
const auto stateIt = mDeviceStates.find(deviceId);
if (stateIt == mDeviceStates.end()) {
@@ -312,6 +337,11 @@
return out;
}
+std::ostream& operator<<(std::ostream& out, const TouchedWindow::HoveringPointer& pointer) {
+ out << pointer.properties << " at (" << pointer.x << ", " << pointer.y << ")";
+ return out;
+}
+
std::ostream& operator<<(std::ostream& out, const TouchedWindow& window) {
out << window.dump();
return out;
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 4f0ad16..c38681e 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -38,7 +38,7 @@
bool hasHoveringPointers() const;
bool hasHoveringPointers(DeviceId deviceId) const;
bool hasHoveringPointer(DeviceId deviceId, int32_t pointerId) const;
- void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer);
+ void addHoveringPointer(DeviceId deviceId, const PointerProperties& pointer, float x, float y);
void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
// Touching
@@ -69,6 +69,15 @@
void clearHoveringPointers(DeviceId deviceId);
std::string dump() const;
+ struct HoveringPointer {
+ PointerProperties properties;
+ float x;
+ float y;
+ };
+
+ std::vector<DeviceId> eraseHoveringPointersIf(
+ std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition);
+
private:
struct DeviceState {
std::vector<PointerProperties> touchingPointers;
@@ -78,7 +87,7 @@
// NOTE: This is not initialized in case of HOVER entry/exit and DISPATCH_AS_OUTSIDE
// scenario.
std::optional<nsecs_t> downTimeInTarget;
- std::vector<PointerProperties> hoveringPointers;
+ std::vector<HoveringPointer> hoveringPointers;
bool hasPointers() const { return !touchingPointers.empty() || !hoveringPointers.empty(); };
};
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
index 5eb3a32..ba197d4 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherConfiguration.h
@@ -34,8 +34,13 @@
// The key repeat inter-key delay.
nsecs_t keyRepeatDelay;
+ // Whether key repeat is enabled.
+ bool keyRepeatEnabled;
+
InputDispatcherConfiguration()
- : keyRepeatTimeout(500 * 1000000LL), keyRepeatDelay(50 * 1000000LL) {}
+ : keyRepeatTimeout(500 * 1000000LL),
+ keyRepeatDelay(50 * 1000000LL),
+ keyRepeatEnabled(true) {}
};
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 653f595..463a952 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -227,10 +227,11 @@
virtual void cancelCurrentTouch() = 0;
/*
- * Updates key repeat configuration timeout and delay.
+ * Updates whether key repeat is enabled and key repeat configuration timeout and delay.
*/
virtual void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
- std::chrono::nanoseconds delay) = 0;
+ std::chrono::nanoseconds delay,
+ bool keyRepeatEnabled) = 0;
/*
* Determine if a pointer from a device is being dispatched to the given window.
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 7bec94e..756a29b 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -93,6 +93,13 @@
// The touchpad settings changed.
TOUCHPAD_SETTINGS = 1u << 13,
+ // 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,
};
@@ -246,6 +253,18 @@
// True if a pointer icon should be shown for direct stylus pointers.
bool stylusPointerIconEnabled;
+ // 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),
@@ -276,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)
@@ -333,9 +354,6 @@
virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) = 0;
virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) = 0;
- virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
- int32_t toKeyCode) const = 0;
-
virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
/* Toggle Caps Lock */
@@ -466,6 +484,9 @@
virtual void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
int32_t deviceId) = 0;
+ /* Sends the Info of gestures that happen on the touchpad. */
+ virtual void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) = 0;
+
/* Gets the keyboard layout for a particular input device. */
virtual std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
diff --git a/services/inputflinger/include/InputThread.h b/services/inputflinger/include/InputThread.h
index 5e75027..ed92b8f 100644
--- a/services/inputflinger/include/InputThread.h
+++ b/services/inputflinger/include/InputThread.h
@@ -28,14 +28,13 @@
*/
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;
};
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index e34ed0f..8f3d9ca 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -72,10 +72,6 @@
/* Dumps the state of the pointer controller. */
virtual std::string dump() = 0;
- /* Gets the bounds of the region that the pointer can traverse.
- * Returns true if the bounds are available. */
- virtual std::optional<FloatRect> getBounds() const = 0;
-
/* Move the pointer. */
virtual void move(float deltaX, float deltaY) = 0;
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 e11adb8..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;
}
@@ -1177,7 +1191,8 @@
return false;
}
-void EventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+void EventHub::setKeyRemapping(int32_t deviceId,
+ const std::map<int32_t, int32_t>& keyRemapping) const {
std::scoped_lock _l(mLock);
Device* device = getDeviceLocked(deviceId);
if (device == nullptr) {
@@ -1185,7 +1200,7 @@
}
const std::shared_ptr<KeyCharacterMap> kcm = device->getKeyCharacterMap();
if (kcm) {
- kcm->addKeyRemapping(fromKeyCode, toKeyCode);
+ kcm->setKeyRemapping(keyRemapping);
}
}
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 70f024e..02eeb0a 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -365,6 +365,18 @@
// so update the enabled state when there is a change in display info.
out += updateEnableState(when, readerConfig, forceEnable);
}
+
+ if (!changes.any() || changes.test(InputReaderConfiguration::Change::KEY_REMAPPING)) {
+ const bool isFullKeyboard =
+ (mSources & AINPUT_SOURCE_KEYBOARD) == AINPUT_SOURCE_KEYBOARD &&
+ mKeyboardType == KeyboardType::ALPHABETIC;
+ if (isFullKeyboard) {
+ for_each_subdevice([&readerConfig](auto& context) {
+ context.setKeyRemapping(readerConfig.keyRemapping);
+ });
+ bumpGeneration();
+ }
+ }
}
return out;
}
@@ -679,22 +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::addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) {
- for_each_subdevice([fromKeyCode, toKeyCode](auto& context) {
- context.addKeyRemapping(fromKeyCode, toKeyCode);
- });
-}
-
void InputDevice::bumpGeneration() {
mGeneration = mContext->bumpGeneration();
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index a5b1249..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,
@@ -625,15 +617,6 @@
return result;
}
-void InputReader::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
- std::scoped_lock _l(mLock);
-
- InputDevice* device = findInputDeviceLocked(deviceId);
- if (device != nullptr) {
- device->addKeyRemapping(fromKeyCode, toKeyCode);
- }
-}
-
int32_t InputReader::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 657126a..dffd8e3 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -281,8 +281,8 @@
virtual bool hasMscEvent(int32_t deviceId, int mscEvent) const = 0;
- virtual void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
- int32_t toKeyCode) const = 0;
+ virtual void setKeyRemapping(int32_t deviceId,
+ const std::map<int32_t, int32_t>& keyRemapping) const = 0;
virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode,
int32_t metaState, int32_t* outKeycode, int32_t* outMetaState,
@@ -513,8 +513,8 @@
bool hasMscEvent(int32_t deviceId, int mscEvent) const override final;
- void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode,
- int32_t toKeyCode) const override final;
+ void setKeyRemapping(int32_t deviceId,
+ const std::map<int32_t, int32_t>& keyRemapping) const override final;
status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState,
@@ -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 021978d..8958d9e 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -122,10 +122,6 @@
std::optional<int32_t> getLightPlayerId(int32_t lightId);
int32_t getMetaState();
- void updateMetaState(int32_t keyCode);
-
- void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
-
void setKeyboardType(KeyboardType keyboardType);
void bumpGeneration();
@@ -329,8 +325,8 @@
inline bool hasMscEvent(int mscEvent) const { return mEventHub->hasMscEvent(mId, mscEvent); }
- inline void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode) const {
- mEventHub->addKeyRemapping(mId, fromKeyCode, toKeyCode);
+ inline void setKeyRemapping(const std::map<int32_t, int32_t>& keyRemapping) const {
+ mEventHub->setKeyRemapping(mId, keyRemapping);
}
inline status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t metaState,
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 2cc0a00..1003871 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -65,8 +65,6 @@
int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override;
int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
- void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override;
-
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override;
void toggleCapsLockState(int32_t deviceId) override;
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/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index dbc2872..9a36bfb 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -425,7 +425,7 @@
std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
if (state) {
if (mTouchpadHardwareStateNotificationsEnabled) {
- getPolicy()->notifyTouchpadHardwareState(*state, rawEvent.deviceId);
+ getPolicy()->notifyTouchpadHardwareState(*state, getDeviceId());
}
updatePalmDetectionMetrics();
return sendHardwareState(rawEvent.when, rawEvent.readTime, *state);
@@ -480,6 +480,9 @@
return;
}
mGesturesToProcess.push_back(*gesture);
+ if (mTouchpadHardwareStateNotificationsEnabled) {
+ getPolicy()->notifyTouchpadGestureInfo(gesture->type, getDeviceId());
+ }
}
std::list<NotifyArgs> TouchpadInputMapper::processGestures(nsecs_t when, nsecs_t readTime) {
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/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 9924d0d..da2c683 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -60,6 +60,21 @@
}
}
+bool isGestureNoFocusChange(MotionClassification classification) {
+ switch (classification) {
+ case MotionClassification::TWO_FINGER_SWIPE:
+ case MotionClassification::MULTI_FINGER_SWIPE:
+ case MotionClassification::PINCH:
+ // Most gestures can be performed on an unfocused window, so they should not
+ // not affect window focus.
+ return true;
+ case MotionClassification::NONE:
+ case MotionClassification::AMBIGUOUS_GESTURE:
+ case MotionClassification::DEEP_PRESS:
+ return false;
+ }
+}
+
} // namespace
GestureConverter::GestureConverter(InputReaderContext& readerContext,
@@ -67,6 +82,7 @@
: mDeviceId(deviceId),
mReaderContext(readerContext),
mEnableFlingStop(input_flags::enable_touchpad_fling_stop()),
+ mEnableNoFocusChange(input_flags::enable_touchpad_no_focus_change()),
// We can safely assume that ABS_MT_POSITION_X and _Y axes will be available, as EventHub
// won't classify a device as a touchpad if they're not present.
mXAxisInfo(deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X).value()),
@@ -338,7 +354,6 @@
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0,
mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
- args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
out.push_back(args);
}
float deltaX = gesture.details.scroll.dx;
@@ -353,7 +368,6 @@
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
- args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
out.push_back(args);
return out;
}
@@ -427,7 +441,6 @@
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
- args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
out.push_back(args);
mCurrentClassification = MotionClassification::NONE;
out += enterHover(when, readTime);
@@ -624,6 +637,18 @@
int32_t actionButton, int32_t buttonState,
uint32_t pointerCount,
const PointerCoords* pointerCoords) {
+ int32_t flags = 0;
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ flags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ if (mEnableNoFocusChange && isGestureNoFocusChange(mCurrentClassification)) {
+ flags |= AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
+ }
+ if (mCurrentClassification == MotionClassification::TWO_FINGER_SWIPE) {
+ // This helps to make GestureDetector responsive.
+ flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
+ }
+
return {mReaderContext.getNextId(),
when,
readTime,
@@ -633,7 +658,7 @@
/* policyFlags= */ POLICY_FLAG_WAKE,
action,
/* actionButton= */ actionButton,
- /* flags= */ action == AMOTION_EVENT_ACTION_CANCEL ? AMOTION_EVENT_FLAG_CANCELED : 0,
+ flags,
mReaderContext.getGlobalMetaState(),
buttonState,
mCurrentClassification,
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index 829fb92..c9a35c1 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -99,6 +99,7 @@
const int32_t mDeviceId;
InputReaderContext& mReaderContext;
const bool mEnableFlingStop;
+ const bool mEnableNoFocusChange;
std::optional<ui::LogicalDisplayId> mDisplayId;
FloatRect mBoundsInLogicalDisplay{};
diff --git a/services/inputflinger/rust/bounce_keys_filter.rs b/services/inputflinger/rust/bounce_keys_filter.rs
index dc4f1df..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)]
@@ -65,7 +66,10 @@
impl Filter for BounceKeysFilter {
fn notify_key(&mut self, event: &KeyEvent) {
- if !(self.supported_devices.contains(&event.deviceId) && event.source == Source::KEYBOARD) {
+ // Check if it is a supported device and event source contains Source::KEYBOARD
+ if !(self.supported_devices.contains(&event.deviceId)
+ && event.source.0 & Source::KEYBOARD.0 != 0)
+ {
self.next.notify_key(event);
return;
}
@@ -130,6 +134,26 @@
fn destroy(&mut self) {
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);
+ result += &format!("\tkey_event_map = {:?}\n", self.key_event_map);
+ result += &format!("\tblocked_events = {:?}\n", self.blocked_events);
+ result += &format!("\tsupported_devices = {:?}\n", self.supported_devices);
+ self.next.dump(dump_str + &result)
+ }
}
#[cfg(test)]
@@ -191,6 +215,41 @@
}
#[test]
+ fn test_is_notify_key_for_tv_remote() {
+ let mut next = TestFilter::new();
+ let mut filter = setup_filter_with_external_device(
+ Box::new(next.clone()),
+ 1, /* device_id */
+ 100, /* threshold */
+ KeyboardType::NonAlphabetic,
+ );
+
+ let source = Source(Source::KEYBOARD.0 | Source::DPAD.0);
+ let event = KeyEvent { action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ let event = KeyEvent { action: KeyEventAction::UP, source, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+
+ next.clear();
+ let event = KeyEvent { action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event =
+ KeyEvent { eventTime: 100, action: KeyEventAction::UP, source, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert!(next.last_event().is_none());
+
+ let event =
+ KeyEvent { eventTime: 200, action: KeyEventAction::DOWN, source, ..BASE_KEY_EVENT };
+ filter.notify_key(&event);
+ assert_eq!(next.last_event().unwrap(), event);
+ }
+
+ #[test]
fn test_is_notify_key_blocks_for_internal_keyboard() {
let mut next = TestFilter::new();
let mut filter = setup_filter_with_internal_device(
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index f166723..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,12 @@
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;
}
struct InputFilterState {
@@ -104,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()));
@@ -122,18 +131,31 @@
self.input_filter_thread.clone(),
));
state.enabled = true;
- info!("Slow keys filter is installed");
+ info!(
+ "Slow keys filter is installed, threshold = {:?}ns",
+ config.slowKeysThresholdNs
+ );
}
if config.bounceKeysThresholdNs > 0 {
first_filter =
Box::new(BounceKeysFilter::new(first_filter, config.bounceKeysThresholdNs));
state.enabled = true;
- info!("Bounce keys filter is installed");
+ info!(
+ "Bounce keys filter is installed, threshold = {:?}ns",
+ config.bounceKeysThresholdNs
+ );
}
state.first_filter = first_filter;
+ state.first_filter.restore(&saved_state);
}
Result::Ok(())
}
+
+ fn dumpFilter(&self) -> binder::Result<String> {
+ let first_filter = &mut self.state.lock().unwrap().first_filter;
+ let dump_str = first_filter.dump(String::new());
+ Result::Ok(dump_str)
+ }
}
struct BaseFilter {
@@ -161,6 +183,23 @@
fn destroy(&mut self) {
// 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
+ }
}
/// This struct wraps around IInputFilterCallbacks restricting access to only
@@ -349,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)]
@@ -397,6 +438,20 @@
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 3e242d8..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
@@ -100,7 +101,7 @@
// acquire write lock
let mut slow_filter = self.write_inner();
if !(slow_filter.supported_devices.contains(&event.deviceId)
- && event.source == Source::KEYBOARD)
+ && event.source.0 & Source::KEYBOARD.0 != 0)
{
slow_filter.next.notify_key(event);
return;
@@ -186,6 +187,29 @@
slow_filter.input_filter_thread.unregister_thread_callback(Box::new(self.clone()));
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();
+ result += &format!("\tthreshold = {:?}ns\n", slow_filter.slow_key_threshold_ns);
+ result += &format!("\tongoing_down_events = {:?}\n", slow_filter.ongoing_down_events);
+ result += &format!("\tpending_down_events = {:?}\n", slow_filter.pending_down_events);
+ result += &format!("\tsupported_devices = {:?}\n", slow_filter.supported_devices);
+ slow_filter.next.dump(dump_str + &result)
+ }
}
impl ThreadCallback for SlowKeysFilter {
@@ -286,6 +310,63 @@
}
#[test]
+ fn test_notify_key_for_tv_remote_when_key_pressed_for_threshold_time() {
+ let test_callbacks = TestCallbacks::new();
+ let test_thread = get_thread(test_callbacks.clone());
+ let next = TestFilter::new();
+ let mut filter = setup_filter_with_external_device(
+ Box::new(next.clone()),
+ test_thread.clone(),
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
+ KeyboardType::NonAlphabetic,
+ );
+ let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ let source = Source(Source::KEYBOARD.0 | Source::DPAD.0);
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: down_time,
+ eventTime: down_time,
+ source,
+ ..BASE_KEY_EVENT
+ });
+ assert!(next.last_event().is_none());
+
+ std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64));
+ assert_eq!(
+ next.last_event().unwrap(),
+ KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ source,
+ policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT,
+ ..BASE_KEY_EVENT
+ }
+ );
+
+ let up_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time,
+ eventTime: up_time,
+ source,
+ ..BASE_KEY_EVENT
+ });
+
+ assert_eq!(
+ next.last_event().unwrap(),
+ KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: up_time,
+ source,
+ ..BASE_KEY_EVENT
+ }
+ );
+ }
+
+ #[test]
fn test_notify_key_for_internal_alphabetic_keyboard_when_key_pressed_for_threshold_time() {
let test_callbacks = TestCallbacks::new();
let test_thread = get_thread(test_callbacks.clone());
diff --git a/services/inputflinger/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
index dfcea79..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,32 +110,64 @@
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.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)
+ }
}
fn is_modifier_key(keycode: i32) -> bool {
@@ -237,6 +271,7 @@
};
use input::KeyboardType;
use input::ModifierState;
+ use std::collections::HashMap;
use std::sync::{Arc, RwLock};
static DEVICE_ID: i32 = 1;
@@ -444,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/Android.bp b/services/inputflinger/tests/Android.bp
index 95283ba..744cf4a 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -123,4 +123,5 @@
"device-tests",
"device-platinum-tests",
],
+ native_coverage: false,
}
diff --git a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
index d39ad3f..353011a 100644
--- a/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
+++ b/services/inputflinger/tests/CapturedTouchpadEventConverter_test.cpp
@@ -755,7 +755,7 @@
WithMotionAction(AMOTION_EVENT_ACTION_UP))));
EXPECT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(AllOf(WithPointerCount(1u), WithCoords(255, 202),
- WithPointerRelativeMotion(1, 0, 0),
+ WithRelativeMotion(0, 0),
WithToolType(ToolType::FINGER)))));
}
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/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 31fbf20..943de6e 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -151,9 +151,10 @@
getDevice(deviceId)->keyCodeMapping.insert_or_assign(fromKeyCode, toKeyCode);
}
-void FakeEventHub::addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
+void FakeEventHub::setKeyRemapping(int32_t deviceId,
+ const std::map<int32_t, int32_t>& keyRemapping) const {
Device* device = getDevice(deviceId);
- device->keyRemapping.insert_or_assign(fromKeyCode, toKeyCode);
+ device->keyRemapping = keyRemapping;
}
void FakeEventHub::addLed(int32_t deviceId, int32_t led, bool initialState) {
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index 3d8dddd..2dfbb23 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -55,7 +55,7 @@
KeyedVector<int32_t, int32_t> absoluteAxisValue;
KeyedVector<int32_t, KeyInfo> keysByScanCode;
KeyedVector<int32_t, KeyInfo> keysByUsageCode;
- std::unordered_map<int32_t, int32_t> keyRemapping;
+ std::map<int32_t, int32_t> keyRemapping;
KeyedVector<int32_t, bool> leds;
// fake mapping which would normally come from keyCharacterMap
std::unordered_map<int32_t, int32_t> keyCodeMapping;
@@ -129,7 +129,7 @@
void addKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t keyCode,
uint32_t flags);
void addKeyCodeMapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode);
- void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const;
+ void setKeyRemapping(int32_t deviceId, const std::map<int32_t, int32_t>& keyRemapping) const;
void addVirtualKeyDefinition(int32_t deviceId, const VirtualKeyDefinition& definition);
void addSensorAxis(int32_t deviceId, int32_t absCode, InputDeviceSensorType sensorType,
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index d77d539..f373cac 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -32,19 +32,23 @@
} // namespace
void FakeInputReaderPolicy::assertInputDevicesChanged() {
- waitForInputDevices([](bool devicesChanged) {
- if (!devicesChanged) {
- FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
- }
- });
+ waitForInputDevices(
+ [](bool devicesChanged) {
+ if (!devicesChanged) {
+ FAIL() << "Timed out waiting for notifyInputDevicesChanged() to be called.";
+ }
+ },
+ ADD_INPUT_DEVICE_TIMEOUT);
}
void FakeInputReaderPolicy::assertInputDevicesNotChanged() {
- waitForInputDevices([](bool devicesChanged) {
- if (devicesChanged) {
- FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
- }
- });
+ waitForInputDevices(
+ [](bool devicesChanged) {
+ if (devicesChanged) {
+ FAIL() << "Expected notifyInputDevicesChanged() to not be called.";
+ }
+ },
+ INPUT_DEVICES_DIDNT_CHANGE_TIMEOUT);
}
void FakeInputReaderPolicy::assertStylusGestureNotified(int32_t deviceId) {
@@ -252,6 +256,10 @@
mTouchpadHardwareStateNotified.notify_all();
}
+void FakeInputReaderPolicy::notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) {
+ std::scoped_lock lock(mLock);
+}
+
std::shared_ptr<KeyCharacterMap> FakeInputReaderPolicy::getKeyboardLayoutOverlay(
const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) {
return nullptr;
@@ -261,13 +269,13 @@
return "";
}
-void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged) {
+void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> processDevicesChanged,
+ std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
const bool devicesChanged =
- mDevicesChangedCondition.wait_for(lock,
- ADD_INPUT_DEVICE_TIMEOUT * HW_TIMEOUT_MULTIPLIER,
+ mDevicesChangedCondition.wait_for(lock, timeout * HW_TIMEOUT_MULTIPLIER,
[this]() REQUIRES(mLock) {
return mInputDevicesChanged;
});
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index e5ba620..3a2b4e9 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -85,10 +85,12 @@
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
int32_t deviceId) override;
+ void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
std::string getDeviceAlias(const InputDeviceIdentifier&) override;
- void waitForInputDevices(std::function<void(bool)> processDevicesChanged);
+ void waitForInputDevices(std::function<void(bool)> processDevicesChanged,
+ std::chrono::milliseconds timeout);
void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
mutable std::mutex mLock;
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index d0998ba..887a939 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -148,12 +148,6 @@
return mIsPointerShown;
}
-std::optional<FloatRect> FakePointerController::getBounds() const {
- if (!mEnabled) return std::nullopt;
-
- return mHaveBounds ? std::make_optional<FloatRect>(mMinX, mMinY, mMaxX, mMaxY) : std::nullopt;
-}
-
void FakePointerController::move(float deltaX, float deltaY) {
if (!mEnabled) return;
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 2c76c62..9b773a7 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -65,7 +65,6 @@
private:
std::string dump() override { return ""; }
- std::optional<FloatRect> getBounds() const override;
void move(float deltaX, float deltaY) override;
void unfade(Transition) override;
void setPresentation(Presentation) override {}
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index d0cd677..225ae0f 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -279,6 +279,8 @@
}
TEST_F(GestureConverterTest, Scroll) {
+ input_flags::enable_touchpad_no_focus_change(true);
+
const nsecs_t downTime = 12345;
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
@@ -300,7 +302,8 @@
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE |
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE),
WithToolType(ToolType::FINGER),
WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
@@ -312,7 +315,8 @@
WithGestureScrollDistance(0, 5, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE |
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE),
WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
@@ -325,7 +329,8 @@
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(
MotionClassification::TWO_FINGER_SWIPE),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE |
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithCoords(0, 0),
@@ -845,6 +850,8 @@
}
TEST_F(GestureConverterTest, Pinch_Inwards) {
+ input_flags::enable_touchpad_no_focus_change(true);
+
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
@@ -867,7 +874,8 @@
AllOf(WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithToolType(ToolType::FINGER),
- WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)))));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 0.8, GESTURES_ZOOM_UPDATE);
@@ -879,7 +887,8 @@
WithGesturePinchScaleFactor(0.8f, EPSILON),
WithPointerCoords(0, -80, 0), WithPointerCoords(1, 80, 0),
WithPointerCount(2u), WithToolType(ToolType::FINGER),
- WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)))));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -891,12 +900,14 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(2u))),
+ WithPointerCount(2u),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(1u))),
+ WithPointerCount(1u),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithCoords(0, 0),
@@ -908,6 +919,8 @@
}
TEST_F(GestureConverterTest, Pinch_Outwards) {
+ input_flags::enable_touchpad_no_focus_change(true);
+
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
@@ -930,7 +943,8 @@
AllOf(WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithToolType(ToolType::FINGER),
- WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)))));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 1.1, GESTURES_ZOOM_UPDATE);
@@ -942,7 +956,8 @@
WithGesturePinchScaleFactor(1.1f, EPSILON),
WithPointerCoords(0, -110, 0), WithPointerCoords(1, 110, 0),
WithPointerCount(2u), WithToolType(ToolType::FINGER),
- WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)))));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -954,12 +969,14 @@
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(2u))),
+ WithPointerCount(2u),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(1u))),
+ WithPointerCount(1u),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithCoords(0, 0),
@@ -1055,6 +1072,8 @@
}
TEST_F(GestureConverterTest, ResetDuringScroll) {
+ input_flags::enable_touchpad_no_focus_change(true);
+
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
@@ -1070,7 +1089,8 @@
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(
MotionClassification::TWO_FINGER_SWIPE),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE |
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithCoords(0, 0),
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index c2f174f..3413caa 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1913,6 +1913,99 @@
window->consumeMotionEvent(WithMotionAction(ACTION_SCROLL));
}
+/**
+ * Two windows: a trusted overlay and a regular window underneath. Both windows are visible.
+ * Mouse is hovered, and the hover event should only go to the overlay.
+ * However, next, the touchable region of the trusted overlay shrinks. The mouse position hasn't
+ * changed, but the cursor would now end up hovering above the regular window underneatch.
+ * If the mouse is now clicked, this would generate an ACTION_DOWN event, which would go to the
+ * regular window. However, the trusted overlay is also watching for outside touch.
+ * The trusted overlay should get two events:
+ * 1) The ACTION_OUTSIDE event, since the click is now not inside its touchable region
+ * 2) The HOVER_EXIT event, since the mouse pointer is no longer hovering inside this window
+ *
+ * This test reproduces a crash where there is an overlap between dispatch modes for the trusted
+ * overlay touch target, since the event is causing both an ACTION_OUTSIDE, and as a HOVER_EXIT.
+ */
+TEST_F(InputDispatcherTest, MouseClickUnderShrinkingTrustedOverlay) {
+ std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> overlay = sp<FakeWindowHandle>::make(app, mDispatcher, "Trusted overlay",
+ ui::LogicalDisplayId::DEFAULT);
+ overlay->setTrustedOverlay(true);
+ overlay->setWatchOutsideTouch(true);
+ overlay->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(app, mDispatcher, "Regular window",
+ ui::LogicalDisplayId::DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+ // Hover the mouse into the overlay
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Now, shrink the touchable region of the overlay! This will cause the cursor to suddenly have
+ // the regular window as the touch target
+ overlay->setTouchableRegion(Region({0, 0, 0, 0}));
+ mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ // Now we can click with the mouse. The click should go into the regular window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ overlay->consumeMotionEvent(WithMotionAction(ACTION_OUTSIDE));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+}
+
+/**
+ * Similar to above, but also has a spy on top that also catches the HOVER
+ * events. Also, instead of ACTION_DOWN, we are continuing to send the hovering
+ * stream to ensure that the spy receives hover events correctly.
+ */
+TEST_F(InputDispatcherTest, MouseClickUnderShrinkingTrustedOverlayWithSpy) {
+ std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(app, mDispatcher, "Spy", ui::LogicalDisplayId::DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 200, 200));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> overlay = sp<FakeWindowHandle>::make(app, mDispatcher, "Trusted overlay",
+ ui::LogicalDisplayId::DEFAULT);
+ overlay->setTrustedOverlay(true);
+ overlay->setWatchOutsideTouch(true);
+ overlay->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(app, mDispatcher, "Regular window",
+ ui::LogicalDisplayId::DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+ // Hover the mouse into the overlay
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(110))
+ .build());
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Now, shrink the touchable region of the overlay! This will cause the cursor to suddenly have
+ // the regular window as the touch target
+ overlay->setTouchableRegion(Region({0, 0, 0, 0}));
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ // Now we can click with the mouse. The click should go into the regular window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(110))
+ .build());
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
+ overlay->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+}
+
using InputDispatcherMultiDeviceTest = InputDispatcherTest;
/**
@@ -5012,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);
@@ -5030,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));
}
/**
@@ -9091,6 +9183,7 @@
protected:
static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms;
static constexpr std::chrono::nanoseconds KEY_REPEAT_DELAY = 40ms;
+ static constexpr bool KEY_REPEAT_ENABLED = true;
std::shared_ptr<FakeApplicationHandle> mApp;
sp<FakeWindowHandle> mWindow;
@@ -9098,7 +9191,8 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
- mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY);
+ mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY,
+ KEY_REPEAT_ENABLED);
setUpWindow();
}
@@ -9247,6 +9341,24 @@
expectKeyRepeatOnce(3);
}
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_NoRepeatWhenKeyRepeatDisabled) {
+ SCOPED_FLAG_OVERRIDE(keyboard_repeat_keys, true);
+ static constexpr std::chrono::milliseconds KEY_NO_REPEAT_ASSERTION_TIMEOUT = 100ms;
+
+ mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY,
+ /*repeatKeyEnabled=*/false);
+ sendAndConsumeKeyDown(/*deviceId=*/1);
+
+ ASSERT_GT(KEY_NO_REPEAT_ASSERTION_TIMEOUT, KEY_REPEAT_TIMEOUT)
+ << "Ensure the check for no key repeats extends beyond the repeat timeout duration.";
+ ASSERT_GT(KEY_NO_REPEAT_ASSERTION_TIMEOUT, KEY_REPEAT_DELAY)
+ << "Ensure the check for no key repeats extends beyond the repeat delay duration.";
+
+ // No events should be returned if key repeat is turned off.
+ // Wait for KEY_NO_REPEAT_ASSERTION_TIMEOUT to return no events to ensure key repeat disabled.
+ mWindow->assertNoEvents(KEY_NO_REPEAT_ASSERTION_TIMEOUT);
+}
+
/* Test InputDispatcher for MultiDisplay */
class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
public:
@@ -12776,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,
@@ -12814,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 4a9e893..6c8b65c 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -3330,11 +3330,11 @@
TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) {
mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0);
- mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B);
KeyboardInputMapper& mapper =
constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+ mFakeEventHub->setKeyRemapping(EVENTHUB_ID, {{AKEYCODE_A, AKEYCODE_B}});
// Key down by scan code.
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
NotifyKeyArgs args;
@@ -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/InputTracingTest.cpp b/services/inputflinger/tests/InputTracingTest.cpp
index 2ccd93e..3cc4bdd 100644
--- a/services/inputflinger/tests/InputTracingTest.cpp
+++ b/services/inputflinger/tests/InputTracingTest.cpp
@@ -133,8 +133,8 @@
mDispatcher->setFocusedWindow(request);
}
- void tapAndExpect(const std::vector<const sp<FakeWindowHandle>>& windows,
- Level inboundTraceLevel, Level dispatchTraceLevel, InputTraceSession& s) {
+ void tapAndExpect(const std::vector<sp<FakeWindowHandle>>& windows, Level inboundTraceLevel,
+ Level dispatchTraceLevel, InputTraceSession& s) {
const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110))
.build();
@@ -156,7 +156,7 @@
}
}
- void keypressAndExpect(const std::vector<const sp<FakeWindowHandle>>& windows,
+ void keypressAndExpect(const std::vector<sp<FakeWindowHandle>>& windows,
Level inboundTraceLevel, Level dispatchTraceLevel,
InputTraceSession& s) {
const auto down = KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build();
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 5a3d79d..a43e4e4 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -97,7 +97,8 @@
MOCK_METHOD(bool, hasRelativeAxis, (int32_t deviceId, int axis), (const));
MOCK_METHOD(bool, hasInputProperty, (int32_t deviceId, int property), (const));
MOCK_METHOD(bool, hasMscEvent, (int32_t deviceId, int mscEvent), (const));
- MOCK_METHOD(void, addKeyRemapping, (int32_t deviceId, int fromKeyCode, int toKeyCode), (const));
+ MOCK_METHOD(void, setKeyRemapping,
+ (int32_t deviceId, (const std::map<int32_t, int32_t>& keyRemapping)), (const));
MOCK_METHOD(status_t, mapKey,
(int32_t deviceId, int scanCode, int usageCode, int32_t metaState,
int32_t* outKeycode, int32_t* outMetaState, uint32_t* outFlags),
@@ -245,10 +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, addKeyRemapping, (int32_t fromKeyCode, int32_t toKeyCode), ());
-
MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ());
MOCK_METHOD(void, bumpGeneration, (), ());
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index 5650286..3f14c23 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -61,7 +61,6 @@
InputEventTimeline getTestTimeline() {
InputEventTimeline t(
- /*isDown=*/false,
/*eventTime=*/2,
/*readTime=*/3,
/*vendorId=*/0,
@@ -88,7 +87,7 @@
connection1 = sp<BBinder>::make();
connection2 = sp<BBinder>::make();
- mTracker = std::make_unique<LatencyTracker>(this);
+ mTracker = std::make_unique<LatencyTracker>(*this);
setDefaultInputDeviceInfo(*mTracker);
}
void TearDown() override {}
@@ -107,6 +106,8 @@
void processTimeline(const InputEventTimeline& timeline) override {
mReceivedTimelines.push_back(timeline);
}
+ void pushLatencyStatistics() override {}
+ std::string dump(const char* prefix) const { return ""; };
std::deque<InputEventTimeline> mReceivedTimelines;
};
@@ -174,7 +175,7 @@
AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
triggerEventReporting(/*eventTime=*/2);
assertReceivedTimeline(
- InputEventTimeline{/*isDown=*/false, /*eventTime=*/2,
+ InputEventTimeline{/*eventTime=*/2,
/*readTime=*/3, /*vendorId=*/0, /*productID=*/0,
/*sources=*/{InputDeviceUsageSource::UNKNOWN},
/*inputEventActionType=*/InputEventActionType::UNKNOWN_INPUT_EVENT});
@@ -226,7 +227,6 @@
TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) {
constexpr nsecs_t inputEventId = 1;
constexpr nsecs_t readTime = 3; // does not matter for this test
- constexpr bool isDown = false; // does not matter for this test
// In the following 2 calls to trackListener, the inputEventId's are the same, but event times
// are different.
@@ -246,7 +246,6 @@
TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) {
constexpr int32_t inputEventId1 = 1;
InputEventTimeline timeline1(
- /*isDown*/ false,
/*eventTime*/ 2,
/*readTime*/ 3,
/*vendorId=*/0,
@@ -264,7 +263,6 @@
constexpr int32_t inputEventId2 = 10;
InputEventTimeline timeline2(
- /*isDown=*/false,
/*eventTime=*/20,
/*readTime=*/30,
/*vendorId=*/0,
@@ -317,9 +315,9 @@
/*deviceId=*/DEVICE_ID,
/*sources=*/{InputDeviceUsageSource::UNKNOWN},
AMOTION_EVENT_ACTION_CANCEL, InputEventType::MOTION);
- expectedTimelines.push_back(InputEventTimeline{timeline.isDown, timeline.eventTime,
- timeline.readTime, timeline.vendorId,
- timeline.productId, timeline.sources,
+ expectedTimelines.push_back(InputEventTimeline{timeline.eventTime, timeline.readTime,
+ timeline.vendorId, timeline.productId,
+ timeline.sources,
timeline.inputEventActionType});
}
// Now, complete the first event that was sent.
@@ -350,10 +348,9 @@
{InputDeviceUsageSource::UNKNOWN}, AMOTION_EVENT_ACTION_CANCEL,
InputEventType::MOTION);
triggerEventReporting(expected.eventTime);
- assertReceivedTimeline(InputEventTimeline{expected.isDown, expected.eventTime,
- expected.readTime, expected.vendorId,
- expected.productId, expected.sources,
- expected.inputEventActionType});
+ assertReceivedTimeline(InputEventTimeline{expected.eventTime, expected.readTime,
+ expected.vendorId, expected.productId,
+ expected.sources, expected.inputEventActionType});
}
/**
@@ -364,7 +361,7 @@
TEST_F(LatencyTrackerTest, TrackListenerCheck_DeviceInfoFieldsInputEventTimeline) {
constexpr int32_t inputEventId = 1;
InputEventTimeline timeline(
- /*isDown*/ false, /*eventTime*/ 2, /*readTime*/ 3,
+ /*eventTime*/ 2, /*readTime*/ 3,
/*vendorId=*/50, /*productId=*/60,
/*sources=*/
{InputDeviceUsageSource::TOUCHSCREEN, InputDeviceUsageSource::STYLUS_DIRECT},
@@ -390,37 +387,37 @@
constexpr int32_t inputEventId = 1;
// Create timelines for different event types (Motion, Key)
InputEventTimeline motionDownTimeline(
- /*isDown*/ true, /*eventTime*/ 2, /*readTime*/ 3,
+ /*eventTime*/ 2, /*readTime*/ 3,
/*vendorId*/ 0, /*productId*/ 0,
/*sources*/ {InputDeviceUsageSource::UNKNOWN},
/*inputEventActionType*/ InputEventActionType::MOTION_ACTION_DOWN);
InputEventTimeline motionMoveTimeline(
- /*isDown*/ false, /*eventTime*/ 4, /*readTime*/ 5,
+ /*eventTime*/ 4, /*readTime*/ 5,
/*vendorId*/ 0, /*productId*/ 0,
/*sources*/ {InputDeviceUsageSource::UNKNOWN},
/*inputEventActionType*/ InputEventActionType::MOTION_ACTION_MOVE);
InputEventTimeline motionUpTimeline(
- /*isDown*/ false, /*eventTime*/ 6, /*readTime*/ 7,
+ /*eventTime*/ 6, /*readTime*/ 7,
/*vendorId*/ 0, /*productId*/ 0,
/*sources*/ {InputDeviceUsageSource::UNKNOWN},
/*inputEventActionType*/ InputEventActionType::MOTION_ACTION_UP);
InputEventTimeline keyDownTimeline(
- /*isDown*/ false, /*eventTime*/ 8, /*readTime*/ 9,
+ /*eventTime*/ 8, /*readTime*/ 9,
/*vendorId*/ 0, /*productId*/ 0,
/*sources*/ {InputDeviceUsageSource::UNKNOWN},
/*inputEventActionType*/ InputEventActionType::KEY);
InputEventTimeline keyUpTimeline(
- /*isDown*/ false, /*eventTime*/ 10, /*readTime*/ 11,
+ /*eventTime*/ 10, /*readTime*/ 11,
/*vendorId*/ 0, /*productId*/ 0,
/*sources*/ {InputDeviceUsageSource::UNKNOWN},
/*inputEventActionType*/ InputEventActionType::KEY);
InputEventTimeline unknownTimeline(
- /*isDown*/ false, /*eventTime*/ 12, /*readTime*/ 13,
+ /*eventTime*/ 12, /*readTime*/ 13,
/*vendorId*/ 0, /*productId*/ 0,
/*sources*/ {InputDeviceUsageSource::UNKNOWN},
/*inputEventActionType*/ InputEventActionType::UNKNOWN_INPUT_EVENT);
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 18222dd..411c7ba 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -978,6 +978,36 @@
assertPointerControllerRemoved(pc);
}
+/**
+ * When both "show touches" and "stylus hover icons" are enabled, if the app doesn't specify an
+ * icon for the hovering stylus, fall back to using the spot hover icon.
+ */
+TEST_F(PointerChoreographerTest, ShowTouchesOverridesUnspecifiedStylusIcon) {
+ mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.setStylusPointerIconEnabled(true);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+ DISPLAY_ID)}});
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS)
+ .pointer(STYLUS_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::STYLUS);
+
+ mChoreographer.setPointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED, DISPLAY_ID, DEVICE_ID);
+ pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER);
+
+ mChoreographer.setPointerIcon(PointerIconStyle::TYPE_ARROW, DISPLAY_ID, DEVICE_ID);
+ pc->assertPointerIconSet(PointerIconStyle::TYPE_ARROW);
+
+ mChoreographer.setPointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED, DISPLAY_ID, DEVICE_ID);
+ pc->assertPointerIconSet(PointerIconStyle::TYPE_SPOT_HOVER);
+}
+
using StylusFixtureParam =
std::tuple</*name*/ std::string_view, /*source*/ uint32_t, ControllerType>;
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/TestConstants.h b/services/inputflinger/tests/TestConstants.h
index 082bbb8..d2337dd 100644
--- a/services/inputflinger/tests/TestConstants.h
+++ b/services/inputflinger/tests/TestConstants.h
@@ -25,7 +25,10 @@
using std::chrono_literals::operator""ms;
// Timeout for waiting for an input device to be added and processed
-static constexpr std::chrono::duration ADD_INPUT_DEVICE_TIMEOUT = 500ms;
+static constexpr std::chrono::duration ADD_INPUT_DEVICE_TIMEOUT = 5000ms;
+
+// Timeout for asserting that an input device change did not occur
+static constexpr std::chrono::duration INPUT_DEVICES_DIDNT_CHANGE_TIMEOUT = 100ms;
// Timeout for waiting for an expected event
static constexpr std::chrono::duration WAIT_TIMEOUT = 100ms;
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index f3be041..6fa3365 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -615,7 +615,12 @@
explicit WithPointerIdMatcher(size_t index, int32_t pointerId)
: mIndex(index), mPointerId(pointerId) {}
- bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
+ bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream* os) const {
+ if (mIndex >= args.pointerCoords.size()) {
+ *os << "Pointer index " << mIndex << " is out of bounds";
+ return false;
+ }
+
return args.pointerProperties[mIndex].id == mPointerId;
}
@@ -646,21 +651,51 @@
return (isnan(x) ? isnan(argX) : x == argX) && (isnan(y) ? isnan(argY) : y == argY);
}
-MATCHER_P2(WithRelativeMotion, x, y, "InputEvent with specified relative motion") {
- const auto argX = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const auto argY = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- *result_listener << "expected relative motion (" << x << ", " << y << "), but got (" << argX
- << ", " << argY << ")";
- return argX == x && argY == y;
+/// Relative motion matcher
+class WithRelativeMotionMatcher {
+public:
+ using is_gtest_matcher = void;
+ explicit WithRelativeMotionMatcher(size_t pointerIndex, float relX, float relY)
+ : mPointerIndex(pointerIndex), mRelX(relX), mRelY(relY) {}
+
+ bool MatchAndExplain(const NotifyMotionArgs& event, std::ostream* os) const {
+ if (mPointerIndex >= event.pointerCoords.size()) {
+ *os << "Pointer index " << mPointerIndex << " is out of bounds";
+ return false;
+ }
+
+ const PointerCoords& coords = event.pointerCoords[mPointerIndex];
+ bool matches = mRelX == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X) &&
+ mRelY == coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ if (!matches) {
+ *os << "expected relative motion (" << mRelX << ", " << mRelY << ") at pointer index "
+ << mPointerIndex << ", but got ("
+ << coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X) << ", "
+ << coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y) << ")";
+ }
+ return matches;
+ }
+
+ void DescribeTo(std::ostream* os) const {
+ *os << "with relative motion (" << mRelX << ", " << mRelY << ") at pointer index "
+ << mPointerIndex;
+ }
+
+ void DescribeNegationTo(std::ostream* os) const { *os << "wrong relative motion"; }
+
+private:
+ const size_t mPointerIndex;
+ const float mRelX;
+ const float mRelY;
+};
+
+inline WithRelativeMotionMatcher WithRelativeMotion(float relX, float relY) {
+ return WithRelativeMotionMatcher(0, relX, relY);
}
-MATCHER_P3(WithPointerRelativeMotion, pointer, x, y,
- "InputEvent with specified relative motion for pointer") {
- const auto argX = arg.pointerCoords[pointer].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const auto argY = arg.pointerCoords[pointer].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- *result_listener << "expected pointer " << pointer << " to have relative motion (" << x << ", "
- << y << "), but got (" << argX << ", " << argY << ")";
- return argX == x && argY == y;
+inline WithRelativeMotionMatcher WithPointerRelativeMotion(size_t pointerIndex, float relX,
+ float relY) {
+ return WithRelativeMotionMatcher(pointerIndex, relX, relY);
}
MATCHER_P3(WithGestureOffset, dx, dy, epsilon,
@@ -767,10 +802,14 @@
return argToolType == toolType;
}
-MATCHER_P2(WithPointerToolType, pointer, toolType,
+MATCHER_P2(WithPointerToolType, pointerIndex, toolType,
"InputEvent with specified tool type for pointer") {
- const auto argToolType = arg.pointerProperties[pointer].toolType;
- *result_listener << "expected pointer " << pointer << " to have tool type "
+ if (std::cmp_greater_equal(pointerIndex, arg.getPointerCount())) {
+ *result_listener << "Pointer index " << pointerIndex << " is out of bounds";
+ return false;
+ }
+ const auto argToolType = arg.pointerProperties[pointerIndex].toolType;
+ *result_listener << "expected pointer " << pointerIndex << " to have tool type "
<< ftl::enum_string(toolType) << ", but got " << ftl::enum_string(argToolType);
return argToolType == toolType;
}
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 3e4a19b..5442a65 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -155,10 +155,6 @@
return reader->getLightPlayerId(deviceId, lightId);
}
- void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const {
- reader->addKeyRemapping(deviceId, fromKeyCode, toKeyCode);
- }
-
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
return reader->getKeyCodeForKeyLocation(deviceId, locationKeyCode);
}
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/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
index 80c2213..908fa40 100644
--- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -38,6 +38,10 @@
connectionTimeline.isComplete();
}
};
+
+ void pushLatencyStatistics() override {}
+
+ std::string dump(const char* prefix) const { return ""; };
};
static sp<IBinder> getConnectionToken(FuzzedDataProvider& fdp,
@@ -53,7 +57,7 @@
FuzzedDataProvider fdp(data, size);
EmptyProcessor emptyProcessor;
- LatencyTracker tracker(&emptyProcessor);
+ LatencyTracker tracker(emptyProcessor);
// Make some pre-defined tokens to ensure that some timelines are complete.
std::array<sp<IBinder> /*token*/, 10> predefinedTokens;
@@ -71,7 +75,7 @@
const DeviceId deviceId = fdp.ConsumeIntegral<int32_t>();
std::set<InputDeviceUsageSource> sources = {
fdp.ConsumeEnum<InputDeviceUsageSource>()};
- int32_t inputEventActionType = fdp.ConsumeIntegral<int32_t>();
+ const int32_t inputEventActionType = fdp.ConsumeIntegral<int32_t>();
const InputEventType inputEventType = fdp.ConsumeEnum<InputEventType>();
tracker.trackListener(inputEventId, eventTime, readTime, deviceId, sources,
inputEventActionType, inputEventType);
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index ddc3310..fa8270a 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -202,7 +202,8 @@
int32_t getSwitchState(int32_t deviceId, int32_t sw) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
- void addKeyRemapping(int32_t deviceId, int32_t fromKeyCode, int32_t toKeyCode) const override {}
+ void setKeyRemapping(int32_t deviceId,
+ const std::map<int32_t, int32_t>& keyRemapping) const override {}
int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const override {
return mFdp->ConsumeIntegral<int32_t>();
}
@@ -283,6 +284,7 @@
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {}
void notifyTouchpadHardwareState(const SelfContainedHardwareState& schs,
int32_t deviceId) override {}
+ void notifyTouchpadGestureInfo(GestureType type, int32_t deviceId) override {}
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
const std::optional<KeyboardLayoutInfo> layoutInfo) override {
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index f4b0265..7b2596a 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -52,6 +52,7 @@
"-Wall",
"-Werror",
"-Wextra",
+ "-Wthread-safety",
"-fvisibility=hidden",
],
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index f56642b..0d00642 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -711,14 +711,17 @@
if (err == OK && isSensorCapped) {
if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) ||
!isRateCappedBasedOnPermission()) {
+ Mutex::Autolock _l(mConnectionLock);
mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
} else {
+ Mutex::Autolock _l(mConnectionLock);
mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
}
}
} else {
err = mService->disable(this, handle);
+ Mutex::Autolock _l(mConnectionLock);
mMicSamplingPeriodBackup.erase(handle);
}
return err;
@@ -750,8 +753,10 @@
if (ret == OK && isSensorCapped) {
if ((requestedSamplingPeriodNs >= SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS) ||
!isRateCappedBasedOnPermission()) {
+ Mutex::Autolock _l(mConnectionLock);
mMicSamplingPeriodBackup[handle] = requestedSamplingPeriodNs;
} else {
+ Mutex::Autolock _l(mConnectionLock);
mMicSamplingPeriodBackup[handle] = SENSOR_SERVICE_CAPPED_SAMPLING_PERIOD_NS;
}
}
diff --git a/services/sensorservice/SensorEventConnection.h b/services/sensorservice/SensorEventConnection.h
index 6a98a40..bb8733d 100644
--- a/services/sensorservice/SensorEventConnection.h
+++ b/services/sensorservice/SensorEventConnection.h
@@ -199,7 +199,8 @@
// valid mapping for sensors that require a permission in order to reduce the lookup time.
std::unordered_map<int32_t, int32_t> mHandleToAppOp;
// Mapping of sensor handles to its rate before being capped by the mic toggle.
- std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup;
+ std::unordered_map<int, nsecs_t> mMicSamplingPeriodBackup
+ GUARDED_BY(mConnectionLock);
userid_t mUserId;
std::optional<bool> mIsRateCappedBasedOnPermission;
diff --git a/services/sensorservice/SensorRegistrationInfo.h b/services/sensorservice/SensorRegistrationInfo.h
index dc9e821..a8a773a 100644
--- a/services/sensorservice/SensorRegistrationInfo.h
+++ b/services/sensorservice/SensorRegistrationInfo.h
@@ -38,10 +38,11 @@
mActivated = false;
}
- SensorRegistrationInfo(int32_t handle, const String8 &packageName,
- int64_t samplingRateNs, int64_t maxReportLatencyNs, bool activate) {
+ SensorRegistrationInfo(int32_t handle, const String8& packageName, int64_t samplingRateNs,
+ int64_t maxReportLatencyNs, bool activate, status_t registerStatus) {
mSensorHandle = handle;
mPackageName = packageName;
+ mRegisteredStatus = registerStatus;
mSamplingRateUs = static_cast<int64_t>(samplingRateNs/1000);
mMaxReportLatencyUs = static_cast<int64_t>(maxReportLatencyNs/1000);
@@ -60,28 +61,43 @@
return (info.mSensorHandle == INT32_MIN && info.mRealtimeSec == 0);
}
- // Dumpable interface
- virtual std::string dump() const override {
+ std::string dump(SensorService* sensorService) const {
struct tm* timeinfo = localtime(&mRealtimeSec);
const int8_t hour = static_cast<int8_t>(timeinfo->tm_hour);
const int8_t min = static_cast<int8_t>(timeinfo->tm_min);
const int8_t sec = static_cast<int8_t>(timeinfo->tm_sec);
std::ostringstream ss;
- ss << std::setfill('0') << std::setw(2) << static_cast<int>(hour) << ":"
- << std::setw(2) << static_cast<int>(min) << ":"
- << std::setw(2) << static_cast<int>(sec)
- << (mActivated ? " +" : " -")
- << " 0x" << std::hex << std::setw(8) << mSensorHandle << std::dec
- << std::setfill(' ') << " pid=" << std::setw(5) << mPid
- << " uid=" << std::setw(5) << mUid << " package=" << mPackageName;
- if (mActivated) {
- ss << " samplingPeriod=" << mSamplingRateUs << "us"
- << " batchingPeriod=" << mMaxReportLatencyUs << "us";
- };
+ ss << std::setfill('0') << std::setw(2) << static_cast<int>(hour) << ":" << std::setw(2)
+ << static_cast<int>(min) << ":" << std::setw(2) << static_cast<int>(sec)
+ << (mActivated ? " +" : " -") << " 0x" << std::hex << std::setw(8) << mSensorHandle;
+ ss << std::dec << std::setfill(' ') << " pid=" << std::setw(5) << mPid
+ << " uid=" << std::setw(5) << mUid;
+
+ std::string samplingRate =
+ mActivated ? std::to_string(mSamplingRateUs) : " N/A ";
+ std::string batchingInterval =
+ mActivated ? std::to_string(mMaxReportLatencyUs) : " N/A ";
+ ss << " samplingPeriod=" << std::setfill(' ') << std::setw(8)
+ << samplingRate << "us" << " batchingPeriod=" << std::setfill(' ')
+ << std::setw(8) << batchingInterval << "us"
+ << " result=" << statusToString(mRegisteredStatus)
+ << " (sensor, package): (" << std::setfill(' ') << std::left
+ << std::setw(27);
+
+ if (sensorService != nullptr) {
+ ss << sensorService->getSensorName(mSensorHandle);
+ } else {
+ ss << "null";
+ }
+ ss << ", " << mPackageName << ")";
+
return ss.str();
}
+ // Dumpable interface
+ virtual std::string dump() const override { return dump(static_cast<SensorService*>(nullptr)); }
+
/**
* Dump debugging information as android.service.SensorRegistrationInfoProto protobuf message
* using ProtoOutputStream.
@@ -110,6 +126,7 @@
int64_t mMaxReportLatencyUs;
bool mActivated;
time_t mRealtimeSec;
+ status_t mRegisteredStatus;
};
} // namespace android;
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 3895ffe..eabbb39 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -708,7 +708,7 @@
SENSOR_REGISTRATIONS_BUF_SIZE;
continue;
}
- result.appendFormat("%s\n", reg_info.dump().c_str());
+ result.appendFormat("%s\n", reg_info.dump(this).c_str());
currentIndex = (currentIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) %
SENSOR_REGISTRATIONS_BUF_SIZE;
} while(startIndex != currentIndex);
@@ -1583,7 +1583,11 @@
// Only 4 modes supported for a SensorEventConnection ... NORMAL, DATA_INJECTION,
// REPLAY_DATA_INJECTION and HAL_BYPASS_REPLAY_DATA_INJECTION
if (requestedMode != NORMAL && !isInjectionMode(requestedMode)) {
- return nullptr;
+ ALOGE(
+ "Failed to create sensor event connection: invalid request mode. "
+ "requestMode: %d",
+ requestedMode);
+ return nullptr;
}
resetTargetSdkVersionCache(opPackageName);
@@ -1591,8 +1595,19 @@
// To create a client in DATA_INJECTION mode to inject data, SensorService should already be
// operating in DI mode.
if (requestedMode == DATA_INJECTION) {
- if (mCurrentOperatingMode != DATA_INJECTION) return nullptr;
- if (!isAllowListedPackage(packageName)) return nullptr;
+ if (mCurrentOperatingMode != DATA_INJECTION) {
+ ALOGE(
+ "Failed to create sensor event connection: sensor service not in "
+ "DI mode when creating a client in DATA_INJECTION mode");
+ return nullptr;
+ }
+ if (!isAllowListedPackage(packageName)) {
+ ALOGE(
+ "Failed to create sensor event connection: package %s not in "
+ "allowed list for DATA_INJECTION mode",
+ packageName.c_str());
+ return nullptr;
+ }
}
uid_t uid = IPCThreadState::self()->getCallingUid();
@@ -2034,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);
@@ -2152,17 +2168,17 @@
sensor->getSensor().getRequiredAppOp() >= 0) {
connection->mHandleToAppOp[handle] = sensor->getSensor().getRequiredAppOp();
}
-
- mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
- SensorRegistrationInfo(handle, connection->getPackageName(),
- samplingPeriodNs, maxBatchReportLatencyNs, true);
- mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
}
if (err != NO_ERROR) {
// batch/activate has failed, reset our state.
cleanupWithoutDisableLocked(connection, handle);
}
+
+ mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
+ SensorRegistrationInfo(handle, connection->getPackageName(), samplingPeriodNs,
+ maxBatchReportLatencyNs, /*activate=*/ true, err);
+ mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
return err;
}
@@ -2175,13 +2191,10 @@
if (err == NO_ERROR) {
std::shared_ptr<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);
err = sensor != nullptr ? sensor->activate(connection.get(), false) : status_t(BAD_VALUE);
-
}
- if (err == NO_ERROR) {
- mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
- SensorRegistrationInfo(handle, connection->getPackageName(), 0, 0, false);
- mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
- }
+ mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =
+ SensorRegistrationInfo(handle, connection->getPackageName(), 0, 0, /*activate=*/ false, err);
+ mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
return err;
}
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 839fb7d..5c55ce7 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -51,7 +51,8 @@
MOCK_CONST_METHOD0(getMaxVirtualDisplayCount, size_t());
MOCK_CONST_METHOD0(getMaxVirtualDisplayDimension, size_t());
MOCK_METHOD3(allocateVirtualDisplay, bool(HalVirtualDisplayId, ui::Size, ui::PixelFormat*));
- MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
+ MOCK_METHOD3(allocatePhysicalDisplay,
+ void(hal::HWDisplayId, PhysicalDisplayId, std::optional<ui::Size>));
MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
MOCK_METHOD(status_t, getDeviceCompositionChanges,
@@ -151,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 20ae74a..77bd804 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -60,6 +60,7 @@
using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities;
using AidlHdrConversionCapability =
aidl::android::hardware::graphics::common::HdrConversionCapability;
+using AidlHdcpLevels = aidl::android::hardware::drm::HdcpLevels;
using AidlHdrConversionStrategy = aidl::android::hardware::graphics::common::HdrConversionStrategy;
using AidlOverlayProperties = aidl::android::hardware::graphics::composer3::OverlayProperties;
using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata;
@@ -223,6 +224,12 @@
return ::ndk::ScopedAStatus::ok();
}
+ ::ndk::ScopedAStatus onHdcpLevelsChanged(int64_t in_display,
+ const AidlHdcpLevels& levels) override {
+ mCallback.onComposerHalHdcpLevelsChanged(translate<Display>(in_display), levels);
+ return ::ndk::ScopedAStatus::ok();
+ }
+
private:
HWC2::ComposerCallback& mCallback;
};
@@ -1540,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)) {
@@ -1549,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 8e78af4..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(
@@ -675,6 +687,11 @@
}
});
}
+
+void Display::setPhysicalSizeInMm(std::optional<ui::Size> size) {
+ mPhysicalSize = size;
+}
+
} // namespace impl
// Layer methods
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index c2dc943..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>
@@ -67,6 +68,7 @@
namespace hal = android::hardware::graphics::composer::hal;
+using aidl::android::hardware::drm::HdcpLevels;
using aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
@@ -85,6 +87,7 @@
virtual void onComposerHalSeamlessPossible(hal::HWDisplayId) = 0;
virtual void onComposerHalVsyncIdle(hal::HWDisplayId) = 0;
virtual void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) = 0;
+ virtual void onComposerHalHdcpLevelsChanged(hal::HWDisplayId, const HdcpLevels& levels) = 0;
protected:
~ComposerCallback() = default;
@@ -103,6 +106,14 @@
virtual bool isVsyncPeriodSwitchSupported() const = 0;
virtual bool hasDisplayIdleTimerCapability() const = 0;
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>
@@ -180,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;
@@ -265,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;
@@ -283,6 +292,8 @@
bool hasDisplayIdleTimerCapability() const override;
void onLayerDestroyed(hal::HWLayerId layerId) override;
hal::Error getPhysicalDisplayOrientation(Hwc2::AidlTransform* outTransform) const override;
+ void setPhysicalSizeInMm(std::optional<ui::Size> size);
+ std::optional<ui::Size> getPhysicalSizeInMm() const override { return mPhysicalSize; }
private:
void loadDisplayCapabilities();
@@ -316,6 +327,8 @@
std::optional<
std::unordered_set<aidl::android::hardware::graphics::composer3::DisplayCapability>>
mDisplayCapabilities GUARDED_BY(mDisplayCapabilitiesMutex);
+ // Physical size in mm.
+ std::optional<ui::Size> mPhysicalSize;
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index c83e0da..e37c0ba 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -76,6 +76,7 @@
using aidl::android::hardware::graphics::common::HdrConversionStrategy;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
+using aidl::android::hardware::graphics::composer3::DisplayConfiguration;
using namespace std::string_literals;
namespace android {
@@ -222,8 +223,8 @@
return true;
}
-void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId,
- PhysicalDisplayId displayId) {
+void HWComposer::allocatePhysicalDisplay(hal::HWDisplayId hwcDisplayId, PhysicalDisplayId displayId,
+ std::optional<ui::Size> physicalSize) {
mPhysicalDisplayIdMap[hwcDisplayId] = displayId;
if (!mPrimaryHwcDisplayId) {
@@ -235,6 +236,7 @@
std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
hal::DisplayType::PHYSICAL);
newDisplay->setConnected(true);
+ newDisplay->setPhysicalSizeInMm(physicalSize);
displayData.hwcDisplay = std::move(newDisplay);
}
@@ -276,6 +278,47 @@
return getModesFromLegacyDisplayConfigs(hwcDisplayId);
}
+DisplayConfiguration::Dpi HWComposer::getEstimatedDotsPerInchFromSize(
+ uint64_t hwcDisplayId, const HWCDisplayMode& hwcMode) const {
+ if (!FlagManager::getInstance().correct_dpi_with_display_size()) {
+ return {-1, -1};
+ }
+ if (const auto displayId = toPhysicalDisplayId(hwcDisplayId)) {
+ if (const auto it = mDisplayData.find(displayId.value());
+ it != mDisplayData.end() && it->second.hwcDisplay->getPhysicalSizeInMm()) {
+ ui::Size size = it->second.hwcDisplay->getPhysicalSizeInMm().value();
+ if (hwcMode.width > 0 && hwcMode.height > 0 && size.width > 0 && size.height > 0) {
+ static constexpr float kMmPerInch = 25.4f;
+ return {hwcMode.width * kMmPerInch / size.width,
+ hwcMode.height * kMmPerInch / size.height};
+ }
+ }
+ }
+ return {-1, -1};
+}
+
+DisplayConfiguration::Dpi HWComposer::correctedDpiIfneeded(
+ DisplayConfiguration::Dpi dpi, DisplayConfiguration::Dpi estimatedDpi) const {
+ // hwc can be unreliable when it comes to dpi. A rough estimated dpi may yield better
+ // results. For instance, libdrm and bad edid may result in a dpi of {350, 290} for a
+ // 16:9 3840x2160 display, which would match a 4:3 aspect ratio.
+ // The logic here checks if hwc was able to provide some dpi, and if so if the dpi
+ // disparity between the axes is more reasonable than a rough estimate, otherwise use
+ // the estimated dpi as a corrected value.
+ if (estimatedDpi.x == -1 || estimatedDpi.y == -1) {
+ return dpi;
+ }
+ if (dpi.x == -1 || dpi.y == -1) {
+ return estimatedDpi;
+ }
+ if (std::min(dpi.x, dpi.y) != 0 && std::min(estimatedDpi.x, estimatedDpi.y) != 0 &&
+ abs(dpi.x - dpi.y) / std::min(dpi.x, dpi.y) >
+ abs(estimatedDpi.x - estimatedDpi.y) / std::min(estimatedDpi.x, estimatedDpi.y)) {
+ return estimatedDpi;
+ }
+ return dpi;
+}
+
std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromDisplayConfigurations(
uint64_t hwcDisplayId, int32_t maxFrameIntervalNs) const {
std::vector<hal::DisplayConfiguration> configs;
@@ -294,9 +337,16 @@
.configGroup = config.configGroup,
.vrrConfig = config.vrrConfig};
+ const DisplayConfiguration::Dpi estimatedDPI =
+ getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode);
if (config.dpi) {
- hwcMode.dpiX = config.dpi->x;
- hwcMode.dpiY = config.dpi->y;
+ const DisplayConfiguration::Dpi dpi =
+ correctedDpiIfneeded(config.dpi.value(), estimatedDPI);
+ hwcMode.dpiX = dpi.x;
+ hwcMode.dpiY = dpi.y;
+ } else if (estimatedDPI.x != -1 && estimatedDPI.y != -1) {
+ hwcMode.dpiX = estimatedDPI.x;
+ hwcMode.dpiY = estimatedDPI.y;
}
if (!mEnableVrrTimeout) {
@@ -328,12 +378,14 @@
const int32_t dpiX = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_X);
const int32_t dpiY = getAttribute(hwcDisplayId, configId, hal::Attribute::DPI_Y);
- if (dpiX != -1) {
- hwcMode.dpiX = static_cast<float>(dpiX) / 1000.f;
- }
- if (dpiY != -1) {
- hwcMode.dpiY = static_cast<float>(dpiY) / 1000.f;
- }
+ const DisplayConfiguration::Dpi hwcDpi =
+ DisplayConfiguration::Dpi{dpiX == -1 ? dpiY : dpiX / 1000.f,
+ dpiY == -1 ? dpiY : dpiY / 1000.f};
+ const DisplayConfiguration::Dpi estimatedDPI =
+ getEstimatedDotsPerInchFromSize(hwcDisplayId, hwcMode);
+ const DisplayConfiguration::Dpi dpi = correctedDpiIfneeded(hwcDpi, estimatedDPI);
+ hwcMode.dpiX = dpi.x;
+ hwcMode.dpiY = dpi.y;
modes.push_back(hwcMode);
}
@@ -535,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);
@@ -926,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);
@@ -984,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");
@@ -1072,6 +1119,8 @@
getDisplayIdentificationData(hwcDisplayId, &port, &data);
if (auto newInfo = parseDisplayIdentificationData(port, data)) {
info->deviceProductInfo = std::move(newInfo->deviceProductInfo);
+ info->preferredDetailedTimingDescriptor =
+ std::move(newInfo->preferredDetailedTimingDescriptor);
} else {
ALOGE("Failed to parse identification data for display %" PRIu64, hwcDisplayId);
}
@@ -1114,7 +1163,11 @@
}
if (!isConnected(info->id)) {
- allocatePhysicalDisplay(hwcDisplayId, info->id);
+ std::optional<ui::Size> size = std::nullopt;
+ if (info->preferredDetailedTimingDescriptor) {
+ size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
+ }
+ allocatePhysicalDisplay(hwcDisplayId, info->id, size);
}
return info;
}
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index dec4bfe..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 {
@@ -134,7 +138,8 @@
// supported by the HWC can be queried in advance, but allocation may fail for other reasons.
virtual bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) = 0;
- virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) = 0;
+ virtual void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId,
+ std::optional<ui::Size> physicalSize) = 0;
// Attempts to create a new layer on this display
virtual std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) = 0;
@@ -310,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 {
@@ -349,7 +351,8 @@
bool allocateVirtualDisplay(HalVirtualDisplayId, ui::Size, ui::PixelFormat*) override;
// Called from SurfaceFlinger, when the state for a new physical display needs to be recreated.
- void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId) override;
+ void allocatePhysicalDisplay(hal::HWDisplayId, PhysicalDisplayId,
+ std::optional<ui::Size> physicalSize) override;
// Attempts to create a new layer on this display
std::shared_ptr<HWC2::Layer> createLayer(HalDisplayId) override;
@@ -478,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;
@@ -532,6 +532,13 @@
std::optional<DisplayIdentificationInfo> onHotplugDisconnect(hal::HWDisplayId);
bool shouldIgnoreHotplugConnect(hal::HWDisplayId, bool hasDisplayIdentificationData) const;
+ aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi
+ getEstimatedDotsPerInchFromSize(uint64_t hwcDisplayId, const HWCDisplayMode& hwcMode) const;
+
+ aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi correctedDpiIfneeded(
+ aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi dpi,
+ aidl::android::hardware::graphics::composer3::DisplayConfiguration::Dpi estimatedDpi)
+ const;
std::vector<HWCDisplayMode> getModesFromDisplayConfigurations(uint64_t hwcDisplayId,
int32_t maxFrameIntervalNs) const;
std::vector<HWCDisplayMode> getModesFromLegacyDisplayConfigs(uint64_t hwcDisplayId) const;
@@ -562,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/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index 2a0ee5a..47b811b 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -38,6 +38,8 @@
using FrameTimelineEvent = perfetto::protos::pbzero::FrameTimelineEvent;
using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource;
+namespace {
+
void dumpTable(std::string& result, TimelineItem predictions, TimelineItem actuals,
const std::string& indent, PredictionState predictionState, nsecs_t baseTime) {
StringAppendF(&result, "%s", indent.c_str());
@@ -319,6 +321,16 @@
return minTime;
}
+bool shouldTraceForDataSource(const FrameTimelineDataSource::TraceContext& ctx, nsecs_t timestamp) {
+ if (auto ds = ctx.GetDataSourceLocked(); ds && ds->getStartTime() > timestamp) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
int64_t TraceCookieCounter::getCookieForTracing() {
return ++mTraceCookie;
}
@@ -697,6 +709,23 @@
jd.jankType = mJankType;
jd.frameIntervalNs =
(mRenderRate ? *mRenderRate : mDisplayFrameRenderRate).getPeriodNsecs();
+
+ if (mPredictionState == PredictionState::Valid) {
+ jd.scheduledAppFrameTimeNs = mPredictions.endTime - mPredictions.startTime;
+
+ // Using expected start, rather than actual, to measure the entire frame time. That is
+ // if the application starts the frame later than scheduled, include that delay in the
+ // frame time, as it usually means main thread being busy with non-rendering work.
+ if (mPresentState == PresentState::Dropped) {
+ jd.actualAppFrameTimeNs = mDropTime - mPredictions.startTime;
+ } else {
+ jd.actualAppFrameTimeNs = mActuals.endTime - mPredictions.startTime;
+ }
+ } else {
+ jd.scheduledAppFrameTimeNs = 0;
+ jd.actualAppFrameTimeNs = 0;
+ }
+
JankTracker::onJankData(mLayerId, jd);
}
}
@@ -709,15 +738,24 @@
classifyJankLocked(JankType::None, refreshRate, displayFrameRenderRate, nullptr);
}
-void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const {
+void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const {
int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ bool traced = false;
// Expected timeline start
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ const auto timestamp = mPredictions.startTime;
+ if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) {
+ // Do not trace packets started before tracing starts.
+ return;
+ }
+ traced = true;
+
std::scoped_lock lock(mMutex);
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime + monoBootOffset));
+ packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
auto* event = packet->set_frame_timeline_event();
auto* expectedSurfaceFrameStartEvent = event->set_expected_surface_frame_start();
@@ -731,42 +769,54 @@
expectedSurfaceFrameStartEvent->set_layer_name(mDebugName);
});
- // Expected timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- std::scoped_lock lock(mMutex);
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime + monoBootOffset));
+ if (traced) {
+ // Expected timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::scoped_lock lock(mMutex);
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime + monoBootOffset));
- auto* event = packet->set_frame_timeline_event();
- auto* expectedSurfaceFrameEndEvent = event->set_frame_end();
+ auto* event = packet->set_frame_timeline_event();
+ auto* expectedSurfaceFrameEndEvent = event->set_frame_end();
- expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie);
- });
+ expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie);
+ });
+ }
}
-void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const {
+void SurfaceFrame::traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const {
int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ bool traced = false;
// Actual timeline start
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ const auto timestamp = [&]() {
+ std::scoped_lock lock(mMutex);
+ // Actual start time is not yet available, so use expected start instead
+ if (mPredictionState == PredictionState::Expired) {
+ // If prediction is expired, we can't use the predicted start time. Instead, just
+ // use a start time a little earlier than the end time so that we have some info
+ // about this frame in the trace.
+ nsecs_t endTime =
+ (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime);
+ return endTime - kPredictionExpiredStartTimeDelta;
+ }
+
+ return mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime;
+ }();
+
+ if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) {
+ // Do not trace packets started before tracing starts.
+ return;
+ }
+ traced = true;
+
std::scoped_lock lock(mMutex);
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- // Actual start time is not yet available, so use expected start instead
- if (mPredictionState == PredictionState::Expired) {
- // If prediction is expired, we can't use the predicted start time. Instead, just use a
- // start time a little earlier than the end time so that we have some info about this
- // frame in the trace.
- nsecs_t endTime =
- (mPresentState == PresentState::Dropped ? mDropTime : mActuals.endTime);
- const auto timestamp = endTime - kPredictionExpiredStartTimeDelta;
- packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
- } else {
- const auto timestamp =
- mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime;
- packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
- }
+ packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
auto* event = packet->set_frame_timeline_event();
auto* actualSurfaceFrameStartEvent = event->set_actual_surface_frame_start();
@@ -795,28 +845,31 @@
actualSurfaceFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType));
});
- // Actual timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- std::scoped_lock lock(mMutex);
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- if (mPresentState == PresentState::Dropped) {
- packet->set_timestamp(static_cast<uint64_t>(mDropTime + monoBootOffset));
- } else {
- packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime + monoBootOffset));
- }
+ if (traced) {
+ // Actual timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ std::scoped_lock lock(mMutex);
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ if (mPresentState == PresentState::Dropped) {
+ packet->set_timestamp(static_cast<uint64_t>(mDropTime + monoBootOffset));
+ } else {
+ packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime + monoBootOffset));
+ }
- auto* event = packet->set_frame_timeline_event();
- auto* actualSurfaceFrameEndEvent = event->set_frame_end();
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualSurfaceFrameEndEvent = event->set_frame_end();
- actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie);
- });
+ actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie);
+ });
+ }
}
/**
* TODO(b/178637512): add inputEventId to the perfetto trace.
*/
-void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset) const {
+void SurfaceFrame::trace(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const {
if (mToken == FrameTimelineInfo::INVALID_VSYNC_ID ||
displayFrameToken == FrameTimelineInfo::INVALID_VSYNC_ID) {
// No packets can be traced with a missing token.
@@ -825,9 +878,9 @@
if (getPredictionState() != PredictionState::Expired) {
// Expired predictions have zeroed timestamps. This cannot be used in any meaningful way in
// a trace.
- tracePredictions(displayFrameToken, monoBootOffset);
+ tracePredictions(displayFrameToken, monoBootOffset, filterFramesBeforeTraceStarts);
}
- traceActuals(displayFrameToken, monoBootOffset);
+ traceActuals(displayFrameToken, monoBootOffset, filterFramesBeforeTraceStarts);
}
namespace impl {
@@ -853,8 +906,12 @@
}
FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
- JankClassificationThresholds thresholds, bool useBootTimeClock)
+ JankClassificationThresholds thresholds, bool useBootTimeClock,
+ bool filterFramesBeforeTraceStarts)
: mUseBootTimeClock(useBootTimeClock),
+ mFilterFramesBeforeTraceStarts(
+ FlagManager::getInstance().filter_frames_before_trace_starts() &&
+ filterFramesBeforeTraceStarts),
mMaxDisplayFrames(kDefaultMaxDisplayFrames),
mTimeStats(std::move(timeStats)),
mSurfaceFlingerPid(surfaceFlingerPid),
@@ -1137,16 +1194,23 @@
}
}
-void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid,
- nsecs_t monoBootOffset) const {
+void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const {
int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ bool traced = false;
// Expected timeline start
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ const auto timestamp = mSurfaceFlingerPredictions.startTime;
+ if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) {
+ // Do not trace packets started before tracing starts.
+ return;
+ }
+ traced = true;
+
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(
- static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime + monoBootOffset));
+ packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
auto* event = packet->set_frame_timeline_event();
auto* expectedDisplayFrameStartEvent = event->set_expected_display_frame_start();
@@ -1157,22 +1221,25 @@
expectedDisplayFrameStartEvent->set_pid(surfaceFlingerPid);
});
- // Expected timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(
- static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime + monoBootOffset));
+ if (traced) {
+ // Expected timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(
+ static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime + monoBootOffset));
- auto* event = packet->set_frame_timeline_event();
- auto* expectedDisplayFrameEndEvent = event->set_frame_end();
+ auto* event = packet->set_frame_timeline_event();
+ auto* expectedDisplayFrameEndEvent = event->set_frame_end();
- expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie);
- });
+ expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie);
+ });
+ }
}
void FrameTimeline::DisplayFrame::addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
- nsecs_t previousPredictionPresentTime) const {
+ nsecs_t previousPredictionPresentTime,
+ bool filterFramesBeforeTraceStarts) const {
nsecs_t skippedFrameStartTime = 0, skippedFramePresentTime = 0;
const constexpr float kThresh = 0.5f;
const constexpr float kRange = 1.5f;
@@ -1188,7 +1255,7 @@
(static_cast<float>(previousPredictionPresentTime) +
kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) &&
// sf skipped frame is not considered if app is self janked
- !surfaceFrame->isSelfJanky()) {
+ surfaceFrame->getJankType() != JankType::None && !surfaceFrame->isSelfJanky()) {
skippedFrameStartTime = surfaceFrame->getPredictions().endTime;
skippedFramePresentTime = surfaceFrame->getPredictions().presentTime;
break;
@@ -1198,9 +1265,17 @@
// add slice
if (skippedFrameStartTime != 0 && skippedFramePresentTime != 0) {
int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ bool traced = false;
// Actual timeline start
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ if (filterFramesBeforeTraceStarts &&
+ !shouldTraceForDataSource(ctx, skippedFrameStartTime)) {
+ // Do not trace packets started before tracing starts.
+ return;
+ }
+ traced = true;
+
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
packet->set_timestamp(static_cast<uint64_t>(skippedFrameStartTime + monoBootOffset));
@@ -1221,30 +1296,40 @@
actualDisplayFrameStartEvent->set_jank_severity_type(toProto(JankSeverityType::None));
});
- // Actual timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(static_cast<uint64_t>(skippedFramePresentTime + monoBootOffset));
+ if (traced) {
+ // Actual timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(
+ static_cast<uint64_t>(skippedFramePresentTime + monoBootOffset));
- auto* event = packet->set_frame_timeline_event();
- auto* actualDisplayFrameEndEvent = event->set_frame_end();
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameEndEvent = event->set_frame_end();
- actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
- });
+ actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
+ });
+ }
}
}
-void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid,
- nsecs_t monoBootOffset) const {
+void FrameTimeline::DisplayFrame::traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const {
int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing();
+ bool traced = false;
// Actual timeline start
FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ const auto timestamp = mSurfaceFlingerActuals.startTime;
+ if (filterFramesBeforeTraceStarts && !shouldTraceForDataSource(ctx, timestamp)) {
+ // Do not trace packets started before tracing starts.
+ return;
+ }
+ traced = true;
+
auto packet = ctx.NewTracePacket();
packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(
- static_cast<uint64_t>(mSurfaceFlingerActuals.startTime + monoBootOffset));
+ packet->set_timestamp(static_cast<uint64_t>(timestamp + monoBootOffset));
auto* event = packet->set_frame_timeline_event();
auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start();
@@ -1263,22 +1348,25 @@
actualDisplayFrameStartEvent->set_jank_severity_type(toProto(mJankSeverityType));
});
- // Actual timeline end
- FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
- auto packet = ctx.NewTracePacket();
- packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
- packet->set_timestamp(
- static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime + monoBootOffset));
+ if (traced) {
+ // Actual timeline end
+ FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) {
+ auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME);
+ packet->set_timestamp(
+ static_cast<uint64_t>(mSurfaceFlingerActuals.presentTime + monoBootOffset));
- auto* event = packet->set_frame_timeline_event();
- auto* actualDisplayFrameEndEvent = event->set_frame_end();
+ auto* event = packet->set_frame_timeline_event();
+ auto* actualDisplayFrameEndEvent = event->set_frame_end();
- actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
- });
+ actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie);
+ });
+ }
}
nsecs_t FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
- nsecs_t previousPredictionPresentTime) const {
+ nsecs_t previousPredictionPresentTime,
+ bool filterFramesBeforeTraceStarts) const {
if (mSurfaceFrames.empty()) {
// We don't want to trace display frames without any surface frames updates as this cannot
// be janky
@@ -1294,16 +1382,17 @@
if (mPredictionState == PredictionState::Valid) {
// Expired and unknown predictions have zeroed timestamps. This cannot be used in any
// meaningful way in a trace.
- tracePredictions(surfaceFlingerPid, monoBootOffset);
+ tracePredictions(surfaceFlingerPid, monoBootOffset, filterFramesBeforeTraceStarts);
}
- traceActuals(surfaceFlingerPid, monoBootOffset);
+ traceActuals(surfaceFlingerPid, monoBootOffset, filterFramesBeforeTraceStarts);
for (auto& surfaceFrame : mSurfaceFrames) {
- surfaceFrame->trace(mToken, monoBootOffset);
+ surfaceFrame->trace(mToken, monoBootOffset, filterFramesBeforeTraceStarts);
}
if (FlagManager::getInstance().add_sf_skipped_frames_to_trace()) {
- addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime);
+ addSkippedFrame(surfaceFlingerPid, monoBootOffset, previousPredictionPresentTime,
+ filterFramesBeforeTraceStarts);
}
return mSurfaceFlingerPredictions.presentTime;
}
@@ -1397,8 +1486,9 @@
const nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID;
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
- mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
- mPreviousPredictionPresentTime);
+ mPreviousPredictionPresentTime =
+ displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
+ mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts);
mPendingPresentFences.erase(mPendingPresentFences.begin());
}
@@ -1414,8 +1504,9 @@
auto& displayFrame = pendingPresentFence.second;
displayFrame->onPresent(signalTime, mPreviousActualPresentTime);
- mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
- mPreviousPredictionPresentTime);
+ mPreviousPredictionPresentTime =
+ displayFrame->trace(mSurfaceFlingerPid, monoBootOffset,
+ mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts);
mPreviousActualPresentTime = signalTime;
mPendingPresentFences.erase(mPendingPresentFences.begin() + static_cast<int>(i));
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 94cfcb4..cffb61e 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -214,7 +214,8 @@
// enabled. The displayFrameToken is needed to link the SurfaceFrame to the corresponding
// DisplayFrame at the trace processor side. monoBootOffset is the difference
// between SYSTEM_TIME_BOOTTIME and SYSTEM_TIME_MONOTONIC.
- void trace(int64_t displayFrameToken, nsecs_t monoBootOffset) const;
+ void trace(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const;
// Getter functions used only by FrameTimelineTests and SurfaceFrame internally
TimelineItem getActuals() const;
@@ -234,8 +235,10 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(2ms).count();
private:
- void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const;
- void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const;
+ void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const;
+ void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const;
void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
Fps displayFrameRenderRate, nsecs_t* outDeadlineDelta) REQUIRES(mMutex);
@@ -367,9 +370,15 @@
class FrameTimeline : public android::frametimeline::FrameTimeline {
public:
class FrameTimelineDataSource : public perfetto::DataSource<FrameTimelineDataSource> {
- void OnSetup(const SetupArgs&) override{};
- void OnStart(const StartArgs&) override{};
- void OnStop(const StopArgs&) override{};
+ public:
+ nsecs_t getStartTime() const { return mTraceStartTime; }
+
+ private:
+ void OnSetup(const SetupArgs&) override {};
+ void OnStart(const StartArgs&) override { mTraceStartTime = systemTime(); };
+ void OnStop(const StopArgs&) override {};
+
+ nsecs_t mTraceStartTime = 0;
};
/*
@@ -390,7 +399,8 @@
// is enabled. monoBootOffset is the difference between SYSTEM_TIME_BOOTTIME
// and SYSTEM_TIME_MONOTONIC.
nsecs_t trace(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
- nsecs_t previousPredictionPresentTime) const;
+ nsecs_t previousPredictionPresentTime,
+ bool filterFramesBeforeTraceStarts) const;
// Sets the token, vsyncPeriod, predictions and SF start time.
void onSfWakeUp(int64_t token, Fps refreshRate, Fps renderRate,
std::optional<TimelineItem> predictions, nsecs_t wakeUpTime);
@@ -424,10 +434,13 @@
private:
void dump(std::string& result, nsecs_t baseTime) const;
- void tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const;
- void traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset) const;
+ void tracePredictions(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const;
+ void traceActuals(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
+ bool filterFramesBeforeTraceStarts) const;
void addSkippedFrame(pid_t surfaceFlingerPid, nsecs_t monoBootOffset,
- nsecs_t previousActualPresentTime) const;
+ nsecs_t previousActualPresentTime,
+ bool filterFramesBeforeTraceStarts) const;
void classifyJank(nsecs_t& deadlineDelta, nsecs_t& deltaToVsync,
nsecs_t previousPresentTime);
@@ -471,7 +484,8 @@
};
FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid,
- JankClassificationThresholds thresholds = {}, bool useBootTimeClock = true);
+ JankClassificationThresholds thresholds = {}, bool useBootTimeClock = true,
+ bool filterFramesBeforeTraceStarts = true);
~FrameTimeline() = default;
frametimeline::TokenManager* getTokenManager() override { return &mTokenManager; }
@@ -516,6 +530,7 @@
TraceCookieCounter mTraceCookieCounter;
mutable std::mutex mMutex;
const bool mUseBootTimeClock;
+ const bool mFilterFramesBeforeTraceStarts;
uint32_t mMaxDisplayFrames;
std::shared_ptr<TimeStats> mTimeStats;
const pid_t mSurfaceFlingerPid;
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 ac15b92..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;
@@ -279,24 +279,6 @@
snapshot.getDebugString().c_str());
}
-bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& requested) {
- if (requested.potentialCursor) {
- return false;
- }
-
- if (snapshot.inputInfo.token != nullptr) {
- return true;
- }
-
- if (snapshot.hasBufferOrSidebandStream()) {
- return true;
- }
-
- return requested.windowInfoHandle &&
- requested.windowInfoHandle->getInfo()->inputConfig.test(
- gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
-}
-
void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requested,
const LayerSnapshotBuilder::Args& args) {
snapshot.metadata.clear();
@@ -988,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();
@@ -1079,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);
@@ -1090,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);
}
}
@@ -1162,7 +1144,7 @@
}
updateVisibility(snapshot, snapshot.isVisible);
- if (!needsInputInfo(snapshot, requested)) {
+ if (!requested.needsInputInfo()) {
return;
}
@@ -1172,7 +1154,7 @@
bool noValidDisplay = !displayInfoOpt.has_value();
auto displayInfo = displayInfoOpt.value_or(sDefaultInfo);
- if (!requested.windowInfoHandle) {
+ if (!requested.hasInputInfo()) {
snapshot.inputInfo.inputConfig = InputConfig::NO_INPUT_CHANNEL;
}
fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot);
@@ -1210,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 17d2610..713a5c5 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -56,12 +56,15 @@
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);
changes |= RequestedLayerState::Changes::Metadata;
handleAlive = true;
+ // TODO: b/305254099 remove once we don't pass invisible windows to input
+ windowInfoHandle = nullptr;
if (parentId != UNASSIGNED_LAYER_ID) {
canBeRoot = false;
}
@@ -94,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;
@@ -471,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;
}
@@ -553,6 +556,24 @@
windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
}
+bool RequestedLayerState::needsInputInfo() const {
+ if (potentialCursor) {
+ return false;
+ }
+
+ if ((sidebandStream != nullptr) || (externalTexture != nullptr)) {
+ return true;
+ }
+
+ if (!windowInfoHandle) {
+ return false;
+ }
+
+ const auto windowInfo = windowInfoHandle->getInfo();
+ return windowInfo->token != nullptr ||
+ windowInfo->inputConfig.test(gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL);
+}
+
bool RequestedLayerState::hasBlur() const {
return backgroundBlurRadius > 0 || blurRegions.size() > 0;
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 48b9640..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;
@@ -87,6 +87,7 @@
aidl::android::hardware::graphics::composer3::Composition getCompositionType() const;
bool hasValidRelativeParent() const;
bool hasInputInfo() const;
+ bool needsInputInfo() const;
bool hasBlur() const;
bool hasFrameUpdate() const;
bool hasReadyFrame() const;
@@ -130,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 3c8af19..c14769e 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -60,9 +60,7 @@
#include <utils/StopWatch.h>
#include <algorithm>
-#include <mutex>
#include <optional>
-#include <sstream>
#include "DisplayDevice.h"
#include "DisplayHardware/HWComposer.h"
@@ -72,7 +70,6 @@
#include "FrontEnd/LayerHandle.h"
#include "Layer.h"
#include "LayerProtoHelper.h"
-#include "MutexUtils.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
#include "TransactionCallbackInvoker.h"
@@ -89,18 +86,6 @@
const ui::Transform kIdentityTransform;
-ui::LogicalDisplayId toLogicalDisplayId(const ui::LayerStack& layerStack) {
- return ui::LogicalDisplayId{static_cast<int32_t>(layerStack.id)};
-}
-
-bool assignTransform(ui::Transform* dst, ui::Transform& from) {
- if (*dst == from) {
- return false;
- }
- *dst = from;
- return true;
-}
-
TimeStats::SetFrameRateVote frameRateToSetFrameRateVotePayload(Layer::FrameRate frameRate) {
using FrameRateCompatibility = TimeStats::SetFrameRateVote::FrameRateCompatibility;
using Seamlessness = TimeStats::SetFrameRateVote::Seamlessness;
@@ -149,24 +134,11 @@
: sequence(args.sequence),
mFlinger(sp<SurfaceFlinger>::fromExisting(args.flinger)),
mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)),
- mClientRef(args.client),
mWindowType(static_cast<WindowInfo::Type>(
- args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))),
- mLayerCreationFlags(args.flags),
- mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName, this)) {
+ args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))) {
ALOGV("Creating Layer %s", getDebugName());
- uint32_t layerFlags = 0;
- if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
- if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
- if (args.flags & ISurfaceComposerClient::eSecure) layerFlags |= layer_state_t::eLayerSecure;
- if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
- layerFlags |= layer_state_t::eLayerSkipScreenshot;
- mDrawingState.flags = layerFlags;
- mDrawingState.crop.makeInvalid();
- mDrawingState.z = 0;
- mDrawingState.color.a = 1.0f;
- mDrawingState.layerStack = ui::DEFAULT_LAYER_STACK;
+ mDrawingState.crop = {0, 0, -1, -1};
mDrawingState.sequence = 0;
mDrawingState.transform.set(0, 0);
mDrawingState.frameNumber = 0;
@@ -179,33 +151,9 @@
mDrawingState.acquireFence = sp<Fence>::make(-1);
mDrawingState.acquireFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
mDrawingState.dataspace = ui::Dataspace::V0_SRGB;
- mDrawingState.hdrMetadata.validTypes = 0;
- mDrawingState.surfaceDamageRegion = Region::INVALID_REGION;
- mDrawingState.cornerRadius = 0.0f;
- mDrawingState.backgroundBlurRadius = 0;
- mDrawingState.api = -1;
- mDrawingState.hasColorTransform = false;
- mDrawingState.colorSpaceAgnostic = false;
- mDrawingState.frameRateSelectionPriority = PRIORITY_UNSET;
mDrawingState.metadata = args.metadata;
- mDrawingState.shadowRadius = 0.f;
- mDrawingState.fixedTransformHint = ui::Transform::ROT_INVALID;
mDrawingState.frameTimelineInfo = {};
mDrawingState.postTime = -1;
- mDrawingState.destinationFrame.makeInvalid();
- mDrawingState.isTrustedOverlay = false;
- mDrawingState.dropInputMode = gui::DropInputMode::NONE;
- mDrawingState.dimmingEnabled = true;
- mDrawingState.defaultFrameRateCompatibility = FrameRateCompatibility::Default;
- mDrawingState.frameRateSelectionStrategy = FrameRateSelectionStrategy::Propagate;
-
- if (args.flags & ISurfaceComposerClient::eNoColorFill) {
- // Set an invalid color so there is no color fill.
- mDrawingState.color.r = -1.0_hf;
- mDrawingState.color.g = -1.0_hf;
- mDrawingState.color.b = -1.0_hf;
- }
-
mFrameTracker.setDisplayRefreshPeriod(
args.flinger->mScheduler->getPacesetterVsyncPeriod().ns());
@@ -213,14 +161,9 @@
mOwnerPid = args.ownerPid;
mOwnerAppId = mOwnerUid % PER_USER_RANGE;
- mPremultipliedAlpha = !(args.flags & ISurfaceComposerClient::eNonPremultiplied);
mPotentialCursor = args.flags & ISurfaceComposerClient::eCursorWindow;
- mProtectedByApp = args.flags & ISurfaceComposerClient::eProtectedByApp;
-
- mSnapshot->sequence = sequence;
- mSnapshot->name = getDebugName();
- mSnapshot->premultipliedAlpha = mPremultipliedAlpha;
- mSnapshot->parentTransform = {};
+ mLayerFEs.emplace_back(frontend::LayerHierarchy::TraversalPath{static_cast<uint32_t>(sequence)},
+ args.flinger->getFactory().createLayerFE(mName, this));
}
void Layer::onFirstRef() {
@@ -240,7 +183,6 @@
mFlinger->mTimeStats->onDestroy(layerId);
mFlinger->mFrameTracer->onDestroy(layerId);
- mFrameTracker.logAndResetStats(mName);
mFlinger->onLayerDestroyed(this);
if (mDrawingState.sidebandStream != nullptr) {
@@ -253,35 +195,8 @@
}
// ---------------------------------------------------------------------------
-// callbacks
-// ---------------------------------------------------------------------------
-
-void Layer::removeRelativeZ(const std::vector<Layer*>& layersInTree) {
- if (mDrawingState.zOrderRelativeOf == nullptr) {
- return;
- }
-
- sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote();
- if (strongRelative == nullptr) {
- setZOrderRelativeOf(nullptr);
- return;
- }
-
- if (!std::binary_search(layersInTree.begin(), layersInTree.end(), strongRelative.get())) {
- strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this));
- mFlinger->setTransactionFlags(eTraversalNeeded);
- setZOrderRelativeOf(nullptr);
- }
-}
-
-// ---------------------------------------------------------------------------
// set-up
// ---------------------------------------------------------------------------
-
-bool Layer::getPremultipledAlpha() const {
- return mPremultipliedAlpha;
-}
-
sp<IBinder> Layer::getHandle() {
Mutex::Autolock _l(mLock);
if (mGetHandleCalled) {
@@ -297,46 +212,6 @@
// h/w composer set-up
// ---------------------------------------------------------------------------
-static Rect reduce(const Rect& win, const Region& exclude) {
- if (CC_LIKELY(exclude.isEmpty())) {
- return win;
- }
- if (exclude.isRect()) {
- return win.reduce(exclude.getBounds());
- }
- return Region(win).subtract(exclude).getBounds();
-}
-
-static FloatRect reduce(const FloatRect& win, const Region& exclude) {
- if (CC_LIKELY(exclude.isEmpty())) {
- return win;
- }
- // Convert through Rect (by rounding) for lack of FloatRegion
- return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect();
-}
-
-Rect Layer::getScreenBounds(bool reduceTransparentRegion) const {
- if (!reduceTransparentRegion) {
- return Rect{mScreenBounds};
- }
-
- FloatRect bounds = getBounds();
- ui::Transform t = getTransform();
- // Transform to screen space.
- bounds = t.transform(bounds);
- return Rect{bounds};
-}
-
-FloatRect Layer::getBounds() const {
- const State& s(getDrawingState());
- return getBounds(getActiveTransparentRegion(s));
-}
-
-FloatRect Layer::getBounds(const Region& activeTransparentRegion) const {
- // Subtract the transparent region and snap to the bounds.
- return reduce(mBounds, activeTransparentRegion);
-}
-
// No early returns.
void Layer::updateTrustedPresentationState(const DisplayDevice* display,
const frontend::LayerSnapshot* snapshot,
@@ -438,60 +313,9 @@
return true;
}
-void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform,
- float parentShadowRadius) {
- const State& s(getDrawingState());
-
- // Calculate effective layer transform
- mEffectiveTransform = parentTransform * getActiveTransform(s);
-
- if (CC_UNLIKELY(!isTransformValid())) {
- ALOGW("Stop computing bounds for %s because it has invalid transformation.",
- getDebugName());
- return;
- }
-
- // Transform parent bounds to layer space
- parentBounds = getActiveTransform(s).inverse().transform(parentBounds);
-
- // Calculate source bounds
- mSourceBounds = computeSourceBounds(parentBounds);
-
- // Calculate bounds by croping diplay frame with layer crop and parent bounds
- FloatRect bounds = mSourceBounds;
- const Rect layerCrop = getCrop(s);
- if (!layerCrop.isEmpty()) {
- bounds = mSourceBounds.intersect(layerCrop.toFloatRect());
- }
- bounds = bounds.intersect(parentBounds);
-
- mBounds = bounds;
- mScreenBounds = mEffectiveTransform.transform(mBounds);
-
- // Use the layer's own shadow radius if set. Otherwise get the radius from
- // parent.
- if (s.shadowRadius > 0.f) {
- mEffectiveShadowRadius = s.shadowRadius;
- } else {
- mEffectiveShadowRadius = parentShadowRadius;
- }
-
- // Shadow radius is passed down to only one layer so if the layer can draw shadows,
- // don't pass it to its children.
- const float childShadowRadius = canDrawShadows() ? 0.f : mEffectiveShadowRadius;
-
- for (const sp<Layer>& child : mDrawingChildren) {
- child->computeBounds(mBounds, mEffectiveTransform, childShadowRadius);
- }
-
- if (mPotentialCursor) {
- prepareCursorCompositionState();
- }
-}
-
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()) {
@@ -500,180 +324,6 @@
return size;
}
-void Layer::setupRoundedCornersCropCoordinates(Rect win,
- const FloatRect& roundedCornersCrop) const {
- // Translate win by the rounded corners rect coordinates, to have all values in
- // layer coordinate space.
- win.left -= roundedCornersCrop.left;
- win.right -= roundedCornersCrop.left;
- win.top -= roundedCornersCrop.top;
- win.bottom -= roundedCornersCrop.top;
-}
-
-void Layer::prepareBasicGeometryCompositionState() {
- const auto& drawingState{getDrawingState()};
- const auto alpha = static_cast<float>(getAlpha());
- const bool opaque = isOpaque(drawingState);
- const bool usesRoundedCorners = hasRoundedCorners();
-
- auto blendMode = Hwc2::IComposerClient::BlendMode::NONE;
- if (!opaque || alpha != 1.0f) {
- blendMode = mPremultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED
- : Hwc2::IComposerClient::BlendMode::COVERAGE;
- }
-
- // Please keep in sync with LayerSnapshotBuilder
- auto* snapshot = editLayerSnapshot();
- snapshot->outputFilter = getOutputFilter();
- snapshot->isVisible = isVisible();
- snapshot->isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
- snapshot->shadowSettings.length = mEffectiveShadowRadius;
-
- snapshot->contentDirty = contentDirty;
- contentDirty = false;
-
- snapshot->geomLayerBounds = mBounds;
- snapshot->geomLayerTransform = getTransform();
- snapshot->geomInverseLayerTransform = snapshot->geomLayerTransform.inverse();
- snapshot->transparentRegionHint = getActiveTransparentRegion(drawingState);
- snapshot->localTransform = getActiveTransform(drawingState);
- snapshot->localTransformInverse = snapshot->localTransform.inverse();
- snapshot->blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
- snapshot->alpha = alpha;
- snapshot->backgroundBlurRadius = getBackgroundBlurRadius();
- snapshot->blurRegions = getBlurRegions();
-}
-
-void Layer::prepareGeometryCompositionState() {
- const auto& drawingState{getDrawingState()};
- auto* snapshot = editLayerSnapshot();
-
- // Please keep in sync with LayerSnapshotBuilder
- snapshot->geomBufferSize = getBufferSize(drawingState);
- snapshot->geomContentCrop = getBufferCrop();
- snapshot->geomCrop = getCrop(drawingState);
- snapshot->geomBufferTransform = getBufferTransform();
- snapshot->geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse();
- snapshot->geomUsesSourceCrop = usesSourceCrop();
- snapshot->isSecure = isSecure();
-
- snapshot->metadata.clear();
- const auto& supportedMetadata = mFlinger->getHwComposer().getSupportedLayerGenericMetadata();
- for (const auto& [key, mandatory] : supportedMetadata) {
- const auto& genericLayerMetadataCompatibilityMap =
- mFlinger->getGenericLayerMetadataKeyMap();
- auto compatIter = genericLayerMetadataCompatibilityMap.find(key);
- if (compatIter == std::end(genericLayerMetadataCompatibilityMap)) {
- continue;
- }
- const uint32_t id = compatIter->second;
-
- auto it = drawingState.metadata.mMap.find(id);
- if (it == std::end(drawingState.metadata.mMap)) {
- continue;
- }
-
- snapshot->metadata.emplace(key,
- compositionengine::GenericLayerMetadataEntry{mandatory,
- it->second});
- }
-}
-
-void Layer::preparePerFrameCompositionState() {
- const auto& drawingState{getDrawingState()};
- // Please keep in sync with LayerSnapshotBuilder
- auto* snapshot = editLayerSnapshot();
-
- snapshot->forceClientComposition = false;
-
- snapshot->isColorspaceAgnostic = isColorSpaceAgnostic();
- snapshot->dataspace = getDataSpace();
- snapshot->colorTransform = getColorTransform();
- snapshot->colorTransformIsIdentity = !hasColorTransform();
- snapshot->surfaceDamage = surfaceDamageRegion;
- snapshot->hasProtectedContent = isProtected();
- snapshot->dimmingEnabled = isDimmingEnabled();
- snapshot->currentHdrSdrRatio = getCurrentHdrSdrRatio();
- snapshot->desiredHdrSdrRatio = getDesiredHdrSdrRatio();
- snapshot->cachingHint = getCachingHint();
-
- const bool usesRoundedCorners = hasRoundedCorners();
-
- snapshot->isOpaque = isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf;
-
- // Force client composition for special cases known only to the front-end.
- // Rounded corners no longer force client composition, since we may use a
- // hole punch so that the layer will appear to have rounded corners.
- if (drawShadows() || snapshot->stretchEffect.hasEffect()) {
- snapshot->forceClientComposition = true;
- }
- // If there are no visible region changes, we still need to update blur parameters.
- snapshot->blurRegions = getBlurRegions();
- snapshot->backgroundBlurRadius = getBackgroundBlurRadius();
-
- // Layer framerate is used in caching decisions.
- // Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in
- // LayerFECompositionState where it would be visible to Flattener.
- snapshot->fps = mFlinger->getLayerFramerate(systemTime(), getSequence());
-
- if (hasBufferOrSidebandStream()) {
- preparePerFrameBufferCompositionState();
- } else {
- preparePerFrameEffectsCompositionState();
- }
-}
-
-void Layer::preparePerFrameBufferCompositionState() {
- // Please keep in sync with LayerSnapshotBuilder
- auto* snapshot = editLayerSnapshot();
- // Sideband layers
- if (snapshot->sidebandStream.get() && !snapshot->sidebandStreamHasFrame) {
- snapshot->compositionType =
- aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
- return;
- } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) {
- snapshot->compositionType =
- aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION;
- } else if ((mDrawingState.flags & layer_state_t::eLayerIsRefreshRateIndicator) != 0) {
- snapshot->compositionType =
- aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR;
- } else {
- // Normal buffer layers
- snapshot->hdrMetadata = mBufferInfo.mHdrMetadata;
- snapshot->compositionType = mPotentialCursor
- ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
- : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
- }
-
- snapshot->buffer = getBuffer();
- snapshot->acquireFence = mBufferInfo.mFence;
- snapshot->frameNumber = mBufferInfo.mFrameNumber;
- snapshot->sidebandStreamHasFrame = false;
-}
-
-void Layer::preparePerFrameEffectsCompositionState() {
- // Please keep in sync with LayerSnapshotBuilder
- auto* snapshot = editLayerSnapshot();
- snapshot->color = getColor();
- snapshot->compositionType =
- aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
-}
-
-void Layer::prepareCursorCompositionState() {
- const State& drawingState{getDrawingState()};
- // Please keep in sync with LayerSnapshotBuilder
- auto* snapshot = editLayerSnapshot();
-
- // Apply the layer's transform, followed by the display's global transform
- // Here we're guaranteed that the layer's transform preserves rects
- Rect win = getCroppedBufferSize(drawingState);
- // Subtract the transparent region and snap to the bounds
- Rect bounds = reduce(win, getActiveTransparentRegion(drawingState));
- Rect frame(getTransform().transform(bounds));
-
- snapshot->cursorFrame = frame;
-}
-
const char* Layer::getDebugName() const {
return mName.c_str();
}
@@ -701,56 +351,9 @@
}
// ----------------------------------------------------------------------------
-// local state
-// ----------------------------------------------------------------------------
-
-bool Layer::isSecure() const {
- const State& s(mDrawingState);
- if (s.flags & layer_state_t::eLayerSecure) {
- return true;
- }
-
- const auto p = mDrawingParent.promote();
- return (p != nullptr) ? p->isSecure() : false;
-}
-
-// ----------------------------------------------------------------------------
// transaction
// ----------------------------------------------------------------------------
-uint32_t Layer::doTransaction(uint32_t flags) {
- SFTRACE_CALL();
-
- // TODO: This is unfortunate.
- mDrawingStateModified = mDrawingState.modified;
- mDrawingState.modified = false;
-
- const State& s(getDrawingState());
-
- if (updateGeometry()) {
- // invalidate and recompute the visible regions if needed
- flags |= Layer::eVisibleRegion;
- }
-
- if (s.sequence != mLastCommittedTxSequence) {
- // invalidate and recompute the visible regions if needed
- mLastCommittedTxSequence = s.sequence;
- flags |= eVisibleRegion;
- this->contentDirty = true;
-
- // we may use linear filtering, if the matrix scales us
- mNeedsFiltering = getActiveTransform(s).needsBilinearFiltering();
- }
-
- if (!mPotentialCursor && (flags & Layer::eVisibleRegion)) {
- mFlinger->mUpdateInputInfo = true;
- }
-
- commitTransaction();
-
- return flags;
-}
-
void Layer::commitTransaction() {
// Set the present state for all bufferlessSurfaceFramesTX to Presented. The
// bufferSurfaceFrameTX will be presented in latchBuffer.
@@ -765,218 +368,15 @@
mDrawingState.bufferlessSurfaceFramesTX.clear();
}
-uint32_t Layer::clearTransactionFlags(uint32_t mask) {
- const auto flags = mTransactionFlags & mask;
- mTransactionFlags &= ~mask;
- return flags;
-}
-
void Layer::setTransactionFlags(uint32_t mask) {
mTransactionFlags |= mask;
}
-bool Layer::setLayer(int32_t z) {
- if (mDrawingState.z == z && !usingRelativeZ(LayerVector::StateSet::Current)) return false;
- mDrawingState.sequence++;
- mDrawingState.z = z;
- mDrawingState.modified = true;
-
- mFlinger->mSomeChildrenChanged = true;
-
- // Discard all relative layering.
- if (mDrawingState.zOrderRelativeOf != nullptr) {
- sp<Layer> strongRelative = mDrawingState.zOrderRelativeOf.promote();
- if (strongRelative != nullptr) {
- strongRelative->removeZOrderRelative(wp<Layer>::fromExisting(this));
- }
- setZOrderRelativeOf(nullptr);
- }
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-void Layer::removeZOrderRelative(const wp<Layer>& relative) {
- mDrawingState.zOrderRelatives.remove(relative);
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
-}
-
-void Layer::addZOrderRelative(const wp<Layer>& relative) {
- mDrawingState.zOrderRelatives.add(relative);
- mDrawingState.modified = true;
- mDrawingState.sequence++;
- setTransactionFlags(eTransactionNeeded);
-}
-
-void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) {
- mDrawingState.zOrderRelativeOf = relativeOf;
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- mDrawingState.isRelativeOf = relativeOf != nullptr;
-
- setTransactionFlags(eTransactionNeeded);
-}
-
-bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) {
- sp<Layer> relative = LayerHandle::getLayer(relativeToHandle);
- if (relative == nullptr) {
- return false;
- }
-
- if (mDrawingState.z == relativeZ && usingRelativeZ(LayerVector::StateSet::Current) &&
- mDrawingState.zOrderRelativeOf == relative) {
- return false;
- }
-
- if (CC_UNLIKELY(relative->usingRelativeZ(LayerVector::StateSet::Drawing)) &&
- (relative->mDrawingState.zOrderRelativeOf == this)) {
- ALOGE("Detected relative layer loop between %s and %s",
- mName.c_str(), relative->mName.c_str());
- ALOGE("Ignoring new call to set relative layer");
- return false;
- }
-
- mFlinger->mSomeChildrenChanged = true;
-
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- mDrawingState.z = relativeZ;
-
- auto oldZOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote();
- if (oldZOrderRelativeOf != nullptr) {
- oldZOrderRelativeOf->removeZOrderRelative(wp<Layer>::fromExisting(this));
- }
- setZOrderRelativeOf(relative);
- relative->addZOrderRelative(wp<Layer>::fromExisting(this));
-
- setTransactionFlags(eTransactionNeeded);
-
- return true;
-}
-
-bool Layer::setTrustedOverlay(bool isTrustedOverlay) {
- if (mDrawingState.isTrustedOverlay == isTrustedOverlay) return false;
- mDrawingState.isTrustedOverlay = isTrustedOverlay;
- mDrawingState.modified = true;
- mFlinger->mUpdateInputInfo = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::isTrustedOverlay() const {
- if (getDrawingState().isTrustedOverlay) {
- return true;
- }
- const auto& p = mDrawingParent.promote();
- return (p != nullptr) && p->isTrustedOverlay();
-}
-
-bool Layer::setAlpha(float alpha) {
- if (mDrawingState.color.a == alpha) return false;
- mDrawingState.sequence++;
- mDrawingState.color.a = alpha;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setCornerRadius(float cornerRadius) {
- if (mDrawingState.cornerRadius == cornerRadius) return false;
-
- mDrawingState.sequence++;
- mDrawingState.cornerRadius = cornerRadius;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) {
- if (mDrawingState.backgroundBlurRadius == backgroundBlurRadius) return false;
- // If we start or stop drawing blur then the layer's visibility state may change so increment
- // the magic sequence number.
- if (mDrawingState.backgroundBlurRadius == 0 || backgroundBlurRadius == 0) {
- mDrawingState.sequence++;
- }
- mDrawingState.backgroundBlurRadius = backgroundBlurRadius;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setTransparentRegionHint(const Region& transparent) {
- mDrawingState.sequence++;
- mDrawingState.transparentRegionHint = transparent;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) {
- // If we start or stop drawing blur then the layer's visibility state may change so increment
- // the magic sequence number.
- if (mDrawingState.blurRegions.size() == 0 || blurRegions.size() == 0) {
- mDrawingState.sequence++;
- }
- mDrawingState.blurRegions = blurRegions;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setFlags(uint32_t flags, uint32_t mask) {
- const uint32_t newFlags = (mDrawingState.flags & ~mask) | (flags & mask);
- if (mDrawingState.flags == newFlags) return false;
- mDrawingState.sequence++;
- mDrawingState.flags = newFlags;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setCrop(const Rect& crop) {
+bool Layer::setCrop(const FloatRect& crop) {
if (mDrawingState.crop == crop) return false;
mDrawingState.sequence++;
mDrawingState.crop = crop;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setMetadata(const LayerMetadata& data) {
- if (!mDrawingState.metadata.merge(data, true /* eraseEmpty */)) return false;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setLayerStack(ui::LayerStack layerStack) {
- if (mDrawingState.layerStack == layerStack) return false;
- mDrawingState.sequence++;
- mDrawingState.layerStack = layerStack;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setColorSpaceAgnostic(const bool agnostic) {
- if (mDrawingState.colorSpaceAgnostic == agnostic) {
- return false;
- }
- mDrawingState.sequence++;
- mDrawingState.colorSpaceAgnostic = agnostic;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setDimmingEnabled(const bool dimmingEnabled) {
- if (mDrawingState.dimmingEnabled == dimmingEnabled) return false;
-
- mDrawingState.sequence++;
- mDrawingState.dimmingEnabled = dimmingEnabled;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -985,52 +385,6 @@
return priority == PRIORITY_FOCUSED_WITH_MODE || priority == PRIORITY_FOCUSED_WITHOUT_MODE;
};
-ui::LayerStack Layer::getLayerStack(LayerVector::StateSet state) const {
- bool useDrawing = state == LayerVector::StateSet::Drawing;
- const auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
- if (parent) {
- return parent->getLayerStack();
- }
- return getDrawingState().layerStack;
-}
-
-bool Layer::setShadowRadius(float shadowRadius) {
- if (mDrawingState.shadowRadius == shadowRadius) {
- return false;
- }
-
- mDrawingState.sequence++;
- mDrawingState.shadowRadius = shadowRadius;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint) {
- if (mDrawingState.fixedTransformHint == fixedTransformHint) {
- return false;
- }
-
- mDrawingState.sequence++;
- mDrawingState.fixedTransformHint = fixedTransformHint;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setStretchEffect(const StretchEffect& effect) {
- StretchEffect temp = effect;
- temp.sanitize();
- if (mDrawingState.stretchEffect == temp) {
- return false;
- }
- mDrawingState.sequence++;
- mDrawingState.stretchEffect = temp;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
void Layer::setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info,
nsecs_t postTime, gui::GameMode gameMode) {
mDrawingState.postTime = postTime;
@@ -1059,7 +413,6 @@
gui::GameMode gameMode) {
mDrawingState.frameTimelineInfo = info;
mDrawingState.postTime = postTime;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
if (const auto& bufferSurfaceFrameTX = mDrawingState.bufferSurfaceFrameTX;
@@ -1181,40 +534,6 @@
return getDrawingState().frameRateForLayerTree;
}
-bool Layer::isHiddenByPolicy() const {
- const State& s(mDrawingState);
- const auto& parent = mDrawingParent.promote();
- if (parent != nullptr && parent->isHiddenByPolicy()) {
- return true;
- }
- if (usingRelativeZ(LayerVector::StateSet::Drawing)) {
- auto zOrderRelativeOf = mDrawingState.zOrderRelativeOf.promote();
- if (zOrderRelativeOf != nullptr) {
- if (zOrderRelativeOf->isHiddenByPolicy()) {
- return true;
- }
- }
- }
- if (CC_UNLIKELY(!isTransformValid())) {
- ALOGW("Hide layer %s because it has invalid transformation.", getDebugName());
- return true;
- }
- return s.flags & layer_state_t::eLayerHidden;
-}
-
-uint32_t Layer::getEffectiveUsage(uint32_t usage) const {
- // TODO: should we do something special if mSecure is set?
- if (mProtectedByApp) {
- // need a hardware-protected path to external video sink
- usage |= GraphicBuffer::USAGE_PROTECTED;
- }
- if (mPotentialCursor) {
- usage |= GraphicBuffer::USAGE_CURSOR;
- }
- usage |= GraphicBuffer::USAGE_HW_COMPOSER;
- return usage;
-}
-
// ----------------------------------------------------------------------------
// debugging
// ----------------------------------------------------------------------------
@@ -1285,256 +604,16 @@
mFrameTracker.clearStats();
}
-void Layer::logFrameStats() {
- mFrameTracker.logAndResetStats(mName);
-}
-
void Layer::getFrameStats(FrameStats* outStats) const {
mFrameTracker.getStats(outStats);
}
-void Layer::dumpOffscreenDebugInfo(std::string& result) const {
- std::string hasBuffer = hasBufferOrSidebandStream() ? " (contains buffer)" : "";
- StringAppendF(&result, "Layer %s%s pid:%d uid:%d%s\n", getName().c_str(), hasBuffer.c_str(),
- mOwnerPid, mOwnerUid, isHandleAlive() ? " handleAlive" : "");
-}
-
void Layer::onDisconnect() {
const int32_t layerId = getSequence();
mFlinger->mTimeStats->onDestroy(layerId);
mFlinger->mFrameTracer->onDestroy(layerId);
}
-void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) {
- for (const sp<Layer>& child : mDrawingChildren) {
- child->mDrawingParent = newParent;
- const float parentShadowRadius =
- newParent->canDrawShadows() ? 0.f : newParent->mEffectiveShadowRadius;
- child->computeBounds(newParent->mBounds, newParent->mEffectiveTransform,
- parentShadowRadius);
- }
-}
-
-bool Layer::setColorTransform(const mat4& matrix) {
- static const mat4 identityMatrix = mat4();
-
- if (mDrawingState.colorTransform == matrix) {
- return false;
- }
- ++mDrawingState.sequence;
- mDrawingState.colorTransform = matrix;
- mDrawingState.hasColorTransform = matrix != identityMatrix;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-mat4 Layer::getColorTransform() const {
- mat4 colorTransform = mat4(getDrawingState().colorTransform);
- if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
- colorTransform = parent->getColorTransform() * colorTransform;
- }
- return colorTransform;
-}
-
-bool Layer::hasColorTransform() const {
- bool hasColorTransform = getDrawingState().hasColorTransform;
- if (sp<Layer> parent = mDrawingParent.promote(); parent != nullptr) {
- hasColorTransform = hasColorTransform || parent->hasColorTransform();
- }
- return hasColorTransform;
-}
-
-bool Layer::isLegacyDataSpace() const {
- // return true when no higher bits are set
- return !(getDataSpace() &
- (ui::Dataspace::STANDARD_MASK | ui::Dataspace::TRANSFER_MASK |
- ui::Dataspace::RANGE_MASK));
-}
-
-void Layer::setParent(const sp<Layer>& layer) {
- mCurrentParent = layer;
-}
-
-int32_t Layer::getZ(LayerVector::StateSet) const {
- return mDrawingState.z;
-}
-
-bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const {
- const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
- const State& state = useDrawing ? mDrawingState : mDrawingState;
- return state.isRelativeOf;
-}
-
-__attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::makeTraversalList(
- LayerVector::StateSet stateSet, bool* outSkipRelativeZUsers) {
- LOG_ALWAYS_FATAL_IF(stateSet == LayerVector::StateSet::Invalid,
- "makeTraversalList received invalid stateSet");
- const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
- const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
- const State& state = useDrawing ? mDrawingState : mDrawingState;
-
- if (state.zOrderRelatives.size() == 0) {
- *outSkipRelativeZUsers = true;
- return children;
- }
-
- LayerVector traverse(stateSet);
- for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
- sp<Layer> strongRelative = weakRelative.promote();
- if (strongRelative != nullptr) {
- traverse.add(strongRelative);
- }
- }
-
- for (const sp<Layer>& child : children) {
- if (child->usingRelativeZ(stateSet)) {
- continue;
- }
- traverse.add(child);
- }
-
- return traverse;
-}
-
-ui::Transform Layer::getTransform() const {
- return mEffectiveTransform;
-}
-
-bool Layer::isTransformValid() const {
- float transformDet = getTransform().det();
- return transformDet != 0 && !isinf(transformDet) && !isnan(transformDet);
-}
-
-half Layer::getAlpha() const {
- const auto& p = mDrawingParent.promote();
-
- half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
- return parentAlpha * getDrawingState().color.a;
-}
-
-ui::Transform::RotationFlags Layer::getFixedTransformHint() const {
- ui::Transform::RotationFlags fixedTransformHint = mDrawingState.fixedTransformHint;
- if (fixedTransformHint != ui::Transform::ROT_INVALID) {
- return fixedTransformHint;
- }
- const auto& p = mCurrentParent.promote();
- if (!p) return fixedTransformHint;
- return p->getFixedTransformHint();
-}
-
-half4 Layer::getColor() const {
- const half4 color(getDrawingState().color);
- return half4(color.r, color.g, color.b, getAlpha());
-}
-
-int32_t Layer::getBackgroundBlurRadius() const {
- if (getDrawingState().backgroundBlurRadius == 0) {
- return 0;
- }
-
- const auto& p = mDrawingParent.promote();
- half parentAlpha = (p != nullptr) ? p->getAlpha() : 1.0_hf;
- return parentAlpha * getDrawingState().backgroundBlurRadius;
-}
-
-const std::vector<BlurRegion> Layer::getBlurRegions() const {
- auto regionsCopy(getDrawingState().blurRegions);
- float layerAlpha = getAlpha();
- for (auto& region : regionsCopy) {
- region.alpha = region.alpha * layerAlpha;
- }
- return regionsCopy;
-}
-
-RoundedCornerState Layer::getRoundedCornerState() const {
- // Today's DPUs cannot do rounded corners. If RenderEngine cannot render
- // protected content, remove rounded corners from protected content so it
- // can be rendered by the DPU.
- if (isProtected() && !mFlinger->getRenderEngine().supportsProtectedContent()) {
- return {};
- }
-
- // Get parent settings
- RoundedCornerState parentSettings;
- const auto& parent = mDrawingParent.promote();
- if (parent != nullptr) {
- parentSettings = parent->getRoundedCornerState();
- if (parentSettings.hasRoundedCorners()) {
- ui::Transform t = getActiveTransform(getDrawingState());
- t = t.inverse();
- parentSettings.cropRect = t.transform(parentSettings.cropRect);
- parentSettings.radius.x *= t.getScaleX();
- parentSettings.radius.y *= t.getScaleY();
- }
- }
-
- // Get layer settings
- Rect layerCropRect = getCroppedBufferSize(getDrawingState());
- const vec2 radius(getDrawingState().cornerRadius, getDrawingState().cornerRadius);
- RoundedCornerState layerSettings(layerCropRect.toFloatRect(), radius);
- const bool layerSettingsValid = layerSettings.hasRoundedCorners() && layerCropRect.isValid();
-
- if (layerSettingsValid && parentSettings.hasRoundedCorners()) {
- // If the parent and the layer have rounded corner settings, use the parent settings if the
- // parent crop is entirely inside the layer crop.
- // This has limitations and cause rendering artifacts. See b/200300845 for correct fix.
- if (parentSettings.cropRect.left > layerCropRect.left &&
- parentSettings.cropRect.top > layerCropRect.top &&
- parentSettings.cropRect.right < layerCropRect.right &&
- parentSettings.cropRect.bottom < layerCropRect.bottom) {
- return parentSettings;
- } else {
- return layerSettings;
- }
- } else if (layerSettingsValid) {
- return layerSettings;
- } else if (parentSettings.hasRoundedCorners()) {
- return parentSettings;
- }
- return {};
-}
-
-bool Layer::findInHierarchy(const sp<Layer>& l) {
- if (l == this) {
- return true;
- }
- for (auto& child : mDrawingChildren) {
- if (child->findInHierarchy(l)) {
- return true;
- }
- }
- return false;
-}
-
-void Layer::setInputInfo(const WindowInfo& info) {
- mDrawingState.inputInfo = info;
- mDrawingState.touchableRegionCrop =
- LayerHandle::getLayer(info.touchableRegionCropHandle.promote());
- mDrawingState.modified = true;
- mFlinger->mUpdateInputInfo = true;
- setTransactionFlags(eTransactionNeeded);
-}
-
-perfetto::protos::LayerProto* Layer::writeToProto(perfetto::protos::LayersProto& layersProto,
- uint32_t traceFlags) {
- perfetto::protos::LayerProto* layerProto = layersProto.add_layers();
- writeToProtoDrawingState(layerProto);
- writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
-
- if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
- ui::LayerStack layerStack =
- (mSnapshot) ? mSnapshot->outputFilter.layerStack : ui::INVALID_LAYER_STACK;
- writeCompositionStateToProto(layerProto, layerStack);
- }
-
- for (const sp<Layer>& layer : mDrawingChildren) {
- layer->writeToProto(layersProto, traceFlags);
- }
-
- return layerProto;
-}
-
void Layer::writeCompositionStateToProto(perfetto::protos::LayerProto* layerProto,
ui::LayerStack layerStack) {
ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread.
@@ -1550,352 +629,6 @@
}
}
-void Layer::writeToProtoDrawingState(perfetto::protos::LayerProto* layerInfo) {
- const ui::Transform transform = getTransform();
- auto buffer = getExternalTexture();
- if (buffer != nullptr) {
- LayerProtoHelper::writeToProto(*buffer,
- [&]() { return layerInfo->mutable_active_buffer(); });
- LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()),
- layerInfo->mutable_buffer_transform());
- }
- layerInfo->set_invalidate(contentDirty);
- layerInfo->set_is_protected(isProtected());
- layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
- layerInfo->set_queued_frames(getQueuedFrameCount());
- layerInfo->set_curr_frame(mCurrentFrameNumber);
- layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
- layerInfo->set_corner_radius(
- (getRoundedCornerState().radius.x + getRoundedCornerState().radius.y) / 2.0);
- layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
- layerInfo->set_is_trusted_overlay(isTrustedOverlay());
- LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
- LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
- [&]() { return layerInfo->mutable_position(); });
- LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
- LayerProtoHelper::writeToProto(surfaceDamageRegion,
- [&]() { return layerInfo->mutable_damage_region(); });
-
- if (hasColorTransform()) {
- LayerProtoHelper::writeToProto(getColorTransform(), layerInfo->mutable_color_transform());
- }
-
- LayerProtoHelper::writeToProto(mSourceBounds,
- [&]() { return layerInfo->mutable_source_bounds(); });
- LayerProtoHelper::writeToProto(mScreenBounds,
- [&]() { return layerInfo->mutable_screen_bounds(); });
- LayerProtoHelper::writeToProto(getRoundedCornerState().cropRect,
- [&]() { return layerInfo->mutable_corner_radius_crop(); });
- layerInfo->set_shadow_radius(mEffectiveShadowRadius);
-}
-
-void Layer::writeToProtoCommonState(perfetto::protos::LayerProto* layerInfo,
- LayerVector::StateSet stateSet, uint32_t traceFlags) {
- const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
- const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
- const State& state = useDrawing ? mDrawingState : mDrawingState;
-
- ui::Transform requestedTransform = state.transform;
-
- layerInfo->set_id(sequence);
- layerInfo->set_name(getName().c_str());
- layerInfo->set_type(getType());
-
- for (const auto& child : children) {
- layerInfo->add_children(child->sequence);
- }
-
- for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
- sp<Layer> strongRelative = weakRelative.promote();
- if (strongRelative != nullptr) {
- layerInfo->add_relatives(strongRelative->sequence);
- }
- }
-
- LayerProtoHelper::writeToProto(state.transparentRegionHint,
- [&]() { return layerInfo->mutable_transparent_region(); });
-
- layerInfo->set_layer_stack(getLayerStack().id);
- layerInfo->set_z(state.z);
-
- LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() {
- return layerInfo->mutable_requested_position();
- });
-
- LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
-
- layerInfo->set_is_opaque(isOpaque(state));
-
- layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
- LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); });
- LayerProtoHelper::writeToProto(state.color,
- [&]() { return layerInfo->mutable_requested_color(); });
- layerInfo->set_flags(state.flags);
-
- LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
- layerInfo->mutable_requested_transform());
-
- auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
- if (parent != nullptr) {
- layerInfo->set_parent(parent->sequence);
- }
-
- auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
- if (zOrderRelativeOf != nullptr) {
- layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
- }
-
- layerInfo->set_is_relative_of(state.isRelativeOf);
-
- layerInfo->set_owner_uid(mOwnerUid);
-
- if ((traceFlags & LayerTracing::TRACE_INPUT) && needsInputInfo()) {
- WindowInfo info;
- if (useDrawing) {
- info = fillInputInfo(
- InputDisplayArgs{.transform = &kIdentityTransform, .isSecure = true});
- } else {
- info = state.inputInfo;
- }
-
- LayerProtoHelper::writeToProto(info, state.touchableRegionCrop,
- [&]() { return layerInfo->mutable_input_window_info(); });
- }
-
- if (traceFlags & LayerTracing::TRACE_EXTRA) {
- auto protoMap = layerInfo->mutable_metadata();
- for (const auto& entry : state.metadata.mMap) {
- (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
- }
- }
-
- LayerProtoHelper::writeToProto(state.destinationFrame,
- [&]() { return layerInfo->mutable_destination_frame(); });
-}
-
-// Applies the given transform to the region, while protecting against overflows caused by any
-// offsets. If applying the offset in the transform to any of the Rects in the region would result
-// in an overflow, they are not added to the output Region.
-static Region transformTouchableRegionSafely(const ui::Transform& t, const Region& r,
- const std::string& debugWindowName) {
- // Round the translation using the same rounding strategy used by ui::Transform.
- const auto tx = static_cast<int32_t>(t.tx() + 0.5);
- const auto ty = static_cast<int32_t>(t.ty() + 0.5);
-
- ui::Transform transformWithoutOffset = t;
- transformWithoutOffset.set(0.f, 0.f);
-
- const Region transformed = transformWithoutOffset.transform(r);
-
- // Apply the translation to each of the Rects in the region while discarding any that overflow.
- Region ret;
- for (const auto& rect : transformed) {
- Rect newRect;
- if (__builtin_add_overflow(rect.left, tx, &newRect.left) ||
- __builtin_add_overflow(rect.top, ty, &newRect.top) ||
- __builtin_add_overflow(rect.right, tx, &newRect.right) ||
- __builtin_add_overflow(rect.bottom, ty, &newRect.bottom)) {
- ALOGE("Applying transform to touchable region of window '%s' resulted in an overflow.",
- debugWindowName.c_str());
- continue;
- }
- ret.orSelf(newRect);
- }
- return ret;
-}
-
-void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) {
- auto [inputBounds, inputBoundsValid] = getInputBounds(/*fillParentBounds=*/false);
- if (!inputBoundsValid) {
- info.touchableRegion.clear();
- }
-
- info.frame = getInputBoundsInDisplaySpace(inputBounds, screenToDisplay);
-
- ui::Transform inputToLayer;
- inputToLayer.set(inputBounds.left, inputBounds.top);
- const ui::Transform layerToScreen = getInputTransform();
- const ui::Transform inputToDisplay = screenToDisplay * layerToScreen * inputToLayer;
-
- // InputDispatcher expects a display-to-input transform.
- info.transform = inputToDisplay.inverse();
-
- // The touchable region is specified in the input coordinate space. Change it to display space.
- info.touchableRegion =
- transformTouchableRegionSafely(inputToDisplay, info.touchableRegion, mName);
-}
-
-void Layer::fillTouchOcclusionMode(WindowInfo& info) {
- sp<Layer> p = sp<Layer>::fromExisting(this);
- while (p != nullptr && !p->hasInputInfo()) {
- p = p->mDrawingParent.promote();
- }
- if (p != nullptr) {
- info.touchOcclusionMode = p->mDrawingState.inputInfo.touchOcclusionMode;
- }
-}
-
-gui::DropInputMode Layer::getDropInputMode() const {
- gui::DropInputMode mode = mDrawingState.dropInputMode;
- if (mode == gui::DropInputMode::ALL) {
- return mode;
- }
- sp<Layer> parent = mDrawingParent.promote();
- if (parent) {
- gui::DropInputMode parentMode = parent->getDropInputMode();
- if (parentMode != gui::DropInputMode::NONE) {
- return parentMode;
- }
- }
- return mode;
-}
-
-void Layer::handleDropInputMode(gui::WindowInfo& info) const {
- if (mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) {
- return;
- }
-
- // Check if we need to drop input unconditionally
- gui::DropInputMode dropInputMode = getDropInputMode();
- if (dropInputMode == gui::DropInputMode::ALL) {
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
- ALOGV("Dropping input for %s as requested by policy.", getDebugName());
- return;
- }
-
- // Check if we need to check if the window is obscured by parent
- if (dropInputMode != gui::DropInputMode::OBSCURED) {
- return;
- }
-
- // Check if the parent has set an alpha on the layer
- sp<Layer> parent = mDrawingParent.promote();
- if (parent && parent->getAlpha() != 1.0_hf) {
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
- ALOGV("Dropping input for %s as requested by policy because alpha=%f", getDebugName(),
- static_cast<float>(getAlpha()));
- }
-
- // Check if the parent has cropped the buffer
- Rect bufferSize = getCroppedBufferSize(getDrawingState());
- if (!bufferSize.isValid()) {
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
- return;
- }
-
- // Screenbounds are the layer bounds cropped by parents, transformed to screenspace.
- // To check if the layer has been cropped, we take the buffer bounds, apply the local
- // layer crop and apply the same set of transforms to move to screenspace. If the bounds
- // match then the layer has not been cropped by its parents.
- Rect bufferInScreenSpace(getTransform().transform(bufferSize));
- bool croppedByParent = bufferInScreenSpace != Rect{mScreenBounds};
-
- if (croppedByParent) {
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
- ALOGV("Dropping input for %s as requested by policy because buffer is cropped by parent",
- getDebugName());
- } else {
- // If the layer is not obscured by its parents (by setting an alpha or crop), then only drop
- // input if the window is obscured. This check should be done in surfaceflinger but the
- // logic currently resides in inputflinger. So pass the if_obscured check to input to only
- // drop input events if the window is obscured.
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED;
- }
-}
-
-WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
- if (!hasInputInfo()) {
- mDrawingState.inputInfo.name = getName();
- mDrawingState.inputInfo.ownerUid = gui::Uid{mOwnerUid};
- mDrawingState.inputInfo.ownerPid = gui::Pid{mOwnerPid};
- mDrawingState.inputInfo.inputConfig |= WindowInfo::InputConfig::NO_INPUT_CHANNEL;
- mDrawingState.inputInfo.displayId = toLogicalDisplayId(getLayerStack());
- }
-
- const ui::Transform& displayTransform =
- displayArgs.transform != nullptr ? *displayArgs.transform : kIdentityTransform;
-
- WindowInfo info = mDrawingState.inputInfo;
- info.id = sequence;
- info.displayId = toLogicalDisplayId(getLayerStack());
-
- fillInputFrameInfo(info, displayTransform);
-
- if (displayArgs.transform == nullptr) {
- // Do not let the window receive touches if it is not associated with a valid display
- // transform. We still allow the window to receive keys and prevent ANRs.
- info.inputConfig |= WindowInfo::InputConfig::NOT_TOUCHABLE;
- }
-
- info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !isVisibleForInput());
-
- info.alpha = getAlpha();
- fillTouchOcclusionMode(info);
- handleDropInputMode(info);
-
- // If the window will be blacked out on a display because the display does not have the secure
- // flag and the layer has the secure flag set, then drop input.
- if (!displayArgs.isSecure && isSecure()) {
- info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT;
- }
-
- sp<Layer> cropLayer = mDrawingState.touchableRegionCrop.promote();
- if (info.replaceTouchableRegionWithCrop) {
- Rect inputBoundsInDisplaySpace;
- if (!cropLayer) {
- FloatRect inputBounds = getInputBounds(/*fillParentBounds=*/true).first;
- inputBoundsInDisplaySpace = getInputBoundsInDisplaySpace(inputBounds, displayTransform);
- } else {
- FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first;
- inputBoundsInDisplaySpace =
- cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform);
- }
- info.touchableRegion = Region(inputBoundsInDisplaySpace);
- } else if (cropLayer != nullptr) {
- FloatRect inputBounds = cropLayer->getInputBounds(/*fillParentBounds=*/true).first;
- Rect inputBoundsInDisplaySpace =
- cropLayer->getInputBoundsInDisplaySpace(inputBounds, displayTransform);
- info.touchableRegion = info.touchableRegion.intersect(inputBoundsInDisplaySpace);
- }
-
- // Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
- // if it was set by WM for a known system overlay
- if (isTrustedOverlay()) {
- info.inputConfig |= WindowInfo::InputConfig::TRUSTED_OVERLAY;
- }
-
- Rect bufferSize = getBufferSize(getDrawingState());
- info.contentSize = Size(bufferSize.width(), bufferSize.height());
-
- return info;
-}
-
-Rect Layer::getInputBoundsInDisplaySpace(const FloatRect& inputBounds,
- const ui::Transform& screenToDisplay) {
- // InputDispatcher works in the display device's coordinate space. Here, we calculate the
- // frame and transform used for the layer, which determines the bounds and the coordinate space
- // within which the layer will receive input.
-
- // Coordinate space definitions:
- // - display: The display device's coordinate space. Correlates to pixels on the display.
- // - screen: The post-rotation coordinate space for the display, a.k.a. logical display space.
- // - layer: The coordinate space of this layer.
- // - input: The coordinate space in which this layer will receive input events. This could be
- // different than layer space if a surfaceInset is used, which changes the origin
- // of the input space.
-
- // Crop the input bounds to ensure it is within the parent's bounds.
- const FloatRect croppedInputBounds = mBounds.intersect(inputBounds);
- const ui::Transform layerToScreen = getInputTransform();
- const ui::Transform layerToDisplay = screenToDisplay * layerToScreen;
- return Rect{layerToDisplay.transform(croppedInputBounds)};
-}
-
-bool Layer::hasInputInfo() const {
- return mDrawingState.inputInfo.token != nullptr ||
- mDrawingState.inputInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
-}
-
compositionengine::OutputLayer* Layer::findOutputLayerForDisplay(
const DisplayDevice* display) const {
if (!display) return nullptr;
@@ -1930,24 +663,6 @@
return outputLayer ? outputLayer->getState().visibleRegion : Region();
}
-bool Layer::isInternalDisplayOverlay() const {
- const State& s(mDrawingState);
- if (s.flags & layer_state_t::eLayerSkipScreenshot) {
- return true;
- }
-
- sp<Layer> parent = mDrawingParent.promote();
- return parent && parent->isInternalDisplayOverlay();
-}
-
-bool Layer::setDropInputMode(gui::DropInputMode mode) {
- if (mDrawingState.dropInputMode == mode) {
- return false;
- }
- mDrawingState.dropInputMode = mode;
- return true;
-}
-
void Layer::callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
const sp<Fence>& releaseFence) {
@@ -2106,17 +821,9 @@
mDrawingState.callbackHandles = {};
}
-bool Layer::willPresentCurrentTransaction() const {
- // Returns true if the most recent Transaction applied to CurrentState will be presented.
- return (getSidebandStreamChanged() || getAutoRefresh() ||
- (mDrawingState.modified &&
- (mDrawingState.buffer != nullptr || mDrawingState.bgColorLayer != nullptr)));
-}
-
bool Layer::setTransform(uint32_t transform) {
if (mDrawingState.bufferTransform == transform) return false;
mDrawingState.bufferTransform = transform;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -2125,111 +832,10 @@
if (mDrawingState.transformToDisplayInverse == transformToDisplayInverse) return false;
mDrawingState.sequence++;
mDrawingState.transformToDisplayInverse = transformToDisplayInverse;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
-bool Layer::setBufferCrop(const Rect& bufferCrop) {
- if (mDrawingState.bufferCrop == bufferCrop) return false;
-
- mDrawingState.sequence++;
- mDrawingState.bufferCrop = bufferCrop;
-
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setDestinationFrame(const Rect& destinationFrame) {
- if (mDrawingState.destinationFrame == destinationFrame) return false;
-
- mDrawingState.sequence++;
- mDrawingState.destinationFrame = destinationFrame;
-
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-// Translate destination frame into scale and position. If a destination frame is not set, use the
-// provided scale and position
-bool Layer::updateGeometry() {
- if ((mDrawingState.flags & layer_state_t::eIgnoreDestinationFrame) ||
- mDrawingState.destinationFrame.isEmpty()) {
- // If destination frame is not set, use the requested transform set via
- // Layer::setPosition and Layer::setMatrix.
- return assignTransform(&mDrawingState.transform, mRequestedTransform);
- }
-
- Rect destRect = mDrawingState.destinationFrame;
- int32_t destW = destRect.width();
- int32_t destH = destRect.height();
- if (destRect.left < 0) {
- destRect.left = 0;
- destRect.right = destW;
- }
- if (destRect.top < 0) {
- destRect.top = 0;
- destRect.bottom = destH;
- }
-
- if (!mDrawingState.buffer) {
- ui::Transform t;
- t.set(destRect.left, destRect.top);
- return assignTransform(&mDrawingState.transform, t);
- }
-
- uint32_t bufferWidth = mDrawingState.buffer->getWidth();
- uint32_t bufferHeight = mDrawingState.buffer->getHeight();
- // Undo any transformations on the buffer.
- if (mDrawingState.bufferTransform & ui::Transform::ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
- uint32_t invTransform = SurfaceFlinger::getActiveDisplayRotationFlags();
- if (mDrawingState.transformToDisplayInverse) {
- if (invTransform & ui::Transform::ROT_90) {
- std::swap(bufferWidth, bufferHeight);
- }
- }
-
- float sx = destW / static_cast<float>(bufferWidth);
- float sy = destH / static_cast<float>(bufferHeight);
- ui::Transform t;
- t.set(sx, 0, 0, sy);
- t.set(destRect.left, destRect.top);
- return assignTransform(&mDrawingState.transform, t);
-}
-
-bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix) {
- if (mRequestedTransform.dsdx() == matrix.dsdx && mRequestedTransform.dtdy() == matrix.dtdy &&
- mRequestedTransform.dtdx() == matrix.dtdx && mRequestedTransform.dsdy() == matrix.dsdy) {
- return false;
- }
-
- mRequestedTransform.set(matrix.dsdx, matrix.dtdy, matrix.dtdx, matrix.dsdy);
-
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
-
- return true;
-}
-
-bool Layer::setPosition(float x, float y) {
- if (mRequestedTransform.tx() == x && mRequestedTransform.ty() == y) {
- return false;
- }
-
- mRequestedTransform.set(x, y);
-
- mDrawingState.sequence++;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
-
- return true;
-}
-
void Layer::releasePreviousBuffer() {
mReleasePreviousBuffer = true;
if (!mBufferInfo.mBuffer ||
@@ -2293,7 +899,6 @@
mDrawingState.isAutoTimestamp = isAutoTimestamp;
mDrawingState.latchedVsyncId = info.vsyncId;
mDrawingState.useVsyncIdForRefreshRateSelection = info.useForRefreshRateSelection;
- mDrawingState.modified = true;
if (!buffer) {
resetDrawingStateBufferInfo();
setTransactionFlags(eTransactionNeeded);
@@ -2441,7 +1046,6 @@
bool Layer::setDataspace(ui::Dataspace dataspace) {
if (mDrawingState.dataspace == dataspace) return false;
mDrawingState.dataspace = dataspace;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -2452,7 +1056,6 @@
return false;
mDrawingState.currentHdrSdrRatio = currentBufferRatio;
mDrawingState.desiredHdrSdrRatio = desiredRatio;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -2460,40 +1063,6 @@
bool Layer::setDesiredHdrHeadroom(float desiredRatio) {
if (mDrawingState.desiredHdrSdrRatio == desiredRatio) return false;
mDrawingState.desiredHdrSdrRatio = desiredRatio;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setCachingHint(gui::CachingHint cachingHint) {
- if (mDrawingState.cachingHint == cachingHint) return false;
- mDrawingState.cachingHint = cachingHint;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) {
- if (mDrawingState.hdrMetadata == hdrMetadata) return false;
- mDrawingState.hdrMetadata = hdrMetadata;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::setSurfaceDamageRegion(const Region& surfaceDamage) {
- if (mDrawingState.surfaceDamageRegion.hasSameRects(surfaceDamage)) return false;
- mDrawingState.surfaceDamageRegion = surfaceDamage;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- setIsSmallDirty(surfaceDamage, getTransform());
- return true;
-}
-
-bool Layer::setApi(int32_t api) {
- if (mDrawingState.api == api) return false;
- mDrawingState.api = api;
- mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
@@ -2509,7 +1078,6 @@
}
mDrawingState.sidebandStream = sidebandStream;
- mDrawingState.modified = true;
if (sidebandStream != nullptr && mDrawingState.buffer != nullptr) {
releasePreviousBuffer();
resetDrawingStateBufferInfo();
@@ -2606,14 +1174,6 @@
return Rect(0, 0, static_cast<int32_t>(bufWidth), static_cast<int32_t>(bufHeight));
}
-FloatRect Layer::computeSourceBounds(const FloatRect& parentBounds) const {
- if (mBufferInfo.mBuffer == nullptr) {
- return parentBounds;
- }
-
- return getBufferSize(getDrawingState()).toFloatRect();
-}
-
bool Layer::fenceHasSignaled() const {
if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
return true;
@@ -2635,37 +1195,21 @@
}
}
-void Layer::setAutoRefresh(bool autoRefresh) {
- mDrawingState.autoRefresh = autoRefresh;
-}
-
bool Layer::latchSidebandStream(bool& recomputeVisibleRegions) {
- // We need to update the sideband stream if the layer has both a buffer and a sideband stream.
- auto* snapshot = editLayerSnapshot();
- snapshot->sidebandStreamHasFrame = hasFrameUpdate() && mSidebandStream.get();
-
if (mSidebandStreamChanged.exchange(false)) {
const State& s(getDrawingState());
// mSidebandStreamChanged was true
mSidebandStream = s.sidebandStream;
- snapshot->sidebandStream = mSidebandStream;
if (mSidebandStream != nullptr) {
setTransactionFlags(eTransactionNeeded);
mFlinger->setTransactionFlags(eTraversalNeeded);
}
recomputeVisibleRegions = true;
-
return true;
}
return false;
}
-bool Layer::hasFrameUpdate() const {
- const State& c(getDrawingState());
- return (mDrawingStateModified || mDrawingState.modified) &&
- (c.buffer != nullptr || c.bgColorLayer != nullptr);
-}
-
void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) {
const State& s(getDrawingState());
@@ -2712,8 +1256,6 @@
mFlinger->getTransactionCallbackInvoker()
.addOnCommitCallbackHandles(mDrawingState.callbackHandles, remainingHandles);
mDrawingState.callbackHandles = remainingHandles;
-
- mDrawingStateModified = false;
}
void Layer::gatherBufferInfo() {
@@ -2737,7 +1279,6 @@
mBufferInfo.mFrameLatencyNeeded = true;
mBufferInfo.mDesiredPresentTime = mDrawingState.desiredPresentTime;
mBufferInfo.mFenceTime = std::make_shared<FenceTime>(mDrawingState.acquireFence);
- mBufferInfo.mFence = mDrawingState.acquireFence;
mBufferInfo.mTransform = mDrawingState.bufferTransform;
auto lastDataspace = mBufferInfo.mDataspace;
mBufferInfo.mDataspace = translateDataspace(mDrawingState.dataspace);
@@ -2785,10 +1326,6 @@
mFlinger->mHdrLayerInfoChanged = true;
}
mBufferInfo.mCrop = computeBufferCrop(mDrawingState);
- mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW;
- mBufferInfo.mSurfaceDamage = mDrawingState.surfaceDamageRegion;
- mBufferInfo.mHdrMetadata = mDrawingState.hdrMetadata;
- mBufferInfo.mApi = mDrawingState.api;
mBufferInfo.mTransformToDisplayInverse = mDrawingState.transformToDisplayInverse;
}
@@ -2805,7 +1342,7 @@
}
void Layer::decrementPendingBufferCount() {
- int32_t pendingBuffers = --mPendingBufferTransactions;
+ int32_t pendingBuffers = --mPendingBuffers;
tracePendingBufferCount(pendingBuffers);
}
@@ -2813,294 +1350,6 @@
SFTRACE_INT(mBlastTransactionName.c_str(), pendingBuffers);
}
-/*
- * We don't want to send the layer's transform to input, but rather the
- * parent's transform. This is because Layer's transform is
- * information about how the buffer is placed on screen. The parent's
- * transform makes more sense to send since it's information about how the
- * layer is placed on screen. This transform is used by input to determine
- * how to go from screen space back to window space.
- */
-ui::Transform Layer::getInputTransform() const {
- if (!hasBufferOrSidebandStream()) {
- return getTransform();
- }
- sp<Layer> parent = mDrawingParent.promote();
- if (parent == nullptr) {
- return ui::Transform();
- }
-
- return parent->getTransform();
-}
-
-/**
- * Returns the bounds used to fill the input frame and the touchable region.
- *
- * Similar to getInputTransform, we need to update the bounds to include the transform.
- * This is because bounds don't include the buffer transform, where the input assumes
- * that's already included.
- */
-std::pair<FloatRect, bool> Layer::getInputBounds(bool fillParentBounds) const {
- Rect croppedBufferSize = getCroppedBufferSize(getDrawingState());
- FloatRect inputBounds = croppedBufferSize.toFloatRect();
- if (hasBufferOrSidebandStream() && croppedBufferSize.isValid() &&
- mDrawingState.transform.getType() != ui::Transform::IDENTITY) {
- inputBounds = mDrawingState.transform.transform(inputBounds);
- }
-
- bool inputBoundsValid = croppedBufferSize.isValid();
- if (!inputBoundsValid) {
- /**
- * Input bounds are based on the layer crop or buffer size. But if we are using
- * the layer bounds as the input bounds (replaceTouchableRegionWithCrop flag) then
- * we can use the parent bounds as the input bounds if the layer does not have buffer
- * or a crop. We want to unify this logic but because of compat reasons we cannot always
- * use the parent bounds. A layer without a buffer can get input. So when a window is
- * initially added, its touchable region can fill its parent layer bounds and that can
- * have negative consequences.
- */
- inputBounds = fillParentBounds ? mBounds : FloatRect{};
- }
-
- // Clamp surface inset to the input bounds.
- const float inset = static_cast<float>(mDrawingState.inputInfo.surfaceInset);
- const float xSurfaceInset = std::clamp(inset, 0.f, inputBounds.getWidth() / 2.f);
- const float ySurfaceInset = std::clamp(inset, 0.f, inputBounds.getHeight() / 2.f);
-
- // Apply the insets to the input bounds.
- inputBounds.left += xSurfaceInset;
- inputBounds.top += ySurfaceInset;
- inputBounds.right -= xSurfaceInset;
- inputBounds.bottom -= ySurfaceInset;
-
- return {inputBounds, inputBoundsValid};
-}
-
-bool Layer::isSimpleBufferUpdate(const layer_state_t& s) const {
- const uint64_t requiredFlags = layer_state_t::eBufferChanged;
-
- const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
- layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged |
- layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged |
- layer_state_t::eLayerStackChanged | layer_state_t::eReparent |
- (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
- ? 0
- : layer_state_t::eAutoRefreshChanged);
-
- if ((s.what & requiredFlags) != requiredFlags) {
- SFTRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
- (s.what | requiredFlags) & ~s.what);
- return false;
- }
-
- if (s.what & deniedFlags) {
- SFTRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__,
- s.what & deniedFlags);
- return false;
- }
-
- if (s.what & layer_state_t::ePositionChanged) {
- if (mRequestedTransform.tx() != s.x || mRequestedTransform.ty() != s.y) {
- SFTRACE_FORMAT_INSTANT("%s: false [ePositionChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eAlphaChanged) {
- if (mDrawingState.color.a != s.color.a) {
- SFTRACE_FORMAT_INSTANT("%s: false [eAlphaChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eColorTransformChanged) {
- if (mDrawingState.colorTransform != s.colorTransform) {
- SFTRACE_FORMAT_INSTANT("%s: false [eColorTransformChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eBackgroundColorChanged) {
- if (mDrawingState.bgColorLayer || s.bgColor.a != 0) {
- SFTRACE_FORMAT_INSTANT("%s: false [eBackgroundColorChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eMatrixChanged) {
- if (mRequestedTransform.dsdx() != s.matrix.dsdx ||
- mRequestedTransform.dtdy() != s.matrix.dtdy ||
- mRequestedTransform.dtdx() != s.matrix.dtdx ||
- mRequestedTransform.dsdy() != s.matrix.dsdy) {
- SFTRACE_FORMAT_INSTANT("%s: false [eMatrixChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eCornerRadiusChanged) {
- if (mDrawingState.cornerRadius != s.cornerRadius) {
- SFTRACE_FORMAT_INSTANT("%s: false [eCornerRadiusChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eBackgroundBlurRadiusChanged) {
- if (mDrawingState.backgroundBlurRadius != static_cast<int>(s.backgroundBlurRadius)) {
- SFTRACE_FORMAT_INSTANT("%s: false [eBackgroundBlurRadiusChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eBufferTransformChanged) {
- if (mDrawingState.bufferTransform != s.bufferTransform) {
- SFTRACE_FORMAT_INSTANT("%s: false [eBufferTransformChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eTransformToDisplayInverseChanged) {
- if (mDrawingState.transformToDisplayInverse != s.transformToDisplayInverse) {
- SFTRACE_FORMAT_INSTANT("%s: false [eTransformToDisplayInverseChanged changed]",
- __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eCropChanged) {
- if (mDrawingState.crop != s.crop) {
- SFTRACE_FORMAT_INSTANT("%s: false [eCropChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eDataspaceChanged) {
- if (mDrawingState.dataspace != s.dataspace) {
- SFTRACE_FORMAT_INSTANT("%s: false [eDataspaceChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eHdrMetadataChanged) {
- if (mDrawingState.hdrMetadata != s.hdrMetadata) {
- SFTRACE_FORMAT_INSTANT("%s: false [eHdrMetadataChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eSidebandStreamChanged) {
- if (mDrawingState.sidebandStream != s.sidebandStream) {
- SFTRACE_FORMAT_INSTANT("%s: false [eSidebandStreamChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eColorSpaceAgnosticChanged) {
- if (mDrawingState.colorSpaceAgnostic != s.colorSpaceAgnostic) {
- SFTRACE_FORMAT_INSTANT("%s: false [eColorSpaceAgnosticChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eShadowRadiusChanged) {
- if (mDrawingState.shadowRadius != s.shadowRadius) {
- SFTRACE_FORMAT_INSTANT("%s: false [eShadowRadiusChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eFixedTransformHintChanged) {
- if (mDrawingState.fixedTransformHint != s.fixedTransformHint) {
- SFTRACE_FORMAT_INSTANT("%s: false [eFixedTransformHintChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eTrustedOverlayChanged) {
- if (mDrawingState.isTrustedOverlay != (s.trustedOverlay == gui::TrustedOverlay::ENABLED)) {
- SFTRACE_FORMAT_INSTANT("%s: false [eTrustedOverlayChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eStretchChanged) {
- StretchEffect temp = s.stretchEffect;
- temp.sanitize();
- if (mDrawingState.stretchEffect != temp) {
- SFTRACE_FORMAT_INSTANT("%s: false [eStretchChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eBufferCropChanged) {
- if (mDrawingState.bufferCrop != s.bufferCrop) {
- SFTRACE_FORMAT_INSTANT("%s: false [eBufferCropChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eDestinationFrameChanged) {
- if (mDrawingState.destinationFrame != s.destinationFrame) {
- SFTRACE_FORMAT_INSTANT("%s: false [eDestinationFrameChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eDimmingEnabledChanged) {
- if (mDrawingState.dimmingEnabled != s.dimmingEnabled) {
- SFTRACE_FORMAT_INSTANT("%s: false [eDimmingEnabledChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eExtendedRangeBrightnessChanged) {
- if (mDrawingState.currentHdrSdrRatio != s.currentHdrSdrRatio ||
- mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) {
- SFTRACE_FORMAT_INSTANT("%s: false [eExtendedRangeBrightnessChanged changed]", __func__);
- return false;
- }
- }
-
- if (s.what & layer_state_t::eDesiredHdrHeadroomChanged) {
- if (mDrawingState.desiredHdrSdrRatio != s.desiredHdrSdrRatio) {
- SFTRACE_FORMAT_INSTANT("%s: false [eDesiredHdrHeadroomChanged changed]", __func__);
- return false;
- }
- }
-
- return true;
-}
-
-sp<LayerFE> Layer::getCompositionEngineLayerFE() const {
- // There's no need to get a CE Layer if the layer isn't going to draw anything.
- return hasSomethingToDraw() ? mLegacyLayerFE : nullptr;
-}
-
-const LayerSnapshot* Layer::getLayerSnapshot() const {
- return mSnapshot.get();
-}
-
-LayerSnapshot* Layer::editLayerSnapshot() {
- return mSnapshot.get();
-}
-
-std::unique_ptr<frontend::LayerSnapshot> Layer::stealLayerSnapshot() {
- return std::move(mSnapshot);
-}
-
-void Layer::updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot) {
- mSnapshot = std::move(snapshot);
-}
-
-const compositionengine::LayerFECompositionState* Layer::getCompositionState() const {
- return mSnapshot.get();
-}
-
-sp<LayerFE> Layer::copyCompositionEngineLayerFE() const {
- auto result = mFlinger->getFactory().createLayerFE(mName, this);
- result->mSnapshot = std::make_unique<LayerSnapshot>(*mSnapshot);
- return result;
-}
-
sp<LayerFE> Layer::getCompositionEngineLayerFE(
const frontend::LayerHierarchy::TraversalPath& path) {
for (auto& [p, layerFE] : mLayerFEs) {
@@ -3113,55 +1362,6 @@
return layerFE;
}
-void Layer::useSurfaceDamage() {
- if (mFlinger->mForceFullDamage) {
- surfaceDamageRegion = Region::INVALID_REGION;
- } else {
- surfaceDamageRegion = mBufferInfo.mSurfaceDamage;
- }
-}
-
-void Layer::useEmptyDamage() {
- surfaceDamageRegion.clear();
-}
-
-bool Layer::isOpaque(const Layer::State& s) const {
- // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the
- // layer's opaque flag.
- if (!hasSomethingToDraw()) {
- return false;
- }
-
- // if the layer has the opaque flag, then we're always opaque
- if ((s.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque) {
- return true;
- }
-
- // If the buffer has no alpha channel, then we are opaque
- if (hasBufferOrSidebandStream() && LayerSnapshot::isOpaqueFormat(getPixelFormat())) {
- return true;
- }
-
- // Lastly consider the layer opaque if drawing a color with alpha == 1.0
- return fillsColor() && getAlpha() == 1.0_hf;
-}
-
-bool Layer::canReceiveInput() const {
- return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
-}
-
-bool Layer::isVisible() const {
- if (!hasSomethingToDraw()) {
- return false;
- }
-
- if (isHiddenByPolicy()) {
- return false;
- }
-
- return getAlpha() > 0.0f || hasBlur();
-}
-
void Layer::onCompositionPresented(const DisplayDevice* display,
const std::shared_ptr<FenceTime>& glDoneFence,
const std::shared_ptr<FenceTime>& presentFence,
@@ -3250,15 +1450,6 @@
mBufferInfo.mFrameLatencyNeeded = false;
}
-bool Layer::willReleaseBufferOnLatch() const {
- return !mDrawingState.buffer && mBufferInfo.mBuffer;
-}
-
-bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) {
- const bool bgColorOnly = mDrawingState.bgColorLayer != nullptr;
- return latchBufferImpl(recomputeVisibleRegions, latchTime, bgColorOnly);
-}
-
bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) {
SFTRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(),
getDrawingState().frameNumber);
@@ -3280,7 +1471,6 @@
// Capture the old state of the layer for comparisons later
BufferInfo oldBufferInfo = mBufferInfo;
- const bool oldOpacity = isOpaque(mDrawingState);
mPreviousFrameNumber = mCurrentFrameNumber;
mCurrentFrameNumber = mDrawingState.frameNumber;
gatherBufferInfo();
@@ -3305,7 +1495,6 @@
if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) ||
(mBufferInfo.mTransform != oldBufferInfo.mTransform) ||
- (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) ||
(mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) {
recomputeVisibleRegions = true;
}
@@ -3318,70 +1507,13 @@
recomputeVisibleRegions = true;
}
}
-
- if (oldOpacity != isOpaque(mDrawingState)) {
- recomputeVisibleRegions = true;
- }
-
return true;
}
-bool Layer::hasReadyFrame() const {
- return hasFrameUpdate() || getSidebandStreamChanged() || getAutoRefresh();
-}
-
-bool Layer::isProtected() const {
- return (mBufferInfo.mBuffer != nullptr) &&
- (mBufferInfo.mBuffer->getUsage() & GRALLOC_USAGE_PROTECTED);
-}
-
-void Layer::latchAndReleaseBuffer() {
- if (hasReadyFrame()) {
- bool ignored = false;
- latchBuffer(ignored, systemTime());
- }
- releasePendingBuffer(systemTime());
-}
-
-PixelFormat Layer::getPixelFormat() const {
- return mBufferInfo.mPixelFormat;
-}
-
bool Layer::getTransformToDisplayInverse() const {
return mBufferInfo.mTransformToDisplayInverse;
}
-Rect Layer::getBufferCrop() const {
- // this is the crop rectangle that applies to the buffer
- // itself (as opposed to the window)
- if (!mBufferInfo.mCrop.isEmpty()) {
- // if the buffer crop is defined, we use that
- return mBufferInfo.mCrop;
- } else if (mBufferInfo.mBuffer != nullptr) {
- // otherwise we use the whole buffer
- return mBufferInfo.mBuffer->getBounds();
- } else {
- // if we don't have a buffer yet, we use an empty/invalid crop
- return Rect();
- }
-}
-
-uint32_t Layer::getBufferTransform() const {
- return mBufferInfo.mTransform;
-}
-
-ui::Dataspace Layer::getDataSpace() const {
- return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace;
-}
-
-bool Layer::isFrontBuffered() const {
- if (mBufferInfo.mBuffer == nullptr) {
- return false;
- }
-
- return mBufferInfo.mBuffer->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER;
-}
-
ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) {
ui::Dataspace updatedDataspace = dataspace;
// translate legacy dataspaces to modern dataspaces
@@ -3417,84 +1549,6 @@
return mBufferInfo.mBuffer ? mBufferInfo.mBuffer->getBuffer() : nullptr;
}
-const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const {
- return mBufferInfo.mBuffer;
-}
-
-bool Layer::setColor(const half3& color) {
- if (mDrawingState.color.rgb == color) {
- return false;
- }
-
- mDrawingState.sequence++;
- mDrawingState.color.rgb = color;
- mDrawingState.modified = true;
- setTransactionFlags(eTransactionNeeded);
- return true;
-}
-
-bool Layer::fillsColor() const {
- return !hasBufferOrSidebandStream() && mDrawingState.color.r >= 0.0_hf &&
- mDrawingState.color.g >= 0.0_hf && mDrawingState.color.b >= 0.0_hf;
-}
-
-bool Layer::hasBlur() const {
- return getBackgroundBlurRadius() > 0 || getDrawingState().blurRegions.size() > 0;
-}
-
-void Layer::updateSnapshot(bool updateGeometry) {
- if (!getCompositionEngineLayerFE()) {
- return;
- }
-
- auto* snapshot = editLayerSnapshot();
- if (updateGeometry) {
- prepareBasicGeometryCompositionState();
- prepareGeometryCompositionState();
- snapshot->roundedCorner = getRoundedCornerState();
- snapshot->transformedBounds = mScreenBounds;
- if (mEffectiveShadowRadius > 0.f) {
- snapshot->shadowSettings = mFlinger->mDrawingState.globalShadowSettings;
-
- // Note: this preserves existing behavior of shadowing the entire layer and not cropping
- // it if transparent regions are present. This may not be necessary since shadows are
- // typically cast by layers without transparent regions.
- snapshot->shadowSettings.boundaries = mBounds;
-
- const float casterAlpha = snapshot->alpha;
- const bool casterIsOpaque =
- ((mBufferInfo.mBuffer != nullptr) && isOpaque(mDrawingState));
-
- // If the casting layer is translucent, we need to fill in the shadow underneath the
- // layer. Otherwise the generated shadow will only be shown around the casting layer.
- snapshot->shadowSettings.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
- snapshot->shadowSettings.ambientColor *= casterAlpha;
- snapshot->shadowSettings.spotColor *= casterAlpha;
- }
- snapshot->shadowSettings.length = mEffectiveShadowRadius;
- }
- snapshot->contentOpaque = isOpaque(mDrawingState);
- snapshot->layerOpaqueFlagSet =
- (mDrawingState.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque;
- sp<Layer> p = mDrawingParent.promote();
- if (p != nullptr) {
- snapshot->parentTransform = p->getTransform();
- } else {
- snapshot->parentTransform.reset();
- }
- snapshot->bufferSize = getBufferSize(mDrawingState);
- snapshot->externalTexture = mBufferInfo.mBuffer;
- snapshot->hasReadyFrame = hasReadyFrame();
- preparePerFrameCompositionState();
-}
-
-void Layer::updateChildrenSnapshots(bool updateGeometry) {
- for (const sp<Layer>& child : mDrawingChildren) {
- child->updateSnapshot(updateGeometry);
- child->updateChildrenSnapshots(updateGeometry);
- }
-}
-
bool Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds,
TrustedPresentationListener const& listener) {
bool hadTrustedPresentationListener = hasTrustedPresentationListener();
@@ -3527,35 +1581,32 @@
mLastLatchTime = latchTime;
}
-void Layer::setIsSmallDirty(const Region& damageRegion,
- const ui::Transform& layerToDisplayTransform) {
- mSmallDirty = false;
+void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) {
if (!mFlinger->mScheduler->supportSmallDirtyDetection(mOwnerAppId)) {
+ snapshot->isSmallDirty = false;
return;
}
if (mWindowType != WindowInfo::Type::APPLICATION &&
mWindowType != WindowInfo::Type::BASE_APPLICATION) {
+ snapshot->isSmallDirty = false;
return;
}
- Rect bounds = damageRegion.getBounds();
+ Rect bounds = snapshot->surfaceDamage.getBounds();
if (!bounds.isValid()) {
+ snapshot->isSmallDirty = false;
return;
}
// Transform to screen space.
- bounds = layerToDisplayTransform.transform(bounds);
+ bounds = snapshot->localTransform.transform(bounds);
// If the damage region is a small dirty, this could give the hint for the layer history that
// it could suppress the heuristic rate when calculating.
- mSmallDirty = mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId,
- bounds.getWidth() * bounds.getHeight());
-}
-
-void Layer::setIsSmallDirty(frontend::LayerSnapshot* snapshot) {
- setIsSmallDirty(snapshot->surfaceDamage, snapshot->localTransform);
- snapshot->isSmallDirty = mSmallDirty;
+ snapshot->isSmallDirty =
+ mFlinger->mScheduler->isSmallDirtyArea(mOwnerAppId,
+ bounds.getWidth() * bounds.getHeight());
}
} // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 1e4f2dc..ce4b9c4 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -43,9 +43,7 @@
#include <scheduler/Fps.h>
#include <scheduler/Seamlessness.h>
-#include <chrono>
#include <cstdint>
-#include <list>
#include <optional>
#include <vector>
@@ -56,7 +54,6 @@
#include "LayerVector.h"
#include "Scheduler/LayerInfo.h"
#include "SurfaceFlinger.h"
-#include "Tracing/LayerTracing.h"
#include "TransactionCallbackInvoker.h"
using namespace android::surfaceflinger;
@@ -91,48 +88,15 @@
// Windows that are not in focus, but voted for a specific mode ID.
static constexpr int32_t PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
- enum { // flags for doTransaction()
- eDontUpdateGeometryState = 0x00000001,
- eVisibleRegion = 0x00000002,
- eInputInfoChanged = 0x00000004
- };
-
- struct Geometry {
- uint32_t w;
- uint32_t h;
- ui::Transform transform;
-
- inline bool operator==(const Geometry& rhs) const {
- return (w == rhs.w && h == rhs.h) && (transform.tx() == rhs.transform.tx()) &&
- (transform.ty() == rhs.transform.ty());
- }
- inline bool operator!=(const Geometry& rhs) const { return !operator==(rhs); }
- };
-
using FrameRate = scheduler::LayerInfo::FrameRate;
using FrameRateCompatibility = scheduler::FrameRateCompatibility;
using FrameRateSelectionStrategy = scheduler::LayerInfo::FrameRateSelectionStrategy;
struct State {
- int32_t z;
- ui::LayerStack layerStack;
- uint32_t flags;
int32_t sequence; // changes when visible regions can change
- bool modified;
// Crop is expressed in layer space coordinate.
- Rect crop;
+ FloatRect crop;
LayerMetadata metadata;
- // If non-null, a Surface this Surface's Z-order is interpreted relative to.
- wp<Layer> zOrderRelativeOf;
- bool isRelativeOf{false};
-
- // A list of surfaces whose Z-order is interpreted relative to ours.
- SortedVector<wp<Layer>> zOrderRelatives;
- half4 color;
- float cornerRadius;
- int backgroundBlurRadius;
- gui::WindowInfo inputInfo;
- wp<Layer> touchableRegionCrop;
ui::Dataspace dataspace;
@@ -154,52 +118,18 @@
std::shared_ptr<renderengine::ExternalTexture> buffer;
sp<Fence> acquireFence;
std::shared_ptr<FenceTime> acquireFenceTime;
- HdrMetadata hdrMetadata;
- Region surfaceDamageRegion;
- int32_t api;
sp<NativeHandle> sidebandStream;
mat4 colorTransform;
- bool hasColorTransform;
- // pointer to background color layer that, if set, appears below the buffer state layer
- // and the buffer state layer's children. Z order will be set to
- // INT_MIN
- sp<Layer> bgColorLayer;
// The deque of callback handles for this frame. The back of the deque contains the most
// recent callback handle.
std::deque<sp<CallbackHandle>> callbackHandles;
- bool colorSpaceAgnostic;
nsecs_t desiredPresentTime = 0;
bool isAutoTimestamp = true;
- // Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will
- // be rendered around the layer.
- float shadowRadius;
-
- // Layer regions that are made of custom materials, like frosted glass
- std::vector<BlurRegion> blurRegions;
-
- // Priority of the layer assigned by Window Manager.
- int32_t frameRateSelectionPriority;
-
- // Default frame rate compatibility used to set the layer refresh rate votetype.
- FrameRateCompatibility defaultFrameRateCompatibility;
- FrameRate frameRate;
-
// The combined frame rate of parents / children of this layer
FrameRate frameRateForLayerTree;
- FrameRateSelectionStrategy frameRateSelectionStrategy;
-
- // Set by window manager indicating the layer and all its children are
- // in a different orientation than the display. The hint suggests that
- // the graphic producers should receive a transform hint as if the
- // display was in this orientation. When the display changes to match
- // the layer orientation, the graphic producer may not need to allocate
- // a buffer of a different size. ui::Transform::ROT_INVALID means the
- // a fixed transform hint is not set.
- ui::Transform::RotationFlags fixedTransformHint;
-
// The vsync info that was used to start the transaction
FrameTimelineInfo frameTimelineInfo;
@@ -219,21 +149,12 @@
// An arbitrary threshold for the number of BufferlessSurfaceFrames in the state. Used to
// trigger a warning if the number of SurfaceFrames crosses the threshold.
static constexpr uint32_t kStateSurfaceFramesThreshold = 25;
-
- // Stretch effect to apply to this layer
- StretchEffect stretchEffect;
-
- // Whether or not this layer is a trusted overlay for input
- bool isTrustedOverlay;
Rect bufferCrop;
Rect destinationFrame;
sp<IBinder> releaseBufferEndpoint;
- gui::DropInputMode dropInputMode;
bool autoRefresh = false;
- bool dimmingEnabled = true;
float currentHdrSdrRatio = 1.f;
float desiredHdrSdrRatio = -1.f;
- gui::CachingHint cachingHint = gui::CachingHint::Enabled;
int64_t latchedVsyncId = 0;
bool useVsyncIdForRefreshRateSelection = false;
};
@@ -244,65 +165,14 @@
static bool isLayerFocusedBasedOnPriority(int32_t priority);
static void miniDumpHeader(std::string& result);
- // Provide unique string for each class type in the Layer hierarchy
- virtual const char* getType() const { return "Layer"; }
-
- // true if this layer is visible, false otherwise
- virtual bool isVisible() const;
-
- // Set a 2x2 transformation matrix on the layer. This transform
- // will be applied after parent transforms, but before any final
- // producer specified transform.
- bool setMatrix(const layer_state_t::matrix22_t& matrix);
-
// This second set of geometry attributes are controlled by
// setGeometryAppliesWithResize, and their default mode is to be
// immediate. If setGeometryAppliesWithResize is specified
// while a resize is pending, then update of these attributes will
// be delayed until the resize completes.
- // setPosition operates in parent buffer space (pre parent-transform) or display
- // space for top-level layers.
- bool setPosition(float x, float y);
// Buffer space
- bool setCrop(const Rect& crop);
-
- // TODO(b/38182121): Could we eliminate the various latching modes by
- // using the layer hierarchy?
- // -----------------------------------------------------------------------
- virtual bool setLayer(int32_t z);
- virtual bool setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ);
-
- virtual bool setAlpha(float alpha);
- bool setColor(const half3& /*color*/);
-
- // Set rounded corner radius for this layer and its children.
- //
- // We only support 1 radius per layer in the hierarchy, where parent layers have precedence.
- // The shape of the rounded corner rectangle is specified by the crop rectangle of the layer
- // from which we inferred the rounded corner radius.
- virtual bool setCornerRadius(float cornerRadius);
- // When non-zero, everything below this layer will be blurred by backgroundBlurRadius, which
- // is specified in pixels.
- virtual bool setBackgroundBlurRadius(int backgroundBlurRadius);
- virtual bool setBlurRegions(const std::vector<BlurRegion>& effectRegions);
- bool setTransparentRegionHint(const Region& transparent);
- virtual bool setTrustedOverlay(bool);
- virtual bool setFlags(uint32_t flags, uint32_t mask);
- virtual bool setLayerStack(ui::LayerStack);
- virtual ui::LayerStack getLayerStack(
- LayerVector::StateSet state = LayerVector::StateSet::Drawing) const;
-
- virtual bool setMetadata(const LayerMetadata& data);
- virtual void setChildrenDrawingParent(const sp<Layer>&);
- virtual bool setColorTransform(const mat4& matrix);
- virtual mat4 getColorTransform() const;
- virtual bool hasColorTransform() const;
- virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; }
- virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; }
- float getDesiredHdrSdrRatio() const { return getDrawingState().desiredHdrSdrRatio; }
- float getCurrentHdrSdrRatio() const { return getDrawingState().currentHdrSdrRatio; }
- gui::CachingHint getCachingHint() const { return getDrawingState().cachingHint; }
+ bool setCrop(const FloatRect& crop);
bool setTransform(uint32_t /*transform*/);
bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/);
@@ -314,110 +184,21 @@
bool setDataspace(ui::Dataspace /*dataspace*/);
bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
bool setDesiredHdrHeadroom(float desiredRatio);
- bool setCachingHint(gui::CachingHint cachingHint);
- bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/);
- bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/);
- bool setApi(int32_t /*api*/);
bool setSidebandStream(const sp<NativeHandle>& /*sidebandStream*/,
const FrameTimelineInfo& /* info*/, nsecs_t /* postTime */,
gui::GameMode gameMode);
bool setTransactionCompletedListeners(const std::vector<sp<CallbackHandle>>& /*handles*/,
bool willPresent);
- virtual bool setColorSpaceAgnostic(const bool agnostic);
- virtual bool setDimmingEnabled(const bool dimmingEnabled);
- virtual bool setFixedTransformHint(ui::Transform::RotationFlags fixedTransformHint);
- void setAutoRefresh(bool /* autoRefresh */);
- bool setDropInputMode(gui::DropInputMode);
- ui::Dataspace getDataSpace() const;
-
- virtual bool isFrontBuffered() const;
-
- virtual sp<LayerFE> getCompositionEngineLayerFE() const;
- virtual sp<LayerFE> copyCompositionEngineLayerFE() const;
sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
- sp<LayerFE> getOrCreateCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&);
-
- const frontend::LayerSnapshot* getLayerSnapshot() const;
- frontend::LayerSnapshot* editLayerSnapshot();
- std::unique_ptr<frontend::LayerSnapshot> stealLayerSnapshot();
- void updateLayerSnapshot(std::unique_ptr<frontend::LayerSnapshot> snapshot);
// If we have received a new buffer this frame, we will pass its surface
// damage down to hardware composer. Otherwise, we must send a region with
// one empty rect.
- void useSurfaceDamage();
- void useEmptyDamage();
Region getVisibleRegion(const DisplayDevice*) const;
void updateLastLatchTime(nsecs_t latchtime);
- /*
- * isOpaque - true if this surface is opaque
- *
- * This takes into account the buffer format (i.e. whether or not the
- * pixel format includes an alpha channel) and the "opaque" flag set
- * on the layer. It does not examine the current plane alpha value.
- */
- bool isOpaque(const Layer::State&) const;
-
- /*
- * Returns whether this layer can receive input.
- */
- bool canReceiveInput() const;
-
- /*
- * Whether or not the layer should be considered visible for input calculations.
- */
- virtual bool isVisibleForInput() const {
- // For compatibility reasons we let layers which can receive input
- // receive input before they have actually submitted a buffer. Because
- // of this we use canReceiveInput instead of isVisible to check the
- // policy-visibility, ignoring the buffer state. However for layers with
- // hasInputInfo()==false we can use the real visibility state.
- // We are just using these layers for occlusion detection in
- // InputDispatcher, and obviously if they aren't visible they can't occlude
- // anything.
- return hasInputInfo() ? canReceiveInput() : isVisible();
- }
-
- /*
- * isProtected - true if the layer may contain protected contents in the
- * GRALLOC_USAGE_PROTECTED sense.
- */
- bool isProtected() const;
-
- /*
- * isFixedSize - true if content has a fixed size
- */
- virtual bool isFixedSize() const { return true; }
-
- /*
- * usesSourceCrop - true if content should use a source crop
- */
- bool usesSourceCrop() const { return hasBufferOrSidebandStream(); }
-
- // Most layers aren't created from the main thread, and therefore need to
- // grab the SF state lock to access HWC, but ContainerLayer does, so we need
- // to avoid grabbing the lock again to avoid deadlock
- virtual bool isCreatedFromMainThread() const { return false; }
-
- ui::Transform getActiveTransform(const Layer::State& s) const { return s.transform; }
- Region getActiveTransparentRegion(const Layer::State& s) const {
- return s.transparentRegionHint;
- }
- Rect getCrop(const Layer::State& s) const { return s.crop; }
- bool needsFiltering(const DisplayDevice*) const;
-
- // True if this layer requires filtering
- // This method is distinct from needsFiltering() in how the filter
- // requirement is computed. needsFiltering() compares displayFrame and crop,
- // where as this method transforms the displayFrame to layer-stack space
- // first. This method should be used if there is no physical display to
- // project onto when taking screenshots, as the filtering requirements are
- // different.
- // If the parent transform needs to be undone when capturing the layer, then
- // the inverse parent transform is also required.
- bool needsFilteringForScreenshots(const DisplayDevice*, const ui::Transform&) const;
+ Rect getCrop(const Layer::State& s) const { return Rect(s.crop); }
// from graphics API
static ui::Dataspace translateDataspace(ui::Dataspace dataspace);
@@ -437,46 +218,10 @@
* operation, so this should be set only if needed). Typically this is used
* to figure out if the content or size of a surface has changed.
*/
- bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/);
-
bool latchBufferImpl(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/,
bool bgColorOnly);
- /*
- * Returns true if the currently presented buffer will be released when this layer state
- * is latched. This will return false if there is no buffer currently presented.
- */
- bool willReleaseBufferOnLatch() const;
-
- /*
- * Calls latchBuffer if the buffer has a frame queued and then releases the buffer.
- * This is used if the buffer is just latched and releases to free up the buffer
- * and will not be shown on screen.
- * Should only be called on the main thread.
- */
- void latchAndReleaseBuffer();
-
- /*
- * returns the rectangle that crops the content of the layer and scales it
- * to the layer's size.
- */
- Rect getBufferCrop() const;
-
- /*
- * Returns the transform applied to the buffer.
- */
- uint32_t getBufferTransform() const;
-
sp<GraphicBuffer> getBuffer() const;
- const std::shared_ptr<renderengine::ExternalTexture>& getExternalTexture() const;
-
- /*
- * Returns if a frame is ready
- */
- bool hasReadyFrame() const;
-
- virtual int32_t getQueuedFrameCount() const { return 0; }
-
/**
* Returns active buffer size in the correct orientation. Buffer size is determined by undoing
* any buffer transformations. Returns Rect::INVALID_RECT if the layer has no buffer or the
@@ -484,33 +229,10 @@
*/
Rect getBufferSize(const Layer::State&) const;
- /**
- * Returns the source bounds. If the bounds are not defined, it is inferred from the
- * buffer size. Failing that, the bounds are determined from the passed in parent bounds.
- * For the root layer, this is the display viewport size.
- */
- FloatRect computeSourceBounds(const FloatRect& parentBounds) const;
- virtual FrameRate getFrameRateForLayerTree() const;
+ FrameRate getFrameRateForLayerTree() const;
bool getTransformToDisplayInverse() const;
- // Returns how rounded corners should be drawn for this layer.
- // A layer can override its parent's rounded corner settings if the parent's rounded
- // corner crop does not intersect with its own rounded corner crop.
- virtual frontend::RoundedCornerState getRoundedCornerState() const;
-
- bool hasRoundedCorners() const { return getRoundedCornerState().hasRoundedCorners(); }
-
- PixelFormat getPixelFormat() const;
- /**
- * Return whether this layer needs an input info. We generate InputWindowHandles for all
- * non-cursor buffered layers regardless of whether they have an InputChannel. This is to enable
- * the InputDispatcher to do PID based occlusion detection.
- */
- bool needsInputInfo() const {
- return (hasInputInfo() || hasBufferOrSidebandStream()) && !mPotentialCursor;
- }
-
// Implements RefBase.
void onFirstRef() override;
@@ -521,17 +243,11 @@
uint32_t mTransform{0};
ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN};
Rect mCrop;
- uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE};
- Region mSurfaceDamage;
- HdrMetadata mHdrMetadata;
- int mApi;
PixelFormat mPixelFormat{PIXEL_FORMAT_NONE};
bool mTransformToDisplayInverse{false};
-
std::shared_ptr<renderengine::ExternalTexture> mBuffer;
uint64_t mFrameNumber;
sp<IBinder> mReleaseBufferEndpoint;
-
bool mFrameLatencyNeeded{false};
float mDesiredHdrSdrRatio = -1.f;
};
@@ -539,8 +255,6 @@
BufferInfo mBufferInfo;
std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseChannel;
- // implements compositionengine::LayerFE
- const compositionengine::LayerFECompositionState* getCompositionState() const;
bool fenceHasSignaled() const;
void onPreComposition(nsecs_t refreshStartTime);
void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack,
@@ -561,17 +275,6 @@
const char* getDebugName() const;
- bool setShadowRadius(float shadowRadius);
-
- // Before color management is introduced, contents on Android have to be
- // desaturated in order to match what they appears like visually.
- // With color management, these contents will appear desaturated, thus
- // needed to be saturated so that they match what they are designed for
- // visually.
- bool isLegacyDataSpace() const;
-
- uint32_t getTransactionFlags() const { return mTransactionFlags; }
-
static bool computeTrustedPresentationState(const FloatRect& bounds,
const FloatRect& sourceBounds,
const Region& coveredRegion,
@@ -589,17 +292,6 @@
// Sets the masked bits.
void setTransactionFlags(uint32_t mask);
- // Clears and returns the masked bits.
- uint32_t clearTransactionFlags(uint32_t mask);
-
- FloatRect getBounds(const Region& activeTransparentRegion) const;
- FloatRect getBounds() const;
- Rect getInputBoundsInDisplaySpace(const FloatRect& insetBounds,
- const ui::Transform& displayTransform);
-
- // Compute bounds for the layer and cache the results.
- void computeBounds(FloatRect parentBounds, ui::Transform parentTransform, float shadowRadius);
-
int32_t getSequence() const { return sequence; }
// For tracing.
@@ -610,90 +302,20 @@
// only used within a single layer.
uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; }
- /*
- * isSecure - true if this surface is secure, that is if it prevents
- * screenshots or VNC servers. A surface can be set to be secure by the
- * application, being secure doesn't mean the surface has DRM contents.
- */
- bool isSecure() const;
-
- /*
- * isHiddenByPolicy - true if this layer has been forced invisible.
- * just because this is false, doesn't mean isVisible() is true.
- * For example if this layer has no active buffer, it may not be hidden by
- * policy, but it still can not be visible.
- */
- bool isHiddenByPolicy() const;
-
- // True if the layer should be skipped in screenshots, screen recordings,
- // and mirroring to external or virtual displays.
- bool isInternalDisplayOverlay() const;
-
- ui::LayerFilter getOutputFilter() const {
- return {getLayerStack(), isInternalDisplayOverlay()};
- }
-
- perfetto::protos::LayerProto* writeToProto(perfetto::protos::LayersProto& layersProto,
- uint32_t traceFlags);
void writeCompositionStateToProto(perfetto::protos::LayerProto* layerProto,
ui::LayerStack layerStack);
- // Write states that are modified by the main thread. This includes drawing
- // state as well as buffer data. This should be called in the main or tracing
- // thread.
- void writeToProtoDrawingState(perfetto::protos::LayerProto* layerInfo);
- // Write drawing or current state. If writing current state, the caller should hold the
- // external mStateLock. If writing drawing state, this function should be called on the
- // main or tracing thread.
- void writeToProtoCommonState(perfetto::protos::LayerProto* layerInfo, LayerVector::StateSet,
- uint32_t traceFlags = LayerTracing::TRACE_ALL);
-
- gui::WindowInfo::Type getWindowType() const { return mWindowType; }
-
- /*
- * doTransaction - process the transaction. This is a good place to figure
- * out which attributes of the surface have changed.
- */
- virtual uint32_t doTransaction(uint32_t transactionFlags);
-
- /*
- * Remove relative z for the layer if its relative parent is not part of the
- * provided layer tree.
- */
- void removeRelativeZ(const std::vector<Layer*>& layersInTree);
-
inline const State& getDrawingState() const { return mDrawingState; }
inline State& getDrawingState() { return mDrawingState; }
void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
- void dumpOffscreenDebugInfo(std::string& result) const;
void clearFrameStats();
void logFrameStats();
void getFrameStats(FrameStats* outStats) const;
void onDisconnect();
- ui::Transform getTransform() const;
- bool isTransformValid() const;
-
- // Returns the Alpha of the Surface, accounting for the Alpha
- // of parent Surfaces in the hierarchy (alpha's will be multiplied
- // down the hierarchy).
- half getAlpha() const;
- half4 getColor() const;
- int32_t getBackgroundBlurRadius() const;
- bool drawShadows() const { return mEffectiveShadowRadius > 0.f; };
-
- // Returns the transform hint set by Window Manager on the layer or one of its parents.
- // This traverses the current state because the data is needed when creating
- // the layer(off drawing thread) and the hint should be available before the producer
- // is ready to acquire a buffer.
- ui::Transform::RotationFlags getFixedTransformHint() const;
-
- bool isHandleAlive() const { return mHandleAlive; }
bool onHandleDestroyed() { return mHandleAlive = false; }
- Rect getScreenBounds(bool reduceTransparentRegion = true) const;
- int32_t getZ(LayerVector::StateSet) const;
/**
* Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
@@ -703,7 +325,6 @@
*/
Rect getCroppedBufferSize(const Layer::State& s) const;
- virtual void setFrameTimelineInfoForBuffer(const FrameTimelineInfo& /*info*/) {}
void setFrameTimelineVsyncForBufferTransaction(const FrameTimelineInfo& info, nsecs_t postTime,
gui::GameMode gameMode);
void setFrameTimelineVsyncForBufferlessTransaction(const FrameTimelineInfo& info,
@@ -732,31 +353,9 @@
// this to be called once.
sp<IBinder> getHandle();
const std::string& getName() const { return mName; }
- bool getPremultipledAlpha() const;
- void setInputInfo(const gui::WindowInfo& info);
-
- struct InputDisplayArgs {
- const ui::Transform* transform = nullptr;
- bool isSecure = false;
- };
- gui::WindowInfo fillInputInfo(const InputDisplayArgs& displayArgs);
-
- /**
- * Returns whether this layer has an explicitly set input-info.
- */
- bool hasInputInfo() const;
virtual uid_t getOwnerUid() const { return mOwnerUid; }
- pid_t getOwnerPid() { return mOwnerPid; }
-
- int32_t getOwnerAppId() { return mOwnerAppId; }
-
- mutable bool contentDirty{false};
- Region surfaceDamageRegion;
-
- // True when the surfaceDamageRegion is recognized as a small area update.
- bool mSmallDirty{false};
// Used to check if mUsedVsyncIdForRefreshRateSelection should be expired when it stop updating.
nsecs_t mMaxTimeForUseVsyncId = 0;
// True when DrawState.useVsyncIdForRefreshRateSelection previously set to true during updating
@@ -768,37 +367,10 @@
// the same.
const int32_t sequence;
- bool mPendingHWCDestroy{false};
-
- bool backpressureEnabled() const {
- return mDrawingState.flags & layer_state_t::eEnableBackpressure;
- }
-
- bool setStretchEffect(const StretchEffect& effect);
-
- bool setBufferCrop(const Rect& /* bufferCrop */);
- bool setDestinationFrame(const Rect& /* destinationFrame */);
// See mPendingBufferTransactions
void decrementPendingBufferCount();
- std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBufferTransactions; }
+ std::atomic<int32_t>* getPendingBufferCounter() { return &mPendingBuffers; }
std::string getPendingBufferCounterName() { return mBlastTransactionName; }
- bool updateGeometry();
-
- bool isSimpleBufferUpdate(const layer_state_t& s) const;
-
- static bool isOpaqueFormat(PixelFormat format);
-
- // Updates the LayerSnapshot. This must be called prior to sending layer data to
- // CompositionEngine or RenderEngine (i.e. before calling CompositionEngine::present or
- // LayerFE::prepareClientComposition).
- //
- // TODO(b/238781169) Remove direct calls to RenderEngine::drawLayers that don't go through
- // CompositionEngine to create a single path for composing layers.
- void updateSnapshot(bool updateGeometry);
- void updateChildrenSnapshots(bool updateGeometry);
-
- bool willPresentCurrentTransaction() const;
-
void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener,
const sp<GraphicBuffer>& buffer, uint64_t framenumber,
const sp<Fence>& releaseFence);
@@ -844,7 +416,6 @@
const sp<SurfaceFlinger> mFlinger;
// Check if the damage region is a small dirty.
- void setIsSmallDirty(const Region& damageRegion, const ui::Transform& layerToDisplayTransform);
void setIsSmallDirty(frontend::LayerSnapshot* snapshot);
protected:
@@ -856,52 +427,16 @@
friend class TransactionFrameTracerTest;
friend class TransactionSurfaceFrameTest;
- void preparePerFrameCompositionState();
- void preparePerFrameBufferCompositionState();
- void preparePerFrameEffectsCompositionState();
void gatherBufferInfo();
- void prepareBasicGeometryCompositionState();
- void prepareGeometryCompositionState();
- void prepareCursorCompositionState();
-
- uint32_t getEffectiveUsage(uint32_t usage) const;
-
- /**
- * Setup rounded corners coordinates of this layer, taking into account the layer bounds and
- * crop coordinates, transforming them into layer space.
- */
- void setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const;
- void setParent(const sp<Layer>&);
- LayerVector makeTraversalList(LayerVector::StateSet, bool* outSkipRelativeZUsers);
- void addZOrderRelative(const wp<Layer>& relative);
- void removeZOrderRelative(const wp<Layer>& relative);
compositionengine::OutputLayer* findOutputLayerForDisplay(const DisplayDevice*) const;
compositionengine::OutputLayer* findOutputLayerForDisplay(
const DisplayDevice*, const frontend::LayerHierarchy::TraversalPath& path) const;
- bool usingRelativeZ(LayerVector::StateSet) const;
- virtual ui::Transform getInputTransform() const;
- /**
- * Get the bounds in layer space within which this layer can receive input.
- *
- * These bounds are used to:
- * - Determine the input frame for the layer to be used for occlusion detection; and
- * - Determine the coordinate space within which the layer will receive input. The top-left of
- * this rect will be the origin of the coordinate space that the input events sent to the
- * layer will be in (prior to accounting for surface insets).
- *
- * The layer can still receive touch input if these bounds are invalid if
- * "replaceTouchableRegionWithCrop" is specified. In this case, the layer will receive input
- * in this layer's space, regardless of the specified crop layer.
- */
- std::pair<FloatRect, bool> getInputBounds(bool fillParentBounds) const;
-
- bool mPremultipliedAlpha{true};
const std::string mName;
const std::string mTransactionName{"TX - " + mName};
- // These are only accessed by the main thread or the tracing thread.
+ // These are only accessed by the main thread.
State mDrawingState;
TrustedPresentationThresholds mTrustedPresentationThresholds;
@@ -911,44 +446,22 @@
int64_t mEnteredTrustedPresentationStateTime = -1;
uint32_t mTransactionFlags{0};
- // Updated in doTransaction, used to track the last sequence number we
- // committed. Currently this is really only used for updating visible
- // regions.
- int32_t mLastCommittedTxSequence = -1;
// Timestamp history for UIAutomation. Thread safe.
FrameTracker mFrameTracker;
// main thread
sp<NativeHandle> mSidebandStream;
- // False if the buffer and its contents have been previously used for GPU
- // composition, true otherwise.
- bool mIsActiveBufferUpdatedForGpu = true;
// We encode unset as -1.
std::atomic<uint64_t> mCurrentFrameNumber{0};
- // Whether filtering is needed b/c of the drawingstate
- bool mNeedsFiltering{false};
-
- std::atomic<bool> mRemovedFromDrawingState{false};
-
- // page-flip thread (currently main thread)
- bool mProtectedByApp{false}; // application requires protected path to external sink
// protected by mLock
mutable Mutex mLock;
- const wp<Client> mClientRef;
-
// This layer can be a cursor on some displays.
bool mPotentialCursor{false};
- LayerVector mCurrentChildren{LayerVector::StateSet::Current};
- LayerVector mDrawingChildren{LayerVector::StateSet::Drawing};
-
- wp<Layer> mCurrentParent;
- wp<Layer> mDrawingParent;
-
// Window types from WindowManager.LayoutParams
const gui::WindowInfo::Type mWindowType;
@@ -966,8 +479,6 @@
// Used in buffer stuffing analysis in FrameTimeline.
nsecs_t mLastLatchTime = 0;
- mutable bool mDrawingStateModified = false;
-
sp<Fence> mLastClientCompositionFence;
bool mClearClientCompositionFenceOnLayerDisplayed = false;
private:
@@ -979,44 +490,20 @@
friend class TransactionFrameTracerTest;
friend class TransactionSurfaceFrameTest;
- bool getAutoRefresh() const { return mDrawingState.autoRefresh; }
bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
std::atomic<bool> mSidebandStreamChanged{false};
- // Returns true if the layer can draw shadows on its border.
- virtual bool canDrawShadows() const { return true; }
-
aidl::android::hardware::graphics::composer3::Composition getCompositionType(
const DisplayDevice&) const;
aidl::android::hardware::graphics::composer3::Composition getCompositionType(
const compositionengine::OutputLayer*) const;
- bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
- bool* transactionNeeded);
- void setZOrderRelativeOf(const wp<Layer>& relativeOf);
- bool isTrustedOverlay() const;
- gui::DropInputMode getDropInputMode() const;
- void handleDropInputMode(gui::WindowInfo& info) const;
-
- // Finds the top most layer in the hierarchy. This will find the root Layer where the parent is
- // null.
- sp<Layer> getRootLayer();
-
- // Fills in the touch occlusion mode of the first parent (including this layer) that
- // hasInputInfo() or no-op if no such parent is found.
- void fillTouchOcclusionMode(gui::WindowInfo& info);
-
- // Fills in the frame and transform info for the gui::WindowInfo.
- void fillInputFrameInfo(gui::WindowInfo&, const ui::Transform& screenToDisplay);
-
inline void tracePendingBufferCount(int32_t pendingBuffers);
// Latch sideband stream and returns true if the dirty region should be updated.
bool latchSidebandStream(bool& recomputeVisibleRegions);
- bool hasFrameUpdate() const;
-
void updateTexImage(nsecs_t latchTime, bool bgColorOnly = false);
// Crop that applies to the buffer
@@ -1027,15 +514,6 @@
const sp<Fence>& releaseFence,
uint32_t currentMaxAcquiredBufferCount);
- // Returns true if the transformed buffer size does not match the layer size and we need
- // to apply filtering.
- bool bufferNeedsFiltering() const;
-
- // Returns true if there is a valid color to fill.
- bool fillsColor() const;
- // Returns true if this layer has a blur value.
- bool hasBlur() const;
- bool hasEffect() const { return fillsColor() || drawShadows() || hasBlur(); }
bool hasBufferOrSidebandStream() const {
return ((mSidebandStream != nullptr) || (mBufferInfo.mBuffer != nullptr));
}
@@ -1044,33 +522,6 @@
return ((mDrawingState.sidebandStream != nullptr) || (mDrawingState.buffer != nullptr));
}
- bool hasSomethingToDraw() const { return hasEffect() || hasBufferOrSidebandStream(); }
-
- bool shouldOverrideChildrenFrameRate() const {
- return getDrawingState().frameRateSelectionStrategy ==
- FrameRateSelectionStrategy::OverrideChildren;
- }
-
- bool shouldPropagateFrameRate() const {
- return getDrawingState().frameRateSelectionStrategy != FrameRateSelectionStrategy::Self;
- }
-
- // Cached properties computed from drawing state
- // Effective transform taking into account parent transforms and any parent scaling, which is
- // a transform from the current layer coordinate space to display(screen) coordinate space.
- ui::Transform mEffectiveTransform;
-
- // Bounds of the layer before any transformation is applied and before it has been cropped
- // by its parents.
- FloatRect mSourceBounds;
-
- // Bounds of the layer in layer space. This is the mSourceBounds cropped by its layer crop and
- // its parent bounds.
- FloatRect mBounds;
-
- // Layer bounds in screen space.
- FloatRect mScreenBounds;
-
bool mGetHandleCalled = false;
// The inherited shadow radius after taking into account the layer hierarchy. This is the
@@ -1081,15 +532,10 @@
// Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats.
gui::GameMode mGameMode = gui::GameMode::Unsupported;
- // A list of regions on this layer that should have blurs.
- const std::vector<BlurRegion> getBlurRegions() const;
-
bool mIsAtRoot = false;
uint32_t mLayerCreationFlags;
- bool findInHierarchy(const sp<Layer>&);
-
void releasePreviousBuffer();
void resetDrawingStateBufferInfo();
@@ -1115,17 +561,14 @@
// You can understand the trace this way:
// - 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 or doTransaction, a buffer was dropped
- std::atomic<int32_t> mPendingBufferTransactions{0};
+ // - If the integer decreases in setBuffer, a buffer was dropped
+ 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.
ui::Transform mRequestedTransform;
- sp<LayerFE> mLegacyLayerFE;
std::vector<std::pair<frontend::LayerHierarchy::TraversalPath, sp<LayerFE>>> mLayerFEs;
- std::unique_ptr<frontend::LayerSnapshot> mSnapshot =
- std::make_unique<frontend::LayerSnapshot>();
bool mHandleAlive = false;
};
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index 885c3d3..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) {
@@ -178,12 +185,8 @@
}
void LayerProtoHelper::writeToProto(
- const WindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds,
+ 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());
@@ -208,13 +211,6 @@
proto->set_global_scale_factor(inputInfo.globalScaleFactor);
LayerProtoHelper::writeToProtoDeprecated(inputInfo.transform, proto->mutable_transform());
proto->set_replace_touchable_region_with_crop(inputInfo.replaceTouchableRegionWithCrop);
- auto cropLayer = touchableRegionBounds.promote();
- if (cropLayer != nullptr) {
- proto->set_crop_layer_id(cropLayer->sequence);
- LayerProtoHelper::writeToProto(cropLayer->getScreenBounds(
- false /* reduceTransparentRegion */),
- [&]() { return proto->mutable_touchable_region_crop(); });
- }
}
void LayerProtoHelper::writeToProto(const mat4 matrix,
@@ -434,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(); });
@@ -462,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);
@@ -482,7 +478,7 @@
layerInfo->set_owner_uid(requestedState.ownerUid.val());
if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) {
- LayerProtoHelper::writeToProto(snapshot.inputInfo, {},
+ LayerProtoHelper::writeToProto(snapshot.inputInfo,
[&]() { return layerInfo->mutable_input_window_info(); });
}
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
index c0198b6..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,
@@ -62,7 +63,7 @@
const renderengine::ExternalTexture& buffer,
std::function<perfetto::protos::ActiveBufferProto*()> getActiveBufferProto);
static void writeToProto(
- const gui::WindowInfo& inputInfo, const wp<Layer>& touchableRegionBounds,
+ const gui::WindowInfo& inputInfo,
std::function<perfetto::protos::InputWindowInfoProto*()> getInputWindowInfoProto);
static void writeToProto(const mat4 matrix,
perfetto::protos::ColorTransformProto* colorTransformProto);
diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp
index ff0a955..13e054e 100644
--- a/services/surfaceflinger/LayerVector.cpp
+++ b/services/surfaceflinger/LayerVector.cpp
@@ -45,16 +45,6 @@
const auto& lState = l->getDrawingState();
const auto& rState = r->getDrawingState();
- const auto ls = lState.layerStack;
- const auto rs = rState.layerStack;
- if (ls != rs)
- return (ls > rs) ? 1 : -1;
-
- int32_t lz = lState.z;
- int32_t rz = rState.z;
- if (lz != rz)
- return (lz > rz) ? 1 : -1;
-
if (l->sequence == r->sequence)
return 0;
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/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 7712d38..06c2f26 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -346,6 +346,7 @@
constexpr bool kRegionSampling = true;
constexpr bool kGrayscale = false;
constexpr bool kIsProtected = false;
+ constexpr bool kAttachGainmap = false;
SurfaceFlinger::RenderAreaBuilderVariant
renderAreaBuilder(std::in_place_type<DisplayRenderAreaBuilder>, sampledBounds,
@@ -358,15 +359,15 @@
std::vector<sp<LayerFE>> layerFEs;
auto displayState = mFlinger.getSnapshotsFromMainThread(renderAreaBuilder,
getLayerSnapshotsFn, layerFEs);
- fenceResult =
- mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale,
- kIsProtected, nullptr, displayState, layerFEs)
- .get();
+ fenceResult = mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling,
+ kGrayscale, kIsProtected, kAttachGainmap, nullptr,
+ displayState, layerFEs)
+ .get();
} else {
- fenceResult =
- mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, buffer,
- kRegionSampling, kGrayscale, kIsProtected, nullptr)
- .get();
+ fenceResult = mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn,
+ buffer, kRegionSampling, kGrayscale,
+ kIsProtected, kAttachGainmap, nullptr)
+ .get();
}
if (fenceResult.ok()) {
fenceResult.value()->waitForever(LOG_TAG);
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 034e467..aa66ccf 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -39,21 +39,6 @@
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill) {}
- static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda(
- std::function<void(const LayerVector::Visitor&)> traverseLayers) {
- return [traverseLayers = std::move(traverseLayers)]() {
- std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
- traverseLayers([&](Layer* layer) {
- // Layer::prepareClientComposition uses the layer's snapshot to populate the
- // resulting LayerSettings. Calling Layer::updateSnapshot ensures that LayerSettings
- // are generated with the layer's current buffer and geometry.
- layer->updateSnapshot(true /* updateGeometry */);
- layers.emplace_back(layer, layer->copyCompositionEngineLayerFE());
- });
- return layers;
- };
- }
-
virtual ~RenderArea() = default;
// Returns true if the render area is secure. A secure layer should be
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index d31fcea..e385f18 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -320,7 +320,8 @@
mVsyncRegistration.update({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .lastVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns(),
+ .committedVsyncOpt = mLastCommittedVsyncTime.ns()});
}
sp<EventThreadConnection> EventThread::createEventConnection(
@@ -425,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();
}
@@ -485,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 {
@@ -527,10 +528,11 @@
}
if (mState == State::VSync) {
- const auto scheduleResult =
- mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
- .readyDuration = mReadyDuration.count(),
- .lastVsync = mLastVsyncCallbackTime.ns()});
+ const auto scheduleResult = mVsyncRegistration.schedule(
+ {.workDuration = mWorkDuration.get().count(),
+ .readyDuration = mReadyDuration.count(),
+ .lastVsync = mLastVsyncCallbackTime.ns(),
+ .committedVsyncOpt = mLastCommittedVsyncTime.ns()});
LOG_ALWAYS_FATAL_IF(!scheduleResult, "Error scheduling callback");
} else {
mVsyncRegistration.cancel();
@@ -557,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));
}
@@ -725,8 +727,9 @@
}
if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC &&
FlagManager::getInstance().vrr_config()) {
- mCallback.onExpectedPresentTimePosted(
- TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime()));
+ mLastCommittedVsyncTime =
+ TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime());
+ mCallback.onExpectedPresentTimePosted(mLastCommittedVsyncTime);
}
}
@@ -736,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");
@@ -744,9 +747,12 @@
const auto relativeLastCallTime =
ticks<std::milli, float>(mLastVsyncCallbackTime - TimePoint::now());
+ const auto relativeLastCommittedTime =
+ ticks<std::milli, float>(mLastCommittedVsyncTime - TimePoint::now());
StringAppendF(&result, "mWorkDuration=%.2f mReadyDuration=%.2f last vsync time ",
mWorkDuration.get().count() / 1e6f, mReadyDuration.count() / 1e6f);
StringAppendF(&result, "%.2fms relative to now\n", relativeLastCallTime);
+ StringAppendF(&result, " with vsync committed at %.2fms", relativeLastCommittedTime);
StringAppendF(&result, " pending events (count=%zu):\n", mPendingEvents.size());
for (const auto& event : mPendingEvents) {
@@ -794,7 +800,8 @@
if (reschedule) {
mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(),
.readyDuration = mReadyDuration.count(),
- .lastVsync = mLastVsyncCallbackTime.ns()});
+ .lastVsync = mLastVsyncCallbackTime.ns(),
+ .committedVsyncOpt = mLastCommittedVsyncTime.ns()});
}
return oldRegistration;
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index f772126..c3c7eb0 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -220,6 +220,7 @@
std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex);
std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule GUARDED_BY(mMutex);
TimePoint mLastVsyncCallbackTime GUARDED_BY(mMutex) = TimePoint::now();
+ TimePoint mLastCommittedVsyncTime GUARDED_BY(mMutex) = TimePoint::now();
scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex);
frametimeline::TokenManager* const mTokenManager;
@@ -234,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/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index dbc458c..ff1926e 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -595,8 +595,7 @@
return true;
}
- if (FlagManager::getInstance().view_set_requested_frame_rate_mrr() &&
- category == FrameRateCategory::NoPreference && vote.rate.isValid() &&
+ if (category == FrameRateCategory::NoPreference && vote.rate.isValid() &&
vote.type == FrameRateCompatibility::ExactOrMultiple) {
return true;
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 9f6eab2..ab9014e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -841,7 +841,8 @@
return score.overallScore == 0;
});
- if (policy->primaryRangeIsSingleRate()) {
+ // TODO(b/364651864): Evaluate correctness of primaryRangeIsSingleRate.
+ if (!mIsVrrDevice.load() && policy->primaryRangeIsSingleRate()) {
// If we never scored any layers, then choose the rate from the primary
// range instead of picking a random score from the app range.
if (noLayerScore) {
@@ -887,8 +888,8 @@
const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
using fps_approx_ops::operator<;
- if (scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
- ALOGV("Touch Boost");
+ if (scores.front().frameRateMode.fps <= touchRefreshRates.front().frameRateMode.fps) {
+ ALOGV("Touch Boost [late]");
SFTRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
to_string(touchRefreshRates.front().frameRateMode.fps).c_str());
return {touchRefreshRates, GlobalSignals{.touch = true}};
@@ -1394,13 +1395,14 @@
const auto& idleScreenConfigOpt = getCurrentPolicyLocked()->idleScreenConfigOpt;
if (idleScreenConfigOpt != oldPolicy.idleScreenConfigOpt) {
if (!idleScreenConfigOpt.has_value()) {
- // fallback to legacy timer if existed, otherwise pause the old timer
- LOG_ALWAYS_FATAL_IF(!mIdleTimer);
- if (mConfig.legacyIdleTimerTimeout > 0ms) {
- mIdleTimer->setInterval(mConfig.legacyIdleTimerTimeout);
- mIdleTimer->resume();
- } else {
- mIdleTimer->pause();
+ if (mIdleTimer) {
+ // fallback to legacy timer if existed, otherwise pause the old timer
+ if (mConfig.legacyIdleTimerTimeout > 0ms) {
+ mIdleTimer->setInterval(mConfig.legacyIdleTimerTimeout);
+ mIdleTimer->resume();
+ } else {
+ mIdleTimer->pause();
+ }
}
} else if (idleScreenConfigOpt->timeoutMillis > 0) {
// create a new timer or reconfigure
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index be00079..b83ff19 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -38,7 +38,6 @@
#include <FrameTimeline/FrameTimeline.h>
#include <scheduler/interface/ICompositor.h>
-#include <algorithm>
#include <cinttypes>
#include <cstdint>
#include <functional>
@@ -46,16 +45,15 @@
#include <numeric>
#include <common/FlagManager.h>
-#include "../Layer.h"
#include "EventThread.h"
#include "FrameRateOverrideMappings.h"
#include "FrontEnd/LayerHandle.h"
+#include "Layer.h"
#include "OneShotTimer.h"
#include "RefreshRateStats.h"
#include "SurfaceFlingerFactory.h"
#include "SurfaceFlingerProperties.h"
#include "TimeStats/TimeStats.h"
-#include "VSyncTracker.h"
#include "VsyncConfiguration.h"
#include "VsyncController.h"
#include "VsyncSchedule.h"
@@ -361,10 +359,8 @@
if (cycle == Cycle::Render) {
mRenderEventThread = std::move(eventThread);
- mRenderEventConnection = mRenderEventThread->createEventConnection();
} else {
mLastCompositeEventThread = std::move(eventThread);
- mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection();
}
}
@@ -430,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,
@@ -450,6 +448,7 @@
return isPacesetter;
}
+#pragma clang diagnostic pop
void Scheduler::emitModeChangeIfNeeded() {
if (!mPolicy.modeOpt || !mPolicy.emittedModeOpt) {
@@ -487,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,
@@ -498,6 +499,7 @@
setVsyncConfig(mVsyncModulator->setVsyncConfigSet(mVsyncConfiguration->getCurrentConfigs()),
refreshRate.getPeriod());
}
+#pragma clang diagnostic pop
void Scheduler::setActiveDisplayPowerModeForRefreshRateStats(hal::PowerMode powerMode) {
mRefreshRateStats->setPowerMode(powerMode);
@@ -913,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));
@@ -921,6 +925,7 @@
onFrameRateOverridesChanged();
}
}
+#pragma clang diagnostic pop
bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals,
Fps displayRefreshRate) {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 1367ec3..c88b563 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -145,10 +145,6 @@
Cycle, EventRegistrationFlags eventRegistration = {},
const sp<IBinder>& layerHandle = nullptr) EXCLUDES(mChoreographerLock);
- const sp<EventThreadConnection>& getEventConnection(Cycle cycle) const {
- return cycle == Cycle::Render ? mRenderEventConnection : mLastCompositeEventConnection;
- }
-
enum class Hotplug { Connected, Disconnected };
void dispatchHotplug(PhysicalDisplayId, Hotplug);
@@ -467,10 +463,7 @@
void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock);
std::unique_ptr<EventThread> mRenderEventThread;
- sp<EventThreadConnection> mRenderEventConnection;
-
std::unique_ptr<EventThread> mLastCompositeEventThread;
- sp<EventThreadConnection> mLastCompositeEventConnection;
std::atomic<nsecs_t> mLastResyncTime = 0;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 0c43ffb..8993c38 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -93,6 +93,8 @@
* readyDuration will typically be 0.
* @lastVsync: The targeted display time. This will be snapped to the closest
* predicted vsync time after lastVsync.
+ * @committedVsyncOpt: The display time that is committed to the callback as the
+ * target vsync time.
*
* callback will be dispatched at 'workDuration + readyDuration' nanoseconds before a vsync
* event.
@@ -101,10 +103,11 @@
nsecs_t workDuration = 0;
nsecs_t readyDuration = 0;
nsecs_t lastVsync = 0;
+ std::optional<nsecs_t> committedVsyncOpt;
bool operator==(const ScheduleTiming& other) const {
return workDuration == other.workDuration && readyDuration == other.readyDuration &&
- lastVsync == other.lastVsync;
+ lastVsync == other.lastVsync && committedVsyncOpt == other.committedVsyncOpt;
}
bool operator!=(const ScheduleTiming& other) const { return !(*this == other); }
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 900bce0..1925f11 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -103,7 +103,8 @@
tracker.nextAnticipatedVSyncTimeFrom(std::max(timing.lastVsync,
now + timing.workDuration +
timing.readyDuration),
- timing.lastVsync);
+ timing.committedVsyncOpt.value_or(
+ timing.lastVsync));
auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
bool const wouldSkipAVsyncTarget =
@@ -208,9 +209,12 @@
const auto workDelta = mWorkloadUpdateInfo->workDuration - mScheduleTiming.workDuration;
const auto readyDelta = mWorkloadUpdateInfo->readyDuration - mScheduleTiming.readyDuration;
const auto lastVsyncDelta = mWorkloadUpdateInfo->lastVsync - mScheduleTiming.lastVsync;
+ const auto lastCommittedVsyncDelta =
+ mWorkloadUpdateInfo->committedVsyncOpt.value_or(mWorkloadUpdateInfo->lastVsync) -
+ mScheduleTiming.committedVsyncOpt.value_or(mScheduleTiming.lastVsync);
SFTRACE_FORMAT_INSTANT("Workload updated workDelta=%" PRId64 " readyDelta=%" PRId64
- " lastVsyncDelta=%" PRId64,
- workDelta, readyDelta, lastVsyncDelta);
+ " lastVsyncDelta=%" PRId64 " committedVsyncDelta=%" PRId64,
+ workDelta, readyDelta, lastVsyncDelta, lastCommittedVsyncDelta);
mScheduleTiming = *mWorkloadUpdateInfo;
mWorkloadUpdateInfo.reset();
}
@@ -261,10 +265,14 @@
StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
mRunning ? "(in callback function)" : "", armedInfo.c_str());
StringAppendF(&result,
- "\t\t\tworkDuration: %.2fms readyDuration: %.2fms lastVsync: %.2fms relative "
- "to now\n",
+ "\t\t\tworkDuration: %.2fms readyDuration: %.2fms "
+ "lastVsync: %.2fms relative to now "
+ "committedVsync: %.2fms relative to now\n",
mScheduleTiming.workDuration / 1e6f, mScheduleTiming.readyDuration / 1e6f,
- (mScheduleTiming.lastVsync - systemTime()) / 1e6f);
+ (mScheduleTiming.lastVsync - systemTime()) / 1e6f,
+ (mScheduleTiming.committedVsyncOpt.value_or(mScheduleTiming.lastVsync) -
+ systemTime()) /
+ 1e6f);
if (mLastDispatchTime) {
StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 4a7cff5..6e36f02 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -695,9 +695,12 @@
if (lastFrameMissed) {
// If the last frame missed is the last vsync, we already shifted the timeline. Depends
// on whether we skipped the frame (onFrameMissed) or not (onFrameBegin) we apply a
- // different fixup. There is no need to to shift the vsync timeline again.
- vsyncTime += missedVsync.fixup.ns();
- SFTRACE_FORMAT_INSTANT("lastFrameMissed");
+ // different fixup if we are violating the minFramePeriod.
+ // There is no need to shift the vsync timeline again.
+ if (vsyncTime - missedVsync.vsync.ns() < minFramePeriodOpt->ns()) {
+ vsyncTime += missedVsync.fixup.ns();
+ SFTRACE_FORMAT_INSTANT("lastFrameMissed");
+ }
} else if (mightBackpressure && lastVsyncOpt) {
if (!FlagManager::getInstance().vrr_bugfix_24q4()) {
// lastVsyncOpt does not need to be corrected with the new rate, and
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
index fa377e9..3c5f68c 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp
@@ -21,7 +21,6 @@
#include "VsyncModulator.h"
-#include <android-base/properties.h>
#include <common/trace.h>
#include <log/log.h>
@@ -37,8 +36,7 @@
VsyncModulator::VsyncModulator(const VsyncConfigSet& config, Now now)
: mVsyncConfigSet(config),
- mNow(now),
- mTraceDetailedInfo(base::GetBoolProperty("debug.sf.vsync_trace_detailed_info", false)) {}
+ mNow(now) {}
VsyncConfig VsyncModulator::setVsyncConfigSet(const VsyncConfigSet& config) {
std::lock_guard<std::mutex> lock(mMutex);
@@ -71,10 +69,6 @@
break;
}
- if (mTraceDetailedInfo) {
- SFTRACE_INT("mEarlyWakeup", static_cast<int>(mEarlyWakeupRequests.size()));
- }
-
if (mEarlyWakeupRequests.empty() && schedule == Schedule::EarlyEnd) {
mEarlyTransactionFrames = MIN_EARLY_TRANSACTION_FRAMES;
mEarlyTransactionStartTime = mNow();
@@ -167,15 +161,19 @@
const VsyncConfig& offsets = getNextVsyncConfig();
mVsyncConfig = offsets;
- if (mTraceDetailedInfo) {
- const bool isEarly = &offsets == &mVsyncConfigSet.early;
- const bool isEarlyGpu = &offsets == &mVsyncConfigSet.earlyGpu;
- const bool isLate = &offsets == &mVsyncConfigSet.late;
+ // Trace config type
+ SFTRACE_INT("Vsync-Early", &mVsyncConfig == &mVsyncConfigSet.early);
+ SFTRACE_INT("Vsync-EarlyGpu", &mVsyncConfig == &mVsyncConfigSet.earlyGpu);
+ SFTRACE_INT("Vsync-Late", &mVsyncConfig == &mVsyncConfigSet.late);
- SFTRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
- SFTRACE_INT("Vsync-EarlyGpuOffsetsOn", isEarlyGpu);
- SFTRACE_INT("Vsync-LateOffsetsOn", isLate);
- }
+ // Trace early vsync conditions
+ SFTRACE_INT("EarlyWakeupRequests",
+ static_cast<int>(mEarlyWakeupRequests.size()));
+ SFTRACE_INT("EarlyTransactionFrames", mEarlyTransactionFrames);
+ SFTRACE_INT("RefreshRateChangePending", mRefreshRateChangePending);
+
+ // Trace early gpu conditions
+ SFTRACE_INT("EarlyGpuFrames", mEarlyGpuFrames);
return offsets;
}
@@ -183,7 +181,6 @@
void VsyncModulator::binderDied(const wp<IBinder>& who) {
std::lock_guard<std::mutex> lock(mMutex);
mEarlyWakeupRequests.erase(who);
-
static_cast<void>(updateVsyncConfigLocked());
}
diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index be0d334..d0a7935 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -105,7 +105,6 @@
std::atomic<TimePoint> mLastTransactionCommitTime = TimePoint();
const Now mNow;
- const bool mTraceDetailedInfo;
};
} // namespace android::scheduler
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/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index 8bb72b8..41a9a1b 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -93,6 +93,12 @@
if (mEnableLocalTonemapping) {
clientCompositionDisplay.tonemapStrategy =
renderengine::DisplaySettings::TonemapStrategy::Local;
+ if (static_cast<ui::PixelFormat>(buffer->getPixelFormat()) == ui::PixelFormat::RGBA_FP16) {
+ clientCompositionDisplay.targetHdrSdrRatio =
+ getState().displayBrightnessNits / getState().sdrWhitePointNits;
+ } else {
+ clientCompositionDisplay.targetHdrSdrRatio = 1.f;
+ }
}
return clientCompositionDisplay;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f4bff8f..cad179c 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -532,9 +532,6 @@
mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
// These are set by the HWC implementation to indicate that they will use the workarounds.
- mIsHotplugErrViaNegVsync =
- base::GetBoolProperty("debug.sf.hwc_hotplug_error_via_neg_vsync"s, false);
-
mIsHdcpViaNegVsync = base::GetBoolProperty("debug.sf.hwc_hdcp_via_neg_vsync"s, false);
}
@@ -949,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();
@@ -1006,6 +1007,8 @@
// which we maintain for backwards compatibility.
config.cacheUltraHDR =
base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false);
+ config.cacheEdgeExtension =
+ base::GetBoolProperty("debug.sf.edge_extension_shader"s, true);
return getRenderEngine().primeCache(config);
});
@@ -1212,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());
@@ -1651,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;
}
@@ -2172,21 +2192,13 @@
std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
if (FlagManager::getInstance().connected_display() && timestamp < 0 &&
vsyncPeriod.has_value()) {
- // use ~0 instead of -1 as AidlComposerHal.cpp passes the param as unsigned int32
- if (mIsHotplugErrViaNegVsync && vsyncPeriod.value() == ~0) {
- const auto errorCode = static_cast<int32_t>(-timestamp);
- ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId);
- mScheduler->dispatchHotplugError(errorCode);
- return;
- }
-
if (mIsHdcpViaNegVsync && vsyncPeriod.value() == ~1) {
const int32_t value = static_cast<int32_t>(-timestamp);
// one byte is good enough to encode android.hardware.drm.HdcpLevel
const int32_t maxLevel = (value >> 8) & 0xFF;
const int32_t connectedLevel = value & 0xFF;
- ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for display %" PRIu64, __func__,
- connectedLevel, maxLevel, hwcDisplayId);
+ ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for hwcDisplayId %" PRIu64,
+ __func__, connectedLevel, maxLevel, hwcDisplayId);
updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel);
return;
}
@@ -2226,7 +2238,7 @@
if (FlagManager::getInstance().hotplug2()) {
// TODO(b/311403559): use enum type instead of int
const auto errorCode = static_cast<int32_t>(event);
- ALOGD("%s: Hotplug error %d for display %" PRIu64, __func__, errorCode, hwcDisplayId);
+ ALOGD("%s: Hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode, hwcDisplayId);
mScheduler->dispatchHotplugError(errorCode);
}
}
@@ -2276,6 +2288,18 @@
}));
}
+void SurfaceFlinger::onComposerHalHdcpLevelsChanged(hal::HWDisplayId hwcDisplayId,
+ const HdcpLevels& levels) {
+ if (FlagManager::getInstance().hdcp_level_hal()) {
+ // TODO(b/362270040): propagate enum constants
+ const int32_t maxLevel = static_cast<int32_t>(levels.maxLevel);
+ const int32_t connectedLevel = static_cast<int32_t>(levels.connectedLevel);
+ ALOGD("%s: HDCP levels changed (connected=%d, max=%d) for hwcDisplayId %" PRIu64, __func__,
+ connectedLevel, maxLevel, hwcDisplayId);
+ updateHdcpLevels(hwcDisplayId, connectedLevel, maxLevel);
+ }
+}
+
void SurfaceFlinger::configure() {
Mutex::Autolock lock(mStateLock);
if (configureLocked()) {
@@ -2448,7 +2472,7 @@
bool newDataLatched = false;
SFTRACE_NAME("DisplayCallbackAndStatsUpdates");
- mustComposite |= applyTransactionsLocked(update.transactions, vsyncId);
+ mustComposite |= applyTransactionsLocked(update.transactions);
traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); });
const nsecs_t latchTime = systemTime();
bool unused = false;
@@ -2738,7 +2762,8 @@
if (!FlagManager::getInstance().ce_fence_promise()) {
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
for (auto& [layer, _] : mLayersWithQueuedFrames) {
- if (const auto& layerFE = layer->getCompositionEngineLayerFE())
+ if (const auto& layerFE = layer->getCompositionEngineLayerFE(
+ {static_cast<uint32_t>(layer->sequence)}))
refreshArgs.layersWithQueuedFrames.push_back(layerFE);
}
}
@@ -2814,7 +2839,8 @@
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
for (auto& [layer, _] : mLayersWithQueuedFrames) {
- if (const auto& layerFE = layer->getCompositionEngineLayerFE()) {
+ if (const auto& layerFE = layer->getCompositionEngineLayerFE(
+ {static_cast<uint32_t>(layer->sequence)})) {
refreshArgs.layersWithQueuedFrames.push_back(layerFE);
// Some layers are not displayed and do not yet have a future release fence
if (layerFE->getReleaseFencePromiseStatus() ==
@@ -3259,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() {
@@ -3814,7 +3838,8 @@
mDisplays.erase(displayToken);
if (const auto& physical = currentState.physical) {
- getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id);
+ getHwComposer().allocatePhysicalDisplay(physical->hwcDisplayId, physical->id,
+ /*physicalSize=*/std::nullopt);
}
processDisplayAdded(displayToken, currentState);
@@ -3910,7 +3935,6 @@
// Commit display transactions.
const bool displayTransactionNeeded = transactionFlags & eDisplayTransactionNeeded;
mFrontEndDisplayInfosChanged = displayTransactionNeeded;
- mForceTransactionDisplayChange = displayTransactionNeeded;
if (mSomeChildrenChanged) {
mVisibleRegionsDirty = true;
@@ -4231,6 +4255,8 @@
if (data.hintStatus.compare_exchange_strong(scheduleHintOnTx,
NotifyExpectedPresentHintStatus::Sent)) {
sendHint();
+ constexpr bool kAllowToEnable = true;
+ mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable);
}
}));
}
@@ -4570,20 +4596,18 @@
}
// For tests only
-bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) {
+bool SurfaceFlinger::flushTransactionQueues() {
mTransactionHandler.collectTransactions();
std::vector<TransactionState> transactions = mTransactionHandler.flushTransactions();
- return applyTransactions(transactions, vsyncId);
+ return applyTransactions(transactions);
}
-bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
- VsyncId vsyncId) {
+bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions) {
Mutex::Autolock lock(mStateLock);
- return applyTransactionsLocked(transactions, vsyncId);
+ return applyTransactionsLocked(transactions);
}
-bool SurfaceFlinger::applyTransactionsLocked(std::vector<TransactionState>& transactions,
- VsyncId vsyncId) {
+bool SurfaceFlinger::applyTransactionsLocked(std::vector<TransactionState>& transactions) {
bool needsTraversal = false;
// Now apply all transactions.
for (auto& transaction : transactions) {
@@ -4714,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);
@@ -4725,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);
@@ -5170,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:
@@ -5218,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);
@@ -5231,7 +5256,7 @@
Mutex::Autolock stateLock(mStateLock);
layer->onHandleDestroyed();
- mBufferCountTracker.remove(handle);
+ mBufferCountTracker.remove(layerId);
layer.clear();
setTransactionFlags(eTransactionFlushNeeded | eTransactionNeeded);
}
@@ -5732,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) {
@@ -5760,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) {
@@ -6961,7 +6986,8 @@
displayWeak, options),
getLayerSnapshotsFn, reqSize,
static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
- captureArgs.allowProtected, captureArgs.grayscale, captureListener);
+ captureArgs.allowProtected, captureArgs.grayscale,
+ captureArgs.attachGainmap, captureListener);
}
void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
@@ -7018,7 +7044,7 @@
static_cast<ui::Dataspace>(args.dataspace),
displayWeak, options),
getLayerSnapshotsFn, size, static_cast<ui::PixelFormat>(args.pixelFormat),
- kAllowProtected, kGrayscale, captureListener);
+ kAllowProtected, kGrayscale, args.attachGainmap, captureListener);
}
ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) {
@@ -7129,7 +7155,8 @@
options),
getLayerSnapshotsFn, reqSize,
static_cast<ui::PixelFormat>(captureArgs.pixelFormat),
- captureArgs.allowProtected, captureArgs.grayscale, captureListener);
+ captureArgs.allowProtected, captureArgs.grayscale,
+ captureArgs.attachGainmap, captureListener);
}
// Creates a Future release fence for a layer and keeps track of it in a list to
@@ -7180,7 +7207,7 @@
void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder,
GetLayerSnapshotsFunction getLayerSnapshotsFn,
ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
- bool allowProtected, bool grayscale,
+ bool allowProtected, bool grayscale, bool attachGainmap,
const sp<IScreenCaptureListener>& captureListener) {
SFTRACE_CALL();
@@ -7226,9 +7253,9 @@
renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
- auto futureFence =
- captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale,
- isProtected, captureListener, displayState, layerFEs);
+ auto futureFence = captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */,
+ grayscale, isProtected, attachGainmap, captureListener,
+ displayState, layerFEs);
futureFence.get();
} else {
@@ -7263,7 +7290,7 @@
WRITEABLE);
auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture,
false /* regionSampling */, grayscale,
- isProtected, captureListener);
+ isProtected, attachGainmap, captureListener);
futureFence.get();
}
}
@@ -7317,7 +7344,8 @@
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
const RenderAreaBuilderVariant& renderAreaBuilder,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
+ bool grayscale, bool isProtected, bool attachGainmap,
+ const sp<IScreenCaptureListener>& captureListener,
std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) {
SFTRACE_CALL();
@@ -7334,19 +7362,87 @@
}
return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
}
+ float displayBrightnessNits = displayState.value().displayBrightnessNits;
+ float sdrWhitePointNits = displayState.value().sdrWhitePointNits;
// Empty vector needed to pass into renderScreenImpl for legacy path
std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers;
ftl::SharedFuture<FenceResult> renderFuture =
- renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected,
- captureResults, displayState, layers, layerFEs);
+ renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
+ attachGainmap, captureResults, displayState, layers, layerFEs);
+
+ if (captureResults.capturedHdrLayers && attachGainmap &&
+ FlagManager::getInstance().true_hdr_screenshots()) {
+ sp<GraphicBuffer> hdrBuffer =
+ getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
+ HAL_PIXEL_FORMAT_RGBA_FP16, 1 /* layerCount */,
+ buffer->getUsage(), "screenshot-hdr");
+ sp<GraphicBuffer> gainmapBuffer =
+ getFactory().createGraphicBuffer(buffer->getWidth(), buffer->getHeight(),
+ buffer->getPixelFormat(), 1 /* layerCount */,
+ buffer->getUsage(), "screenshot-gainmap");
+
+ const status_t bufferStatus = hdrBuffer->initCheck();
+ const status_t gainmapBufferStatus = gainmapBuffer->initCheck();
+
+ if (bufferStatus != OK) {
+ ALOGW("%s: Buffer failed to allocate for hdr: %d. Screenshoting SDR instead.", __func__,
+ bufferStatus);
+ } else if (gainmapBufferStatus != OK) {
+ ALOGW("%s: Buffer failed to allocate for gainmap: %d. Screenshoting SDR instead.",
+ __func__, gainmapBufferStatus);
+ } else {
+ captureResults.optionalGainMap = gainmapBuffer;
+ const auto hdrTexture = std::make_shared<
+ renderengine::impl::ExternalTexture>(hdrBuffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::
+ Usage::WRITEABLE);
+ const auto gainmapTexture = std::make_shared<
+ renderengine::impl::ExternalTexture>(gainmapBuffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::
+ Usage::WRITEABLE);
+ ScreenCaptureResults unusedResults;
+ ftl::SharedFuture<FenceResult> hdrRenderFuture =
+ renderScreenImpl(renderArea.get(), hdrTexture, regionSampling, grayscale,
+ isProtected, attachGainmap, unusedResults, displayState,
+ layers, layerFEs);
+
+ renderFuture =
+ ftl::Future(std::move(renderFuture))
+ .then([&, hdrRenderFuture = std::move(hdrRenderFuture),
+ displayBrightnessNits, sdrWhitePointNits,
+ dataspace = captureResults.capturedDataspace, buffer, hdrTexture,
+ gainmapTexture](FenceResult fenceResult) -> FenceResult {
+ if (!fenceResult.ok()) {
+ return fenceResult;
+ }
+
+ auto hdrFenceResult = hdrRenderFuture.get();
+
+ if (!hdrFenceResult.ok()) {
+ return hdrFenceResult;
+ }
+
+ return getRenderEngine()
+ .drawGainmap(buffer, fenceResult.value()->get(), hdrTexture,
+ hdrFenceResult.value()->get(),
+ displayBrightnessNits / sdrWhitePointNits,
+ static_cast<ui::Dataspace>(dataspace),
+ gainmapTexture)
+ .get();
+ })
+ .share();
+ };
+ }
if (captureListener) {
// Defer blocking on renderFuture back to the Binder thread.
return ftl::Future(std::move(renderFuture))
- .then([captureListener, captureResults = std::move(captureResults)](
- FenceResult fenceResult) mutable -> FenceResult {
+ .then([captureListener, captureResults = std::move(captureResults),
+ displayBrightnessNits,
+ sdrWhitePointNits](FenceResult fenceResult) mutable -> FenceResult {
captureResults.fenceResult = std::move(fenceResult);
+ captureResults.hdrSdrRatio = displayBrightnessNits / sdrWhitePointNits;
captureListener->onScreenCaptureCompleted(captureResults);
return base::unexpected(NO_ERROR);
})
@@ -7358,7 +7454,8 @@
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy(
RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) {
+ bool grayscale, bool isProtected, bool attachGainmap,
+ const sp<IScreenCaptureListener>& captureListener) {
SFTRACE_CALL();
auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
@@ -7387,8 +7484,8 @@
auto layerFEs = extractLayerFEs(layers);
ftl::SharedFuture<FenceResult> renderFuture =
- renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale,
- isProtected, captureResults, displayState, layers, layerFEs);
+ renderScreenImpl(renderArea.get(), buffer, regionSampling, grayscale, isProtected,
+ attachGainmap, captureResults, displayState, layers, layerFEs);
if (captureListener) {
// Defer blocking on renderFuture back to the Binder thread.
@@ -7418,10 +7515,9 @@
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
- std::unique_ptr<const RenderArea> renderArea,
- const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
- std::optional<OutputCompositionState>& displayState,
+ const RenderArea* renderArea, const std::shared_ptr<renderengine::ExternalTexture>& buffer,
+ bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap,
+ ScreenCaptureResults& captureResults, std::optional<OutputCompositionState>& displayState,
std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) {
SFTRACE_CALL();
@@ -8544,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 414088e..db0e15e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -135,6 +135,7 @@
class ScreenCapturer;
class WindowInfosListenerInvoker;
+using ::aidl::android::hardware::drm::HdcpLevels;
using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
using frontend::TransactionHandler;
@@ -295,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;
@@ -432,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 {
@@ -671,6 +672,7 @@
void onComposerHalSeamlessPossible(hal::HWDisplayId) override;
void onComposerHalVsyncIdle(hal::HWDisplayId) override;
void onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) override;
+ void onComposerHalHdcpLevelsChanged(hal::HWDisplayId, const HdcpLevels& levels) override;
// ICompositor overrides:
void configure() override REQUIRES(kMainThreadContext);
@@ -783,9 +785,9 @@
REQUIRES(mStateLock, kMainThreadContext);
// Flush pending transactions that were presented after desiredPresentTime.
// For test only
- bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
+ bool flushTransactionQueues() REQUIRES(kMainThreadContext);
- bool applyTransactions(std::vector<TransactionState>&, VsyncId) REQUIRES(kMainThreadContext);
+ bool applyTransactions(std::vector<TransactionState>&) REQUIRES(kMainThreadContext);
bool applyAndCommitDisplayTransactionStatesLocked(std::vector<TransactionState>& transactions)
REQUIRES(kMainThreadContext, mStateLock);
@@ -815,7 +817,7 @@
static LatchUnsignaledConfig getLatchUnsignaledConfig();
bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const;
- bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId)
+ bool applyTransactionsLocked(std::vector<TransactionState>& transactions)
REQUIRES(mStateLock, kMainThreadContext);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
@@ -859,7 +861,7 @@
void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
ui::Size bufferSize, ui::PixelFormat, bool allowProtected,
- bool grayscale, const sp<IScreenCaptureListener>&);
+ bool grayscale, bool attachGainmap, const sp<IScreenCaptureListener>&);
std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder(
RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext);
@@ -873,20 +875,21 @@
ftl::SharedFuture<FenceResult> captureScreenshot(
const RenderAreaBuilderVariant& renderAreaBuilder,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
+ bool grayscale, bool isProtected, bool attachGainmap,
+ const sp<IScreenCaptureListener>& captureListener,
std::optional<OutputCompositionState>& displayState,
std::vector<sp<LayerFE>>& layerFEs);
ftl::SharedFuture<FenceResult> captureScreenshotLegacy(
RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
- bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&);
+ bool grayscale, bool isProtected, bool attachGainmap,
+ const sp<IScreenCaptureListener>&);
ftl::SharedFuture<FenceResult> renderScreenImpl(
- std::unique_ptr<const RenderArea>,
- const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
- bool grayscale, bool isProtected, ScreenCaptureResults&,
- std::optional<OutputCompositionState>& displayState,
+ const RenderArea*, const std::shared_ptr<renderengine::ExternalTexture>&,
+ bool regionSampling, bool grayscale, bool isProtected, bool attachGainmap,
+ ScreenCaptureResults&, std::optional<OutputCompositionState>& displayState,
std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
std::vector<sp<LayerFE>>& layerFEs);
@@ -1228,7 +1231,6 @@
// TODO: Also move visibleRegions over to a boolean system.
bool mUpdateInputInfo = false;
bool mSomeChildrenChanged;
- bool mForceTransactionDisplayChange = false;
bool mUpdateAttachedChoreographer = false;
struct LayerIntHash {
@@ -1259,7 +1261,6 @@
};
bool mIsHdcpViaNegVsync = false;
- bool mIsHotplugErrViaNegVsync = false;
std::mutex mHotplugMutex;
std::vector<HotplugEvent> mPendingHotplugEvents GUARDED_BY(mHotplugMutex);
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/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 8ec908f..12d6138 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -119,7 +119,6 @@
DUMP_READ_ONLY_FLAG(connected_display);
DUMP_READ_ONLY_FLAG(enable_small_area_detection);
DUMP_READ_ONLY_FLAG(frame_rate_category_mrr);
- DUMP_READ_ONLY_FLAG(view_set_requested_frame_rate_mrr);
DUMP_READ_ONLY_FLAG(misc1);
DUMP_READ_ONLY_FLAG(vrr_config);
DUMP_READ_ONLY_FLAG(hotplug2);
@@ -144,11 +143,13 @@
DUMP_READ_ONLY_FLAG(ce_fence_promise);
DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout);
DUMP_READ_ONLY_FLAG(graphite_renderengine);
+ DUMP_READ_ONLY_FLAG(filter_frames_before_trace_starts);
DUMP_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed);
DUMP_READ_ONLY_FLAG(deprecate_vsync_sf);
DUMP_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter);
DUMP_READ_ONLY_FLAG(detached_mirror);
DUMP_READ_ONLY_FLAG(commit_not_composited);
+ DUMP_READ_ONLY_FLAG(correct_dpi_with_display_size);
DUMP_READ_ONLY_FLAG(local_tonemap_screenshots);
DUMP_READ_ONLY_FLAG(override_trusted_overlay);
DUMP_READ_ONLY_FLAG(flush_buffer_slots_to_uncache);
@@ -224,8 +225,6 @@
FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "")
FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "")
FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
-FLAG_MANAGER_READ_ONLY_FLAG(view_set_requested_frame_rate_mrr,
- "debug.sf.view_set_requested_frame_rate_mrr")
FLAG_MANAGER_READ_ONLY_FLAG(misc1, "")
FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config")
FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "")
@@ -250,11 +249,13 @@
FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_dropped_frame, "")
FLAG_MANAGER_READ_ONLY_FLAG(ce_fence_promise, "");
FLAG_MANAGER_READ_ONLY_FLAG(graphite_renderengine, "debug.renderengine.graphite")
+FLAG_MANAGER_READ_ONLY_FLAG(filter_frames_before_trace_starts, "")
FLAG_MANAGER_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed, "");
FLAG_MANAGER_READ_ONLY_FLAG(deprecate_vsync_sf, "");
FLAG_MANAGER_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter, "");
FLAG_MANAGER_READ_ONLY_FLAG(detached_mirror, "");
FLAG_MANAGER_READ_ONLY_FLAG(commit_not_composited, "");
+FLAG_MANAGER_READ_ONLY_FLAG(correct_dpi_with_display_size, "");
FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots");
FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, "");
FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, "");
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 473e564..a1be194 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -56,7 +56,6 @@
/// Trunk stable readonly flags ///
bool connected_display() const;
bool frame_rate_category_mrr() const;
- bool view_set_requested_frame_rate_mrr() const;
bool enable_small_area_detection() const;
bool misc1() const;
bool vrr_config() const;
@@ -82,11 +81,13 @@
bool ce_fence_promise() const;
bool idle_screen_refresh_rate_timeout() const;
bool graphite_renderengine() const;
+ bool filter_frames_before_trace_starts() const;
bool latch_unsignaled_with_auto_refresh_changed() const;
bool deprecate_vsync_sf() const;
bool allow_n_vsyncs_in_targeter() const;
bool detached_mirror() const;
bool commit_not_composited() const;
+ bool correct_dpi_with_display_size() const;
bool local_tonemap_screenshots() const;
bool override_trusted_overlay() const;
bool flush_buffer_slots_to_uncache() const;
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 0ff846e7..e40be51 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -4,13 +4,21 @@
container: "system"
flag {
- name: "adpf_gpu_sf"
- namespace: "game"
- description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL"
- bug: "284324521"
+ name: "adpf_gpu_sf"
+ namespace: "game"
+ description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL"
+ bug: "284324521"
} # 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"
@@ -21,18 +29,29 @@
}
} # ce_fence_promise
- flag {
- name: "commit_not_composited"
- namespace: "core_graphics"
- description: "mark frames as non janky if the transaction resulted in no composition"
- bug: "340633280"
- is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
- } # commit_not_composited
+flag {
+ name: "commit_not_composited"
+ namespace: "core_graphics"
+ description: "mark frames as non janky if the transaction resulted in no composition"
+ bug: "340633280"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # commit_not_composited
- flag {
+flag {
+ name: "correct_dpi_with_display_size"
+ namespace: "core_graphics"
+ description: "indicate whether missing or likely incorrect dpi should be corrected using the display size."
+ bug: "328425848"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # correct_dpi_with_display_size
+
+flag {
name: "deprecate_vsync_sf"
namespace: "core_graphics"
description: "Depracate eVsyncSourceSurfaceFlinger and use vsync_app everywhere"
@@ -43,7 +62,7 @@
}
} # deprecate_vsync_sf
- flag {
+flag {
name: "detached_mirror"
namespace: "window_surfaces"
description: "Ignore local transform when mirroring a partial hierarchy"
@@ -55,6 +74,17 @@
} # detached_mirror
flag {
+ name: "filter_frames_before_trace_starts"
+ namespace: "core_graphics"
+ description: "Do not trace FrameTimeline events for frames started before the trace started"
+ bug: "364194637"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # filter_frames_before_trace_starts
+
+flag {
name: "flush_buffer_slots_to_uncache"
namespace: "core_graphics"
description: "Flush DisplayCommands for disabled displays in order to uncache requested buffers."
@@ -96,11 +126,11 @@
} # latch_unsignaled_with_auto_refresh_changed
flag {
- name: "local_tonemap_screenshots"
- namespace: "core_graphics"
- description: "Enables local tonemapping when capturing screenshots"
- bug: "329464641"
- is_fixed_read_only: true
+ name: "local_tonemap_screenshots"
+ namespace: "core_graphics"
+ description: "Enables local tonemapping when capturing screenshots"
+ bug: "329464641"
+ is_fixed_read_only: true
} # local_tonemap_screenshots
flag {
@@ -115,11 +145,11 @@
} # single_hop_screenshot
flag {
- name: "true_hdr_screenshots"
- namespace: "core_graphics"
- description: "Enables screenshotting display content in HDR, sans tone mapping"
- bug: "329470026"
- is_fixed_read_only: true
+ name: "true_hdr_screenshots"
+ namespace: "core_graphics"
+ description: "Enables screenshotting display content in HDR, sans tone mapping"
+ bug: "329470026"
+ is_fixed_read_only: true
} # true_hdr_screenshots
flag {
@@ -134,11 +164,11 @@
} # override_trusted_overlay
flag {
- name: "view_set_requested_frame_rate_mrr"
- namespace: "core_graphics"
- description: "Enable to use frame rate category NoPreference with fixed frame rate vote on MRR devices"
- bug: "352206100"
- is_fixed_read_only: true
+ name: "view_set_requested_frame_rate_mrr"
+ namespace: "core_graphics"
+ description: "Enable to use frame rate category NoPreference with fixed frame rate vote on MRR devices"
+ bug: "352206100"
+ is_fixed_read_only: true
} # view_set_requested_frame_rate_mrr
flag {
diff --git a/services/surfaceflinger/tests/OWNERS b/services/surfaceflinger/tests/OWNERS
index 56f2f1b..7857961 100644
--- a/services/surfaceflinger/tests/OWNERS
+++ b/services/surfaceflinger/tests/OWNERS
@@ -4,5 +4,5 @@
per-file Layer* = set noparent
per-file Layer* = pdwilliams@google.com, vishnun@google.com, melodymhsu@google.com
-per-file LayerHistoryTest.cpp = file:/services/surfaceflinger/OWNERS
+per-file LayerHistoryIntegrationTest.cpp = file:/services/surfaceflinger/OWNERS
per-file LayerInfoTest.cpp = file:/services/surfaceflinger/OWNERS
\ No newline at end of file
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 3104dd4..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();
@@ -515,6 +517,23 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setEdgeExtensionEffect(uint32_t id, int edge) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.what |= layer_state_t::eEdgeExtensionChanged;
+ transactions.back().states.front().state.edgeExtensionParameters =
+ gui::EdgeExtensionParameters();
+ transactions.back().states.front().state.edgeExtensionParameters.extendLeft = edge & LEFT;
+ transactions.back().states.front().state.edgeExtensionParameters.extendRight = edge & RIGHT;
+ transactions.back().states.front().state.edgeExtensionParameters.extendTop = edge & TOP;
+ transactions.back().states.front().state.edgeExtensionParameters.extendBottom =
+ edge & BOTTOM;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
private:
LayerLifecycleManager& mLifecycleManager;
};
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/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index fa31643..9b10c94 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -64,17 +64,6 @@
void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) {
LOG_ALWAYS_FATAL_IF(mFlinger.scheduler());
-
- EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*mEventThread, createEventConnection(_, _))
- .WillOnce(Return(
- sp<EventThreadConnection>::make(mEventThread, mock::EventThread::kCallingUid)));
-
- EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*mSFEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(mSFEventThread,
- mock::EventThread::kCallingUid)));
-
mFlinger.setupScheduler(std::make_unique<mock::VsyncController>(),
std::make_shared<mock::VSyncTracker>(),
std::unique_ptr<EventThread>(mEventThread),
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 9be0fc3..0dfbd61 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -84,9 +84,11 @@
void SetUp() override {
constexpr bool kUseBootTimeClock = true;
+ constexpr bool kFilterFramesBeforeTraceStarts = false;
mTimeStats = std::make_shared<mock::TimeStats>();
mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid,
- kTestThresholds, !kUseBootTimeClock);
+ kTestThresholds, !kUseBootTimeClock,
+ kFilterFramesBeforeTraceStarts);
mFrameTimeline->registerDataSource();
mTokenManager = &mFrameTimeline->mTokenManager;
mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter;
diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 2cff2f2..e0753a3 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -58,6 +58,7 @@
using Hwc2::Config;
+using ::aidl::android::hardware::drm::HdcpLevels;
using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent;
using ::aidl::android::hardware::graphics::composer3::RefreshRateChangedDebugData;
using hal::IComposerClient;
@@ -165,6 +166,7 @@
expectHotplugConnect(kHwcDisplayId);
const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
ASSERT_TRUE(info);
+ ASSERT_TRUE(info->preferredDetailedTimingDescriptor.has_value());
EXPECT_CALL(*mHal, isVrrSupported()).WillRepeatedly(Return(false));
@@ -178,6 +180,10 @@
constexpr int32_t kHeight = 720;
constexpr int32_t kConfigGroup = 1;
constexpr int32_t kVsyncPeriod = 16666667;
+ constexpr float kMmPerInch = 25.4f;
+ const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
+ const float expectedDpiX = (kWidth * kMmPerInch / size.width);
+ const float expectedDpiY = (kHeight * kMmPerInch / size.height);
EXPECT_CALL(*mHal,
getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH,
@@ -217,8 +223,13 @@
EXPECT_EQ(modes.front().height, kHeight);
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
- EXPECT_EQ(modes.front().dpiX, -1);
- EXPECT_EQ(modes.front().dpiY, -1);
+ if (!FlagManager::getInstance().correct_dpi_with_display_size()) {
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+ } else {
+ EXPECT_EQ(modes.front().dpiX, expectedDpiX);
+ EXPECT_EQ(modes.front().dpiY, expectedDpiY);
+ }
// Optional parameters are supported
constexpr int32_t kDpi = 320;
@@ -270,6 +281,10 @@
constexpr int32_t kHeight = 720;
constexpr int32_t kConfigGroup = 1;
constexpr int32_t kVsyncPeriod = 16666667;
+ constexpr float kMmPerInch = 25.4f;
+ const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
+ const float expectedDpiX = (kWidth * kMmPerInch / size.width);
+ const float expectedDpiY = (kHeight * kMmPerInch / size.height);
EXPECT_CALL(*mHal,
getDisplayAttribute(kHwcDisplayId, kConfigId, IComposerClient::Attribute::WIDTH,
@@ -309,8 +324,13 @@
EXPECT_EQ(modes.front().height, kHeight);
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
- EXPECT_EQ(modes.front().dpiX, -1);
- EXPECT_EQ(modes.front().dpiY, -1);
+ if (!FlagManager::getInstance().correct_dpi_with_display_size()) {
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+ } else {
+ EXPECT_EQ(modes.front().dpiX, expectedDpiX);
+ EXPECT_EQ(modes.front().dpiY, expectedDpiY);
+ }
// Optional parameters are supported
constexpr int32_t kDpi = 320;
@@ -360,6 +380,10 @@
constexpr int32_t kHeight = 720;
constexpr int32_t kConfigGroup = 1;
constexpr int32_t kVsyncPeriod = 16666667;
+ constexpr float kMmPerInch = 25.4f;
+ const ui::Size size = info->preferredDetailedTimingDescriptor->physicalSizeInMm;
+ const float expectedDpiX = (kWidth * kMmPerInch / size.width);
+ const float expectedDpiY = (kHeight * kMmPerInch / size.height);
const hal::VrrConfig vrrConfig =
hal::VrrConfig{.minFrameIntervalNs = static_cast<Fps>(120_Hz).getPeriodNsecs(),
.notifyExpectedPresentConfig = hal::VrrConfig::
@@ -386,8 +410,13 @@
EXPECT_EQ(modes.front().configGroup, kConfigGroup);
EXPECT_EQ(modes.front().vsyncPeriod, kVsyncPeriod);
EXPECT_EQ(modes.front().vrrConfig, vrrConfig);
- EXPECT_EQ(modes.front().dpiX, -1);
- EXPECT_EQ(modes.front().dpiY, -1);
+ if (!FlagManager::getInstance().correct_dpi_with_display_size()) {
+ EXPECT_EQ(modes.front().dpiX, -1);
+ EXPECT_EQ(modes.front().dpiY, -1);
+ } else {
+ EXPECT_EQ(modes.front().dpiX, expectedDpiX);
+ EXPECT_EQ(modes.front().dpiY, expectedDpiY);
+ }
// Supports optional dpi parameter
constexpr int32_t kDpi = 320;
@@ -454,6 +483,8 @@
MOCK_METHOD1(onComposerHalSeamlessPossible, void(hal::HWDisplayId));
MOCK_METHOD1(onComposerHalVsyncIdle, void(hal::HWDisplayId));
MOCK_METHOD(void, onRefreshRateChangedDebug, (const RefreshRateChangedDebugData&), (override));
+ MOCK_METHOD(void, onComposerHalHdcpLevelsChanged, (hal::HWDisplayId, const HdcpLevels&),
+ (override));
};
struct HWComposerSetCallbackTest : HWComposerTest {
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 7e84408..de37b63 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -894,7 +894,6 @@
TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitVoteWithFixedSourceAndNoPreferenceCategory) {
SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
- SET_FLAG_FOR_TEST(flags::view_set_requested_frame_rate_mrr, true);
auto layer = createLegacyAndFrontedEndLayer(1);
setFrameRate(1, (45.6_Hz).getValue(), ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE,
diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index b4efe0f..c7cc21c 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -619,4 +619,14 @@
}
}
+TEST_F(LayerLifecycleManagerTest, testInputInfoOfRequestedLayerState) {
+ // By default the layer has no buffer, so it doesn't need an input info
+ EXPECT_FALSE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo());
+
+ setBuffer(111);
+ mLifecycleManager.commitChanges();
+
+ EXPECT_TRUE(getRequestedLayerState(mLifecycleManager, 111)->needsInputInfo());
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 2860345..a35ae15 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -27,6 +27,7 @@
#include "LayerHierarchyTest.h"
#include "ui/GraphicTypes.h"
+#include <com_android_graphics_libgui_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
#define UPDATE_AND_VERIFY(BUILDER, ...) \
@@ -161,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);
@@ -1761,4 +1762,177 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
EXPECT_TRUE(getSnapshot(1)->isHiddenByPolicy());
}
+
+TEST_F(LayerSnapshotTest, edgeExtensionPropagatesInHierarchy) {
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ setCrop(1, Rect(0, 0, 20, 20));
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(20 /* width */,
+ 20 /* height */,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ setEdgeExtensionEffect(12, LEFT);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(LEFT));
+ EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(LEFT));
+ EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(LEFT));
+
+ setEdgeExtensionEffect(12, RIGHT);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(RIGHT));
+ EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(RIGHT));
+ EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(RIGHT));
+
+ setEdgeExtensionEffect(12, TOP);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(TOP));
+ EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(TOP));
+ EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(TOP));
+
+ setEdgeExtensionEffect(12, BOTTOM);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot({.id = 12})->edgeExtensionEffect.extendsEdge(BOTTOM));
+ EXPECT_TRUE(getSnapshot({.id = 121})->edgeExtensionEffect.extendsEdge(BOTTOM));
+ EXPECT_TRUE(getSnapshot({.id = 1221})->edgeExtensionEffect.extendsEdge(BOTTOM));
+}
+
+TEST_F(LayerSnapshotTest, leftEdgeExtensionIncreaseBoundSizeWithinCrop) {
+ // The left bound is extended when shifting to the right
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ setCrop(1, Rect(0, 0, 20, 20));
+ const int texSize = 10;
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */,
+ texSize /* height*/,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ const float translation = 5.0;
+ setPosition(12, translation, 0);
+ setEdgeExtensionEffect(12, LEFT);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation);
+ EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.left, translation);
+ EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.left, 0.0);
+}
+
+TEST_F(LayerSnapshotTest, rightEdgeExtensionIncreaseBoundSizeWithinCrop) {
+ // The right bound is extended when shifting to the left
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ const int crop = 20;
+ setCrop(1, Rect(0, 0, crop, crop));
+ const int texSize = 10;
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */,
+ texSize /* height*/,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ const float translation = -5.0;
+ setPosition(12, translation, 0);
+ setEdgeExtensionEffect(12, RIGHT);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.left, 0);
+ EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation);
+ EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.right, (float)crop);
+}
+
+TEST_F(LayerSnapshotTest, topEdgeExtensionIncreaseBoundSizeWithinCrop) {
+ // The top bound is extended when shifting to the bottom
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ setCrop(1, Rect(0, 0, 20, 20));
+ const int texSize = 10;
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */,
+ texSize /* height*/,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ const float translation = 5.0;
+ setPosition(12, 0, translation);
+ setEdgeExtensionEffect(12, TOP);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize + translation);
+ EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.top, translation);
+ EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.top, 0.0);
+}
+
+TEST_F(LayerSnapshotTest, bottomEdgeExtensionIncreaseBoundSizeWithinCrop) {
+ // The bottom bound is extended when shifting to the top
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ const int crop = 20;
+ setCrop(1, Rect(0, 0, crop, crop));
+ const int texSize = 10;
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */,
+ texSize /* height*/,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ const float translation = -5.0;
+ setPosition(12, 0, translation);
+ setEdgeExtensionEffect(12, BOTTOM);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot({.id = 1221})->transformedBounds.top, 0);
+ EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize - translation);
+ EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.bottom, (float)crop);
+}
+
+TEST_F(LayerSnapshotTest, multipleEdgeExtensionIncreaseBoundSizeWithinCrop) {
+ // The left bound is extended when shifting to the right
+ if (!com::android::graphics::libgui::flags::edge_extension_shader()) {
+ GTEST_SKIP() << "Skipping test because edge_extension_shader is off";
+ }
+ const int crop = 20;
+ setCrop(1, Rect(0, 0, crop, crop));
+ const int texSize = 10;
+ setBuffer(1221,
+ std::make_shared<renderengine::mock::FakeExternalTexture>(texSize /* width */,
+ texSize /* height*/,
+ 42ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ 0 /*usage*/));
+ const float translation = 5.0;
+ setPosition(12, translation, translation);
+ setEdgeExtensionEffect(12, LEFT | RIGHT | TOP | BOTTOM);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.right, texSize + translation);
+ EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.right, (float)crop);
+ EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.left, translation);
+ EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.left, 0.0);
+ EXPECT_GT(getSnapshot({.id = 1221})->transformedBounds.bottom, texSize + translation);
+ EXPECT_LE(getSnapshot({.id = 1221})->transformedBounds.bottom, (float)crop);
+ EXPECT_LT(getSnapshot({.id = 1221})->transformedBounds.top, translation);
+ EXPECT_GE(getSnapshot({.id = 1221})->transformedBounds.top, 0);
+}
+
+TEST_F(LayerSnapshotTest, shouldUpdateInputWhenNoInputInfo) {
+ // By default the layer has no buffer, so we don't expect it to have an input info
+ EXPECT_FALSE(getSnapshot(111)->hasInputInfo());
+
+ setBuffer(111);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot(111)->hasInputInfo());
+ EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL));
+ EXPECT_FALSE(getSnapshot(2)->hasInputInfo());
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 06c4e30..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> {
@@ -1837,6 +1841,43 @@
}
}
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_vrrHighHintTouch_primaryRangeIsSingleRate) {
+ if (GetParam() != Config::FrameRateOverride::Enabled) {
+ return;
+ }
+
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ auto selector = createSelector(kVrrMode_120, kModeId120);
+ selector.setActiveMode(kModeId120, 60_Hz);
+
+ // Change primary physical range to be single rate, which on VRR device should not affect
+ // fps scoring.
+ EXPECT_EQ(SetPolicyResult::Changed,
+ selector.setDisplayManagerPolicy({kModeId120, {120_Hz, 120_Hz}}));
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ layers[0].vote = LayerVoteType::ExplicitCategory;
+ layers[0].frameRateCategory = FrameRateCategory::HighHint;
+ layers[0].name = "ExplicitCategory HighHint";
+
+ auto actualRankedFrameRates = selector.getRankedFrameRates(layers);
+ // Expect late touch boost from HighHint.
+ EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ layers[1].vote = LayerVoteType::ExplicitExactOrMultiple;
+ layers[1].desiredRefreshRate = 30_Hz;
+ layers[1].name = "ExplicitExactOrMultiple 30Hz";
+
+ actualRankedFrameRates = selector.getRankedFrameRates(layers);
+ // Expect late touch boost from HighHint.
+ EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+}
+
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighHint) {
auto selector = createSelector(makeModes(kMode24, kMode30, kMode60, kMode120), kModeId60);
@@ -1955,7 +1996,7 @@
// Gets touch boost
EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
- EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_TouchBoost) {
@@ -2049,7 +2090,7 @@
lr2.name = "Max";
actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
- EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
lr1.vote = LayerVoteType::ExplicitCategory;
lr1.frameRateCategory = FrameRateCategory::Normal;
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 45ca7e2..ac09cbc 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -124,7 +124,7 @@
// createConnection call to scheduler makes a createEventConnection call to EventThread. Make
// sure that call gets executed and returns an EventThread::Connection object.
- EXPECT_CALL(*mEventThread, createEventConnection(_, _))
+ EXPECT_CALL(*mEventThread, createEventConnection(_))
.WillRepeatedly(Return(mEventThreadConnection));
mScheduler->setEventThread(Cycle::Render, std::move(eventThread));
@@ -797,7 +797,7 @@
const auto mockConnection1 = sp<MockEventThreadConnection>::make(mEventThread);
const auto mockConnection2 = sp<MockEventThreadConnection>::make(mEventThread);
- EXPECT_CALL(*mEventThread, createEventConnection(_, _))
+ EXPECT_CALL(*mEventThread, createEventConnection(_))
.WillOnce(Return(mockConnection1))
.WillOnce(Return(mockConnection2));
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 4b0a7c3..8699621 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -187,16 +187,6 @@
mAppEventThread = eventThread.get();
auto sfEventThread = std::make_unique<mock::EventThread>();
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid)));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid)));
-
auto vsyncController = std::make_unique<mock::VsyncController>();
auto vsyncTracker = std::make_shared<mock::VSyncTracker>();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index 933d03d..352000e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -239,7 +239,7 @@
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
ASSERT_TRUE(hwcDisplayId);
- mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId);
+ mFlinger.getHwComposer().allocatePhysicalDisplay(*hwcDisplayId, *displayId, std::nullopt);
DisplayModePtr activeMode = DisplayMode::Builder(Case::Display::HWC_ACTIVE_CONFIG_ID)
.setResolution(Case::Display::RESOLUTION)
.setVsyncPeriod(DEFAULT_VSYNC_PERIOD)
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index df16b2e..9de3346 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -74,10 +74,8 @@
void setEventThread(Cycle cycle, std::unique_ptr<EventThread> eventThreadPtr) {
if (cycle == Cycle::Render) {
mRenderEventThread = std::move(eventThreadPtr);
- mRenderEventConnection = mRenderEventThread->createEventConnection();
} else {
mLastCompositeEventThread = std::move(eventThreadPtr);
- mLastCompositeEventConnection = mLastCompositeEventThread->createEventConnection();
}
}
@@ -133,7 +131,9 @@
using Scheduler::resyncAllToHardwareVsync;
auto& mutableLayerHistory() { return mLayerHistory; }
- auto& mutableAttachedChoreographers() { return mAttachedChoreographers; }
+ auto& mutableAttachedChoreographers() NO_THREAD_SAFETY_ANALYSIS {
+ return mAttachedChoreographers;
+ }
size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 710b5cc..4dec5f6 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -16,7 +16,6 @@
#pragma once
-#include <algorithm>
#include <chrono>
#include <memory>
#include <variant>
@@ -44,7 +43,6 @@
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "RenderArea.h"
-#include "Scheduler/MessageQueue.h"
#include "Scheduler/RefreshRateSelector.h"
#include "SurfaceFlinger.h"
#include "TestableScheduler.h"
@@ -60,7 +58,6 @@
#include "Scheduler/VSyncTracker.h"
#include "Scheduler/VsyncController.h"
-#include "mock/MockVSyncDispatch.h"
#include "mock/MockVSyncTracker.h"
#include "mock/MockVsyncController.h"
@@ -88,9 +85,7 @@
public:
~Factory() = default;
- std::unique_ptr<HWComposer> createHWComposer(const std::string&) override {
- return nullptr;
- }
+ std::unique_ptr<HWComposer> createHWComposer(const std::string&) override { return nullptr; }
std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps /*currentRefreshRate*/) override {
@@ -276,17 +271,6 @@
auto eventThread = makeMock<mock::EventThread>(options.useNiceMock);
auto sfEventThread = makeMock<mock::EventThread>(options.useNiceMock);
-
- EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*eventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(eventThread.get(),
- mock::EventThread::kCallingUid)));
-
- EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
- EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
- .WillOnce(Return(sp<EventThreadConnection>::make(sfEventThread.get(),
- mock::EventThread::kCallingUid)));
-
auto vsyncController = makeMock<mock::VsyncController>(options.useNiceMock);
auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock);
@@ -335,13 +319,6 @@
return mFlinger->mLegacyLayers[layerId]->findOutputLayerForDisplay(display.get());
}
- static void setLayerSidebandStream(const sp<Layer>& layer,
- const sp<NativeHandle>& sidebandStream) {
- layer->mDrawingState.sidebandStream = sidebandStream;
- layer->mSidebandStream = sidebandStream;
- layer->editLayerSnapshot()->sidebandStream = sidebandStream;
- }
-
void setLayerCompositionType(const sp<Layer>& layer,
aidl::android::hardware::graphics::composer3::Composition type) {
auto outputLayer = findOutputLayerForDisplay(static_cast<uint32_t>(layer->sequence),
@@ -352,10 +329,6 @@
(*state.hwc).hwcCompositionType = type;
}
- static void setLayerDrawingParent(const sp<Layer>& layer, const sp<Layer>& drawingParent) {
- layer->mDrawingParent = drawingParent;
- }
-
/* ------------------------------------------------------------------------
* Forwarding for functions being tested
*/
@@ -501,9 +474,10 @@
auto layers = getLayerSnapshotsFn();
auto layerFEs = mFlinger->extractLayerFEs(layers);
- return mFlinger->renderScreenImpl(std::move(renderArea), buffer, regionSampling,
+ return mFlinger->renderScreenImpl(renderArea.get(), buffer, regionSampling,
false /* grayscale */, false /* isProtected */,
- captureResults, displayState, layers, layerFEs);
+ false /* attachGainmap */, captureResults, displayState,
+ layers, layerFEs);
}
auto getLayerSnapshotsForScreenshotsFn(ui::LayerStack layerStack, uint32_t uid) {
@@ -512,12 +486,14 @@
}
auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
- ui::DisplayPrimaries &primaries) {
+ ui::DisplayPrimaries& primaries) {
return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries);
}
- auto& getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
- auto& getPendingTransactionQueue() {
+ auto& getTransactionQueue() NO_THREAD_SAFETY_ANALYSIS {
+ return mFlinger->mTransactionHandler.mLocklessTransactionQueue;
+ }
+ auto& getPendingTransactionQueue() NO_THREAD_SAFETY_ANALYSIS {
ftl::FakeGuard guard(kMainThreadContext);
return mFlinger->mTransactionHandler.mPendingTransactionQueues;
}
@@ -547,7 +523,7 @@
}
auto flushTransactionQueues() {
- return FTL_FAKE_GUARD(kMainThreadContext, mFlinger->flushTransactionQueues(kVsyncId));
+ return FTL_FAKE_GUARD(kMainThreadContext, mFlinger->flushTransactionQueues());
}
auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
@@ -661,7 +637,7 @@
void destroyAllLayerHandles() {
ftl::FakeGuard guard(kMainThreadContext);
for (auto [layerId, legacyLayer] : mFlinger->mLegacyLayers) {
- mFlinger->onHandleDestroyed(nullptr, legacyLayer, layerId);
+ mFlinger->onHandleDestroyed(legacyLayer, layerId);
}
}
@@ -685,8 +661,10 @@
* post-conditions.
*/
- const auto& displays() const { return mFlinger->mDisplays; }
- const auto& physicalDisplays() const { return mFlinger->mPhysicalDisplays; }
+ const auto& displays() const NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mDisplays; }
+ const auto& physicalDisplays() const NO_THREAD_SAFETY_ANALYSIS {
+ return mFlinger->mPhysicalDisplays;
+ }
const auto& currentState() const { return mFlinger->mCurrentState; }
const auto& drawingState() const { return mFlinger->mDrawingState; }
const auto& transactionFlags() const { return mFlinger->mTransactionFlags; }
@@ -699,13 +677,17 @@
auto& mutableDisplayModeController() { return mFlinger->mDisplayModeController; }
auto& mutableCurrentState() { return mFlinger->mCurrentState; }
auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
- auto& mutableDisplays() { return mFlinger->mDisplays; }
- auto& mutablePhysicalDisplays() { return mFlinger->mPhysicalDisplays; }
+ auto& mutableDisplays() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mDisplays; }
+ auto& mutablePhysicalDisplays() NO_THREAD_SAFETY_ANALYSIS {
+ return mFlinger->mPhysicalDisplays;
+ }
auto& mutableDrawingState() { return mFlinger->mDrawingState; }
auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
auto& mutableVisibleRegionsDirty() { return mFlinger->mVisibleRegionsDirty; }
auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
- auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; }
+ auto& mutablePendingHotplugEvents() NO_THREAD_SAFETY_ANALYSIS {
+ return mFlinger->mPendingHotplugEvents;
+ }
auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; }
auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; }
auto& mutableMaxRenderTargetSize() { return mFlinger->mMaxRenderTargetSize; }
@@ -713,7 +695,7 @@
auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
- auto& mutableActiveDisplayId() { return mFlinger->mActiveDisplayId; }
+ auto& mutableActiveDisplayId() NO_THREAD_SAFETY_ANALYSIS { return mFlinger->mActiveDisplayId; }
auto& mutablePreviouslyComposedLayers() { return mFlinger->mPreviouslyComposedLayers; }
auto& mutableActiveDisplayRotationFlags() {
@@ -721,7 +703,9 @@
}
auto& mutableMinAcquiredBuffers() { return SurfaceFlinger::minAcquiredBuffers; }
- auto& mutableLayerSnapshotBuilder() { return mFlinger->mLayerSnapshotBuilder; };
+ auto& mutableLayerSnapshotBuilder() NO_THREAD_SAFETY_ANALYSIS {
+ return mFlinger->mLayerSnapshotBuilder;
+ }
auto fromHandle(const sp<IBinder>& handle) { return LayerHandle::getLayer(handle); }
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/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 7c678bd..918107d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -915,7 +915,8 @@
vrrTracker.onFrameBegin(TimePoint::fromNs(7000),
{TimePoint::fromNs(6500), TimePoint::fromNs(6500)});
- EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000));
+ EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 7000));
+ EXPECT_EQ(9500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000));
}
TEST_F(VSyncPredictorTest, adjustsVrrTimelineTwoClients) {
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 2cc1987..53ed2e1 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -35,6 +35,7 @@
(const, override));
MOCK_METHOD(bool, isVsyncPeriodSwitchSupported, (), (const, override));
MOCK_METHOD(void, onLayerDestroyed, (hal::HWLayerId), (override));
+ MOCK_METHOD(std::optional<ui::Size>, getPhysicalSizeInMm, (), (const override));
MOCK_METHOD(hal::Error, acceptChanges, (), (override));
MOCK_METHOD((base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>), createLayer, (),
@@ -110,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/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index 8dd1a34..7398cbe 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -24,21 +24,11 @@
class EventThread : public android::EventThread {
public:
- static constexpr auto kCallingUid = static_cast<uid_t>(0);
-
EventThread();
~EventThread() override;
- // TODO(b/302035909): workaround otherwise gtest complains about
- // error: no viable conversion from
- // 'tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration> &&>' to 'const
- // tuple<android::ftl::Flags<android::gui::ISurfaceComposer::EventRegistration>>'
- sp<EventThreadConnection> createEventConnection(EventRegistrationFlags flags) const override {
- return createEventConnection(false, flags);
- }
- MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (bool, EventRegistrationFlags),
- (const));
-
+ MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, (EventRegistrationFlags),
+ (const, override));
MOCK_METHOD(void, enableSyntheticVsync, (bool), (override));
MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override));
MOCK_METHOD(void, onHotplugConnectionError, (int32_t), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index fdb6f4d..45f86fa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -31,15 +31,11 @@
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
- MOCK_CONST_METHOD0(getType, const char*());
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
- MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD0(createClone, sp<Layer>());
MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, scheduler::FrameRateCompatibility());
MOCK_CONST_METHOD0(getOwnerUid, uid_t());
- MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace());
- MOCK_METHOD(bool, isFrontBuffered, (), (const, override));
};
} // namespace android::mock
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 3d8124b..b06ee3b 100644
--- a/services/vibratorservice/VibratorHalWrapper.cpp
+++ b/services/vibratorservice/VibratorHalWrapper.cpp
@@ -96,6 +96,21 @@
if (mInfoCache.mMaxAmplitudes.isFailed()) {
mInfoCache.mMaxAmplitudes = getMaxAmplitudesInternal();
}
+ if (mInfoCache.mMaxEnvelopeEffectSize.isFailed()) {
+ mInfoCache.mMaxEnvelopeEffectSize = getMaxEnvelopeEffectSizeInternal();
+ }
+ if (mInfoCache.mMinEnvelopeEffectControlPointDuration.isFailed()) {
+ mInfoCache.mMinEnvelopeEffectControlPointDuration =
+ getMinEnvelopeEffectControlPointDurationInternal();
+ }
+ if (mInfoCache.mMaxEnvelopeEffectControlPointDuration.isFailed()) {
+ mInfoCache.mMaxEnvelopeEffectControlPointDuration =
+ getMaxEnvelopeEffectControlPointDurationInternal();
+ }
+ if (mInfoCache.mFrequencyToOutputAccelerationMap.isFailed()) {
+ mInfoCache.mFrequencyToOutputAccelerationMap =
+ getFrequencyToOutputAccelerationMapInternal();
+ }
return mInfoCache.get();
}
@@ -210,6 +225,30 @@
ALOGV("Skipped getMaxAmplitudes because it's not available in Vibrator HAL");
return HalResult<std::vector<float>>::unsupported();
}
+HalResult<int32_t> HalWrapper::getMaxEnvelopeEffectSizeInternal() {
+ ALOGV("Skipped getMaxEnvelopeEffectSizeInternal because it's not available "
+ "in Vibrator HAL");
+ return HalResult<int32_t>::unsupported();
+}
+
+HalResult<milliseconds> HalWrapper::getMinEnvelopeEffectControlPointDurationInternal() {
+ ALOGV("Skipped getMinEnvelopeEffectControlPointDurationInternal because it's not "
+ "available in Vibrator HAL");
+ return HalResult<milliseconds>::unsupported();
+}
+
+HalResult<milliseconds> HalWrapper::getMaxEnvelopeEffectControlPointDurationInternal() {
+ ALOGV("Skipped getMaxEnvelopeEffectControlPointDurationInternal because it's not "
+ "available in Vibrator HAL");
+ 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();
+}
// -------------------------------------------------------------------------------------------------
@@ -441,6 +480,33 @@
return HalResultFactory::fromStatus<std::vector<float>>(std::move(status), amplitudes);
}
+HalResult<int32_t> AidlHalWrapper::getMaxEnvelopeEffectSizeInternal() {
+ int32_t size = 0;
+ auto status = getHal()->getPwleV2CompositionSizeMax(&size);
+ return HalResultFactory::fromStatus<int32_t>(std::move(status), size);
+}
+
+HalResult<milliseconds> AidlHalWrapper::getMinEnvelopeEffectControlPointDurationInternal() {
+ int32_t durationMs = 0;
+ auto status = getHal()->getPwleV2PrimitiveDurationMinMillis(&durationMs);
+ return HalResultFactory::fromStatus<milliseconds>(std::move(status), milliseconds(durationMs));
+}
+
+HalResult<milliseconds> AidlHalWrapper::getMaxEnvelopeEffectControlPointDurationInternal() {
+ int32_t durationMs = 0;
+ auto status = getHal()->getPwleV2PrimitiveDurationMaxMillis(&durationMs);
+ 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 ae0d9ab..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;
@@ -258,6 +259,10 @@
const HalResult<float> frequencyResolution;
const HalResult<float> qFactor;
const HalResult<std::vector<float>> maxAmplitudes;
+ 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");
@@ -276,6 +281,13 @@
logFailure<float>(frequencyResolution, "getFrequencyResolution");
logFailure<float>(qFactor, "getQFactor");
logFailure<std::vector<float>>(maxAmplitudes, "getMaxAmplitudes");
+ logFailure<int32_t>(maxEnvelopeEffectSize, "getMaxEnvelopeEffectSize");
+ logFailure<std::chrono::milliseconds>(minEnvelopeEffectControlPointDuration,
+ "getMinEnvelopeEffectControlPointDuration");
+ logFailure<std::chrono::milliseconds>(maxEnvelopeEffectControlPointDuration,
+ "getMaxEnvelopeEffectControlPointDuration");
+ logFailure<std::vector<PwleV2OutputMapEntry>>(frequencyToOutputAccelerationMap,
+ "getfrequencyToOutputAccelerationMap");
}
bool shouldRetry() const {
@@ -285,7 +297,11 @@
pwlePrimitiveDurationMax.shouldRetry() || compositionSizeMax.shouldRetry() ||
pwleSizeMax.shouldRetry() || minFrequency.shouldRetry() ||
resonantFrequency.shouldRetry() || frequencyResolution.shouldRetry() ||
- qFactor.shouldRetry() || maxAmplitudes.shouldRetry();
+ qFactor.shouldRetry() || maxAmplitudes.shouldRetry() ||
+ maxEnvelopeEffectSize.shouldRetry() ||
+ minEnvelopeEffectControlPointDuration.shouldRetry() ||
+ maxEnvelopeEffectControlPointDuration.shouldRetry() ||
+ frequencyToOutputAccelerationMap.shouldRetry();
}
private:
@@ -313,7 +329,11 @@
mResonantFrequency,
mFrequencyResolution,
mQFactor,
- mMaxAmplitudes};
+ mMaxAmplitudes,
+ mMaxEnvelopeEffectSize,
+ mMinEnvelopeEffectControlPointDuration,
+ mMaxEnvelopeEffectControlPointDuration,
+ mFrequencyToOutputAccelerationMap};
}
private:
@@ -340,6 +360,13 @@
HalResult<float> mQFactor = HalResult<float>::transactionFailed(MSG);
HalResult<std::vector<float>> mMaxAmplitudes =
HalResult<std::vector<float>>::transactionFailed(MSG);
+ HalResult<int32_t> mMaxEnvelopeEffectSize = HalResult<int>::transactionFailed(MSG);
+ HalResult<std::chrono::milliseconds> mMinEnvelopeEffectControlPointDuration =
+ 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;
};
@@ -420,6 +447,11 @@
virtual HalResult<float> getFrequencyResolutionInternal();
virtual HalResult<float> getQFactorInternal();
virtual HalResult<std::vector<float>> getMaxAmplitudesInternal();
+ 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;
@@ -495,6 +527,13 @@
HalResult<float> getFrequencyResolutionInternal() override final;
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 ba7e1f0..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;
@@ -235,10 +236,18 @@
constexpr int32_t PWLE_SIZE_MAX = 20;
constexpr int32_t PRIMITIVE_DELAY_MAX = 100;
constexpr int32_t PWLE_DURATION_MAX = 200;
+ constexpr int32_t PWLE_V2_COMPOSITION_SIZE_MAX = 16;
+ 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<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>();
@@ -305,6 +314,26 @@
.Times(Exactly(2))
.WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
.WillOnce(DoAll(SetArgPointee<0>(amplitudes), Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2CompositionSizeMax(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_COMPOSITION_SIZE_MAX),
+ Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMinMillis(_))
+ .Times(Exactly(2))
+ .WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY)))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS),
+ Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMaxMillis(_))
+ .Times(Exactly(2))
+ .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());
@@ -321,6 +350,10 @@
ASSERT_TRUE(failed.frequencyResolution.isFailed());
ASSERT_TRUE(failed.qFactor.isFailed());
ASSERT_TRUE(failed.maxAmplitudes.isFailed());
+ 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());
@@ -338,6 +371,13 @@
ASSERT_EQ(F_RESOLUTION, successful.frequencyResolution.value());
ASSERT_EQ(Q_FACTOR, successful.qFactor.value());
ASSERT_EQ(amplitudes, successful.maxAmplitudes.value());
+ ASSERT_EQ(PWLE_V2_COMPOSITION_SIZE_MAX, successful.maxEnvelopeEffectSize.value());
+ ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS),
+ 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) {
@@ -347,7 +387,15 @@
constexpr int32_t PWLE_SIZE_MAX = 20;
constexpr int32_t PRIMITIVE_DELAY_MAX = 100;
constexpr int32_t PWLE_DURATION_MAX = 200;
+ constexpr int32_t PWLE_V2_COMPOSITION_SIZE_MAX = 16;
+ 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))
@@ -391,6 +439,22 @@
EXPECT_CALL(*mMockHal.get(), getSupportedBraking(_))
.Times(Exactly(1))
.WillOnce(Return(ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION)));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2CompositionSizeMax(_))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_COMPOSITION_SIZE_MAX),
+ Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMinMillis(_))
+ .Times(Exactly(1))
+ .WillOnce(DoAll(SetArgPointee<0>(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS),
+ Return(ndk::ScopedAStatus::ok())));
+ EXPECT_CALL(*mMockHal.get(), getPwleV2PrimitiveDurationMaxMillis(_))
+ .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++) {
@@ -414,6 +478,12 @@
ASSERT_TRUE(info.frequencyResolution.isUnsupported());
ASSERT_TRUE(info.qFactor.isUnsupported());
ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
+ ASSERT_EQ(PWLE_V2_COMPOSITION_SIZE_MAX, info.maxEnvelopeEffectSize.value());
+ ASSERT_EQ(std::chrono::milliseconds(PWLE_V2_MAX_ALLOWED_PRIMITIVE_MIN_DURATION_MS),
+ 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 83430d7..d6dab8d 100644
--- a/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
+++ b/services/vibratorservice/test/VibratorHalWrapperHidlV1_0Test.cpp
@@ -220,6 +220,10 @@
ASSERT_TRUE(info.frequencyResolution.isUnsupported());
ASSERT_TRUE(info.qFactor.isUnsupported());
ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
+ 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) {
@@ -253,6 +257,10 @@
ASSERT_TRUE(info.frequencyResolution.isUnsupported());
ASSERT_TRUE(info.qFactor.isUnsupported());
ASSERT_TRUE(info.maxAmplitudes.isUnsupported());
+ 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/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 4c4e341..879d2d0 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -27,9 +27,6 @@
symbol_file: "libvulkan.map.txt",
first_version: "24",
unversioned_until: "current",
- export_header_libs: [
- "ndk_vulkan_headers",
- ],
}
aconfig_declarations {
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);
}