Fix Instrumentation#setInTouchMode behavior.

Instrumentation#setInTouchMode should set the touch mode for all
displays, regardless of the value of the perDisplayFocusEnabled flag
and whether any display has own touch mode / own focus.

The fix is WindowManagerService#setInTouchModeOnAllDisplays ignoring
the "own focus" property of displays.

Test: atest TouchModeTest
Test: atest WindowManagerServiceTests
Fix: 267331091
Change-Id: I5a9010e23e3d91ac65f2d7ee8790cf2288bed1a2
This commit is contained in:
Vladimir Komsiyski
2023-02-01 12:36:19 +01:00
parent 67d4868207
commit dd448e5823
2 changed files with 87 additions and 27 deletions

View File

@@ -3831,8 +3831,9 @@ public class WindowManagerService extends IWindowManager.Stub
*
* If {@code com.android.internal.R.bool.config_perDisplayFocusEnabled} is set to true, then
* only the display represented by the {@code displayId} parameter will be requested to switch
* the touch mode state. Otherwise all all displays will be requested to switch their touch mode
* state (disregarding {@code displayId} parameter).
* the touch mode state. Otherwise all displays that do not maintain their own focus and touch
* mode will be requested to switch their touch mode state (disregarding {@code displayId}
* parameter).
*
* To be able to change touch mode state, the caller must either own the focused window, or must
* have the {@link android.Manifest.permission#MODIFY_TOUCH_MODE_STATE} permission. Instrumented
@@ -3844,27 +3845,9 @@ public class WindowManagerService extends IWindowManager.Stub
*/
@Override // Binder call
public void setInTouchMode(boolean inTouch, int displayId) {
boolean perDisplayFocusEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_perDisplayFocusEnabled);
setInTouchMode(inTouch, displayId, perDisplayFocusEnabled);
}
/**
* Sets the touch mode state on all displays (disregarding the value of
* {@code com.android.internal.R.bool.config_perDisplayFocusEnabled}).
*
* @param inTouch the touch mode to set
*/
@Override // Binder call
public void setInTouchModeOnAllDisplays(boolean inTouch) {
setInTouchMode(inTouch, /* any display id */ DEFAULT_DISPLAY,
/* perDisplayFocusEnabled= */ false);
}
private void setInTouchMode(boolean inTouch, int displayId, boolean perDisplayFocusEnabled) {
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (perDisplayFocusEnabled && (displayContent == null
if (mPerDisplayFocusEnabled && (displayContent == null
|| displayContent.isInTouchMode() == inTouch)) {
return;
}
@@ -3875,15 +3858,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final boolean hasPermission =
mAtmService.instrumentationSourceHasPermission(pid, MODIFY_TOUCH_MODE_STATE)
|| checkCallingPermission(MODIFY_TOUCH_MODE_STATE, "setInTouchMode()",
/* printlog= */ false);
final boolean hasPermission = hasTouchModePermission(pid);
final long token = Binder.clearCallingIdentity();
try {
// If perDisplayFocusEnabled is set or the display maintains its own touch mode,
// If mPerDisplayFocusEnabled is set or the display maintains its own touch mode,
// then just update the display pointed by displayId
if (perDisplayFocusEnabled || displayHasOwnTouchMode) {
if (mPerDisplayFocusEnabled || displayHasOwnTouchMode) {
if (mInputManager.setInTouchMode(inTouch, pid, uid, hasPermission, displayId)) {
displayContent.setInTouchMode(inTouch);
}
@@ -3906,6 +3886,41 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
/**
* Sets the touch mode state forcibly on all displays (disregarding both the value of
* {@code com.android.internal.R.bool.config_perDisplayFocusEnabled} and whether the display
* maintains its own focus and touch mode).
*
* @param inTouch the touch mode to set
*/
@Override // Binder call
public void setInTouchModeOnAllDisplays(boolean inTouch) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
final boolean hasPermission = hasTouchModePermission(pid);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
for (int i = 0; i < mRoot.mChildren.size(); ++i) {
DisplayContent dc = mRoot.mChildren.get(i);
if (dc.isInTouchMode() != inTouch
&& mInputManager.setInTouchMode(inTouch, pid, uid, hasPermission,
dc.mDisplayId)) {
dc.setInTouchMode(inTouch);
}
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
private boolean hasTouchModePermission(int pid) {
return mAtmService.instrumentationSourceHasPermission(pid, MODIFY_TOUCH_MODE_STATE)
|| checkCallingPermission(MODIFY_TOUCH_MODE_STATE, "setInTouchMode()",
/* printlog= */ false);
}
/**
* Returns the touch mode state for the display id passed as argument.
*/

View File

@@ -500,6 +500,51 @@ public class WindowManagerServiceTests extends WindowTestsBase {
virtualDisplay.getDisplay().getDisplayId());
}
@Test
public void testSetInTouchModeOnAllDisplays_perDisplayFocusDisabled() {
testSetInTouchModeOnAllDisplays(/* perDisplayFocusEnabled= */ false);
}
@Test
public void testSetInTouchModeOnAllDisplays_perDisplayFocusEnabled() {
testSetInTouchModeOnAllDisplays(/* perDisplayFocusEnabled= */ true);
}
private void testSetInTouchModeOnAllDisplays(boolean perDisplayFocusEnabled) {
// Create a couple of extra displays.
// setInTouchModeOnAllDisplays should ignore the ownFocus setting.
final VirtualDisplay virtualDisplay = createVirtualDisplay(/* ownFocus= */ false);
final VirtualDisplay virtualDisplayOwnFocus = createVirtualDisplay(/* ownFocus= */ true);
// Enable or disable global touch mode (config_perDisplayFocusEnabled setting).
// setInTouchModeOnAllDisplays should ignore this value.
Resources mockResources = mock(Resources.class);
spyOn(mContext);
when(mContext.getResources()).thenReturn(mockResources);
doReturn(perDisplayFocusEnabled).when(mockResources).getBoolean(
com.android.internal.R.bool.config_perDisplayFocusEnabled);
int callingPid = Binder.getCallingPid();
int callingUid = Binder.getCallingUid();
doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString(), anyBoolean());
when(mWm.mAtmService.instrumentationSourceHasPermission(callingPid,
android.Manifest.permission.MODIFY_TOUCH_MODE_STATE)).thenReturn(true);
for (boolean inTouchMode : new boolean[]{true, false}) {
mWm.setInTouchModeOnAllDisplays(inTouchMode);
for (int i = 0; i < mWm.mRoot.mChildren.size(); ++i) {
DisplayContent dc = mWm.mRoot.mChildren.get(i);
// All displays that are not already in the desired touch mode are requested to
// change their touch mode.
if (dc.isInTouchMode() != inTouchMode) {
verify(mWm.mInputManager).setInTouchMode(
true, callingPid, callingUid, /* hasPermission= */ true,
dc.getDisplay().getDisplayId());
}
}
}
}
private VirtualDisplay createVirtualDisplay(boolean ownFocus) {
// Create virtual display
Point surfaceSize = new Point(