diff options
9 files changed, 182 insertions, 19 deletions
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 819e89b67b38..01e8c4c64f27 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -344,4 +344,17 @@ interface IWindowSession { */ void updateInputChannel(in IBinder channelToken, int displayId, in SurfaceControl surface, int flags, int privateFlags, in Region region); + + /** + * Transfer window focus to an embedded window if the calling window has focus. + * + * @param window - calling window owned by the caller. Window can be null if there + * is no host window but the caller must have permissions to create an embedded + * window without a host window. + * @param inputToken - token identifying the embedded window that should gain focus. + * @param grantFocus - true if focus should be granted to the embedded window, false if focus + * should be transferred back to the host window. If there is no host + * window, the system will try to find a new focus target. + */ + void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus); } diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index 66ab3a32edfa..4d1faf79273f 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -70,10 +70,13 @@ public class SurfaceControlViewHost { public static final class SurfacePackage implements Parcelable { private SurfaceControl mSurfaceControl; private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; + private final IBinder mInputToken; - SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection) { + SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection, + IBinder inputToken) { mSurfaceControl = sc; mAccessibilityEmbeddedConnection = connection; + mInputToken = inputToken; } private SurfacePackage(Parcel in) { @@ -81,6 +84,7 @@ public class SurfaceControlViewHost { mSurfaceControl.readFromParcel(in); mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface( in.readStrongBinder()); + mInputToken = in.readStrongBinder(); } /** @@ -126,6 +130,15 @@ public class SurfaceControlViewHost { mSurfaceControl = null; } + /** + * Returns an input token used which can be used to request focus on the embedded surface. + * + * @hide + */ + public IBinder getInputToken() { + return mInputToken; + } + public static final @NonNull Creator<SurfacePackage> CREATOR = new Creator<SurfacePackage>() { public SurfacePackage createFromParcel(Parcel in) { @@ -198,7 +211,8 @@ public class SurfaceControlViewHost { */ public @Nullable SurfacePackage getSurfacePackage() { if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { - return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection); + return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection, + mViewRoot.getInputToken()); } else { return null; } @@ -210,6 +224,7 @@ public class SurfaceControlViewHost { @TestApi public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) { Objects.requireNonNull(view); + view.setLayoutParams(attrs); mViewRoot.setView(view, attrs, null); } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index f937bc9e84a9..03822d60fa12 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1846,6 +1846,23 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } + @Override + protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction, + @Nullable Rect previouslyFocusedRect) { + super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); + final ViewRootImpl viewRoot = getViewRootImpl(); + if (mSurfacePackage == null || viewRoot == null) { + return; + } + try { + viewRoot.mWindowSession.grantEmbeddedWindowFocus(viewRoot.mWindow, + mSurfacePackage.getInputToken(), gainFocus); + } catch (Exception e) { + Log.e(TAG, System.identityHashCode(this) + + "Exception requesting focus on embedded window", e); + } + } + /** * Wrapper of accessibility embedded connection for embedded view hierarchy. */ diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 060311ec3da8..67abb4c933cc 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -457,4 +457,9 @@ public class WindowlessWindowManager implements IWindowSession { return surfaceInsets != null ? attrs.height + surfaceInsets.top + surfaceInsets.bottom : attrs.height; } + + @Override + public void grantEmbeddedWindowFocus(IWindow callingWindow, IBinder targetInputToken, + boolean grantFocus) { + } } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index e69319896762..217de84cfc3e 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -31,6 +31,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "-2107721178": { + "message": "grantEmbeddedWindowFocus win=%s grantFocus=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_FOCUS", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "-2101985723": { "message": "Failed looking up window session=%s callers=%s", "level": "WARN", @@ -1597,6 +1603,12 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "397105698": { + "message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found", + "level": "VERBOSE", + "group": "WM_DEBUG_FOCUS", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "399841913": { "message": "SURFACE RECOVER DESTROY: %s", "level": "INFO", diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index 5a2484735fb9..6d67f62a248a 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -134,10 +134,13 @@ class EmbeddedWindowController { final int mOwnerUid; final int mOwnerPid; final WindowManagerService mWmService; + final int mDisplayId; + public Session mSession; InputChannel mInputChannel; final int mWindowType; /** + * @param session calling session to check ownership of the window * @param clientToken client token used to clean up the map if the embedding process dies * @param hostWindowState input channel token belonging to the host window. This is needed * to handle input callbacks to wm. It's used when raising ANR and @@ -145,9 +148,13 @@ class EmbeddedWindowController { * can be null if there is no host window. * @param ownerUid calling uid * @param ownerPid calling pid used for anr blaming + * @param windowType to forward to input + * @param displayId used for focus requests */ - EmbeddedWindow(WindowManagerService service, IWindow clientToken, - WindowState hostWindowState, int ownerUid, int ownerPid, int windowType) { + EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken, + WindowState hostWindowState, int ownerUid, int ownerPid, int windowType, + int displayId) { + mSession = session; mWmService = service; mClient = clientToken; mHostWindowState = hostWindowState; @@ -156,6 +163,7 @@ class EmbeddedWindowController { mOwnerUid = ownerUid; mOwnerPid = ownerPid; mWindowType = windowType; + mDisplayId = displayId; } String getName() { diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 3b32a9d76258..f1e15550b4ff 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -677,7 +677,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final long identity = Binder.clearCallingIdentity(); try { - mService.grantInputChannel(mUid, mPid, displayId, surface, window, hostInputToken, + mService.grantInputChannel(this, mUid, mPid, displayId, surface, window, hostInputToken, flags, mCanAddInternalSystemWindow ? privateFlags : 0, mCanAddInternalSystemWindow ? type : 0, outInputChannel); } finally { @@ -696,4 +696,25 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { Binder.restoreCallingIdentity(identity); } } + + @Override + public void grantEmbeddedWindowFocus(IWindow callingWindow, IBinder targetInputToken, + boolean grantFocus) { + final long identity = Binder.clearCallingIdentity(); + try { + if (callingWindow == null) { + if (!mCanAddInternalSystemWindow) { + // Callers without INTERNAL_SYSTEM_WINDOW permission cannot request focus on + // embedded windows without providing the calling window + throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission"); + } + mService.grantEmbeddedWindowFocus(this, targetInputToken, grantFocus); + } else { + mService.grantEmbeddedWindowFocus(this, callingWindow, targetInputToken, + grantFocus); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 3fd8aaf9102b..1e7764252cb0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7995,16 +7995,17 @@ public class WindowManagerService extends IWindowManager.Stub * Used by WindowlessWindowManager to enable input on SurfaceControl embedded * views. */ - void grantInputChannel(int callingUid, int callingPid, int displayId, SurfaceControl surface, - IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type, - InputChannel outInputChannel) { + void grantInputChannel(Session session, int callingUid, int callingPid, int displayId, + SurfaceControl surface, IWindow window, IBinder hostInputToken, + int flags, int privateFlags, int type, InputChannel outInputChannel) { final InputApplicationHandle applicationHandle; final String name; final InputChannel clientChannel; synchronized (mGlobalLock) { EmbeddedWindowController.EmbeddedWindow win = - new EmbeddedWindowController.EmbeddedWindow(this, window, - mInputToWindowMap.get(hostInputToken), callingUid, callingPid, type); + new EmbeddedWindowController.EmbeddedWindow(session, this, window, + mInputToWindowMap.get(hostInputToken), callingUid, callingPid, type, + displayId); clientChannel = win.openInputChannel(); mEmbeddedWindowController.add(clientChannel.getToken(), win); applicationHandle = win.getApplicationHandle(); @@ -8019,19 +8020,19 @@ public class WindowManagerService extends IWindowManager.Stub } private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid, - int displayId, SurfaceControl surface, String name, - InputApplicationHandle applicationHandle, int flags, int privateFlags, int type, - Region region) { + int displayId, SurfaceControl surface, String name, + InputApplicationHandle applicationHandle, int flags, + int privateFlags, int type, Region region) { InputWindowHandle h = new InputWindowHandle(applicationHandle, displayId); h.token = channelToken; h.name = name; final int sanitizedFlags = flags & (LayoutParams.FLAG_NOT_TOUCHABLE - | LayoutParams.FLAG_SLIPPERY); + | LayoutParams.FLAG_SLIPPERY | LayoutParams.FLAG_NOT_FOCUSABLE); h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags; h.layoutParamsType = type; h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; - h.focusable = false; + h.focusable = (flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0; h.hasWallpaper = false; h.paused = false; @@ -8217,4 +8218,75 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); } } + + void grantEmbeddedWindowFocus(Session session, IBinder targetInputToken, boolean grantFocus) { + synchronized (mGlobalLock) { + final EmbeddedWindowController.EmbeddedWindow embeddedWindow = + mEmbeddedWindowController.get(targetInputToken); + if (embeddedWindow == null) { + Slog.e(TAG, "Embedded window not found"); + return; + } + if (embeddedWindow.mSession != session) { + Slog.e(TAG, "Window not in session:" + session); + return; + } + SurfaceControl.Transaction t = mTransactionFactory.get(); + final int displayId = embeddedWindow.mDisplayId; + if (grantFocus) { + t.setFocusedWindow(targetInputToken, displayId).apply(); + } else { + // Search for a new focus target + DisplayContent displayContent = mRoot.getDisplayContent(displayId); + WindowState newFocusTarget = displayContent == null + ? null : displayContent.findFocusedWindow(); + if (newFocusTarget == null) { + ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus remove request for " + + "win=%s dropped since no candidate was found", + embeddedWindow.getName()); + return; + } + t.requestFocusTransfer(newFocusTarget.mInputWindowHandle.token, targetInputToken, + displayId).apply(); + } + ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s", + embeddedWindow.getName(), grantFocus); + } + } + + void grantEmbeddedWindowFocus(Session session, IWindow callingWindow, IBinder targetInputToken, + boolean grantFocus) { + synchronized (mGlobalLock) { + final WindowState hostWindow = + windowForClientLocked(session, callingWindow, false /* throwOnError*/); + if (hostWindow == null) { + Slog.e(TAG, "Host window not found"); + return; + } + if (hostWindow.mInputChannel == null) { + Slog.e(TAG, "Host window does not have an input channel"); + return; + } + final EmbeddedWindowController.EmbeddedWindow embeddedWindow = + mEmbeddedWindowController.get(targetInputToken); + if (embeddedWindow == null) { + Slog.e(TAG, "Embedded window not found"); + return; + } + if (embeddedWindow.mHostWindowState != hostWindow) { + Slog.e(TAG, "Embedded window does not belong to the host"); + return; + } + SurfaceControl.Transaction t = mTransactionFactory.get(); + if (grantFocus) { + t.requestFocusTransfer(targetInputToken, hostWindow.mInputChannel.getToken(), + hostWindow.getDisplayId()).apply(); + } else { + t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), targetInputToken, + hostWindow.getDisplayId()).apply(); + } + ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s", + embeddedWindow.getName(), grantFocus); + } + } } diff --git a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java index 3bc530975580..73e01634709e 100644 --- a/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java +++ b/tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java @@ -22,12 +22,11 @@ import android.graphics.Color; import android.graphics.PixelFormat; import android.os.Bundle; import android.view.Gravity; -import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.WindowManager; -import android.view.SurfaceControlViewHost; import android.widget.Button; import android.widget.FrameLayout; @@ -46,8 +45,6 @@ public class SurfaceControlViewHostTest extends Activity implements SurfaceHolde mView.setZOrderOnTop(true); mView.getHolder().addCallback(this); - - addEmbeddedView(); } void addEmbeddedView() { @@ -63,6 +60,8 @@ public class SurfaceControlViewHostTest extends Activity implements SurfaceHolde v.setBackgroundColor(Color.RED); } }); + v.getViewTreeObserver().addOnWindowFocusChangeListener(focused -> + v.setBackgroundColor(focused ? Color.MAGENTA : Color.DKGRAY)); WindowManager.LayoutParams lp = new WindowManager.LayoutParams(500, 500, WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE); @@ -71,6 +70,7 @@ public class SurfaceControlViewHostTest extends Activity implements SurfaceHolde @Override public void surfaceCreated(SurfaceHolder holder) { + addEmbeddedView(); } @Override |