summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/IWindowSession.aidl13
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java19
-rw-r--r--core/java/android/view/SurfaceView.java17
-rw-r--r--core/java/android/view/WindowlessWindowManager.java5
-rw-r--r--data/etc/services.core.protolog.json12
-rw-r--r--services/core/java/com/android/server/wm/EmbeddedWindowController.java12
-rw-r--r--services/core/java/com/android/server/wm/Session.java23
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java92
-rw-r--r--tests/SurfaceControlViewHostTest/src/com/android/test/viewembed/SurfaceControlViewHostTest.java8
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