diff options
58 files changed, 1751 insertions, 1351 deletions
diff --git a/Android.bp b/Android.bp index 13954ef181..a689b2d601 100644 --- a/Android.bp +++ b/Android.bp @@ -48,6 +48,7 @@ cc_library_headers { export_include_dirs: [ "include/", ], + product_available: true, } ndk_headers { diff --git a/data/etc/android.hardware.bluetooth_le.channel_sounding.xml b/data/etc/android.hardware.bluetooth_le.channel_sounding.xml new file mode 100644 index 0000000000..f0ee5d999f --- /dev/null +++ b/data/etc/android.hardware.bluetooth_le.channel_sounding.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<!-- Adds the feature indicating support for the BLE Channel sounding --> +<permissions> + <feature name="android.hardware.bluetooth_le.channel_sounding" /> +</permissions> diff --git a/include/android/OWNERS b/include/android/OWNERS index fad8c1b890..64d7e5438a 100644 --- a/include/android/OWNERS +++ b/include/android/OWNERS @@ -3,3 +3,6 @@ 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
\ No newline at end of file diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 976c7d6fb3..22eab9410c 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -49,6 +49,7 @@ #define ANDROID_NATIVE_PERFORMANCE_HINT_H #include <sys/cdefs.h> +#include <jni.h> /****************************************************************** * @@ -202,6 +203,9 @@ int APerformanceHint_reportActualWorkDuration( * Release the performance hint manager pointer acquired via * {@link APerformanceHint_createSession}. * + * This cannot be used to close a Java PerformanceHintManager.Session, as its + * lifecycle is tied to the object in the SDK. + * * @param session The performance hint session instance to release. */ void APerformanceHint_closeSession( @@ -370,6 +374,21 @@ void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* _Nonnull aWorkDurati void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, int64_t actualGpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); +/** + * Return the APerformanceHintSession wrapped by a Java PerformanceHintManager.Session object. + * + * The Java session maintains ownership over the wrapped native session, so it cannot be + * closed using {@link APerformanceHint_closeSession}. + * + * @param env The Java environment where the PerformanceHintManager.Session lives. + * @param sessionObj The Java Session to unwrap. + * + * @return A pointer to the APerformanceHintManager that backs the Java Session. + */ +APerformanceHintSession* _Nonnull APerformanceHint_borrowSessionFromJava( + JNIEnv* _Nonnull env, jobject _Nonnull sessionObj) __INTRODUCED_IN(36); + + __END_DECLS #endif // ANDROID_NATIVE_PERFORMANCE_HINT_H diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h index 0b7e16bc7d..a35a145084 100644 --- a/include/audiomanager/IAudioManager.h +++ b/include/audiomanager/IAudioManager.h @@ -56,7 +56,7 @@ public: /*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage, audio_content_type_t content)= 0; /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event, - audio_port_handle_t eventId) = 0; + const std::vector<audio_port_handle_t>& eventIds) = 0; /*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0; virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0; /*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0; diff --git a/include/input/Input.h b/include/input/Input.h index a8684bd19b..127046da82 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -294,6 +294,8 @@ enum class KeyboardType { NONE = AINPUT_KEYBOARD_TYPE_NONE, NON_ALPHABETIC = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, ALPHABETIC = AINPUT_KEYBOARD_TYPE_ALPHABETIC, + ftl_first = NONE, + ftl_last = ALPHABETIC, }; bool isStylusToolType(ToolType toolType); 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/private/performance_hint_private.h b/include/private/performance_hint_private.h index 3229e45203..fb31351807 100644 --- a/include/private/performance_hint_private.h +++ b/include/private/performance_hint_private.h @@ -110,6 +110,18 @@ APerformanceHintSession* APerformanceHint_createSessionInternal(APerformanceHint int64_t initialTargetWorkDurationNanos, SessionTag tag); /** + * Creates a session from the Java SDK implementation + */ +APerformanceHintSession* APerformanceHint_createSessionFromJava(APerformanceHintManager* manager, + const int32_t* threadIds, size_t size, + int64_t initialTargetWorkDurationNanos); + +/** + * Special method for Java SDK implementation to kill sessions + */ +void APerformanceHint_closeSessionFromJava(APerformanceHintSession* session); + +/** * Forces FMQ to be enabled or disabled, for testing only. */ void APerformanceHint_setUseFMQForTesting(bool enabled); diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 2ef642a3a0..0a6117808a 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -829,6 +829,7 @@ aidl_interface { backend: { rust: { apex_available: [ + "//apex_available:platform", "com.android.virt", ], enabled: true, @@ -881,7 +882,6 @@ cc_library { ":__subpackages__", "//packages/modules/Virtualization:__subpackages__", "//device/google/cuttlefish/shared/minidroid:__subpackages__", - "//system/software_defined_vehicle:__subpackages__", "//visibility:any_system_partition", ], } diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp index 174fe8aba8..4036551ef3 100644 --- a/libs/binder/rust/rpcbinder/Android.bp +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -27,7 +27,6 @@ rust_library { visibility: [ "//device/google/cuttlefish/shared/minidroid/sample", "//packages/modules/Virtualization:__subpackages__", - "//system/software_defined_vehicle:__subpackages__", ], apex_available: [ "//apex_available:platform", diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp index 8dabc2c79e..f9f304a812 100644 --- a/libs/gralloc/types/Android.bp +++ b/libs/gralloc/types/Android.bp @@ -56,7 +56,7 @@ cc_library { ], export_shared_lib_headers: [ - "android.hardware.graphics.common-V5-ndk", + "android.hardware.graphics.common-V6-ndk", "android.hardware.graphics.mapper@4.0", "libhidlbase", ], diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index d1a56635f5..a8d5fe7371 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -92,7 +92,7 @@ static const char* kFormerlyVndkspLibrariesList = "android.hardware.common-V2-ndk.so:" "android.hardware.common.fmq-V1-ndk.so:" "android.hardware.graphics.allocator-V2-ndk.so:" - "android.hardware.graphics.common-V5-ndk.so:" + "android.hardware.graphics.common-V6-ndk.so:" "android.hardware.graphics.common@1.0.so:" "android.hardware.graphics.common@1.1.so:" "android.hardware.graphics.common@1.2.so:" diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index bfe3d6e023..8566419435 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -140,7 +140,7 @@ status_t BufferItemConsumer::releaseBufferSlotLocked(int slotIndex, const sp<Gra BI_LOGE("Failed to addReleaseFenceLocked"); } - err = releaseBufferLocked(slotIndex, buffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + err = releaseBufferLocked(slotIndex, buffer); if (err != OK && err != IGraphicBufferConsumer::STALE_BUFFER_SLOT) { BI_LOGE("Failed to release buffer: %s (%d)", strerror(-err), err); diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 95cce5c1df..f2173cd740 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -314,7 +314,7 @@ status_t GLConsumer::releaseTexImage() { // so... basically, nothing more to do here. } - err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer); if (err < NO_ERROR) { GLC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); @@ -418,16 +418,14 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, if (!mAttached) { GLC_LOGE("updateAndRelease: GLConsumer is not attached to an OpenGL " "ES context"); - releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, - mEglDisplay, EGL_NO_SYNC_KHR); + releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer); return INVALID_OPERATION; } // Confirm state. err = checkAndUpdateEglStateLocked(); if (err != NO_ERROR) { - releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, - mEglDisplay, EGL_NO_SYNC_KHR); + releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer); return err; } @@ -440,8 +438,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, if (err != NO_ERROR) { GLC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, slot); - releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, - mEglDisplay, EGL_NO_SYNC_KHR); + releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer); return UNKNOWN_ERROR; } @@ -453,8 +450,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item, // release the old buffer, so instead we just drop the new frame. // As we are still under lock since acquireBuffer, it is safe to // release by slot. - releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer, - mEglDisplay, EGL_NO_SYNC_KHR); + releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer); return err; } } diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp index c705d3926d..282957b940 100644 --- a/libs/gui/IGraphicBufferConsumer.cpp +++ b/libs/gui/IGraphicBufferConsumer.cpp @@ -26,6 +26,7 @@ #include <utils/NativeHandle.h> #include <utils/String8.h> +#include <cstdint> namespace android { @@ -84,7 +85,8 @@ public: EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)), const sp<Fence>& releaseFence) override { - return callRemote<ReleaseBuffer>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence); + using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp<Fence>&); + return callRemote<Signature>(Tag::RELEASE_BUFFER, buf, frameNumber, releaseFence); } status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) override { @@ -188,8 +190,10 @@ status_t BnGraphicBufferConsumer::onTransact(uint32_t code, const Parcel& data, return callLocal(data, reply, &IGraphicBufferConsumer::detachBuffer); case Tag::ATTACH_BUFFER: return callLocal(data, reply, &IGraphicBufferConsumer::attachBuffer); - case Tag::RELEASE_BUFFER: - return callLocal(data, reply, &IGraphicBufferConsumer::releaseHelper); + case Tag::RELEASE_BUFFER: { + using Signature = status_t (IGraphicBufferConsumer::*)(int, uint64_t, const sp<Fence>&); + return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::releaseBuffer); + } case Tag::CONSUMER_CONNECT: return callLocal(data, reply, &IGraphicBufferConsumer::consumerConnect); case Tag::CONSUMER_DISCONNECT: diff --git a/libs/gui/StreamSplitter.cpp b/libs/gui/StreamSplitter.cpp index 2f8e104ea0..653b91bcf6 100644 --- a/libs/gui/StreamSplitter.cpp +++ b/libs/gui/StreamSplitter.cpp @@ -234,8 +234,7 @@ void StreamSplitter::onBufferReleasedByOutput( LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "attaching buffer to input failed (%d)", status); - status = mInput->releaseBuffer(consumerSlot, /* frameNumber */ 0, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, tracker->getMergedFence()); + status = mInput->releaseBuffer(consumerSlot, /* frameNumber */ 0, tracker->getMergedFence()); LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "releasing buffer to input failed (%d)", status); diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index bfe3eb31e8..8a66dc0cf1 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -268,9 +268,9 @@ protected: // releaseBufferLocked overrides the ConsumerBase method to update the // mEglSlots array in addition to the ConsumerBase. - virtual status_t releaseBufferLocked(int slot, - const sp<GraphicBuffer> graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) override; + virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, + EGLDisplay display = EGL_NO_DISPLAY, + EGLSyncKHR eglFence = EGL_NO_SYNC_KHR) override; status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, EGLSyncKHR eglFence) { diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h index 0b92e7df62..18f5488173 100644 --- a/libs/gui/include/gui/IGraphicBufferConsumer.h +++ b/libs/gui/include/gui/IGraphicBufferConsumer.h @@ -137,16 +137,9 @@ public: virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR fence, const sp<Fence>& releaseFence) = 0; - status_t releaseHelper(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) { + status_t releaseBuffer(int buf, uint64_t frameNumber, const sp<Fence>& releaseFence) { return releaseBuffer(buf, frameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence); } - // This is explicitly *not* the actual signature of IGBC::releaseBuffer, but: - // 1) We have no easy way to send the EGL objects across Binder - // 2) This has always been broken, probably because - // 3) IGBC is rarely remoted - // For now, we will choose to bury our heads in the sand and ignore this problem until such time - // as we can finally finish converting away from EGL sync to native Android sync - using ReleaseBuffer = decltype(&IGraphicBufferConsumer::releaseHelper); // consumerConnect connects a consumer to the BufferQueue. Only one consumer may be connected, // and when that consumer disconnects the BufferQueue is placed into the "abandoned" state, diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index 22d32e9769..6bf38c05f1 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -131,3 +131,11 @@ flag { bug: "359252619" is_fixed_read_only: true } # bq_producer_throttles_only_async_mode + +flag { + name: "bq_gl_fence_cleanup" + namespace: "core_graphics" + description: "Remove BufferQueueProducer::dequeue's wait on this fence (or the fence entirely) to prevent deadlocks" + bug: "339705065" + is_fixed_read_only: true +} # bq_gl_fence_cleanup diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index b026e640aa..16060990bd 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -425,8 +425,7 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { ASSERT_EQ(BAD_VALUE, mConsumer->attachBuffer(&newSlot, nullptr)); ASSERT_EQ(OK, mConsumer->attachBuffer(&newSlot, item.mGraphicBuffer)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(newSlot, 0, Fence::NO_FENCE)); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, GRALLOC_USAGE_SW_WRITE_OFTEN, @@ -609,8 +608,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) { ASSERT_EQ(true, item.mQueuedBuffer); ASSERT_EQ(false, item.mAutoRefresh); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // attempt to acquire a second time should return no buffer available ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE, @@ -653,8 +651,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) { ASSERT_EQ(i == 0, item.mQueuedBuffer); ASSERT_EQ(true, item.mAutoRefresh); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); } // Repeatedly queue and dequeue a buffer from the producer side, it should @@ -684,8 +681,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) { ASSERT_EQ(i == 0, item.mQueuedBuffer); ASSERT_EQ(true, item.mAutoRefresh); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); } } @@ -735,8 +731,7 @@ TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) { ASSERT_EQ(true, item.mQueuedBuffer); ASSERT_EQ(false, item.mAutoRefresh); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // attempt to acquire a second time should return no buffer available ASSERT_EQ(IGraphicBufferConsumer::NO_BUFFER_AVAILABLE, @@ -874,8 +869,7 @@ TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) { for (size_t i = 0; i < 2; ++i) { BufferItem item; ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); } // Make sure we got the second buffer back @@ -929,8 +923,7 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); } @@ -946,8 +939,7 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); } ASSERT_EQ(OK, @@ -959,12 +951,10 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // Sleep between segments std::this_thread::sleep_for(500ms); @@ -981,13 +971,11 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { nullptr, nullptr)); ASSERT_EQ(OK, mProducer->queueBuffer(slot, input, &output)); ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); std::this_thread::sleep_for(16ms); } ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // Now we read the segments std::vector<OccupancyTracker::Segment> history; @@ -1108,8 +1096,7 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { // Acquire and free 1 buffer ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); int releasedSlot = item.mSlot; // Acquire 1 buffer, leaving 1 filled buffer in queue @@ -1376,8 +1363,7 @@ TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) { ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(slot, item.mSlot); ASSERT_NE(nullptr, item.mGraphicBuffer.get()); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // Dequeue and queue the buffer again ASSERT_EQ(OK, @@ -1390,8 +1376,7 @@ TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) { ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(slot, item.mSlot); ASSERT_EQ(nullptr, item.mGraphicBuffer.get()); - ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // Dequeue and queue the buffer again ASSERT_EQ(OK, diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp index f34b03eade..1c439cdb45 100644 --- a/libs/gui/tests/StreamSplitter_test.cpp +++ b/libs/gui/tests/StreamSplitter_test.cpp @@ -95,8 +95,7 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) { ASSERT_EQ(*dataOut, TEST_DATA); ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); - ASSERT_EQ(OK, outputConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, - EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + ASSERT_EQ(OK, outputConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, Fence::NO_FENCE)); // This should succeed even with allocation disabled since it will have // received the buffer back from the output BufferQueue @@ -168,9 +167,9 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { ASSERT_EQ(*dataOut, TEST_DATA); ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); - ASSERT_EQ(OK, outputConsumers[output]->releaseBuffer(item.mSlot, - item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, - Fence::NO_FENCE)); + ASSERT_EQ(OK, + outputConsumers[output]->releaseBuffer(item.mSlot, item.mFrameNumber, + Fence::NO_FENCE)); } // This should succeed even with allocation disabled since it will have diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 88893b64ba..c74186682e 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -201,9 +201,9 @@ protected: releasedItems.resize(1+extraDiscardedBuffers); for (size_t i = 0; i < releasedItems.size(); i++) { ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&releasedItems[i], 0)); - ASSERT_EQ(NO_ERROR, consumer->releaseBuffer(releasedItems[i].mSlot, - releasedItems[i].mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, - Fence::NO_FENCE)); + ASSERT_EQ(NO_ERROR, + consumer->releaseBuffer(releasedItems[i].mSlot, releasedItems[i].mFrameNumber, + Fence::NO_FENCE)); } int32_t expectedReleaseCb = (enableReleasedCb ? releasedItems.size() : 0); if (hasSurfaceListener) { diff --git a/libs/input/rust/data_store.rs b/libs/input/rust/data_store.rs index 6bdcefda36..beb6e23478 100644 --- a/libs/input/rust/data_store.rs +++ b/libs/input/rust/data_store.rs @@ -17,7 +17,7 @@ //! Contains the DataStore, used to store input related data in a persistent way. use crate::input::KeyboardType; -use log::{debug, error}; +use log::{debug, error, info}; use serde::{Deserialize, Serialize}; use std::fs::File; use std::io::{Read, Write}; @@ -157,7 +157,7 @@ impl FileReaderWriter for DefaultFileReaderWriter { let path = Path::new(&self.filepath); let mut fs_string = String::new(); match File::open(path) { - Err(e) => error!("couldn't open {:?}: {}", path, e), + Err(e) => info!("couldn't open {:?}: {}", path, e), Ok(mut file) => match file.read_to_string(&mut fs_string) { Err(e) => error!("Couldn't read from {:?}: {}", path, e), Ok(_) => debug!("Successfully read from file {:?}", path), diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp index 1210f711de..39bb841c0a 100644 --- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp @@ -14,15 +14,18 @@ * limitations under the License. */ +#include <TestEventMatchers.h> #include <android-base/logging.h> #include <attestation/HmacKeyManager.h> #include <ftl/enum.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <input/BlockingQueue.h> #include <input/InputConsumerNoResampling.h> #include <input/InputTransport.h> using android::base::Result; +using ::testing::Matcher; namespace android { @@ -278,7 +281,7 @@ protected: void SetUp() override { std::unique_ptr<InputChannel> serverChannel; status_t result = - InputChannel::openInputChannelPair("channel name", serverChannel, mClientChannel); + InputChannel::openInputChannelPair("test channel", serverChannel, mClientChannel); ASSERT_EQ(OK, result); mPublisher = std::make_unique<InputPublisher>(std::move(serverChannel)); @@ -336,6 +339,8 @@ protected: // accessed on the test thread. BlockingQueue<bool> mProbablyHasInputResponses; + std::unique_ptr<MotionEvent> assertReceivedMotionEvent(const Matcher<MotionEvent>& matcher); + private: sp<MessageHandler> mMessageHandler; void handleMessage(const Message& message); @@ -389,6 +394,20 @@ void InputPublisherAndConsumerNoResamplingTest::sendMessage(LooperMessage messag mLooper->sendMessage(mMessageHandler, msg); } +std::unique_ptr<MotionEvent> InputPublisherAndConsumerNoResamplingTest::assertReceivedMotionEvent( + const Matcher<MotionEvent>& matcher) { + std::optional<std::unique_ptr<MotionEvent>> event = mMotionEvents.popWithTimeout(TIMEOUT); + if (!event) { + ADD_FAILURE() << "No event was received, but expected motion " << matcher; + return nullptr; + } + if (*event == nullptr) { + LOG(FATAL) << "Event was received, but it was null"; + } + EXPECT_THAT(**event, matcher); + return std::move(*event); +} + void InputPublisherAndConsumerNoResamplingTest::handleMessage(const Message& message) { switch (static_cast<LooperMessage>(message.what)) { case LooperMessage::CALL_PROBABLY_HAS_INPUT: { @@ -572,8 +591,7 @@ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeSinglePointerMu const nsecs_t publishTimeOfDown = systemTime(SYSTEM_TIME_MONOTONIC); publishMotionEvent(*mPublisher, argsDown); - // Consume the DOWN event. - ASSERT_TRUE(mMotionEvents.popWithTimeout(TIMEOUT).has_value()); + assertReceivedMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN)); verifyFinishedSignal(*mPublisher, mSeq, publishTimeOfDown); @@ -622,10 +640,10 @@ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeSinglePointerMu // the motion as a batched event, or as a sequence of multiple single-sample MotionEvents (or a // mix of those) while (singleSampledMotionEvents.size() != nSamples) { - const std::optional<std::unique_ptr<MotionEvent>> batchedMotionEvent = - mMotionEvents.popWithTimeout(TIMEOUT); + const std::unique_ptr<MotionEvent> batchedMotionEvent = + assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE)); // The events received by these calls are never null - std::vector<MotionEvent> splitMotionEvents = splitBatchedMotionEvent(**batchedMotionEvent); + std::vector<MotionEvent> splitMotionEvents = splitBatchedMotionEvent(*batchedMotionEvent); singleSampledMotionEvents.insert(singleSampledMotionEvents.end(), splitMotionEvents.begin(), splitMotionEvents.end()); } @@ -681,10 +699,7 @@ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeBatchedMotionMo } mNotifyLooperMayProceed.notify_all(); - std::optional<std::unique_ptr<MotionEvent>> optMotion = mMotionEvents.popWithTimeout(TIMEOUT); - ASSERT_TRUE(optMotion.has_value()); - std::unique_ptr<MotionEvent> motion = std::move(*optMotion); - ASSERT_EQ(ACTION_MOVE, motion->getAction()); + assertReceivedMotionEvent(WithMotionAction(ACTION_MOVE)); verifyFinishedSignal(*mPublisher, seq, publishTime); } @@ -696,9 +711,7 @@ void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeMotionEvent( nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); publishMotionEvent(*mPublisher, args); - std::optional<std::unique_ptr<MotionEvent>> optMotion = mMotionEvents.popWithTimeout(TIMEOUT); - ASSERT_TRUE(optMotion.has_value()); - std::unique_ptr<MotionEvent> event = std::move(*optMotion); + std::unique_ptr<MotionEvent> event = assertReceivedMotionEvent(WithMotionAction(action)); verifyArgsEqualToEvent(args, *event); diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h index f1453bd64d..006a785cb7 100644 --- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h +++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h @@ -344,7 +344,8 @@ protected: * mEglSlots array in addition to the ConsumerBase. */ virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer, - EGLDisplay display, EGLSyncKHR eglFence) override; + EGLDisplay display = EGL_NO_DISPLAY, + EGLSyncKHR eglFence = EGL_NO_SYNC_KHR) override; /** * freeBufferLocked frees up the given buffer slot. If the slot has been diff --git a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp index 275b7a4888..3959fce008 100644 --- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp @@ -150,8 +150,7 @@ status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) { } } - err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); + err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer); if (err < NO_ERROR) { EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err); return err; @@ -234,14 +233,14 @@ status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRele if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) { EGC_LOGE("updateAndRelease: EGLConsumer is not attached to an OpenGL " "ES context"); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return INVALID_OPERATION; } // Confirm state. err = checkAndUpdateEglStateLocked(st); if (err != NO_ERROR) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return err; } @@ -254,7 +253,7 @@ status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRele if (err != NO_ERROR) { EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay, slot); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return UNKNOWN_ERROR; } @@ -266,8 +265,7 @@ status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRele // release the old buffer, so instead we just drop the new frame. // As we are still under lock since acquireBuffer, it is safe to // release by slot. - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, - EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return err; } } diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp index 32b229d77c..60e87b54d5 100644 --- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp +++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp @@ -64,8 +64,7 @@ sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace // Wait on the producer fence for the buffer to be ready. err = fenceWait(item.mFence->get(), fencePassThroughHandle); if (err != OK) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return nullptr; } } @@ -79,8 +78,7 @@ sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace err = createFence(st.mUseFenceSync, &mImageSlots[slot].eglFence(), &display, &releaseFenceId, fencePassThroughHandle); if (OK != err) { - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return nullptr; } @@ -91,8 +89,7 @@ sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace releaseFence); if (err != OK) { IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err); - st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY, - EGL_NO_SYNC_KHR); + st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer); return nullptr; } } diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs index 014c912a67..9876362cec 100644 --- a/libs/nativewindow/rust/src/lib.rs +++ b/libs/nativewindow/rust/src/lib.rs @@ -31,11 +31,13 @@ use binder::{ }; use ffi::{ AHardwareBuffer, AHardwareBuffer_Desc, AHardwareBuffer_readFromParcel, - AHardwareBuffer_writeToParcel, + AHardwareBuffer_writeToParcel, ARect, }; +use std::ffi::c_void; use std::fmt::{self, Debug, Formatter}; -use std::mem::ManuallyDrop; -use std::ptr::{self, null_mut, NonNull}; +use std::mem::{forget, ManuallyDrop}; +use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd}; +use std::ptr::{self, null, null_mut, NonNull}; /// Wrapper around a C `AHardwareBuffer_Desc`. #[derive(Clone, Debug, PartialEq, Eq)] @@ -267,10 +269,141 @@ impl HardwareBuffer { rfu0: 0, rfu1: 0, }; - // SAFETY: neither the buffer nor AHardwareBuffer_Desc pointers will be null. + // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the + // AHardwareBuffer_Desc pointer is valid because it comes from a reference. unsafe { ffi::AHardwareBuffer_describe(self.0.as_ref(), &mut buffer_desc) }; HardwareBufferDescription(buffer_desc) } + + /// Locks the hardware buffer for direct CPU access. + /// + /// # Safety + /// + /// - If `fence` is `None`, the caller must ensure that all writes to the buffer have completed + /// before calling this function. + /// - If the buffer has `AHARDWAREBUFFER_FORMAT_BLOB`, multiple threads or process may lock the + /// buffer simultaneously, but the caller must ensure that they don't access it simultaneously + /// and break Rust's aliasing rules, like any other shared memory. + /// - Otherwise if `usage` includes `AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY` or + /// `AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN`, the caller must ensure that no other threads or + /// processes lock the buffer simultaneously for any usage. + /// - Otherwise, the caller must ensure that no other threads lock the buffer for writing + /// simultaneously. + /// - If `rect` is not `None`, the caller must not modify the buffer outside of that rectangle. + pub unsafe fn lock<'a>( + &'a self, + usage: AHardwareBuffer_UsageFlags, + fence: Option<BorrowedFd>, + rect: Option<&ARect>, + ) -> Result<HardwareBufferGuard<'a>, StatusCode> { + let fence = if let Some(fence) = fence { fence.as_raw_fd() } else { -1 }; + let rect = rect.map(ptr::from_ref).unwrap_or(null()); + let mut address = null_mut(); + // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the buffer address out + // pointer is valid because it comes from a reference. Our caller promises that writes have + // completed and there will be no simultaneous read/write locks. + let status = unsafe { + ffi::AHardwareBuffer_lock(self.0.as_ptr(), usage.0, fence, rect, &mut address) + }; + status_result(status)?; + Ok(HardwareBufferGuard { + buffer: self, + address: NonNull::new(address) + .expect("AHardwareBuffer_lock set a null outVirtualAddress"), + }) + } + + /// Locks the hardware buffer for direct CPU access, returning information about the bytes per + /// pixel and stride as well. + /// + /// # Safety + /// + /// - If `fence` is `None`, the caller must ensure that all writes to the buffer have completed + /// before calling this function. + /// - If the buffer has `AHARDWAREBUFFER_FORMAT_BLOB`, multiple threads or process may lock the + /// buffer simultaneously, but the caller must ensure that they don't access it simultaneously + /// and break Rust's aliasing rules, like any other shared memory. + /// - Otherwise if `usage` includes `AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY` or + /// `AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN`, the caller must ensure that no other threads or + /// processes lock the buffer simultaneously for any usage. + /// - Otherwise, the caller must ensure that no other threads lock the buffer for writing + /// simultaneously. + pub unsafe fn lock_and_get_info<'a>( + &'a self, + usage: AHardwareBuffer_UsageFlags, + fence: Option<BorrowedFd>, + rect: Option<&ARect>, + ) -> Result<LockedBufferInfo<'a>, StatusCode> { + let fence = if let Some(fence) = fence { fence.as_raw_fd() } else { -1 }; + let rect = rect.map(ptr::from_ref).unwrap_or(null()); + let mut address = null_mut(); + let mut bytes_per_pixel = 0; + let mut stride = 0; + // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid, and the various out + // pointers are valid because they come from references. Our caller promises that writes have + // completed and there will be no simultaneous read/write locks. + let status = unsafe { + ffi::AHardwareBuffer_lockAndGetInfo( + self.0.as_ptr(), + usage.0, + fence, + rect, + &mut address, + &mut bytes_per_pixel, + &mut stride, + ) + }; + status_result(status)?; + Ok(LockedBufferInfo { + guard: HardwareBufferGuard { + buffer: self, + address: NonNull::new(address) + .expect("AHardwareBuffer_lockAndGetInfo set a null outVirtualAddress"), + }, + bytes_per_pixel: bytes_per_pixel as u32, + stride: stride as u32, + }) + } + + /// Unlocks the hardware buffer from direct CPU access. + /// + /// Must be called after all changes to the buffer are completed by the caller. This will block + /// until the unlocking is complete and the buffer contents are updated. + fn unlock(&self) -> Result<(), StatusCode> { + // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid. + let status = unsafe { ffi::AHardwareBuffer_unlock(self.0.as_ptr(), null_mut()) }; + status_result(status)?; + Ok(()) + } + + /// Unlocks the hardware buffer from direct CPU access. + /// + /// Must be called after all changes to the buffer are completed by the caller. + /// + /// This may not block until all work is completed, but rather will return a file descriptor + /// which will be signalled once the unlocking is complete and the buffer contents is updated. + /// If `Ok(None)` is returned then unlocking has already completed and no further waiting is + /// necessary. The file descriptor may be passed to a subsequent call to [`Self::lock`]. + pub fn unlock_with_fence( + &self, + guard: HardwareBufferGuard, + ) -> Result<Option<OwnedFd>, StatusCode> { + // Forget the guard so that its `Drop` implementation doesn't try to unlock the + // HardwareBuffer again. + forget(guard); + + let mut fence = -2; + // SAFETY: The `AHardwareBuffer` pointer we wrap is always valid. + let status = unsafe { ffi::AHardwareBuffer_unlock(self.0.as_ptr(), &mut fence) }; + let fence = if fence < 0 { + None + } else { + // SAFETY: `AHardwareBuffer_unlock` gives us ownership of the fence file descriptor. + Some(unsafe { OwnedFd::from_raw_fd(fence) }) + }; + status_result(status)?; + Ok(fence) + } } impl Drop for HardwareBuffer { @@ -346,6 +479,37 @@ unsafe impl Send for HardwareBuffer {} // according to the docs on the underlying gralloc calls) unsafe impl Sync for HardwareBuffer {} +/// A guard for when a `HardwareBuffer` is locked. +/// +/// The `HardwareBuffer` will be unlocked when this is dropped, or may be unlocked via +/// [`HardwareBuffer::unlock_with_fence`]. +#[derive(Debug)] +pub struct HardwareBufferGuard<'a> { + buffer: &'a HardwareBuffer, + /// The address of the buffer in memory. + pub address: NonNull<c_void>, +} + +impl<'a> Drop for HardwareBufferGuard<'a> { + fn drop(&mut self) { + self.buffer + .unlock() + .expect("Failed to unlock HardwareBuffer when dropping HardwareBufferGuard"); + } +} + +/// A guard for when a `HardwareBuffer` is locked, with additional information about the number of +/// bytes per pixel and stride. +#[derive(Debug)] +pub struct LockedBufferInfo<'a> { + /// The locked buffer guard. + pub guard: HardwareBufferGuard<'a>, + /// The number of bytes used for each pixel in the buffer. + pub bytes_per_pixel: u32, + /// The stride in bytes between rows in the buffer. + pub stride: u32, +} + #[cfg(test)] mod test { use super::*; @@ -499,4 +663,108 @@ mod test { assert_eq!(buffer.description(), buffer_description); assert_eq!(buffer2.description(), buffer_description); } + + #[test] + fn lock() { + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( + 1024, + 512, + 1, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + 0, + )) + .expect("Failed to create buffer"); + + // SAFETY: No other threads or processes have access to the buffer. + let guard = unsafe { + buffer.lock( + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + None, + None, + ) + } + .unwrap(); + + drop(guard); + } + + #[test] + fn lock_with_rect() { + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( + 1024, + 512, + 1, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + 0, + )) + .expect("Failed to create buffer"); + let rect = ARect { left: 10, right: 20, top: 35, bottom: 45 }; + + // SAFETY: No other threads or processes have access to the buffer. + let guard = unsafe { + buffer.lock( + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + None, + Some(&rect), + ) + } + .unwrap(); + + drop(guard); + } + + #[test] + fn unlock_with_fence() { + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( + 1024, + 512, + 1, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + 0, + )) + .expect("Failed to create buffer"); + + // SAFETY: No other threads or processes have access to the buffer. + let guard = unsafe { + buffer.lock( + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + None, + None, + ) + } + .unwrap(); + + buffer.unlock_with_fence(guard).unwrap(); + } + + #[test] + fn lock_with_info() { + const WIDTH: u32 = 1024; + let buffer = HardwareBuffer::new(&HardwareBufferDescription::new( + WIDTH, + 512, + 1, + AHardwareBuffer_Format::AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM, + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + 0, + )) + .expect("Failed to create buffer"); + + // SAFETY: No other threads or processes have access to the buffer. + let info = unsafe { + buffer.lock_and_get_info( + AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, + None, + None, + ) + } + .unwrap(); + + assert_eq!(info.bytes_per_pixel, 4); + assert_eq!(info.stride, WIDTH * 4); + drop(info); + } } diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp index 8b13d78840..8d6f74b605 100644 --- a/libs/ui/DisplayIdentification.cpp +++ b/libs/ui/DisplayIdentification.cpp @@ -19,9 +19,12 @@ #include <algorithm> #include <cctype> +#include <cstdint> #include <numeric> #include <optional> #include <span> +#include <string> +#include <string_view> #include <ftl/hash.h> #include <log/log.h> @@ -194,6 +197,21 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { const uint16_t productId = static_cast<uint16_t>(edid[kProductIdOffset] | (edid[kProductIdOffset + 1] << 8)); + // Bytes 12-15: display serial number, in little-endian (LSB). This field is + // optional and its absence is marked by having all bytes set to 0x00. + // Values do not represent ASCII characters. + constexpr size_t kSerialNumberOffset = 12; + if (edid.size() < kSerialNumberOffset + sizeof(uint32_t)) { + ALOGE("Invalid EDID: block zero S/N is truncated."); + return {}; + } + const uint32_t blockZeroSerialNumber = edid[kSerialNumberOffset] + + (edid[kSerialNumberOffset + 1] << 8) + (edid[kSerialNumberOffset + 2] << 16) + + (edid[kSerialNumberOffset + 3] << 24); + const auto hashedBlockZeroSNOpt = blockZeroSerialNumber == 0 + ? std::nullopt + : ftl::stable_hash(std::string_view(std::to_string(blockZeroSerialNumber))); + constexpr size_t kManufactureWeekOffset = 16; if (edid.size() < kManufactureWeekOffset + sizeof(uint8_t)) { ALOGE("Invalid EDID: manufacture week is truncated."); @@ -212,6 +230,15 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { ALOGW_IF(manufactureOrModelYear <= 0xf, "Invalid EDID: model year or manufacture year cannot be in the range [0x0, 0xf]."); + constexpr size_t kMaxHorizontalPhysicalSizeOffset = 21; + constexpr size_t kMaxVerticalPhysicalSizeOffset = 22; + if (edid.size() < kMaxVerticalPhysicalSizeOffset + sizeof(uint8_t)) { + ALOGE("Invalid EDID: display's physical size is truncated."); + return {}; + } + ui::Size maxPhysicalSizeInCm(edid[kMaxHorizontalPhysicalSizeOffset], + edid[kMaxVerticalPhysicalSizeOffset]); + constexpr size_t kDescriptorOffset = 54; if (edid.size() < kDescriptorOffset) { ALOGE("Invalid EDID: descriptors are missing."); @@ -222,7 +249,8 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { view = view.subspan(kDescriptorOffset); std::string_view displayName; - std::string_view serialNumber; + std::string_view descriptorBlockSerialNumber; + std::optional<uint64_t> hashedDescriptorBlockSNOpt = std::nullopt; std::string_view asciiText; ui::Size preferredDTDPixelSize; ui::Size preferredDTDPhysicalSize; @@ -247,7 +275,10 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { asciiText = parseEdidText(descriptor); break; case 0xff: - serialNumber = parseEdidText(descriptor); + descriptorBlockSerialNumber = parseEdidText(descriptor); + hashedDescriptorBlockSNOpt = descriptorBlockSerialNumber.empty() + ? std::nullopt + : ftl::stable_hash(descriptorBlockSerialNumber); break; } } else if (isDetailedTimingDescriptor(view)) { @@ -288,7 +319,7 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { if (modelString.empty()) { ALOGW("Invalid EDID: falling back to serial number due to missing display name."); - modelString = serialNumber; + modelString = descriptorBlockSerialNumber; } if (modelString.empty()) { ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number."); @@ -341,11 +372,14 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { return Edid{ .manufacturerId = manufacturerId, .productId = productId, + .hashedBlockZeroSerialNumberOpt = hashedBlockZeroSNOpt, + .hashedDescriptorBlockSerialNumberOpt = hashedDescriptorBlockSNOpt, .pnpId = *pnpId, .modelHash = modelHash, .displayName = displayName, .manufactureOrModelYear = manufactureOrModelYear, .manufactureWeek = manufactureWeek, + .physicalSizeInCm = maxPhysicalSizeInCm, .cea861Block = cea861Block, .preferredDetailedTimingDescriptor = preferredDetailedTimingDescriptor, }; diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h index 648e024d86..cf67d7bf93 100644 --- a/libs/ui/include/ui/DisplayIdentification.h +++ b/libs/ui/include/ui/DisplayIdentification.h @@ -69,11 +69,14 @@ struct Cea861ExtensionBlock : ExtensionBlock { struct Edid { uint16_t manufacturerId; uint16_t productId; + std::optional<uint64_t> hashedBlockZeroSerialNumberOpt; + std::optional<uint64_t> hashedDescriptorBlockSerialNumberOpt; PnpId pnpId; uint32_t modelHash; std::string_view displayName; uint8_t manufactureOrModelYear; uint8_t manufactureWeek; + ui::Size physicalSizeInCm; std::optional<Cea861ExtensionBlock> cea861Block; std::optional<DetailedTimingDescriptor> preferredDetailedTimingDescriptor; }; diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp index 76e3f66e1b..d1699e79b6 100644 --- a/libs/ui/tests/DisplayIdentification_test.cpp +++ b/libs/ui/tests/DisplayIdentification_test.cpp @@ -33,7 +33,7 @@ namespace android { namespace { const unsigned char kInternalEdid[] = - "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00" + "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x4e\x61\xbc\x00" "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27" "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30" @@ -54,7 +54,7 @@ const unsigned char kExternalEdid[] = // Extended EDID with timing extension. const unsigned char kExternalEedid[] = - "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00" + "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\xb1\x7f\x39\x05" "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26" "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00" "\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c" @@ -112,7 +112,7 @@ const unsigned char kHisenseTvEdid[] = "\x07"; const unsigned char kCtlDisplayEdid[] = - "\x00\xff\xff\xff\xff\xff\xff\x00\x0e\x8c\x9d\x24\x00\x00\x00\x00" + "\x00\xff\xff\xff\xff\xff\xff\x00\x0e\x8c\x9d\x24\x30\x41\xab\x00" "\xff\x17\x01\x04\xa5\x34\x1d\x78\x3a\xa7\x25\xa4\x57\x51\xa0\x26" "\x10\x50\x54\xbf\xef\x80\xb3\x00\xa9\x40\x95\x00\x81\x40\x81\x80" "\x95\x0f\x71\x4f\x90\x40\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c" @@ -191,8 +191,13 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("121AT11-801"), 626564263); EXPECT_TRUE(edid->displayName.empty()); EXPECT_EQ(12610, edid->productId); + EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("12345678"), edid->hashedBlockZeroSerialNumberOpt.value()); + EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); EXPECT_EQ(21, edid->manufactureOrModelYear); EXPECT_EQ(0, edid->manufactureWeek); + EXPECT_EQ(26, edid->physicalSizeInCm.width); + EXPECT_EQ(16, edid->physicalSizeInCm.height); EXPECT_FALSE(edid->cea861Block); EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); @@ -207,8 +212,14 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("HP ZR30w"), 918492362); EXPECT_EQ("HP ZR30w", edid->displayName); EXPECT_EQ(10348, edid->productId); + EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("16843009"), edid->hashedBlockZeroSerialNumberOpt.value()); + EXPECT_TRUE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("CN4202137Q"), edid->hashedDescriptorBlockSerialNumberOpt.value()); EXPECT_EQ(22, edid->manufactureOrModelYear); EXPECT_EQ(2, edid->manufactureWeek); + EXPECT_EQ(64, edid->physicalSizeInCm.width); + EXPECT_EQ(40, edid->physicalSizeInCm.height); EXPECT_FALSE(edid->cea861Block); EXPECT_EQ(1280, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); EXPECT_EQ(800, edid->preferredDetailedTimingDescriptor->pixelSizeCount.height); @@ -223,8 +234,13 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("SAMSUNG"), 1201368132); EXPECT_EQ("SAMSUNG", edid->displayName); EXPECT_EQ(2302, edid->productId); + EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("87654321"), edid->hashedBlockZeroSerialNumberOpt.value()); + EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); EXPECT_EQ(21, edid->manufactureOrModelYear); EXPECT_EQ(41, edid->manufactureWeek); + EXPECT_EQ(16, edid->physicalSizeInCm.width); + EXPECT_EQ(9, edid->physicalSizeInCm.height); ASSERT_TRUE(edid->cea861Block); ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock); auto physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress; @@ -245,8 +261,13 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("Panasonic-TV"), 3876373262); EXPECT_EQ("Panasonic-TV", edid->displayName); EXPECT_EQ(41622, edid->productId); + EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("16843009"), edid->hashedBlockZeroSerialNumberOpt.value()); + EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); EXPECT_EQ(29, edid->manufactureOrModelYear); EXPECT_EQ(0, edid->manufactureWeek); + EXPECT_EQ(128, edid->physicalSizeInCm.width); + EXPECT_EQ(72, edid->physicalSizeInCm.height); ASSERT_TRUE(edid->cea861Block); ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock); physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress; @@ -267,8 +288,12 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("Hisense"), 2859844809); EXPECT_EQ("Hisense", edid->displayName); EXPECT_EQ(0, edid->productId); + EXPECT_FALSE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); EXPECT_EQ(29, edid->manufactureOrModelYear); EXPECT_EQ(18, edid->manufactureWeek); + EXPECT_EQ(0, edid->physicalSizeInCm.width); + EXPECT_EQ(0, edid->physicalSizeInCm.height); ASSERT_TRUE(edid->cea861Block); ASSERT_TRUE(edid->cea861Block->hdmiVendorDataBlock); physicalAddress = edid->cea861Block->hdmiVendorDataBlock->physicalAddress; @@ -289,8 +314,13 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(hash("LP2361"), 1523181158); EXPECT_EQ("LP2361", edid->displayName); EXPECT_EQ(9373, edid->productId); + EXPECT_TRUE(edid->hashedBlockZeroSerialNumberOpt.has_value()); + EXPECT_EQ(ftl::stable_hash("11223344"), edid->hashedBlockZeroSerialNumberOpt.value()); + EXPECT_FALSE(edid->hashedDescriptorBlockSerialNumberOpt.has_value()); EXPECT_EQ(23, edid->manufactureOrModelYear); EXPECT_EQ(0xff, edid->manufactureWeek); + EXPECT_EQ(52, edid->physicalSizeInCm.width); + EXPECT_EQ(29, edid->physicalSizeInCm.height); ASSERT_TRUE(edid->cea861Block); EXPECT_FALSE(edid->cea861Block->hdmiVendorDataBlock); EXPECT_EQ(1360, edid->preferredDetailedTimingDescriptor->pixelSizeCount.width); @@ -447,4 +477,4 @@ TEST(DisplayIdentificationTest, getVirtualDisplayId) { } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues -#pragma clang diagnostic pop // ignored "-Wextra"
\ No newline at end of file +#pragma clang diagnostic pop // ignored "-Wextra" diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp index da1aae2acb..f8a38d1e38 100644 --- a/services/audiomanager/IAudioManager.cpp +++ b/services/audiomanager/IAudioManager.cpp @@ -87,12 +87,15 @@ public: } virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event, - audio_port_handle_t eventId) { + const std::vector<audio_port_handle_t>& eventIds) { Parcel data, reply; data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor()); data.writeInt32((int32_t) piid); data.writeInt32((int32_t) event); - data.writeInt32((int32_t) eventId); + data.writeInt32((int32_t) eventIds.size()); + for (auto eventId: eventIds) { + data.writeInt32((int32_t) eventId); + } return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY); } diff --git a/services/gpuservice/gpuwork/bpfprogs/gpuWork.c b/services/gpuservice/gpuwork/bpfprogs/gpuWork.c index f4701896b6..94abc69426 100644 --- a/services/gpuservice/gpuwork/bpfprogs/gpuWork.c +++ b/services/gpuservice/gpuwork/bpfprogs/gpuWork.c @@ -20,11 +20,7 @@ #include <stddef.h> #include <stdint.h> -#ifdef MOCK_BPF -#include <test/mock_bpf_helpers.h> -#else #include <bpf_helpers.h> -#endif #define S_IN_NS (1000000000) #define SMALL_TIME_GAP_LIMIT_NS (S_IN_NS) diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 305feab6c7..580cde39b1 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -362,6 +362,9 @@ public: /* Toggle Caps Lock */ virtual void toggleCapsLockState(int32_t deviceId) = 0; + /* Resets locked modifier state */ + virtual void resetLockedModifierState() = 0; + /* Determine whether physical keys exist for the given framework-domain key codes. */ virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) = 0; diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index a2b7e82154..24919b678d 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -19,6 +19,7 @@ #include "InputReader.h" #include <android-base/stringprintf.h> +#include <com_android_input_flags.h> #include <errno.h> #include <input/Keyboard.h> #include <input/VirtualKeyMap.h> @@ -589,6 +590,11 @@ void InputReader::toggleCapsLockState(int32_t deviceId) { } } +void InputReader::resetLockedModifierState() { + std::scoped_lock _l(mLock); + updateLedMetaStateLocked(0); +} + bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) { std::scoped_lock _l(mLock); @@ -903,7 +909,9 @@ void InputReader::notifyMouseCursorFadedOnTyping() { bool InputReader::setKernelWakeEnabled(int32_t deviceId, bool enabled) { std::scoped_lock _l(mLock); - + if (!com::android::input::flags::set_input_device_kernel_wake()){ + return false; + } InputDevice* device = findInputDeviceLocked(deviceId); if (device) { return device->setKernelWakeEnabled(enabled); diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index abe7a5fa6e..4744dd0e4e 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -48,7 +48,7 @@ public: inline InputReaderContext* getContext() { return mContext; } inline int32_t getId() const { return mId; } inline int32_t getControllerNumber() const { return mControllerNumber; } - inline int32_t getGeneration() const { return mGeneration; } + inline virtual int32_t getGeneration() const { return mGeneration; } inline const std::string getName() const { return mIdentifier.name; } inline const std::string getDescriptor() { return mIdentifier.descriptor; } inline std::optional<std::string> getBluetoothAddress() const { @@ -59,7 +59,7 @@ public: inline virtual uint32_t getSources() const { return mSources; } inline bool hasEventHubDevices() const { return !mDevices.empty(); } - inline bool isExternal() { return mIsExternal; } + inline virtual bool isExternal() { return mIsExternal; } inline std::optional<uint8_t> getAssociatedDisplayPort() const { return mAssociatedDisplayPort; } @@ -79,7 +79,7 @@ public: inline bool isIgnored() { return !getMapperCount() && !mController; } - inline KeyboardType getKeyboardType() const { return mKeyboardType; } + inline virtual KeyboardType getKeyboardType() const { return mKeyboardType; } bool isEnabled(); @@ -124,7 +124,7 @@ public: int32_t getMetaState(); void setKeyboardType(KeyboardType keyboardType); - void bumpGeneration(); + virtual void bumpGeneration(); [[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when); diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 7614a05470..1403ca2986 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -69,6 +69,8 @@ public: void toggleCapsLockState(int32_t deviceId) override; + void resetLockedModifierState() override; + bool hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) override; diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index ca797dce95..8235c90da6 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -100,9 +100,14 @@ std::list<NotifyArgs> InputMapperUnitTest::process(int32_t type, int32_t code, i std::list<NotifyArgs> InputMapperUnitTest::process(nsecs_t when, int32_t type, int32_t code, int32_t value) { + return process(when, when, type, code, value); +} + +std::list<NotifyArgs> InputMapperUnitTest::process(nsecs_t when, nsecs_t readTime, int32_t type, + int32_t code, int32_t value) { RawEvent event; event.when = when; - event.readTime = when; + event.readTime = readTime; event.deviceId = mMapper->getDeviceContext().getEventHubId(); event.type = type; event.code = code; diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h index b6c5812ec5..10ef6f1660 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -56,6 +56,8 @@ protected: std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value); std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value); + std::list<NotifyArgs> process(nsecs_t when, nsecs_t readTime, int32_t type, int32_t code, + int32_t value); InputDeviceIdentifier mIdentifier; MockEventHubInterface mMockEventHub; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index ee3b2a24d7..9d2256f52f 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -3031,1048 +3031,6 @@ TEST_F(InputDeviceTest, KernelBufferOverflowResetsMappers) { mapper.assertProcessWasCalled(); } -// --- KeyboardInputMapperTest --- - -class KeyboardInputMapperTest : public InputMapperTest { -protected: - void SetUp() override { - InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD | - InputDeviceClass::ALPHAKEY); - } - const std::string UNIQUE_ID = "local:0"; - const KeyboardLayoutInfo DEVICE_KEYBOARD_LAYOUT_INFO = KeyboardLayoutInfo("en-US", "qwerty"); - void prepareDisplay(ui::Rotation orientation); - - void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode, - int32_t originalKeyCode, int32_t rotatedKeyCode, - ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID); -}; - -/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the - * orientation. - */ -void KeyboardInputMapperTest::prepareDisplay(ui::Rotation orientation) { - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, UNIQUE_ID, - NO_PORT, ViewportType::INTERNAL); -} - -void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper, - int32_t originalScanCode, int32_t originalKeyCode, - int32_t rotatedKeyCode, - ui::LogicalDisplayId displayId) { - NotifyKeyArgs args; - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(originalScanCode, args.scanCode); - ASSERT_EQ(rotatedKeyCode, args.keyCode); - ASSERT_EQ(displayId, args.displayId); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(originalScanCode, args.scanCode); - ASSERT_EQ(rotatedKeyCode, args.keyCode); - ASSERT_EQ(displayId, args.displayId); -} - -TEST_F(KeyboardInputMapperTest, GetSources) { - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper.getSources()); -} - -TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { - const int32_t USAGE_A = 0x070004; - const int32_t USAGE_UNKNOWN = 0x07ffff; - mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, POLICY_FLAG_WAKE); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - // Initial metastate is AMETA_NONE. - ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); - - // Key down by scan code. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1); - NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Key up by scan code. - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Key down by usage code. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, 0, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(AKEYCODE_A, args.keyCode); - ASSERT_EQ(0, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Key up by usage code. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_A); - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEYCODE_A, args.keyCode); - ASSERT_EQ(0, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Key down with unknown scan code or usage code. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UNKNOWN, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(0, args.keyCode); - ASSERT_EQ(KEY_UNKNOWN, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(0U, args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Key up with unknown scan code or usage code. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN); - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_UNKNOWN, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(0, args.keyCode); - ASSERT_EQ(KEY_UNKNOWN, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(0U, args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); -} - -TEST_F(KeyboardInputMapperTest, Process_KeyRemapping) { - mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_B, 0, AKEYCODE_B, 0); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - mFakeEventHub->setKeyRemapping(EVENTHUB_ID, {{AKEYCODE_A, AKEYCODE_B}}); - // Key down by scan code. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1); - NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEYCODE_B, args.keyCode); - - // Key up by scan code. - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEYCODE_B, args.keyCode); -} - -/** - * Ensure that the readTime is set to the time when the EV_KEY is received. - */ -TEST_F(KeyboardInputMapperTest, Process_SendsReadTime) { - mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - NotifyKeyArgs args; - - // Key down - process(mapper, ARBITRARY_TIME, /*readTime=*/12, EV_KEY, KEY_HOME, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(12, args.readTime); - - // Key up - process(mapper, ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(15, args.readTime); -} - -TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { - mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFTSHIFT, 0, AKEYCODE_SHIFT_LEFT, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); - mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_NUMLOCK, AKEYCODE_NUM_LOCK, 0); - mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK, 0); - mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - // Initial metastate is AMETA_NONE. - ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); - - // Metakey down. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFTSHIFT, 1); - NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState()); - ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled()); - - // Key down. - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState()); - - // Key up. - process(mapper, ARBITRARY_TIME + 2, READ_TIME, EV_KEY, KEY_A, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper.getMetaState()); - - // Metakey up. - process(mapper, ARBITRARY_TIME + 3, READ_TIME, EV_KEY, KEY_LEFTSHIFT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); - ASSERT_NO_FATAL_FAILURE(mReader->getContext()->assertUpdateGlobalMetaStateWasCalled()); -} - -TEST_F(KeyboardInputMapperTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) { - mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - prepareDisplay(ui::ROTATION_90); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); -} - -TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { - mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); - - addConfigurationProperty("keyboard.orientationAware", "1"); - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - prepareDisplay(ui::ROTATION_0); - ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, - AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, - AKEYCODE_DPAD_DOWN, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, - AKEYCODE_DPAD_LEFT, DISPLAY_ID)); - - clearViewports(); - prepareDisplay(ui::ROTATION_90); - ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, - AKEYCODE_DPAD_UP, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, - AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, - AKEYCODE_DPAD_DOWN, DISPLAY_ID)); - - clearViewports(); - prepareDisplay(ui::ROTATION_180); - ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, - AKEYCODE_DPAD_LEFT, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, - AKEYCODE_DPAD_UP, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, - AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); - - clearViewports(); - prepareDisplay(ui::ROTATION_270); - ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, - AKEYCODE_DPAD_DOWN, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, - AKEYCODE_DPAD_LEFT, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, - AKEYCODE_DPAD_UP, DISPLAY_ID)); - - // Special case: if orientation changes while key is down, we still emit the same keycode - // in the key up as we did in the key down. - NotifyKeyArgs args; - clearViewports(); - prepareDisplay(ui::ROTATION_270); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(KEY_UP, args.scanCode); - ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); - - clearViewports(); - prepareDisplay(ui::ROTATION_180); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(KEY_UP, args.scanCode); - ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); -} - -TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_NotOrientationAware) { - // If the keyboard is not orientation aware, - // key events should not be associated with a specific display id - mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - NotifyKeyArgs args; - - // Display id should be LogicalDisplayId::INVALID without any display configuration. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(ui::LogicalDisplayId::INVALID, args.displayId); - - prepareDisplay(ui::ROTATION_0); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(ui::LogicalDisplayId::INVALID, args.displayId); -} - -TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) { - // If the keyboard is orientation aware, - // key events should be associated with the internal viewport - mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - - addConfigurationProperty("keyboard.orientationAware", "1"); - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - NotifyKeyArgs args; - - // Display id should be LogicalDisplayId::INVALID without any display configuration. - // ^--- already checked by the previous test - - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DISPLAY_ID, args.displayId); - - constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2}; - clearViewports(); - setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(newDisplayId, args.displayId); -} - -TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 1); - ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); - - mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 0); - ASSERT_EQ(0, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); -} - -TEST_F(KeyboardInputMapperTest, GetKeyCodeForKeyLocation) { - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - mFakeEventHub->addKeyCodeMapping(EVENTHUB_ID, AKEYCODE_Y, AKEYCODE_Z); - ASSERT_EQ(AKEYCODE_Z, mapper.getKeyCodeForKeyLocation(AKEYCODE_Y)) - << "If a mapping is available, the result is equal to the mapping"; - - ASSERT_EQ(AKEYCODE_A, mapper.getKeyCodeForKeyLocation(AKEYCODE_A)) - << "If no mapping is available, the result is the key location"; -} - -TEST_F(KeyboardInputMapperTest, GetScanCodeState) { - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 1); - ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); - - mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 0); - ASSERT_EQ(0, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); -} - -TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) { - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); - - uint8_t flags[2] = { 0, 0 }; - ASSERT_TRUE(mapper.markSupportedKeyCodes(AINPUT_SOURCE_ANY, {AKEYCODE_A, AKEYCODE_B}, flags)); - ASSERT_TRUE(flags[0]); - ASSERT_FALSE(flags[1]); -} - -TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) { - mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/); - mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/); - mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - // Initial metastate is AMETA_NONE. - ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); - - // Initialization should have turned all of the lights off. - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); - - // Toggle caps lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); - - // Toggle num lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState()); - - // Toggle caps lock off. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper.getMetaState()); - - // Toggle scroll lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); - - // Toggle num lock off. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); - - // Toggle scroll lock off. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); -} - -TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { - // keyboard 1. - mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); - - // keyboard 2. - const std::string USB2 = "USB2"; - const std::string DEVICE_NAME2 = "KEYBOARD2"; - constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; - constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; - std::shared_ptr<InputDevice> device2 = - newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, - ftl::Flags<InputDeviceClass>(0)); - - mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); - mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); - mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID); - KeyboardInputMapper& mapper2 = - device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, - mFakePolicy - ->getReaderConfiguration(), - AINPUT_SOURCE_KEYBOARD); - std::list<NotifyArgs> unused = - device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - /*changes=*/{}); - unused += device2->reset(ARBITRARY_TIME); - - // Prepared displays and associated info. - constexpr uint8_t hdmi1 = 0; - constexpr uint8_t hdmi2 = 1; - const std::string SECONDARY_UNIQUE_ID = "local:1"; - - mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1); - mFakePolicy->addInputPortAssociation(USB2, hdmi2); - - // No associated display viewport found, should disable the device. - unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::Change::DISPLAY_INFO); - ASSERT_FALSE(device2->isEnabled()); - - // Prepare second display. - constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2}; - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - UNIQUE_ID, hdmi1, ViewportType::INTERNAL); - setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL); - // Default device will reconfigure above, need additional reconfiguration for another device. - unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::Change::DISPLAY_INFO); - - // Device should be enabled after the associated display is found. - ASSERT_TRUE(mDevice->isEnabled()); - ASSERT_TRUE(device2->isEnabled()); - - // Test pad key events - ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, - AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, - AKEYCODE_DPAD_DOWN, DISPLAY_ID)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, - AKEYCODE_DPAD_LEFT, DISPLAY_ID)); - - ASSERT_NO_FATAL_FAILURE( - testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, - AKEYCODE_DPAD_RIGHT, newDisplayId)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN, - AKEYCODE_DPAD_DOWN, newDisplayId)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT, - AKEYCODE_DPAD_LEFT, newDisplayId)); -} - -TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) { - mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/); - mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/); - mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - // Initial metastate is AMETA_NONE. - ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); - - // Initialization should have turned all of the lights off. - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); - - // Toggle caps lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); - ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); - - // Toggle num lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); - ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState()); - - // Toggle scroll lock on. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); - - mFakeEventHub->removeDevice(EVENTHUB_ID); - mReader->loopOnce(); - - // keyboard 2 should default toggle keys. - const std::string USB2 = "USB2"; - const std::string DEVICE_NAME2 = "KEYBOARD2"; - constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; - constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; - std::shared_ptr<InputDevice> device2 = - newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, - ftl::Flags<InputDeviceClass>(0)); - mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/); - mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/); - mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); - mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); - mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); - mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); - - device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID); - KeyboardInputMapper& mapper2 = - device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, - mFakePolicy - ->getReaderConfiguration(), - AINPUT_SOURCE_KEYBOARD); - std::list<NotifyArgs> unused = - device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - /*changes=*/{}); - unused += device2->reset(ARBITRARY_TIME); - - ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL)); - ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML)); - ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, - mapper2.getMetaState()); -} - -TEST_F(KeyboardInputMapperTest, Process_toggleCapsLockState) { - mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); - - // Suppose we have two mappers. (DPAD + KEYBOARD) - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD); - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - // Initial metastate is AMETA_NONE. - ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); - - mReader->toggleCapsLockState(DEVICE_ID); - ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); -} - -TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) { - // keyboard 1. - mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/); - mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/); - mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); - - KeyboardInputMapper& mapper1 = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - // keyboard 2. - const std::string USB2 = "USB2"; - const std::string DEVICE_NAME2 = "KEYBOARD2"; - constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; - constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; - std::shared_ptr<InputDevice> device2 = - newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, - ftl::Flags<InputDeviceClass>(0)); - mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/); - mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/); - mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); - mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); - mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); - mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); - - device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID); - KeyboardInputMapper& mapper2 = - device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, - mFakePolicy - ->getReaderConfiguration(), - AINPUT_SOURCE_KEYBOARD); - std::list<NotifyArgs> unused = - device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - /*changes=*/{}); - unused += device2->reset(ARBITRARY_TIME); - - // Initial metastate is AMETA_NONE. - ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); - ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); - - // Toggle num lock on and off. - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); - ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper1.getMetaState()); - ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper2.getMetaState()); - - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); - ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); - ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); - - // Toggle caps lock on and off. - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); - ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper1.getMetaState()); - ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper2.getMetaState()); - - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); - ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); - ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); - - // Toggle scroll lock on and off. - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); - ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper1.getMetaState()); - ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper2.getMetaState()); - - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); - process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); - ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); - ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); - ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); -} - -TEST_F(KeyboardInputMapperTest, Process_DisabledDevice) { - const int32_t USAGE_A = 0x070004; - mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - // Key down by scan code. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1); - NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - - // Disable device, it should synthesize cancellation events for down events. - mFakePolicy->addDisabledDevice(DEVICE_ID); - configureDevice(InputReaderConfiguration::Change::ENABLED_STATE); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, args.flags); -} - -TEST_F(KeyboardInputMapperTest, Configure_AssignKeyboardLayoutInfo) { - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - std::list<NotifyArgs> unused = - mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - /*changes=*/{}); - - uint32_t generation = mReader->getContext()->getGeneration(); - mFakePolicy->addKeyboardLayoutAssociation(DEVICE_LOCATION, DEVICE_KEYBOARD_LAYOUT_INFO); - - unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION); - - InputDeviceInfo deviceInfo = mDevice->getDeviceInfo(); - ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.languageTag, - deviceInfo.getKeyboardLayoutInfo()->languageTag); - ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.layoutType, - deviceInfo.getKeyboardLayoutInfo()->layoutType); - ASSERT_TRUE(mReader->getContext()->getGeneration() != generation); - - // Call change layout association with the same values: Generation shouldn't change - generation = mReader->getContext()->getGeneration(); - mFakePolicy->addKeyboardLayoutAssociation(DEVICE_LOCATION, DEVICE_KEYBOARD_LAYOUT_INFO); - unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), - InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION); - ASSERT_TRUE(mReader->getContext()->getGeneration() == generation); -} - -TEST_F(KeyboardInputMapperTest, LayoutInfoCorrectlyMapped) { - mFakeEventHub->setRawLayoutInfo(EVENTHUB_ID, - RawLayoutInfo{.languageTag = "en", .layoutType = "extended"}); - - // Configuration - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - InputReaderConfiguration config; - std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{}); - - ASSERT_EQ("en", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->languageTag); - ASSERT_EQ("extended", mDevice->getDeviceInfo().getKeyboardLayoutInfo()->layoutType); -} - -TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) { - mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE); - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - NotifyKeyArgs args; - - // Key down - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_LEFT, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags); -} - -TEST_F_WITH_FLAGS(KeyboardInputMapperTest, WakeBehavior_AlphabeticKeyboard, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, - enable_alphabetic_keyboard_wake))) { - // For internal alphabetic devices, keys will trigger wake on key down. - - mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, 0); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1); - NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_A, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(uint32_t(0), args.policyFlags); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(uint32_t(0), args.policyFlags); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(uint32_t(0), args.policyFlags); -} - -/** - * When there is more than one KeyboardInputMapper for an InputDevice, each mapper should produce - * events that use the shared keyboard source across all mappers. This is to ensure that each - * input device generates key events in a consistent manner, regardless of which mapper produces - * the event. - */ -TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) { - mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); - - // Add a mapper with SOURCE_KEYBOARD - KeyboardInputMapper& keyboardMapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); - ASSERT_NO_FATAL_FAILURE( - mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD))); - process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); - ASSERT_NO_FATAL_FAILURE( - mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD))); - - // Add a mapper with SOURCE_DPAD - KeyboardInputMapper& dpadMapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD); - for (auto* mapper : {&keyboardMapper, &dpadMapper}) { - process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( - WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD))); - process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( - WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD))); - } - - // Add a mapper with SOURCE_GAMEPAD - KeyboardInputMapper& gamepadMapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD); - for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) { - process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( - WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD))); - process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( - WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD))); - } -} - -// --- KeyboardInputMapperTest_ExternalAlphabeticDevice --- - -class KeyboardInputMapperTest_ExternalAlphabeticDevice : public InputMapperTest { -protected: - void SetUp() override { - InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD | - InputDeviceClass::ALPHAKEY | InputDeviceClass::EXTERNAL); - } -}; - -// --- KeyboardInputMapperTest_ExternalNonAlphabeticDevice --- - -class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public InputMapperTest { -protected: - void SetUp() override { - InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD | - InputDeviceClass::EXTERNAL); - } -}; - -TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, WakeBehavior_AlphabeticKeyboard) { - // For external devices, keys will trigger wake on key down. Media keys should also trigger - // wake if triggered from external devices. - - mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, - POLICY_FLAG_WAKE); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1); - NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(uint32_t(0), args.policyFlags); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(uint32_t(0), args.policyFlags); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); -} - -TEST_F(KeyboardInputMapperTest_ExternalNonAlphabeticDevice, WakeBehavior_NonAlphabeticKeyboard) { - // For external devices, keys will trigger wake on key down. Media keys should not trigger - // wake if triggered from external non-alphaebtic keyboard (e.g. headsets). - - mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, - POLICY_FLAG_WAKE); - - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1); - NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(uint32_t(0), args.policyFlags); - - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(uint32_t(0), args.policyFlags); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); -} - -TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, DoNotWakeByDefaultBehavior) { - // Tv Remote key's wake behavior is prescribed by the keylayout file. - - mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); - mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE); - - addConfigurationProperty("keyboard.doNotWakeByDefault", "1"); - KeyboardInputMapper& mapper = - constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1); - NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_HOME, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_DOWN, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(uint32_t(0), args.policyFlags); - - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_DOWN, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(uint32_t(0), args.policyFlags); - - process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - - process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); -} - // --- TouchInputMapperTest --- class TouchInputMapperTest : public InputMapperTest { diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index 25e2b4557e..6f7c2e5271 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -201,7 +201,9 @@ public: MOCK_METHOD(uint32_t, getSources, (), (const, override)); MOCK_METHOD(std::optional<DisplayViewport>, getAssociatedViewport, (), (const)); + MOCK_METHOD(KeyboardType, getKeyboardType, (), (const, override)); MOCK_METHOD(bool, isEnabled, (), ()); + MOCK_METHOD(bool, isExternal, (), (override)); MOCK_METHOD(void, dump, (std::string& dump, const std::string& eventHubDevStr), ()); MOCK_METHOD(void, addEmptyEventHubDevice, (int32_t eventHubId), ()); @@ -249,8 +251,6 @@ public: MOCK_METHOD(int32_t, getMetaState, (), ()); MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ()); - MOCK_METHOD(void, bumpGeneration, (), ()); - MOCK_METHOD(const PropertyMap&, getConfiguration, (), (const, override)); MOCK_METHOD(NotifyDeviceResetArgs, notifyReset, (nsecs_t when), ()); @@ -260,5 +260,11 @@ public: MOCK_METHOD(void, updateLedState, (bool reset), ()); MOCK_METHOD(size_t, getMapperCount, (), ()); + + virtual int32_t getGeneration() const override { return mGeneration; } + virtual void bumpGeneration() override { mGeneration++; } + +private: + int32_t mGeneration = 0; }; } // namespace android diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp index bcc6062bf8..1dd32c447b 100644 --- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -16,29 +16,82 @@ #include "KeyboardInputMapper.h" +#include <cstdint> +#include <list> +#include <memory> +#include <optional> +#include <string> + +#include <android/input.h> +#include <android/keycodes.h> +#include <com_android_input_flags.h> +#include <flag_macros.h> +#include <ftl/flags.h> #include <gtest/gtest.h> +#include <input/DisplayViewport.h> +#include <input/Input.h> +#include <input/InputDevice.h> +#include <ui/LogicalDisplayId.h> +#include <ui/Rotation.h> +#include <utils/Errors.h> +#include "EventHub.h" #include "InputMapperTest.h" #include "InterfaceMocks.h" +#include "NotifyArgs.h" +#include "TestConstants.h" #include "TestEventMatchers.h" #define TAG "KeyboardInputMapper_test" namespace android { +using namespace ftl::flag_operators; using testing::_; using testing::AllOf; +using testing::AnyOf; using testing::Args; using testing::DoAll; +using testing::IsEmpty; using testing::Return; +using testing::ReturnArg; +using testing::SaveArg; using testing::SetArgPointee; using testing::VariantWith; +namespace { + +// Arbitrary display properties. +constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT; +constexpr int32_t DISPLAY_WIDTH = 480; +constexpr int32_t DISPLAY_HEIGHT = 800; + +DisplayViewport createPrimaryViewport(ui::Rotation orientation) { + const bool isRotated = + orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270; + DisplayViewport v; + v.displayId = DISPLAY_ID; + v.orientation = orientation; + v.logicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + v.logicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + v.physicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + v.physicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + v.deviceWidth = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + v.deviceHeight = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + v.isActive = true; + v.uniqueId = "local:1"; + return v; +} + +} // namespace + /** * Unit tests for KeyboardInputMapper. */ class KeyboardInputMapperUnitTest : public InputMapperUnitTest { protected: + const KeyboardLayoutInfo DEVICE_KEYBOARD_LAYOUT_INFO = KeyboardLayoutInfo("en-US", "qwerty"); + sp<FakeInputReaderPolicy> mFakePolicy; const std::unordered_map<int32_t, int32_t> mKeyCodeMap{{KEY_0, AKEYCODE_0}, {KEY_A, AKEYCODE_A}, @@ -60,9 +113,8 @@ protected: InputMapperUnitTest::SetUp(); // set key-codes expected in tests - for (const auto& [scanCode, outKeycode] : mKeyCodeMap) { - EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, scanCode, _, _, _, _, _)) - .WillRepeatedly(DoAll(SetArgPointee<4>(outKeycode), Return(NO_ERROR))); + for (const auto& [evdevCode, outKeycode] : mKeyCodeMap) { + addKeyByEvdevCode(evdevCode, outKeycode); } mFakePolicy = sp<FakeInputReaderPolicy>::make(); @@ -73,8 +125,79 @@ protected: mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, AINPUT_SOURCE_KEYBOARD); } + + void addKeyByEvdevCode(int32_t evdevCode, int32_t keyCode, int32_t flags = 0) { + EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, evdevCode, _, _, _, _, _)) + .WillRepeatedly([=](int32_t, int32_t, int32_t, int32_t metaState, + int32_t* outKeycode, int32_t* outMetaState, + uint32_t* outFlags) { + if (outKeycode != nullptr) { + *outKeycode = keyCode; + } + if (outMetaState != nullptr) { + *outMetaState = metaState; + } + if (outFlags != nullptr) { + *outFlags = flags; + } + return NO_ERROR; + }); + } + + void addKeyByUsageCode(int32_t usageCode, int32_t keyCode, int32_t flags = 0) { + EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, _, usageCode, _, _, _, _)) + .WillRepeatedly([=](int32_t, int32_t, int32_t, int32_t metaState, + int32_t* outKeycode, int32_t* outMetaState, + uint32_t* outFlags) { + if (outKeycode != nullptr) { + *outKeycode = keyCode; + } + if (outMetaState != nullptr) { + *outMetaState = metaState; + } + if (outFlags != nullptr) { + *outFlags = flags; + } + return NO_ERROR; + }); + } + + void setDisplayOrientation(ui::Rotation orientation) { + EXPECT_CALL((*mDevice), getAssociatedViewport) + .WillRepeatedly(Return(createPrimaryViewport(orientation))); + std::list<NotifyArgs> args = + mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_EQ(0u, args.size()); + } + + NotifyKeyArgs expectSingleKeyArg(const std::list<NotifyArgs>& args) { + EXPECT_EQ(1u, args.size()); + return std::get<NotifyKeyArgs>(args.front()); + } + + void testDPadKeyRotation(int32_t originalEvdevCode, int32_t originalKeyCode, + int32_t rotatedKeyCode, ui::LogicalDisplayId displayId = DISPLAY_ID) { + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 1); + NotifyKeyArgs args = expectSingleKeyArg(argsList); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); + ASSERT_EQ(originalEvdevCode, args.scanCode); + ASSERT_EQ(rotatedKeyCode, args.keyCode); + ASSERT_EQ(displayId, args.displayId); + + argsList = process(ARBITRARY_TIME, EV_KEY, originalEvdevCode, 0); + args = expectSingleKeyArg(argsList); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); + ASSERT_EQ(originalEvdevCode, args.scanCode); + ASSERT_EQ(rotatedKeyCode, args.keyCode); + ASSERT_EQ(displayId, args.displayId); + } }; +TEST_F(KeyboardInputMapperUnitTest, GetSources) { + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mMapper->getSources()); +} + TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) { nsecs_t when = ARBITRARY_TIME; std::vector<int32_t> keyCodes{KEY_0, KEY_A, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTSHIFT}; @@ -109,4 +232,962 @@ TEST_F(KeyboardInputMapperUnitTest, RepeatEventsDiscarded) { WithScanCode(KEY_0))))); } +TEST_F(KeyboardInputMapperUnitTest, Process_SimpleKeyPress) { + const int32_t USAGE_A = 0x070004; + addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE); + addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); + + // Initial metastate is AMETA_NONE. + ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); + + // Key down by evdev code. + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + NotifyKeyArgs args = expectSingleKeyArg(argsList); + ASSERT_EQ(DEVICE_ID, args.deviceId); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); + ASSERT_EQ(ARBITRARY_TIME, args.eventTime); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); + ASSERT_EQ(AKEYCODE_HOME, args.keyCode); + ASSERT_EQ(KEY_HOME, args.scanCode); + ASSERT_EQ(AMETA_NONE, args.metaState); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + ASSERT_EQ(ARBITRARY_TIME, args.downTime); + + // Key up by evdev code. + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); + args = expectSingleKeyArg(argsList); + ASSERT_EQ(DEVICE_ID, args.deviceId); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); + ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); + ASSERT_EQ(AKEYCODE_HOME, args.keyCode); + ASSERT_EQ(KEY_HOME, args.scanCode); + ASSERT_EQ(AMETA_NONE, args.metaState); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + ASSERT_EQ(ARBITRARY_TIME, args.downTime); + + // Key down by usage code. + argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A); + argsList += process(ARBITRARY_TIME, EV_KEY, 0, 1); + args = expectSingleKeyArg(argsList); + ASSERT_EQ(DEVICE_ID, args.deviceId); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); + ASSERT_EQ(ARBITRARY_TIME, args.eventTime); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); + ASSERT_EQ(AKEYCODE_A, args.keyCode); + ASSERT_EQ(0, args.scanCode); + ASSERT_EQ(AMETA_NONE, args.metaState); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + ASSERT_EQ(ARBITRARY_TIME, args.downTime); + + // Key up by usage code. + argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_A); + argsList += process(ARBITRARY_TIME + 1, EV_KEY, 0, 0); + args = expectSingleKeyArg(argsList); + ASSERT_EQ(DEVICE_ID, args.deviceId); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); + ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); + ASSERT_EQ(AKEYCODE_A, args.keyCode); + ASSERT_EQ(0, args.scanCode); + ASSERT_EQ(AMETA_NONE, args.metaState); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); + ASSERT_EQ(ARBITRARY_TIME, args.downTime); +} + +TEST_F(KeyboardInputMapperUnitTest, Process_UnknownKey) { + const int32_t USAGE_UNKNOWN = 0x07ffff; + EXPECT_CALL(mMockEventHub, mapKey(EVENTHUB_ID, KEY_UNKNOWN, USAGE_UNKNOWN, _, _, _, _)) + .WillRepeatedly(Return(NAME_NOT_FOUND)); + + // Key down with unknown scan code or usage code. + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN); + argsList += process(ARBITRARY_TIME, EV_KEY, KEY_UNKNOWN, 1); + NotifyKeyArgs args = expectSingleKeyArg(argsList); + ASSERT_EQ(DEVICE_ID, args.deviceId); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); + ASSERT_EQ(ARBITRARY_TIME, args.eventTime); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); + ASSERT_EQ(0, args.keyCode); + ASSERT_EQ(KEY_UNKNOWN, args.scanCode); + ASSERT_EQ(AMETA_NONE, args.metaState); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); + ASSERT_EQ(0U, args.policyFlags); + ASSERT_EQ(ARBITRARY_TIME, args.downTime); + + // Key up with unknown scan code or usage code. + argsList = process(ARBITRARY_TIME, EV_MSC, MSC_SCAN, USAGE_UNKNOWN); + argsList += process(ARBITRARY_TIME + 1, EV_KEY, KEY_UNKNOWN, 0); + args = expectSingleKeyArg(argsList); + ASSERT_EQ(DEVICE_ID, args.deviceId); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); + ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); + ASSERT_EQ(0, args.keyCode); + ASSERT_EQ(KEY_UNKNOWN, args.scanCode); + ASSERT_EQ(AMETA_NONE, args.metaState); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); + ASSERT_EQ(0U, args.policyFlags); + ASSERT_EQ(ARBITRARY_TIME, args.downTime); +} + +/** + * Ensure that the readTime is set to the time when the EV_KEY is received. + */ +TEST_F(KeyboardInputMapperUnitTest, Process_SendsReadTime) { + addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME); + + // Key down + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, /*readTime=*/12, EV_KEY, KEY_HOME, 1); + ASSERT_EQ(12, expectSingleKeyArg(argsList).readTime); + + // Key up + argsList = process(ARBITRARY_TIME, /*readTime=*/15, EV_KEY, KEY_HOME, 1); + ASSERT_EQ(15, expectSingleKeyArg(argsList).readTime); +} + +TEST_F(KeyboardInputMapperUnitTest, Process_ShouldUpdateMetaState) { + addKeyByEvdevCode(KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT); + addKeyByEvdevCode(KEY_A, AKEYCODE_A); + + EXPECT_CALL(mMockInputReaderContext, updateGlobalMetaState()).Times(2); + + // Initial metastate is AMETA_NONE. + ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); + + // Metakey down. + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFTSHIFT, 1); + ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState); + ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState()); + + // Key down. + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 1); + ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState); + ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState()); + + // Key up. + argsList = process(ARBITRARY_TIME + 2, EV_KEY, KEY_A, 0); + ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, expectSingleKeyArg(argsList).metaState); + ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mMapper->getMetaState()); + + // Metakey up. + argsList = process(ARBITRARY_TIME + 3, EV_KEY, KEY_LEFTSHIFT, 0); + ASSERT_EQ(AMETA_NONE, expectSingleKeyArg(argsList).metaState); + ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); +} + +TEST_F(KeyboardInputMapperUnitTest, Process_WhenNotOrientationAware_ShouldNotRotateDPad) { + addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP); + addKeyByEvdevCode(KEY_RIGHT, AKEYCODE_DPAD_RIGHT); + addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN); + addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT); + + setDisplayOrientation(ui::Rotation::Rotation90); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); +} + +TEST_F(KeyboardInputMapperUnitTest, Process_WhenOrientationAware_ShouldRotateDPad) { + addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP); + addKeyByEvdevCode(KEY_RIGHT, AKEYCODE_DPAD_RIGHT); + addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN); + addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT); + + mPropertyMap.addProperty("keyboard.orientationAware", "1"); + mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, + AINPUT_SOURCE_KEYBOARD); + setDisplayOrientation(ui::ROTATION_0); + + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); + + setDisplayOrientation(ui::ROTATION_90); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN)); + + setDisplayOrientation(ui::ROTATION_180); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT)); + + setDisplayOrientation(ui::ROTATION_270); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP)); + + // Special case: if orientation changes while key is down, we still emit the same keycode + // in the key up as we did in the key down. + setDisplayOrientation(ui::ROTATION_270); + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); + NotifyKeyArgs args = expectSingleKeyArg(argsList); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); + ASSERT_EQ(KEY_UP, args.scanCode); + ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); + + setDisplayOrientation(ui::ROTATION_180); + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); + args = expectSingleKeyArg(argsList); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); + ASSERT_EQ(KEY_UP, args.scanCode); + ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); +} + +TEST_F(KeyboardInputMapperUnitTest, DisplayIdConfigurationChange_NotOrientationAware) { + // If the keyboard is not orientation aware, + // key events should not be associated with a specific display id + addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP); + + // Display id should be LogicalDisplayId::INVALID without any display configuration. + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); + ASSERT_GT(argsList.size(), 0u); + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); + ASSERT_GT(argsList.size(), 0u); + ASSERT_EQ(ui::LogicalDisplayId::INVALID, std::get<NotifyKeyArgs>(argsList.front()).displayId); +} + +TEST_F(KeyboardInputMapperUnitTest, DisplayIdConfigurationChange_OrientationAware) { + // If the keyboard is orientation aware, + // key events should be associated with the internal viewport + addKeyByEvdevCode(KEY_UP, AKEYCODE_DPAD_UP); + + mPropertyMap.addProperty("keyboard.orientationAware", "1"); + mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, + AINPUT_SOURCE_KEYBOARD); + + // Display id should be LogicalDisplayId::INVALID without any display configuration. + // ^--- already checked by the previous test + + setDisplayOrientation(ui::ROTATION_0); + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); + ASSERT_GT(argsList.size(), 0u); + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); + ASSERT_GT(argsList.size(), 0u); + ASSERT_EQ(DISPLAY_ID, std::get<NotifyKeyArgs>(argsList.front()).displayId); + + constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2}; + DisplayViewport newViewport = createPrimaryViewport(ui::ROTATION_0); + newViewport.displayId = newDisplayId; + EXPECT_CALL((*mDevice), getAssociatedViewport).WillRepeatedly(Return(newViewport)); + argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_EQ(0u, argsList.size()); + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 1); + ASSERT_GT(argsList.size(), 0u); + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_UP, 0); + ASSERT_GT(argsList.size(), 0u); + ASSERT_EQ(newDisplayId, std::get<NotifyKeyArgs>(argsList.front()).displayId); +} + +TEST_F(KeyboardInputMapperUnitTest, GetKeyCodeState) { + EXPECT_CALL(mMockEventHub, getKeyCodeState(EVENTHUB_ID, AKEYCODE_A)) + .WillRepeatedly(Return(AKEY_STATE_DOWN)); + ASSERT_EQ(AKEY_STATE_DOWN, mMapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); + + EXPECT_CALL(mMockEventHub, getKeyCodeState(EVENTHUB_ID, AKEYCODE_A)) + .WillRepeatedly(Return(AKEY_STATE_UP)); + ASSERT_EQ(AKEY_STATE_UP, mMapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); +} + +TEST_F(KeyboardInputMapperUnitTest, GetKeyCodeForKeyLocation) { + EXPECT_CALL(mMockEventHub, getKeyCodeForKeyLocation(EVENTHUB_ID, _)) + .WillRepeatedly(ReturnArg<1>()); + EXPECT_CALL(mMockEventHub, getKeyCodeForKeyLocation(EVENTHUB_ID, AKEYCODE_Y)) + .WillRepeatedly(Return(AKEYCODE_Z)); + ASSERT_EQ(AKEYCODE_Z, mMapper->getKeyCodeForKeyLocation(AKEYCODE_Y)) + << "If a mapping is available, the result is equal to the mapping"; + + ASSERT_EQ(AKEYCODE_A, mMapper->getKeyCodeForKeyLocation(AKEYCODE_A)) + << "If no mapping is available, the result is the key location"; +} + +TEST_F(KeyboardInputMapperUnitTest, GetScanCodeState) { + EXPECT_CALL(mMockEventHub, getScanCodeState(EVENTHUB_ID, KEY_A)) + .WillRepeatedly(Return(AKEY_STATE_DOWN)); + ASSERT_EQ(AKEY_STATE_DOWN, mMapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); + + EXPECT_CALL(mMockEventHub, getScanCodeState(EVENTHUB_ID, KEY_A)) + .WillRepeatedly(Return(AKEY_STATE_UP)); + ASSERT_EQ(AKEY_STATE_UP, mMapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); +} + +TEST_F(KeyboardInputMapperUnitTest, Process_LockedKeysShouldToggleMetaStateAndLeds) { + EXPECT_CALL(mMockEventHub, + hasLed(EVENTHUB_ID, AnyOf(LED_CAPSL, LED_NUML, LED_SCROLLL /*NOTYPO*/))) + .WillRepeatedly(Return(true)); + bool capsLockLed = true; // Initially on + bool numLockLed = false; // Initially off + bool scrollLockLed = false; // Initially off + EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_CAPSL, _)) + .WillRepeatedly(SaveArg<2>(&capsLockLed)); + EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_NUML, _)) + .WillRepeatedly(SaveArg<2>(&numLockLed)); + EXPECT_CALL(mMockEventHub, setLedState(EVENTHUB_ID, LED_SCROLLL /*NOTYPO*/, _)) + .WillRepeatedly(SaveArg<2>(&scrollLockLed)); + addKeyByEvdevCode(KEY_CAPSLOCK, AKEYCODE_CAPS_LOCK); + addKeyByEvdevCode(KEY_NUMLOCK, AKEYCODE_NUM_LOCK); + addKeyByEvdevCode(KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK); + + // In real operation, mappers pass new LED states to InputReader (via the context), which then + // calls back to the mappers to apply that state. Mimic the same thing here with mocks. + int32_t ledMetaState; + EXPECT_CALL(mMockInputReaderContext, updateLedMetaState(_)) + .WillRepeatedly([&](int32_t newState) { + ledMetaState = newState; + mMapper->updateLedState(false); + }); + EXPECT_CALL(mMockInputReaderContext, getLedMetaState()) + .WillRepeatedly(testing::ReturnPointee(&ledMetaState)); + + ASSERT_THAT(mMapper->reset(ARBITRARY_TIME), IsEmpty()); + + // Initial metastate is AMETA_NONE. + ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); + + // Initialization should have turned all of the lights off. + ASSERT_FALSE(capsLockLed); + ASSERT_FALSE(numLockLed); + ASSERT_FALSE(scrollLockLed); + + // Toggle caps lock on. + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0); + ASSERT_TRUE(capsLockLed); + ASSERT_FALSE(numLockLed); + ASSERT_FALSE(scrollLockLed); + ASSERT_EQ(AMETA_CAPS_LOCK_ON, mMapper->getMetaState()); + + // Toggle num lock on. + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0); + ASSERT_TRUE(capsLockLed); + ASSERT_TRUE(numLockLed); + ASSERT_FALSE(scrollLockLed); + ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mMapper->getMetaState()); + + // Toggle caps lock off. + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 1); + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_CAPSLOCK, 0); + ASSERT_FALSE(capsLockLed); + ASSERT_TRUE(numLockLed); + ASSERT_FALSE(scrollLockLed); + ASSERT_EQ(AMETA_NUM_LOCK_ON, mMapper->getMetaState()); + + // Toggle scroll lock on. + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + ASSERT_FALSE(capsLockLed); + ASSERT_TRUE(numLockLed); + ASSERT_TRUE(scrollLockLed); + ASSERT_EQ(AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mMapper->getMetaState()); + + // Toggle num lock off. + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 1); + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_NUMLOCK, 0); + ASSERT_FALSE(capsLockLed); + ASSERT_FALSE(numLockLed); + ASSERT_TRUE(scrollLockLed); + ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mMapper->getMetaState()); + + // Toggle scroll lock off. + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 1); + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + ASSERT_FALSE(capsLockLed); + ASSERT_FALSE(numLockLed); + ASSERT_FALSE(scrollLockLed); + ASSERT_EQ(AMETA_NONE, mMapper->getMetaState()); +} + +TEST_F(KeyboardInputMapperUnitTest, DisablingDeviceResetsPressedKeys) { + const int32_t USAGE_A = 0x070004; + addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE); + addKeyByUsageCode(USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE); + + // Key down by scan code. + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + NotifyKeyArgs args = expectSingleKeyArg(argsList); + ASSERT_EQ(DEVICE_ID, args.deviceId); + ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); + ASSERT_EQ(ARBITRARY_TIME, args.eventTime); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); + ASSERT_EQ(AKEYCODE_HOME, args.keyCode); + ASSERT_EQ(KEY_HOME, args.scanCode); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); + + // Disable device, it should synthesize cancellation events for down events. + mReaderConfiguration.disabledDevices.insert(DEVICE_ID); + argsList = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::ENABLED_STATE); + argsList += mMapper->reset(ARBITRARY_TIME); + args = expectSingleKeyArg(argsList); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); + ASSERT_EQ(AKEYCODE_HOME, args.keyCode); + ASSERT_EQ(KEY_HOME, args.scanCode); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_CANCELED, args.flags); +} + +TEST_F(KeyboardInputMapperUnitTest, Configure_AssignKeyboardLayoutInfo) { + std::list<NotifyArgs> unused = + mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, /*changes=*/{}); + + int32_t generation = mDevice->getGeneration(); + mReaderConfiguration.keyboardLayoutAssociations.insert( + {mIdentifier.location, DEVICE_KEYBOARD_LAYOUT_INFO}); + + unused += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION); + + InputDeviceInfo deviceInfo; + mMapper->populateDeviceInfo(deviceInfo); + ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.languageTag, + deviceInfo.getKeyboardLayoutInfo()->languageTag); + ASSERT_EQ(DEVICE_KEYBOARD_LAYOUT_INFO.layoutType, + deviceInfo.getKeyboardLayoutInfo()->layoutType); + ASSERT_GT(mDevice->getGeneration(), generation); + + // Call change layout association with the same values: Generation shouldn't change + generation = mDevice->getGeneration(); + unused += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::KEYBOARD_LAYOUT_ASSOCIATION); + ASSERT_EQ(mDevice->getGeneration(), generation); +} + +TEST_F(KeyboardInputMapperUnitTest, LayoutInfoCorrectlyMapped) { + EXPECT_CALL(mMockEventHub, getRawLayoutInfo(EVENTHUB_ID)) + .WillRepeatedly(Return(RawLayoutInfo{.languageTag = "en", .layoutType = "extended"})); + + // Configuration + std::list<NotifyArgs> unused = + mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, /*changes=*/{}); + + InputDeviceInfo deviceInfo; + mMapper->populateDeviceInfo(deviceInfo); + ASSERT_EQ("en", deviceInfo.getKeyboardLayoutInfo()->languageTag); + ASSERT_EQ("extended", deviceInfo.getKeyboardLayoutInfo()->layoutType); +} + +TEST_F(KeyboardInputMapperUnitTest, Process_GestureEventToSetFlagKeepTouchMode) { + addKeyByEvdevCode(KEY_LEFT, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE); + + // Key down + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_LEFT, 1); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, + expectSingleKeyArg(argsList).flags); +} + +TEST_F_WITH_FLAGS(KeyboardInputMapperUnitTest, WakeBehavior_AlphabeticKeyboard, + REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags, + enable_alphabetic_keyboard_wake))) { + // For internal alphabetic devices, keys will trigger wake on key down. + + addKeyByEvdevCode(KEY_A, AKEYCODE_A); + addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME); + addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE); + + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_A, 1); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_A, 0); + ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); + ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0); + ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); +} + +// --- KeyboardInputMapperTest --- + +// TODO(b/283812079): convert the tests for this class, which use multiple mappers each, to use +// InputMapperUnitTest. +class KeyboardInputMapperTest : public InputMapperTest { +protected: + void SetUp() override { + InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD | + InputDeviceClass::ALPHAKEY); + } + const std::string UNIQUE_ID = "local:0"; + + void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode, + int32_t originalKeyCode, int32_t rotatedKeyCode, + ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID); +}; + +void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper, + int32_t originalScanCode, int32_t originalKeyCode, + int32_t rotatedKeyCode, + ui::LogicalDisplayId displayId) { + NotifyKeyArgs args; + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); + ASSERT_EQ(originalScanCode, args.scanCode); + ASSERT_EQ(rotatedKeyCode, args.keyCode); + ASSERT_EQ(displayId, args.displayId); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); + ASSERT_EQ(originalScanCode, args.scanCode); + ASSERT_EQ(rotatedKeyCode, args.keyCode); + ASSERT_EQ(displayId, args.displayId); +} + +TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { + // keyboard 1. + mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + + // keyboard 2. + const std::string USB2 = "USB2"; + const std::string DEVICE_NAME2 = "KEYBOARD2"; + constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; + constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; + std::shared_ptr<InputDevice> device2 = + newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, + ftl::Flags<InputDeviceClass>(0)); + + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + + KeyboardInputMapper& mapper = + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); + + device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID); + KeyboardInputMapper& mapper2 = + device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, + mFakePolicy + ->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + std::list<NotifyArgs> unused = + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + /*changes=*/{}); + unused += device2->reset(ARBITRARY_TIME); + + // Prepared displays and associated info. + constexpr uint8_t hdmi1 = 0; + constexpr uint8_t hdmi2 = 1; + const std::string SECONDARY_UNIQUE_ID = "local:1"; + + mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1); + mFakePolicy->addInputPortAssociation(USB2, hdmi2); + + // No associated display viewport found, should disable the device. + unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_FALSE(device2->isEnabled()); + + // Prepare second display. + constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2}; + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + UNIQUE_ID, hdmi1, ViewportType::INTERNAL); + setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + SECONDARY_UNIQUE_ID, hdmi2, ViewportType::EXTERNAL); + // Default device will reconfigure above, need additional reconfiguration for another device. + unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::Change::DISPLAY_INFO); + + // Device should be enabled after the associated display is found. + ASSERT_TRUE(mDevice->isEnabled()); + ASSERT_TRUE(device2->isEnabled()); + + // Test pad key events + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_DOWN, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_LEFT, DISPLAY_ID)); + + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_RIGHT, newDisplayId)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_DOWN, newDisplayId)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_LEFT, newDisplayId)); +} + +TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleAfterReattach) { + mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/); + mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/); + mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); + + KeyboardInputMapper& mapper = + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); + // Initial metastate is AMETA_NONE. + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); + + // Initialization should have turned all of the lights off. + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); + + // Toggle caps lock on. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); + + // Toggle num lock on. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON, mapper.getMetaState()); + + // Toggle scroll lock on. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); + ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); + + mFakeEventHub->removeDevice(EVENTHUB_ID); + mReader->loopOnce(); + + // keyboard 2 should default toggle keys. + const std::string USB2 = "USB2"; + const std::string DEVICE_NAME2 = "KEYBOARD2"; + constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; + constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; + std::shared_ptr<InputDevice> device2 = + newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, + ftl::Flags<InputDeviceClass>(0)); + mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/); + mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/); + mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); + + device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID); + KeyboardInputMapper& mapper2 = + device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, + mFakePolicy + ->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + std::list<NotifyArgs> unused = + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + /*changes=*/{}); + unused += device2->reset(ARBITRARY_TIME); + + ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_CAPSL)); + ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_NUML)); + ASSERT_TRUE(mFakeEventHub->getLedState(SECOND_EVENTHUB_ID, LED_SCROLLL)); + ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, + mapper2.getMetaState()); +} + +TEST_F(KeyboardInputMapperTest, Process_toggleCapsLockState) { + mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); + + // Suppose we have two mappers. (DPAD + KEYBOARD) + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD); + KeyboardInputMapper& mapper = + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); + // Initial metastate is AMETA_NONE. + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); + + mReader->toggleCapsLockState(DEVICE_ID); + ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper.getMetaState()); +} + +TEST_F(KeyboardInputMapperTest, Process_ResetLockedModifierState) { + mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); + + KeyboardInputMapper& mapper = + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); + // Initial metastate is AMETA_NONE. + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); + + // Toggle caps lock on. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); + + // Toggle num lock on. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); + + // Toggle scroll lock on. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + ASSERT_EQ(AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON | AMETA_SCROLL_LOCK_ON, mapper.getMetaState()); + + mReader->resetLockedModifierState(); + ASSERT_EQ(AMETA_NONE, mapper.getMetaState()); +} + +TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleInMultiDevices) { + // keyboard 1. + mFakeEventHub->addLed(EVENTHUB_ID, LED_CAPSL, true /*initially on*/); + mFakeEventHub->addLed(EVENTHUB_ID, LED_NUML, false /*initially off*/); + mFakeEventHub->addLed(EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); + + KeyboardInputMapper& mapper1 = + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); + + // keyboard 2. + const std::string USB2 = "USB2"; + const std::string DEVICE_NAME2 = "KEYBOARD2"; + constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; + constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1; + std::shared_ptr<InputDevice> device2 = + newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID, + ftl::Flags<InputDeviceClass>(0)); + mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_CAPSL, true /*initially on*/); + mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_NUML, false /*initially off*/); + mFakeEventHub->addLed(SECOND_EVENTHUB_ID, LED_SCROLLL, false /*initially off*/); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_CAPSLOCK, 0, AKEYCODE_CAPS_LOCK, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_NUMLOCK, 0, AKEYCODE_NUM_LOCK, 0); + mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0); + + device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID); + KeyboardInputMapper& mapper2 = + device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID, + mFakePolicy + ->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + std::list<NotifyArgs> unused = + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + /*changes=*/{}); + unused += device2->reset(ARBITRARY_TIME); + + // Initial metastate is AMETA_NONE. + ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); + ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); + + // Toggle num lock on and off. + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper1.getMetaState()); + ASSERT_EQ(AMETA_NUM_LOCK_ON, mapper2.getMetaState()); + + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 1); + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_NUMLOCK, 0); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_NUML)); + ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); + ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); + + // Toggle caps lock on and off. + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper1.getMetaState()); + ASSERT_EQ(AMETA_CAPS_LOCK_ON, mapper2.getMetaState()); + + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 1); + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_CAPSLOCK, 0); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_CAPSL)); + ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); + ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); + + // Toggle scroll lock on and off. + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + ASSERT_TRUE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); + ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper1.getMetaState()); + ASSERT_EQ(AMETA_SCROLL_LOCK_ON, mapper2.getMetaState()); + + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 1); + process(mapper1, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_SCROLLLOCK, 0); + ASSERT_FALSE(mFakeEventHub->getLedState(EVENTHUB_ID, LED_SCROLLL)); + ASSERT_EQ(AMETA_NONE, mapper1.getMetaState()); + ASSERT_EQ(AMETA_NONE, mapper2.getMetaState()); +} + +/** + * When there is more than one KeyboardInputMapper for an InputDevice, each mapper should produce + * events that use the shared keyboard source across all mappers. This is to ensure that each + * input device generates key events in a consistent manner, regardless of which mapper produces + * the event. + */ +TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) { + mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE); + + // Add a mapper with SOURCE_KEYBOARD + KeyboardInputMapper& keyboardMapper = + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD); + + process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); + ASSERT_NO_FATAL_FAILURE( + mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD))); + process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); + ASSERT_NO_FATAL_FAILURE( + mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD))); + + // Add a mapper with SOURCE_DPAD + KeyboardInputMapper& dpadMapper = + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD); + for (auto* mapper : {&keyboardMapper, &dpadMapper}) { + process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( + WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD))); + process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( + WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD))); + } + + // Add a mapper with SOURCE_GAMEPAD + KeyboardInputMapper& gamepadMapper = + constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD); + for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) { + process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( + WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD))); + process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled( + WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD))); + } +} + +// --- KeyboardInputMapperTest_ExternalAlphabeticDevice --- + +class KeyboardInputMapperTest_ExternalAlphabeticDevice : public KeyboardInputMapperUnitTest { +protected: + void SetUp() override { + InputMapperUnitTest::SetUp(); + ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD)); + ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::ALPHABETIC)); + ON_CALL((*mDevice), isExternal).WillByDefault(Return(true)); + EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID)) + .WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::ALPHAKEY | + InputDeviceClass::EXTERNAL)); + mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, + AINPUT_SOURCE_KEYBOARD); + } +}; + +// --- KeyboardInputMapperTest_ExternalNonAlphabeticDevice --- + +class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public KeyboardInputMapperUnitTest { +protected: + void SetUp() override { + InputMapperUnitTest::SetUp(); + ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD)); + ON_CALL((*mDevice), getKeyboardType).WillByDefault(Return(KeyboardType::NON_ALPHABETIC)); + ON_CALL((*mDevice), isExternal).WillByDefault(Return(true)); + EXPECT_CALL(mMockEventHub, getDeviceClasses(EVENTHUB_ID)) + .WillRepeatedly(Return(InputDeviceClass::KEYBOARD | InputDeviceClass::EXTERNAL)); + mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, + AINPUT_SOURCE_KEYBOARD); + } +}; + +TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, WakeBehavior_AlphabeticKeyboard) { + // For external devices, keys will trigger wake on key down. Media keys should also trigger + // wake if triggered from external devices. + + addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME); + addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY); + addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE); + + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); + ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); + ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); +} + +TEST_F(KeyboardInputMapperTest_ExternalNonAlphabeticDevice, WakeBehavior_NonAlphabeticKeyboard) { + // For external devices, keys will trigger wake on key down. Media keys should not trigger + // wake if triggered from external non-alphaebtic keyboard (e.g. headsets). + + addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY); + addKeyByEvdevCode(KEY_PLAYPAUSE, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE); + + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); + ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); + ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); +} + +TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, DoNotWakeByDefaultBehavior) { + // Tv Remote key's wake behavior is prescribed by the keylayout file. + + addKeyByEvdevCode(KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE); + addKeyByEvdevCode(KEY_DOWN, AKEYCODE_DPAD_DOWN); + addKeyByEvdevCode(KEY_PLAY, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE); + + mPropertyMap.addProperty("keyboard.doNotWakeByDefault", "1"); + mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration, + AINPUT_SOURCE_KEYBOARD); + + std::list<NotifyArgs> argsList = process(ARBITRARY_TIME, EV_KEY, KEY_HOME, 1); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1); + ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0); + ASSERT_EQ(uint32_t(0), expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); + + argsList = process(ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0); + ASSERT_EQ(POLICY_FLAG_WAKE, expectSingleKeyArg(argsList).policyFlags); +} + } // namespace android diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp index 64f3c279a6..6be922dfdb 100644 --- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp @@ -75,6 +75,8 @@ public: void toggleCapsLockState(int32_t deviceId) { reader->toggleCapsLockState(deviceId); } + void resetLockedModifierState() { reader->resetLockedModifierState(); } + bool hasKeys(int32_t deviceId, uint32_t sourceMask, const std::vector<int32_t>& keyCodes, uint8_t* outFlags) { return reader->hasKeys(deviceId, sourceMask, keyCodes, outFlags); @@ -226,6 +228,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { fdp->ConsumeIntegral<int32_t>()); }, [&]() -> void { reader->toggleCapsLockState(fdp->ConsumeIntegral<int32_t>()); }, + [&]() -> void { reader->resetLockedModifierState(); }, [&]() -> void { size_t count = fdp->ConsumeIntegralInRange<size_t>(1, 1024); std::vector<uint8_t> outFlags(count); diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 98b6666a1f..ee813bf340 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -895,6 +895,12 @@ void Output::writeCompositionState(const compositionengine::CompositionRefreshAr })) { editState().earliestPresentTime = frameTargetPtrOpt->get()->earliestPresentTime(); editState().expectedPresentTime = frameTargetPtrOpt->get()->expectedPresentTime().ns(); + const auto debugPresentDelay = frameTargetPtrOpt->get()->debugPresentDelay(); + if (debugPresentDelay) { + SFTRACE_FORMAT_INSTANT("DEBUG delaying presentation by %.2fms", + debugPresentDelay->ns() / 1e6f); + editState().expectedPresentTime += debugPresentDelay->ns(); + } } editState().frameInterval = refreshArgs.frameInterval; editState().powerCallback = refreshArgs.powerCallback; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 7569c1b8de..4d9a9ca06e 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -261,20 +261,25 @@ void updateVisibility(LayerSnapshot& snapshot, bool visible) { } snapshot.isVisible = visible; - // TODO(b/238781169) we are ignoring this compat for now, since we will have - // to remove any optimization based on visibility. - - // For compatibility reasons we let layers which can receive input - // receive input before they have actually submitted a buffer. Because - // of this we use canReceiveInput instead of isVisible to check the - // policy-visibility, ignoring the buffer state. However for layers with - // hasInputInfo()==false we can use the real visibility state. - // We are just using these layers for occlusion detection in - // InputDispatcher, and obviously if they aren't visible they can't occlude - // anything. - const bool visibleForInput = - snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible; - snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput); + if (FlagManager::getInstance().skip_invisible_windows_in_input()) { + snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visible); + } else { + // TODO(b/238781169) we are ignoring this compat for now, since we will have + // to remove any optimization based on visibility. + + // For compatibility reasons we let layers which can receive input + // receive input before they have actually submitted a buffer. Because + // of this we use canReceiveInput instead of isVisible to check the + // policy-visibility, ignoring the buffer state. However for layers with + // hasInputInfo()==false we can use the real visibility state. + // We are just using these layers for occlusion detection in + // InputDispatcher, and obviously if they aren't visible they can't occlude + // anything. + const bool visibleForInput = + snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible; + snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, + !visibleForInput); + } LLOGV(snapshot.sequence, "updating visibility %s %s", visible ? "true" : "false", snapshot.getDebugString().c_str()); } @@ -1260,6 +1265,10 @@ void LayerSnapshotBuilder::forEachInputSnapshot(const ConstVisitor& visitor) con for (int i = mNumInterestingSnapshots - 1; i >= 0; i--) { LayerSnapshot& snapshot = *mSnapshots[(size_t)i]; if (!snapshot.hasInputInfo()) continue; + if (FlagManager::getInstance().skip_invisible_windows_in_input() && + snapshot.inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) { + continue; + } visitor(snapshot); } } diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index 84fa1390a4..e4069dd691 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -770,17 +770,13 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi const bool inPrimaryPhysicalRange = policy->primaryRanges.physical.includes(modePtr->getPeakFps()); const bool inPrimaryRenderRange = policy->primaryRanges.render.includes(fps); - if (!mIsVrrDevice.load() && - ((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) || + if (((policy->primaryRangeIsSingleRate() && !inPrimaryPhysicalRange) || !inPrimaryRenderRange) && !(layer.focused && (layer.vote == LayerVoteType::ExplicitDefault || layer.vote == LayerVoteType::ExplicitExact))) { // Only focused layers with ExplicitDefault frame rate settings are allowed to score // refresh rates outside the primary range. - ALOGV("%s ignores %s (primaryRangeIsSingleRate). Current mode = %s", - formatLayerInfo(layer, weight).c_str(), to_string(*modePtr).c_str(), - to_string(activeMode).c_str()); continue; } @@ -904,8 +900,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi to_string(descending.front().frameRateMode.fps).c_str()); return {descending, kNoSignals}; } else { - ALOGV("%s (primaryRangeIsSingleRate)", - to_string(ranking.front().frameRateMode.fps).c_str()); + ALOGV("primaryRangeIsSingleRate"); SFTRACE_FORMAT_INSTANT("%s (primaryRangeIsSingleRate)", to_string(ranking.front().frameRateMode.fps).c_str()); return {ranking, kNoSignals}; @@ -1041,14 +1036,12 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme // Layers with ExplicitExactOrMultiple expect touch boost if (globalSignals.touch && hasExplicitExactOrMultiple) { - ALOGV("%s: Skipping for touch (input signal): uid=%d", __func__, uid); continue; } // Mirrors getRankedFrameRates. If there is no ExplicitDefault, expect touch boost and // skip frame rate override. if (hasHighHint && !hasExplicitDefault) { - ALOGV("%s: Skipping for touch (HighHint): uid=%d", __func__, uid); continue; } @@ -1072,9 +1065,6 @@ auto RefreshRateSelector::getFrameRateOverrides(const std::vector<LayerRequireme constexpr bool isSeamlessSwitch = true; const auto layerScore = calculateLayerScoreLocked(*layer, fps, isSeamlessSwitch); score += layer->weight * layerScore; - ALOGV("%s: %s gives %s fps score of %.4f", __func__, - formatLayerInfo(*layer, layer->weight).c_str(), to_string(fps).c_str(), - layerScore); } } @@ -1329,8 +1319,6 @@ void RefreshRateSelector::updateDisplayModes(DisplayModes modes, DisplayModeId a LOG_ALWAYS_FATAL_IF(!activeModeOpt); mActiveModeOpt = FrameRateMode{activeModeOpt->get()->getPeakFps(), ftl::as_non_null(activeModeOpt->get())}; - mIsVrrDevice = FlagManager::getInstance().vrr_config() && - activeModeOpt->get()->getVrrConfig().has_value(); const auto sortedModes = sortByRefreshRate(mDisplayModes); mMinRefreshRateModeIt = sortedModes.front(); diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 8c587a9376..2875650ed0 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -203,12 +203,16 @@ void Scheduler::run() { void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, TimePoint expectedVsyncTime) { + const auto debugPresentDelay = mDebugPresentDelay.load(); + mDebugPresentDelay.store(std::nullopt); + const FrameTargeter::BeginFrameArgs beginFrameArgs = {.frameBeginTime = SchedulerClock::now(), .vsyncId = vsyncId, .expectedVsyncTime = expectedVsyncTime, .sfWorkDuration = mVsyncModulator->getVsyncConfig().sfWorkDuration, - .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration}; + .hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration, + .debugPresentTimeDelay = debugPresentDelay}; ftl::NonNull<const Display*> pacesetterPtr = pacesetterPtrLocked(); pacesetterPtr->targeterPtr->beginFrame(beginFrameArgs, *pacesetterPtr->schedulePtr); diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 8340880918..61c68a9473 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -337,6 +337,8 @@ public: // recovery should begin. void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids); + void setDebugPresentDelay(TimePoint delay) { mDebugPresentDelay = delay; } + private: friend class TestableScheduler; @@ -602,6 +604,8 @@ private: FrameRateOverrideMappings mFrameRateOverrideMappings; SmallAreaDetectionAllowMappings mSmallAreaDetectionAllowMappings; + + std::atomic<std::optional<TimePoint>> mDebugPresentDelay; }; } // namespace scheduler diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h index 2185bb07ec..813d4dedff 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h @@ -53,6 +53,8 @@ public: TimePoint expectedPresentTime() const { return mExpectedPresentTime; } + std::optional<TimePoint> debugPresentDelay() const { return mDebugPresentTimeDelay; } + std::optional<TimePoint> earliestPresentTime() const { return mEarliestPresentTime; } // Equivalent to `expectedSignaledPresentFence` unless running N VSYNCs ahead. @@ -84,6 +86,7 @@ protected: TimePoint mFrameBeginTime; TimePoint mExpectedPresentTime; std::optional<TimePoint> mEarliestPresentTime; + std::optional<TimePoint> mDebugPresentTimeDelay; TracedOrdinal<bool> mFramePending; TracedOrdinal<bool> mFrameMissed; @@ -135,6 +138,7 @@ public: TimePoint expectedVsyncTime; Duration sfWorkDuration; Duration hwcMinWorkDuration; + std::optional<TimePoint> debugPresentTimeDelay; // used to introduce jank for testing }; void beginFrame(const BeginFrameArgs&, const IVsyncSource&); diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp index 3ee1e541c3..4f16130aa5 100644 --- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp +++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp @@ -86,6 +86,7 @@ void FrameTargeter::beginFrame(const BeginFrameArgs& args, const IVsyncSource& v IsFencePendingFuncPtr isFencePendingFuncPtr) { mVsyncId = args.vsyncId; mFrameBeginTime = args.frameBeginTime; + mDebugPresentTimeDelay = args.debugPresentTimeDelay; // The `expectedVsyncTime`, which was predicted when this frame was scheduled, is normally in // the future relative to `frameBeginTime`, but may not be for delayed frames. Adjust diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index eef4086f62..1f35173f65 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1420,8 +1420,6 @@ status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToke return future.get(); } -// TODO: b/241285876 - Restore thread safety analysis once mStateLock below is unconditional. -[[clang::no_thread_safety_analysis]] void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) { SFTRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); @@ -1437,8 +1435,6 @@ void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) { if (const auto oldResolution = mDisplayModeController.getActiveMode(displayId).modePtr->getResolution(); oldResolution != activeMode.modePtr->getResolution()) { - ConditionalLock lock(mStateLock, !FlagManager::getInstance().connected_display()); - auto& state = mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId)); // We need to generate new sequenceId in order to recreate the display (and this // way the framebuffer). @@ -2252,12 +2248,10 @@ void SurfaceFlinger::onComposerHalHotplugEvent(hal::HWDisplayId hwcDisplayId, return; } - if (FlagManager::getInstance().hotplug2()) { - // TODO(b/311403559): use enum type instead of int - const auto errorCode = static_cast<int32_t>(event); - ALOGD("%s: Hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode, hwcDisplayId); - mScheduler->dispatchHotplugError(errorCode); - } + // TODO(b/311403559): use enum type instead of int + const auto errorCode = static_cast<int32_t>(event); + ALOGD("%s: Hotplug error %d for hwcDisplayId %" PRIu64, __func__, errorCode, hwcDisplayId); + mScheduler->dispatchHotplugError(errorCode); } void SurfaceFlinger::onComposerHalVsyncPeriodTimingChanged( @@ -2599,7 +2593,7 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, } { - ConditionalLock lock(mStateLock, FlagManager::getInstance().connected_display()); + Mutex::Autolock lock(mStateLock); for (const auto [displayId, _] : frameTargets) { if (mDisplayModeController.isModeSetPending(displayId)) { @@ -2702,13 +2696,6 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, mScheduler->chooseRefreshRateForContent(&mLayerHierarchyBuilder.getHierarchy(), updateAttachedChoreographer); - if (FlagManager::getInstance().connected_display()) { - initiateDisplayModeChanges(); - } - } - - if (!FlagManager::getInstance().connected_display()) { - ftl::FakeGuard guard(mStateLock); initiateDisplayModeChanges(); } @@ -3494,10 +3481,8 @@ bool SurfaceFlinger::configureLocked() { processHotplugConnect(displayId, hwcDisplayId, std::move(*info), displayString.c_str()); if (!activeModeIdOpt) { - if (FlagManager::getInstance().hotplug2()) { - mScheduler->dispatchHotplugError( - static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN)); - } + mScheduler->dispatchHotplugError( + static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN)); getHwComposer().disconnectDisplay(displayId); continue; } @@ -6263,7 +6248,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { } // Numbers from 1000 to 1045 are currently used for backdoors. The code // in onTransact verifies that the user is root, and has access to use SF. - if (code >= 1000 && code <= 1045) { + if (code >= 1000 && code <= 1046) { ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code); return OK; } @@ -6796,6 +6781,15 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } return err; } + // Introduce jank to HWC + case 1046: { + int32_t jankDelayMs = 0; + if (data.readInt32(&jankDelayMs) != NO_ERROR) { + return BAD_VALUE; + } + mScheduler->setDebugPresentDelay(TimePoint::fromNs(ms2ns(jankDelayMs))); + return NO_ERROR; + } } } return err; diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index 12616e3edb..b56ee01422 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -119,10 +119,10 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(adpf_fmq_sf); DUMP_READ_ONLY_FLAG(connected_display); DUMP_READ_ONLY_FLAG(enable_small_area_detection); + DUMP_READ_ONLY_FLAG(stable_edid_ids); DUMP_READ_ONLY_FLAG(frame_rate_category_mrr); DUMP_READ_ONLY_FLAG(misc1); DUMP_READ_ONLY_FLAG(vrr_config); - DUMP_READ_ONLY_FLAG(hotplug2); DUMP_READ_ONLY_FLAG(hdcp_level_hal); DUMP_READ_ONLY_FLAG(multithreaded_present); DUMP_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace); @@ -159,6 +159,7 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(display_config_error_hal); DUMP_READ_ONLY_FLAG(connected_display_hdr); DUMP_READ_ONLY_FLAG(deprecate_frame_tracker); + DUMP_READ_ONLY_FLAG(skip_invisible_windows_in_input); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG @@ -227,10 +228,10 @@ FLAG_MANAGER_LEGACY_SERVER_FLAG(use_skia_tracing, PROPERTY_SKIA_ATRACE_ENABLED, FLAG_MANAGER_READ_ONLY_FLAG(adpf_fmq_sf, "") FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "") FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "") +FLAG_MANAGER_READ_ONLY_FLAG(stable_edid_ids, "debug.sf.stable_edid_ids") FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr") FLAG_MANAGER_READ_ONLY_FLAG(misc1, "") FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config") -FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "") FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "") FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present") FLAG_MANAGER_READ_ONLY_FLAG(add_sf_skipped_frames_to_trace, "") @@ -266,6 +267,7 @@ FLAG_MANAGER_READ_ONLY_FLAG(true_hdr_screenshots, "debug.sf.true_hdr_screenshots FLAG_MANAGER_READ_ONLY_FLAG(display_config_error_hal, ""); FLAG_MANAGER_READ_ONLY_FLAG(connected_display_hdr, ""); FLAG_MANAGER_READ_ONLY_FLAG(deprecate_frame_tracker, ""); +FLAG_MANAGER_READ_ONLY_FLAG(skip_invisible_windows_in_input, ""); /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "") diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index f5bea7237f..9086537619 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -58,9 +58,9 @@ public: bool connected_display() const; bool frame_rate_category_mrr() const; bool enable_small_area_detection() const; + bool stable_edid_ids() const; bool misc1() const; bool vrr_config() const; - bool hotplug2() const; bool hdcp_level_hal() const; bool multithreaded_present() const; bool add_sf_skipped_frames_to_trace() const; @@ -97,6 +97,7 @@ public: bool display_config_error_hal() const; bool connected_display_hdr() const; bool deprecate_frame_tracker() const; + bool skip_invisible_windows_in_input() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig index 014c736a6b..d4250bcccc 100644 --- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig @@ -196,6 +196,25 @@ flag { } # single_hop_screenshot flag { + name: "skip_invisible_windows_in_input" + namespace: "window_surfaces" + description: "Only send visible windows to input list" + bug: "305254099" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } + } # skip_invisible_windows_in_input + +flag { + name: "stable_edid_ids" + namespace: "core_graphics" + description: "Guard use of the new stable EDID-based display IDs system." + bug: "352320847" + is_fixed_read_only: true +} # stable_edid_ids + +flag { name: "true_hdr_screenshots" namespace: "core_graphics" description: "Enables screenshotting display content in HDR, sans tone mapping" diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 0dfbd6185e..08e426564a 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -166,6 +166,7 @@ public: a.presentTime == b.presentTime; } + NO_THREAD_SAFETY_ANALYSIS const std::map<int64_t, TimelineItem>& getPredictions() const { return mTokenManager->mPredictions; } diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 6aec7434de..8c53eef01a 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -1550,6 +1550,9 @@ TEST_F(LayerSnapshotTest, propagateDropInputMode) { } TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags:: + skip_invisible_windows_in_input, + false); LayerHierarchyTestBase::createRootLayer(3); setColor(3, {-1._hf, -1._hf, -1._hf}); UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); @@ -1575,6 +1578,39 @@ TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) { EXPECT_TRUE(foundInputLayer); } +TEST_F(LayerSnapshotTest, NonVisibleLayerWithInputShouldNotBeIncluded) { + SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags:: + skip_invisible_windows_in_input, + true); + LayerHierarchyTestBase::createRootLayer(3); + setColor(3, {-1._hf, -1._hf, -1._hf}); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; + transactions.back().states.front().layerId = 3; + transactions.back().states.front().state.windowInfoHandle = sp<gui::WindowInfoHandle>::make(); + auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); + inputInfo->token = sp<BBinder>::make(); + hideLayer(3); + mLifecycleManager.applyTransactions(transactions); + + update(mSnapshotBuilder); + + bool foundInputLayer = false; + mSnapshotBuilder.forEachInputSnapshot([&](const frontend::LayerSnapshot& snapshot) { + if (snapshot.uniqueSequence == 3) { + EXPECT_TRUE( + snapshot.inputInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)); + EXPECT_FALSE(snapshot.isVisible); + foundInputLayer = true; + } + }); + EXPECT_FALSE(foundInputLayer); +} + TEST_F(LayerSnapshotTest, ForEachSnapshotsWithPredicate) { std::vector<uint32_t> visitedUniqueSequences; mSnapshotBuilder.forEachSnapshot( diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index b5be8dba96..80b2b8de69 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -308,42 +308,6 @@ protected: << " category=" << ftl::enum_string(testCase.frameRateCategory); } } - - template <class T> - std::vector<LayerRequirement> createLayers(const std::initializer_list<T>& surfaceVotes) { - std::vector<LayerRequirement> layers; - for (auto surfaceVote : surfaceVotes) { - ALOGI("**** %s: Adding layers for %s: (desiredFrameRate=%s, voteType=%s), " - "(frameRateCategory=%s)", - __func__, surfaceVote.name.c_str(), - to_string(surfaceVote.desiredFrameRate).c_str(), - ftl::enum_string(surfaceVote.voteType).c_str(), - ftl::enum_string(surfaceVote.frameRateCategory).c_str()); - - if (surfaceVote.desiredFrameRate.isValid()) { - std::stringstream ss; - ss << surfaceVote.name << " (" << surfaceVote.weight << "): ExplicitDefault (" - << to_string(surfaceVote.desiredFrameRate) << ")"; - LayerRequirement layer = {.name = ss.str(), - .vote = surfaceVote.voteType, - .desiredRefreshRate = surfaceVote.desiredFrameRate, - .weight = surfaceVote.weight}; - layers.push_back(layer); - } - - if (surfaceVote.frameRateCategory != FrameRateCategory::Default) { - std::stringstream ss; - ss << surfaceVote.name << " (" << surfaceVote.weight << "): ExplicitCategory (" - << ftl::enum_string(surfaceVote.frameRateCategory) << ")"; - LayerRequirement layer = {.name = ss.str(), - .vote = LayerVoteType::ExplicitCategory, - .frameRateCategory = surfaceVote.frameRateCategory, - .weight = surfaceVote.weight}; - layers.push_back(layer); - } - } - return layers; - } }; RefreshRateSelectorTest::RefreshRateSelectorTest() { @@ -1812,98 +1776,6 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategoryMultiL selector); } -TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_multiSurface_arr) { - if (GetParam() != Config::FrameRateOverride::Enabled) { - return; - } - - SET_FLAG_FOR_TEST(flags::vrr_config, true); - - auto selector = createSelector(kVrrMode_120, kModeId120); - - // Switch the policy to be more like an ARR device (primary range is a single rate). - constexpr FpsRange k120_120Hz = {120_Hz, 120_Hz}; - constexpr FpsRange k0_120Hz = {0_Hz, 120_Hz}; - constexpr FpsRanges kPrimaryRanges = {/*physical*/ k120_120Hz, - /*render*/ k120_120Hz}; - constexpr FpsRanges kAppRequestRanges = {/*physical*/ k120_120Hz, - /*render*/ k0_120Hz}; - EXPECT_EQ(SetPolicyResult::Changed, - selector.setDisplayManagerPolicy( - {/*defaultMode*/ kModeId120, kPrimaryRanges, kAppRequestRanges})); - - // Surface can translate to multiple layers in SF scheduler due to category and frame rate - // value. - struct SurfaceVote { - // Params - std::string name = ""; - Fps desiredFrameRate = 0_Hz; - LayerVoteType voteType = LayerVoteType::ExplicitDefault; - FrameRateCategory frameRateCategory = FrameRateCategory::Default; - float weight = 1.f; - }; - - auto layers = createLayers( - std::initializer_list<SurfaceVote>{{.name = "60 fixed source", - .desiredFrameRate = 60_Hz, - .voteType = LayerVoteType::ExplicitExactOrMultiple, - .weight = 0.27f}, - {.name = "1 fixed source + NoPreference", - .desiredFrameRate = 1_Hz, - .voteType = LayerVoteType::ExplicitExactOrMultiple, - .frameRateCategory = - FrameRateCategory::NoPreference}}); - auto actualRankedFrameRates = selector.getRankedFrameRates(layers); - EXPECT_EQ(60_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); - - layers = createLayers( - std::initializer_list<SurfaceVote>{{.name = "60 fixed source", - .desiredFrameRate = 60_Hz, - .voteType = LayerVoteType::ExplicitExactOrMultiple, - .weight = 0.27f}, - {.name = "1 fixed source + Normal", - .desiredFrameRate = 1_Hz, - .voteType = LayerVoteType::ExplicitExactOrMultiple, - .frameRateCategory = FrameRateCategory::Normal}}); - actualRankedFrameRates = selector.getRankedFrameRates(layers); - EXPECT_EQ(60_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); - - layers = createLayers(std::initializer_list<SurfaceVote>{ - {.name = "30 fixed source + NoPreference", - .desiredFrameRate = 30_Hz, - .voteType = LayerVoteType::ExplicitExactOrMultiple, - .frameRateCategory = FrameRateCategory::NoPreference}}); - actualRankedFrameRates = selector.getRankedFrameRates(layers); - EXPECT_EQ(30_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); - - layers = createLayers(std::initializer_list<SurfaceVote>{ - {.name = "1 fixed source + NoPreference", - .desiredFrameRate = 1_Hz, - .voteType = LayerVoteType::ExplicitExactOrMultiple, - .frameRateCategory = FrameRateCategory::NoPreference}}); - actualRankedFrameRates = selector.getRankedFrameRates(layers); - // Result affected by RefreshRateSelector.kMinSupportedFrameRate. - EXPECT_EQ(20_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); - - layers = createLayers(std::initializer_list<SurfaceVote>{ - {.name = "24 fixed source + NoPreference", - .desiredFrameRate = 24_Hz, - .voteType = LayerVoteType::ExplicitExactOrMultiple, - .frameRateCategory = FrameRateCategory::NoPreference}}); - actualRankedFrameRates = selector.getRankedFrameRates(layers); - EXPECT_EQ(24_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); - - layers = createLayers(std::initializer_list<SurfaceVote>{ - {.name = "23.976 fixed source + NoPreference", - .desiredFrameRate = 23.976_Hz, - .voteType = LayerVoteType::ExplicitExactOrMultiple, - .frameRateCategory = FrameRateCategory::NoPreference}}); - actualRankedFrameRates = selector.getRankedFrameRates(layers); - // Chooses 120 unless certain threshold is set, see tests test23976Chooses120 and - // test23976Chooses60IfThresholdIs120. - EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); -} - TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_120) { auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60); diff --git a/vulkan/libvulkan/libvulkan_flags.aconfig b/vulkan/libvulkan/libvulkan_flags.aconfig index 891bc0261b..dae5b52160 100644 --- a/vulkan/libvulkan/libvulkan_flags.aconfig +++ b/vulkan/libvulkan/libvulkan_flags.aconfig @@ -8,3 +8,11 @@ flag { bug: "341978292" is_fixed_read_only: true } + +flag { + name: "vulkan_1_4_instance_api" + namespace: "core_graphics" + description: "Enable support for the Vulkan 1.4 instance API" + bug: "370568136" + is_fixed_read_only: true +} |