summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/android/OWNERS12
-rw-r--r--include/android/choreographer.h2
-rw-r--r--include/android/input.h1
-rw-r--r--include/android/input_transfer_token_jni.h68
-rw-r--r--include/android/looper.h32
-rw-r--r--include/android/performance_hint.h5
-rw-r--r--include/android/surface_control.h80
-rw-r--r--include/android/surface_control_input_receiver.h198
-rw-r--r--include/android/thermal.h8
-rw-r--r--include/audiomanager/IAudioManager.h4
-rw-r--r--include/audiomanager/OWNERS1
-rw-r--r--include/ftl/algorithm.h12
-rw-r--r--include/ftl/concat.h4
-rw-r--r--include/ftl/details/hash.h123
-rw-r--r--include/ftl/expected.h78
-rw-r--r--include/ftl/fake_guard.h8
-rw-r--r--include/ftl/function.h2
-rw-r--r--include/ftl/hash.h44
-rw-r--r--include/ftl/non_null.h112
-rw-r--r--include/ftl/optional.h10
-rw-r--r--include/ftl/small_vector.h10
-rw-r--r--include/ftl/unit.h18
-rw-r--r--include/input/DisplayViewport.h16
-rw-r--r--include/input/Input.h231
-rw-r--r--include/input/InputConsumer.h254
-rw-r--r--include/input/InputConsumerNoResampling.h253
-rw-r--r--include/input/InputDevice.h18
-rw-r--r--include/input/InputEventBuilders.h128
-rw-r--r--include/input/InputTransport.h259
-rw-r--r--include/input/KeyCharacterMap.h10
-rw-r--r--include/input/KeyboardClassifier.h53
-rw-r--r--include/input/MotionPredictor.h41
-rw-r--r--include/input/OWNERS1
-rw-r--r--include/input/Resampler.h154
-rw-r--r--include/input/TfLiteMotionPredictor.h8
-rw-r--r--include/input/VirtualInputDevice.h28
-rw-r--r--include/powermanager/HalResult.h165
-rw-r--r--include/powermanager/OWNERS1
-rw-r--r--include/powermanager/PowerHalController.h17
-rw-r--r--include/powermanager/PowerHalLoader.h4
-rw-r--r--include/powermanager/PowerHalWrapper.h180
-rw-r--r--include/powermanager/PowerHintSessionWrapper.h54
-rw-r--r--include/private/OWNERS4
-rw-r--r--include/private/performance_hint_private.h26
44 files changed, 2149 insertions, 588 deletions
diff --git a/include/android/OWNERS b/include/android/OWNERS
index c6cf7ade48..9dfa27995d 100644
--- a/include/android/OWNERS
+++ b/include/android/OWNERS
@@ -1 +1,13 @@
per-file input.h,keycodes.h = file:platform/frameworks/base:/INPUT_OWNERS
+
+# Window manager
+per-file surface_control_input_receiver.h = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
+per-file input_transfer_token.h = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
+
+# CoGS
+per-file *luts* = file:platform/frameworks/base:/graphics/java/android/graphics/OWNERS
+
+# ADPF
+per-file performance_hint.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file system_health.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file thermal.h = file:platform/frameworks/base:/ADPF_OWNERS
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index bec3283cd8..2622a01935 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -318,7 +318,7 @@ size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
/**
* Gets the token used by the platform to identify the frame timeline at the given \c index.
- * q
+ *
* Available since API level 33.
*
* \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See
diff --git a/include/android/input.h b/include/android/input.h
index fec56f02f9..ee98d7aee9 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -1002,7 +1002,6 @@ enum {
* Keyboard types.
*
* Refer to the documentation on android.view.InputDevice for more details.
- * Note: When adding a new keyboard type here InputDeviceInfo::setKeyboardType needs to be updated.
*/
enum {
/** none */
diff --git a/include/android/input_transfer_token_jni.h b/include/android/input_transfer_token_jni.h
new file mode 100644
index 0000000000..92fe9b6921
--- /dev/null
+++ b/include/android/input_transfer_token_jni.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+/**
+ * @file input_transfer_token_jni.h
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <jni.h>
+
+__BEGIN_DECLS
+struct AInputTransferToken;
+
+/**
+ * AInputTransferToken can be used to request focus on or to transfer touch gesture to and from
+ * an embedded SurfaceControl
+ */
+typedef struct AInputTransferToken AInputTransferToken;
+
+/**
+ * Return the AInputTransferToken wrapped by a Java InputTransferToken object. This must be released
+ * using AInputTransferToken_release
+ *
+ * inputTransferTokenObj must be a non-null instance of android.window.InputTransferToken.
+ *
+ * Available since API level 35.
+ */
+AInputTransferToken* _Nonnull AInputTransferToken_fromJava(JNIEnv* _Nonnull env,
+ jobject _Nonnull inputTransferTokenObj) __INTRODUCED_IN(__ANDROID_API_V__);
+/**
+ * Return the Java InputTransferToken object that wraps AInputTransferToken
+ *
+ * aInputTransferToken must be non null and the returned value is an object of instance
+ * android.window.InputTransferToken.
+ *
+ * Available since API level 35.
+ */
+jobject _Nonnull AInputTransferToken_toJava(JNIEnv* _Nonnull env,
+ const AInputTransferToken* _Nonnull aInputTransferToken) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Removes a reference that was previously acquired in native.
+ *
+ * Available since API level 35.
+ */
+void AInputTransferToken_release(AInputTransferToken* _Nullable aInputTransferToken)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+__END_DECLS
+/** @} */
diff --git a/include/android/looper.h b/include/android/looper.h
index d80a3660a6..409db848f8 100644
--- a/include/android/looper.h
+++ b/include/android/looper.h
@@ -90,20 +90,23 @@ enum {
ALOOPER_POLL_WAKE = -1,
/**
- * Result from ALooper_pollOnce() and ALooper_pollAll():
- * One or more callbacks were executed.
+ * Result from ALooper_pollOnce():
+ * One or more callbacks were executed. The poll may also have been
+ * explicitly woken by ALooper_wake().
*/
ALOOPER_POLL_CALLBACK = -2,
/**
* Result from ALooper_pollOnce() and ALooper_pollAll():
- * The timeout expired.
+ * The timeout expired. The poll may also have been explicitly woken by
+ * ALooper_wake().
*/
ALOOPER_POLL_TIMEOUT = -3,
/**
* Result from ALooper_pollOnce() and ALooper_pollAll():
- * An error occurred.
+ * An error occurred. The poll may also have been explicitly woken by
+ * ALooper_wake().
*/
ALOOPER_POLL_ERROR = -4,
};
@@ -182,10 +185,13 @@ typedef int (*ALooper_callbackFunc)(int fd, int events, void* data);
* If the timeout is zero, returns immediately without blocking.
* If the timeout is negative, waits indefinitely until an event appears.
*
+ * **All return values may also imply ALOOPER_POLL_WAKE.** If you call this in a
+ * loop, you must treat all return values as if they also indicated
+ * ALOOPER_POLL_WAKE.
+ *
* Returns ALOOPER_POLL_WAKE if the poll was awoken using ALooper_wake() before
* the timeout expired and no callbacks were invoked and no other file
- * descriptors were ready. **All return values may also imply
- * ALOOPER_POLL_WAKE.**
+ * descriptors were ready.
*
* Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. The poll
* may also have been explicitly woken by ALooper_wake.
@@ -214,15 +220,15 @@ int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa
* data has been consumed or a file descriptor is available with no callback.
* This function will never return ALOOPER_POLL_CALLBACK.
*
- * This API cannot be used safely, but a safe alternative exists (see below). As
- * such, new builds will not be able to call this API and must migrate to the
- * safe API. Binary compatibility is preserved to support already-compiled apps.
+ * This API will not reliably respond to ALooper_wake. As such, this API is
+ * hidden and callers should migrate to ALooper_pollOnce. Binary compatibility
+ * is preserved to support already-compiled apps.
*
- * \bug ALooper_pollAll will not wake in response to ALooper_wake calls if it
+ * \bug ALooper_pollAll() will not wake in response to ALooper_wake() calls if it
* also handles another event at the same time.
*
- * \deprecated Calls to ALooper_pollAll should be replaced with
- * ALooper_pollOnce. If you call ALooper_pollOnce in a loop, you *must* treat
+ * \deprecated Calls to ALooper_pollAll() should be replaced with
+ * ALooper_pollOnce(). If you call ALooper_pollOnce() in a loop, you *must* treat
* all return values as if they also indicate ALOOPER_POLL_WAKE.
*/
int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData)
@@ -235,6 +241,8 @@ int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outDat
*
* This method can be called on any thread.
* This method returns immediately.
+ *
+ * \bug ALooper_pollAll() will not reliably wake in response to ALooper_wake().
*/
void ALooper_wake(ALooper* looper);
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 62d042348b..3486e9b1d7 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -52,7 +52,6 @@
* - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
*/
-#include <android/api-level.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
@@ -85,7 +84,6 @@ typedef struct AWorkDuration AWorkDuration;
/**
* An opaque type representing a handle to a performance hint manager.
- * It must be released after use.
*
* To use:<ul>
* <li>Obtain the performance hint manager instance by calling
@@ -115,6 +113,9 @@ typedef struct APerformanceHintManager APerformanceHintManager;
* API, the client is expected to call {@link APerformanceHint_reportActualWorkDuration} each
* cycle to report the actual time taken to complete to the system.
*
+ * Note, methods of {@link APerformanceHintSession_*} are not thread safe so callers must
+ * ensure thread safety.
+ *
* All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)`
*/
typedef struct APerformanceHintSession APerformanceHintSession;
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 665d9c6db0..bf9acb37da 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -145,6 +145,9 @@ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats;
* 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.
*
@@ -157,8 +160,7 @@ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats;
* 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
@@ -186,8 +188,36 @@ typedef void (*ASurfaceTransaction_OnComplete)(void* _Null_unspecified context,
* 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.
+ *
+ * Available since API level 36.
+ */
+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
@@ -239,6 +269,10 @@ void ASurfaceTransactionStats_releaseASurfaceControls(
* it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1.
*
* Available since API level 29.
+ *
+ * @deprecated This may return SIGNAL_PENDING because the stats can arrive before the acquire
+ * fence has signaled, depending on internal timing differences. Therefore the caller should
+ * use the acquire fence passed in to setBuffer and query the signal time.
*/
int64_t ASurfaceTransactionStats_getAcquireTime(
ASurfaceTransactionStats* _Nonnull surface_transaction_stats,
@@ -247,7 +281,7 @@ int64_t ASurfaceTransactionStats_getAcquireTime(
/**
* 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.
*
@@ -349,6 +383,9 @@ void ASurfaceTransaction_setZOrder(ASurfaceTransaction* _Nonnull transaction,
* 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,
@@ -357,6 +394,29 @@ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* _Nonnull transaction,
__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
@@ -573,7 +633,7 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* _Nonnull t
* using this API for formats that encode an HDR/SDR ratio as part of generating the buffer.
*
* @param surface_control The layer whose extended range brightness is being specified
- * @param currentBufferRatio The current hdr/sdr ratio of the current buffer as represented as
+ * @param currentBufferRatio The current HDR/SDR ratio of the current buffer as represented as
* peakHdrBrightnessInNits / targetSdrWhitePointInNits. For example if the
* buffer was rendered with a target SDR whitepoint of 100nits and a max
* display brightness of 200nits, this should be set to 2.0f.
@@ -587,7 +647,7 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* _Nonnull t
*
* Must be finite && >= 1.0f
*
- * @param desiredRatio The desired hdr/sdr ratio as represented as peakHdrBrightnessInNits /
+ * @param desiredRatio The desired HDR/SDR ratio as represented as peakHdrBrightnessInNits /
* targetSdrWhitePointInNits. This can be used to communicate the max desired
* brightness range. This is similar to the "max luminance" value in other
* HDR metadata formats, but represented as a ratio of the target SDR whitepoint
@@ -620,13 +680,13 @@ void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* _Nonnul
__INTRODUCED_IN(__ANDROID_API_U__);
/**
- * Sets the desired hdr headroom for the layer. See: ASurfaceTransaction_setExtendedRangeBrightness,
+ * Sets the desired HDR headroom for the layer. See: ASurfaceTransaction_setExtendedRangeBrightness,
* prefer using this API for formats that conform to HDR standards like HLG or HDR10, that do not
* communicate a HDR/SDR ratio as part of generating the buffer.
*
- * @param surface_control The layer whose desired hdr headroom is being specified
+ * @param surface_control The layer whose desired HDR headroom is being specified
*
- * @param desiredHeadroom The desired hdr/sdr ratio as represented as peakHdrBrightnessInNits /
+ * @param desiredHeadroom The desired HDR/SDR ratio as represented as peakHdrBrightnessInNits /
* targetSdrWhitePointInNits. This can be used to communicate the max
* desired brightness range of the panel. The system may not be able to, or
* may choose not to, deliver the requested range.
diff --git a/include/android/surface_control_input_receiver.h b/include/android/surface_control_input_receiver.h
new file mode 100644
index 0000000000..f0503f6324
--- /dev/null
+++ b/include/android/surface_control_input_receiver.h
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ */
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+/**
+ * @file surface_control_input_receiver.h
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <android/input.h>
+#include <android/surface_control.h>
+#include <android/input_transfer_token_jni.h>
+
+__BEGIN_DECLS
+
+/**
+ * The AInputReceiver_onMotionEvent callback is invoked when the registered input channel receives
+ * a motion event.
+ *
+ * \param context Optional context provided by the client that is passed when creating the
+ * AInputReceiverCallbacks.
+ *
+ * \param motionEvent The motion event. This must be released with AInputEvent_release.
+ *
+ * Available since API level 35.
+ */
+typedef bool (*AInputReceiver_onMotionEvent)(void *_Null_unspecified context,
+ AInputEvent *_Nonnull motionEvent)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+/**
+ * The AInputReceiver_onKeyEvent callback is invoked when the registered input channel receives
+ * a key event.
+ *
+ * \param context Optional context provided by the client that is passed when creating the
+ * AInputReceiverCallbacks.
+ *
+ * \param keyEvent The key event. This must be released with AInputEvent_release.
+ *
+ * Available since API level 35.
+ */
+typedef bool (*AInputReceiver_onKeyEvent)(void *_Null_unspecified context,
+ AInputEvent *_Nonnull keyEvent)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+typedef struct AInputReceiverCallbacks AInputReceiverCallbacks;
+
+/**
+ * The InputReceiver that holds the reference to the registered input channel. This must be released
+ * using AInputReceiver_release
+ */
+typedef struct AInputReceiver AInputReceiver;
+
+/**
+ * Registers an input receiver for an ASurfaceControl that will receive batched input event. For
+ * those events that are batched, the invocation will happen once per AChoreographer frame, and
+ * other input events will be delivered immediately.
+ *
+ * This is different from AInputReceiver_createUnbatchedInputReceiver in that the input events are
+ * received batched. The caller must invoke AInputReceiver_release to clean up the resources when
+ * no longer needing to use the input receiver.
+ *
+ * \param aChoreographer The AChoreographer used for batching. This should match the
+ * rendering AChoreographer.
+ * \param hostInputTransferToken The host token to link the embedded. This is used to handle
+ * transferring touch gesture from host to embedded and for ANRs
+ * to ensure the host receives the ANR if any issues with
+ * touch on the embedded. This can be retrieved for the host window
+ * by calling AttachedSurfaceControl#getInputTransferToken()
+ * \param aSurfaceControl The ASurfaceControl to register the InputChannel for
+ * \param aInputReceiverCallbacks The SurfaceControlInputReceiver that will receive the input events
+ *
+ * Returns the reference to AInputReceiver to clean up resources when done.
+ *
+ * Available since API level 35.
+ */
+AInputReceiver* _Nonnull
+AInputReceiver_createBatchedInputReceiver(AChoreographer* _Nonnull aChoreographer,
+ const AInputTransferToken* _Nonnull hostInputTransferToken,
+ const ASurfaceControl* _Nonnull aSurfaceControl,
+ AInputReceiverCallbacks* _Nonnull aInputReceiverCallbacks)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Registers an input receiver for an ASurfaceControl that will receive every input event.
+ * This is different from AInputReceiver_createBatchedInputReceiver in that the input events are
+ * received unbatched. The caller must invoke AInputReceiver_release to clean up the resources when
+ * no longer needing to use the input receiver.
+ *
+ * \param aLooper The looper to use when invoking callbacks.
+ * \param hostInputTransferToken The host token to link the embedded. This is used to handle
+ * transferring touch gesture from host to embedded and for ANRs
+ * to ensure the host receives the ANR if any issues with
+ * touch on the embedded. This can be retrieved for the host window
+ * by calling AttachedSurfaceControl#getInputTransferToken()
+ * \param aSurfaceControl The ASurfaceControl to register the InputChannel for
+ * \param aInputReceiverCallbacks The SurfaceControlInputReceiver that will receive the input events
+ *
+ * Returns the reference to AInputReceiver to clean up resources when done.
+ *
+ * Available since API level 35.
+ */
+AInputReceiver* _Nonnull
+AInputReceiver_createUnbatchedInputReceiver(ALooper* _Nonnull aLooper,
+ const AInputTransferToken* _Nonnull hostInputTransferToken,
+ const ASurfaceControl* _Nonnull aSurfaceControl,
+ AInputReceiverCallbacks* _Nonnull aInputReceiverCallbacks)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Returns the AInputTransferToken that can be used to transfer touch gesture to or from other
+ * windows. This InputTransferToken is associated with the SurfaceControl that registered an input
+ * receiver and can be used with the host token for things like transfer touch gesture via
+ * WindowManager#transferTouchGesture().
+ *
+ * This must be released with AInputTransferToken_release.
+ *
+ * \param aInputReceiver The inputReceiver object to retrieve the AInputTransferToken for.
+ *
+ * Available since API level 35.
+ */
+const AInputTransferToken *_Nonnull
+AInputReceiver_getInputTransferToken(AInputReceiver *_Nonnull aInputReceiver)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Unregisters the input channel and deletes the AInputReceiver. This must be called on the same
+ * looper thread it was created with.
+ *
+ * \param aInputReceiver The inputReceiver object to release.
+ *
+ * Available since API level 35.
+ */
+void
+AInputReceiver_release(AInputReceiver *_Nullable aInputReceiver) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Creates a AInputReceiverCallbacks object that is used when registering for an AInputReceiver.
+ * This must be released using AInputReceiverCallbacks_release
+ *
+ * \param context Optional context provided by the client that will be passed into the callbacks.
+ *
+ * Available since API level 35.
+ */
+AInputReceiverCallbacks* _Nonnull AInputReceiverCallbacks_create(void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Releases the AInputReceiverCallbacks. This must be called on the same
+ * looper thread the AInputReceiver was created with. The receiver will not invoke any callbacks
+ * once it's been released.
+ *
+ * Available since API level 35
+ */
+void AInputReceiverCallbacks_release(AInputReceiverCallbacks* _Nullable callbacks)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets a AInputReceiver_onMotionEvent callback for an AInputReceiverCallbacks
+ *
+ * \param callbacks The callback object to set the motion event on.
+ * \param onMotionEvent The motion event that will be invoked
+ *
+ * Available since API level 35.
+ */
+void AInputReceiverCallbacks_setMotionEventCallback(AInputReceiverCallbacks* _Nonnull callbacks,
+ AInputReceiver_onMotionEvent _Nonnull onMotionEvent)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets a AInputReceiver_onKeyEvent callback for an AInputReceiverCallbacks
+ *
+ * \param callbacks The callback object to set the motion event on.
+ * \param onMotionEvent The key event that will be invoked
+ *
+ * Available since API level 35.
+ */
+void AInputReceiverCallbacks_setKeyEventCallback(AInputReceiverCallbacks* _Nonnull callbacks,
+ AInputReceiver_onKeyEvent _Nonnull onKeyEvent)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+__END_DECLS
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 7f9d2edfc7..1d7ad12294 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -254,7 +254,7 @@ typedef struct AThermalHeadroomThreshold AThermalHeadroomThreshold;
* The headroom threshold is used to interpret the possible thermal throttling status based on
* the headroom prediction. For example, if the headroom threshold for
* {@link ATHERMAL_STATUS_LIGHT} is 0.7, and a headroom prediction in 10s returns 0.75
- * (or {@code AThermal_getThermalHeadroom(10)=0.75}), one can expect that in 10 seconds the system
+ * (or `AThermal_getThermalHeadroom(10)=0.75`), one can expect that in 10 seconds the system
* could be in lightly throttled state if the workload remains the same. The app can consider
* taking actions according to the nearest throttling status the difference between the headroom and
* the threshold.
@@ -262,10 +262,10 @@ typedef struct AThermalHeadroomThreshold AThermalHeadroomThreshold;
* For new devices it's guaranteed to have a single sensor, but for older devices with multiple
* sensors reporting different threshold values, the minimum threshold is taken to be conservative
* on predictions. Thus, when reading real-time headroom, it's not guaranteed that a real-time value
- * of 0.75 (or {@code AThermal_getThermalHeadroom(0)}=0.75) exceeding the threshold of 0.7 above
+ * of 0.75 (or `AThermal_getThermalHeadroom(0)`=0.75) exceeding the threshold of 0.7 above
* will always come with lightly throttled state
- * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_LIGHT}) but it can be lower
- * (or {@code AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_NONE}).
+ * (or `AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_LIGHT`) but it can be lower
+ * (or `AThermal_getCurrentThermalStatus()=ATHERMAL_STATUS_NONE`).
* While it's always guaranteed that the device won't be throttled heavier than the unmet
* threshold's state, so a real-time headroom of 0.75 will never come with
* {@link #ATHERMAL_STATUS_MODERATE} but always lower, and 0.65 will never come with
diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h
index 769670ea99..0b7e16bc7d 100644
--- a/include/audiomanager/IAudioManager.h
+++ b/include/audiomanager/IAudioManager.h
@@ -27,7 +27,7 @@
namespace android {
// ----------------------------------------------------------------------------
-
+// TODO(b/309532236) replace this class with AIDL generated parcelable
class IAudioManager : public IInterface
{
public:
@@ -43,6 +43,7 @@ public:
RELEASE_RECORDER = IBinder::FIRST_CALL_TRANSACTION + 6,
PLAYER_SESSION_ID = IBinder::FIRST_CALL_TRANSACTION + 7,
PORT_EVENT = IBinder::FIRST_CALL_TRANSACTION + 8,
+ PERMISSION_UPDATE_BARRIER = IBinder::FIRST_CALL_TRANSACTION + 9,
};
DECLARE_META_INTERFACE(AudioManager)
@@ -63,6 +64,7 @@ public:
/*oneway*/ virtual status_t playerSessionId(audio_unique_id_t piid, audio_session_t sessionId) = 0;
/*oneway*/ virtual status_t portEvent(audio_port_handle_t portId, player_state_t event,
const std::unique_ptr<os::PersistableBundle>& extras) = 0;
+ virtual status_t permissionUpdateBarrier() = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/audiomanager/OWNERS b/include/audiomanager/OWNERS
index 2bd527cc3f..58257baf20 100644
--- a/include/audiomanager/OWNERS
+++ b/include/audiomanager/OWNERS
@@ -1,2 +1,3 @@
+atneya@google.com
elaurent@google.com
jmtrivi@google.com
diff --git a/include/ftl/algorithm.h b/include/ftl/algorithm.h
index c0f67683ab..68826bb068 100644
--- a/include/ftl/algorithm.h
+++ b/include/ftl/algorithm.h
@@ -24,6 +24,18 @@
namespace android::ftl {
+// Determines if a container contains a value. This is a simplified version of the C++23
+// std::ranges::contains function.
+//
+// const ftl::StaticVector vector = {1, 2, 3};
+// assert(ftl::contains(vector, 1));
+//
+// TODO: Remove in C++23.
+template <typename Container, typename Value>
+auto contains(const Container& container, const Value& value) -> bool {
+ return std::find(container.begin(), container.end(), value) != container.end();
+}
+
// Adapter for std::find_if that converts the return value from iterator to optional.
//
// const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv};
diff --git a/include/ftl/concat.h b/include/ftl/concat.h
index e0774d39f3..7680bed5e8 100644
--- a/include/ftl/concat.h
+++ b/include/ftl/concat.h
@@ -57,7 +57,7 @@ struct Concat<N, T, Ts...> : Concat<N + details::StaticString<T>::N, Ts...> {
template <std::size_t N>
struct Concat<N> {
static constexpr std::size_t max_size() { return N; }
- constexpr std::size_t size() const { return end_ - buffer_; }
+ constexpr std::size_t size() const { return static_cast<std::size_t>(end_ - buffer_); }
constexpr const char* c_str() const { return buffer_; }
@@ -68,6 +68,8 @@ struct Concat<N> {
protected:
constexpr Concat() : end_(buffer_) {}
+ constexpr Concat(const Concat&) = delete;
+
constexpr void append() { *end_ = '\0'; }
char buffer_[N + 1];
diff --git a/include/ftl/details/hash.h b/include/ftl/details/hash.h
new file mode 100644
index 0000000000..230ae51257
--- /dev/null
+++ b/include/ftl/details/hash.h
@@ -0,0 +1,123 @@
+/*
+ * 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 <cinttypes>
+#include <cstring>
+
+namespace android::ftl::details {
+
+// Based on CityHash64 v1.0.1 (http://code.google.com/p/cityhash/), but slightly
+// modernized and trimmed for cases with bounded lengths.
+
+template <typename T = std::uint64_t>
+inline T read_unaligned(const void* ptr) {
+ T v;
+ std::memcpy(&v, ptr, sizeof(T));
+ return v;
+}
+
+template <bool NonZeroShift = false>
+constexpr std::uint64_t rotate(std::uint64_t v, std::uint8_t shift) {
+ if constexpr (!NonZeroShift) {
+ if (shift == 0) return v;
+ }
+ return (v >> shift) | (v << (64 - shift));
+}
+
+constexpr std::uint64_t shift_mix(std::uint64_t v) {
+ return v ^ (v >> 47);
+}
+
+__attribute__((no_sanitize("unsigned-integer-overflow")))
+constexpr std::uint64_t hash_length_16(std::uint64_t u, std::uint64_t v) {
+ constexpr std::uint64_t kPrime = 0x9ddfea08eb382d69ull;
+ auto a = (u ^ v) * kPrime;
+ a ^= (a >> 47);
+ auto b = (v ^ a) * kPrime;
+ b ^= (b >> 47);
+ b *= kPrime;
+ return b;
+}
+
+constexpr std::uint64_t kPrime0 = 0xc3a5c85c97cb3127ull;
+constexpr std::uint64_t kPrime1 = 0xb492b66fbe98f273ull;
+constexpr std::uint64_t kPrime2 = 0x9ae16a3b2f90404full;
+constexpr std::uint64_t kPrime3 = 0xc949d7c7509e6557ull;
+
+__attribute__((no_sanitize("unsigned-integer-overflow")))
+inline std::uint64_t hash_length_0_to_16(const char* str, std::uint64_t length) {
+ if (length > 8) {
+ const auto a = read_unaligned(str);
+ const auto b = read_unaligned(str + length - 8);
+ return hash_length_16(a, rotate<true>(b + length, static_cast<std::uint8_t>(length))) ^ b;
+ }
+ if (length >= 4) {
+ const auto a = read_unaligned<std::uint32_t>(str);
+ const auto b = read_unaligned<std::uint32_t>(str + length - 4);
+ return hash_length_16(length + (a << 3), b);
+ }
+ if (length > 0) {
+ const auto a = static_cast<unsigned char>(str[0]);
+ const auto b = static_cast<unsigned char>(str[length >> 1]);
+ const auto c = static_cast<unsigned char>(str[length - 1]);
+ const auto y = static_cast<std::uint32_t>(a) + (static_cast<std::uint32_t>(b) << 8);
+ const auto z = static_cast<std::uint32_t>(length) + (static_cast<std::uint32_t>(c) << 2);
+ return shift_mix(y * kPrime2 ^ z * kPrime3) * kPrime2;
+ }
+ return kPrime2;
+}
+
+__attribute__((no_sanitize("unsigned-integer-overflow")))
+inline std::uint64_t hash_length_17_to_32(const char* str, std::uint64_t length) {
+ const auto a = read_unaligned(str) * kPrime1;
+ const auto b = read_unaligned(str + 8);
+ const auto c = read_unaligned(str + length - 8) * kPrime2;
+ const auto d = read_unaligned(str + length - 16) * kPrime0;
+ return hash_length_16(rotate(a - b, 43) + rotate(c, 30) + d,
+ a + rotate(b ^ kPrime3, 20) - c + length);
+}
+
+__attribute__((no_sanitize("unsigned-integer-overflow")))
+inline std::uint64_t hash_length_33_to_64(const char* str, std::uint64_t length) {
+ auto z = read_unaligned(str + 24);
+ auto a = read_unaligned(str) + (length + read_unaligned(str + length - 16)) * kPrime0;
+ auto b = rotate(a + z, 52);
+ auto c = rotate(a, 37);
+
+ a += read_unaligned(str + 8);
+ c += rotate(a, 7);
+ a += read_unaligned(str + 16);
+
+ const auto vf = a + z;
+ const auto vs = b + rotate(a, 31) + c;
+
+ a = read_unaligned(str + 16) + read_unaligned(str + length - 32);
+ z += read_unaligned(str + length - 8);
+ b = rotate(a + z, 52);
+ c = rotate(a, 37);
+ a += read_unaligned(str + length - 24);
+ c += rotate(a, 7);
+ a += read_unaligned(str + length - 16);
+
+ const auto wf = a + z;
+ const auto ws = b + rotate(a, 31) + c;
+ const auto r = shift_mix((vf + ws) * kPrime2 + (wf + vs) * kPrime0);
+ return shift_mix(r * kPrime0 + vs) * kPrime2;
+}
+
+} // namespace android::ftl::details
diff --git a/include/ftl/expected.h b/include/ftl/expected.h
index 12b6102b6f..7e765c5681 100644
--- a/include/ftl/expected.h
+++ b/include/ftl/expected.h
@@ -18,9 +18,87 @@
#include <android-base/expected.h>
#include <ftl/optional.h>
+#include <ftl/unit.h>
#include <utility>
+// Given an expression `expr` that evaluates to an ftl::Expected<T, E> result (R for short), FTL_TRY
+// unwraps T out of R, or bails out of the enclosing function F if R has an error E. The return type
+// of F must be R, since FTL_TRY propagates R in the error case. As a special case, ftl::Unit may be
+// used as the error E to allow FTL_TRY expressions when F returns `void`.
+//
+// The non-standard syntax requires `-Wno-gnu-statement-expression-from-macro-expansion` to compile.
+// The UnitToVoid conversion allows the macro to be used for early exit from a function that returns
+// `void`.
+//
+// Example usage:
+//
+// using StringExp = ftl::Expected<std::string, std::errc>;
+//
+// StringExp repeat(StringExp exp) {
+// const std::string str = FTL_TRY(exp);
+// return StringExp(str + str);
+// }
+//
+// assert(StringExp("haha"s) == repeat(StringExp("ha"s)));
+// assert(repeat(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) {
+// return e == std::errc::bad_message;
+// }));
+//
+//
+// FTL_TRY may be used in void-returning functions by using ftl::Unit as the error type:
+//
+// void uppercase(char& c, ftl::Optional<char> opt) {
+// c = std::toupper(FTL_TRY(std::move(opt).ok_or(ftl::Unit())));
+// }
+//
+// char c = '?';
+// uppercase(c, std::nullopt);
+// assert(c == '?');
+//
+// uppercase(c, 'a');
+// assert(c == 'A');
+//
+#define FTL_TRY(expr) \
+ ({ \
+ auto exp_ = (expr); \
+ if (!exp_.has_value()) { \
+ using E = decltype(exp_)::error_type; \
+ return android::ftl::details::UnitToVoid<E>::from(std::move(exp_)); \
+ } \
+ exp_.value(); \
+ })
+
+// Given an expression `expr` that evaluates to an ftl::Expected<T, E> result (R for short),
+// FTL_EXPECT unwraps T out of R, or bails out of the enclosing function F if R has an error E.
+// While FTL_TRY bails out with R, FTL_EXPECT bails out with E, which is useful when F does not
+// need to propagate R because T is not relevant to the caller.
+//
+// Example usage:
+//
+// using StringExp = ftl::Expected<std::string, std::errc>;
+//
+// std::errc repeat(StringExp exp, std::string& out) {
+// const std::string str = FTL_EXPECT(exp);
+// out = str + str;
+// return std::errc::operation_in_progress;
+// }
+//
+// std::string str;
+// assert(std::errc::operation_in_progress == repeat(StringExp("ha"s), str));
+// assert("haha"s == str);
+// assert(std::errc::bad_message == repeat(ftl::Unexpected(std::errc::bad_message), str));
+// assert("haha"s == str);
+//
+#define FTL_EXPECT(expr) \
+ ({ \
+ auto exp_ = (expr); \
+ if (!exp_.has_value()) { \
+ return std::move(exp_.error()); \
+ } \
+ exp_.value(); \
+ })
+
namespace android::ftl {
// Superset of base::expected<T, E> with monadic operations.
diff --git a/include/ftl/fake_guard.h b/include/ftl/fake_guard.h
index e6012516fc..0bf2870b69 100644
--- a/include/ftl/fake_guard.h
+++ b/include/ftl/fake_guard.h
@@ -76,12 +76,8 @@ struct [[clang::scoped_lockable]] FakeGuard final {
FTL_ATTRIBUTE(release_capability(mutex))
#endif
-// The parentheses around `expr` are needed to deduce an lvalue or rvalue reference.
-#define FTL_FAKE_GUARD2(mutex, expr) \
- [&]() -> decltype(auto) { \
- const android::ftl::FakeGuard guard(mutex); \
- return (expr); \
- }()
+#define FTL_FAKE_GUARD2(mutex, expr) \
+ (android::ftl::FakeGuard(mutex), expr)
#define FTL_MAKE_FAKE_GUARD(arg1, arg2, guard, ...) guard
diff --git a/include/ftl/function.h b/include/ftl/function.h
index 3538ca4eae..bda5b759bb 100644
--- a/include/ftl/function.h
+++ b/include/ftl/function.h
@@ -123,7 +123,7 @@ namespace android::ftl {
// // Create a typedef to give a more meaningful name and bound the size.
// using MyFunction = ftl::Function<int(std::string_view), 2>;
// int* ptr = nullptr;
-// auto f1 = MyFunction::make_function(
+// auto f1 = MyFunction::make(
// [cls = &cls, ptr](std::string_view sv) {
// return cls->on_string(ptr, sv);
// });
diff --git a/include/ftl/hash.h b/include/ftl/hash.h
new file mode 100644
index 0000000000..29a6f719ef
--- /dev/null
+++ b/include/ftl/hash.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 <cinttypes>
+#include <optional>
+#include <string_view>
+
+#include <ftl/details/hash.h>
+
+namespace android::ftl {
+
+// Non-cryptographic hash function (namely CityHash64) for strings with at most 64 characters.
+// Unlike std::hash, which returns std::size_t and is only required to produce the same result
+// for the same input within a single execution of a program, this hash is stable.
+inline std::optional<std::uint64_t> stable_hash(std::string_view view) {
+ const auto length = view.length();
+ if (length <= 16) {
+ return details::hash_length_0_to_16(view.data(), length);
+ }
+ if (length <= 32) {
+ return details::hash_length_17_to_32(view.data(), length);
+ }
+ if (length <= 64) {
+ return details::hash_length_33_to_64(view.data(), length);
+ }
+ return {};
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/non_null.h b/include/ftl/non_null.h
index 35d09d71de..4a5d8bffd0 100644
--- a/include/ftl/non_null.h
+++ b/include/ftl/non_null.h
@@ -68,26 +68,28 @@ class NonNull final {
constexpr NonNull(const NonNull&) = default;
constexpr NonNull& operator=(const NonNull&) = default;
- constexpr const Pointer& get() const { return pointer_; }
- constexpr explicit operator const Pointer&() const { return get(); }
+ [[nodiscard]] constexpr const Pointer& get() const { return pointer_; }
+ [[nodiscard]] constexpr explicit operator const Pointer&() const { return get(); }
// Move operations. These break the invariant, so care must be taken to avoid subsequent access.
constexpr NonNull(NonNull&&) = default;
constexpr NonNull& operator=(NonNull&&) = default;
- constexpr Pointer take() && { return std::move(pointer_); }
- constexpr explicit operator Pointer() && { return take(); }
+ [[nodiscard]] constexpr Pointer take() && { return std::move(pointer_); }
+ [[nodiscard]] constexpr explicit operator Pointer() && { return take(); }
// Dereferencing.
- constexpr decltype(auto) operator*() const { return *get(); }
- constexpr decltype(auto) operator->() const { return get(); }
+ [[nodiscard]] constexpr decltype(auto) operator*() const { return *get(); }
+ [[nodiscard]] constexpr decltype(auto) operator->() const { return get(); }
+
+ [[nodiscard]] constexpr explicit operator bool() const { return !(pointer_ == nullptr); }
// Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions
// through the passkey idiom, for clear compilation errors.
template <typename P>
constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward<P>(pointer)) {
- if (!pointer_) std::abort();
+ if (pointer_ == nullptr) std::abort();
}
private:
@@ -98,11 +100,13 @@ class NonNull final {
};
template <typename P>
-constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> {
+[[nodiscard]] constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> {
using Passkey = typename NonNull<std::decay_t<P>>::Passkey;
return {Passkey{}, std::forward<P>(pointer)};
}
+// NonNull<P> <=> NonNull<Q>
+
template <typename P, typename Q>
constexpr bool operator==(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
return lhs.get() == rhs.get();
@@ -113,4 +117,96 @@ constexpr bool operator!=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
return !operator==(lhs, rhs);
}
+template <typename P, typename Q>
+constexpr bool operator<(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return lhs.get() < rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator<=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return lhs.get() <= rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator>=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return lhs.get() >= rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator>(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return lhs.get() > rhs.get();
+}
+
+// NonNull<P> <=> Q
+
+template <typename P, typename Q>
+constexpr bool operator==(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() == rhs;
+}
+
+template <typename P, typename Q>
+constexpr bool operator!=(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() != rhs;
+}
+
+template <typename P, typename Q>
+constexpr bool operator<(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() < rhs;
+}
+
+template <typename P, typename Q>
+constexpr bool operator<=(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() <= rhs;
+}
+
+template <typename P, typename Q>
+constexpr bool operator>=(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() >= rhs;
+}
+
+template <typename P, typename Q>
+constexpr bool operator>(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() > rhs;
+}
+
+// P <=> NonNull<Q>
+
+template <typename P, typename Q>
+constexpr bool operator==(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs == rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator!=(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs != rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator<(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs < rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator<=(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs <= rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator>=(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs >= rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator>(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs > rhs.get();
+}
+
} // namespace android::ftl
+
+// Specialize std::hash for ftl::NonNull<T>
+template <typename P>
+struct std::hash<android::ftl::NonNull<P>> {
+ std::size_t operator()(const android::ftl::NonNull<P>& ptr) const {
+ return std::hash<P>()(ptr.get());
+ }
+};
diff --git a/include/ftl/optional.h b/include/ftl/optional.h
index 94d8e3d7cb..e245d88c1c 100644
--- a/include/ftl/optional.h
+++ b/include/ftl/optional.h
@@ -20,13 +20,14 @@
#include <optional>
#include <utility>
+#include <android-base/expected.h>
#include <ftl/details/optional.h>
namespace android::ftl {
// Superset of std::optional<T> with monadic operations, as proposed in https://wg21.link/P0798R8.
//
-// TODO: Remove in C++23.
+// TODO: Remove standard APIs in C++23.
//
template <typename T>
struct Optional final : std::optional<T> {
@@ -109,6 +110,13 @@ struct Optional final : std::optional<T> {
return std::forward<F>(f)();
}
+ // Maps this Optional<T> to expected<T, E> where nullopt becomes E.
+ template <typename E>
+ constexpr auto ok_or(E&& e) && -> base::expected<T, E> {
+ if (has_value()) return std::move(value());
+ return base::unexpected(std::forward<E>(e));
+ }
+
// Delete new for this class. Its base doesn't have a virtual destructor, and
// if it got deleted via base class pointer, it would cause undefined
// behavior. There's not a good reason to allocate this object on the heap
diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h
index 43e9fac5e2..3d5d52e80c 100644
--- a/include/ftl/small_vector.h
+++ b/include/ftl/small_vector.h
@@ -234,7 +234,7 @@ class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<Sma
}
// Extracts the elements as std::vector.
- std::vector<T> promote() && {
+ std::vector<std::remove_const_t<T>> promote() && {
if (dynamic()) {
return std::get<Dynamic>(std::move(vector_)).promote();
} else {
@@ -290,11 +290,11 @@ template <typename T>
class SmallVector<T, 0> final : details::ArrayTraits<T>,
details::ArrayComparators<SmallVector>,
details::ArrayIterators<SmallVector<T, 0>, T>,
- std::vector<T> {
+ std::vector<std::remove_const_t<T>> {
using details::ArrayTraits<T>::replace_at;
using Iter = details::ArrayIterators<SmallVector, T>;
- using Impl = std::vector<T>;
+ using Impl = std::vector<std::remove_const_t<T>>;
friend Iter;
@@ -394,12 +394,12 @@ class SmallVector<T, 0> final : details::ArrayTraits<T>,
pop_back();
}
- std::vector<T> promote() && { return std::move(*this); }
+ std::vector<std::remove_const_t<T>> promote() && { return std::move(*this); }
private:
template <typename U, std::size_t M>
static Impl convert(SmallVector<U, M>&& other) {
- if constexpr (std::is_constructible_v<Impl, std::vector<U>&&>) {
+ if constexpr (std::is_constructible_v<Impl, std::vector<std::remove_const_t<U>>&&>) {
return std::move(other).promote();
} else {
SmallVector vector(other.size());
diff --git a/include/ftl/unit.h b/include/ftl/unit.h
index e38230b976..62549a3f9d 100644
--- a/include/ftl/unit.h
+++ b/include/ftl/unit.h
@@ -58,4 +58,22 @@ constexpr auto unit_fn(F&& f) -> UnitFn<std::decay_t<F>> {
return {std::forward<F>(f)};
}
+namespace details {
+
+// Identity function for all T except Unit, which maps to void.
+template <typename T>
+struct UnitToVoid {
+ template <typename U>
+ static auto from(U&& value) {
+ return value;
+ }
+};
+
+template <>
+struct UnitToVoid<Unit> {
+ template <typename U>
+ static void from(U&&) {}
+};
+
+} // namespace details
} // namespace android::ftl
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index b0eceefba0..56294dd91a 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -19,7 +19,6 @@
#include <android-base/stringprintf.h>
#include <ftl/enum.h>
#include <ftl/string.h>
-#include <gui/constants.h>
#include <input/Input.h>
#include <ui/Rotation.h>
@@ -47,7 +46,7 @@ enum class ViewportType : int32_t {
* See com.android.server.display.DisplayViewport.
*/
struct DisplayViewport {
- int32_t displayId; // -1 if invalid
+ ui::LogicalDisplayId displayId;
ui::Rotation orientation;
int32_t logicalLeft;
int32_t logicalTop;
@@ -67,7 +66,7 @@ struct DisplayViewport {
ViewportType type;
DisplayViewport()
- : displayId(ADISPLAY_ID_NONE),
+ : displayId(ui::LogicalDisplayId::INVALID),
orientation(ui::ROTATION_0),
logicalLeft(0),
logicalTop(0),
@@ -99,12 +98,10 @@ struct DisplayViewport {
return !(*this == other);
}
- inline bool isValid() const {
- return displayId >= 0;
- }
+ inline bool isValid() const { return displayId.isValid(); }
void setNonDisplayViewport(int32_t width, int32_t height) {
- displayId = ADISPLAY_ID_NONE;
+ displayId = ui::LogicalDisplayId::INVALID;
orientation = ui::ROTATION_0;
logicalLeft = 0;
logicalTop = 0;
@@ -123,12 +120,13 @@ struct DisplayViewport {
}
std::string toString() const {
- return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, "
+ return StringPrintf("Viewport %s: displayId=%s, uniqueId=%s, port=%s, orientation=%d, "
"logicalFrame=[%d, %d, %d, %d], "
"physicalFrame=[%d, %d, %d, %d], "
"deviceSize=[%d, %d], "
"isActive=[%d]",
- ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(),
+ ftl::enum_string(type).c_str(), displayId.toString().c_str(),
+ uniqueId.c_str(),
physicalPort ? ftl::to_string(*physicalPort).c_str() : "<none>",
static_cast<int>(orientation), logicalLeft, logicalTop, logicalRight,
logicalBottom, physicalLeft, physicalTop, physicalRight, physicalBottom,
diff --git a/include/input/Input.h b/include/input/Input.h
index a84dcfc63c..a8684bd19b 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -25,9 +25,12 @@
#include <android/input.h>
#ifdef __linux__
#include <android/os/IInputConstants.h>
+#include <android/os/MotionEventFlag.h>
#endif
+#include <android/os/PointerIconType.h>
#include <math.h>
#include <stdint.h>
+#include <ui/LogicalDisplayId.h>
#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/Timers.h>
@@ -39,13 +42,11 @@
* Additional private constants not defined in ndk/ui/input.h.
*/
enum {
-#ifdef __linux__
+
/* This event was generated or modified by accessibility service. */
AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT =
- android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, // 0x800,
-#else
- AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800,
-#endif
+ android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
+
/* Signifies that the key is being predispatched */
AKEY_EVENT_FLAG_PREDISPATCH = 0x20000000,
@@ -53,11 +54,11 @@ enum {
AKEY_EVENT_FLAG_START_TRACKING = 0x40000000,
/* Key event is inconsistent with previously sent key events. */
- AKEY_EVENT_FLAG_TAINTED = 0x80000000,
+ AKEY_EVENT_FLAG_TAINTED = android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED,
};
enum {
-
+ // AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED is defined in include/android/input.h
/**
* This flag indicates that the window that received this motion event is partly
* or wholly obscured by another visible window above it. This flag is set to true
@@ -68,13 +69,18 @@ enum {
* to drop the suspect touches or to take additional precautions to confirm the user's
* actual intent.
*/
- AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2,
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED =
+ static_cast<int32_t>(android::os::MotionEventFlag::WINDOW_IS_PARTIALLY_OBSCURED),
+
+ AMOTION_EVENT_FLAG_HOVER_EXIT_PENDING =
+ static_cast<int32_t>(android::os::MotionEventFlag::HOVER_EXIT_PENDING),
/**
* This flag indicates that the event has been generated by a gesture generator. It
* provides a hint to the GestureDetector to not apply any touch slop.
*/
- AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8,
+ AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE =
+ static_cast<int32_t>(android::os::MotionEventFlag::IS_GENERATED_GESTURE),
/**
* This flag indicates that the event will not cause a focus change if it is directed to an
@@ -82,20 +88,32 @@ enum {
* gestures to allow the user to direct gestures to an unfocused window without bringing it
* into focus.
*/
- AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE =
+ static_cast<int32_t>(android::os::MotionEventFlag::NO_FOCUS_CHANGE),
-#if defined(__linux__)
/**
* This event was generated or modified by accessibility service.
*/
AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT =
- android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, // 0x800,
-#else
- AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800,
-#endif
+ static_cast<int32_t>(android::os::MotionEventFlag::IS_ACCESSIBILITY_EVENT),
+
+ AMOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS =
+ static_cast<int32_t>(android::os::MotionEventFlag::TARGET_ACCESSIBILITY_FOCUS),
/* Motion event is inconsistent with previously sent motion events. */
- AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
+ AMOTION_EVENT_FLAG_TAINTED = static_cast<int32_t>(android::os::MotionEventFlag::TAINTED),
+
+ /** Private flag, not used in Java. */
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION =
+ static_cast<int32_t>(android::os::MotionEventFlag::PRIVATE_FLAG_SUPPORTS_ORIENTATION),
+
+ /** Private flag, not used in Java. */
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = static_cast<int32_t>(
+ android::os::MotionEventFlag::PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION),
+
+ /** Mask for all private flags that are not used in Java. */
+ AMOTION_EVENT_PRIVATE_FLAG_MASK = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION,
};
/**
@@ -115,9 +133,10 @@ constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS = AMOTION_EVENT_FLAG_WINDOW_IS_OBS
/**
* This flag indicates that the point up event has been canceled.
* Typically this is used for palm event when the user has accidental touches.
- * TODO: Adjust flag to public api
+ * TODO(b/338143308): Add this to NDK
*/
-constexpr int32_t AMOTION_EVENT_FLAG_CANCELED = 0x20;
+constexpr int32_t AMOTION_EVENT_FLAG_CANCELED =
+ android::os::IInputConstants::INPUT_EVENT_FLAG_CANCELED;
enum {
/*
@@ -177,6 +196,13 @@ static constexpr size_t MAX_POINTERS = 16;
#define MAX_POINTER_ID 31
/*
+ * Number of high resolution scroll units for one detent (scroll wheel click), as defined in
+ * evdev. This is relevant when an input device is emitting REL_WHEEL_HI_RES or REL_HWHEEL_HI_RES
+ * events.
+ */
+constexpr int32_t kEvdevHighResScrollUnitsPerDetent = 120;
+
+/*
* Declare a concrete type for the NDK's input event forward declaration.
*/
struct AInputEvent {
@@ -194,9 +220,7 @@ struct AInputDevice {
namespace android {
-#ifdef __linux__
class Parcel;
-#endif
/*
* Apply the given transform to the point without applying any translation/offset.
@@ -207,8 +231,12 @@ vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy)
* Transform an angle on the x-y plane. An angle of 0 radians corresponds to "north" or
* pointing upwards in the negative Y direction, a positive angle points towards the right, and a
* negative angle points towards the left.
+ *
+ * If the angle represents a direction that needs to be preserved, set isDirectional to true to get
+ * an output range of [-pi, pi]. If the angle's direction does not need to be preserved, set
+ * isDirectional to false to get an output range of [-pi/2, pi/2].
*/
-float transformAngle(const ui::Transform& transform, float angleRadians);
+float transformAngle(const ui::Transform& transform, float angleRadians, bool isDirectional);
/**
* The type of the InputEvent.
@@ -223,6 +251,8 @@ enum class InputEventType {
TOUCH_MODE = AINPUT_EVENT_TYPE_TOUCH_MODE,
ftl_first = KEY,
ftl_last = TOUCH_MODE,
+ // Used by LatencyTracker fuzzer
+ kMaxValue = ftl_last
};
std::string inputEventSourceToString(int32_t source);
@@ -256,6 +286,16 @@ enum class KeyState {
ftl_last = VIRTUAL,
};
+/**
+ * The keyboard type. This should have 1:1 correspondence with the values of anonymous enum
+ * defined in input.h
+ */
+enum class KeyboardType {
+ NONE = AINPUT_KEYBOARD_TYPE_NONE,
+ NON_ALPHABETIC = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+ ALPHABETIC = AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+};
+
bool isStylusToolType(ToolType toolType);
struct PointerProperties;
@@ -302,12 +342,8 @@ enum {
POLICY_FLAG_RAW_MASK = 0x0000ffff,
-#ifdef __linux__
POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY =
android::os::IInputConstants::POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
-#else
- POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000,
-#endif
/* These flags are set by the input dispatcher. */
@@ -464,8 +500,6 @@ struct PointerCoords {
// axes, however the window scaling will not.
void scale(float globalScale, float windowXScale, float windowYScale);
- void transform(const ui::Transform& transform);
-
inline float getX() const {
return getAxisValue(AMOTION_EVENT_AXIS_X);
}
@@ -476,10 +510,8 @@ struct PointerCoords {
vec2 getXYValue() const { return vec2(getX(), getY()); }
-#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
-#endif
bool operator==(const PointerCoords& other) const;
inline bool operator!=(const PointerCoords& other) const {
@@ -538,9 +570,9 @@ public:
inline void setSource(uint32_t source) { mSource = source; }
- inline int32_t getDisplayId() const { return mDisplayId; }
+ inline ui::LogicalDisplayId getDisplayId() const { return mDisplayId; }
- inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; }
+ inline void setDisplayId(ui::LogicalDisplayId displayId) { mDisplayId = displayId; }
inline std::array<uint8_t, 32> getHmac() const { return mHmac; }
@@ -549,7 +581,7 @@ public:
bool operator==(const InputEvent&) const = default;
protected:
- void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, ui::LogicalDisplayId displayId,
std::array<uint8_t, 32> hmac);
void initialize(const InputEvent& from);
@@ -557,7 +589,7 @@ protected:
int32_t mId;
DeviceId mDeviceId;
uint32_t mSource;
- int32_t mDisplayId;
+ ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::INVALID};
std::array<uint8_t, 32> mHmac;
};
@@ -593,7 +625,7 @@ public:
static const char* getLabel(int32_t keyCode);
static std::optional<int> getKeyCodeFromLabel(const char* label);
- void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, ui::LogicalDisplayId displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime,
nsecs_t eventTime);
@@ -662,10 +694,6 @@ public:
inline void setActionButton(int32_t button) { mActionButton = button; }
- inline float getXOffset() const { return mTransform.tx(); }
-
- inline float getYOffset() const { return mTransform.ty(); }
-
inline const ui::Transform& getTransform() const { return mTransform; }
std::optional<ui::Rotation> getSurfaceRotation() const;
@@ -859,7 +887,7 @@ public:
ssize_t findPointerIndex(int32_t pointerId) const;
- void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, ui::LogicalDisplayId displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
@@ -870,12 +898,30 @@ public:
void copyFrom(const MotionEvent* other, bool keepHistory);
- void addSample(
- nsecs_t eventTime,
- const PointerCoords* pointerCoords);
+ // Initialize this event by keeping only the pointers from "other" that are in splitPointerIds.
+ void splitFrom(const MotionEvent& other, std::bitset<MAX_POINTER_ID + 1> splitPointerIds,
+ int32_t newEventId);
+
+ void addSample(nsecs_t eventTime, const PointerCoords* pointerCoords, int32_t eventId);
void offsetLocation(float xOffset, float yOffset);
+ /**
+ * Get the X offset of this motion event relative to the origin of the raw coordinate space.
+ *
+ * In practice, this is the delta that was added to the raw screen coordinates (i.e. in logical
+ * display space) to adjust for the absolute position of the containing windows and views.
+ */
+ float getRawXOffset() const;
+
+ /**
+ * Get the Y offset of this motion event relative to the origin of the raw coordinate space.
+ *
+ * In practice, this is the delta that was added to the raw screen coordinates (i.e. in logical
+ * display space) to adjust for the absolute position of the containing windows and views.
+ */
+ float getRawYOffset() const;
+
void scale(float globalScaleFactor);
// Set 3x3 perspective matrix transformation.
@@ -886,10 +932,8 @@ public:
// Matrix is in row-major form and compatible with SkMatrix.
void applyTransform(const std::array<float, 9>& matrix);
-#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
-#endif
static bool isTouchEvent(uint32_t source, int32_t action);
inline bool isTouchEvent() const {
@@ -910,15 +954,22 @@ public:
static std::string actionToString(int32_t action);
+ static std::tuple<int32_t /*action*/, std::vector<PointerProperties>,
+ std::vector<PointerCoords>>
+ split(int32_t action, int32_t flags, int32_t historySize, const std::vector<PointerProperties>&,
+ const std::vector<PointerCoords>&, std::bitset<MAX_POINTER_ID + 1> splitPointerIds);
+
// MotionEvent will transform various axes in different ways, based on the source. For
// example, the x and y axes will not have any offsets/translations applied if it comes from a
// relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods
// are used to apply these transformations for different axes.
static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy);
- static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&,
- const PointerCoords&);
- static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&,
- const PointerCoords&);
+ static float calculateTransformedAxisValue(int32_t axis, uint32_t source, int32_t flags,
+ const ui::Transform&, const PointerCoords&);
+ static void calculateTransformedCoordsInPlace(PointerCoords& coords, uint32_t source,
+ int32_t flags, const ui::Transform&);
+ static PointerCoords calculateTransformedCoords(uint32_t source, int32_t flags,
+ const ui::Transform&, const PointerCoords&);
// The rounding precision for transformed motion events.
static constexpr float ROUNDING_PRECISION = 0.001f;
@@ -1043,7 +1094,7 @@ struct __attribute__((__packed__)) VerifiedInputEvent {
DeviceId deviceId;
nsecs_t eventTimeNanos;
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId;
};
/**
@@ -1171,15 +1222,17 @@ public:
*/
struct PointerCaptureRequest {
public:
- inline PointerCaptureRequest() : enable(false), seq(0) {}
- inline PointerCaptureRequest(bool enable, uint32_t seq) : enable(enable), seq(seq) {}
+ inline PointerCaptureRequest() : window(), seq(0) {}
+ inline PointerCaptureRequest(sp<IBinder> window, uint32_t seq) : window(window), seq(seq) {}
inline bool operator==(const PointerCaptureRequest& other) const {
- return enable == other.enable && seq == other.seq;
+ return window == other.window && seq == other.seq;
}
- explicit inline operator bool() const { return enable; }
+ inline bool isEnable() const { return window != nullptr; }
- // True iff this is a request to enable Pointer Capture.
- bool enable;
+ // The requesting window.
+ // If the request is to enable the capture, this is the input token of the window that requested
+ // pointer capture. Otherwise, this is nullptr.
+ sp<IBinder> window;
// The sequence number for the request.
uint32_t seq;
@@ -1190,43 +1243,41 @@ public:
*
* Due to backwards compatibility and public api constraints, this is a duplicate (but type safe)
* definition of PointerIcon.java.
- *
- * TODO(b/235023317) move this definition to an aidl and statically assign to the below java public
- * api values.
- *
- * WARNING: Keep these definitions in sync with
- * frameworks/base/core/java/android/view/PointerIcon.java
*/
enum class PointerIconStyle : int32_t {
- TYPE_CUSTOM = -1,
- TYPE_NULL = 0,
- TYPE_NOT_SPECIFIED = 1,
- TYPE_ARROW = 1000,
- TYPE_CONTEXT_MENU = 1001,
- TYPE_HAND = 1002,
- TYPE_HELP = 1003,
- TYPE_WAIT = 1004,
- TYPE_CELL = 1006,
- TYPE_CROSSHAIR = 1007,
- TYPE_TEXT = 1008,
- TYPE_VERTICAL_TEXT = 1009,
- TYPE_ALIAS = 1010,
- TYPE_COPY = 1011,
- TYPE_NO_DROP = 1012,
- TYPE_ALL_SCROLL = 1013,
- TYPE_HORIZONTAL_DOUBLE_ARROW = 1014,
- TYPE_VERTICAL_DOUBLE_ARROW = 1015,
- TYPE_TOP_RIGHT_DOUBLE_ARROW = 1016,
- TYPE_TOP_LEFT_DOUBLE_ARROW = 1017,
- TYPE_ZOOM_IN = 1018,
- TYPE_ZOOM_OUT = 1019,
- TYPE_GRAB = 1020,
- TYPE_GRABBING = 1021,
- TYPE_HANDWRITING = 1022,
-
- TYPE_SPOT_HOVER = 2000,
- TYPE_SPOT_TOUCH = 2001,
- TYPE_SPOT_ANCHOR = 2002,
+ TYPE_CUSTOM = static_cast<int32_t>(::android::os::PointerIconType::CUSTOM),
+ TYPE_NULL = static_cast<int32_t>(::android::os::PointerIconType::TYPE_NULL),
+ TYPE_NOT_SPECIFIED = static_cast<int32_t>(::android::os::PointerIconType::NOT_SPECIFIED),
+ TYPE_ARROW = static_cast<int32_t>(::android::os::PointerIconType::ARROW),
+ TYPE_CONTEXT_MENU = static_cast<int32_t>(::android::os::PointerIconType::CONTEXT_MENU),
+ TYPE_HAND = static_cast<int32_t>(::android::os::PointerIconType::HAND),
+ TYPE_HELP = static_cast<int32_t>(::android::os::PointerIconType::HELP),
+ TYPE_WAIT = static_cast<int32_t>(::android::os::PointerIconType::WAIT),
+ TYPE_CELL = static_cast<int32_t>(::android::os::PointerIconType::CELL),
+ TYPE_CROSSHAIR = static_cast<int32_t>(::android::os::PointerIconType::CROSSHAIR),
+ TYPE_TEXT = static_cast<int32_t>(::android::os::PointerIconType::TEXT),
+ TYPE_VERTICAL_TEXT = static_cast<int32_t>(::android::os::PointerIconType::VERTICAL_TEXT),
+ TYPE_ALIAS = static_cast<int32_t>(::android::os::PointerIconType::ALIAS),
+ TYPE_COPY = static_cast<int32_t>(::android::os::PointerIconType::COPY),
+ TYPE_NO_DROP = static_cast<int32_t>(::android::os::PointerIconType::NO_DROP),
+ TYPE_ALL_SCROLL = static_cast<int32_t>(::android::os::PointerIconType::ALL_SCROLL),
+ TYPE_HORIZONTAL_DOUBLE_ARROW =
+ static_cast<int32_t>(::android::os::PointerIconType::HORIZONTAL_DOUBLE_ARROW),
+ TYPE_VERTICAL_DOUBLE_ARROW =
+ static_cast<int32_t>(::android::os::PointerIconType::VERTICAL_DOUBLE_ARROW),
+ TYPE_TOP_RIGHT_DOUBLE_ARROW =
+ static_cast<int32_t>(::android::os::PointerIconType::TOP_RIGHT_DOUBLE_ARROW),
+ TYPE_TOP_LEFT_DOUBLE_ARROW =
+ static_cast<int32_t>(::android::os::PointerIconType::TOP_LEFT_DOUBLE_ARROW),
+ TYPE_ZOOM_IN = static_cast<int32_t>(::android::os::PointerIconType::ZOOM_IN),
+ TYPE_ZOOM_OUT = static_cast<int32_t>(::android::os::PointerIconType::ZOOM_OUT),
+ TYPE_GRAB = static_cast<int32_t>(::android::os::PointerIconType::GRAB),
+ TYPE_GRABBING = static_cast<int32_t>(::android::os::PointerIconType::GRABBING),
+ TYPE_HANDWRITING = static_cast<int32_t>(::android::os::PointerIconType::HANDWRITING),
+
+ TYPE_SPOT_HOVER = static_cast<int32_t>(::android::os::PointerIconType::SPOT_HOVER),
+ TYPE_SPOT_TOUCH = static_cast<int32_t>(::android::os::PointerIconType::SPOT_TOUCH),
+ TYPE_SPOT_ANCHOR = static_cast<int32_t>(::android::os::PointerIconType::SPOT_ANCHOR),
};
} // namespace android
diff --git a/include/input/InputConsumer.h b/include/input/InputConsumer.h
new file mode 100644
index 0000000000..611478cbeb
--- /dev/null
+++ b/include/input/InputConsumer.h
@@ -0,0 +1,254 @@
+/*
+ * 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
+
+/*
+ * Native input transport.
+ *
+ * The InputConsumer is used by the application to receive events from the input dispatcher.
+ */
+
+#include "InputTransport.h"
+
+namespace android {
+
+/*
+ * Consumes input events from an input channel.
+ */
+class InputConsumer {
+public:
+ /* Create a consumer associated with an input channel. */
+ explicit InputConsumer(const std::shared_ptr<InputChannel>& channel);
+ /* Create a consumer associated with an input channel, override resampling system property */
+ explicit InputConsumer(const std::shared_ptr<InputChannel>& channel,
+ bool enableTouchResampling);
+
+ /* Destroys the consumer and releases its input channel. */
+ ~InputConsumer();
+
+ /* Gets the underlying input channel. */
+ inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
+
+ /* Consumes an input event from the input channel and copies its contents into
+ * an InputEvent object created using the specified factory.
+ *
+ * Tries to combine a series of move events into larger batches whenever possible.
+ *
+ * If consumeBatches is false, then defers consuming pending batched events if it
+ * is possible for additional samples to be added to them later. Call hasPendingBatch()
+ * to determine whether a pending batch is available to be consumed.
+ *
+ * If consumeBatches is true, then events are still batched but they are consumed
+ * immediately as soon as the input channel is exhausted.
+ *
+ * The frameTime parameter specifies the time when the current display frame started
+ * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown.
+ *
+ * The returned sequence number is never 0 unless the operation failed.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if there is no event present.
+ * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Returns NO_MEMORY if the event could not be created.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime,
+ uint32_t* outSeq, InputEvent** outEvent);
+
+ /* Sends a finished signal to the publisher to inform it that the message
+ * with the specified sequence number has finished being process and whether
+ * the message was handled by the consumer.
+ *
+ * Returns OK on success.
+ * Returns BAD_VALUE if seq is 0.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t sendFinishedSignal(uint32_t seq, bool handled);
+
+ status_t sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
+
+ /* Returns true if there is a pending batch.
+ *
+ * Should be called after calling consume() with consumeBatches == false to determine
+ * whether consume() should be called again later on with consumeBatches == true.
+ */
+ bool hasPendingBatch() const;
+
+ /* Returns the source of first pending batch if exist.
+ *
+ * Should be called after calling consume() with consumeBatches == false to determine
+ * whether consume() should be called again later on with consumeBatches == true.
+ */
+ int32_t getPendingBatchSource() const;
+
+ /* Returns true when there is *likely* a pending batch or a pending event in the channel.
+ *
+ * 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;
+
+ std::string dump() const;
+
+private:
+ // True if touch resampling is enabled.
+ const bool mResampleTouch;
+
+ std::shared_ptr<InputChannel> mChannel;
+
+ // TODO(b/311142655): delete this temporary tracing after the ANR bug is fixed
+ const std::string mProcessingTraceTag;
+ const std::string mLifetimeTraceTag;
+ const int32_t mLifetimeTraceCookie;
+
+ // The current input message.
+ InputMessage mMsg;
+
+ // True if mMsg contains a valid input message that was deferred from the previous
+ // call to consume and that still needs to be handled.
+ bool mMsgDeferred;
+
+ // Batched motion events per device and source.
+ struct Batch {
+ std::vector<InputMessage> samples;
+ };
+ std::vector<Batch> mBatches;
+
+ // Touch state per device and source, only for sources of class pointer.
+ struct History {
+ nsecs_t eventTime;
+ BitSet32 idBits;
+ int32_t idToIndex[MAX_POINTER_ID + 1];
+ PointerCoords pointers[MAX_POINTERS];
+
+ void initializeFrom(const InputMessage& msg) {
+ eventTime = msg.body.motion.eventTime;
+ idBits.clear();
+ for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
+ uint32_t id = msg.body.motion.pointers[i].properties.id;
+ idBits.markBit(id);
+ idToIndex[id] = i;
+ pointers[i].copyFrom(msg.body.motion.pointers[i].coords);
+ }
+ }
+
+ void initializeFrom(const History& other) {
+ eventTime = other.eventTime;
+ idBits = other.idBits; // temporary copy
+ for (size_t i = 0; i < other.idBits.count(); i++) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ int32_t index = other.idToIndex[id];
+ idToIndex[id] = index;
+ pointers[index].copyFrom(other.pointers[index]);
+ }
+ idBits = other.idBits; // final copy
+ }
+
+ const PointerCoords& getPointerById(uint32_t id) const { return pointers[idToIndex[id]]; }
+
+ bool hasPointerId(uint32_t id) const { return idBits.hasBit(id); }
+ };
+ struct TouchState {
+ int32_t deviceId;
+ int32_t source;
+ size_t historyCurrent;
+ size_t historySize;
+ History history[2];
+ History lastResample;
+
+ void initialize(int32_t incomingDeviceId, int32_t incomingSource) {
+ deviceId = incomingDeviceId;
+ source = incomingSource;
+ historyCurrent = 0;
+ historySize = 0;
+ lastResample.eventTime = 0;
+ lastResample.idBits.clear();
+ }
+
+ void addHistory(const InputMessage& msg) {
+ historyCurrent ^= 1;
+ if (historySize < 2) {
+ historySize += 1;
+ }
+ history[historyCurrent].initializeFrom(msg);
+ }
+
+ const History* getHistory(size_t index) const {
+ return &history[(historyCurrent + index) & 1];
+ }
+
+ bool recentCoordinatesAreIdentical(uint32_t id) const {
+ // Return true if the two most recently received "raw" coordinates are identical
+ if (historySize < 2) {
+ return false;
+ }
+ if (!getHistory(0)->hasPointerId(id) || !getHistory(1)->hasPointerId(id)) {
+ return false;
+ }
+ float currentX = getHistory(0)->getPointerById(id).getX();
+ float currentY = getHistory(0)->getPointerById(id).getY();
+ float previousX = getHistory(1)->getPointerById(id).getX();
+ float previousY = getHistory(1)->getPointerById(id).getY();
+ if (currentX == previousX && currentY == previousY) {
+ return true;
+ }
+ return false;
+ }
+ };
+ std::vector<TouchState> mTouchStates;
+
+ // Chain of batched sequence numbers. When multiple input messages are combined into
+ // a batch, we append a record here that associates the last sequence number in the
+ // batch with the previous one. When the finished signal is sent, we traverse the
+ // chain to individually finish all input messages that were part of the batch.
+ struct SeqChain {
+ uint32_t seq; // sequence number of batched input message
+ uint32_t chain; // sequence number of previous batched input message
+ };
+ std::vector<SeqChain> mSeqChains;
+
+ // The time at which each event with the sequence number 'seq' was consumed.
+ // This data is provided in 'finishInputEvent' so that the receiving end can measure the latency
+ // This collection is populated when the event is received, and the entries are erased when the
+ // events are finished. It should not grow infinitely because if an event is not ack'd, ANR
+ // will be raised for that connection, and no further events will be posted to that channel.
+ std::unordered_map<uint32_t /*seq*/, nsecs_t /*consumeTime*/> mConsumeTimes;
+
+ status_t consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime, uint32_t* outSeq,
+ InputEvent** outEvent);
+ status_t consumeSamples(InputEventFactoryInterface* factory, Batch& batch, size_t count,
+ uint32_t* outSeq, InputEvent** outEvent);
+
+ void updateTouchState(InputMessage& msg);
+ void resampleTouchState(nsecs_t frameTime, MotionEvent* event, const InputMessage* next);
+
+ ssize_t findBatch(int32_t deviceId, int32_t source) const;
+ ssize_t findTouchState(int32_t deviceId, int32_t source) const;
+
+ nsecs_t getConsumeTime(uint32_t seq) const;
+ void popConsumeTime(uint32_t seq);
+ status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
+
+ static void rewriteMessage(TouchState& state, InputMessage& msg);
+ static bool canAddSample(const Batch& batch, const InputMessage* msg);
+ static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
+
+ static bool isTouchResamplingEnabled();
+};
+
+} // namespace android
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
new file mode 100644
index 0000000000..c98b9cf8c1
--- /dev/null
+++ b/include/input/InputConsumerNoResampling.h
@@ -0,0 +1,253 @@
+/**
+ * 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 <map>
+#include <memory>
+#include <optional>
+
+#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/Resampler.h>
+#include <utils/Looper.h>
+
+namespace android {
+
+/**
+ * An interface to receive batched input events. Even if you don't want batching, you still have to
+ * use this interface, and some of the events will be batched if your implementation is slow to
+ * handle the incoming input. The events received by these callbacks are never null.
+ */
+class InputConsumerCallbacks {
+public:
+ virtual ~InputConsumerCallbacks(){};
+ virtual void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) = 0;
+ virtual void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) = 0;
+ /**
+ * When you receive this callback, you must (eventually) call "consumeBatchedInputEvents".
+ * If you don't want batching, then call "consumeBatchedInputEvents" immediately with
+ * 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;
+ virtual void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) = 0;
+ virtual void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) = 0;
+ virtual void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) = 0;
+ virtual void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) = 0;
+};
+
+/**
+ * Consumes input events from an input channel.
+ *
+ * This is a re-implementation of InputConsumer. At the moment it only supports resampling for
+ * single pointer events. A lot of the higher-level logic has been folded into this class, to make
+ * it easier to use. In the legacy class, InputConsumer, the consumption logic was partially handled
+ * in the jni layer, as well as various actions like adding the fd to the Choreographer.
+ *
+ * TODO(b/297226446): use this instead of "InputConsumer":
+ * - Add resampling for multiple pointer events.
+ * - Allow various resampling strategies to be specified
+ * - Delete the old "InputConsumer" and use this class instead, renaming it to "InputConsumer".
+ * - Add tracing
+ * - Update all tests to use the new InputConsumer
+ *
+ * This class is not thread-safe. We are currently allowing the constructor to run on any thread,
+ * but all of the remaining APIs should be invoked on the looper thread only.
+ */
+class InputConsumerNoResampling final {
+public:
+ /**
+ * @param callbacks are used to interact with InputConsumerNoResampling. They're called whenever
+ * 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.
+ */
+ explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
+ sp<Looper> looper, InputConsumerCallbacks& callbacks,
+ std::unique_ptr<Resampler> resampler);
+
+ ~InputConsumerNoResampling();
+
+ /**
+ * Must be called exactly once for each event received through the callbacks.
+ */
+ 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), 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> requestedFrameTime);
+
+ /**
+ * Returns true when there is *likely* a pending batch or a pending event in the channel.
+ *
+ * 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;
+
+ std::string getName() { return mChannel->getName(); }
+
+ std::string dump() const;
+
+private:
+ std::shared_ptr<InputChannel> mChannel;
+ sp<Looper> mLooper;
+ InputConsumerCallbacks& mCallbacks;
+ std::unique_ptr<Resampler> mResampler;
+
+ // Looper-related infrastructure
+ /**
+ * This class is needed to associate the function "handleReceiveCallback" with the provided
+ * looper. The callback sent to the looper is RefBase - based, so we can't just send a reference
+ * of this class directly to the looper.
+ */
+ class LooperEventCallback : public LooperCallback {
+ public:
+ LooperEventCallback(std::function<int(int events)> callback) : mCallback(callback) {}
+ int handleEvent(int /*fd*/, int events, void* /*data*/) override {
+ return mCallback(events);
+ }
+
+ private:
+ std::function<int(int events)> mCallback;
+ };
+ sp<LooperEventCallback> mCallback;
+ /**
+ * The actual code that executes when the looper encounters available data on the InputChannel.
+ */
+ int handleReceiveCallback(int events);
+ int mFdEvents;
+ void setFdEvents(int events);
+
+ void ensureCalledOnLooperThread(const char* func) const;
+
+ // Event-reading infrastructure
+ /**
+ * A fifo queue of events to be sent to the InputChannel. We can't send all InputMessages to
+ * the channel immediately when they are produced, because it's possible that the InputChannel
+ * is blocked (if the channel buffer is full). When that happens, we don't want to drop the
+ * events. Therefore, events should only be erased from the queue after they've been
+ * successfully written to the InputChannel.
+ */
+ std::queue<InputMessage> mOutboundQueue;
+ /**
+ * Try to send all of the events in mOutboundQueue over the InputChannel. Not all events might
+ * actually get sent, because it's possible that the channel is blocked.
+ */
+ void processOutboundEvents();
+
+ /**
+ * The time at which each event with the sequence number 'seq' was consumed.
+ * This data is provided in 'finishInputEvent' so that the receiving end can measure the latency
+ * This collection is populated when the event is received, and the entries are erased when the
+ * events are finished. It should not grow infinitely because if an event is not ack'd, ANR
+ * will be raised for that connection, and no further events will be posted to that channel.
+ */
+ std::unordered_map<uint32_t /*seq*/, nsecs_t /*consumeTime*/> mConsumeTimes;
+ /**
+ * Find and return the consumeTime associated with the provided sequence number. Crashes if
+ * the provided seq number is not found.
+ */
+ nsecs_t popConsumeTime(uint32_t seq);
+
+ // Event reading and processing
+ /**
+ * Read all of the available events from the InputChannel
+ */
+ std::vector<InputMessage> readAllMessages();
+
+ /**
+ * Send InputMessage to the corresponding InputConsumerCallbacks function.
+ * @param msg
+ */
+ void handleMessage(const InputMessage& msg) const;
+
+ // Batching
+ /**
+ * 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.
+ */
+ 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 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 > 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 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
+ * sample came from an individual InputMessage of Type::Motion, and therefore will have to be
+ * finished individually. Therefore, when the app calls "finish" on a (possibly batched)
+ * MotionEvent, we will need to check this map in case there are multiple sequence numbers
+ * associated with a single number that the app provided.
+ *
+ * For example:
+ * Suppose we received 4 InputMessage's of type Motion, with action MOVE:
+ * InputMessage(MOVE) InputMessage(MOVE) InputMessage(MOVE) InputMessage(MOVE)
+ * seq=10 seq=11 seq=12 seq=13
+ * The app consumed them all as a batch, which means that the app received a single MotionEvent
+ * with historySize=3 and seq = 10.
+ *
+ * This map will look like:
+ * {
+ * 10: [11, 12, 13],
+ * }
+ * So the sequence number 10 will have 3 other sequence numbers associated with it.
+ * When the app calls 'finish' for seq=10, we need to call 'finish' 4 times total, for sequence
+ * numbers 10, 11, 12, 13. The app is not aware of the sequence numbers of each sample inside
+ * the batched MotionEvent that it received.
+ */
+ std::map<uint32_t, std::vector<uint32_t>> mBatchedSequenceNumbers;
+};
+
+} // namespace android
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 57b659d9ee..1a482396ee 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -115,6 +115,8 @@ enum class InputDeviceSensorAccuracy : int32_t {
ACCURACY_LOW = 1,
ACCURACY_MEDIUM = 2,
ACCURACY_HIGH = 3,
+
+ ftl_last = ACCURACY_HIGH,
};
enum class InputDeviceSensorReportingMode : int32_t {
@@ -128,8 +130,9 @@ enum class InputDeviceLightType : int32_t {
INPUT = 0,
PLAYER_ID = 1,
KEYBOARD_BACKLIGHT = 2,
+ KEYBOARD_MIC_MUTE = 3,
- ftl_last = KEYBOARD_BACKLIGHT
+ ftl_last = KEYBOARD_MIC_MUTE
};
enum class InputDeviceLightCapability : uint32_t {
@@ -277,8 +280,8 @@ public:
void initialize(int32_t id, int32_t generation, int32_t controllerNumber,
const InputDeviceIdentifier& identifier, const std::string& alias,
- bool isExternal, bool hasMic, int32_t associatedDisplayId,
- InputDeviceViewBehavior viewBehavior = {{}});
+ bool isExternal, bool hasMic, ui::LogicalDisplayId associatedDisplayId,
+ InputDeviceViewBehavior viewBehavior = {{}}, bool enabled = true);
inline int32_t getId() const { return mId; }
inline int32_t getControllerNumber() const { return mControllerNumber; }
@@ -345,7 +348,10 @@ public:
}
inline std::optional<InputDeviceUsiVersion> getUsiVersion() const { return mUsiVersion; }
- inline int32_t getAssociatedDisplayId() const { return mAssociatedDisplayId; }
+ inline ui::LogicalDisplayId getAssociatedDisplayId() const { return mAssociatedDisplayId; }
+
+ inline void setEnabled(bool enabled) { mEnabled = enabled; }
+ inline bool isEnabled() const { return mEnabled; }
private:
int32_t mId;
@@ -360,7 +366,8 @@ private:
int32_t mKeyboardType;
std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
std::optional<InputDeviceUsiVersion> mUsiVersion;
- int32_t mAssociatedDisplayId;
+ ui::LogicalDisplayId mAssociatedDisplayId{ui::LogicalDisplayId::INVALID};
+ bool mEnabled;
bool mHasVibrator;
bool mHasBattery;
@@ -382,6 +389,7 @@ enum class InputDeviceConfigurationFileType : int32_t {
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 2d23b97386..5bd5070488 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -18,8 +18,9 @@
#include <android/input.h>
#include <attestation/HmacKeyManager.h>
-#include <gui/constants.h>
#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <ui/LogicalDisplayId.h>
#include <utils/Timers.h> // for nsecs_t, systemTime
#include <vector>
@@ -45,6 +46,11 @@ public:
PointerBuilder& y(float y) { return axis(AMOTION_EVENT_AXIS_Y, y); }
+ PointerBuilder& isResampled(bool isResampled) {
+ mCoords.isResampled = isResampled;
+ return *this;
+ }
+
PointerBuilder& axis(int32_t axis, float value) {
mCoords.setAxisValue(axis, value);
return *this;
@@ -59,6 +65,87 @@ private:
PointerCoords mCoords;
};
+class InputMessageBuilder {
+public:
+ InputMessageBuilder(InputMessage::Type type, uint32_t seq) : mType{type}, mSeq{seq} {}
+
+ InputMessageBuilder& eventId(int32_t eventId) {
+ mEventId = eventId;
+ return *this;
+ }
+
+ InputMessageBuilder& eventTime(nsecs_t eventTime) {
+ mEventTime = eventTime;
+ return *this;
+ }
+
+ InputMessageBuilder& deviceId(DeviceId deviceId) {
+ mDeviceId = deviceId;
+ return *this;
+ }
+
+ InputMessageBuilder& source(int32_t source) {
+ mSource = source;
+ return *this;
+ }
+
+ InputMessageBuilder& displayId(ui::LogicalDisplayId displayId) {
+ mDisplayId = displayId;
+ return *this;
+ }
+
+ InputMessageBuilder& action(int32_t action) {
+ mAction = action;
+ return *this;
+ }
+
+ InputMessageBuilder& downTime(nsecs_t downTime) {
+ mDownTime = downTime;
+ return *this;
+ }
+
+ InputMessageBuilder& pointer(PointerBuilder pointerBuilder) {
+ mPointers.push_back(pointerBuilder);
+ return *this;
+ }
+
+ InputMessage build() const {
+ InputMessage message{};
+ // Header
+ message.header.type = mType;
+ message.header.seq = mSeq;
+ // Body
+ message.body.motion.eventId = mEventId;
+ message.body.motion.pointerCount = mPointers.size();
+ message.body.motion.eventTime = mEventTime;
+ message.body.motion.deviceId = mDeviceId;
+ message.body.motion.source = mSource;
+ message.body.motion.displayId = mDisplayId.val();
+ message.body.motion.action = mAction;
+ message.body.motion.downTime = mDownTime;
+
+ for (size_t i = 0; i < mPointers.size(); ++i) {
+ message.body.motion.pointers[i].properties = mPointers[i].buildProperties();
+ message.body.motion.pointers[i].coords = mPointers[i].buildCoords();
+ }
+ return message;
+ }
+
+private:
+ const InputMessage::Type mType;
+ const uint32_t mSeq;
+
+ int32_t mEventId{InputEvent::nextId()};
+ nsecs_t mEventTime{systemTime(SYSTEM_TIME_MONOTONIC)};
+ DeviceId mDeviceId{DEFAULT_DEVICE_ID};
+ int32_t mSource{AINPUT_SOURCE_TOUCHSCREEN};
+ ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::DEFAULT};
+ int32_t mAction{AMOTION_EVENT_ACTION_MOVE};
+ nsecs_t mDownTime{mEventTime};
+
+ std::vector<PointerBuilder> mPointers;
+};
+
class MotionEventBuilder {
public:
MotionEventBuilder(int32_t action, int32_t source) {
@@ -83,7 +170,7 @@ public:
return *this;
}
- MotionEventBuilder& displayId(int32_t displayId) {
+ MotionEventBuilder& displayId(ui::LogicalDisplayId displayId) {
mDisplayId = displayId;
return *this;
}
@@ -118,7 +205,17 @@ public:
return *this;
}
- MotionEvent build() {
+ MotionEventBuilder& transform(ui::Transform t) {
+ mTransform = t;
+ return *this;
+ }
+
+ MotionEventBuilder& rawTransform(ui::Transform t) {
+ mRawTransform = t;
+ return *this;
+ }
+
+ MotionEvent build() const {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
for (const PointerBuilder& pointer : mPointers) {
@@ -126,21 +223,22 @@ public:
pointerCoords.push_back(pointer.buildCoords());
}
+ auto [xCursorPosition, yCursorPosition] =
+ std::make_pair(mRawXCursorPosition, mRawYCursorPosition);
// Set mouse cursor position for the most common cases to avoid boilerplate.
if (mSource == AINPUT_SOURCE_MOUSE &&
- !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) {
- mRawXCursorPosition = pointerCoords[0].getX();
- mRawYCursorPosition = pointerCoords[0].getY();
+ !MotionEvent::isValidCursorPosition(xCursorPosition, yCursorPosition)) {
+ xCursorPosition = pointerCoords[0].getX();
+ yCursorPosition = pointerCoords[0].getY();
}
MotionEvent event;
- static const ui::Transform kIdentityTransform;
event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState,
- MotionClassification::NONE, kIdentityTransform,
- /*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
- mRawYCursorPosition, kIdentityTransform, mDownTime, mEventTime,
- mPointers.size(), pointerProperties.data(), pointerCoords.data());
+ MotionClassification::NONE, mTransform,
+ /*xPrecision=*/0, /*yPrecision=*/0, xCursorPosition, yCursorPosition,
+ mRawTransform, mDownTime, mEventTime, mPointers.size(),
+ pointerProperties.data(), pointerCoords.data());
return event;
}
@@ -150,12 +248,14 @@ private:
int32_t mSource;
nsecs_t mDownTime;
nsecs_t mEventTime;
- int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::DEFAULT};
int32_t mActionButton{0};
int32_t mButtonState{0};
int32_t mFlags{0};
float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ ui::Transform mTransform;
+ ui::Transform mRawTransform;
std::vector<PointerBuilder> mPointers;
};
@@ -198,7 +298,7 @@ public:
return *this;
}
- KeyEventBuilder& displayId(int32_t displayId) {
+ KeyEventBuilder& displayId(ui::LogicalDisplayId displayId) {
mDisplayId = displayId;
return *this;
}
@@ -237,7 +337,7 @@ private:
uint32_t mSource;
nsecs_t mDownTime;
nsecs_t mEventTime;
- int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::DEFAULT};
uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
int32_t mFlags{0};
int32_t mKeyCode{AKEYCODE_UNKNOWN};
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 42dcd3c394..0cd87201fb 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -263,7 +263,7 @@ public:
* 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 @@ public:
* Return DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
- status_t receiveMessage(InputMessage* msg);
+ 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 @@ public:
*/
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);
};
/*
@@ -353,22 +354,24 @@ public:
* Other errors probably indicate that the channel is broken.
*/
status_t publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source,
- int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
- int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
- int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime);
+ ui::LogicalDisplayId displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+ int32_t metaState, int32_t repeatCount, nsecs_t downTime,
+ nsecs_t eventTime);
/* Publishes a motion event to the input channel.
*
* Returns OK on success.
* Returns WOULD_BLOCK if the channel is full.
* Returns DEAD_OBJECT if the channel's peer has been closed.
- * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS.
+ * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS,
+ * or if the verifier is enabled and the event failed verification upon publishing.
* Other errors probably indicate that the channel is broken.
*/
status_t publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source,
- int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
- int32_t actionButton, int32_t flags, int32_t edgeFlags,
- int32_t metaState, int32_t buttonState,
+ ui::LogicalDisplayId displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t actionButton, int32_t flags,
+ int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
float xPrecision, float yPrecision, float xCursorPosition,
float yCursorPosition, const ui::Transform& rawTransform,
@@ -451,236 +454,4 @@ private:
InputVerifier mInputVerifier;
};
-/*
- * Consumes input events from an input channel.
- */
-class InputConsumer {
-public:
- /* Create a consumer associated with an input channel. */
- explicit InputConsumer(const std::shared_ptr<InputChannel>& channel);
- /* Create a consumer associated with an input channel, override resampling system property */
- explicit InputConsumer(const std::shared_ptr<InputChannel>& channel,
- bool enableTouchResampling);
-
- /* Destroys the consumer and releases its input channel. */
- ~InputConsumer();
-
- /* Gets the underlying input channel. */
- inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
-
- /* Consumes an input event from the input channel and copies its contents into
- * an InputEvent object created using the specified factory.
- *
- * Tries to combine a series of move events into larger batches whenever possible.
- *
- * If consumeBatches is false, then defers consuming pending batched events if it
- * is possible for additional samples to be added to them later. Call hasPendingBatch()
- * to determine whether a pending batch is available to be consumed.
- *
- * If consumeBatches is true, then events are still batched but they are consumed
- * immediately as soon as the input channel is exhausted.
- *
- * The frameTime parameter specifies the time when the current display frame started
- * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown.
- *
- * The returned sequence number is never 0 unless the operation failed.
- *
- * Returns OK on success.
- * Returns WOULD_BLOCK if there is no event present.
- * Returns DEAD_OBJECT if the channel's peer has been closed.
- * Returns NO_MEMORY if the event could not be created.
- * Other errors probably indicate that the channel is broken.
- */
- status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime,
- uint32_t* outSeq, InputEvent** outEvent);
-
- /* Sends a finished signal to the publisher to inform it that the message
- * with the specified sequence number has finished being process and whether
- * the message was handled by the consumer.
- *
- * Returns OK on success.
- * Returns BAD_VALUE if seq is 0.
- * Other errors probably indicate that the channel is broken.
- */
- status_t sendFinishedSignal(uint32_t seq, bool handled);
-
- status_t sendTimeline(int32_t inputEventId,
- std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
-
- /* Returns true if there is a pending batch.
- *
- * Should be called after calling consume() with consumeBatches == false to determine
- * whether consume() should be called again later on with consumeBatches == true.
- */
- bool hasPendingBatch() const;
-
- /* Returns the source of first pending batch if exist.
- *
- * Should be called after calling consume() with consumeBatches == false to determine
- * whether consume() should be called again later on with consumeBatches == true.
- */
- int32_t getPendingBatchSource() const;
-
- /* Returns true when there is *likely* a pending batch or a pending event in the channel.
- *
- * 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;
-
- std::string dump() const;
-
-private:
- // True if touch resampling is enabled.
- const bool mResampleTouch;
-
- std::shared_ptr<InputChannel> mChannel;
-
- // The current input message.
- InputMessage mMsg;
-
- // True if mMsg contains a valid input message that was deferred from the previous
- // call to consume and that still needs to be handled.
- bool mMsgDeferred;
-
- // Batched motion events per device and source.
- struct Batch {
- std::vector<InputMessage> samples;
- };
- std::vector<Batch> mBatches;
-
- // Touch state per device and source, only for sources of class pointer.
- struct History {
- nsecs_t eventTime;
- BitSet32 idBits;
- int32_t idToIndex[MAX_POINTER_ID + 1];
- PointerCoords pointers[MAX_POINTERS];
-
- void initializeFrom(const InputMessage& msg) {
- eventTime = msg.body.motion.eventTime;
- idBits.clear();
- for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
- uint32_t id = msg.body.motion.pointers[i].properties.id;
- idBits.markBit(id);
- idToIndex[id] = i;
- pointers[i].copyFrom(msg.body.motion.pointers[i].coords);
- }
- }
-
- void initializeFrom(const History& other) {
- eventTime = other.eventTime;
- idBits = other.idBits; // temporary copy
- for (size_t i = 0; i < other.idBits.count(); i++) {
- uint32_t id = idBits.clearFirstMarkedBit();
- int32_t index = other.idToIndex[id];
- idToIndex[id] = index;
- pointers[index].copyFrom(other.pointers[index]);
- }
- idBits = other.idBits; // final copy
- }
-
- const PointerCoords& getPointerById(uint32_t id) const {
- return pointers[idToIndex[id]];
- }
-
- bool hasPointerId(uint32_t id) const {
- return idBits.hasBit(id);
- }
- };
- struct TouchState {
- int32_t deviceId;
- int32_t source;
- size_t historyCurrent;
- size_t historySize;
- History history[2];
- History lastResample;
-
- void initialize(int32_t deviceId, int32_t source) {
- this->deviceId = deviceId;
- this->source = source;
- historyCurrent = 0;
- historySize = 0;
- lastResample.eventTime = 0;
- lastResample.idBits.clear();
- }
-
- void addHistory(const InputMessage& msg) {
- historyCurrent ^= 1;
- if (historySize < 2) {
- historySize += 1;
- }
- history[historyCurrent].initializeFrom(msg);
- }
-
- const History* getHistory(size_t index) const {
- return &history[(historyCurrent + index) & 1];
- }
-
- bool recentCoordinatesAreIdentical(uint32_t id) const {
- // Return true if the two most recently received "raw" coordinates are identical
- if (historySize < 2) {
- return false;
- }
- if (!getHistory(0)->hasPointerId(id) || !getHistory(1)->hasPointerId(id)) {
- return false;
- }
- float currentX = getHistory(0)->getPointerById(id).getX();
- float currentY = getHistory(0)->getPointerById(id).getY();
- float previousX = getHistory(1)->getPointerById(id).getX();
- float previousY = getHistory(1)->getPointerById(id).getY();
- if (currentX == previousX && currentY == previousY) {
- return true;
- }
- return false;
- }
- };
- std::vector<TouchState> mTouchStates;
-
- // Chain of batched sequence numbers. When multiple input messages are combined into
- // a batch, we append a record here that associates the last sequence number in the
- // batch with the previous one. When the finished signal is sent, we traverse the
- // chain to individually finish all input messages that were part of the batch.
- struct SeqChain {
- uint32_t seq; // sequence number of batched input message
- uint32_t chain; // sequence number of previous batched input message
- };
- std::vector<SeqChain> mSeqChains;
-
- // The time at which each event with the sequence number 'seq' was consumed.
- // This data is provided in 'finishInputEvent' so that the receiving end can measure the latency
- // This collection is populated when the event is received, and the entries are erased when the
- // events are finished. It should not grow infinitely because if an event is not ack'd, ANR
- // will be raised for that connection, and no further events will be posted to that channel.
- std::unordered_map<uint32_t /*seq*/, nsecs_t /*consumeTime*/> mConsumeTimes;
-
- status_t consumeBatch(InputEventFactoryInterface* factory,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
- status_t consumeSamples(InputEventFactoryInterface* factory,
- Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent);
-
- void updateTouchState(InputMessage& msg);
- void resampleTouchState(nsecs_t frameTime, MotionEvent* event,
- const InputMessage *next);
-
- ssize_t findBatch(int32_t deviceId, int32_t source) const;
- ssize_t findTouchState(int32_t deviceId, int32_t source) const;
-
- nsecs_t getConsumeTime(uint32_t seq) const;
- void popConsumeTime(uint32_t seq);
- status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
-
- static void rewriteMessage(TouchState& state, InputMessage& msg);
- static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
- static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
- static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
- static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg);
- static void initializeDragEvent(DragEvent* event, const InputMessage* msg);
- static void initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg);
- static void addSample(MotionEvent* event, const InputMessage* msg);
- static bool canAddSample(const Batch& batch, const InputMessage* msg);
- static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
-
- static bool isTouchResamplingEnabled();
-};
-
} // namespace android
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index dfcf766402..67b37b1213 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -19,9 +19,7 @@
#include <stdint.h>
#include <list>
-#ifdef __linux__
#include <binder/IBinder.h>
-#endif
#include <android-base/result.h>
#include <input/Input.h>
@@ -128,9 +126,9 @@ public:
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. */
@@ -144,13 +142,11 @@ public:
std::pair<int32_t /*keyCode*/, int32_t /*metaState*/> applyKeyBehavior(int32_t keyCode,
int32_t metaState) const;
-#ifdef __linux__
/* Reads a key map from a parcel. */
static std::unique_ptr<KeyCharacterMap> readFromParcel(Parcel* parcel);
/* Writes a key map to a parcel. */
void writeToParcel(Parcel* parcel) const;
-#endif
bool operator==(const KeyCharacterMap& other) const = default;
diff --git a/include/input/KeyboardClassifier.h b/include/input/KeyboardClassifier.h
new file mode 100644
index 0000000000..457d474ee7
--- /dev/null
+++ b/include/input/KeyboardClassifier.h
@@ -0,0 +1,53 @@
+/*
+ * 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/result.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+
+#include "rust/cxx.h"
+
+namespace android {
+
+namespace input {
+namespace keyboardClassifier {
+struct KeyboardClassifier;
+}
+} // namespace input
+
+/*
+ * Keyboard classifier to classify keyboard into alphabetic and non-alphabetic keyboards
+ */
+class KeyboardClassifier {
+public:
+ KeyboardClassifier();
+ /**
+ * Get the type of keyboard that the classifier currently believes the device to be.
+ */
+ KeyboardType getKeyboardType(DeviceId deviceId);
+ void notifyKeyboardChanged(DeviceId deviceId, const InputDeviceIdentifier& identifier,
+ uint32_t deviceClasses);
+ void processKey(DeviceId deviceId, int32_t evdevCode, uint32_t metaState);
+
+private:
+ std::optional<rust::Box<android::input::keyboardClassifier::KeyboardClassifier>>
+ mRustClassifier;
+ std::unordered_map<DeviceId, KeyboardType> mKeyboardTypeMap;
+};
+
+} // namespace android
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index 3b6e40183f..200c301ffe 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -16,6 +16,7 @@
#pragma once
+#include <array>
#include <cstdint>
#include <memory>
#include <mutex>
@@ -28,6 +29,7 @@
#include <android/sysprop/InputProperties.sysprop.h>
#include <input/Input.h>
#include <input/MotionPredictorMetricsManager.h>
+#include <input/RingBuffer.h>
#include <input/TfLiteMotionPredictor.h>
#include <utils/Timers.h> // for nsecs_t
@@ -37,6 +39,36 @@ static inline bool isMotionPredictionEnabled() {
return sysprop::InputProperties::enable_motion_prediction().value_or(true);
}
+// Tracker to calculate jerk from motion position samples.
+class JerkTracker {
+public:
+ // Initialize the tracker. If normalizedDt is true, assume that each sample pushed has dt=1.
+ // alpha is the coefficient of the first-order IIR filter for jerk. A factor of 1 results
+ // in no smoothing.
+ JerkTracker(bool normalizedDt, float alpha);
+
+ // Add a position to the tracker and update derivative estimates.
+ void pushSample(int64_t timestamp, float xPos, float yPos);
+
+ // Reset JerkTracker for a new motion input.
+ void reset();
+
+ // Return last jerk calculation, if enough samples have been collected.
+ // Jerk is defined as the 3rd derivative of position (change in
+ // acceleration) and has the units of d^3p/dt^3.
+ std::optional<float> jerkMagnitude() const;
+
+private:
+ const bool mNormalizedDt;
+ // Coefficient of first-order IIR filter to smooth jerk calculation.
+ const float mAlpha;
+
+ RingBuffer<int64_t> mTimestamps{4};
+ std::array<float, 4> mXDerivatives{}; // [x, x', x'', x''']
+ std::array<float, 4> mYDerivatives{}; // [y, y', y'', y''']
+ float mJerkMagnitude;
+};
+
/**
* Given a set of MotionEvents for the current gesture, predict the motion. The returned MotionEvent
* contains a set of samples in the future.
@@ -98,9 +130,16 @@ private:
std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers;
std::optional<MotionEvent> mLastEvent;
- std::optional<MotionPredictorMetricsManager> mMetricsManager;
+ std::unique_ptr<JerkTracker> mJerkTracker;
+
+ std::unique_ptr<MotionPredictorMetricsManager> mMetricsManager;
const ReportAtomFunction mReportAtomFunction;
+
+ // Initialize prediction model and associated objects.
+ // Called during lazy initialization.
+ // TODO: b/210158587 Consider removing lazy initialization.
+ void initializeObjects();
};
} // namespace android
diff --git a/include/input/OWNERS b/include/input/OWNERS
index c88bfe97ca..21d208f577 100644
--- a/include/input/OWNERS
+++ b/include/input/OWNERS
@@ -1 +1,2 @@
+# Bug component: 136048
include platform/frameworks/base:/INPUT_OWNERS
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
new file mode 100644
index 0000000000..dcb25b729f
--- /dev/null
+++ b/include/input/Resampler.h
@@ -0,0 +1,154 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <optional>
+#include <vector>
+
+#include <input/Input.h>
+#include <input/InputTransport.h>
+#include <input/RingBuffer.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+/**
+ * Resampler is an interface for resampling MotionEvents. Every resampling implementation
+ * must use this interface to enable resampling inside InputConsumer's logic.
+ */
+struct Resampler {
+ virtual ~Resampler() = default;
+
+ /**
+ * 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
+ * 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,
+ * if motionEvent had N samples before being passed to Resampler, then it will have N + 1
+ * 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 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 `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.
+ */
+ void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
+ const InputMessage* futureSample) override;
+
+ std::chrono::nanoseconds getResampleLatency() const override;
+
+private:
+ struct Pointer {
+ PointerProperties properties;
+ PointerCoords coords;
+ };
+
+ struct Sample {
+ std::chrono::nanoseconds eventTime;
+ std::vector<Pointer> pointers;
+
+ std::vector<PointerCoords> asPointerCoords() const {
+ std::vector<PointerCoords> pointersCoords;
+ for (const Pointer& pointer : pointers) {
+ pointersCoords.push_back(pointer.coords);
+ }
+ return pointersCoords;
+ }
+ };
+
+ /**
+ * 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.
+ */
+ RingBuffer<Sample> mLatestSamples{/*capacity=*/2};
+
+ /**
+ * 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.
+ */
+ void updateLatestSamples(const MotionEvent& motionEvent);
+
+ static Sample messageToSample(const InputMessage& message);
+
+ /**
+ * Checks if auxiliary sample has the same pointer properties of target sample. That is,
+ * auxiliary pointer IDs must appear in the same order as target pointer IDs, their toolType
+ * must match and be resampleable.
+ */
+ static bool pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary);
+
+ /**
+ * Checks if there are necessary conditions to interpolate. For example, interpolation cannot
+ * take place if samples are too far apart in time. mLatestSamples must have at least one sample
+ * when canInterpolate is invoked.
+ */
+ bool canInterpolate(const InputMessage& futureSample) const;
+
+ /**
+ * Returns a sample interpolated between the latest sample of mLatestSamples and futureSample,
+ * if the conditions from canInterpolate are satisfied. Otherwise, returns nullopt.
+ * mLatestSamples must have at least one sample when attemptInterpolation is called.
+ */
+ std::optional<Sample> attemptInterpolation(std::chrono::nanoseconds resampleTime,
+ const InputMessage& futureSample) const;
+
+ /**
+ * Checks if there are necessary conditions to extrapolate. That is, there are at least two
+ * samples in mLatestSamples, and delta is bounded within a time interval.
+ */
+ bool canExtrapolate() const;
+
+ /**
+ * Returns a sample extrapolated from the two samples of mLatestSamples, if the conditions from
+ * canExtrapolate are satisfied. The returned sample either has eventTime equal to resampleTime,
+ * or an earlier time if resampleTime is too far in the future. If canExtrapolate returns false,
+ * this function returns nullopt.
+ */
+ std::optional<Sample> attemptExtrapolation(std::chrono::nanoseconds resampleTime) const;
+
+ inline static void addSampleToMotionEvent(const Sample& sample, MotionEvent& motionEvent);
+};
+} // namespace android
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index 2edc138f67..49e909ea55 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -105,6 +105,14 @@ public:
// The noise floor for predictions.
// Distances (r) less than this should be discarded as noise.
float distanceNoiseFloor = 0;
+
+ // Low and high jerk thresholds (with normalized dt = 1) for predictions.
+ // High jerk means more predictions will be pruned, vice versa for low.
+ float lowJerk = 0;
+ float highJerk = 0;
+
+ // Coefficient for the first-order IIR filter for jerk calculation.
+ float jerkAlpha = 1;
};
// Creates a model from an encoded Flatbuffer model.
diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h
index 222dac8557..b6c630529c 100644
--- a/include/input/VirtualInputDevice.h
+++ b/include/input/VirtualInputDevice.h
@@ -17,14 +17,30 @@
#pragma once
#include <android-base/unique_fd.h>
+#include <input/Input.h>
+#include <map>
namespace android {
+enum class DeviceType {
+ KEYBOARD,
+ MOUSE,
+ TOUCHSCREEN,
+ DPAD,
+ STYLUS,
+ ROTARY_ENCODER,
+};
+
+android::base::unique_fd openUinput(const char* readableName, int32_t vendorId, int32_t productId,
+ const char* phys, DeviceType deviceType, int32_t screenHeight,
+ int32_t screenWidth);
+
enum class UinputAction {
RELEASE = 0,
PRESS = 1,
MOVE = 2,
CANCEL = 3,
+ ftl_last = CANCEL,
};
class VirtualInputDevice {
@@ -77,6 +93,8 @@ public:
private:
static const std::map<int, int> BUTTON_CODE_MAPPING;
+ int32_t mAccumulatedHighResScrollX;
+ int32_t mAccumulatedHighResScrollY;
};
class VirtualTouchscreen : public VirtualInputDevice {
@@ -122,4 +140,14 @@ private:
bool handleStylusUp(uint16_t tool, std::chrono::nanoseconds eventTime);
};
+class VirtualRotaryEncoder : public VirtualInputDevice {
+public:
+ VirtualRotaryEncoder(android::base::unique_fd fd);
+ virtual ~VirtualRotaryEncoder() override;
+ bool writeScrollEvent(float scrollAmount, std::chrono::nanoseconds eventTime);
+
+private:
+ int32_t mAccumulatedHighResScrollAmount;
+};
+
} // namespace android
diff --git a/include/powermanager/HalResult.h b/include/powermanager/HalResult.h
new file mode 100644
index 0000000000..7fe3822be0
--- /dev/null
+++ b/include/powermanager/HalResult.h
@@ -0,0 +1,165 @@
+/*
+ * 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_auto_utils.h>
+#include <android/binder_status.h>
+#include <android/hardware/power/1.0/IPower.h>
+#include <binder/Status.h>
+#include <hidl/HidlSupport.h>
+#include <string>
+
+namespace android::power {
+
+static bool checkUnsupported(const ndk::ScopedAStatus& ndkStatus) {
+ return ndkStatus.getExceptionCode() == EX_UNSUPPORTED_OPERATION ||
+ ndkStatus.getStatus() == STATUS_UNKNOWN_TRANSACTION;
+}
+
+static bool checkUnsupported(const binder::Status& status) {
+ return status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == UNKNOWN_TRANSACTION;
+}
+
+// Result of a call to the Power HAL wrapper, holding data if successful.
+template <typename T>
+class HalResult {
+public:
+ static HalResult<T> ok(T&& value) { return HalResult(std::forward<T>(value)); }
+ static HalResult<T> ok(T& value) { return HalResult<T>::ok(T{value}); }
+ static HalResult<T> failed(std::string msg) { return HalResult(msg, /* unsupported= */ false); }
+ static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
+
+ static HalResult<T> fromStatus(const binder::Status& status, T&& data) {
+ if (checkUnsupported(status)) {
+ return HalResult<T>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<T>::ok(std::forward<T>(data));
+ }
+ return HalResult<T>::failed(std::string(status.toString8().c_str()));
+ }
+
+ static HalResult<T> fromStatus(const binder::Status& status, T& data) {
+ return HalResult<T>::fromStatus(status, T{data});
+ }
+
+ static HalResult<T> fromStatus(const ndk::ScopedAStatus& ndkStatus, T&& data) {
+ if (checkUnsupported(ndkStatus)) {
+ return HalResult<T>::unsupported();
+ }
+ if (ndkStatus.isOk()) {
+ return HalResult<T>::ok(std::forward<T>(data));
+ }
+ return HalResult<T>::failed(std::string(ndkStatus.getDescription()));
+ }
+
+ static HalResult<T> fromStatus(const ndk::ScopedAStatus& ndkStatus, T& data) {
+ return HalResult<T>::fromStatus(ndkStatus, T{data});
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T&& data) {
+ return ret.isOk() ? HalResult<T>::ok(std::forward<T>(data))
+ : HalResult<T>::failed(ret.description());
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T& data) {
+ return HalResult<T>::fromReturn(ret, T{data});
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
+ T&& data) {
+ return ret.isOk() ? HalResult<T>::fromStatus(status, std::forward<T>(data))
+ : HalResult<T>::failed(ret.description());
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
+ T& data) {
+ return HalResult<T>::fromReturn(ret, status, T{data});
+ }
+
+ // This will throw std::bad_optional_access if this result is not ok.
+ const T& value() const { return mValue.value(); }
+ bool isOk() const { return !mUnsupported && mValue.has_value(); }
+ bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::optional<T> mValue;
+ std::string mErrorMessage;
+ bool mUnsupported;
+
+ explicit HalResult(T&& value)
+ : mValue{std::move(value)}, mErrorMessage(), mUnsupported(false) {}
+ explicit HalResult(std::string errorMessage, bool unsupported)
+ : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
+};
+
+// Empty result
+template <>
+class HalResult<void> {
+public:
+ static HalResult<void> ok() { return HalResult(); }
+ static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
+ static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
+
+ static HalResult<void> fromStatus(const binder::Status& status) {
+ if (checkUnsupported(status)) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(std::string(status.toString8().c_str()));
+ }
+
+ static HalResult<void> fromStatus(const ndk::ScopedAStatus& ndkStatus) {
+ if (ndkStatus.isOk()) {
+ return HalResult<void>::ok();
+ }
+ if (checkUnsupported(ndkStatus)) {
+ return HalResult<void>::unsupported();
+ }
+ return HalResult<void>::failed(ndkStatus.getDescription());
+ }
+
+ template <typename R>
+ static HalResult<void> fromReturn(hardware::Return<R>& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
+ }
+
+ bool isOk() const { return !mUnsupported && !mFailed; }
+ bool isFailed() const { return !mUnsupported && mFailed; }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::string mErrorMessage;
+ bool mFailed;
+ bool mUnsupported;
+
+ explicit HalResult(bool unsupported = false)
+ : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
+ explicit HalResult(std::string errorMessage)
+ : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
+};
+} // namespace android::power
diff --git a/include/powermanager/OWNERS b/include/powermanager/OWNERS
new file mode 100644
index 0000000000..9f40e27a10
--- /dev/null
+++ b/include/powermanager/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:/ADPF_OWNERS \ No newline at end of file
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index c50bc4a188..7e0bd5bedc 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -23,6 +23,7 @@
#include <aidl/android/hardware/power/Mode.h>
#include <android-base/thread_annotations.h>
#include <powermanager/PowerHalWrapper.h>
+#include <powermanager/PowerHintSessionWrapper.h>
namespace android {
@@ -38,6 +39,7 @@ public:
virtual std::unique_ptr<HalWrapper> connect();
virtual void reset();
+ virtual int32_t getAidlVersion();
};
// -------------------------------------------------------------------------------------------------
@@ -59,14 +61,13 @@ public:
int32_t durationMs) override;
virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode,
bool enabled) override;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos) override;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) override;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) override;
virtual HalResult<int64_t> getHintSessionPreferredRate() override;
virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(
int tgid, int uid) override;
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
index cbbfa597ba..ab66336738 100644
--- a/include/powermanager/PowerHalLoader.h
+++ b/include/powermanager/PowerHalLoader.h
@@ -36,6 +36,8 @@ public:
static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
static sp<hardware::power::V1_2::IPower> loadHidlV1_2();
static sp<hardware::power::V1_3::IPower> loadHidlV1_3();
+ // Returns aidl interface version, or 0 if AIDL is not used
+ static int32_t getAidlVersion();
private:
static std::mutex gHalMutex;
@@ -48,6 +50,8 @@ private:
static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
+ static int32_t gAidlInterfaceVersion;
+
PowerHalLoader() = delete;
~PowerHalLoader() = delete;
};
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index e2da014606..6e347a9ce9 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -26,6 +26,9 @@
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/1.2/IPower.h>
#include <android/hardware/power/1.3/IPower.h>
+#include <powermanager/HalResult.h>
+#include <powermanager/PowerHintSessionWrapper.h>
+
#include <binder/Status.h>
#include <utility>
@@ -41,134 +44,6 @@ enum class HalSupport {
OFF = 2,
};
-// Result of a call to the Power HAL wrapper, holding data if successful.
-template <typename T>
-class HalResult {
-public:
- static HalResult<T> ok(T&& value) { return HalResult(std::forward<T>(value)); }
- static HalResult<T> ok(T& value) { return HalResult<T>::ok(T{value}); }
- static HalResult<T> failed(std::string msg) { return HalResult(msg, /* unsupported= */ false); }
- static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
-
- static HalResult<T> fromStatus(const binder::Status& status, T&& data) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<T>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<T>::ok(std::forward<T>(data));
- }
- return HalResult<T>::failed(std::string(status.toString8().c_str()));
- }
-
- static HalResult<T> fromStatus(const binder::Status& status, T& data) {
- return HalResult<T>::fromStatus(status, T{data});
- }
-
- static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T&& data) {
- if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<T>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<T>::ok(std::forward<T>(data));
- }
- return HalResult<T>::failed(std::string(status.getDescription()));
- }
-
- static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T& data) {
- return HalResult<T>::fromStatus(status, T{data});
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, T&& data) {
- return ret.isOk() ? HalResult<T>::ok(std::forward<T>(data))
- : HalResult<T>::failed(ret.description());
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, T& data) {
- return HalResult<T>::fromReturn(ret, T{data});
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
- T&& data) {
- return ret.isOk() ? HalResult<T>::fromStatus(status, std::forward<T>(data))
- : HalResult<T>::failed(ret.description());
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
- T& data) {
- return HalResult<T>::fromReturn(ret, status, T{data});
- }
-
- // This will throw std::bad_optional_access if this result is not ok.
- const T& value() const { return mValue.value(); }
- bool isOk() const { return !mUnsupported && mValue.has_value(); }
- bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
- bool isUnsupported() const { return mUnsupported; }
- const char* errorMessage() const { return mErrorMessage.c_str(); }
-
-private:
- std::optional<T> mValue;
- std::string mErrorMessage;
- bool mUnsupported;
-
- explicit HalResult(T&& value)
- : mValue{std::move(value)}, mErrorMessage(), mUnsupported(false) {}
- explicit HalResult(std::string errorMessage, bool unsupported)
- : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
-};
-
-// Empty result of a call to the Power HAL wrapper.
-template <>
-class HalResult<void> {
-public:
- static HalResult<void> ok() { return HalResult(); }
- static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
- static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
-
- static HalResult<void> fromStatus(const binder::Status& status) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<void>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<void>::ok();
- }
- return HalResult<void>::failed(std::string(status.toString8().c_str()));
- }
-
- static HalResult<void> fromStatus(const ndk::ScopedAStatus& status) {
- if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<void>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<void>::ok();
- }
- return HalResult<void>::failed(std::string(status.getDescription()));
- }
-
- template <typename R>
- static HalResult<void> fromReturn(hardware::Return<R>& ret) {
- return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
- }
-
- bool isOk() const { return !mUnsupported && !mFailed; }
- bool isFailed() const { return !mUnsupported && mFailed; }
- bool isUnsupported() const { return mUnsupported; }
- const char* errorMessage() const { return mErrorMessage.c_str(); }
-
-private:
- std::string mErrorMessage;
- bool mFailed;
- bool mUnsupported;
-
- explicit HalResult(bool unsupported = false)
- : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
- explicit HalResult(std::string errorMessage)
- : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
-};
-
// Wrapper for Power HAL handlers.
class HalWrapper {
public:
@@ -177,14 +52,13 @@ public:
virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
int32_t durationMs) = 0;
virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos) = 0;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) = 0;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) = 0;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) = 0;
virtual HalResult<int64_t> getHintSessionPreferredRate() = 0;
virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
int uid) = 0;
@@ -200,14 +74,13 @@ public:
HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
int32_t durationMs) override;
HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) override;
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) override;
HalResult<int64_t> getHintSessionPreferredRate() override;
HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
int uid) override;
@@ -285,14 +158,13 @@ public:
HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
int32_t durationMs) override;
HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) override;
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) override;
HalResult<int64_t> getHintSessionPreferredRate() override;
HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
@@ -307,14 +179,12 @@ private:
std::mutex mBoostMutex;
std::mutex mModeMutex;
std::shared_ptr<aidl::android::hardware::power::IPower> mHandle;
- // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
- // Need to increase the array size if more boost supported.
- std::array<
- std::atomic<HalSupport>,
- static_cast<int32_t>(aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) +
- 1>
+ std::array<HalSupport,
+ static_cast<int32_t>(
+ *(ndk::enum_range<aidl::android::hardware::power::Boost>().end() - 1)) +
+ 1>
mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
- std::array<std::atomic<HalSupport>,
+ std::array<HalSupport,
static_cast<int32_t>(
*(ndk::enum_range<aidl::android::hardware::power::Mode>().end() - 1)) +
1>
diff --git a/include/powermanager/PowerHintSessionWrapper.h b/include/powermanager/PowerHintSessionWrapper.h
new file mode 100644
index 0000000000..ba6fe77c80
--- /dev/null
+++ b/include/powermanager/PowerHintSessionWrapper.h
@@ -0,0 +1,54 @@
+/*
+ * 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 <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/ChannelConfig.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/SessionConfig.h>
+#include <android-base/thread_annotations.h>
+#include "HalResult.h"
+
+namespace android::power {
+
+// Wrapper for power hint sessions, which allows for better mocking,
+// support checking, and failure handling than using hint sessions directly
+class PowerHintSessionWrapper {
+public:
+ virtual ~PowerHintSessionWrapper() = default;
+ PowerHintSessionWrapper(
+ std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>&& session);
+ virtual HalResult<void> updateTargetWorkDuration(int64_t in_targetDurationNanos);
+ virtual HalResult<void> reportActualWorkDuration(
+ const std::vector<::aidl::android::hardware::power::WorkDuration>& in_durations);
+ virtual HalResult<void> pause();
+ virtual HalResult<void> resume();
+ virtual HalResult<void> close();
+ virtual HalResult<void> sendHint(::aidl::android::hardware::power::SessionHint in_hint);
+ virtual HalResult<void> setThreads(const std::vector<int32_t>& in_threadIds);
+ virtual HalResult<void> setMode(::aidl::android::hardware::power::SessionMode in_type,
+ bool in_enabled);
+ virtual HalResult<aidl::android::hardware::power::SessionConfig> getSessionConfig();
+
+private:
+ std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mSession;
+ int32_t mInterfaceVersion;
+};
+
+} // namespace android::power \ No newline at end of file
diff --git a/include/private/OWNERS b/include/private/OWNERS
new file mode 100644
index 0000000000..db3ae48698
--- /dev/null
+++ b/include/private/OWNERS
@@ -0,0 +1,4 @@
+# ADPF
+per-file thermal_private.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file performance_hint_private.h = file:platform/frameworks/base:/ADPF_OWNERS
+per-file system_health_private.h = file:platform/frameworks/base:/ADPF_OWNERS
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index d8f9db4882..e5eee340ca 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -18,6 +18,7 @@
#define ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
#include <stdint.h>
+#include <android/performance_hint.h>
__BEGIN_DECLS
@@ -75,6 +76,15 @@ enum SessionHint: int32_t {
GPU_LOAD_RESET = 7,
};
+// Allows access to PowerHAL's SessionTags without needing to import its AIDL
+enum class SessionTag : int32_t {
+ OTHER = 0,
+ SURFACEFLINGER = 1,
+ HWUI = 2,
+ GAME = 3,
+ APP = 4,
+};
+
/**
* Sends performance hints to inform the hint session of changes in the workload.
*
@@ -83,14 +93,26 @@ enum SessionHint: int32_t {
* @return 0 on success
* EPIPE if communication with the system service has failed.
*/
-int APerformanceHint_sendHint(void* session, SessionHint hint);
+int APerformanceHint_sendHint(APerformanceHintSession* session, SessionHint hint);
/**
* Return the list of thread ids, this API should only be used for testing only.
*/
-int APerformanceHint_getThreadIds(void* aPerformanceHintSession,
+int APerformanceHint_getThreadIds(APerformanceHintSession* session,
int32_t* const threadIds, size_t* const size);
+/**
+ * Creates a session with additional options
+ */
+APerformanceHintSession* APerformanceHint_createSessionInternal(APerformanceHintManager* manager,
+ 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
#endif // ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H