Merge "InputReader: Notify policy of device changes after flushing events" into main
diff --git a/cmds/evemu-record/README.md b/cmds/evemu-record/README.md
new file mode 100644
index 0000000..5d16d51
--- /dev/null
+++ b/cmds/evemu-record/README.md
@@ -0,0 +1,48 @@
+# `evemu-record`
+
+This is a Rust implementation of the `evemu-record` command from the [FreeDesktop project's evemu
+suite][FreeDesktop]. It records the descriptor and events produced by a single input device in a
+[simple text-based format][format] that can be replayed using the [`uinput` command on
+Android][uinput] or the FreeDesktop evemu tools on other Linux-based platforms. It is included by
+default with `userdebug` and `eng` builds of Android.
+
+The command-line interface is the same as that of the FreeDesktop version, except for
+Android-specific features. For usage instructions, run `evemu-record --help`.
+
+## Usage example
+
+From a computer connected to the device over ADB, you can start a recording:
+
+```
+$ adb shell evemu-record > my-recording.evemu
+Available devices:
+/dev/input/event0: gpio_keys
+/dev/input/event1: s2mpg12-power-keys
+/dev/input/event2: NVTCapacitiveTouchScreen
+/dev/input/event3: NVTCapacitivePen
+/dev/input/event4: uinput-folio
+/dev/input/event5: ACME Touchpad
+Select the device event number [0-5]: 5
+```
+
+...then use the input device for a while, and press Ctrl+C to finish. You will now have a
+`my-recording.evemu` file that you can examine in a text editor. To replay it, use the [`uinput`
+command][uinput]:
+
+```
+$ adb shell uinput - < my-recording.evemu
+```
+
+## Android-specific features
+
+### Timestamp bases
+
+By default, event timestamps are recorded relative to the time of the first event received during
+the recording. Passing `--timestamp-base=boot` causes the timestamps to be recorded relative to the
+system boot time instead. While this does not affect the playback of the recording, it can be useful
+for matching recorded events with other logs that use such timestamps, such as `dmesg` or the
+touchpad gesture debug logs emitted by `TouchpadInputMapper`.
+
+[FreeDesktop]: https://gitlab.freedesktop.org/libevdev/evemu
+[format]: https://gitlab.freedesktop.org/libevdev/evemu#device-description-format
+[uinput]: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/cmds/uinput/README.md
diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml
index 855b110..4c9932d 100644
--- a/data/etc/wearable_core_hardware.xml
+++ b/data/etc/wearable_core_hardware.xml
@@ -36,6 +36,7 @@
<feature name="android.hardware.security.model.compatible" />
<!-- basic system services -->
+ <feature name="android.software.credentials" />
<feature name="android.software.home_screen" />
<feature name="android.software.secure_lock_screen" />
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 19693e3..fb69fda 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -887,6 +887,9 @@
int callbackTicket = 0;
uint64_t currentFrameNumber = 0;
BufferItem item;
+ int connectedApi;
+ sp<Fence> lastQueuedFence;
+
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
@@ -1056,6 +1059,13 @@
callbackTicket = mNextCallbackTicket++;
VALIDATE_CONSISTENCY();
+
+ connectedApi = mCore->mConnectedApi;
+ lastQueuedFence = std::move(mLastQueueBufferFence);
+
+ mLastQueueBufferFence = std::move(acquireFence);
+ mLastQueuedCrop = item.mCrop;
+ mLastQueuedTransform = item.mTransform;
} // Autolock scope
// It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because
@@ -1079,9 +1089,6 @@
// Call back without the main BufferQueue lock held, but with the callback
// lock held so we can ensure that callbacks occur in order
- int connectedApi;
- sp<Fence> lastQueuedFence;
-
{ // scope for the lock
std::unique_lock<std::mutex> lock(mCallbackMutex);
while (callbackTicket != mCurrentCallbackTicket) {
@@ -1094,13 +1101,6 @@
frameReplacedListener->onFrameReplaced(item);
}
- connectedApi = mCore->mConnectedApi;
- lastQueuedFence = std::move(mLastQueueBufferFence);
-
- mLastQueueBufferFence = std::move(acquireFence);
- mLastQueuedCrop = item.mCrop;
- mLastQueuedTransform = item.mTransform;
-
++mCurrentCallbackTicket;
mCallbackCondition.notify_all();
}
@@ -1653,9 +1653,10 @@
status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence, float outTransformMatrix[16]) {
ATRACE_CALL();
- BQ_LOGV("getLastQueuedBuffer");
std::lock_guard<std::mutex> lock(mCore->mMutex);
+ BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot);
+
if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) {
*outBuffer = nullptr;
*outFence = Fence::NO_FENCE;
@@ -1679,10 +1680,11 @@
status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence,
Rect* outRect, uint32_t* outTransform) {
ATRACE_CALL();
- BQ_LOGV("getLastQueuedBuffer");
std::lock_guard<std::mutex> lock(mCore->mMutex);
- if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) {
+ BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot);
+ if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT ||
+ mSlots[mCore->mLastQueuedSlot].mBufferState.isDequeued()) {
*outBuffer = nullptr;
*outFence = Fence::NO_FENCE;
return NO_ERROR;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 9494ee5..fd45840 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -48,6 +48,7 @@
static_libs: [
"libshaders",
"libtonemap",
+ "libsurfaceflinger_common",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index 55c34cd..87e21c2 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -37,6 +37,7 @@
static_libs: [
"librenderengine",
"libshaders",
+ "libsurfaceflinger_common",
"libtonemap",
],
cflags: [
@@ -54,6 +55,7 @@
"libsync",
"libui",
"libutils",
+ "server_configurable_flags",
],
data: ["resources/*"],
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 330cc66..6e393f0 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -53,6 +53,7 @@
#include <SkSurface.h>
#include <SkTileMode.h>
#include <android-base/stringprintf.h>
+#include <common/FlagManager.h>
#include <gui/FenceMonitor.h>
#include <gui/TraceUtils.h>
#include <include/gpu/ganesh/SkSurfaceGanesh.h>
@@ -419,6 +420,9 @@
mGraphicBufferExternalRefs[buffer->getId()]++;
if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) {
+ if (FlagManager::getInstance().renderable_buffer_usage()) {
+ isRenderable = buffer->getUsage() & GRALLOC_USAGE_HW_RENDER;
+ }
std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
std::make_shared<AutoBackendTexture::LocalRef>(grContext,
buffer->toAHardwareBuffer(),
@@ -760,10 +764,11 @@
// save a snapshot of the activeSurface to use as input to the blur shaders
blurInput = activeSurface->makeImageSnapshot();
- // blit the offscreen framebuffer into the destination AHB, but only
- // if there are blur regions. backgroundBlurRadius blurs the entire
- // image below, so it can skip this step.
- if (layer.blurRegions.size()) {
+ // blit the offscreen framebuffer into the destination AHB. This ensures that
+ // even if the blurred image does not cover the screen (for example, during
+ // a rotation animation, or if blur regions are used), the entire screen is
+ // initialized.
+ if (layer.blurRegions.size() || FlagManager::getInstance().restore_blur_step()) {
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
if (CC_UNLIKELY(mCapture->isCaptureRunning())) {
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 50e166d..473e1d4 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -44,6 +44,7 @@
"librenderengine_mocks",
"libshaders",
"libtonemap",
+ "libsurfaceflinger_common",
],
header_libs: [
"libtonemap_headers",
@@ -61,5 +62,6 @@
"libsync",
"libui",
"libutils",
+ "server_configurable_flags",
],
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 77c2222..aeab1f8 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -804,6 +804,20 @@
}
}
+std::pair<bool /*cancelPointers*/, bool /*cancelNonPointers*/> expandCancellationMode(
+ CancelationOptions::Mode mode) {
+ switch (mode) {
+ case CancelationOptions::Mode::CANCEL_ALL_EVENTS:
+ return {true, true};
+ case CancelationOptions::Mode::CANCEL_POINTER_EVENTS:
+ return {true, false};
+ case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS:
+ return {false, true};
+ case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS:
+ return {false, true};
+ }
+}
+
} // namespace
// --- InputDispatcher ---
@@ -2078,7 +2092,9 @@
if (connection->status == Connection::Status::NORMAL) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS,
"application not responding");
- synthesizeCancelationEventsForConnectionLocked(connection, options);
+ synthesizeCancelationEventsForConnectionLocked(connection, options,
+ getWindowHandleLocked(
+ connection->getToken()));
}
}
@@ -3328,7 +3344,13 @@
void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection,
std::shared_ptr<const EventEntry> eventEntry,
const InputTarget& inputTarget) {
- // TODO(b/210460522): Verify all targets excluding global monitors are associated with a window.
+ const bool isKeyOrMotion = eventEntry->type == EventEntry::Type::KEY ||
+ eventEntry->type == EventEntry::Type::MOTION;
+ if (isKeyOrMotion && !inputTarget.windowHandle && !connection->monitor) {
+ LOG(FATAL) << "All InputTargets for non-monitors must be associated with a window; target: "
+ << inputTarget << " connection: " << connection->getInputChannelName()
+ << " entry: " << eventEntry->getDescription();
+ }
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
std::unique_ptr<DispatchEntry> dispatchEntry =
@@ -3977,22 +3999,78 @@
void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked(
const CancelationOptions& options) {
- for (const auto& [token, connection] : mConnectionsByToken) {
- synthesizeCancelationEventsForConnectionLocked(connection, options);
+ // Cancel windows (i.e. non-monitors).
+ // A channel must have at least one window to receive any input. If a window was removed, the
+ // event streams directed to the window will already have been canceled during window removal.
+ // So there is no need to generate cancellations for connections without any windows.
+ const auto [cancelPointers, cancelNonPointers] = expandCancellationMode(options.mode);
+ // Generate cancellations for touched windows first. This is to avoid generating cancellations
+ // through a non-touched window if there are more than one window for an input channel.
+ if (cancelPointers) {
+ for (const auto& [displayId, touchState] : mTouchStatesByDisplay) {
+ if (options.displayId.has_value() && options.displayId != displayId) {
+ continue;
+ }
+ for (const auto& touchedWindow : touchState.windows) {
+ synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options);
+ }
+ }
}
+ // Follow up by generating cancellations for all windows, because we don't explicitly track
+ // the windows that have an ongoing focus event stream.
+ if (cancelNonPointers) {
+ for (const auto& [_, handles] : mWindowHandlesByDisplay) {
+ for (const auto& windowHandle : handles) {
+ synthesizeCancelationEventsForWindowLocked(windowHandle, options);
+ }
+ }
+ }
+
+ // Cancel monitors.
+ synthesizeCancelationEventsForMonitorsLocked(options);
}
void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked(
const CancelationOptions& options) {
for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) {
for (const Monitor& monitor : monitors) {
- synthesizeCancelationEventsForConnectionLocked(monitor.connection, options);
+ synthesizeCancelationEventsForConnectionLocked(monitor.connection, options,
+ /*window=*/nullptr);
}
}
}
+void InputDispatcher::synthesizeCancelationEventsForWindowLocked(
+ const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options,
+ const std::shared_ptr<Connection>& connection) {
+ if (windowHandle == nullptr) {
+ LOG(FATAL) << __func__ << ": Window handle must not be null";
+ }
+ if (connection) {
+ // The connection can be optionally provided to avoid multiple lookups.
+ if (windowHandle->getToken() != connection->getToken()) {
+ LOG(FATAL) << __func__
+ << ": Wrong connection provided for window: " << windowHandle->getName();
+ }
+ }
+
+ std::shared_ptr<Connection> resolvedConnection =
+ connection ? connection : getConnectionLocked(windowHandle->getToken());
+ if (!resolvedConnection) {
+ LOG(DEBUG) << __func__ << "No connection found for window: " << windowHandle->getName();
+ return;
+ }
+ synthesizeCancelationEventsForConnectionLocked(resolvedConnection, options, windowHandle);
+}
+
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
- const std::shared_ptr<Connection>& connection, const CancelationOptions& options) {
+ const std::shared_ptr<Connection>& connection, const CancelationOptions& options,
+ const sp<WindowInfoHandle>& window) {
+ if (!connection->monitor && window == nullptr) {
+ LOG(FATAL) << __func__
+ << ": Cannot send event to non-monitor channel without a window - channel: "
+ << connection->getInputChannelName();
+ }
if (connection->status != Connection::Status::NORMAL) {
return;
}
@@ -4029,10 +4107,7 @@
switch (cancelationEventEntry->type) {
case EventEntry::Type::KEY: {
const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry);
- const std::optional<int32_t> targetDisplay = keyEntry.displayId != ADISPLAY_ID_NONE
- ? std::make_optional(keyEntry.displayId)
- : std::nullopt;
- if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) {
+ if (window) {
addWindowTargetLocked(window, InputTarget::DispatchMode::AS_IS,
/*targetFlags=*/{}, keyEntry.downTime, targets);
} else {
@@ -4043,11 +4118,7 @@
}
case EventEntry::Type::MOTION: {
const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry);
- const std::optional<int32_t> targetDisplay =
- motionEntry.displayId != ADISPLAY_ID_NONE
- ? std::make_optional(motionEntry.displayId)
- : std::nullopt;
- if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) {
+ if (window) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.getPointerCount();
pointerIndex++) {
@@ -4175,17 +4246,6 @@
}
}
-void InputDispatcher::synthesizeCancelationEventsForWindowLocked(
- const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options) {
- if (windowHandle != nullptr) {
- std::shared_ptr<Connection> wallpaperConnection =
- getConnectionLocked(windowHandle->getToken());
- if (wallpaperConnection != nullptr) {
- synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options);
- }
- }
-}
-
std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
const MotionEntry& originalMotionEntry, std::bitset<MAX_POINTER_ID + 1> pointerIds,
nsecs_t splitDownTime) {
@@ -5195,6 +5255,7 @@
// Copy old handles for release if they are no longer present.
const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
+ const sp<WindowInfoHandle> removedFocusedWindowHandle = getFocusedWindowHandleLocked(displayId);
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
@@ -5203,7 +5264,7 @@
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setInputWindows(displayId, windowHandles);
if (changes) {
- onFocusChangedLocked(*changes);
+ onFocusChangedLocked(*changes, removedFocusedWindowHandle);
}
std::unordered_map<int32_t, TouchState>::iterator stateIt =
@@ -5215,19 +5276,16 @@
if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) {
LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName()
<< " in display %" << displayId;
- std::shared_ptr<Connection> touchedConnection =
- getConnectionLocked(touchedWindow.windowHandle->getToken());
- if (touchedConnection != nullptr) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "touched window was removed");
- synthesizeCancelationEventsForConnectionLocked(touchedConnection, options);
- // Since we are about to drop the touch, cancel the events for the wallpaper as
- // well.
- if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) &&
- touchedWindow.windowHandle->getInfo()->inputConfig.test(
- gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
- sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow();
- synthesizeCancelationEventsForWindowLocked(wallpaper, options);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ "touched window was removed");
+ synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options);
+ // Since we are about to drop the touch, cancel the events for the wallpaper as
+ // well.
+ if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) &&
+ touchedWindow.windowHandle->getInfo()->inputConfig.test(
+ gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
+ if (const auto& ww = state.getWallpaperWindow(); ww) {
+ synthesizeCancelationEventsForWindowLocked(ww, options);
}
}
state.windows.erase(state.windows.begin() + i);
@@ -5325,14 +5383,16 @@
sp<IBinder> oldFocusedWindowToken =
mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (oldFocusedWindowToken != nullptr) {
- std::shared_ptr<Connection> connection = getConnectionLocked(oldFocusedWindowToken);
- if (connection != nullptr) {
- CancelationOptions
- options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
- "The display which contains this window no longer has focus.");
- options.displayId = ADISPLAY_ID_NONE;
- synthesizeCancelationEventsForConnectionLocked(connection, options);
+ const auto windowHandle =
+ getWindowHandleLocked(oldFocusedWindowToken, mFocusedDisplayId);
+ if (windowHandle == nullptr) {
+ LOG(FATAL) << __func__ << ": Previously focused token did not have a window";
}
+ CancelationOptions
+ options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
+ "The display which contains this window no longer has focus.");
+ options.displayId = ADISPLAY_ID_NONE;
+ synthesizeCancelationEventsForWindowLocked(windowHandle, options);
}
mFocusedDisplayId = displayId;
@@ -5512,9 +5572,10 @@
}
const int32_t deviceId = *deviceIds.begin();
- sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
- if (toWindowHandle == nullptr) {
- ALOGW("Cannot transfer touch because to window not found.");
+ const sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
+ const sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
+ if (!toWindowHandle) {
+ ALOGW("Cannot transfer touch because the transfer target window was not found.");
return false;
}
@@ -5527,7 +5588,6 @@
// Erase old window.
ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags;
std::vector<PointerProperties> pointers = touchedWindow->getTouchingPointers(deviceId);
- sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
state->removeWindowByToken(fromToken);
// Add new window.
@@ -5559,7 +5619,7 @@
fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
"transferring touch from this window to another window");
- synthesizeCancelationEventsForConnectionLocked(fromConnection, options);
+ synthesizeCancelationEventsForWindowLocked(fromWindowHandle, options, fromConnection);
synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection,
newTargetFlags);
@@ -6041,12 +6101,10 @@
std::string canceledWindows;
for (const TouchedWindow& w : state.windows) {
- const std::shared_ptr<Connection> connection =
- getConnectionLocked(w.windowHandle->getToken());
- if (connection != nullptr && connection->getToken() != token) {
- synthesizeCancelationEventsForConnectionLocked(connection, options);
+ if (w.windowHandle->getToken() != token) {
+ synthesizeCancelationEventsForWindowLocked(w.windowHandle, options);
canceledWindows += canceledWindows.empty() ? "[" : ", ";
- canceledWindows += connection->getInputChannelName();
+ canceledWindows += w.windowHandle->getName();
}
}
canceledWindows += canceledWindows.empty() ? "[]" : "]";
@@ -6207,8 +6265,18 @@
}
traceWaitQueueLength(*connection);
if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) {
- const InputTarget target{.connection = connection, .flags = dispatchEntry->targetFlags};
- enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry), target);
+ const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ // Only dispatch fallbacks if there is a window for the connection.
+ if (windowHandle != nullptr) {
+ const auto inputTarget =
+ createInputTargetLocked(windowHandle, InputTarget::DispatchMode::AS_IS,
+ dispatchEntry->targetFlags,
+ fallbackKeyEntry->downTime);
+ if (inputTarget.has_value()) {
+ enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry),
+ *inputTarget);
+ }
+ }
}
releaseDispatchEntry(std::move(dispatchEntry));
}
@@ -6440,14 +6508,18 @@
mLock.lock();
- // Cancel the fallback key.
+ // Cancel the fallback key, but only if we still have a window for the channel.
+ // It could have been removed during the policy call.
if (*fallbackKeyCode != AKEYCODE_UNKNOWN) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
- "application handled the original non-fallback key "
- "or is no longer a foreground target, "
- "canceling previously dispatched fallback key");
- options.keyCode = *fallbackKeyCode;
- synthesizeCancelationEventsForConnectionLocked(connection, options);
+ const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ if (windowHandle != nullptr) {
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
+ "application handled the original non-fallback key "
+ "or is no longer a foreground target, "
+ "canceling previously dispatched fallback key");
+ options.keyCode = *fallbackKeyCode;
+ synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection);
+ }
}
connection->inputState.removeFallbackKey(originalKeyCode);
}
@@ -6523,10 +6595,13 @@
}
}
- CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
- "canceling fallback, policy no longer desires it");
- options.keyCode = *fallbackKeyCode;
- synthesizeCancelationEventsForConnectionLocked(connection, options);
+ const auto windowHandle = getWindowHandleLocked(connection->getToken());
+ if (windowHandle != nullptr) {
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
+ "canceling fallback, policy no longer desires it");
+ options.keyCode = *fallbackKeyCode;
+ synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection);
+ }
fallback = false;
*fallbackKeyCode = AKEYCODE_UNKNOWN;
@@ -6665,15 +6740,19 @@
mLooper->wake();
}
-void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes) {
+void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes,
+ const sp<WindowInfoHandle> removedFocusedWindowHandle) {
if (changes.oldFocus) {
- std::shared_ptr<Connection> focusedConnection = getConnectionLocked(changes.oldFocus);
- if (focusedConnection) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
- "focus left window");
- synthesizeCancelationEventsForConnectionLocked(focusedConnection, options);
- enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason);
+ const auto resolvedWindow = removedFocusedWindowHandle != nullptr
+ ? removedFocusedWindowHandle
+ : getWindowHandleLocked(changes.oldFocus, changes.displayId);
+ if (resolvedWindow == nullptr) {
+ LOG(FATAL) << __func__ << ": Previously focused token did not have a window";
}
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
+ "focus left window");
+ synthesizeCancelationEventsForWindowLocked(resolvedWindow, options);
+ enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason);
}
if (changes.newFocus) {
resetNoFocusedWindowTimeoutLocked();
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index dcd1566..f2fd0ca 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -615,18 +615,21 @@
REQUIRES(mLock);
void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options)
REQUIRES(mLock);
- void synthesizeCancelationEventsForConnectionLocked(
- const std::shared_ptr<Connection>& connection, const CancelationOptions& options)
+ void synthesizeCancelationEventsForWindowLocked(const sp<gui::WindowInfoHandle>&,
+ const CancelationOptions&,
+ const std::shared_ptr<Connection>& = nullptr)
REQUIRES(mLock);
+ // This is a convenience function used to generate cancellation for a connection without having
+ // to check whether it's a monitor or a window. For non-monitors, the window handle must not be
+ // null. Always prefer the "-ForWindow" method above when explicitly dealing with windows.
+ void synthesizeCancelationEventsForConnectionLocked(
+ const std::shared_ptr<Connection>& connection, const CancelationOptions& options,
+ const sp<gui::WindowInfoHandle>& window) REQUIRES(mLock);
void synthesizePointerDownEventsForConnectionLocked(
const nsecs_t downTime, const std::shared_ptr<Connection>& connection,
ftl::Flags<InputTarget::Flags> targetFlags) REQUIRES(mLock);
- void synthesizeCancelationEventsForWindowLocked(
- const sp<android::gui::WindowInfoHandle>& windowHandle,
- const CancelationOptions& options) REQUIRES(mLock);
-
// Splitting motion events across windows. When splitting motion event for a target,
// splitDownTime refers to the time of first 'down' event on that particular target
std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry,
@@ -653,7 +656,9 @@
bool handled, nsecs_t consumeTime) REQUIRES(mLock);
void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
const KeyEntry& entry) REQUIRES(mLock);
- void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
+ void onFocusChangedLocked(const FocusResolver::FocusChanges& changes,
+ const sp<gui::WindowInfoHandle> removedFocusedWindowHandle = nullptr)
+ REQUIRES(mLock);
void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken)
REQUIRES(mLock);
void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ab667d0..01bc2f3 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -5779,27 +5779,27 @@
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
- firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+ firstWindowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
// Transfer touch focus
ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken(), SECOND_DISPLAY_ID));
// The first window gets cancel.
- firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID);
- secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);
+ firstWindowInSecondary->consumeMotionCancel(SECOND_DISPLAY_ID);
+ secondWindowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
SECOND_DISPLAY_ID, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- firstWindowInPrimary->assertNoEvents();
- secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID);
+ firstWindowInSecondary->assertNoEvents();
+ secondWindowInSecondary->consumeMotionMove(SECOND_DISPLAY_ID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- firstWindowInPrimary->assertNoEvents();
- secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID);
+ firstWindowInSecondary->assertNoEvents();
+ secondWindowInSecondary->consumeMotionUp(SECOND_DISPLAY_ID);
}
TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
@@ -7263,7 +7263,7 @@
mWindow->assertNoEvents();
}
-TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) {
+TEST_F(InputDispatcherFallbackKeyTest, InputChannelRemovedDuringPolicyCall) {
setFallback(AKEYCODE_B);
mDispatcher->notifyKey(
KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
@@ -7296,6 +7296,71 @@
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
}
+TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ mFakePolicy->setUnhandledKeyHandler([&](const KeyEvent& event) {
+ // When the unhandled key is reported to the policy next, remove the window.
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
+ return KeyEventBuilder(event).keyCode(AKEYCODE_B).build();
+ });
+ // Release the original key, which the app will not handle. When this unhandled key is reported
+ // to the policy, the window will be removed.
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+
+ // Since the window was removed, it loses focus, and the channel state will be reset.
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+ mWindow->consumeFocusEvent(false);
+ mWindow->assertNoEvents();
+}
+
+TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedWhileAwaitingFinishedSignal) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ const auto [seq, event] = mWindow->receiveEvent();
+ ASSERT_TRUE(seq.has_value() && event != nullptr) << "Failed to receive fallback event";
+ ASSERT_EQ(event->getType(), InputEventType::KEY);
+ ASSERT_THAT(static_cast<const KeyEvent&>(*event),
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ // Remove the window now, which should generate a cancellations and make the window lose focus.
+ mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A),
+ WithFlags(AKEY_EVENT_FLAG_CANCELED)));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED)));
+ mWindow->consumeFocusEvent(false);
+
+ // Finish the event by reporting it as handled.
+ mWindow->finishEvent(*seq);
+ mWindow->assertNoEvents();
+}
+
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms;
@@ -8225,13 +8290,13 @@
}
}
- void touchAndAssertPositions(int32_t action, const std::vector<PointF>& touchedPoints,
+ void touchAndAssertPositions(sp<FakeWindowHandle> touchedWindow, int32_t action,
+ const std::vector<PointF>& touchedPoints,
std::vector<PointF> expectedPoints) {
mDispatcher->notifyMotion(generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, touchedPoints));
- // Always consume from window1 since it's the window that has the InputReceiver
- consumeMotionEvent(mWindow1, action, expectedPoints);
+ consumeMotionEvent(touchedWindow, action, expectedPoints);
}
};
@@ -8239,15 +8304,15 @@
// Touch Window 1
PointF touchedPoint = {10, 10};
PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
- touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
+ touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
// Release touch on Window 1
- touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
+ touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
// Touch Window 2
touchedPoint = {150, 150};
expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
- touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
+ touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) {
@@ -8258,21 +8323,21 @@
// Touch Window 1
PointF touchedPoint = {10, 10};
PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
- touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
+ touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
// Release touch on Window 1
- touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
+ touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
// Touch Window 2
touchedPoint = {150, 150};
expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
- touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
- touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
+ touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
+ touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint});
// Update the transform so rotation is set
mWindow2->setWindowTransform(0, -1, 1, 0);
mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
- touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
+ touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint});
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform) {
@@ -8282,22 +8347,25 @@
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
- touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
+ // Since this is part of the same touch gesture that has already been dispatched to Window 1,
+ // the touch stream from Window 2 will be merged with the stream in Window 1. The merged stream
+ // will continue to be dispatched through Window 1.
touchedPoints.push_back(PointF{150, 150});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints);
// Release Window 2
- touchAndAssertPositions(POINTER_1_UP, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, POINTER_1_UP, touchedPoints, expectedPoints);
expectedPoints.pop_back();
// Update the transform so rotation is set for Window 2
mWindow2->setWindowTransform(0, -1, 1, 0);
mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints);
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) {
@@ -8307,37 +8375,37 @@
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
- touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
touchedPoints.push_back(PointF{150, 150});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
- touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
// Release Window 2
- touchAndAssertPositions(POINTER_1_UP, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, POINTER_1_UP, touchedPoints, expectedPoints);
expectedPoints.pop_back();
// Touch Window 2
mWindow2->setWindowTransform(0, -1, 1, 0);
mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
- touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
}
TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
@@ -8347,20 +8415,20 @@
// Touch Window 1
std::vector<PointF> touchedPoints = {PointF{10, 10}};
std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
- touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints);
// Touch Window 2
touchedPoints.push_back(PointF{150, 150});
expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
- touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints);
// Move both windows
touchedPoints = {{20, 20}, {175, 175}};
expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
- touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
+ touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints);
}
/**
@@ -8404,7 +8472,7 @@
.pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150))
.build());
consumeMotionEvent(mWindow1, ACTION_HOVER_EXIT, {{50, 50}});
- consumeMotionEvent(mWindow1, ACTION_HOVER_ENTER,
+ consumeMotionEvent(mWindow2, ACTION_HOVER_ENTER,
{getPointInWindow(mWindow2->getInfo(), PointF{150, 150})});
}
@@ -9543,7 +9611,7 @@
TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAllWindowsFocusable) {
setFocusedWindow(mMirror);
- // window gets focused
+ // window gets focused because it is above the mirror
mWindow->consumeFocusEvent(true);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
@@ -9616,10 +9684,10 @@
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ mMirror->consumeKeyDown(ADISPLAY_ID_NONE);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+ mMirror->consumeKeyUp(ADISPLAY_ID_NONE);
// Both windows are removed
mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index ba0825c..224f50e 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -21,17 +21,17 @@
#include <android-base/stringprintf.h>
#include <android/configuration.h>
+#include <ftl/mixins.h>
#include <ftl/small_map.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMode.h>
#include <ui/Size.h>
#include <utils/Timers.h>
+#include <common/FlagManager.h>
#include <scheduler/Fps.h>
-#include <common/FlagManager.h>
#include "DisplayHardware/Hal.h"
-#include "Scheduler/StrongTyping.h"
namespace android {
@@ -46,7 +46,12 @@
bool operator<=(const DisplayModePtr&, const DisplayModePtr&) = delete;
bool operator>=(const DisplayModePtr&, const DisplayModePtr&) = delete;
-using DisplayModeId = StrongTyping<ui::DisplayModeId, struct DisplayModeIdTag, Compare>;
+struct DisplayModeId : ftl::DefaultConstructible<DisplayModeId, ui::DisplayModeId>,
+ ftl::Incrementable<DisplayModeId>,
+ ftl::Equatable<DisplayModeId>,
+ ftl::Orderable<DisplayModeId> {
+ using DefaultConstructible::DefaultConstructible;
+};
using DisplayModes = ftl::SmallMap<DisplayModeId, DisplayModePtr, 3>;
using DisplayModeIterator = DisplayModes::const_iterator;
@@ -185,7 +190,7 @@
inline std::string to_string(const DisplayMode& mode) {
return base::StringPrintf("{id=%d, hwcId=%d, resolution=%dx%d, vsyncRate=%s, "
"dpi=%.2fx%.2f, group=%d, vrrConfig=%s}",
- mode.getId().value(), mode.getHwcId(), mode.getWidth(),
+ ftl::to_underlying(mode.getId()), mode.getHwcId(), mode.getWidth(),
mode.getHeight(), to_string(mode.getVsyncRate()).c_str(),
mode.getDpi().x, mode.getDpi().y, mode.getGroup(),
to_string(mode.getVrrConfig()).c_str());
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index ccd1c0f..96eccf2 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -148,7 +148,7 @@
DisplayEventReceiver::Event event;
event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE,
mode.modePtr->getPhysicalDisplayId(), systemTime()};
- event.modeChange.modeId = mode.modePtr->getId().value();
+ event.modeChange.modeId = ftl::to_underlying(mode.modePtr->getId());
event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs();
return event;
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 7614453..eeca6be 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -286,7 +286,8 @@
std::string RefreshRateSelector::Policy::toString() const {
return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s"
", primaryRanges=%s, appRequestRanges=%s}",
- defaultMode.value(), allowGroupSwitching ? "true" : "false",
+ ftl::to_underlying(defaultMode),
+ allowGroupSwitching ? "true" : "false",
to_string(primaryRanges).c_str(),
to_string(appRequestRanges).c_str());
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index a1a7c28..6051e89 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -31,7 +31,6 @@
#include "DisplayHardware/DisplayMode.h"
#include "Scheduler/OneShotTimer.h"
-#include "Scheduler/StrongTyping.h"
#include "ThreadContext.h"
#include "Utils/Dumper.h"
diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h
deleted file mode 100644
index a05c123..0000000
--- a/services/surfaceflinger/Scheduler/StrongTyping.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-namespace android {
-
-template <typename T, template <typename> class AbilityType>
-struct Ability {
- T& base() { return static_cast<T&>(*this); }
- T const& base() const { return static_cast<T const&>(*this); }
-};
-
-template <typename T>
-struct Add : Ability<T, Add> {
- inline T operator+(T const& other) const { return T(this->base().value() + other.value()); }
- inline T& operator++() {
- ++this->base().value();
- return this->base();
- };
- inline T operator++(int) {
- T tmp(this->base());
- operator++();
- return tmp;
- };
- inline T& operator+=(T const& other) {
- this->base().value() += other.value();
- return this->base();
- };
-};
-
-template <typename T>
-struct Compare : Ability<T, Compare> {
- inline bool operator==(T const& other) const { return this->base().value() == other.value(); };
- inline bool operator<(T const& other) const { return this->base().value() < other.value(); }
- inline bool operator<=(T const& other) const { return (*this < other) || (*this == other); }
- inline bool operator!=(T const& other) const { return !(*this == other); }
- inline bool operator>=(T const& other) const { return !(*this < other); }
- inline bool operator>(T const& other) const { return !(*this < other || *this == other); }
-};
-
-template <typename T>
-struct Hash : Ability<T, Hash> {
- [[nodiscard]] std::size_t hash() const {
- return std::hash<typename std::remove_const<
- typename std::remove_reference<decltype(this->base().value())>::type>::type>{}(
- this->base().value());
- }
-};
-
-template <typename T, typename W, template <typename> class... Ability>
-struct StrongTyping : Ability<StrongTyping<T, W, Ability...>>... {
- constexpr StrongTyping() = default;
- constexpr explicit StrongTyping(T const& value) : mValue(value) {}
- StrongTyping(StrongTyping const&) = default;
- StrongTyping& operator=(StrongTyping const&) = default;
- explicit inline operator T() const { return mValue; }
- T const& value() const { return mValue; }
- T& value() { return mValue; }
-
- friend std::ostream& operator<<(std::ostream& os, const StrongTyping<T, W, Ability...>& value) {
- return os << value.value();
- }
-
-private:
- T mValue{0};
-};
-} // namespace android
-
-namespace std {
-template <typename T, typename W, template <typename> class... Ability>
-struct hash<android::StrongTyping<T, W, Ability...>> {
- std::size_t operator()(android::StrongTyping<T, W, Ability...> const& k) const {
- return k.hash();
- }
-};
-} // namespace std
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index f978016..ed8f8fe 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -20,10 +20,9 @@
#include <optional>
#include <string>
+#include <ftl/mixins.h>
#include <utils/Timers.h>
-#include "StrongTyping.h"
-
namespace android::scheduler {
using ScheduleResult = std::optional<nsecs_t>;
@@ -35,7 +34,11 @@
*/
class VSyncDispatch {
public:
- using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare, Hash>;
+ struct CallbackToken : ftl::DefaultConstructible<CallbackToken, size_t>,
+ ftl::Equatable<CallbackToken>,
+ ftl::Incrementable<CallbackToken> {
+ using DefaultConstructible::DefaultConstructible;
+ };
virtual ~VSyncDispatch();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 5cb0ffb..963f9e9 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -267,15 +267,15 @@
}
void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) {
- rearmTimerSkippingUpdateFor(now, mCallbacks.end());
+ rearmTimerSkippingUpdateFor(now, mCallbacks.cend());
}
void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
- nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
+ nsecs_t now, CallbackMap::const_iterator skipUpdateIt) {
std::optional<nsecs_t> min;
std::optional<nsecs_t> targetVsync;
std::optional<std::string_view> nextWakeupName;
- for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
+ for (auto it = mCallbacks.cbegin(); it != mCallbacks.cend(); ++it) {
auto& callback = it->second;
if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
continue;
@@ -351,13 +351,12 @@
VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
Callback callback, std::string callbackName) {
std::lock_guard lock(mMutex);
- return CallbackToken{
- mCallbacks
- .emplace(++mCallbackToken,
- std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
- std::move(callback),
- mMinVsyncDistance))
- .first->first};
+ return mCallbacks
+ .try_emplace(++mCallbackToken,
+ std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
+ std::move(callback),
+ mMinVsyncDistance))
+ .first->first;
}
void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) {
@@ -367,7 +366,7 @@
auto it = mCallbacks.find(token);
if (it != mCallbacks.end()) {
entry = it->second;
- mCallbacks.erase(it);
+ mCallbacks.erase(it->first);
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index 3d08410..81c746e 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -16,14 +16,13 @@
#pragma once
-#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
-#include <unordered_map>
#include <android-base/thread_annotations.h>
+#include <ftl/small_map.h>
#include "VSyncDispatch.h"
#include "VsyncSchedule.h"
@@ -135,13 +134,14 @@
VSyncDispatchTimerQueue(const VSyncDispatchTimerQueue&) = delete;
VSyncDispatchTimerQueue& operator=(const VSyncDispatchTimerQueue&) = delete;
+ // The static capacity was chosen to exceed the expected number of callbacks.
using CallbackMap =
- std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
+ ftl::SmallMap<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>, 5>;
void timerCallback();
void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
void rearmTimer(nsecs_t now) REQUIRES(mMutex);
- void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate)
+ void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::const_iterator skipUpdate)
REQUIRES(mMutex);
void cancelTimer() REQUIRES(mMutex);
ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex);
@@ -158,7 +158,7 @@
nsecs_t const mTimerSlack;
nsecs_t const mMinVsyncDistance;
- size_t mCallbackToken GUARDED_BY(mMutex) = 0;
+ CallbackToken mCallbackToken GUARDED_BY(mMutex);
CallbackMap mCallbacks GUARDED_BY(mMutex);
nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime;
diff --git a/services/surfaceflinger/Scheduler/src/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp
index 09e8a1e..eeb9c60 100644
--- a/services/surfaceflinger/Scheduler/src/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/src/Timer.cpp
@@ -159,7 +159,7 @@
ALOGW("Failed to set SCHED_FIFO on dispatch thread");
}
- if (pthread_setname_np(pthread_self(), "TimerDispatch")) {
+ if (pthread_setname_np(pthread_self(), "TimerDispatch") != 0) {
ALOGW("Failed to set thread name on dispatch thread");
}
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 89a341f..d354e4b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1083,7 +1083,7 @@
for (const auto& [id, mode] : displayModes) {
ui::DisplayMode outMode;
- outMode.id = static_cast<int32_t>(id.value());
+ outMode.id = ftl::to_underlying(id);
auto [width, height] = mode->getResolution();
auto [xDpi, yDpi] = mode->getDpi();
@@ -1132,7 +1132,7 @@
const PhysicalDisplayId displayId = snapshot.displayId();
const auto mode = display->refreshRateSelector().getActiveMode();
- info->activeDisplayModeId = mode.modePtr->getId().value();
+ info->activeDisplayModeId = ftl::to_underlying(mode.modePtr->getId());
info->renderFrameRate = mode.fps.getValue();
info->activeColorMode = display->getCompositionDisplay()->getState().colorMode;
info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities());
@@ -1148,7 +1148,7 @@
if (getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG)) {
if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(displayId)) {
if (const auto modeId = snapshot.translateModeId(*hwcId)) {
- info->preferredBootDisplayMode = modeId->value();
+ info->preferredBootDisplayMode = ftl::to_underlying(*modeId);
}
}
}
@@ -1312,7 +1312,7 @@
[](const DisplayModePtr& mode) { return mode->getPeakFps(); });
if (!fpsOpt) {
- ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(),
+ ALOGE("%s: Invalid mode %d for display %s", whence, ftl::to_underlying(modeId),
to_string(snapshot.displayId()).c_str());
return BAD_VALUE;
}
@@ -1421,12 +1421,12 @@
if (!displayModePtrOpt) {
ALOGW("Desired display mode is no longer supported. Mode ID = %d",
- desiredModeId.value());
- dropModeRequest(display);
+ ftl::to_underlying(desiredModeId));
continue;
}
- ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(),
+ ALOGV("%s changing active mode to %d(%s) for display %s", __func__,
+ ftl::to_underlying(desiredModeId),
to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
to_string(display->getId()).c_str());
@@ -1619,7 +1619,7 @@
[](const DisplayModePtr& mode) { return mode->getHwcId(); });
if (!hwcIdOpt) {
- ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(),
+ ALOGE("%s: Invalid mode %d for display %s", whence, ftl::to_underlying(modeId),
to_string(snapshot.displayId()).c_str());
return BAD_VALUE;
}
@@ -3393,15 +3393,15 @@
})
.value_or(DisplayModes{});
- ui::DisplayModeId nextModeId = 1 +
- std::accumulate(oldModes.begin(), oldModes.end(), static_cast<ui::DisplayModeId>(-1),
- [](ui::DisplayModeId max, const auto& pair) {
- return std::max(max, pair.first.value());
- });
+ DisplayModeId nextModeId = std::accumulate(oldModes.begin(), oldModes.end(), DisplayModeId(-1),
+ [](DisplayModeId max, const auto& pair) {
+ return std::max(max, pair.first);
+ });
+ ++nextModeId;
DisplayModes newModes;
for (const auto& hwcMode : hwcModes) {
- const DisplayModeId id{nextModeId++};
+ const auto id = nextModeId++;
newModes.try_emplace(id,
DisplayMode::Builder(hwcMode.hwcId)
.setId(id)
@@ -3476,8 +3476,12 @@
auto [displayModes, activeMode] = loadDisplayModes(displayId);
if (!activeMode) {
- // TODO(b/241286153): Report hotplug failure to the framework.
ALOGE("Failed to hotplug display %s", to_string(displayId).c_str());
+ if (FlagManager::getInstance().hotplug2()) {
+ mScheduler->onHotplugConnectionError(mAppConnectionHandle,
+ static_cast<int32_t>(
+ DisplayHotplugEvent::ERROR_UNKNOWN));
+ }
getHwComposer().disconnectDisplay(displayId);
return nullptr;
}
@@ -4178,8 +4182,8 @@
if (display->refreshRateSelector().isModeAllowed(request.mode)) {
setDesiredMode(std::move(request));
} else {
- ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(),
- to_string(displayId).c_str());
+ ALOGV("%s: Mode %d is disallowed for display %s", __func__,
+ ftl::to_underlying(modePtr->getId()), to_string(displayId).c_str());
}
}
}
@@ -8473,11 +8477,11 @@
const auto preferredModeId = preferredMode.modePtr->getId();
const Fps preferredFps = preferredMode.fps;
- ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(),
+ ALOGV("Switching to Scheduler preferred mode %d (%s)", ftl::to_underlying(preferredModeId),
to_string(preferredFps).c_str());
if (!selector.isModeAllowed(preferredMode)) {
- ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value());
+ ALOGE("%s: Preferred mode %d is disallowed", __func__, ftl::to_underlying(preferredModeId));
return INVALID_OPERATION;
}
@@ -8565,7 +8569,7 @@
scheduler::RefreshRateSelector::Policy policy =
display->refreshRateSelector().getDisplayManagerPolicy();
- outSpecs->defaultMode = policy.defaultMode.value();
+ outSpecs->defaultMode = ftl::to_underlying(policy.defaultMode);
outSpecs->allowGroupSwitching = policy.allowGroupSwitching;
outSpecs->primaryRanges = translate(policy.primaryRanges);
outSpecs->appRequestRanges = translate(policy.appRequestRanges);
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index b07e7ac..425d2da 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -130,6 +130,8 @@
DUMP_READ_ONLY_FLAG(enable_layer_command_batching);
DUMP_READ_ONLY_FLAG(screenshot_fence_preservation);
DUMP_READ_ONLY_FLAG(vulkan_renderengine);
+ DUMP_READ_ONLY_FLAG(renderable_buffer_usage);
+ DUMP_READ_ONLY_FLAG(restore_blur_step);
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
#undef DUMP_FLAG_INTERVAL
@@ -206,6 +208,8 @@
FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "")
FLAG_MANAGER_READ_ONLY_FLAG(screenshot_fence_preservation, "debug.sf.screenshot_fence_preservation")
FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan")
+FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "")
+FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step")
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 2a30a40..86efd30 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -70,6 +70,8 @@
bool enable_layer_command_batching() const;
bool screenshot_fence_preservation() const;
bool vulkan_renderengine() const;
+ bool renderable_buffer_usage() const;
+ bool restore_blur_step() const;
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index fcbef01..0ebc41b 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -174,3 +174,25 @@
bug: "302703346"
is_fixed_read_only: true
}
+
+flag {
+ name: "renderable_buffer_usage"
+ namespace: "core_graphics"
+ description: "Decide whether an ExternalTexture isRenderable based on its buffer's usage."
+ bug: "305445199"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "restore_blur_step"
+ namespace: "core_graphics"
+ description: "Restore drawing the blur input prior to drawing blurred content."
+ bug: "255921628"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 7c191a5..da4e47f 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -129,7 +129,6 @@
"TransactionTraceWriterTest.cpp",
"TransactionTracingTest.cpp",
"TunnelModeEnabledReporterTest.cpp",
- "StrongTypingTest.cpp",
"VSyncCallbackRegistrationTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 01762c1..110f324 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -1026,6 +1026,7 @@
};
TEST_F(SmallAreaDetectionTest, SmallDirtyLayer) {
+ SET_FLAG_FOR_TEST(flags::enable_small_area_detection, true);
auto layer = createLegacyAndFrontedEndLayer(1);
nsecs_t time = systemTime();
@@ -1043,6 +1044,7 @@
}
TEST_F(SmallAreaDetectionTest, NotSmallDirtyLayer) {
+ SET_FLAG_FOR_TEST(flags::enable_small_area_detection, true);
auto layer = createLegacyAndFrontedEndLayer(1);
nsecs_t time = systemTime();
@@ -1060,6 +1062,7 @@
}
TEST_F(SmallAreaDetectionTest, smallDirtyLayerWithMatrix) {
+ SET_FLAG_FOR_TEST(flags::enable_small_area_detection, true);
auto layer = createLegacyAndFrontedEndLayer(1);
nsecs_t time = systemTime();
diff --git a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
deleted file mode 100644
index 45b7610..0000000
--- a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include "Scheduler/StrongTyping.h"
-
-using namespace testing;
-
-namespace android {
-
-TEST(StrongTypeTest, comparison) {
- using SpunkyType = StrongTyping<int, struct SpunkyTypeTag, Compare>;
- SpunkyType f1(10);
-
- EXPECT_TRUE(f1 == f1);
- EXPECT_TRUE(SpunkyType(10) != SpunkyType(11));
- EXPECT_FALSE(SpunkyType(31) != SpunkyType(31));
-
- EXPECT_TRUE(SpunkyType(10) < SpunkyType(11));
- EXPECT_TRUE(SpunkyType(-1) < SpunkyType(0));
- EXPECT_FALSE(SpunkyType(-10) < SpunkyType(-20));
-
- EXPECT_TRUE(SpunkyType(10) <= SpunkyType(11));
- EXPECT_TRUE(SpunkyType(10) <= SpunkyType(10));
- EXPECT_TRUE(SpunkyType(-10) <= SpunkyType(1));
- EXPECT_FALSE(SpunkyType(10) <= SpunkyType(9));
-
- EXPECT_TRUE(SpunkyType(11) >= SpunkyType(11));
- EXPECT_TRUE(SpunkyType(12) >= SpunkyType(11));
- EXPECT_FALSE(SpunkyType(11) >= SpunkyType(12));
-
- EXPECT_FALSE(SpunkyType(11) > SpunkyType(12));
- EXPECT_TRUE(SpunkyType(-11) < SpunkyType(7));
-}
-
-TEST(StrongTypeTest, addition) {
- using FunkyType = StrongTyping<int, struct FunkyTypeTag, Compare, Add>;
- FunkyType f2(22);
- FunkyType f1(10);
-
- EXPECT_THAT(f1 + f2, Eq(FunkyType(32)));
- EXPECT_THAT(f2 + f1, Eq(FunkyType(32)));
-
- EXPECT_THAT(++f1.value(), Eq(11));
- EXPECT_THAT(f1.value(), Eq(11));
- EXPECT_THAT(f1++.value(), Eq(11));
- EXPECT_THAT(f1++.value(), Eq(12));
- EXPECT_THAT(f1.value(), Eq(13));
-
- auto f3 = f1;
- EXPECT_THAT(f1, Eq(f3));
- EXPECT_THAT(f1, Lt(f2));
-
- f3 += f1;
- EXPECT_THAT(f1.value(), Eq(13));
- EXPECT_THAT(f3.value(), Eq(26));
-}
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 82b4ad0..15a6db6 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -28,6 +28,14 @@
using namespace com::android::graphics::surfaceflinger;
+#define EXPECT_SET_ACTIVE_CONFIG(displayId, modeId) \
+ EXPECT_CALL(*mComposer, \
+ setActiveConfigWithConstraints(displayId, \
+ static_cast<hal::HWConfigId>( \
+ ftl::to_underlying(modeId)), \
+ _, _)) \
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)))
+
namespace android {
namespace {
@@ -165,8 +173,7 @@
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
- 120));
+ mock::createDisplayModeSpecs(kModeId90, false, 0, 120));
ASSERT_TRUE(mDisplay->getDesiredMode());
EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90);
@@ -174,10 +181,7 @@
// Verify that next commit will call setActiveConfigWithConstraints in HWC
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
- hal::HWConfigId(kModeId90.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+ EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90);
mFlinger.commit();
@@ -206,8 +210,7 @@
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90.value(), true, 0,
- 120));
+ mock::createDisplayModeSpecs(kModeId90, true, 0, 120));
ASSERT_TRUE(mDisplay->getDesiredMode());
EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90);
@@ -216,10 +219,7 @@
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
- hal::HWConfigId(kModeId90.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+ EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90);
EXPECT_CALL(*mAppEventThread,
onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)}));
@@ -242,28 +242,20 @@
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90.value(), false, 0,
- 120));
+ mock::createDisplayModeSpecs(kModeId90, false, 0, 120));
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
- hal::HWConfigId(kModeId90.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+ EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90);
mFlinger.commit();
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId120.value(), false, 0,
- 180));
+ mock::createDisplayModeSpecs(kModeId120, false, 0, 180));
ASSERT_TRUE(mDisplay->getDesiredMode());
EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120);
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
- hal::HWConfigId(kModeId120.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+ EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId120);
mFlinger.commit();
@@ -285,8 +277,7 @@
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0,
- 120));
+ mock::createDisplayModeSpecs(kModeId90_4K, false, 0, 120));
ASSERT_TRUE(mDisplay->getDesiredMode());
EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90_4K);
@@ -295,10 +286,7 @@
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
- hal::HWConfigId(kModeId90_4K.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+ EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90_4K);
EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplay->getPhysicalId(), true));
@@ -335,7 +323,7 @@
}
if (arg->getDesiredMode()->mode.modePtr->getId() != modeId) {
- *result_listener << "Unexpected desired mode " << modeId;
+ *result_listener << "Unexpected desired mode " << ftl::to_underlying(modeId);
return false;
}
@@ -349,14 +337,15 @@
MATCHER_P(ModeSettledTo, modeId, "") {
if (const auto desiredOpt = arg->getDesiredMode()) {
- *result_listener << "Unsettled desired mode " << desiredOpt->mode.modePtr->getId();
+ *result_listener << "Unsettled desired mode "
+ << ftl::to_underlying(desiredOpt->mode.modePtr->getId());
return false;
}
ftl::FakeGuard guard(kMainThreadContext);
if (arg->getActiveMode().modePtr->getId() != modeId) {
- *result_listener << "Settled to unexpected active mode " << modeId;
+ *result_listener << "Settled to unexpected active mode " << ftl::to_underlying(modeId);
return false;
}
@@ -387,22 +376,19 @@
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90.value(),
- false, 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId90, false,
+ 0.f, 120.f)));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId60.value(),
- false, 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId60, false,
+ 0.f, 120.f)));
EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(kInnerDisplayHwcId,
- hal::HWConfigId(kModeId90.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
mFlinger.commit();
@@ -423,10 +409,7 @@
EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(kOuterDisplayHwcId,
- hal::HWConfigId(kModeId60.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+ EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60);
mFlinger.commit();
@@ -464,27 +447,20 @@
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90.value(),
- false, 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId90, false,
+ 0.f, 120.f)));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId60.value(),
- false, 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId60, false,
+ 0.f, 120.f)));
EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(kInnerDisplayHwcId,
- hal::HWConfigId(kModeId90.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
-
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(kOuterDisplayHwcId,
- hal::HWConfigId(kModeId60.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
+ EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60);
mFlinger.commit();
@@ -503,8 +479,8 @@
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90.value(),
- false, 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId90, false,
+ 0.f, 120.f)));
EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
@@ -512,10 +488,7 @@
mDisplay->setPowerMode(hal::PowerMode::OFF);
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(kInnerDisplayHwcId,
- hal::HWConfigId(kModeId90.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
mFlinger.commit();
@@ -554,13 +527,13 @@
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId90.value(),
- false, 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId90, false,
+ 0.f, 120.f)));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
- mock::createDisplayModeSpecs(kModeId60.value(),
- false, 0.f, 120.f)));
+ mock::createDisplayModeSpecs(kModeId60, false,
+ 0.f, 120.f)));
EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
@@ -569,10 +542,7 @@
outerDisplay->setPowerMode(hal::PowerMode::OFF);
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(kInnerDisplayHwcId,
- hal::HWConfigId(kModeId90.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
mFlinger.commit();
@@ -591,10 +561,7 @@
// Only the outer display is powered on.
mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay);
- EXPECT_CALL(*mComposer,
- setActiveConfigWithConstraints(kOuterDisplayHwcId,
- hal::HWConfigId(kModeId60.value()), _, _))
- .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+ EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60);
mFlinger.commit();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index a270dc9..897f9a0 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -17,8 +17,14 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
+#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
+#include <common/test/FlagUtils.h>
#include "DisplayTransactionTestHelpers.h"
+using namespace com::android::graphics::surfaceflinger;
+using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent;
+
namespace android {
class HotplugTest : public DisplayTransactionTest {};
@@ -87,6 +93,8 @@
}
TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) {
+ SET_FLAG_FOR_TEST(flags::connected_display, true);
+
// Inject a primary display.
PrimaryDisplayVariant::injectHwcDisplay(this);
@@ -94,6 +102,10 @@
constexpr bool kFailedHotplug = true;
ExternalDisplay::setupHwcHotplugCallExpectations<kFailedHotplug>(this);
+ EXPECT_CALL(*mEventThread,
+ onHotplugConnectionError(static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN)))
+ .Times(1);
+
// Simulate a connect event that fails to load display modes due to HWC already having
// disconnected the display but SF yet having to process the queued disconnect event.
EXPECT_CALL(*mComposer, getActiveConfig(ExternalDisplay::HWC_DISPLAY_ID, _))
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index e298e7c..685d8f9 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -24,7 +24,7 @@
DisplayModeId modeId, Fps displayRefreshRate, int32_t group = 0,
ui::Size resolution = ui::Size(1920, 1080),
PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(0)) {
- return DisplayMode::Builder(hal::HWConfigId(modeId.value()))
+ return DisplayMode::Builder(hal::HWConfigId(ftl::to_underlying(modeId)))
.setId(modeId)
.setPhysicalDisplayId(displayId)
.setVsyncPeriod(displayRefreshRate.getPeriodNsecs())
diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
index a71e82c..7b18a82 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h
@@ -18,12 +18,15 @@
#include <android/gui/DisplayModeSpecs.h>
+#include "DisplayHardware/DisplayMode.h"
+
namespace android::mock {
-inline gui::DisplayModeSpecs createDisplayModeSpecs(int32_t defaultMode, bool allowGroupSwitching,
- float minFps, float maxFps) {
+inline gui::DisplayModeSpecs createDisplayModeSpecs(DisplayModeId defaultMode,
+ bool allowGroupSwitching, float minFps,
+ float maxFps) {
gui::DisplayModeSpecs specs;
- specs.defaultMode = defaultMode;
+ specs.defaultMode = ftl::to_underlying(defaultMode);
specs.allowGroupSwitching = allowGroupSwitching;
specs.primaryRanges.physical.min = minFps;
specs.primaryRanges.physical.max = maxFps;