summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/SurfaceControl.java35
-rw-r--r--core/jni/android_view_SurfaceControl.cpp21
-rw-r--r--data/etc/services.core.protolog.json12
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java6
-rw-r--r--services/core/java/com/android/server/wm/InputMonitor.java39
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java4
6 files changed, 107 insertions, 10 deletions
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3af8958368dc..67681dabf3f0 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -224,6 +224,8 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeReleaseFrameRateFlexibilityToken(long token);
private static native void nativeSetFixedTransformHint(long transactionObj, long nativeObject,
int transformHint);
+ private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken,
+ IBinder focusedToken, int displayId);
@Nullable
@GuardedBy("mLock")
@@ -3262,6 +3264,39 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Sets focus on the window identified by the input {@code token} if the window is focusable
+ * otherwise the request is dropped.
+ *
+ * If the window is not visible, the request will be queued until the window becomes
+ * visible or the request is overrriden by another request. The currently focused window
+ * will lose focus immediately. This is to send the newly focused window any focus
+ * dispatched events that occur while it is completing its first draw.
+ *
+ * @hide
+ */
+ public Transaction setFocusedWindow(@NonNull IBinder token, int displayId) {
+ nativeSetFocusedWindow(mNativeObject, token, null /* focusedToken */, 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,
+ @NonNull IBinder focusedToken,
+ int displayId) {
+ nativeSetFocusedWindow(mNativeObject, token, focusedToken, displayId);
+ return this;
+ }
+
+ /**
* Merge the other transaction into this transaction, clearing the
* other transaction as if it had been applied.
*
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 416f8372fd81..f2b4a1b2b032 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1541,10 +1541,25 @@ static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
return reinterpret_cast<jlong>(surfaceControl->getHandle().get());
}
+static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jobject toTokenObj, jobject focusedTokenObj, jint displayId) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ if (toTokenObj == NULL) return;
+
+ sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
+ sp<IBinder> focusedToken;
+ if (focusedTokenObj != NULL) {
+ focusedToken = ibinderForJavaObject(env, focusedTokenObj);
+ }
+ transaction->setFocusedWindow(toToken, focusedToken, systemTime(SYSTEM_TIME_MONOTONIC),
+ displayId);
+}
+
// ----------------------------------------------------------------------------
// clang-format off
static const JNINativeMethod sSurfaceControlMethods[] = {
+ // clang-format off
{"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJLandroid/os/Parcel;)J",
(void*)nativeCreate },
{"nativeReadFromParcel", "(Landroid/os/Parcel;)J",
@@ -1721,7 +1736,11 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetGlobalShadowSettings },
{"nativeGetHandle", "(J)J",
(void*)nativeGetHandle },
- {"nativeSetFixedTransformHint", "(JJI)V", (void*)nativeSetFixedTransformHint},
+ {"nativeSetFixedTransformHint", "(JJI)V",
+ (void*)nativeSetFixedTransformHint},
+ {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Landroid/os/IBinder;I)V",
+ (void*)nativeSetFocusedWindow},
+ // clang-format on
};
// clang-format on
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index a2a2216028ac..e69319896762 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1393,6 +1393,12 @@
"group": "WM_SHOW_SURFACE_ALLOC",
"at": "com\/android\/server\/wm\/BlackFrame.java"
},
+ "155482615": {
+ "message": "Focus requested for window=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/InputMonitor.java"
+ },
"174572959": {
"message": "DisplayArea info changed name=%s",
"level": "VERBOSE",
@@ -2635,6 +2641,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
},
+ "2081291430": {
+ "message": "Focus not requested for window=%s because it has no surface",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_FOCUS_LIGHT",
+ "at": "com\/android\/server\/wm\/InputMonitor.java"
+ },
"2083556954": {
"message": "Set mOrientationChanging of %s",
"level": "VERBOSE",
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8ccbd1166a44..77e573624b8a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -618,6 +618,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private boolean mInEnsureActivitiesVisible = false;
+ /**
+ * Last window to be requested focus via {@code SurfaceControl.Transaction#setFocusedWindow} to
+ * prevent duplicate requests to input.
+ */
+ WindowState mLastRequestedFocus = null;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final ActivityRecord activity = w.mActivityRecord;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 4efd687b7bb4..c493b66b6204 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -275,7 +275,7 @@ final class InputMonitor {
void populateInputWindowHandle(final InputWindowHandle inputWindowHandle,
final WindowState child, int flags, final int type, final boolean isVisible,
- final boolean hasFocus, final boolean hasWallpaper) {
+ final boolean focusable, final boolean hasWallpaper) {
// Add a window to our list of input windows.
inputWindowHandle.name = child.toString();
flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags);
@@ -283,7 +283,7 @@ final class InputMonitor {
inputWindowHandle.layoutParamsType = type;
inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis();
inputWindowHandle.visible = isVisible;
- inputWindowHandle.focusable = hasFocus;
+ inputWindowHandle.focusable = focusable;
inputWindowHandle.hasWallpaper = hasWallpaper;
inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false;
inputWindowHandle.ownerPid = child.mSession.mPid;
@@ -472,8 +472,9 @@ final class InputMonitor {
resetInputConsumers(mInputTransaction);
- mDisplayContent.forAllWindows(this,
- true /* traverseTopToBottom */);
+ mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
+
+ updateInputFocusRequest();
if (!mUpdateInputWindowsImmediately) {
mDisplayContent.getPendingTransaction().merge(mInputTransaction);
@@ -483,6 +484,29 @@ final class InputMonitor {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ private void updateInputFocusRequest() {
+ if (mDisplayContent.mLastRequestedFocus == mDisplayContent.mCurrentFocus) {
+ return;
+ }
+
+ final WindowState focus = mDisplayContent.mCurrentFocus;
+ if (focus == null || focus.mInputWindowHandle.token == null) {
+ mDisplayContent.mLastRequestedFocus = focus;
+ return;
+ }
+
+ if (!focus.mWinAnimator.hasSurface()) {
+ ProtoLog.d(WM_DEBUG_FOCUS_LIGHT,
+ "Focus not requested for window=%s because it has no surface",
+ focus);
+ return;
+ }
+
+ mInputTransaction.setFocusedWindow(focus.mInputWindowHandle.token, mDisplayId);
+ mDisplayContent.mLastRequestedFocus = focus;
+ ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", focus);
+ }
+
@Override
public void accept(WindowState w) {
final InputChannel inputChannel = w.mInputChannel;
@@ -510,11 +534,12 @@ final class InputMonitor {
final int flags = w.mAttrs.flags;
final int privateFlags = w.mAttrs.privateFlags;
- final boolean hasFocus = w.isFocused();
+ final boolean focusable = w.canReceiveKeys()
+ && (mService.mPerDisplayFocusEnabled || mDisplayContent.isOnTop());
if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
if (recentsAnimationController.updateInputConsumerForApp(
- mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
+ mRecentsAnimationInputConsumer.mWindowHandle, focusable)) {
mRecentsAnimationInputConsumer.show(mInputTransaction, w);
mAddRecentsAnimationInputConsumerHandle = false;
}
@@ -559,7 +584,7 @@ final class InputMonitor {
}
populateInputWindowHandle(
- inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper);
+ inputWindowHandle, w, flags, type, isVisible, focusable, hasWallpaper);
// register key interception info
mService.mKeyInterceptionInfoForToken.put(inputWindowHandle.token,
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 6882dc4ca151..b50cb4c34398 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -814,14 +814,14 @@ public class RecentsAnimationController implements DeathRecipient {
}
boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
- boolean hasFocus) {
+ boolean focusable) {
// Update the input consumer touchable region to match the target app main window
final WindowState targetAppMainWindow = mTargetActivityRecord != null
? mTargetActivityRecord.findMainWindow()
: null;
if (targetAppMainWindow != null) {
targetAppMainWindow.getBounds(mTmpRect);
- inputWindowHandle.focusable = hasFocus;
+ inputWindowHandle.focusable = focusable;
inputWindowHandle.touchableRegion.set(mTmpRect);
return true;
}