diff options
Diffstat (limited to 'include')
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 |