diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java index 24a0355dd10eb..d35aff9a72b7c 100644 --- a/core/java/android/view/InputWindowHandle.java +++ b/core/java/android/view/InputWindowHandle.java @@ -158,6 +158,14 @@ public final class InputWindowHandle { */ public Matrix transform; + /** + * The input token for the window to which focus should be transferred when this input window + * can be successfully focused. If null, this input window will not transfer its focus to + * any other window. + */ + @Nullable + public IBinder focusTransferTarget; + private native void nativeDispose(); public InputWindowHandle(InputApplicationHandle inputApplicationHandle, int displayId) { @@ -195,6 +203,7 @@ public final class InputWindowHandle { transform = new Matrix(); transform.set(other.transform); } + focusTransferTarget = other.focusTransferTarget; } @Override diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 0db52aaa8b3d7..bc6a3b540ce7a 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -265,7 +265,7 @@ public final class SurfaceControl implements Parcelable { int transformHint); private static native void nativeRemoveCurrentInputFocus(long nativeObject, int displayId); private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken, - String windowName, IBinder focusedToken, String focusedWindowName, int displayId); + String windowName, int displayId); private static native void nativeSetFrameTimelineVsync(long transactionObj, long frameTimelineVsyncId); private static native void nativeAddJankDataListener(long nativeListener, @@ -3604,28 +3604,7 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setFocusedWindow(@NonNull IBinder token, String windowName, int displayId) { - nativeSetFocusedWindow(mNativeObject, token, windowName, - null /* focusedToken */, null /* focusedWindowName */, displayId); - return this; - } - - /** - * Set focus on the window identified by the input {@code token} if the window identified by - * the input {@code focusedToken} is currently focused. If the {@code focusedToken} does not - * have focus, the request is dropped. - * - * This is used by forward focus transfer requests from clients that host embedded windows, - * and want to transfer focus to/from them. - * - * @hide - */ - public Transaction requestFocusTransfer(@NonNull IBinder token, - String windowName, - @NonNull IBinder focusedToken, - String focusedWindowName, - int displayId) { - nativeSetFocusedWindow(mNativeObject, token, windowName, focusedToken, - focusedWindowName, displayId); + nativeSetFocusedWindow(mNativeObject, token, windowName, displayId); return this; } diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 0560cafe3e52f..98681446446b2 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -57,18 +57,16 @@ public class WindowlessWindowManager implements IWindowSession { SurfaceControl mLeash; Rect mFrame; Rect mAttachedFrame; + IBinder mFocusGrantToken; - State(SurfaceControl sc, WindowManager.LayoutParams p, - int displayId, IBinder inputChannelToken, IWindow client, SurfaceControl leash, - Rect frame, Rect attachedFrame) { + State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId, IWindow client, + SurfaceControl leash, Rect frame) { mSurfaceControl = sc; mParams.copyFrom(p); mDisplayId = displayId; - mInputChannelToken = inputChannelToken; mClient = client; mLeash = leash; mFrame = frame; - mAttachedFrame = attachedFrame; } }; @@ -182,45 +180,53 @@ public class WindowlessWindowManager implements IWindowSession { .setParent(leash) .build(); + final State state = new State(sc, attrs, displayId, window, leash, /* frame= */ new Rect()); + synchronized (this) { + State parentState = mStateForWindow.get(attrs.token); + if (parentState != null) { + state.mAttachedFrame = parentState.mFrame; + } + + // Give the first window the mFocusGrantToken since that's the token the host can use + // to give focus to the embedded. + if (mStateForWindow.isEmpty()) { + state.mFocusGrantToken = mFocusGrantToken; + } else { + state.mFocusGrantToken = new Binder(); + } + + mStateForWindow.put(window.asBinder(), state); + } + + if (state.mAttachedFrame == null) { + outAttachedFrame.set(0, 0, -1, -1); + } else { + outAttachedFrame.set(state.mAttachedFrame); + } + outSizeCompatScale[0] = 1f; + if (((attrs.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) { try { if (mRealWm instanceof IWindowSession.Stub) { mRealWm.grantInputChannel(displayId, new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"), - window, mHostInputToken, - attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type, - attrs.token, mFocusGrantToken, attrs.getTitle().toString(), + window, mHostInputToken, attrs.flags, attrs.privateFlags, + attrs.inputFeatures, attrs.type, + attrs.token, state.mFocusGrantToken, attrs.getTitle().toString(), outInputChannel); } else { mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token, - mFocusGrantToken, attrs.getTitle().toString(), outInputChannel); + state.mFocusGrantToken, attrs.getTitle().toString(), outInputChannel); } + state.mInputChannelToken = + outInputChannel != null ? outInputChannel.getToken() : null; } catch (RemoteException e) { Log.e(TAG, "Failed to grant input to surface: ", e); } } - final State state = new State(sc, attrs, displayId, - outInputChannel != null ? outInputChannel.getToken() : null, window, - leash, /* frame= */ new Rect(), /* attachedFrame= */ null); - Rect parentFrame = null; - synchronized (this) { - State parentState = mStateForWindow.get(attrs.token); - if (parentState != null) { - parentFrame = parentState.mFrame; - } - mStateForWindow.put(window.asBinder(), state); - } - state.mAttachedFrame = parentFrame; - if (parentFrame == null) { - outAttachedFrame.set(0, 0, -1, -1); - } else { - outAttachedFrame.set(parentFrame); - } - outSizeCompatScale[0] = 1f; - final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE | WindowManagerGlobal.ADD_FLAG_USE_BLAST; diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index 241320f317486..416d991bd64ce 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -74,6 +74,7 @@ static struct { WeakRefHandleField touchableRegionSurfaceControl; jfieldID transform; jfieldID windowToken; + jfieldID focusTransferTarget; } gInputWindowHandleClassInfo; static struct { @@ -216,6 +217,17 @@ bool NativeInputWindowHandle::updateInfo() { mInfo.windowToken.clear(); } + ScopedLocalRef + focusTransferTargetObj(env, + env->GetObjectField(obj, + gInputWindowHandleClassInfo + .focusTransferTarget)); + if (focusTransferTargetObj.get()) { + mInfo.focusTransferTarget = ibinderForJavaObject(env, focusTransferTargetObj.get()); + } else { + mInfo.focusTransferTarget.clear(); + } + env->DeleteLocalRef(obj); return true; } @@ -433,6 +445,9 @@ int register_android_view_InputWindowHandle(JNIEnv* env) { GET_FIELD_ID(gInputWindowHandleClassInfo.windowToken, clazz, "windowToken", "Landroid/os/IBinder;"); + GET_FIELD_ID(gInputWindowHandleClassInfo.focusTransferTarget, clazz, "focusTransferTarget", + "Landroid/os/IBinder;"); + jclass weakRefClazz; FIND_CLASS(weakRefClazz, "java/lang/ref/Reference"); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 03d6eece61e63..e42c6f107e6dd 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1820,17 +1820,11 @@ static void nativeRemoveCurrentInputFocus(JNIEnv* env, jclass clazz, jlong trans } static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj, - jobject toTokenObj, jstring windowNameJstr, - jobject focusedTokenObj, jstring focusedWindowNameJstr, - jint displayId) { + jobject toTokenObj, jstring windowNameJstr, jint displayId) { auto transaction = reinterpret_cast(transactionObj); if (toTokenObj == NULL) return; sp toToken(ibinderForJavaObject(env, toTokenObj)); - sp focusedToken; - if (focusedTokenObj != NULL) { - focusedToken = ibinderForJavaObject(env, focusedTokenObj); - } FocusRequest request; request.token = toToken; @@ -1839,11 +1833,6 @@ static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionO request.windowName = windowName.c_str(); } - request.focusedToken = focusedToken; - if (focusedWindowNameJstr != NULL) { - ScopedUtfChars focusedWindowName(env, focusedWindowNameJstr); - request.focusedWindowName = focusedWindowName.c_str(); - } request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); request.displayId = displayId; transaction->setFocusedWindow(request); @@ -2236,7 +2225,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetHandle }, {"nativeSetFixedTransformHint", "(JJI)V", (void*)nativeSetFixedTransformHint}, - {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I)V", + {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;I)V", (void*)nativeSetFocusedWindow}, {"nativeRemoveCurrentInputFocus", "(JI)V", (void*)nativeRemoveCurrentInputFocus}, diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index 052c09a0e0ebf..d65f464590c17 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -99,23 +99,6 @@ class EmbeddedWindowController { } } - WindowState getHostWindow(IBinder inputToken) { - EmbeddedWindow embeddedWindow = mWindows.get(inputToken); - return embeddedWindow != null ? embeddedWindow.mHostWindowState : null; - } - - boolean isOverlay(IBinder inputToken) { - EmbeddedWindow embeddedWindow = mWindows.get(inputToken); - return embeddedWindow != null ? embeddedWindow.getIsOverlay() : false; - } - - void setIsOverlay(IBinder focusGrantToken) { - EmbeddedWindow embeddedWindow = mWindowsByFocusToken.get(focusGrantToken); - if (embeddedWindow != null) { - embeddedWindow.setIsOverlay(); - } - } - void remove(IWindow client) { for (int i = mWindows.size() - 1; i >= 0; i--) { EmbeddedWindow ew = mWindows.valueAt(i); @@ -176,14 +159,15 @@ class EmbeddedWindowController { public Session mSession; InputChannel mInputChannel; final int mWindowType; - // Track whether the EmbeddedWindow is a system hosted overlay via - // {@link OverlayHost}. In the case of client hosted overlays, the client - // view hierarchy will take care of invoking requestEmbeddedWindowFocus - // but for system hosted overlays we have to do this via tapOutsideDetection - // and this variable is mostly used for tracking that. - boolean mIsOverlay = false; - private IBinder mFocusGrantToken; + /** + * A unique token associated with the embedded window that can be used by the host window + * to request focus transfer to the embedded. This is not the input token since we don't + * want to give clients access to each others input token. + */ + private final IBinder mFocusGrantToken; + + private boolean mIsFocusable; /** * @param session calling session to check ownership of the window @@ -199,7 +183,8 @@ class EmbeddedWindowController { */ EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken, WindowState hostWindowState, int ownerUid, int ownerPid, int windowType, - int displayId, IBinder focusGrantToken, String inputHandleName) { + int displayId, IBinder focusGrantToken, String inputHandleName, + boolean isFocusable) { mSession = session; mWmService = service; mClient = clientToken; @@ -214,6 +199,7 @@ class EmbeddedWindowController { final String hostWindowName = (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString() : ""; + mIsFocusable = isFocusable; mName = "Embedded{" + inputHandleName + hostWindowName + "}"; } @@ -279,13 +265,6 @@ class EmbeddedWindowController { return mOwnerUid; } - void setIsOverlay() { - mIsOverlay = true; - } - boolean getIsOverlay() { - return mIsOverlay; - } - IBinder getFocusGrantToken() { return mFocusGrantToken; } @@ -297,20 +276,33 @@ class EmbeddedWindowController { return null; } + void setIsFocusable(boolean isFocusable) { + mIsFocusable = isFocusable; + } + /** - * System hosted overlays need the WM to invoke grantEmbeddedWindowFocus and - * so we need to participate inside handlePointerDownOutsideFocus logic - * however client hosted overlays will rely on the hosting view hierarchy - * to grant and revoke focus, and so the server side logic is not needed. + * When an embedded window is touched when it's not currently focus, we need to switch + * focus to that embedded window unless the embedded window was marked as not focusable. */ @Override public boolean receiveFocusFromTapOutside() { - return mIsOverlay; + return mIsFocusable; } private void handleTap(boolean grantFocus) { if (mInputChannel != null) { - mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus); + if (mHostWindowState != null) { + mWmService.grantEmbeddedWindowFocus(mSession, mHostWindowState.mClient, + mFocusGrantToken, grantFocus); + if (grantFocus) { + // If granting focus to the embedded when tapped, we need to ensure the host + // gains focus as well or the transfer won't take effect since it requires + // the host to transfer the focus to the embedded. + mHostWindowState.handleTapOutsideFocusInsideSelf(); + } + } else { + mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus); + } } } diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java index 301c1846249ff..3d4e0ebac258c 100644 --- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java +++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java @@ -289,6 +289,14 @@ class InputWindowHandleWrapper { mChanged = true; } + void setFocusTransferTarget(IBinder toToken) { + if (mHandle.focusTransferTarget == toToken) { + return; + } + mHandle.focusTransferTarget = toToken; + mChanged = true; + } + @Override public String toString() { return mHandle + ", changed=" + mChanged; diff --git a/services/core/java/com/android/server/wm/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java index 88c410b263ca9..f8edc2b871be8 100644 --- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java +++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java @@ -90,8 +90,6 @@ class TrustedOverlayHost { requireOverlaySurfaceControl(); mOverlays.add(p); - mWmService.mEmbeddedWindowController.setIsOverlay(p.getInputToken()); - SurfaceControl.Transaction t = mWmService.mTransactionFactory.get(); t.reparent(p.getSurfaceControl(), mSurfaceControl) .show(p.getSurfaceControl()); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a7a90604f2286..dde87b1c35e8c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -8624,7 +8624,8 @@ public class WindowManagerService extends IWindowManager.Stub EmbeddedWindowController.EmbeddedWindow win = new EmbeddedWindowController.EmbeddedWindow(session, this, window, mInputToWindowMap.get(hostInputToken), callingUid, callingPid, - sanitizedType, displayId, focusGrantToken, inputHandleName); + sanitizedType, displayId, focusGrantToken, inputHandleName, + (flags & FLAG_NOT_FOCUSABLE) == 0); clientChannel = win.openInputChannel(); mEmbeddedWindowController.add(clientChannel.getToken(), win); applicationHandle = win.getApplicationHandle(); @@ -8745,6 +8746,7 @@ public class WindowManagerService extends IWindowManager.Stub } name = win.toString(); applicationHandle = win.getApplicationHandle(); + win.setIsFocusable((flags & FLAG_NOT_FOCUSABLE) == 0); } updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name, @@ -9022,24 +9024,23 @@ public class WindowManagerService extends IWindowManager.Stub Slog.e(TAG, "Embedded window does not belong to the host"); return; } - SurfaceControl.Transaction t = mTransactionFactory.get(); if (grantFocus) { - t.requestFocusTransfer(embeddedWindow.getInputChannelToken(), embeddedWindow.toString(), - hostWindow.mInputChannel.getToken(), - hostWindow.getName(), - hostWindow.getDisplayId()).apply(); + hostWindow.mInputWindowHandle.setFocusTransferTarget( + embeddedWindow.getInputChannelToken()); EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Transfer focus request " + embeddedWindow, "reason=grantEmbeddedWindowFocus(true)"); } else { - t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), hostWindow.getName(), - embeddedWindow.getInputChannelToken(), - embeddedWindow.toString(), - hostWindow.getDisplayId()).apply(); + hostWindow.mInputWindowHandle.setFocusTransferTarget(null); EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Transfer focus request " + hostWindow, "reason=grantEmbeddedWindowFocus(false)"); } + DisplayContent dc = mRoot.getDisplayContent(hostWindow.getDisplayId()); + if (dc != null) { + dc.getInputMonitor().updateInputWindowsLw(true); + } + ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s", embeddedWindow, grantFocus); }