Request focus using setFocusedWindow

This change introduces two new SurfaceControl apis, one to request focus
to a particular window and the other to transfer focus to an embedded
window if the host window is currently focused.

WindowHandleInfo is updated to specify focusability instead of the
currently focused window. This will provide more information to
InputDipatcher on what all the focusable windows are on screen and
in the future allow it to determine a new focus target.

Lastly, after updating all the WindowHandles in InputMonitor, we switch
over to calling setFocusedWindow to specify the desired focused window.
There should be no functional changes with this cl.

Test: atest inputflinger_tests
Test: atest CtsWindowManagerDeviceTestCases
Test: atest FlickerTests

Bug: 151179149
Change-Id: Iefda51ce73247b208115e64f9f8159a182d93ded
This commit is contained in:
Vishnu Nair
2020-08-15 15:56:20 -07:00
parent 056630f142
commit 3e5d102ef6
6 changed files with 107 additions and 10 deletions

View File

@@ -221,6 +221,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")
@@ -3184,6 +3186,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.
*

View File

@@ -1505,9 +1505,24 @@ 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);
}
// ----------------------------------------------------------------------------
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",
@@ -1686,7 +1701,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
};
int register_android_view_SurfaceControl(JNIEnv* env)

View File

@@ -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",

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;
}