diff options
81 files changed, 2004 insertions, 1406 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/choreographer.h b/include/android/choreographer.h index bec3283cd8..2622a01935 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -318,7 +318,7 @@ size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex( /** * Gets the token used by the platform to identify the frame timeline at the given \c index. - * q + * * Available since API level 33. * * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See diff --git a/include/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/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h index 2e346bb7cd..70d00d16f4 100644 --- a/include/input/InputConsumerNoResampling.h +++ b/include/input/InputConsumerNoResampling.h @@ -141,7 +141,7 @@ private: } private: - std::function<int(int events)> mCallback; + const std::function<int(int events)> mCallback; }; sp<LooperEventCallback> mCallback; /** 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/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/BufferStuffing.md b/libs/gui/BufferStuffing.md new file mode 100644 index 0000000000..6ed8ad988b --- /dev/null +++ b/libs/gui/BufferStuffing.md @@ -0,0 +1,7 @@ +### Buffer Stuffing and Recovery ### + +Buffer stuffing happens on the client side when SurfaceFlinger misses a frame, but the client continues producing buffers at the same rate. This could occur anytime when SurfaceFlinger does not meet the expected timeline’s deadline to finish composing a frame. As a result, SurfaceFlinger cannot yet release the buffer for the frame that it missed and the client has one less buffer to render into. The client may then run out of buffers or have to wait for buffer release callbacks, increasing the chances of janking when clients render multiple windows. + +Recovery is implemented by first detecting when buffer stuffing occurs and ensuring that the elevated buffer counts in the server are from a relevant SurfaceControl (is a ViewRootImpl). Other SurfaceControl buffer producers such as games, media, and camera have other reasons for expectedly increased buffer counts, which do not need buffer stuffing recovery. + +The actual recovery adjusts the animation timeline in the Choreographer so that the client deadlines for subsequent frames are moved forward in time by one frame. This approach adjusts the client buffer production timeline such that SurfaceFlinger does not fall behind when it misses a frame because the client will simply match its frame production rate with SurfaceFlinger. Ordinarily, buffer stuffing is problematic because the client continues producing buffers when SurfaceFlinger is behind. However, if the client delays producing its buffers to match SurfaceFlinger’s rate, the animation has new frame deadlines that can be reasonably met. The animation is effectively paused for one frame longer than originally intended, and continues the remainder of the animation normally.
\ No newline at end of file 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/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 4dbf9e1929..40a6e79a24 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -119,7 +119,7 @@ public: HdcpLevelsChange hdcpLevelsChange; }; }; - static_assert(sizeof(Event) == 216); + static_assert(sizeof(Event) == 224); public: /* 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/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 001e570982..a42ddc466c 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -414,7 +414,7 @@ public: uint64_t nextFrameNumber{0}; FrameEventHistoryDelta frameTimestamps; bool bufferReplaced{false}; - int maxBufferCount{0}; + int maxBufferCount{BufferQueueDefs::NUM_BUFFER_SLOTS}; status_t result{NO_ERROR}; }; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 9098dffa8e..1c31e46cb4 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -171,6 +171,10 @@ struct layer_state_t { // Sets a property on this layer indicating that its visible region should be considered // when computing TrustedPresentation Thresholds. eCanOccludePresentation = 0x1000, + // Indicates that the SurfaceControl should recover from buffer stuffing when + // possible. This is the case when the SurfaceControl is the root SurfaceControl + // owned by ViewRootImpl. + eRecoverableFromBufferStuffing = 0x2000, }; enum { diff --git a/libs/gui/include/gui/VsyncEventData.h b/libs/gui/include/gui/VsyncEventData.h index b40a84099c..ced5130505 100644 --- a/libs/gui/include/gui/VsyncEventData.h +++ b/libs/gui/include/gui/VsyncEventData.h @@ -36,6 +36,9 @@ struct VsyncEventData { // Size of frame timelines provided by the platform; max is kFrameTimelinesCapacity. uint32_t frameTimelinesLength; + // Number of queued buffers to indicate if buffer stuffing mode is detected. + uint32_t numberQueuedBuffers; + struct alignas(8) FrameTimeline { // The Vsync Id corresponsing to this vsync event. This will be used to // populate ISurfaceComposer::setFrameTimelineVsync and 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/DisplayEventStructLayout_test.cpp b/libs/gui/tests/DisplayEventStructLayout_test.cpp index 29eeaa806f..791f471a1d 100644 --- a/libs/gui/tests/DisplayEventStructLayout_test.cpp +++ b/libs/gui/tests/DisplayEventStructLayout_test.cpp @@ -36,15 +36,16 @@ TEST(DisplayEventStructLayoutTest, TestEventAlignment) { CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameInterval, 8); CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.preferredFrameTimelineIndex, 16); CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelinesLength, 20); - CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 24); - CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 24); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.numberQueuedBuffers, 24); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines, 32); + CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].vsyncId, 32); CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[0].deadlineTimestamp, - 32); + 40); CHECK_OFFSET(DisplayEventReceiver::Event::VSync, - vsyncData.frameTimelines[0].expectedPresentationTime, 40); + vsyncData.frameTimelines[0].expectedPresentationTime, 48); // Also test the offsets of the last frame timeline. A loop is not used because the non-const // index cannot be used in static_assert. - const int lastFrameTimelineOffset = /* Start of array */ 24 + + const int lastFrameTimelineOffset = /* Start of array */ 32 + (VsyncEventData::kFrameTimelinesCapacity - 1) * /* Size of FrameTimeline */ 24; CHECK_OFFSET(DisplayEventReceiver::Event::VSync, vsyncData.frameTimelines[VsyncEventData::kFrameTimelinesCapacity - 1].vsyncId, 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/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp index d3653cfc45..2c0f77a814 100644 --- a/libs/input/InputConsumerNoResampling.cpp +++ b/libs/input/InputConsumerNoResampling.cpp @@ -193,13 +193,6 @@ InputConsumerNoResampling::InputConsumerNoResampling( InputConsumerNoResampling::~InputConsumerNoResampling() { ensureCalledOnLooperThread(__func__); - while (!mOutboundQueue.empty()) { - processOutboundEvents(); - // This is our last chance to ack the events. If we don't ack them here, we will get an ANR, - // so keep trying to send the events as long as they are present in the queue. - } - - setFdEvents(0); // If there are any remaining unread batches, send an ack for them and don't deliver // them to callbacks. for (auto& [_, batches] : mBatches) { @@ -208,6 +201,12 @@ InputConsumerNoResampling::~InputConsumerNoResampling() { batches.pop(); } } + + while (!mOutboundQueue.empty()) { + processOutboundEvents(); + // This is our last chance to ack the events. If we don't ack them here, we will get an ANR, + // so keep trying to send the events as long as they are present in the queue. + } // However, it is still up to the app to finish any events that have already been delivered // to the callbacks. If we wanted to change that behaviour and auto-finish all unfinished events // that were already sent to callbacks, we could potentially loop through "mConsumeTimes" @@ -216,6 +215,10 @@ InputConsumerNoResampling::~InputConsumerNoResampling() { const size_t unfinishedEvents = mConsumeTimes.size(); LOG_IF(INFO, unfinishedEvents != 0) << getName() << " has " << unfinishedEvents << " unfinished event(s)"; + // Remove the fd from epoll, so that Looper does not call 'handleReceiveCallback' anymore. + // This must be done at the end of the destructor; otherwise, some of the other functions may + // call 'setFdEvents' as a side-effect, thus adding the fd back to the epoll set of the looper. + setFdEvents(0); } int InputConsumerNoResampling::handleReceiveCallback(int events) { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 77dcaa9ef2..6a55726db1 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -583,15 +583,6 @@ status_t InputPublisher::publishMotionEvent( StringPrintf("publishMotionEvent(inputChannel=%s, action=%s)", mChannel->getName().c_str(), MotionEvent::actionToString(action).c_str())); - if (verifyEvents()) { - Result<void> result = - mInputVerifier.processMovement(deviceId, source, action, pointerCount, - pointerProperties, pointerCoords, flags); - if (!result.ok()) { - LOG(ERROR) << "Bad stream: " << result.error(); - return BAD_VALUE; - } - } if (debugTransportPublisher()) { std::string transformString; transform.dump(transformString, "transform", " "); @@ -657,8 +648,18 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.pointers[i].properties = pointerProperties[i]; msg.body.motion.pointers[i].coords = pointerCoords[i]; } + const status_t status = mChannel->sendMessage(&msg); - return mChannel->sendMessage(&msg); + if (status == OK && verifyEvents()) { + Result<void> result = + mInputVerifier.processMovement(deviceId, source, action, pointerCount, + pointerProperties, pointerCoords, flags); + if (!result.ok()) { + LOG(ERROR) << "Bad stream: " << result.error(); + return BAD_VALUE; + } + } + return status; } status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) { 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/InputConsumer_test.cpp b/libs/input/tests/InputConsumer_test.cpp index 06e19bb644..226b892fdc 100644 --- a/libs/input/tests/InputConsumer_test.cpp +++ b/libs/input/tests/InputConsumer_test.cpp @@ -70,11 +70,20 @@ protected: []() { return std::make_unique<LegacyResampler>(); }); } - void invokeLooperCallback() const { + bool invokeLooperCallback() const { sp<LooperCallback> callback; - ASSERT_TRUE(mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr, - /*events=*/nullptr, &callback, /*data=*/nullptr)); + const bool found = + mLooper->getFdStateDebug(mClientTestChannel->getFd(), /*ident=*/nullptr, + /*events=*/nullptr, &callback, /*data=*/nullptr); + if (!found) { + return false; + } + if (callback == nullptr) { + LOG(FATAL) << "Looper has the fd of interest, but the callback is null!"; + return false; + } callback->handleEvent(mClientTestChannel->getFd(), ALOOPER_EVENT_INPUT, /*data=*/nullptr); + return true; } void assertOnBatchedInputEventPendingWasCalled() { @@ -271,6 +280,27 @@ TEST_F(InputConsumerTest, UnhandledEventsNotFinishedInDestructor) { } /** + * Check what happens when looper invokes callback after consumer has been destroyed. + * This reproduces a crash where the LooperEventCallback was added back to the Looper during + * destructor, thus allowing the looper callback to be invoked onto a null consumer object. + */ +TEST_F(InputConsumerTest, LooperCallbackInvokedAfterConsumerDestroyed) { + mClientTestChannel->enqueueMessage( + InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/0}.action(ACTION_DOWN).build()); + mClientTestChannel->enqueueMessage( + InputMessageBuilder{InputMessage::Type::MOTION, /*seq=*/1}.action(ACTION_MOVE).build()); + ASSERT_TRUE(invokeLooperCallback()); + assertOnBatchedInputEventPendingWasCalled(); + assertReceivedMotionEvent(WithMotionAction(ACTION_DOWN)); + mClientTestChannel->assertFinishMessage(/*seq=*/0, /*handled=*/true); + + // Now, destroy the consumer and invoke the looper callback again after it's been destroyed. + mConsumer.reset(); + mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/false); + ASSERT_FALSE(invokeLooperCallback()); +} + +/** * Send an event to the InputConsumer, but do not invoke "consumeBatchedInputEvents", thus leaving * the input event unconsumed by the callbacks. Ensure that no crash occurs when the consumer is * destroyed. 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/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index ac3a832168..5ce4076476 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -222,6 +222,8 @@ int32_t ANativeWindow_setBuffersDataSpace(ANativeWindow* window, int32_t dataSpa static_cast<int>(HAL_DATASPACE_BT2020_ITU_HLG)); static_assert(static_cast<int>(ADATASPACE_DEPTH) == static_cast<int>(HAL_DATASPACE_DEPTH)); static_assert(static_cast<int>(ADATASPACE_DYNAMIC_DEPTH) == static_cast<int>(HAL_DATASPACE_DYNAMIC_DEPTH)); + static_assert(static_cast<int>(ADATASPACE_DISPLAY_BT2020) == + static_cast<int>(HAL_DATASPACE_DISPLAY_BT2020)); if (!window || !query(window, NATIVE_WINDOW_IS_VALID)) { return -EINVAL; diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index 8056d9ac4f..295a307c3c 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -578,6 +578,13 @@ enum ADataSpace : int32_t { */ ADATASPACE_BT2020_ITU_HLG = 302383104, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_HLG | // ADATASPACE_RANGE_LIMITED + /** + * sRGB-encoded BT. 2020 + * + * Uses full range, sRGB transfer and BT2020 standard. + */ + ADATASPACE_DISPLAY_BT2020 = 142999552, // ADATASPACE_STANDARD_BT2020 | ADATASPACE_TRANSFER_SRGB + // | ADATASPACE_RANGE_FULL /** * Depth 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/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 5c46c9168d..a93f6c36ff 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -544,7 +544,8 @@ sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( } if (graphicBuffer && parameters.layer.luts) { - shader = mLutShader.lutShader(shader, parameters.layer.luts); + shader = mLutShader.lutShader(shader, parameters.layer.luts, + toSkColorSpace(parameters.outputDataSpace)); } if (parameters.requiresLinearEffect) { diff --git a/libs/renderengine/skia/filters/LutShader.cpp b/libs/renderengine/skia/filters/LutShader.cpp index cea46ef40e..1e43ff3cd3 100644 --- a/libs/renderengine/skia/filters/LutShader.cpp +++ b/libs/renderengine/skia/filters/LutShader.cpp @@ -169,7 +169,8 @@ sk_sp<SkShader> LutShader::generateLutShader(sk_sp<SkShader> input, } sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input, - std::shared_ptr<gui::DisplayLuts> displayLuts) { + std::shared_ptr<gui::DisplayLuts> displayLuts, + sk_sp<SkColorSpace> outColorSpace) { if (mBuilder == nullptr) { const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(kShader); mBuilder = std::make_unique<SkRuntimeShaderBuilder>(instance.effect); @@ -179,14 +180,11 @@ sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input, if (fd.ok()) { // de-gamma the image without changing the primaries SkImage* baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr); - if (baseImage) { - sk_sp<SkColorSpace> baseColorSpace = - baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB(); - sk_sp<SkColorSpace> gainmapMathColorSpace = baseColorSpace->makeLinearGamma(); - auto colorXformSdrToGainmap = - SkColorFilterPriv::MakeColorSpaceXform(baseColorSpace, gainmapMathColorSpace); - input = input->makeWithColorFilter(colorXformSdrToGainmap); - } + sk_sp<SkColorSpace> baseColorSpace = baseImage && baseImage->colorSpace() + ? baseImage->refColorSpace() + : SkColorSpace::MakeSRGB(); + sk_sp<SkColorSpace> lutMathColorSpace = baseColorSpace->makeLinearGamma(); + input = input->makeWithWorkingColorSpace(lutMathColorSpace); auto& offsets = displayLuts->offsets; auto& lutProperties = displayLuts->lutProperties; @@ -223,16 +221,9 @@ sk_sp<SkShader> LutShader::lutShader(sk_sp<SkShader>& input, lutProperties[i].samplingKey); } - // re-gamma - baseImage = input->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr); - if (baseImage) { - sk_sp<SkColorSpace> baseColorSpace = - baseImage->colorSpace() ? baseImage->refColorSpace() : SkColorSpace::MakeSRGB(); - sk_sp<SkColorSpace> gainmapMathColorSpace = baseColorSpace->makeLinearGamma(); - auto colorXformGainmapToDst = - SkColorFilterPriv::MakeColorSpaceXform(gainmapMathColorSpace, baseColorSpace); - input = input->makeWithColorFilter(colorXformGainmapToDst); - } + auto colorXformLutToDst = + SkColorFilterPriv::MakeColorSpaceXform(lutMathColorSpace, outColorSpace); + input = input->makeWithColorFilter(colorXformLutToDst); } return input; } diff --git a/libs/renderengine/skia/filters/LutShader.h b/libs/renderengine/skia/filters/LutShader.h index c157904b63..ce3e0592a1 100644 --- a/libs/renderengine/skia/filters/LutShader.h +++ b/libs/renderengine/skia/filters/LutShader.h @@ -28,8 +28,8 @@ namespace skia { class LutShader { public: - sk_sp<SkShader> lutShader(sk_sp<SkShader>& input, - std::shared_ptr<gui::DisplayLuts> displayLuts); + sk_sp<SkShader> lutShader(sk_sp<SkShader>& input, std::shared_ptr<gui::DisplayLuts> displayLuts, + sk_sp<SkColorSpace> outColorSpace); private: sk_sp<SkShader> generateLutShader(sk_sp<SkShader> input, const std::vector<float>& buffers, 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/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index 19594233fa..54270eb340 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -99,6 +99,8 @@ std::string GestureConverter::dump() const { out << "Current classification: " << ftl::enum_string(mCurrentClassification) << "\n"; out << "Is hovering: " << mIsHovering << "\n"; out << "Enable Tap Timestamp: " << mWhenToEnableTapToClick << "\n"; + out << "Three finger tap shortcut enabled: " + << (mThreeFingerTapShortcutEnabled ? "enabled" : "disabled") << "\n"; return out.str(); } 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/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp index f5c4fc547c..ae84d7b133 100644 --- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp +++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp @@ -30,6 +30,8 @@ using aidl::android::hardware::power::Boost; using aidl::android::hardware::power::ChannelConfig; +using aidl::android::hardware::power::CpuHeadroomParams; +using aidl::android::hardware::power::GpuHeadroomParams; using aidl::android::hardware::power::IPower; using aidl::android::hardware::power::IPowerHintSession; using aidl::android::hardware::power::Mode; @@ -71,6 +73,14 @@ public: MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override)); MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override)); MOCK_METHOD(bool, isRemote, (), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getCpuHeadroom, + (const CpuHeadroomParams& params, std::vector<float>* headroom), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroom, + (const GpuHeadroomParams& params, float* headroom), (override)); + MOCK_METHOD(ndk::ScopedAStatus, getCpuHeadroomMinIntervalMillis, (int64_t* interval), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* interval), + (override)); }; // ------------------------------------------------------------------------------------------------- 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/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index c13e444a99..86d7388f5b 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -29,6 +29,7 @@ #include <cinttypes> #include <numeric> #include <unordered_set> +#include <vector> #include "../Jank/JankTracker.h" @@ -1002,6 +1003,11 @@ void FrameTimeline::setSfPresent(nsecs_t sfPresentTime, finalizeCurrentDisplayFrame(); } +const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& FrameTimeline::getPresentFrames() + const { + return mPresentFrames; +} + void FrameTimeline::onCommitNotComposited() { SFTRACE_CALL(); std::scoped_lock lock(mMutex); @@ -1521,6 +1527,7 @@ void FrameTimeline::flushPendingPresentFences() { mPendingPresentFences.erase(mPendingPresentFences.begin()); } + mPresentFrames.clear(); for (size_t i = 0; i < mPendingPresentFences.size(); i++) { const auto& pendingPresentFence = mPendingPresentFences[i]; nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; @@ -1533,6 +1540,13 @@ void FrameTimeline::flushPendingPresentFences() { auto& displayFrame = pendingPresentFence.second; displayFrame->onPresent(signalTime, mPreviousActualPresentTime); + + // Surface frames have been jank classified and can be provided to caller + // to detect if buffer stuffing is occurring. + for (const auto& frame : displayFrame->getSurfaceFrames()) { + mPresentFrames.push_back(frame); + } + mPreviousPredictionPresentTime = displayFrame->trace(mSurfaceFlingerPid, monoBootOffset, mPreviousPredictionPresentTime, mFilterFramesBeforeTraceStarts); diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index 6cda309440..a47bd573de 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -328,6 +328,11 @@ public: virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, const std::shared_ptr<FenceTime>& gpuFence) = 0; + // Provides surface frames that have already been jank classified in the most recent + // flush of pending present fences. This allows buffer stuffing detection from SF. + virtual const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames() + const = 0; + // Tells FrameTimeline that a frame was committed but not composited. This is used to flush // all the associated surface frames. virtual void onCommitNotComposited() = 0; @@ -505,6 +510,8 @@ public: void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override; void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence, const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override; + const std::vector<std::shared_ptr<frametimeline::SurfaceFrame>>& getPresentFrames() + const override; void onCommitNotComposited() override; void parseArgs(const Vector<String16>& args, std::string& result) override; void setMaxDisplayFrames(uint32_t size) override; @@ -552,6 +559,9 @@ private: // display frame, this is a good starting size for the vector so that we can avoid the // internal vector resizing that happens with push_back. static constexpr uint32_t kNumSurfaceFramesInitial = 10; + // Presented surface frames that have been jank classified and can + // indicate of potential buffer stuffing. + std::vector<std::shared_ptr<frametimeline::SurfaceFrame>> mPresentFrames; }; } // namespace impl 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/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 7729671401..fff4284199 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -44,10 +44,8 @@ #include <common/FlagManager.h> #include <scheduler/VsyncConfig.h> -#include "DisplayHardware/DisplayMode.h" #include "FrameTimeline.h" #include "VSyncDispatch.h" -#include "VSyncTracker.h" #include "EventThread.h" @@ -482,6 +480,14 @@ void EventThread::onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t conne mCondition.notify_all(); } +// Merge lists of buffer stuffed Uids +void EventThread::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) { + std::lock_guard<std::mutex> lock(mMutex); + for (auto& [uid, count] : bufferStuffedUids) { + mBufferStuffedUids.emplace_or_replace(uid, count); + } +} + void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { DisplayEventConsumers consumers; @@ -721,6 +727,10 @@ void EventThread::generateFrameTimeline(VsyncEventData& outVsyncEventData, nsecs void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, const DisplayEventConsumers& consumers) { + // List of Uids that have been sent vsync data with queued buffer count. + // Used to keep track of which Uids can be removed from the map of + // buffer stuffed clients. + ftl::SmallVector<uid_t, 10> uidsPostedQueuedBuffers; for (const auto& consumer : consumers) { DisplayEventReceiver::Event copy = event; if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { @@ -730,6 +740,13 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, event.vsync.vsyncData.preferredExpectedPresentationTime(), event.vsync.vsyncData.preferredDeadlineTimestamp()); } + auto it = mBufferStuffedUids.find(consumer->mOwnerUid); + if (it != mBufferStuffedUids.end()) { + copy.vsync.vsyncData.numberQueuedBuffers = it->second; + uidsPostedQueuedBuffers.emplace_back(consumer->mOwnerUid); + } else { + copy.vsync.vsyncData.numberQueuedBuffers = 0; + } switch (consumer->postEvent(copy)) { case NO_ERROR: break; @@ -745,6 +762,12 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, removeDisplayEventConnectionLocked(consumer); } } + // The clients that have already received the queued buffer count + // can be removed from the buffer stuffed Uid list to avoid + // being sent duplicate messages. + for (auto uid : uidsPostedQueuedBuffers) { + mBufferStuffedUids.erase(uid); + } if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC && FlagManager::getInstance().vrr_config()) { mLastCommittedVsyncTime = diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 2daf126d77..95632c7e66 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -32,7 +32,6 @@ #include <thread> #include <vector> -#include "DisplayHardware/DisplayMode.h" #include "TracedOrdinal.h" #include "VSyncDispatch.h" #include "VsyncSchedule.h" @@ -55,6 +54,7 @@ using gui::VsyncEventData; // --------------------------------------------------------------------------- using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride; +using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>; enum class VSyncRequest { None = -2, @@ -136,6 +136,10 @@ public: virtual void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) = 0; + + // An elevated number of queued buffers in the server is detected. This propagates a + // flag to Choreographer indicating that buffer stuffing recovery should begin. + virtual void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids); }; struct IEventThreadCallback { @@ -188,6 +192,8 @@ public: void onHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel) override; + void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) override; + private: friend EventThreadTest; @@ -228,6 +234,10 @@ private: scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex); frametimeline::TokenManager* const mTokenManager; + // All consumers that need to recover from buffer stuffing and the number + // of their queued buffers. + BufferStuffingMap mBufferStuffedUids GUARDED_BY(mMutex); + IEventThreadCallback& mCallback; std::thread mThread; 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 274e121a79..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); @@ -951,6 +955,11 @@ bool Scheduler::updateFrameRateOverridesLocked(GlobalSignals consideredSignals, return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides); } +void Scheduler::addBufferStuffedUids(BufferStuffingMap bufferStuffedUids) { + if (!mRenderEventThread) return; + mRenderEventThread->addBufferStuffedUids(std::move(bufferStuffedUids)); +} + void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) { std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule; { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index e77af609e2..61c68a9473 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -42,7 +42,6 @@ #include <ui/DisplayId.h> #include <ui/DisplayMap.h> -#include "Display/DisplayModeRequest.h" #include "EventThread.h" #include "FrameRateOverrideMappings.h" #include "ISchedulerCallback.h" @@ -334,6 +333,12 @@ public: mPacesetterFrameDurationFractionToSkip = frameDurationFraction; } + // Propagates a flag to the EventThread indicating that buffer stuffing + // recovery should begin. + void addBufferStuffedUids(BufferStuffingMap bufferStuffedUids); + + void setDebugPresentDelay(TimePoint delay) { mDebugPresentDelay = delay; } + private: friend class TestableScheduler; @@ -599,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 0d03a6cef3..37b35f2f63 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -64,11 +64,13 @@ #include <ftl/concat.h> #include <ftl/fake_guard.h> #include <ftl/future.h> +#include <ftl/small_map.h> #include <ftl/unit.h> #include <gui/AidlUtil.h> #include <gui/BufferQueue.h> #include <gui/DebugEGLImageTracker.h> #include <gui/IProducerListener.h> +#include <gui/JankInfo.h> #include <gui/LayerMetadata.h> #include <gui/LayerState.h> #include <gui/Surface.h> @@ -1418,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()); @@ -1435,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). @@ -2250,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( @@ -2597,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)) { @@ -2700,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(); } @@ -3072,12 +3061,40 @@ void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, const TimePoint presentTime = TimePoint::now(); + // The Uids of layer owners that are in buffer stuffing mode, and their elevated + // buffer counts. Messages to start recovery are sent exclusively to these Uids. + BufferStuffingMap bufferStuffedUids; + // Set presentation information before calling Layer::releasePendingBuffer, such that jank // information from previous' frame classification is already available when sending jank info // to clients, so they get jank classification as early as possible. mFrameTimeline->setSfPresent(presentTime.ns(), pacesetterPresentFenceTime, pacesetterGpuCompositionDoneFenceTime); + // Find and register any layers that are in buffer stuffing mode + const auto& presentFrames = mFrameTimeline->getPresentFrames(); + + for (const auto& frame : presentFrames) { + const auto& layer = mLayerLifecycleManager.getLayerFromId(frame->getLayerId()); + if (!layer) continue; + uint32_t numberQueuedBuffers = layer->pendingBuffers ? layer->pendingBuffers->load() : 0; + int32_t jankType = frame->getJankType().value_or(JankType::None); + if (jankType & JankType::BufferStuffing && + layer->flags & layer_state_t::eRecoverableFromBufferStuffing) { + auto [it, wasEmplaced] = + bufferStuffedUids.try_emplace(layer->ownerUid.val(), numberQueuedBuffers); + // Update with maximum number of queued buffers, allows clients drawing + // multiple windows to account for the most severely stuffed window + if (!wasEmplaced && it->second < numberQueuedBuffers) { + it->second = numberQueuedBuffers; + } + } + } + + if (!bufferStuffedUids.empty()) { + mScheduler->addBufferStuffedUids(std::move(bufferStuffedUids)); + } + // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might // be sampled a little later than when we started doing work for this frame, // but that should be okay since CompositorTiming has snapping logic. @@ -3464,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; } @@ -6233,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; } @@ -6766,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/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index d3479b7e52..7e9d5b881f 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1370,6 +1370,8 @@ private: // Flag used to set override desired display mode from backdoor bool mDebugDisplayModeSetByBackdoor = false; + // Tracks the number of maximum queued buffers by layer owner Uid. + using BufferStuffingMap = ftl::SmallMap<uid_t, uint32_t, 10>; BufferCountTracker mBufferCountTracker; std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners 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/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 625d2e68d8..268a6c416d 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -23,6 +23,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <gui/DisplayEventReceiver.h> #include <log/log.h> #include <scheduler/VsyncConfig.h> #include <utils/Errors.h> @@ -111,6 +112,8 @@ protected: void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime); void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, std::vector<FrameRateOverride>); + void expectQueuedBufferCountReceivedByConnection( + ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount); void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedPresentationTime, nsecs_t deadlineTimestamp) { @@ -144,6 +147,7 @@ protected: sp<MockEventThreadConnection> mConnection; sp<MockEventThreadConnection> mThrottledConnection; std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; + std::vector<ConnectionEventRecorder*> mBufferStuffedConnectionRecorders; std::chrono::nanoseconds mVsyncPeriod; @@ -376,6 +380,14 @@ void EventThreadTest::expectUidFrameRateMappingEventReceivedByConnection( EXPECT_EQ(expectedDisplayId, event.header.displayId); } +void EventThreadTest::expectQueuedBufferCountReceivedByConnection( + ConnectionEventRecorder& connectionEventRecorder, uint32_t expectedBufferCount) { + auto args = connectionEventRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + const auto& event = std::get<0>(args.value()); + EXPECT_EQ(expectedBufferCount, event.vsync.vsyncData.numberQueuedBuffers); +} + namespace { using namespace testing; @@ -868,6 +880,63 @@ TEST_F(EventThreadTest, postHcpLevelsChanged) { EXPECT_EQ(HDCP_V2, event.hdcpLevelsChange.maxLevel); } +TEST_F(EventThreadTest, connectionReceivesBufferStuffing) { + setupEventThread(); + + // Create a connection that will experience buffer stuffing. + ConnectionEventRecorder stuffedConnectionEventRecorder{0}; + sp<MockEventThreadConnection> stuffedConnection = + createConnection(stuffedConnectionEventRecorder, + gui::ISurfaceComposer::EventRegistration::modeChanged | + gui::ISurfaceComposer::EventRegistration::frameRateOverride, + 111); + + // Add a connection and buffer count to the list of stuffed Uids that will receive + // data in the next vsync event. + BufferStuffingMap bufferStuffedUids; + bufferStuffedUids.try_emplace(stuffedConnection->mOwnerUid, 3); + mThread->addBufferStuffedUids(bufferStuffedUids); + mBufferStuffedConnectionRecorders.emplace_back(&stuffedConnectionEventRecorder); + + // Signal that we want the next vsync event to be posted to two connections. + mThread->requestNextVsync(mConnection); + mThread->requestNextVsync(stuffedConnection); + onVSyncEvent(123, 456, 789); + + // Vsync event data contains number of queued buffers. + expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 0); + expectQueuedBufferCountReceivedByConnection(stuffedConnectionEventRecorder, 3); +} + +TEST_F(EventThreadTest, connectionsWithSameUidReceiveBufferStuffing) { + setupEventThread(); + + // Create a connection with the same Uid as another connection. + ConnectionEventRecorder secondConnectionEventRecorder{0}; + sp<MockEventThreadConnection> secondConnection = + createConnection(secondConnectionEventRecorder, + gui::ISurfaceComposer::EventRegistration::modeChanged | + gui::ISurfaceComposer::EventRegistration::frameRateOverride, + mConnectionUid); + + // Add connection Uid and buffer count to the list of stuffed Uids that will receive + // data in the next vsync event. + BufferStuffingMap bufferStuffedUids; + bufferStuffedUids.try_emplace(mConnectionUid, 3); + mThread->addBufferStuffedUids(bufferStuffedUids); + mBufferStuffedConnectionRecorders.emplace_back(&mConnectionEventCallRecorder); + mBufferStuffedConnectionRecorders.emplace_back(&secondConnectionEventRecorder); + + // Signal that we want the next vsync event to be posted to two connections. + mThread->requestNextVsync(mConnection); + mThread->requestNextVsync(secondConnection); + onVSyncEvent(123, 456, 789); + + // Vsync event data contains number of queued buffers. + expectQueuedBufferCountReceivedByConnection(mConnectionEventCallRecorder, 3); + expectQueuedBufferCountReceivedByConnection(secondConnectionEventRecorder, 3); +} + } // namespace } // namespace android 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/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 82500fef10..c976697c08 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -53,6 +53,7 @@ public: MOCK_METHOD(void, onHdcpLevelsChanged, (PhysicalDisplayId displayId, int32_t connectedLevel, int32_t maxLevel), (override)); + MOCK_METHOD(void, addBufferStuffedUids, (BufferStuffingMap), (override)); }; } // namespace android::mock 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 +} |