Merge "Clear cooked state and touch spots when disabled"
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index 85e6969..50c1624 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -21,8 +21,6 @@
*
* <p>When bugreport creation is complete one of {@code onError} or {@code onFinished} is called.
*
- * <p>These methods are synchronous by design in order to make dumpstate's lifecycle simpler
- * to handle.
*
* {@hide}
*/
@@ -54,10 +52,8 @@
/**
* Called on an error condition with one of the error codes listed above.
- * This is not an asynchronous method since it can race with dumpstate exiting, thus triggering
- * death recipient.
*/
- void onError(int errorCode);
+ oneway void onError(int errorCode);
/**
* Called when taking bugreport finishes successfully.
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index b038feb..5c34069 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -486,6 +486,19 @@
}
}
+bool IPCThreadState::flushIfNeeded()
+{
+ if (mIsLooper || mServingStackPointer != nullptr) {
+ return false;
+ }
+ // In case this thread is not a looper and is not currently serving a binder transaction,
+ // there's no guarantee that this thread will call back into the kernel driver any time
+ // soon. Therefore, flush pending commands such as BC_FREE_BUFFER, to prevent them from getting
+ // stuck in this thread's out buffer.
+ flushCommands();
+ return true;
+}
+
void IPCThreadState::blockUntilThreadAvailable()
{
pthread_mutex_lock(&mProcess->mThreadCountLock);
@@ -604,6 +617,7 @@
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
+ mIsLooper = true;
status_t result;
do {
processPendingDerefs();
@@ -626,6 +640,7 @@
(void*)pthread_self(), getpid(), result);
mOut.writeInt32(BC_EXIT_LOOPER);
+ mIsLooper = false;
talkWithDriver(false);
}
@@ -739,9 +754,11 @@
LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle);
mOut.writeInt32(BC_ACQUIRE);
mOut.writeInt32(handle);
- // Create a temp reference until the driver has handled this command.
- proxy->incStrong(mProcess.get());
- mPostWriteStrongDerefs.push(proxy);
+ if (!flushIfNeeded()) {
+ // Create a temp reference until the driver has handled this command.
+ proxy->incStrong(mProcess.get());
+ mPostWriteStrongDerefs.push(proxy);
+ }
}
void IPCThreadState::decStrongHandle(int32_t handle)
@@ -749,6 +766,7 @@
LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle);
mOut.writeInt32(BC_RELEASE);
mOut.writeInt32(handle);
+ flushIfNeeded();
}
void IPCThreadState::incWeakHandle(int32_t handle, BpBinder *proxy)
@@ -756,9 +774,11 @@
LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle);
mOut.writeInt32(BC_INCREFS);
mOut.writeInt32(handle);
- // Create a temp reference until the driver has handled this command.
- proxy->getWeakRefs()->incWeak(mProcess.get());
- mPostWriteWeakDerefs.push(proxy->getWeakRefs());
+ if (!flushIfNeeded()) {
+ // Create a temp reference until the driver has handled this command.
+ proxy->getWeakRefs()->incWeak(mProcess.get());
+ mPostWriteWeakDerefs.push(proxy->getWeakRefs());
+ }
}
void IPCThreadState::decWeakHandle(int32_t handle)
@@ -766,6 +786,7 @@
LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle);
mOut.writeInt32(BC_DECREFS);
mOut.writeInt32(handle);
+ flushIfNeeded();
}
status_t IPCThreadState::attemptIncStrongHandle(int32_t handle)
@@ -821,6 +842,7 @@
mServingStackPointer(nullptr),
mWorkSource(kUnsetWorkSource),
mPropagateWorkSource(false),
+ mIsLooper(false),
mStrictModePolicy(0),
mLastTransactionBinderFlags(0),
mCallRestriction(mProcess->mCallRestriction)
@@ -1401,6 +1423,7 @@
IPCThreadState* state = self();
state->mOut.writeInt32(BC_FREE_BUFFER);
state->mOut.writePointer((uintptr_t)data);
+ state->flushIfNeeded();
}
} // namespace android
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index 1173138..f96b6bb 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -38,8 +38,7 @@
bool allowIsolated, int dumpFlags);
void forcePersist(bool persist);
- void setActiveServicesCountCallback(const std::function<bool(int)>&
- activeServicesCountCallback);
+ void setActiveServicesCallback(const std::function<bool(bool)>& activeServicesCallback);
bool tryUnregister();
@@ -82,13 +81,16 @@
// count of services with clients
size_t mNumConnectedServices;
+ // previous value passed to the active services callback
+ std::optional<bool> mPreviousHasClients;
+
// map of registered names and services
std::map<std::string, Service> mRegisteredServices;
bool mForcePersist;
- // Callback used to report the number of services with clients
- std::function<bool(int)> mActiveServicesCountCallback;
+ // Callback used to report if there are services with clients
+ std::function<bool(bool)> mActiveServicesCallback;
};
class ClientCounterCallback {
@@ -103,8 +105,7 @@
*/
void forcePersist(bool persist);
- void setActiveServicesCountCallback(const std::function<bool(int)>&
- activeServicesCountCallback);
+ void setActiveServicesCallback(const std::function<bool(bool)>& activeServicesCallback);
bool tryUnregister();
@@ -158,7 +159,7 @@
void ClientCounterCallbackImpl::forcePersist(bool persist) {
mForcePersist = persist;
- if (!mForcePersist && mNumConnectedServices == 0) {
+ if (!mForcePersist) {
// Attempt a shutdown in case the number of clients hit 0 while the flag was on
maybeTryShutdown();
}
@@ -204,8 +205,12 @@
}
bool handledInCallback = false;
- if (mActiveServicesCountCallback != nullptr) {
- handledInCallback = mActiveServicesCountCallback(mNumConnectedServices);
+ if (mActiveServicesCallback != nullptr) {
+ bool hasClients = mNumConnectedServices != 0;
+ if (hasClients != mPreviousHasClients) {
+ handledInCallback = mActiveServicesCallback(hasClients);
+ mPreviousHasClients = hasClients;
+ }
}
// If there is no callback defined or the callback did not handle this
@@ -256,9 +261,9 @@
reRegister();
}
-void ClientCounterCallbackImpl::setActiveServicesCountCallback(const std::function<bool(int)>&
- activeServicesCountCallback) {
- mActiveServicesCountCallback = activeServicesCountCallback;
+void ClientCounterCallbackImpl::setActiveServicesCallback(const std::function<bool(bool)>&
+ activeServicesCallback) {
+ mActiveServicesCallback = activeServicesCallback;
}
ClientCounterCallback::ClientCounterCallback() {
@@ -274,9 +279,9 @@
mImpl->forcePersist(persist);
}
-void ClientCounterCallback::setActiveServicesCountCallback(const std::function<bool(int)>&
- activeServicesCountCallback) {
- mImpl->setActiveServicesCountCallback(activeServicesCountCallback);
+void ClientCounterCallback::setActiveServicesCallback(const std::function<bool(bool)>&
+ activeServicesCallback) {
+ mImpl->setActiveServicesCallback(activeServicesCallback);
}
bool ClientCounterCallback::tryUnregister() {
@@ -310,9 +315,9 @@
mClientCC->forcePersist(persist);
}
-void LazyServiceRegistrar::setActiveServicesCountCallback(const std::function<bool(int)>&
- activeServicesCountCallback) {
- mClientCC->setActiveServicesCountCallback(activeServicesCountCallback);
+void LazyServiceRegistrar::setActiveServicesCallback(const std::function<bool(bool)>&
+ activeServicesCallback) {
+ mClientCC->setActiveServicesCallback(activeServicesCallback);
}
bool LazyServiceRegistrar::tryUnregister() {
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index e2a0f87..f930d29 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -240,17 +240,9 @@
"android.hardware.ICameraRecordingProxyListener",
"android.hardware.ICrypto",
"android.hardware.IOMXObserver",
- "android.hardware.ISoundTrigger",
- "android.hardware.ISoundTriggerClient",
- "android.hardware.ISoundTriggerHwService",
"android.hardware.IStreamListener",
"android.hardware.IStreamSource",
- "android.media.IAudioFlinger",
- "android.media.IAudioFlingerClient",
- "android.media.IAudioPolicyService",
- "android.media.IAudioPolicyServiceClient",
"android.media.IAudioService",
- "android.media.IAudioTrack",
"android.media.IDataSource",
"android.media.IDrmClient",
"android.media.IMediaCodecList",
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 4da8aa1..0183324 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -110,6 +110,7 @@
status_t setupPolling(int* fd);
status_t handlePolledCommands();
void flushCommands();
+ bool flushIfNeeded();
void joinThreadPool(bool isMain = true);
@@ -204,6 +205,7 @@
int32_t mWorkSource;
// Whether the work source should be propagated.
bool mPropagateWorkSource;
+ bool mIsLooper;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
CallRestriction mCallRestriction;
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
index 73893c7..9659732 100644
--- a/libs/binder/include/binder/LazyServiceRegistrar.h
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -56,10 +56,10 @@
void forcePersist(bool persist);
/**
- * Set a callback that is executed when the total number of services with
- * clients changes.
- * The callback takes an argument, which is the number of registered
- * lazy services for this process which have clients.
+ * Set a callback that is invoked when the active service count (i.e. services with clients)
+ * registered with this process drops to zero (or becomes nonzero).
+ * The callback takes a boolean argument, which is 'true' if there is
+ * at least one service with clients.
*
* Callback return value:
* - false: Default behavior for lazy services (shut down the process if there
@@ -73,8 +73,7 @@
*
* This method should be called before 'registerService' to avoid races.
*/
- void setActiveServicesCountCallback(const std::function<bool(int)>&
- activeServicesCountCallback);
+ void setActiveServicesCallback(const std::function<bool(bool)>& activeServicesCallback);
/**
* Try to unregister all services previously registered with 'registerService'.
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 6b52c0a..0ad400b 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -265,7 +265,18 @@
AStatus_deleteDescription(cStr);
return ret;
}
- return "(not available)";
+ binder_exception_t exception = getExceptionCode();
+ std::string desc = std::to_string(exception);
+ if (exception == EX_SERVICE_SPECIFIC) {
+ desc += " (" + std::to_string(getServiceSpecificError()) + ")";
+ } else if (exception == EX_TRANSACTION_FAILED) {
+ desc += " (" + std::to_string(getStatus()) + ")";
+ }
+ if (const char* msg = getMessage(); msg != nullptr) {
+ desc += ": ";
+ desc += msg;
+ }
+ return desc;
}
/**
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index b7df115..0d1989e 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -375,8 +375,7 @@
AIBinder_decStrong(binder);
- // assert because would need to decStrong if non-null and we shouldn't need to add a no-op here
- ASSERT_NE(nullptr, AIBinder_Weak_promote(wBinder));
+ ASSERT_EQ(nullptr, AIBinder_Weak_promote(wBinder));
AIBinder_Weak_delete(wBinder);
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 97c2693..4a372bb 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1418,17 +1418,6 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
- const sp<IBinder>& token, const sp<IBinder>& focusedToken, nsecs_t timestampNanos,
- int32_t displayId) {
- FocusRequest request;
- request.token = token;
- request.focusedToken = focusedToken;
- request.timestamp = timestampNanos;
- request.displayId = displayId;
- return setFocusedWindow(request);
-}
-
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
const FocusRequest& request) {
mInputWindowCommands.focusRequests.push_back(request);
return *this;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 48bc5d5..11db658 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -522,8 +522,6 @@
#ifndef NO_INPUT
Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
- Transaction& setFocusedWindow(const sp<IBinder>& token, const sp<IBinder>& focusedToken,
- nsecs_t timestampNanos, int32_t displayId);
Transaction& setFocusedWindow(const FocusRequest& request);
Transaction& syncInputWindows();
#endif
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 3965ea0..31cbbdc 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -202,8 +202,14 @@
void requestFocus() {
SurfaceComposerClient::Transaction t;
- t.setFocusedWindow(mInputInfo.token, nullptr, systemTime(SYSTEM_TIME_MONOTONIC),
- 0 /* displayId */);
+ FocusRequest request;
+ request.token = mInputInfo.token;
+ request.windowName = mInputInfo.name;
+ request.focusedToken = nullptr;
+ request.focusedWindowName = "";
+ request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ request.displayId = 0;
+ t.setFocusedWindow(request);
t.apply(true);
}
diff --git a/libs/input/android/FocusRequest.aidl b/libs/input/android/FocusRequest.aidl
index 303dd1c..8812d34 100644
--- a/libs/input/android/FocusRequest.aidl
+++ b/libs/input/android/FocusRequest.aidl
@@ -22,6 +22,7 @@
* Input channel token used to identify the window that should gain focus.
*/
IBinder token;
+ @utf8InCpp String windowName;
/**
* The token that the caller expects currently to be focused. If the
* specified token does not match the currently focused window, this request will be dropped.
@@ -30,6 +31,7 @@
* is.
*/
@nullable IBinder focusedToken;
+ @utf8InCpp String focusedWindowName;
/**
* SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm) when requesting the focus
* change. This determines which request gets precedence if there is a focus change request
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 45db31c..b2ad22d 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -51,11 +51,28 @@
return renderengine::threaded::RenderEngineThreaded::create(
[args]() { return android::renderengine::gl::GLESRenderEngine::create(args); });
case RenderEngineType::SKIA_GL:
+ ALOGD("RenderEngine with SkiaGL Backend");
return renderengine::skia::SkiaGLRenderEngine::create(args);
- case RenderEngineType::SKIA_GL_THREADED:
- return renderengine::threaded::RenderEngineThreaded::create([args]() {
- return android::renderengine::skia::SkiaGLRenderEngine::create(args);
+ case RenderEngineType::SKIA_GL_THREADED: {
+ // These need to be recreated, since they are a constant reference, and we need to
+ // let SkiaRE know that it's running as threaded, and all GL operation will happen on
+ // the same thread.
+ RenderEngineCreationArgs skiaArgs =
+ RenderEngineCreationArgs::Builder()
+ .setPixelFormat(args.pixelFormat)
+ .setImageCacheSize(args.imageCacheSize)
+ .setUseColorManagerment(args.useColorManagement)
+ .setEnableProtectedContext(args.enableProtectedContext)
+ .setPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly)
+ .setSupportsBackgroundBlur(args.supportsBackgroundBlur)
+ .setContextPriority(args.contextPriority)
+ .setRenderEngineType(RenderEngineType::SKIA_GL_THREADED)
+ .build();
+ ALOGD("Threaded RenderEngine with SkiaGL Backend");
+ return renderengine::threaded::RenderEngineThreaded::create([skiaArgs]() {
+ return android::renderengine::skia::SkiaGLRenderEngine::create(skiaArgs);
});
+ }
case RenderEngineType::GLES:
default:
ALOGD("RenderEngine with GLES Backend");
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index c88e298..70ae0b2 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -746,6 +746,7 @@
}
void GLESRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ ATRACE_CALL();
mImageManager->cacheAsync(buffer, nullptr);
}
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 30f4b77..bb75878 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -30,7 +30,7 @@
namespace skia {
/**
- * AutoBackendTexture manages GPU image lifetime. It is a ref-counted object
+ * AutoBackendTexture manages GPU image lifetime. It is a ref-counted object
* that keeps GPU resources alive until the last SkImage or SkSurface object using them is
* destroyed.
*/
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index dc04f69..03e3339 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -267,7 +267,8 @@
mPlaceholderSurface(placeholder),
mProtectedEGLContext(protectedContext),
mProtectedPlaceholderSurface(protectedPlaceholder),
- mUseColorManagement(args.useColorManagement) {
+ mUseColorManagement(args.useColorManagement),
+ mRenderEngineType(args.renderEngineType) {
sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
LOG_ALWAYS_FATAL_IF(!glInterface.get());
@@ -453,7 +454,30 @@
return colorTransform != mat4() || needsToneMapping(sourceDataspace, destinationDataspace);
}
+void SkiaGLRenderEngine::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
+ // Only run this if RE is running on its own thread. This way the access to GL
+ // operations is guaranteed to be happening on the same thread.
+ if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED) {
+ return;
+ }
+ ATRACE_CALL();
+
+ std::lock_guard<std::mutex> lock(mRenderingMutex);
+ auto iter = mTextureCache.find(buffer->getId());
+ if (iter != mTextureCache.end()) {
+ ALOGV("Texture already exists in cache.");
+ return;
+ } else {
+ std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
+ std::make_shared<AutoBackendTexture::LocalRef>();
+ imageTextureRef->setTexture(
+ new AutoBackendTexture(mGrContext.get(), buffer->toAHardwareBuffer(), false));
+ mTextureCache.insert({buffer->getId(), imageTextureRef});
+ }
+}
+
void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) {
+ ATRACE_CALL();
std::lock_guard<std::mutex> lock(mRenderingMutex);
mTextureCache.erase(bufferId);
mProtectedTextureCache.erase(bufferId);
@@ -523,11 +547,13 @@
auto iter = cache.find(buffer->getId());
if (iter != cache.end()) {
ALOGV("Cache hit!");
+ ATRACE_NAME("Cache hit");
surfaceTextureRef = iter->second;
}
}
if (surfaceTextureRef == nullptr || surfaceTextureRef->getTexture() == nullptr) {
+ ATRACE_NAME("Cache miss");
surfaceTextureRef = std::make_shared<AutoBackendTexture::LocalRef>();
surfaceTextureRef->setTexture(
new AutoBackendTexture(grContext.get(), buffer->toAHardwareBuffer(), true));
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index ed62a2a..e344f41 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -50,6 +50,7 @@
EGLSurface protectedPlaceholder);
~SkiaGLRenderEngine() override EXCLUDES(mRenderingMutex);
+ void cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override;
void unbindExternalTextureBuffer(uint64_t bufferId) override;
status_t drawLayers(const DisplaySettings& display,
const std::vector<const LayerSettings*>& layers,
@@ -128,6 +129,10 @@
bool mInProtectedContext = false;
// Object to capture commands send to Skia.
std::unique_ptr<SkiaCapture> mCapture;
+
+ // Keep this information as a local variable to determine whether the access of the GL
+ // operations is working on the same threads.
+ const RenderEngineType mRenderEngineType = RenderEngineType::SKIA_GL;
};
} // namespace skia
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index ba5175d..02ff06f 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -62,22 +62,6 @@
mThreadedRE->deleteTextures(1, &texName);
}
-TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_nullptr) {
- EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(Eq(nullptr)));
- mThreadedRE->cacheExternalTextureBuffer(nullptr);
-}
-
-TEST_F(RenderEngineThreadedTest, cacheExternalTextureBuffer_withBuffer) {
- sp<GraphicBuffer> buf = new GraphicBuffer();
- EXPECT_CALL(*mRenderEngine, cacheExternalTextureBuffer(buf));
- mThreadedRE->cacheExternalTextureBuffer(buf);
-}
-
-TEST_F(RenderEngineThreadedTest, unbindExternalTextureBuffer) {
- EXPECT_CALL(*mRenderEngine, unbindExternalTextureBuffer(0x0));
- mThreadedRE->unbindExternalTextureBuffer(0x0);
-}
-
TEST_F(RenderEngineThreadedTest, getMaxTextureSize_returns20) {
size_t size = 20;
EXPECT_CALL(*mRenderEngine, getMaxTextureSize()).WillOnce(Return(size));
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 08f2949..3b97f56 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -147,33 +147,29 @@
}
void RenderEngineThreaded::cacheExternalTextureBuffer(const sp<GraphicBuffer>& buffer) {
- std::promise<void> resultPromise;
- std::future<void> resultFuture = resultPromise.get_future();
+ // This function is designed so it can run asynchronously, so we do not need to wait
+ // for the futures.
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, &buffer](renderengine::RenderEngine& instance) {
+ mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::cacheExternalTextureBuffer");
instance.cacheExternalTextureBuffer(buffer);
- resultPromise.set_value();
});
}
mCondition.notify_one();
- resultFuture.wait();
}
void RenderEngineThreaded::unbindExternalTextureBuffer(uint64_t bufferId) {
- std::promise<void> resultPromise;
- std::future<void> resultFuture = resultPromise.get_future();
+ // This function is designed so it can run asynchronously, so we do not need to wait
+ // for the futures.
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push([&resultPromise, &bufferId](renderengine::RenderEngine& instance) {
+ mFunctionCalls.push([=](renderengine::RenderEngine& instance) {
ATRACE_NAME("REThreaded::unbindExternalTextureBuffer");
instance.unbindExternalTextureBuffer(bufferId);
- resultPromise.set_value();
});
}
mCondition.notify_one();
- resultFuture.wait();
}
size_t RenderEngineThreaded::getMaxTextureSize() const {
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index d467692..e5b131e 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -25,6 +25,7 @@
"AnrTracker.cpp",
"Connection.cpp",
"Entry.cpp",
+ "FocusResolver.cpp",
"InjectionState.cpp",
"InputDispatcher.cpp",
"InputDispatcherFactory.cpp",
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 6953d04..a19b04f 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -102,7 +102,7 @@
// Focus notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
- std::string_view reason)
+ const std::string& reason)
: EventEntry(id, Type::FOCUS, eventTime, POLICY_FLAG_PASS_TO_USER),
connectionToken(connectionToken),
hasFocus(hasFocus),
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 26b641d..499f42e 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -93,10 +93,10 @@
struct FocusEntry : EventEntry {
sp<IBinder> connectionToken;
bool hasFocus;
- std::string_view reason;
+ std::string reason;
FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus,
- std::string_view reason);
+ const std::string& reason);
std::string getDescription() const override;
virtual ~FocusEntry();
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
new file mode 100644
index 0000000..ee6b38b
--- /dev/null
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#define LOG_TAG "FocusResolver"
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#define INDENT " "
+#define INDENT2 " "
+
+// Log debug messages about input focus tracking.
+static constexpr bool DEBUG_FOCUS = false;
+
+#include <inttypes.h>
+
+#include <android-base/stringprintf.h>
+#include <binder/Binder.h>
+#include <input/InputWindow.h>
+#include <input/NamedEnum.h>
+#include <log/log.h>
+
+#include "FocusResolver.h"
+
+namespace android::inputdispatcher {
+
+sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
+ auto it = mFocusedWindowTokenByDisplay.find(displayId);
+ return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
+}
+
+std::optional<FocusRequest> FocusResolver::getPendingRequest(int32_t displayId) {
+ auto it = mPendingFocusRequests.find(displayId);
+ return it != mPendingFocusRequests.end() ? std::make_optional<>(it->second) : std::nullopt;
+}
+
+std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows) {
+ // If the current focused window becomes unfocusable, remove focus.
+ sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
+ if (currentFocus) {
+ FocusResult result = isTokenFocusable(currentFocus, windows);
+ if (result != FocusResult::OK) {
+ return updateFocusedWindow(displayId, NamedEnum::string(result), nullptr);
+ }
+ }
+
+ // Check if any pending focus requests can be resolved.
+ std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
+ if (!pendingRequest) {
+ return std::nullopt;
+ }
+
+ sp<IBinder> requestedFocus = pendingRequest->token;
+ std::string windowName = pendingRequest->windowName;
+ if (currentFocus == requestedFocus) {
+ ALOGD_IF(DEBUG_FOCUS,
+ "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
+ windowName.c_str(), displayId);
+ mPendingFocusRequests.erase(displayId);
+ return std::nullopt;
+ }
+
+ FocusResult result = isTokenFocusable(requestedFocus, windows);
+ // If the window from the pending request is now visible, provide it focus.
+ if (result == FocusResult::OK) {
+ mPendingFocusRequests.erase(displayId);
+ return updateFocusedWindow(displayId, "Window became visible", requestedFocus, windowName);
+ }
+
+ if (result != FocusResult::NOT_VISIBLE) {
+ // Drop the request if we are unable to change the focus for a reason other than visibility.
+ ALOGW("Focus request %s on display %" PRId32 " ignored, reason:%s", windowName.c_str(),
+ displayId, NamedEnum::string(result).c_str());
+ mPendingFocusRequests.erase(displayId);
+ }
+ return std::nullopt;
+}
+
+std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
+ const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows) {
+ const int32_t displayId = request.displayId;
+ const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
+ if (request.focusedToken && currentFocus != request.focusedToken) {
+ ALOGW("setFocusedWindow %s on display %" PRId32
+ " ignored, reason: focusedToken %s is not focused",
+ request.windowName.c_str(), displayId, request.focusedWindowName.c_str());
+ return std::nullopt;
+ }
+
+ std::optional<FocusRequest> pendingRequest = getPendingRequest(displayId);
+ if (pendingRequest) {
+ ALOGW("Pending focus request %s on display %" PRId32
+ " ignored, reason:replaced by new request",
+ pendingRequest->windowName.c_str(), displayId);
+
+ // clear any pending focus requests
+ mPendingFocusRequests.erase(displayId);
+ }
+
+ if (currentFocus == request.token) {
+ ALOGD_IF(DEBUG_FOCUS,
+ "setFocusedWindow %s on display %" PRId32 " ignored, reason:already focused",
+ request.windowName.c_str(), displayId);
+ return std::nullopt;
+ }
+
+ FocusResult result = isTokenFocusable(request.token, windows);
+ if (result == FocusResult::OK) {
+ std::string reason =
+ (request.focusedToken) ? "setFocusedWindow with focus check" : "setFocusedWindow";
+ return updateFocusedWindow(displayId, reason, request.token, request.windowName);
+ }
+
+ if (result == FocusResult::NOT_VISIBLE) {
+ // The requested window is not currently visible. Wait for the window to become visible
+ // and then provide it focus. This is to handle situations where a user action triggers
+ // a new window to appear. We want to be able to queue any key events after the user
+ // action and deliver it to the newly focused window. In order for this to happen, we
+ // take focus from the currently focused window so key events can be queued.
+ ALOGD_IF(DEBUG_FOCUS,
+ "setFocusedWindow %s on display %" PRId32
+ " pending, reason: window is not visible",
+ request.windowName.c_str(), displayId);
+ mPendingFocusRequests[displayId] = request;
+ return updateFocusedWindow(displayId, "Waiting for window to be visible", nullptr);
+ } else {
+ ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason:%s",
+ request.windowName.c_str(), displayId, NamedEnum::string(result).c_str());
+ }
+
+ return std::nullopt;
+}
+
+FocusResolver::FocusResult FocusResolver::isTokenFocusable(
+ const sp<IBinder>& token, const std::vector<sp<InputWindowHandle>>& windows) {
+ bool allWindowsAreFocusable = true;
+ bool visibleWindowFound = false;
+ bool windowFound = false;
+ for (const sp<InputWindowHandle>& window : windows) {
+ if (window->getToken() != token) {
+ continue;
+ }
+ windowFound = true;
+ if (window->getInfo()->visible) {
+ // Check if at least a single window is visible.
+ visibleWindowFound = true;
+ }
+ if (!window->getInfo()->focusable) {
+ // Check if all windows with the window token are focusable.
+ allWindowsAreFocusable = false;
+ break;
+ }
+ }
+
+ if (!windowFound) {
+ return FocusResult::NO_WINDOW;
+ }
+ if (!allWindowsAreFocusable) {
+ return FocusResult::NOT_FOCUSABLE;
+ }
+ if (!visibleWindowFound) {
+ return FocusResult::NOT_VISIBLE;
+ }
+
+ return FocusResult::OK;
+}
+
+std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
+ int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus,
+ const std::string& tokenName) {
+ sp<IBinder> oldFocus = getFocusedWindowToken(displayId);
+ if (newFocus == oldFocus) {
+ return std::nullopt;
+ }
+ if (newFocus) {
+ mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus};
+ } else {
+ mFocusedWindowTokenByDisplay.erase(displayId);
+ }
+
+ return {{oldFocus, newFocus, displayId, reason}};
+}
+
+std::string FocusResolver::dumpFocusedWindows() const {
+ if (mFocusedWindowTokenByDisplay.empty()) {
+ return INDENT "FocusedWindows: <none>\n";
+ }
+
+ std::string dump;
+ dump += INDENT "FocusedWindows:\n";
+ for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) {
+ dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+ namedToken.first.c_str());
+ }
+ return dump;
+}
+
+std::string FocusResolver::dump() const {
+ std::string dump = dumpFocusedWindows();
+
+ if (mPendingFocusRequests.empty()) {
+ return dump + INDENT "PendingFocusRequests: <none>\n";
+ }
+
+ dump += INDENT "PendingFocusRequests:\n";
+ for (const auto& [displayId, request] : mPendingFocusRequests) {
+ dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
+ request.windowName.c_str());
+ }
+ return dump;
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
new file mode 100644
index 0000000..e067ad9
--- /dev/null
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <optional>
+#include <unordered_map>
+
+#include <android/FocusRequest.h>
+#include <binder/Binder.h>
+#include <input/InputWindow.h>
+
+namespace android::inputdispatcher {
+
+// Keeps track of the focused window per display. The class listens to updates from input dispatcher
+// and provides focus changes.
+//
+// Focus Policy
+// Window focusabilty - A window token can be focused if there is at least one window handle that
+// is visible with the same token and all window handles with the same token are focusable.
+// See FocusResolver::isTokenFocusable
+//
+// Focus request - Request will be granted if the window is focusable. If the window is not
+// visible, then the request is kept in a pending state and granted when it becomes visible.
+// If window becomes not focusable, or another request comes in, the pending request is dropped.
+//
+// Window handle updates - Focus is lost when the currently focused window becomes not focusable.
+class FocusResolver {
+public:
+ // Returns the focused window token on the specified display.
+ sp<IBinder> getFocusedWindowToken(int32_t displayId) const;
+
+ struct FocusChanges {
+ sp<IBinder> oldFocus;
+ sp<IBinder> newFocus;
+ int32_t displayId;
+ std::string reason;
+ };
+ std::optional<FocusResolver::FocusChanges> setInputWindows(
+ int32_t displayId, const std::vector<sp<InputWindowHandle>>& windows);
+ std::optional<FocusResolver::FocusChanges> setFocusedWindow(
+ const FocusRequest& request, const std::vector<sp<InputWindowHandle>>& windows);
+
+ // exposed for debugging
+ bool hasFocusedWindowTokens() const { return !mFocusedWindowTokenByDisplay.empty(); }
+ std::string dumpFocusedWindows() const;
+ std::string dump() const;
+
+private:
+ enum class FocusResult {
+ OK,
+ NO_WINDOW,
+ NOT_FOCUSABLE,
+ NOT_VISIBLE,
+ };
+
+ // Checks if the window token can be focused on a display. The token can be focused if there is
+ // at least one window handle that is visible with the same token and all window handles with
+ // the same token are focusable.
+ //
+ // In the case of mirroring, two windows may share the same window token and their visibility
+ // might be different. Example, the mirrored window can cover the window its mirroring. However,
+ // we expect the focusability of the windows to match since its hard to reason why one window
+ // can receive focus events and the other cannot when both are backed by the same input channel.
+ //
+ static FocusResult isTokenFocusable(const sp<IBinder>& token,
+ const std::vector<sp<InputWindowHandle>>& windows);
+
+ // Focus tracking for keys, trackball, etc. A window token can be associated with one or
+ // more InputWindowHandles. If a window is mirrored, the window and its mirror will share
+ // the same token. Focus is tracked by the token per display and the events are dispatched
+ // to the channel associated by this token.
+ typedef std::pair<std::string /* name */, sp<IBinder>> NamedToken;
+ std::unordered_map<int32_t /* displayId */, NamedToken> mFocusedWindowTokenByDisplay;
+
+ // This map will store a single pending focus request per display that cannot be currently
+ // processed. This can happen if the window requested to be focused is not currently visible.
+ // Such a window might become visible later, and these requests would be processed at that time.
+ std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests;
+
+ std::optional<FocusResolver::FocusChanges> updateFocusedWindow(
+ int32_t displayId, const std::string& reason, const sp<IBinder>& token,
+ const std::string& tokenName = "");
+ std::optional<FocusRequest> getPendingRequest(int32_t displayId);
+};
+
+} // namespace android::inputdispatcher
\ No newline at end of file
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index b3558c6..0495d68 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -127,6 +127,13 @@
return value ? "true" : "false";
}
+static inline const std::string toString(sp<IBinder> binder) {
+ if (binder == nullptr) {
+ return "<null>";
+ }
+ return StringPrintf("%p", binder.get());
+}
+
static inline int32_t getMotionEventActionPointerIndex(int32_t action) {
return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >>
AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
@@ -293,15 +300,6 @@
return removed;
}
-/**
- * Find the entry in std::unordered_map by key and return the value as an optional.
- */
-template <typename K, typename V>
-static std::optional<V> getOptionalValueByKey(const std::unordered_map<K, V>& map, K key) {
- auto it = map.find(key);
- return it != map.end() ? std::optional<V>{it->second} : std::nullopt;
-}
-
static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
if (first == second) {
return true;
@@ -420,19 +418,6 @@
return result;
}
-const char* InputDispatcher::typeToString(InputDispatcher::FocusResult result) {
- switch (result) {
- case InputDispatcher::FocusResult::OK:
- return "Ok";
- case InputDispatcher::FocusResult::NO_WINDOW:
- return "Window not found";
- case InputDispatcher::FocusResult::NOT_FOCUSABLE:
- return "Window not focusable";
- case InputDispatcher::FocusResult::NOT_VISIBLE:
- return "Window not visible";
- }
-}
-
template <typename T>
static bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) {
if (lhs == nullptr && rhs == nullptr) {
@@ -1218,7 +1203,7 @@
}
void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
- std::string_view reason) {
+ const std::string& reason) {
if (mPendingEvent != nullptr) {
// Move the pending event to the front of the queue. This will give the chance
// for the pending event to get dispatched to the newly focused window
@@ -1281,7 +1266,7 @@
ALOGW("No window requested Pointer Capture.");
return;
}
- token = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+ token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
mWindowTokenWithPointerCapture = token;
} else {
@@ -1380,7 +1365,7 @@
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
sp<IBinder> focusedWindowToken =
- getValueByKey(mFocusedWindowTokenByDisplay, getTargetDisplayId(*entry));
+ mFocusResolver.getFocusedWindowToken(getTargetDisplayId(*entry));
commandEntry->connectionToken = focusedWindowToken;
commandEntry->keyEntry = entry;
postCommandLocked(std::move(commandEntry));
@@ -2470,19 +2455,20 @@
std::string InputDispatcher::dumpWindowForTouchOcclusion(const InputWindowInfo* info,
bool isTouchedWindow) const {
- return StringPrintf(INDENT2 "* %stype=%s, package=%s/%" PRId32 ", id=%" PRId32
- ", mode=%s, alpha=%.2f, "
- "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
- "], touchableRegion=%s, window={%s}, applicationInfo=%s, "
- "flags={%s}, inputFeatures={%s}, hasToken=%s\n",
+ return StringPrintf(INDENT2
+ "* %stype=%s, package=%s/%" PRId32 ", id=%" PRId32 ", mode=%s, alpha=%.2f, "
+ "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32
+ "], touchableRegion=%s, window={%s}, flags={%s}, inputFeatures={%s}, "
+ "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n",
(isTouchedWindow) ? "[TOUCHED] " : "",
NamedEnum::string(info->type, "%" PRId32).c_str(),
info->packageName.c_str(), info->ownerUid, info->id,
toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft,
info->frameTop, info->frameRight, info->frameBottom,
dumpRegion(info->touchableRegion).c_str(), info->name.c_str(),
- info->applicationInfo.name.c_str(), info->flags.string().c_str(),
- info->inputFeatures.string().c_str(), toString(info->token != nullptr));
+ info->flags.string().c_str(), info->inputFeatures.string().c_str(),
+ toString(info->token != nullptr), info->applicationInfo.name.c_str(),
+ toString(info->applicationInfo.token).c_str());
}
bool InputDispatcher::isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const {
@@ -2910,7 +2896,7 @@
return;
}
- sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+ sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (focusedToken == token) {
// ignore since token is focused
return;
@@ -4159,7 +4145,7 @@
}
sp<InputWindowHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
- sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+ sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
return getWindowHandleLocked(focusedToken, displayId);
}
@@ -4323,24 +4309,10 @@
mLastHoverWindowHandle = nullptr;
}
- sp<IBinder> focusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
- if (focusedToken) {
- FocusResult result = checkTokenFocusableLocked(focusedToken, displayId);
- if (result != FocusResult::OK) {
- onFocusChangedLocked(focusedToken, nullptr, displayId, typeToString(result));
- }
- }
-
- std::optional<FocusRequest> focusRequest =
- getOptionalValueByKey(mPendingFocusRequests, displayId);
- if (focusRequest) {
- // If the window from the pending request is now visible, provide it focus.
- FocusResult result = handleFocusRequestLocked(*focusRequest);
- if (result != FocusResult::NOT_VISIBLE) {
- // Drop the request if we were able to change the focus or we cannot change
- // it for another reason.
- mPendingFocusRequests.erase(displayId);
- }
+ std::optional<FocusResolver::FocusChanges> changes =
+ mFocusResolver.setInputWindows(displayId, windowHandles);
+ if (changes) {
+ onFocusChangedLocked(*changes);
}
std::unordered_map<int32_t, TouchState>::iterator stateIt =
@@ -4444,7 +4416,7 @@
if (mFocusedDisplayId != displayId) {
sp<IBinder> oldFocusedWindowToken =
- getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+ mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (oldFocusedWindowToken != nullptr) {
std::shared_ptr<InputChannel> inputChannel =
getInputChannelLocked(oldFocusedWindowToken);
@@ -4459,15 +4431,14 @@
mFocusedDisplayId = displayId;
// Find new focused window and validate
- sp<IBinder> newFocusedWindowToken =
- getValueByKey(mFocusedWindowTokenByDisplay, displayId);
+ sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId);
notifyFocusChangedLocked(oldFocusedWindowToken, newFocusedWindowToken);
if (newFocusedWindowToken == nullptr) {
ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
- if (!mFocusedWindowTokenByDisplay.empty()) {
+ if (mFocusResolver.hasFocusedWindowTokens()) {
ALOGE("But another display has a focused window\n%s",
- dumpFocusedWindowsLocked().c_str());
+ mFocusResolver.dumpFocusedWindows().c_str());
}
}
}
@@ -4667,46 +4638,6 @@
}
}
-std::string InputDispatcher::dumpFocusedWindowsLocked() {
- if (mFocusedWindowTokenByDisplay.empty()) {
- return INDENT "FocusedWindows: <none>\n";
- }
-
- std::string dump;
- dump += INDENT "FocusedWindows:\n";
- for (auto& it : mFocusedWindowTokenByDisplay) {
- const int32_t displayId = it.first;
- const sp<InputWindowHandle> windowHandle = getFocusedWindowHandleLocked(displayId);
- if (windowHandle) {
- dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
- windowHandle->getName().c_str());
- } else {
- dump += StringPrintf(INDENT2 "displayId=%" PRId32
- " has focused token without a window'\n",
- displayId);
- }
- }
- return dump;
-}
-
-std::string InputDispatcher::dumpPendingFocusRequestsLocked() {
- if (mPendingFocusRequests.empty()) {
- return INDENT "mPendingFocusRequests: <none>\n";
- }
-
- std::string dump;
- dump += INDENT "mPendingFocusRequests:\n";
- for (const auto& [displayId, focusRequest] : mPendingFocusRequests) {
- // Rather than printing raw values for focusRequest.token and focusRequest.focusedToken,
- // try to resolve them to actual windows.
- std::string windowName = getConnectionNameLocked(focusRequest.token);
- std::string focusedWindowName = getConnectionNameLocked(focusRequest.focusedToken);
- dump += StringPrintf(INDENT2 "displayId=%" PRId32 ", token->%s, focusedToken->%s\n",
- displayId, windowName.c_str(), focusedWindowName.c_str());
- }
- return dump;
-}
-
std::string InputDispatcher::dumpPointerCaptureStateLocked() {
std::string dump;
@@ -4746,8 +4677,7 @@
dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
}
- dump += dumpFocusedWindowsLocked();
- dump += dumpPendingFocusRequestsLocked();
+ dump += mFocusResolver.dump();
dump += dumpPointerCaptureStateLocked();
if (!mTouchStatesByDisplay.empty()) {
@@ -4797,7 +4727,8 @@
"hasWallpaper=%s, visible=%s, alpha=%.2f, "
"flags=%s, type=%s, "
"frame=[%d,%d][%d,%d], globalScale=%f, "
- "applicationInfo=%s, "
+ "applicationInfo.name=%s, "
+ "applicationInfo.token=%s, "
"touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->id,
windowInfo->displayId, windowInfo->portalToDisplayId,
@@ -4810,7 +4741,8 @@
windowInfo->frameLeft, windowInfo->frameTop,
windowInfo->frameRight, windowInfo->frameBottom,
windowInfo->globalScaleFactor,
- windowInfo->applicationInfo.name.c_str());
+ windowInfo->applicationInfo.name.c_str(),
+ toString(windowInfo->applicationInfo.token).c_str());
dump += dumpRegion(windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=%s",
windowInfo->inputFeatures.string().c_str());
@@ -5140,8 +5072,7 @@
: "token without window");
}
- const sp<IBinder> focusedToken =
- getValueByKey(mFocusedWindowTokenByDisplay, mFocusedDisplayId);
+ const sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
if (focusedToken != windowToken) {
ALOGW("Ignoring request to %s Pointer Capture: window does not have focus.",
enabled ? "enable" : "disable");
@@ -5885,81 +5816,28 @@
void InputDispatcher::setFocusedWindow(const FocusRequest& request) {
{ // acquire lock
std::scoped_lock _l(mLock);
-
- const int32_t displayId = request.displayId;
- const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
- if (request.focusedToken && oldFocusedToken != request.focusedToken) {
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow on display %" PRId32
- " ignored, reason: focusedToken is not focused",
- displayId);
- return;
- }
-
- mPendingFocusRequests.erase(displayId);
- FocusResult result = handleFocusRequestLocked(request);
- if (result == FocusResult::NOT_VISIBLE) {
- // The requested window is not currently visible. Wait for the window to become visible
- // and then provide it focus. This is to handle situations where a user action triggers
- // a new window to appear. We want to be able to queue any key events after the user
- // action and deliver it to the newly focused window. In order for this to happen, we
- // take focus from the currently focused window so key events can be queued.
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow on display %" PRId32
- " pending, reason: window is not visible",
- displayId);
- mPendingFocusRequests[displayId] = request;
- onFocusChangedLocked(oldFocusedToken, nullptr, displayId,
- "setFocusedWindow_AwaitingWindowVisibility");
- } else if (result != FocusResult::OK) {
- ALOGW("setFocusedWindow on display %" PRId32 " ignored, reason:%s", displayId,
- typeToString(result));
+ std::optional<FocusResolver::FocusChanges> changes =
+ mFocusResolver.setFocusedWindow(request, getWindowHandlesLocked(request.displayId));
+ if (changes) {
+ onFocusChangedLocked(*changes);
}
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
-InputDispatcher::FocusResult InputDispatcher::handleFocusRequestLocked(
- const FocusRequest& request) {
- const int32_t displayId = request.displayId;
- const sp<IBinder> newFocusedToken = request.token;
- const sp<IBinder> oldFocusedToken = getValueByKey(mFocusedWindowTokenByDisplay, displayId);
-
- if (oldFocusedToken == request.token) {
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow on display %" PRId32 " ignored, reason: already focused",
- displayId);
- return FocusResult::OK;
- }
-
- FocusResult result = checkTokenFocusableLocked(newFocusedToken, displayId);
- if (result != FocusResult::OK) {
- return result;
- }
-
- std::string_view reason =
- (request.focusedToken) ? "setFocusedWindow_FocusCheck" : "setFocusedWindow";
- onFocusChangedLocked(oldFocusedToken, newFocusedToken, displayId, reason);
- return FocusResult::OK;
-}
-
-void InputDispatcher::onFocusChangedLocked(const sp<IBinder>& oldFocusedToken,
- const sp<IBinder>& newFocusedToken, int32_t displayId,
- std::string_view reason) {
- if (oldFocusedToken) {
- std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(oldFocusedToken);
+void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes) {
+ if (changes.oldFocus) {
+ std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus);
if (focusedInputChannel) {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
- enqueueFocusEventLocked(oldFocusedToken, false /*hasFocus*/, reason);
+ enqueueFocusEventLocked(changes.oldFocus, false /*hasFocus*/, changes.reason);
}
- mFocusedWindowTokenByDisplay.erase(displayId);
}
- if (newFocusedToken) {
- mFocusedWindowTokenByDisplay[displayId] = newFocusedToken;
- enqueueFocusEventLocked(newFocusedToken, true /*hasFocus*/, reason);
+ if (changes.newFocus) {
+ enqueueFocusEventLocked(changes.newFocus, true /*hasFocus*/, changes.reason);
}
// If a window has pointer capture, then it must have focus. We need to ensure that this
@@ -5972,8 +5850,8 @@
// front.
disablePointerCaptureForcedLocked();
- if (mFocusedDisplayId == displayId) {
- notifyFocusChangedLocked(oldFocusedToken, newFocusedToken);
+ if (mFocusedDisplayId == changes.displayId) {
+ notifyFocusChangedLocked(changes.oldFocus, changes.newFocus);
}
}
@@ -6006,50 +5884,6 @@
mInboundQueue.push_front(std::move(entry));
}
-/**
- * Checks if the window token can be focused on a display. The token can be focused if there is
- * at least one window handle that is visible with the same token and all window handles with the
- * same token are focusable.
- *
- * In the case of mirroring, two windows may share the same window token and their visibility
- * might be different. Example, the mirrored window can cover the window its mirroring. However,
- * we expect the focusability of the windows to match since its hard to reason why one window can
- * receive focus events and the other cannot when both are backed by the same input channel.
- */
-InputDispatcher::FocusResult InputDispatcher::checkTokenFocusableLocked(const sp<IBinder>& token,
- int32_t displayId) const {
- bool allWindowsAreFocusable = true;
- bool visibleWindowFound = false;
- bool windowFound = false;
- for (const sp<InputWindowHandle>& window : getWindowHandlesLocked(displayId)) {
- if (window->getToken() != token) {
- continue;
- }
- windowFound = true;
- if (window->getInfo()->visible) {
- // Check if at least a single window is visible.
- visibleWindowFound = true;
- }
- if (!window->getInfo()->focusable) {
- // Check if all windows with the window token are focusable.
- allWindowsAreFocusable = false;
- break;
- }
- }
-
- if (!windowFound) {
- return FocusResult::NO_WINDOW;
- }
- if (!allWindowsAreFocusable) {
- return FocusResult::NOT_FOCUSABLE;
- }
- if (!visibleWindowFound) {
- return FocusResult::NOT_VISIBLE;
- }
-
- return FocusResult::OK;
-}
-
void InputDispatcher::setPointerCaptureLocked(bool enabled) {
std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>(
&InputDispatcher::doSetPointerCaptureLockedInterruptible);
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index c7299e9..02f5b87 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -20,6 +20,7 @@
#include "AnrTracker.h"
#include "CancelationOptions.h"
#include "Entry.h"
+#include "FocusResolver.h"
#include "InjectionState.h"
#include "InputDispatcherConfiguration.h"
#include "InputDispatcherInterface.h"
@@ -146,14 +147,6 @@
NO_POINTER_CAPTURE,
};
- enum class FocusResult {
- OK,
- NO_WINDOW,
- NOT_FOCUSABLE,
- NOT_VISIBLE,
- };
- static const char* typeToString(FocusResult result);
-
std::unique_ptr<InputThread> mThread;
sp<InputDispatcherPolicyInterface> mPolicy;
@@ -190,7 +183,7 @@
// Enqueues a focus event.
void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
- std::string_view reason) REQUIRES(mLock);
+ const std::string& reason) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -333,9 +326,6 @@
sp<InputWindowHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock);
bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock);
bool hasResponsiveConnectionLocked(InputWindowHandle& windowHandle) const REQUIRES(mLock);
- FocusResult handleFocusRequestLocked(const FocusRequest&) REQUIRES(mLock);
- FocusResult checkTokenFocusableLocked(const sp<IBinder>& token, int32_t displayId) const
- REQUIRES(mLock);
/*
* Validate and update InputWindowHandles for a given display.
@@ -344,12 +334,6 @@
const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId)
REQUIRES(mLock);
- // Focus tracking for keys, trackball, etc. A window token can be associated with one or more
- // InputWindowHandles. If a window is mirrored, the window and its mirror will share the same
- // token. Focus is tracked by the token per display and the events are dispatched to the
- // channel associated by this token.
- std::unordered_map<int32_t, sp<IBinder>> mFocusedWindowTokenByDisplay GUARDED_BY(mLock);
-
std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
// Focused applications.
@@ -359,6 +343,8 @@
// Top focused display.
int32_t mFocusedDisplayId GUARDED_BY(mLock);
+ // Keeps track of the focused window per display and determines focus changes.
+ FocusResolver mFocusResolver GUARDED_BY(mLock);
// Whether the focused window on the focused display has requested Pointer Capture.
// The state of this variable should always be in sync with the state of Pointer Capture in the
// policy, which is updated through setPointerCaptureLocked(enabled).
@@ -462,13 +448,6 @@
*/
void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock);
- /**
- * This map will store the pending focus requests that cannot be currently processed. This can
- * happen if the window requested to be focused is not currently visible. Such a window might
- * become visible later, and these requests would be processed at that time.
- */
- std::unordered_map<int32_t /* displayId */, FocusRequest> mPendingFocusRequests
- GUARDED_BY(mLock);
// Optimization: AnrTracker is used to quickly find which connection is due for a timeout next.
// AnrTracker must be kept in-sync with all responsive connection.waitQueues.
@@ -586,8 +565,6 @@
void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock);
void dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors);
void logDispatchStateLocked() REQUIRES(mLock);
- std::string dumpFocusedWindowsLocked() REQUIRES(mLock);
- std::string dumpPendingFocusRequestsLocked() REQUIRES(mLock);
std::string dumpPointerCaptureStateLocked() REQUIRES(mLock);
// Registration.
@@ -603,8 +580,7 @@
uint32_t seq, bool handled) REQUIRES(mLock);
void onDispatchCycleBrokenLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
- void onFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus,
- int32_t displayId, std::string_view reason) REQUIRES(mLock);
+ void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock);
void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus)
REQUIRES(mLock);
void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock);
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 8cb7194..959dadc 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -30,6 +30,7 @@
"AnrTracker_test.cpp",
"BlockingQueue_test.cpp",
"EventHub_test.cpp",
+ "FocusResolver_test.cpp",
"IInputFlingerQuery.aidl",
"InputClassifier_test.cpp",
"InputClassifierConverter_test.cpp",
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
new file mode 100644
index 0000000..ef3dd65
--- /dev/null
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2021 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 <gtest/gtest.h>
+
+#include "../FocusResolver.h"
+
+// atest inputflinger_tests:FocusResolverTest
+
+namespace android::inputdispatcher {
+
+class FakeWindowHandle : public InputWindowHandle {
+public:
+ FakeWindowHandle(const std::string& name, const sp<IBinder>& token, bool focusable,
+ bool visible) {
+ mInfo.token = token;
+ mInfo.name = name;
+ mInfo.visible = visible;
+ mInfo.focusable = focusable;
+ }
+
+ bool updateInfo() { return true; }
+ void setFocusable(bool focusable) { mInfo.focusable = focusable; }
+ void setVisible(bool visible) { mInfo.visible = visible; }
+};
+
+TEST(FocusResolverTest, SetFocusedWindow) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ sp<IBinder> unfocusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ windows.push_back(new FakeWindowHandle("Focusable", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+ windows.push_back(new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
+ false /* visible */));
+ windows.push_back(new FakeWindowHandle("unfocusable", unfocusableWindowToken,
+ false /* focusable */, true /* visible */));
+
+ // focusable window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(nullptr, changes->oldFocus);
+ ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ ASSERT_EQ(request.displayId, changes->displayId);
+
+ // invisible window cannot get focused
+ request.token = invisibleWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_EQ(nullptr, changes->newFocus);
+
+ // unfocusableWindowToken window cannot get focused
+ request.token = unfocusableWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+}
+
+TEST(FocusResolverTest, SetFocusedMirroredWindow) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ sp<IBinder> unfocusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror1", focusableWindowToken, true /* focusable */,
+ true /* visible */));
+
+ windows.push_back(new FakeWindowHandle("Mirror2Visible", invisibleWindowToken,
+ true /* focusable */, true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror2Invisible", invisibleWindowToken,
+ true /* focusable */, false /* visible */));
+
+ windows.push_back(new FakeWindowHandle("Mirror3Focusable", unfocusableWindowToken,
+ true /* focusable */, true /* visible */));
+ windows.push_back(new FakeWindowHandle("Mirror3Unfocusable", unfocusableWindowToken,
+ false /* focusable */, true /* visible */));
+
+ // mirrored window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(nullptr, changes->oldFocus);
+ ASSERT_EQ(focusableWindowToken, changes->newFocus);
+
+ // mirrored window with one visible window can get focused
+ request.token = invisibleWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+ ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+
+ // mirrored window with one or more unfocusable window cannot get focused
+ request.token = unfocusableWindowToken;
+ changes = focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+}
+
+TEST(FocusResolverTest, SetInputWindows) {
+ sp<IBinder> focusableWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+ sp<FakeWindowHandle> window = new FakeWindowHandle("Focusable", focusableWindowToken,
+ true /* focusable */, true /* visible */);
+ windows.push_back(window);
+
+ // focusable window can get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_EQ(focusableWindowToken, changes->newFocus);
+
+ // Window visibility changes and the window loses focused
+ window->setVisible(false);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_EQ(nullptr, changes->newFocus);
+ ASSERT_EQ(focusableWindowToken, changes->oldFocus);
+}
+
+TEST(FocusResolverTest, FocusRequestsCanBePending) {
+ sp<IBinder> invisibleWindowToken = new BBinder();
+ std::vector<sp<InputWindowHandle>> windows;
+
+ sp<FakeWindowHandle> invisibleWindow =
+ new FakeWindowHandle("Invisible", invisibleWindowToken, true /* focusable */,
+ false /* visible */);
+ windows.push_back(invisibleWindow);
+
+ // invisible window cannot get focused
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = invisibleWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, windows);
+ ASSERT_FALSE(changes);
+
+ // Window visibility changes and the window gets focused
+ invisibleWindow->setVisible(true);
+ changes = focusResolver.setInputWindows(request.displayId, windows);
+ ASSERT_EQ(nullptr, changes->oldFocus);
+ ASSERT_EQ(invisibleWindowToken, changes->newFocus);
+}
+
+} // namespace android::inputdispatcher